@alstar/studio 0.0.0-beta.6 → 0.0.0-beta.8
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/api-key.ts +1 -2
- package/api/backup.ts +38 -0
- package/api/block.ts +63 -15
- package/api/index.ts +6 -3
- package/components/AdminPanel.ts +46 -24
- package/components/Backup.ts +10 -0
- 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/Settings.ts +3 -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/types.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { type HttpBindings } from '@hono/node-server'
|
|
|
2
2
|
import { type Context } from 'hono'
|
|
3
3
|
import { type HonoOptions } from 'hono/hono-base'
|
|
4
4
|
import { type BlankInput, type BlankEnv } from 'hono/types'
|
|
5
|
+
import {
|
|
6
|
+
BlockFieldInstance,
|
|
7
|
+
BlockInstance,
|
|
8
|
+
FieldInstance,
|
|
9
|
+
} from './utils/define.ts'
|
|
5
10
|
|
|
6
11
|
export type PrimitiveField = {
|
|
7
12
|
name: string
|
|
@@ -25,71 +30,103 @@ export type Block = {
|
|
|
25
30
|
fields: Record<string, Field | Block>
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
export type Structure = Record<string,
|
|
33
|
+
export type Structure = Record<string, BlockDefStructure>
|
|
29
34
|
|
|
30
35
|
// --- Field & block definitions ---
|
|
31
|
-
type FieldType = 'text' | 'slug' | 'markdown' | '
|
|
36
|
+
type FieldType = 'text' | 'slug' | 'markdown' | 'image'
|
|
32
37
|
|
|
33
38
|
interface BaseField {
|
|
34
|
-
label: string
|
|
35
|
-
type: FieldType
|
|
39
|
+
label: string
|
|
40
|
+
type: FieldType
|
|
36
41
|
description?: string
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
interface TextField extends BaseField {
|
|
40
|
-
type: 'text' | 'slug' | 'markdown'
|
|
45
|
+
type: 'text' | 'slug' | 'markdown'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface TextFieldStructure extends TextField {
|
|
49
|
+
instanceOf: typeof FieldInstance
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
interface ImageField extends BaseField {
|
|
44
|
-
type: 'image'
|
|
53
|
+
type: 'image'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface ImageFieldStructure extends ImageField {
|
|
57
|
+
instanceOf: typeof FieldInstance
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface BlocksFieldDef {
|
|
61
|
+
label: string
|
|
62
|
+
type: 'blocks'
|
|
63
|
+
description?: string
|
|
64
|
+
children: Record<string, BlockDefStructure | FieldDefStructure>
|
|
45
65
|
}
|
|
46
66
|
|
|
47
|
-
export interface
|
|
48
|
-
|
|
49
|
-
children: Record<string, BlockDef | FieldDef>;
|
|
67
|
+
export interface BlocksFieldDefStructure extends BlocksFieldDef {
|
|
68
|
+
instanceOf: typeof BlockFieldInstance
|
|
50
69
|
}
|
|
51
70
|
|
|
52
|
-
export type FieldDef = TextField | ImageField
|
|
71
|
+
export type FieldDef = TextField | ImageField
|
|
72
|
+
export type FieldDefStructure = TextFieldStructure | ImageFieldStructure
|
|
53
73
|
|
|
54
74
|
export interface BlockDef {
|
|
55
|
-
label: string
|
|
56
|
-
type: string
|
|
57
|
-
fields: Record<string,
|
|
75
|
+
label: string
|
|
76
|
+
type: string
|
|
77
|
+
fields: Record<string, FieldDefStructure | BlocksFieldDefStructure>
|
|
58
78
|
description?: string
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
created_at: string
|
|
64
|
-
updated_at: string
|
|
65
|
-
name: string
|
|
66
|
-
label: string
|
|
67
|
-
// type: string
|
|
68
|
-
sort_order: number
|
|
69
|
-
value: string
|
|
70
|
-
options: string | null
|
|
71
|
-
status: 'enabled' | 'disabled'
|
|
72
|
-
parent_id: number | null
|
|
73
|
-
depth: number
|
|
81
|
+
export interface BlockDefStructure extends BlockDef {
|
|
82
|
+
instanceOf: typeof BlockInstance
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
// type DBDefaults = {
|
|
86
|
+
// id: number
|
|
87
|
+
// created_at: string
|
|
88
|
+
// updated_at: string
|
|
89
|
+
// name: string
|
|
90
|
+
// label: string
|
|
91
|
+
// // type: string
|
|
92
|
+
// sort_order: number
|
|
93
|
+
// value: string
|
|
94
|
+
// options: string | null
|
|
95
|
+
// status: 'enabled' | 'disabled'
|
|
96
|
+
// parent_id: number | null
|
|
97
|
+
// depth: number
|
|
98
|
+
// }
|
|
99
|
+
|
|
100
|
+
type BaseDBResult = {
|
|
77
101
|
id: number
|
|
78
102
|
created_at: string
|
|
79
103
|
updated_at: string
|
|
80
104
|
name: string
|
|
81
105
|
label: string
|
|
82
|
-
type: string
|
|
83
106
|
sort_order: number
|
|
84
107
|
value: string | null
|
|
85
108
|
options: any
|
|
86
|
-
status:
|
|
109
|
+
status: 'enabled' | 'disabled'
|
|
87
110
|
parent_id: number | null
|
|
88
111
|
depth: number
|
|
89
|
-
children?: DBBlockResult[]
|
|
90
|
-
fields?: Record<string, DBBlockResult>
|
|
91
112
|
}
|
|
92
113
|
|
|
114
|
+
export type DBPrimitiveFieldResult = BaseDBResult & {
|
|
115
|
+
type: FieldDef
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type DBBlockFieldResult = BaseDBResult & {
|
|
119
|
+
type: 'blocks'
|
|
120
|
+
children: DBBlockResult[]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type DBBlockResult = BaseDBResult & {
|
|
124
|
+
type: string
|
|
125
|
+
fields: Record<string, DBFieldResult>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type DBFieldResult = DBPrimitiveFieldResult & DBBlockFieldResult
|
|
129
|
+
|
|
93
130
|
export type DBBlock = Block & {
|
|
94
131
|
id: number
|
|
95
132
|
created_at: string
|
|
@@ -103,8 +140,9 @@ export type DBBlock = Block & {
|
|
|
103
140
|
export type BlockStatus = 'enabled' | 'disabled'
|
|
104
141
|
|
|
105
142
|
export type StudioConfig = {
|
|
106
|
-
siteName
|
|
143
|
+
siteName?: string
|
|
107
144
|
honoConfig?: HonoOptions<BlankEnv>
|
|
145
|
+
port?: number
|
|
108
146
|
structure: Structure
|
|
109
147
|
}
|
|
110
148
|
|
package/utils/define.ts
CHANGED
|
@@ -3,27 +3,39 @@ import { type HtmlEscapedString } from './html.ts'
|
|
|
3
3
|
|
|
4
4
|
export const defineConfig = (config: types.StudioConfig) => config
|
|
5
5
|
|
|
6
|
-
// export const defineStructure = (structure: types.Block[]) => structure
|
|
7
|
-
// export const defineField = (field: types.Field) => field
|
|
8
|
-
// export const defineBlock = (block: types.Block) => block
|
|
9
|
-
|
|
10
6
|
export const defineEntry = (
|
|
11
7
|
fn: (
|
|
12
8
|
c: types.RequestContext,
|
|
13
9
|
) => HtmlEscapedString | Promise<HtmlEscapedString>,
|
|
14
10
|
) => fn
|
|
15
11
|
|
|
12
|
+
export const FieldInstance = Symbol('field')
|
|
13
|
+
export const BlockFieldInstance = Symbol('blockfield')
|
|
14
|
+
export const BlockInstance = Symbol('block')
|
|
15
|
+
|
|
16
16
|
// --- Identity helpers (preserve literal types) ---
|
|
17
|
-
export function defineField(field: types.FieldDef) {
|
|
18
|
-
return
|
|
17
|
+
export function defineField(field: types.FieldDef): types.FieldDefStructure {
|
|
18
|
+
return {
|
|
19
|
+
...field,
|
|
20
|
+
instanceOf: FieldInstance,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function defineBlockField(
|
|
25
|
+
field: types.BlocksFieldDef,
|
|
26
|
+
): types.BlocksFieldDefStructure {
|
|
27
|
+
return {
|
|
28
|
+
...field,
|
|
29
|
+
instanceOf: BlockFieldInstance,
|
|
30
|
+
}
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
export function defineBlock(block: types.BlockDef) {
|
|
22
|
-
return block
|
|
33
|
+
export function defineBlock(block: types.BlockDef): types.BlockDefStructure {
|
|
34
|
+
return { ...block, instanceOf: BlockInstance }
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
export function defineStructure(
|
|
26
|
-
structure: Record<string, types.
|
|
38
|
+
structure: Record<string, types.BlockDefStructure>,
|
|
27
39
|
) {
|
|
28
40
|
return structure
|
|
29
41
|
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { db } from '@alstar/db'
|
|
2
2
|
import { query } from '../queries/index.ts'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
BlockDefStructure,
|
|
5
|
+
BlocksFieldDefStructure,
|
|
6
|
+
FieldDefStructure,
|
|
7
|
+
} from '../types.ts'
|
|
8
|
+
|
|
9
|
+
export function getOrCreateRow(props: {
|
|
10
|
+
parentId: string | number | null
|
|
11
|
+
name: string
|
|
12
|
+
field: BlockDefStructure | BlocksFieldDefStructure | FieldDefStructure
|
|
13
|
+
sortOrder?: number
|
|
14
|
+
id?: number
|
|
15
|
+
}) {
|
|
16
|
+
const { parentId, name, field, sortOrder = 0, id } = props
|
|
17
|
+
|
|
18
|
+
if (id) {
|
|
19
|
+
return query.block({
|
|
20
|
+
id: id?.toString(),
|
|
21
|
+
})
|
|
22
|
+
}
|
|
4
23
|
|
|
5
|
-
export function getOrCreateRow(
|
|
6
|
-
parentId: string | number,
|
|
7
|
-
name: string,
|
|
8
|
-
field: Block | FieldDef,
|
|
9
|
-
sortOrder: number,
|
|
10
|
-
) {
|
|
11
24
|
const data = query.block({
|
|
12
25
|
parent_id: parentId?.toString() || null,
|
|
13
26
|
name: name,
|
package/utils/startup-log.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import packageJSON from '../package.json' with { type: 'json' }
|
|
2
2
|
|
|
3
|
-
export default () => {
|
|
3
|
+
export default ({ port }: { port: number }) => {
|
|
4
4
|
console.log('\x1b[32m%s\x1b[0m', '╭───────────────────────╮')
|
|
5
5
|
console.log('\x1b[32m%s\x1b[0m', '│ Alstar Studio │')
|
|
6
6
|
console.log('\x1b[32m%s\x1b[0m', `│ ${packageJSON.version}${' '.repeat(22 - packageJSON.version.length)}│`)
|
|
7
|
-
console.log('\x1b[32m%s\x1b[0m', `│ http://localhost:${
|
|
7
|
+
console.log('\x1b[32m%s\x1b[0m', `│ http://localhost:${port} │`)
|
|
8
8
|
console.log('\x1b[32m%s\x1b[0m', '╰───────────────────────╯')
|
|
9
9
|
}
|
package/bin/alstar.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
|
|
6
|
-
// process.removeAllListeners('warning')
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
-
|
|
10
|
-
// Dev-only auto watch (opt out with ALSTAR_NO_WATCH=true)
|
|
11
|
-
const isDev =
|
|
12
|
-
process.env.NODE_ENV !== 'production' &&
|
|
13
|
-
process.env.ALSTAR_NO_WATCH !== 'true'
|
|
14
|
-
const alreadyBootstrapped = process.env.ALSTAR_WATCH_BOOTSTRAPPED === '1'
|
|
15
|
-
|
|
16
|
-
if (isDev && !alreadyBootstrapped) {
|
|
17
|
-
// Start a watcher ON THIS FILE and mark the environment so the child won't re-spawn
|
|
18
|
-
const childProcess = spawn(
|
|
19
|
-
process.execPath,
|
|
20
|
-
['--watch', __filename, ...process.argv.slice(2)],
|
|
21
|
-
// [__filename, ...process.argv.slice(2)],
|
|
22
|
-
{
|
|
23
|
-
stdio: 'inherit',
|
|
24
|
-
env: { ...process.env, ALSTAR_WATCH_BOOTSTRAPPED: '1' },
|
|
25
|
-
},
|
|
26
|
-
)
|
|
27
|
-
// Important: exit this launcher so only the watched child keeps running
|
|
28
|
-
process.on('SIGINT', () => {
|
|
29
|
-
childProcess.kill('SIGINT')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
process.on('SIGTERM', () => {
|
|
33
|
-
childProcess.kill('SIGTERM')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
process.exit(0)
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// --- Your actual CLI logic below ---
|
|
41
|
-
// console.log(import.meta.dirname, path.resolve('.'))
|
|
42
|
-
// import("./main.js").then(m => m.default());
|
package/components/Block.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { html } from 'hono/html'
|
|
2
|
-
import type { Block, BlockDef, FieldDef } from '../types.ts'
|
|
3
|
-
import { type HtmlEscapedString } from 'hono/utils/html'
|
|
4
|
-
import { Field } from './fields/index.ts'
|
|
5
|
-
|
|
6
|
-
export default (props: {
|
|
7
|
-
entryId: number
|
|
8
|
-
parentId: number
|
|
9
|
-
blockStructure: BlockDef | Block | FieldDef
|
|
10
|
-
name: string
|
|
11
|
-
sortOrder?: number
|
|
12
|
-
}): HtmlEscapedString | Promise<HtmlEscapedString> => {
|
|
13
|
-
const { entryId, parentId, blockStructure, name, sortOrder = 0 } = props
|
|
14
|
-
|
|
15
|
-
if (!blockStructure) return html`<p>No block</p>`
|
|
16
|
-
|
|
17
|
-
let entries: [string, BlockDef | Block | FieldDef][] = []
|
|
18
|
-
|
|
19
|
-
const fieldTypes = ['text', 'image', 'markdown', 'slug']
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
if (fieldTypes.includes(blockStructure.type)) {
|
|
23
|
-
entries = [[name, blockStructure]]
|
|
24
|
-
} else if (blockStructure.type === 'blocks') {
|
|
25
|
-
entries = Object.entries(blockStructure.children)
|
|
26
|
-
} else if (blockStructure.fields) {
|
|
27
|
-
entries = Object.entries(blockStructure.fields)
|
|
28
|
-
} else {
|
|
29
|
-
console.log(blockStructure)
|
|
30
|
-
}
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.log(error)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return html`
|
|
36
|
-
${entries.map(([name, field]) => {
|
|
37
|
-
switch (field.type) {
|
|
38
|
-
case 'text': {
|
|
39
|
-
return Field.Text({ entryId, parentId, name, field, sortOrder })
|
|
40
|
-
}
|
|
41
|
-
case 'slug': {
|
|
42
|
-
return Field.Text({ entryId, parentId, name, field, sortOrder })
|
|
43
|
-
}
|
|
44
|
-
case 'markdown': {
|
|
45
|
-
return Field.Text({ entryId, parentId, name, field, sortOrder })
|
|
46
|
-
}
|
|
47
|
-
case 'image': {
|
|
48
|
-
return Field.Text({ entryId, parentId, name, field, sortOrder })
|
|
49
|
-
}
|
|
50
|
-
case 'blocks': {
|
|
51
|
-
return Field.Blocks({ entryId, parentId, name, field, sortOrder })
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
})}`
|
|
55
|
-
}
|
package/public/main.css
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/* @import './../node_modules/@alstar/ui/red.css'; */
|
|
2
|
-
@import 'https://esm.sh/@alstar/ui/red.css';
|
|
3
|
-
@import './admin-panel.css';
|
|
4
|
-
@import './entry.css';
|
|
5
|
-
@import './blocks.css';
|
|
6
|
-
@import './settings.css';
|
|
7
|
-
|
|
8
|
-
body {
|
|
9
|
-
min-height: 100vh;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
body {
|
|
13
|
-
padding: 0;
|
|
14
|
-
min-height: inherit;
|
|
15
|
-
|
|
16
|
-
display: grid;
|
|
17
|
-
grid-template-columns: auto 1fr;
|
|
18
|
-
align-content: baseline;
|
|
19
|
-
|
|
20
|
-
> main {
|
|
21
|
-
padding: 40px;
|
|
22
|
-
height: 100vh;
|
|
23
|
-
overflow: auto;
|
|
24
|
-
|
|
25
|
-
section {
|
|
26
|
-
max-width: 900px;
|
|
27
|
-
margin: 0 auto;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.entry > .fields > .block {
|
|
33
|
-
padding-block: var(--pico-spacing);
|
|
34
|
-
|
|
35
|
-
> header {
|
|
36
|
-
margin-block: var(--pico-block-spacing-vertical);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.block {
|
|
41
|
-
> header {
|
|
42
|
-
margin-bottom: var(--pico-spacing);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
header {
|
|
46
|
-
display: flex;
|
|
47
|
-
justify-content: space-between;
|
|
48
|
-
align-items: center;
|
|
49
|
-
|
|
50
|
-
background-color: inherit;
|
|
51
|
-
|
|
52
|
-
h6,
|
|
53
|
-
h5,
|
|
54
|
-
fieldset {
|
|
55
|
-
margin-bottom: 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
button {
|
|
59
|
-
padding: 0;
|
|
60
|
-
background-color: white;
|
|
61
|
-
|
|
62
|
-
svg {
|
|
63
|
-
padding: 8px;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
[data-sortable] {
|
|
70
|
-
> article {
|
|
71
|
-
transition: opacity 200ms;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.sortable-ghost {
|
|
75
|
-
opacity: 0.3;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/* .sortable-chosen {
|
|
79
|
-
filter: brightness(0.9)
|
|
80
|
-
} */
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
div.ink-mde {
|
|
84
|
-
border: var(--pico-border-width) solid #2a3140;
|
|
85
|
-
border-radius: var(--pico-border-radius);
|
|
86
|
-
outline: 0;
|
|
87
|
-
background-color: var(--pico-background-color);
|
|
88
|
-
box-shadow: var(--pico-box-shadow);
|
|
89
|
-
color: var(--pico-color);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.ink-mde-textarea {
|
|
93
|
-
width: 100%;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.disclamer {
|
|
97
|
-
display: flex;
|
|
98
|
-
justify-content: center;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.text-secondary {
|
|
102
|
-
color: var(--pico-secondary);
|
|
103
|
-
}
|
package/public/main.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import barba from '@barba/core'
|
|
2
|
-
import Sortable from 'sortablejs'
|
|
3
|
-
// import { wrap } from 'ink-mde'
|
|
4
|
-
|
|
5
|
-
barba.init({
|
|
6
|
-
cacheIgnore: true,
|
|
7
|
-
views: [
|
|
8
|
-
{
|
|
9
|
-
namespace: 'default',
|
|
10
|
-
afterEnter() {
|
|
11
|
-
init()
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
],
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
function init() {
|
|
18
|
-
// sortable
|
|
19
|
-
{
|
|
20
|
-
const els = document.querySelectorAll('[data-sortable]')
|
|
21
|
-
|
|
22
|
-
els.forEach((el) => {
|
|
23
|
-
const id = el.dataset.sortable
|
|
24
|
-
|
|
25
|
-
var sortable = Sortable.create(el, {
|
|
26
|
-
delay: 0,
|
|
27
|
-
animation: 250,
|
|
28
|
-
dragClass: 'sortable-drag',
|
|
29
|
-
easing: 'ease-in-out',
|
|
30
|
-
handle: `[data-handle="${id}"]`,
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// // ink-mde
|
|
36
|
-
// {
|
|
37
|
-
// const el = document.querySelector('textarea')
|
|
38
|
-
|
|
39
|
-
// if (el) {
|
|
40
|
-
// wrap(el, {
|
|
41
|
-
// interface: {
|
|
42
|
-
// // appearance: InkValues.Appearance.Auto,
|
|
43
|
-
// attribution: false,
|
|
44
|
-
// },
|
|
45
|
-
// })
|
|
46
|
-
// }
|
|
47
|
-
// }
|
|
48
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|