1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
{{define "form-content"}}
<h1>{{.Title}}</h1>
<form method="POST" action="{{.Action}}" id="postForm">
<label for="title">Title</label>
<input type="text" id="title" name="title" value="{{.Post.Title}}" required autofocus>
<label for="slug">Slug</label>
<input type="text" id="slug" name="slug" value="{{.Post.Slug}}" placeholder="auto-generated from title">
<p class="hint">URL path for this post (e.g. "my-post" → /my-post). Lowercase letters, numbers, and hyphens only.</p>
<label for="date">Date</label>
<input type="date" id="date" name="date" value="{{.Post.Date}}">
<label for="tags">Tags</label>
<input type="text" id="tags" name="tags" value="{{range $i, $t := .Post.Tags}}{{if $i}}, {{end}}{{$t}}{{end}}" placeholder="tag1, tag2, tag3">
<p class="hint">Comma-separated list of tags.</p>
<label for="draft">
<input type="checkbox" id="draft" name="draft"{{if .Post.Draft}} checked{{end}}>
Draft (not published)
</label>
<label for="blocks">Content</label>
<div id="editor" style="border: 1px solid #ccc; border-radius: 4px; min-height: 340px;"></div>
<input type="hidden" id="blocks" name="blocks" value="">
<div class="form-actions">
<button type="submit" class="btn btn-primary">
{{if .IsNew}}Create Post{{else}}Save Changes{{end}}
</button>
<a href="/admin/" class="btn btn-secondary">Cancel</a>
</div>
</form>
<script type="module">
import { EditorJS, Header, Paragraph, List, Code, Quote, ImageTool } from 'editorjs-bundle';
import ComponentTool from '/assets/admin/lib/component-tool.js';
const blocksField = document.getElementById('blocks');
const form = document.getElementById('postForm');
const titleInput = document.getElementById('title');
const slugInput = document.getElementById('slug');
// Auto-populate slug from title for new posts only, until user edits slug manually.
{{if .IsNew}}
let slugManuallyEdited = slugInput.value !== '';
slugInput.addEventListener('input', () => { slugManuallyEdited = true; });
titleInput.addEventListener('input', () => {
if (slugManuallyEdited) return;
slugInput.value = titleInput.value
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
});
{{end}}
let initialData = [];
try {
initialData = JSON.parse(blocksField.value || '[]');
} catch (e) {
console.error('Failed to parse blocks JSON:', e);
}
const editor = new EditorJS({
holder: 'editor',
tools: {
header: Header,
paragraph: Paragraph,
list: List,
code: Code,
quote: Quote,
image: {
class: ImageTool,
config: {
endpoints: { byFile: '/admin/upload/image' },
},
},
component: ComponentTool,
},
data: { blocks: initialData },
});
// Sync editor content back to hidden field before form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
try {
const editorData = await editor.save();
blocksField.value = JSON.stringify(editorData);
} catch (err) {
console.error('Failed to save editor data:', err);
return;
}
form.submit();
});
</script>
{{end}}
|