@alstar/studio 0.0.0-beta.6 → 0.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/block.ts +63 -15
- package/components/AdminPanel.ts +46 -24
- package/components/{fields/Blocks.ts → BlockFieldRenderer.ts} +33 -30
- package/components/BlockRenderer.ts +22 -0
- package/components/Entries.ts +3 -2
- package/components/Entry.ts +13 -15
- package/components/FieldRenderer.ts +35 -0
- package/components/Render.ts +46 -0
- package/components/{layout.ts → SiteLayout.ts} +8 -12
- package/components/fields/Markdown.ts +44 -0
- package/components/fields/Text.ts +10 -10
- package/components/fields/index.ts +2 -2
- package/index.ts +31 -28
- package/package.json +3 -3
- package/pages/entry/[id].ts +17 -0
- package/{components → pages}/index.ts +7 -4
- package/pages/settings.ts +10 -0
- package/public/{admin-panel.css → studio/admin-panel.css} +14 -1
- package/public/studio/main.css +162 -0
- package/public/studio/main.js +10 -0
- package/public/studio/markdown-editor.js +34 -0
- package/public/studio/sortable-list.js +40 -0
- package/queries/block.ts +2 -0
- package/types.ts +70 -32
- package/utils/define.ts +21 -9
- package/utils/file-based-router.ts +1 -0
- package/utils/get-or-create-row.ts +20 -7
- package/utils/startup-log.ts +2 -2
- package/bin/alstar.ts +0 -42
- package/components/Block.ts +0 -55
- package/public/main.css +0 -103
- package/public/main.js +0 -48
- /package/public/{blocks.css → studio/blocks.css} +0 -0
- /package/public/{entry.css → studio/entry.css} +0 -0
- /package/public/{favicon.svg → studio/favicon.svg} +0 -0
- /package/public/{settings.css → studio/settings.css} +0 -0
package/api/block.ts
CHANGED
|
@@ -6,12 +6,13 @@ import { stripNewlines } from '../utils/strip-newlines.ts'
|
|
|
6
6
|
import { sql } from '../utils/sql.ts'
|
|
7
7
|
import { type Structure } from '../types.ts'
|
|
8
8
|
import { db } from '@alstar/db'
|
|
9
|
-
import Entries from '../components/Entries.ts'
|
|
10
9
|
import Entry from '../components/Entry.ts'
|
|
11
10
|
import {
|
|
12
11
|
blockWithChildren,
|
|
13
12
|
deleteBlockWithChildren,
|
|
14
13
|
} from '../queries/block-with-children.ts'
|
|
14
|
+
import AdminPanel from '../components/AdminPanel.ts'
|
|
15
|
+
import { query } from '../queries/index.ts'
|
|
15
16
|
|
|
16
17
|
export default (structure: Structure) => {
|
|
17
18
|
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
@@ -33,7 +34,7 @@ export default (structure: Structure) => {
|
|
|
33
34
|
|
|
34
35
|
await stream.writeSSE({
|
|
35
36
|
event: 'datastar-patch-elements',
|
|
36
|
-
data: `elements ${stripNewlines(
|
|
37
|
+
data: `elements ${stripNewlines(AdminPanel())}`,
|
|
37
38
|
})
|
|
38
39
|
})
|
|
39
40
|
})
|
|
@@ -67,30 +68,51 @@ export default (structure: Structure) => {
|
|
|
67
68
|
const name = formData.get('name')?.toString()
|
|
68
69
|
const entryId = formData.get('entryId')?.toString()
|
|
69
70
|
const parentId = formData.get('parentId')?.toString()
|
|
70
|
-
const sortOrder = formData.get('sort_order')?.toString()
|
|
71
|
+
// const sortOrder = formData.get('sort_order')?.toString()
|
|
71
72
|
|
|
72
|
-
if (!id || !value
|
|
73
|
+
if (!id || !value) return
|
|
73
74
|
|
|
74
75
|
const transaction = db.database.prepare(sql`
|
|
75
76
|
update blocks
|
|
76
77
|
set
|
|
77
78
|
value = ?
|
|
78
79
|
where
|
|
79
|
-
id =
|
|
80
|
-
and sort_order = ?;
|
|
80
|
+
id = ?;
|
|
81
81
|
`)
|
|
82
82
|
|
|
83
|
-
transaction.run(value, id
|
|
83
|
+
transaction.run(value, id)
|
|
84
84
|
|
|
85
85
|
if (entryId === parentId && name?.toString() === 'title') {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
data: `elements <a href="/admin/entry/${entryId}" id="block_link_${entryId}">${value}</a>`,
|
|
86
|
+
const rootBlock = query.block({
|
|
87
|
+
id: parentId?.toString() || null,
|
|
89
88
|
})
|
|
89
|
+
|
|
90
|
+
if (rootBlock.type !== 'single') {
|
|
91
|
+
await stream.writeSSE({
|
|
92
|
+
event: 'datastar-patch-elements',
|
|
93
|
+
data: `elements <a href="/admin/entry/${entryId}" id="block_link_${entryId}">${value}</a>`,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
90
96
|
}
|
|
91
97
|
})
|
|
92
98
|
})
|
|
93
99
|
|
|
100
|
+
app.patch('/value', async (c) => {
|
|
101
|
+
const body = await c.req.json()
|
|
102
|
+
|
|
103
|
+
const transaction = db.database.prepare(sql`
|
|
104
|
+
update blocks
|
|
105
|
+
set
|
|
106
|
+
value = ?
|
|
107
|
+
where
|
|
108
|
+
id = ?;
|
|
109
|
+
`)
|
|
110
|
+
|
|
111
|
+
transaction.run(body.value, body.id)
|
|
112
|
+
|
|
113
|
+
return c.json({ status: 200, message: 'success' })
|
|
114
|
+
})
|
|
115
|
+
|
|
94
116
|
app.delete('/block', async (c) => {
|
|
95
117
|
return streamSSE(c, async (stream) => {
|
|
96
118
|
const formData = await c.req.formData()
|
|
@@ -98,18 +120,44 @@ export default (structure: Structure) => {
|
|
|
98
120
|
const id = formData.get('id')?.toString()
|
|
99
121
|
const entryId = formData.get('entry_id')?.toString()
|
|
100
122
|
|
|
101
|
-
if (!id
|
|
123
|
+
if (!id) return
|
|
102
124
|
|
|
103
125
|
const transaction = db.database.prepare(deleteBlockWithChildren)
|
|
104
126
|
|
|
105
127
|
transaction.all(id)
|
|
106
128
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
if (entryId) {
|
|
130
|
+
await stream.writeSSE({
|
|
131
|
+
event: 'datastar-patch-elements',
|
|
132
|
+
data: `elements ${stripNewlines(Entry({ entryId: parseInt(entryId.toString()) }))}`,
|
|
133
|
+
})
|
|
134
|
+
} else {
|
|
135
|
+
await stream.writeSSE({
|
|
136
|
+
event: 'datastar-patch-elements',
|
|
137
|
+
data: `elements ${stripNewlines(AdminPanel())}`,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
111
140
|
})
|
|
112
141
|
})
|
|
113
142
|
|
|
143
|
+
app.post('/sort-order', async (c) => {
|
|
144
|
+
const id = c.req.query('id')
|
|
145
|
+
const sortOrder = c.req.query('sort-order')
|
|
146
|
+
|
|
147
|
+
if (!id || !sortOrder) return
|
|
148
|
+
|
|
149
|
+
const transaction = db.database.prepare(sql`
|
|
150
|
+
update blocks
|
|
151
|
+
set
|
|
152
|
+
sort_order = ?
|
|
153
|
+
where
|
|
154
|
+
id = ?
|
|
155
|
+
`)
|
|
156
|
+
|
|
157
|
+
transaction.run(sortOrder, id)
|
|
158
|
+
|
|
159
|
+
return c.json({ status: 200, message: 'success' })
|
|
160
|
+
})
|
|
161
|
+
|
|
114
162
|
return app
|
|
115
163
|
}
|
package/components/AdminPanel.ts
CHANGED
|
@@ -1,39 +1,61 @@
|
|
|
1
1
|
import { html } from 'hono/html'
|
|
2
|
-
import type { Structure } from '../types.ts'
|
|
3
2
|
import { logo } from './icons.ts'
|
|
4
3
|
import Entries from './Entries.ts'
|
|
5
4
|
import * as icons from './icons.ts'
|
|
5
|
+
import { studioStructure } from '../index.ts'
|
|
6
|
+
import { getOrCreateRow } from '../utils/get-or-create-row.ts'
|
|
6
7
|
|
|
7
|
-
export default (
|
|
8
|
-
const entries = Object.entries(
|
|
9
|
-
const type = typeof entries[0][1] !== 'string' ? entries[0][1].type : null
|
|
8
|
+
export default () => {
|
|
9
|
+
const entries = Object.entries(studioStructure)
|
|
10
10
|
|
|
11
11
|
return html`
|
|
12
|
-
<div class="admin-panel">
|
|
12
|
+
<div class="admin-panel" id="admin_panel">
|
|
13
13
|
<h1>
|
|
14
14
|
<a href="/admin" aria-label="Go to dashboard"> ${logo} </a>
|
|
15
15
|
</h1>
|
|
16
16
|
|
|
17
|
-
<aside>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
<aside style="width: 100%;">
|
|
18
|
+
${entries.map(([name, block]) => {
|
|
19
|
+
if (block.type === 'single') {
|
|
20
|
+
const data = getOrCreateRow({ parentId: null, name, field: block })
|
|
21
|
+
|
|
22
|
+
return html`
|
|
23
|
+
<section id="entries">
|
|
24
|
+
<ul>
|
|
25
|
+
<li>
|
|
26
|
+
<a
|
|
27
|
+
href="/admin/entry/${data.id}"
|
|
28
|
+
id="block_link_${data.id}"
|
|
29
|
+
>
|
|
30
|
+
${block.label}
|
|
31
|
+
</a>
|
|
32
|
+
</li>
|
|
33
|
+
</ul>
|
|
34
|
+
</section>
|
|
35
|
+
`
|
|
36
|
+
}
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
return html`
|
|
39
|
+
<form
|
|
40
|
+
data-on-submit="@post('/admin/api/block', { contentType: 'form' })"
|
|
41
|
+
style="display: flex; align-items: center; gap: 1rem;"
|
|
42
|
+
>
|
|
43
|
+
<input type="hidden" name="name" value="${name}" />
|
|
44
|
+
<button
|
|
45
|
+
class="ghost"
|
|
46
|
+
style="padding: 10px; margin: 0 -13px; display: flex;"
|
|
47
|
+
data-tooltip="New ${block.label}"
|
|
48
|
+
data-placement="right"
|
|
49
|
+
>
|
|
50
|
+
${icons.newDocument}
|
|
51
|
+
</button>
|
|
52
|
+
<p style="user-select: none;"><small>${block.label}</small></p>
|
|
53
|
+
</form>
|
|
54
|
+
|
|
55
|
+
${Entries({ name })}
|
|
56
|
+
`
|
|
57
|
+
})}
|
|
58
|
+
</aside>
|
|
37
59
|
|
|
38
60
|
<footer>
|
|
39
61
|
<a
|
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import { query } from '
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import * as icons from '
|
|
1
|
+
import { query } from '../queries/index.ts'
|
|
2
|
+
import type { BlocksFieldDefStructure } from '../types.ts'
|
|
3
|
+
import { BlockInstance } from '../utils/define.ts'
|
|
4
|
+
import { getOrCreateRow } from '../utils/get-or-create-row.ts'
|
|
5
|
+
import { html } from '../utils/html.ts'
|
|
6
|
+
import * as icons from './icons.ts'
|
|
7
|
+
import Render from './Render.ts'
|
|
7
8
|
|
|
8
9
|
export default (props: {
|
|
9
10
|
entryId: number
|
|
10
11
|
parentId: number
|
|
11
12
|
name: string
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
structure: BlocksFieldDefStructure
|
|
14
|
+
id?: number
|
|
15
|
+
sortOrder?: number
|
|
14
16
|
}) => {
|
|
15
|
-
const { entryId, parentId, name,
|
|
17
|
+
const { entryId, parentId, name, structure, id, sortOrder = 0 } = props
|
|
16
18
|
|
|
17
|
-
const data = getOrCreateRow(
|
|
19
|
+
const data = getOrCreateRow({
|
|
20
|
+
parentId,
|
|
21
|
+
name,
|
|
22
|
+
field: structure,
|
|
23
|
+
sortOrder,
|
|
24
|
+
id,
|
|
25
|
+
})
|
|
18
26
|
|
|
19
27
|
if (!data) return html`<p>No block</p>`
|
|
20
28
|
|
|
21
|
-
const entries = Object.entries(
|
|
22
|
-
|
|
23
|
-
BlockType | Field,
|
|
24
|
-
][]
|
|
29
|
+
const entries = Object.entries(structure.children)
|
|
30
|
+
|
|
25
31
|
const rows = query.blocks({ parent_id: data.id })
|
|
26
32
|
|
|
27
33
|
return html`
|
|
28
34
|
<div class="blocks-field">
|
|
29
35
|
<header>
|
|
30
|
-
<p>${
|
|
36
|
+
<p>${structure.label}</p>
|
|
31
37
|
|
|
32
38
|
<details class="dropdown">
|
|
33
39
|
<summary>Add</summary>
|
|
@@ -59,23 +65,21 @@ export default (props: {
|
|
|
59
65
|
|
|
60
66
|
<hr style="margin-top: 0;" />
|
|
61
67
|
|
|
62
|
-
<
|
|
68
|
+
<sortable-list data-id="${data.id}">
|
|
63
69
|
${rows.map((row, idx) => {
|
|
64
|
-
const [name, struct] =
|
|
65
|
-
[])
|
|
70
|
+
const [name, struct] =
|
|
71
|
+
entries.find(([name]) => name === row.name) || []
|
|
66
72
|
|
|
67
|
-
if (!name) return html
|
|
73
|
+
if (!name || !struct) return html`<p>No name</p>`
|
|
68
74
|
|
|
69
75
|
return html`
|
|
70
|
-
<article>
|
|
76
|
+
<article data-id="${row.id}">
|
|
71
77
|
<header>
|
|
72
78
|
${struct.label}
|
|
73
79
|
|
|
74
|
-
<!-- data-tooltip="Move"
|
|
75
|
-
data-placement="top" -->
|
|
76
80
|
<aside>
|
|
77
81
|
<button
|
|
78
|
-
data-handle="${data.id}"
|
|
82
|
+
data-handle-for="${data.id}"
|
|
79
83
|
class="ghost handle text-secondary"
|
|
80
84
|
style="cursor: grab"
|
|
81
85
|
>
|
|
@@ -100,19 +104,18 @@ export default (props: {
|
|
|
100
104
|
</aside>
|
|
101
105
|
</header>
|
|
102
106
|
|
|
103
|
-
${
|
|
107
|
+
${Render({
|
|
104
108
|
entryId,
|
|
105
|
-
parentId:
|
|
106
|
-
|
|
109
|
+
parentId:
|
|
110
|
+
struct.instanceOf === BlockInstance ? row.id : data.id,
|
|
111
|
+
id: row.id,
|
|
112
|
+
structure: struct,
|
|
107
113
|
name: name,
|
|
108
|
-
sortOrder: row.sort_order,
|
|
109
114
|
})}
|
|
110
|
-
|
|
111
|
-
<!-- <pre><code>{JSON.stringify(struct, null, 2)} - {row.sort_order}</code></pre> -->
|
|
112
115
|
</article>
|
|
113
116
|
`
|
|
114
117
|
})}
|
|
115
|
-
</
|
|
118
|
+
</sortable-list>
|
|
116
119
|
</div>
|
|
117
120
|
`
|
|
118
121
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { html } from 'hono/html'
|
|
2
|
+
import type { BlockDef } from '../types.ts'
|
|
3
|
+
import Render from './Render.ts'
|
|
4
|
+
|
|
5
|
+
export default (props: {
|
|
6
|
+
entryId: number
|
|
7
|
+
parentId: number
|
|
8
|
+
id?: number
|
|
9
|
+
structure: BlockDef
|
|
10
|
+
}) => {
|
|
11
|
+
const { entryId, parentId, structure, id } = props
|
|
12
|
+
|
|
13
|
+
const entries = Object.entries(structure.fields)
|
|
14
|
+
|
|
15
|
+
return entries.map(([name, field]) => {
|
|
16
|
+
try {
|
|
17
|
+
return Render({ entryId, parentId, structure: field, name })
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return html`<p>Cound not render: "${name}"</p>`
|
|
20
|
+
}
|
|
21
|
+
}).join('')
|
|
22
|
+
}
|
package/components/Entries.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { html } from 'hono/html'
|
|
2
2
|
import { query } from '../index.ts'
|
|
3
3
|
import * as icons from './icons.ts'
|
|
4
|
+
import type { BlockDef } from '../types.ts'
|
|
4
5
|
|
|
5
|
-
export default () => {
|
|
6
|
-
const entries = query.blocks({ parent_id: null, status: 'enabled' })
|
|
6
|
+
export default ({ name }: { name: string }) => {
|
|
7
|
+
const entries = query.blocks({ parent_id: null, status: 'enabled', name })
|
|
7
8
|
|
|
8
9
|
return html`
|
|
9
10
|
<section id="entries">
|
package/components/Entry.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { html } from 'hono/html'
|
|
2
2
|
import { query } from '../queries/index.ts'
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { studioStructure } from '../index.ts'
|
|
4
|
+
import Render from './Render.ts'
|
|
5
5
|
|
|
6
6
|
export default (props: { entryId: number }) => {
|
|
7
7
|
const data = query.block({ id: props.entryId?.toString() })
|
|
8
8
|
|
|
9
9
|
if (!data) return html`<p>No entry with id: "${props.entryId}"</p>`
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const structure = studioStructure[data.name]
|
|
12
12
|
|
|
13
|
-
return html
|
|
14
|
-
<div id="entry">
|
|
15
|
-
<div class="entry">
|
|
16
|
-
${blockStructure &&
|
|
17
|
-
Block({
|
|
18
|
-
entryId: props.entryId,
|
|
19
|
-
parentId: props.entryId,
|
|
20
|
-
blockStructure: blockStructure,
|
|
21
|
-
name: data.name,
|
|
22
|
-
})}
|
|
23
|
-
</div>
|
|
13
|
+
if (!structure) return html`<p>No structure of type: ${data.name}</p>`
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
return html`
|
|
16
|
+
<div id="entry" class="entry">
|
|
17
|
+
${Render({
|
|
18
|
+
entryId: props.entryId,
|
|
19
|
+
parentId: props.entryId,
|
|
20
|
+
structure: structure,
|
|
21
|
+
name: data.name,
|
|
22
|
+
})}
|
|
26
23
|
</div>
|
|
24
|
+
|
|
27
25
|
`
|
|
28
26
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Field } from './fields/index.ts'
|
|
2
|
+
import type { BlocksFieldDefStructure, FieldDefStructure } from '../types.ts'
|
|
3
|
+
import BlockFieldRenderer from './BlockFieldRenderer.ts'
|
|
4
|
+
|
|
5
|
+
export default (props: {
|
|
6
|
+
entryId: number
|
|
7
|
+
parentId: number
|
|
8
|
+
structure: FieldDefStructure | BlocksFieldDefStructure
|
|
9
|
+
id?: number
|
|
10
|
+
name: string
|
|
11
|
+
}) => {
|
|
12
|
+
const { entryId, parentId, structure, name, id } = props
|
|
13
|
+
|
|
14
|
+
switch (structure.type) {
|
|
15
|
+
case 'text': {
|
|
16
|
+
return Field.Text({ entryId, parentId, name, id, structure })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
case 'slug': {
|
|
20
|
+
return Field.Text({ entryId, parentId, name, id, structure })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
case 'markdown': {
|
|
24
|
+
return Field.Markdown({ entryId, parentId, name, id, structure })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
case 'image': {
|
|
28
|
+
return Field.Text({ entryId, parentId, name, structure, id })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
case 'blocks': {
|
|
32
|
+
return BlockFieldRenderer({ entryId, parentId, name, structure, id })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { html } from 'hono/html'
|
|
2
|
+
import type { HtmlEscapedString } from 'hono/utils/html'
|
|
3
|
+
import FieldRenderer from './FieldRenderer.ts'
|
|
4
|
+
import BlockFieldRenderer from './BlockFieldRenderer.ts'
|
|
5
|
+
import BlockRenderer from './BlockRenderer.ts'
|
|
6
|
+
import {
|
|
7
|
+
BlockFieldInstance,
|
|
8
|
+
BlockInstance,
|
|
9
|
+
FieldInstance,
|
|
10
|
+
} from '../utils/define.ts'
|
|
11
|
+
import type {
|
|
12
|
+
BlockDefStructure,
|
|
13
|
+
BlocksFieldDefStructure,
|
|
14
|
+
FieldDefStructure,
|
|
15
|
+
} from '../types.ts'
|
|
16
|
+
|
|
17
|
+
export default (props: {
|
|
18
|
+
entryId: number
|
|
19
|
+
parentId: number
|
|
20
|
+
structure: BlockDefStructure | FieldDefStructure | BlocksFieldDefStructure
|
|
21
|
+
id?: number
|
|
22
|
+
name: string
|
|
23
|
+
sortOrder?: number
|
|
24
|
+
}): HtmlEscapedString | Promise<HtmlEscapedString> | string => {
|
|
25
|
+
const { entryId, parentId, structure, name, id } = props
|
|
26
|
+
|
|
27
|
+
if (!structure) return html`<p>No block</p>`
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
switch (structure.instanceOf) {
|
|
31
|
+
case FieldInstance: {
|
|
32
|
+
return FieldRenderer({ entryId, parentId, id, structure, name })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
case BlockFieldInstance: {
|
|
36
|
+
return BlockFieldRenderer({ entryId, parentId, id, structure, name })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case BlockInstance: {
|
|
40
|
+
return BlockRenderer({ entryId, parentId, structure, id })
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return html`<p>Error rendering "${name}"</p>`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import adminPanel from './AdminPanel.ts'
|
|
2
2
|
import { html } from 'hono/html'
|
|
3
3
|
import { type HtmlEscapedString } from 'hono/utils/html'
|
|
4
|
-
import {
|
|
5
|
-
import { type Structure } from '../types.ts'
|
|
4
|
+
import { studioConfig } from '../index.ts'
|
|
6
5
|
|
|
7
6
|
export default (props: {
|
|
8
7
|
content:
|
|
@@ -10,7 +9,6 @@ export default (props: {
|
|
|
10
9
|
| Promise<string>
|
|
11
10
|
| HtmlEscapedString
|
|
12
11
|
| Promise<HtmlEscapedString>
|
|
13
|
-
structure: Structure
|
|
14
12
|
}) => {
|
|
15
13
|
return html`
|
|
16
14
|
<!DOCTYPE html>
|
|
@@ -23,15 +21,11 @@ export default (props: {
|
|
|
23
21
|
Studio
|
|
24
22
|
</title>
|
|
25
23
|
|
|
26
|
-
<link
|
|
27
|
-
rel="icon"
|
|
28
|
-
type="image/svg"
|
|
29
|
-
href="${rootdir}/public/favicon.svg"
|
|
30
|
-
/>
|
|
24
|
+
<link rel="icon" type="image/svg" href="/studio/favicon.svg" />
|
|
31
25
|
|
|
32
26
|
<meta name="color-scheme" content="light dark" />
|
|
33
27
|
|
|
34
|
-
<link rel="stylesheet" href="
|
|
28
|
+
<link rel="stylesheet" href="/studio/main.css" />
|
|
35
29
|
|
|
36
30
|
<script
|
|
37
31
|
type="module"
|
|
@@ -47,18 +41,20 @@ export default (props: {
|
|
|
47
41
|
}
|
|
48
42
|
}
|
|
49
43
|
</script>
|
|
44
|
+
|
|
45
|
+
<script src="/studio/markdown-editor.js" type="module"></script>
|
|
46
|
+
<script src="/studio/sortable-list.js" type="module"></script>
|
|
47
|
+
<script src="/studio/main.js" type="module"></script>
|
|
50
48
|
</head>
|
|
51
49
|
|
|
52
50
|
<body data-barba="wrapper">
|
|
53
|
-
<section style="margin-bottom: 0;">${adminPanel(
|
|
51
|
+
<section style="margin-bottom: 0;">${adminPanel()}</section>
|
|
54
52
|
|
|
55
53
|
<main>
|
|
56
54
|
<section data-barba="container" data-barba-namespace="default">
|
|
57
55
|
${props.content}
|
|
58
56
|
</section>
|
|
59
57
|
</main>
|
|
60
|
-
|
|
61
|
-
<script src="${rootdir}/public/main.js" type="module"></script>
|
|
62
58
|
</body>
|
|
63
59
|
</html>
|
|
64
60
|
`
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { FieldDefStructure } from '../../types.ts'
|
|
2
|
+
import { getOrCreateRow } from '../../utils/get-or-create-row.ts'
|
|
3
|
+
import { html } from '../../utils/html.ts'
|
|
4
|
+
|
|
5
|
+
export default (props: {
|
|
6
|
+
entryId: number
|
|
7
|
+
parentId: number
|
|
8
|
+
name: string
|
|
9
|
+
id?: number
|
|
10
|
+
structure: FieldDefStructure
|
|
11
|
+
}) => {
|
|
12
|
+
const { entryId, parentId, name, structure, id } = props
|
|
13
|
+
|
|
14
|
+
const data = getOrCreateRow({ parentId, name, field: structure, id })
|
|
15
|
+
|
|
16
|
+
if (!data) return html`<p>No block</p>`
|
|
17
|
+
|
|
18
|
+
return html`
|
|
19
|
+
<form
|
|
20
|
+
data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
|
|
21
|
+
>
|
|
22
|
+
<hgroup>
|
|
23
|
+
<label for="block-${data.id}">${structure.label}</label>
|
|
24
|
+
<p><small>${structure.description}</small></p>
|
|
25
|
+
</hgroup>
|
|
26
|
+
|
|
27
|
+
<markdown-editor
|
|
28
|
+
data-content="${data.value?.trim()}"
|
|
29
|
+
data-id="${data.id}"
|
|
30
|
+
>
|
|
31
|
+
<!-- <textarea id="block-{data.id}" name="value" class="markdown">
|
|
32
|
+
{data.value}
|
|
33
|
+
</textarea
|
|
34
|
+
> -->
|
|
35
|
+
</markdown-editor>
|
|
36
|
+
|
|
37
|
+
<input type="hidden" name="type" value="${structure.type}" />
|
|
38
|
+
<input type="hidden" name="id" value="${data.id}" />
|
|
39
|
+
<input type="hidden" name="entryId" value="${entryId}" />
|
|
40
|
+
<input type="hidden" name="parentId" value="${parentId}" />
|
|
41
|
+
<input type="hidden" name="name" value="${name}" />
|
|
42
|
+
</form>
|
|
43
|
+
`
|
|
44
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type { FieldDef } from '../../types.ts'
|
|
2
1
|
import { getOrCreateRow } from '../../utils/get-or-create-row.ts'
|
|
3
2
|
import { html } from '../../utils/html.ts'
|
|
3
|
+
import type { FieldDefStructure } from '../../types.ts'
|
|
4
4
|
|
|
5
5
|
export default (props: {
|
|
6
6
|
entryId: number
|
|
7
7
|
parentId: number
|
|
8
8
|
name: string
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
id?: number
|
|
10
|
+
structure: FieldDefStructure
|
|
11
|
+
sortOrder?: number
|
|
11
12
|
}) => {
|
|
12
|
-
const { entryId, parentId, name,
|
|
13
|
+
const { entryId, parentId, name, structure, sortOrder = 0, id } = props
|
|
13
14
|
|
|
14
|
-
const data = getOrCreateRow(parentId, name, field, sortOrder)
|
|
15
|
+
const data = getOrCreateRow({ parentId, name, field: structure, sortOrder, id })
|
|
15
16
|
|
|
16
17
|
if (!data) return html`<p>No block</p>`
|
|
17
18
|
|
|
@@ -20,10 +21,10 @@ export default (props: {
|
|
|
20
21
|
data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
|
|
21
22
|
>
|
|
22
23
|
<hgroup>
|
|
23
|
-
<label for="block-${data.id}">${
|
|
24
|
-
<p><small>${
|
|
24
|
+
<label for="block-${data.id}">${structure.label}</label>
|
|
25
|
+
<p><small>${structure.description}</small></p>
|
|
25
26
|
</hgroup>
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
<input
|
|
28
29
|
id="block-${data.id}"
|
|
29
30
|
name="value"
|
|
@@ -31,11 +32,10 @@ export default (props: {
|
|
|
31
32
|
value="${data.value}"
|
|
32
33
|
/>
|
|
33
34
|
|
|
34
|
-
<input type="hidden" name="type" value="${
|
|
35
|
+
<input type="hidden" name="type" value="${structure.type}" />
|
|
35
36
|
<input type="hidden" name="id" value="${data.id}" />
|
|
36
37
|
<input type="hidden" name="entryId" value="${entryId}" />
|
|
37
38
|
<input type="hidden" name="parentId" value="${parentId}" />
|
|
38
|
-
<input type="hidden" name="sort_order" value="${sortOrder}" />
|
|
39
39
|
<input type="hidden" name="name" value="${name}" />
|
|
40
40
|
</form>
|
|
41
41
|
`
|