@brixter/brix-builder 0.0.1
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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/core.d.ts +103 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +758 -0
- package/dist/core.js.map +1 -0
- package/dist/editor/BuilderApp.svelte +1299 -0
- package/dist/editor/BuilderFieldEditor.svelte +274 -0
- package/dist/editor/BuilderInspector.svelte +123 -0
- package/dist/editor/BuilderPreviewFrame.svelte +661 -0
- package/dist/editor/ComponentPreviewThumbnail.svelte +197 -0
- package/dist/editor/PageFlowSidebar.svelte +198 -0
- package/dist/editor/PreviewBlockInserter.svelte +35 -0
- package/dist/editor/PreviewIconEditor.svelte +213 -0
- package/dist/editor/PreviewImageEditor.svelte +221 -0
- package/dist/editor/PreviewTextEditor.svelte +246 -0
- package/dist/editor/RichTextEditor.svelte +234 -0
- package/dist/editor/contracts.d.ts +57 -0
- package/dist/editor/contracts.d.ts.map +1 -0
- package/dist/editor/contracts.js +2 -0
- package/dist/editor/contracts.js.map +1 -0
- package/dist/editor/index.d.ts +3 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +2 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/shortcuts.d.ts +28 -0
- package/dist/editor/shortcuts.d.ts.map +1 -0
- package/dist/editor/shortcuts.js +28 -0
- package/dist/editor/shortcuts.js.map +1 -0
- package/dist/editor-controller.d.ts +50 -0
- package/dist/editor-controller.d.ts.map +1 -0
- package/dist/editor-controller.js +157 -0
- package/dist/editor-controller.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/preview/field-edit-debug.d.ts +5 -0
- package/dist/preview/field-edit-debug.d.ts.map +1 -0
- package/dist/preview/field-edit-debug.js +36 -0
- package/dist/preview/field-edit-debug.js.map +1 -0
- package/dist/preview/interactive-content.d.ts +8 -0
- package/dist/preview/interactive-content.d.ts.map +1 -0
- package/dist/preview/interactive-content.js +62 -0
- package/dist/preview/interactive-content.js.map +1 -0
- package/dist/preview-dom.d.ts +67 -0
- package/dist/preview-dom.d.ts.map +1 -0
- package/dist/preview-dom.js +191 -0
- package/dist/preview-dom.js.map +1 -0
- package/dist/svelte/SveltePreviewRenderer.svelte +490 -0
- package/dist/svelte/adapter.d.ts +7 -0
- package/dist/svelte/adapter.d.ts.map +1 -0
- package/dist/svelte/adapter.js +66 -0
- package/dist/svelte/adapter.js.map +1 -0
- package/dist/svelte/index.d.ts +3 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +3 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/svelte/markup-schema.d.ts +5 -0
- package/dist/svelte/markup-schema.d.ts.map +1 -0
- package/dist/svelte/markup-schema.js +177 -0
- package/dist/svelte/markup-schema.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import BuilderFieldEditor from './BuilderFieldEditor.svelte';
|
|
3
|
+
import RichTextEditor from './RichTextEditor.svelte';
|
|
4
|
+
import {
|
|
5
|
+
inferBuilderFieldKind,
|
|
6
|
+
isRichTextValue,
|
|
7
|
+
type BuilderField,
|
|
8
|
+
type BuilderRichTextValue
|
|
9
|
+
} from '../core.js';
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
fieldKey,
|
|
13
|
+
field,
|
|
14
|
+
path,
|
|
15
|
+
value,
|
|
16
|
+
onChange,
|
|
17
|
+
onQueueFileEdit,
|
|
18
|
+
onAddItem,
|
|
19
|
+
onRemoveItem,
|
|
20
|
+
onMoveItem
|
|
21
|
+
}: {
|
|
22
|
+
fieldKey: string;
|
|
23
|
+
field: BuilderField;
|
|
24
|
+
path: string;
|
|
25
|
+
value: unknown;
|
|
26
|
+
onChange: (path: string, value: unknown) => void;
|
|
27
|
+
onQueueFileEdit: (path: string) => void;
|
|
28
|
+
onAddItem: (path: string) => void;
|
|
29
|
+
onRemoveItem: (path: string, index: number) => void;
|
|
30
|
+
onMoveItem: (path: string, index: number, direction: -1 | 1) => void;
|
|
31
|
+
} = $props();
|
|
32
|
+
|
|
33
|
+
const fieldKind = $derived(inferBuilderFieldKind(field));
|
|
34
|
+
const label = $derived(field.label ?? humanizeFieldKey(fieldKey));
|
|
35
|
+
const objectEntries = $derived(Object.entries(field.fields ?? {}));
|
|
36
|
+
const arrayItems = $derived(Array.isArray(value) ? value : []);
|
|
37
|
+
const stringValue = $derived(typeof value === 'string' ? value : '');
|
|
38
|
+
const numberValue = $derived(typeof value === 'number' ? value : 0);
|
|
39
|
+
const booleanValue = $derived(value === true);
|
|
40
|
+
const richTextValue = $derived(
|
|
41
|
+
isRichTextValue(value)
|
|
42
|
+
? value
|
|
43
|
+
: ({
|
|
44
|
+
kind: 'richtext',
|
|
45
|
+
mode: fieldKind === 'richtext-inline' ? 'inline' : 'block',
|
|
46
|
+
html: '',
|
|
47
|
+
json: null
|
|
48
|
+
} satisfies BuilderRichTextValue)
|
|
49
|
+
);
|
|
50
|
+
const multiline = $derived(
|
|
51
|
+
stringValue.includes('\n') ||
|
|
52
|
+
stringValue.length > 120 ||
|
|
53
|
+
/quote|subtitle|description|content/i.test(label)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
function updateText(nextValue: string): void {
|
|
57
|
+
onChange(path, nextValue);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function updateNumber(nextValue: string): void {
|
|
61
|
+
const parsedValue = Number(nextValue);
|
|
62
|
+
onChange(path, Number.isFinite(parsedValue) ? parsedValue : 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function updateBoolean(nextValue: boolean): void {
|
|
66
|
+
onChange(path, nextValue);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getObjectValue(source: unknown, key: string): unknown {
|
|
70
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (source as Record<string, unknown>)[key];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getItemLabel(item: unknown, index: number): string {
|
|
78
|
+
if (
|
|
79
|
+
field.summaryField &&
|
|
80
|
+
item &&
|
|
81
|
+
typeof item === 'object' &&
|
|
82
|
+
!Array.isArray(item) &&
|
|
83
|
+
typeof (item as Record<string, unknown>)[field.summaryField] === 'string'
|
|
84
|
+
) {
|
|
85
|
+
return (item as Record<string, string>)[field.summaryField];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return `${field.itemLabel ?? 'Elemento'} ${index + 1}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function humanizeFieldKey(value: string): string {
|
|
92
|
+
return value
|
|
93
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
94
|
+
.replace(/[-_]/g, ' ')
|
|
95
|
+
.replace(/\s+/g, ' ')
|
|
96
|
+
.trim()
|
|
97
|
+
.replace(/^\w/, (match) => match.toUpperCase());
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
{#if fieldKind === 'object' && field.fields}
|
|
102
|
+
<div class="space-y-3 border-t border-gray-200 pt-4 dark:border-gray-700">
|
|
103
|
+
<p class="text-heading text-sm font-semibold">{label}</p>
|
|
104
|
+
|
|
105
|
+
{#each objectEntries as [nestedKey, nestedField] (nestedKey)}
|
|
106
|
+
<BuilderFieldEditor
|
|
107
|
+
fieldKey={nestedKey}
|
|
108
|
+
field={nestedField}
|
|
109
|
+
path={`${path}.${nestedKey}`}
|
|
110
|
+
value={getObjectValue(value, nestedKey)}
|
|
111
|
+
{onChange}
|
|
112
|
+
{onQueueFileEdit}
|
|
113
|
+
{onAddItem}
|
|
114
|
+
{onRemoveItem}
|
|
115
|
+
{onMoveItem} />
|
|
116
|
+
{/each}
|
|
117
|
+
</div>
|
|
118
|
+
{:else if fieldKind === 'array' && field.item}
|
|
119
|
+
<div class="space-y-3 border-t border-gray-200 pt-4 dark:border-gray-700">
|
|
120
|
+
<div class="flex items-center justify-between gap-3">
|
|
121
|
+
<p class="text-heading text-sm font-semibold">{label}</p>
|
|
122
|
+
<button
|
|
123
|
+
type="button"
|
|
124
|
+
class="border border-gray-300 bg-white px-2 py-1 text-xs font-medium text-gray-900 transition-colors hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700"
|
|
125
|
+
onclick={() => onAddItem(path)}>
|
|
126
|
+
+ Add {field.itemLabel ?? 'item'}
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
{#if arrayItems.length === 0}
|
|
131
|
+
<p class="text-muted text-sm">No items configured.</p>
|
|
132
|
+
{/if}
|
|
133
|
+
|
|
134
|
+
{#each arrayItems as item, index (`${path}-${index}`)}
|
|
135
|
+
<div class="space-y-3 border border-gray-200 bg-white p-3 dark:border-gray-700 dark:bg-gray-800">
|
|
136
|
+
<div class="flex items-center justify-between gap-3">
|
|
137
|
+
<p class="text-heading text-sm font-medium">{getItemLabel(item, index)}</p>
|
|
138
|
+
<div class="flex flex-wrap gap-2">
|
|
139
|
+
<button
|
|
140
|
+
type="button"
|
|
141
|
+
class="border border-gray-300 px-2 py-1 text-xs transition-colors hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-700"
|
|
142
|
+
onclick={() => onMoveItem(path, index, -1)}>
|
|
143
|
+
Up
|
|
144
|
+
</button>
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
class="border border-gray-300 px-2 py-1 text-xs transition-colors hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-700"
|
|
148
|
+
onclick={() => onMoveItem(path, index, 1)}>
|
|
149
|
+
Down
|
|
150
|
+
</button>
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
class="border border-red-300 px-2 py-1 text-xs text-red-600 transition-colors hover:bg-red-50 dark:border-red-800 dark:text-red-400 dark:hover:bg-red-950"
|
|
154
|
+
onclick={() => onRemoveItem(path, index)}>
|
|
155
|
+
Remove
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{#if field.item.fields}
|
|
161
|
+
{#each Object.entries(field.item.fields) as [nestedKey, nestedField] (nestedKey)}
|
|
162
|
+
<BuilderFieldEditor
|
|
163
|
+
fieldKey={nestedKey}
|
|
164
|
+
field={nestedField}
|
|
165
|
+
path={`${path}[${index}].${nestedKey}`}
|
|
166
|
+
value={getObjectValue(item, nestedKey)}
|
|
167
|
+
{onChange}
|
|
168
|
+
{onQueueFileEdit}
|
|
169
|
+
{onAddItem}
|
|
170
|
+
{onRemoveItem}
|
|
171
|
+
{onMoveItem} />
|
|
172
|
+
{/each}
|
|
173
|
+
{:else}
|
|
174
|
+
<BuilderFieldEditor
|
|
175
|
+
fieldKey={`${fieldKey}-${index}`}
|
|
176
|
+
field={field.item}
|
|
177
|
+
path={`${path}[${index}]`}
|
|
178
|
+
value={item}
|
|
179
|
+
{onChange}
|
|
180
|
+
{onQueueFileEdit}
|
|
181
|
+
{onAddItem}
|
|
182
|
+
{onRemoveItem}
|
|
183
|
+
{onMoveItem} />
|
|
184
|
+
{/if}
|
|
185
|
+
</div>
|
|
186
|
+
{/each}
|
|
187
|
+
</div>
|
|
188
|
+
{:else if fieldKind === 'richtext-inline' || fieldKind === 'richtext-block'}
|
|
189
|
+
<label class="block">
|
|
190
|
+
<span class="mb-1 block text-sm font-medium text-label">{label}</span>
|
|
191
|
+
<RichTextEditor
|
|
192
|
+
value={richTextValue}
|
|
193
|
+
mode={fieldKind === 'richtext-inline' ? 'inline' : 'block'}
|
|
194
|
+
onChange={(nextValue) => onChange(path, nextValue)} />
|
|
195
|
+
</label>
|
|
196
|
+
{:else if fieldKind === 'boolean'}
|
|
197
|
+
<label class="flex items-center gap-3 border border-gray-200 bg-gray-50 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800">
|
|
198
|
+
<input
|
|
199
|
+
type="checkbox"
|
|
200
|
+
checked={booleanValue}
|
|
201
|
+
class="h-4 w-4 rounded border-gray-300 dark:border-gray-600"
|
|
202
|
+
onchange={(event) => updateBoolean(event.currentTarget.checked)} />
|
|
203
|
+
<span class="text-sm font-medium text-label">{label}</span>
|
|
204
|
+
</label>
|
|
205
|
+
{:else if fieldKind === 'number'}
|
|
206
|
+
<label class="block">
|
|
207
|
+
<span class="mb-1 block text-sm font-medium text-label">{label}</span>
|
|
208
|
+
<input
|
|
209
|
+
type="number"
|
|
210
|
+
value={numberValue}
|
|
211
|
+
class="block w-full border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:outline-none focus:ring-1 focus:ring-[#FDE047] dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
212
|
+
oninput={(event) => updateNumber(event.currentTarget.value)} />
|
|
213
|
+
</label>
|
|
214
|
+
{:else if fieldKind === 'image'}
|
|
215
|
+
<label class="block">
|
|
216
|
+
<span class="mb-1 block text-sm font-medium text-label">{label}</span>
|
|
217
|
+
{#if stringValue}
|
|
218
|
+
<img
|
|
219
|
+
src={stringValue}
|
|
220
|
+
alt={label}
|
|
221
|
+
class="mb-2 h-24 w-24 border border-gray-200 object-cover dark:border-gray-700" />
|
|
222
|
+
{/if}
|
|
223
|
+
<div class="flex flex-wrap gap-2">
|
|
224
|
+
<input
|
|
225
|
+
type="text"
|
|
226
|
+
value={stringValue}
|
|
227
|
+
class="min-w-0 flex-1 border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:outline-none focus:ring-1 focus:ring-[#FDE047] dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
228
|
+
oninput={(event) => updateText(event.currentTarget.value)} />
|
|
229
|
+
<button
|
|
230
|
+
type="button"
|
|
231
|
+
class="border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-900 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700"
|
|
232
|
+
onclick={() => onQueueFileEdit(path)}>
|
|
233
|
+
File
|
|
234
|
+
</button>
|
|
235
|
+
</div>
|
|
236
|
+
</label>
|
|
237
|
+
{:else if fieldKind === 'icon'}
|
|
238
|
+
<label class="block">
|
|
239
|
+
<span class="mb-1 block text-sm font-medium text-label">{label}</span>
|
|
240
|
+
<div class="flex flex-wrap gap-2">
|
|
241
|
+
<div class="min-w-0 flex-1 flex items-center justify-center h-[46px] border border-gray-300 bg-white px-4 text-sm text-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100">
|
|
242
|
+
{#if stringValue}
|
|
243
|
+
<span class="inline-block h-6 w-6 text-gray-700 dark:text-gray-300">
|
|
244
|
+
{@html stringValue}
|
|
245
|
+
</span>
|
|
246
|
+
{:else}
|
|
247
|
+
<span class="text-gray-400 dark:text-gray-500 text-xs">No icon</span>
|
|
248
|
+
{/if}
|
|
249
|
+
</div>
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
class="border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-900 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700"
|
|
253
|
+
onclick={() => onQueueFileEdit(path)}>
|
|
254
|
+
Choose
|
|
255
|
+
</button>
|
|
256
|
+
</div>
|
|
257
|
+
</label>
|
|
258
|
+
{:else}
|
|
259
|
+
<label class="block">
|
|
260
|
+
<span class="mb-1 block text-sm font-medium text-label">{label}</span>
|
|
261
|
+
{#if multiline}
|
|
262
|
+
<textarea
|
|
263
|
+
value={stringValue}
|
|
264
|
+
class="min-h-24 w-full border border-gray-300 bg-white px-4 py-3 font-mono text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:outline-none focus:ring-1 focus:ring-[#FDE047] dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
265
|
+
oninput={(event) => updateText(event.currentTarget.value)}></textarea>
|
|
266
|
+
{:else}
|
|
267
|
+
<input
|
|
268
|
+
type="text"
|
|
269
|
+
value={stringValue}
|
|
270
|
+
class="block w-full border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:outline-none focus:ring-1 focus:ring-[#FDE047] dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
271
|
+
oninput={(event) => updateText(event.currentTarget.value)} />
|
|
272
|
+
{/if}
|
|
273
|
+
</label>
|
|
274
|
+
{/if}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getCollectionItemSummary, type BuilderBlock, type BuilderFields } from '../core.js';
|
|
3
|
+
import type { BuilderRenderDefinition } from './contracts.js';
|
|
4
|
+
import BuilderFieldEditor from './BuilderFieldEditor.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
title,
|
|
8
|
+
description,
|
|
9
|
+
activeBlock,
|
|
10
|
+
activeDefinition,
|
|
11
|
+
inspectorFields,
|
|
12
|
+
propsError,
|
|
13
|
+
mdsvexOutput,
|
|
14
|
+
copied,
|
|
15
|
+
onTitleChange,
|
|
16
|
+
onDescriptionChange,
|
|
17
|
+
onFieldChange,
|
|
18
|
+
onQueueFileEdit,
|
|
19
|
+
onAddItem,
|
|
20
|
+
onRemoveItem,
|
|
21
|
+
onMoveItem,
|
|
22
|
+
onCopyMdsvex,
|
|
23
|
+
onDeselectBlock
|
|
24
|
+
}: {
|
|
25
|
+
title: string;
|
|
26
|
+
description: string;
|
|
27
|
+
activeBlock: BuilderBlock | null;
|
|
28
|
+
activeDefinition: BuilderRenderDefinition | null;
|
|
29
|
+
inspectorFields: BuilderFields;
|
|
30
|
+
propsError: string | null;
|
|
31
|
+
mdsvexOutput: string;
|
|
32
|
+
copied: boolean;
|
|
33
|
+
onTitleChange: (value: string) => void;
|
|
34
|
+
onDescriptionChange: (value: string) => void;
|
|
35
|
+
onFieldChange: (block: BuilderBlock, path: string, value: unknown) => void;
|
|
36
|
+
onQueueFileEdit: (blockId: string, path: string) => void;
|
|
37
|
+
onAddItem: (block: BuilderBlock, path: string) => void;
|
|
38
|
+
onRemoveItem: (block: BuilderBlock, path: string, index: number) => void;
|
|
39
|
+
onMoveItem: (block: BuilderBlock, path: string, index: number, direction: -1 | 1) => void;
|
|
40
|
+
onCopyMdsvex: () => void;
|
|
41
|
+
onDeselectBlock: () => void;
|
|
42
|
+
} = $props();
|
|
43
|
+
|
|
44
|
+
const inspectorEntries = $derived(Object.entries(inspectorFields));
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<aside
|
|
48
|
+
class="flex h-full min-h-0 w-full flex-col border-l border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900"
|
|
49
|
+
onclick={(event) => {
|
|
50
|
+
if (!(event.target as Element).closest('section')) onDeselectBlock();
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<div class="min-h-0 flex-1 overflow-y-auto">
|
|
54
|
+
{#if activeBlock && activeDefinition}
|
|
55
|
+
<section class="p-4 dark:border-gray-700">
|
|
56
|
+
<div class="mb-4">
|
|
57
|
+
<p class="text-muted text-[11px] font-semibold tracking-wide uppercase">Brik</p>
|
|
58
|
+
<h3 class="text-heading mt-1 text-sm font-semibold">{activeDefinition.type}</h3>
|
|
59
|
+
<p class="text-muted mt-1 text-xs leading-5">{activeDefinition.description}</p>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{#if activeDefinition.mode === 'markdown'}
|
|
63
|
+
<label class="block">
|
|
64
|
+
<span class="text-label mb-1 block text-sm font-medium">Contenuto markdown</span>
|
|
65
|
+
<textarea
|
|
66
|
+
value={typeof activeBlock.props.content === 'string' ? activeBlock.props.content : ''}
|
|
67
|
+
class="min-h-48 w-full border border-gray-300 bg-white px-4 py-3 font-mono text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:ring-1 focus:ring-[#FDE047] focus:outline-none dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
68
|
+
oninput={(event) => onFieldChange(activeBlock, 'content', event.currentTarget.value)}
|
|
69
|
+
placeholder="Scrivi markdown..."
|
|
70
|
+
></textarea>
|
|
71
|
+
</label>
|
|
72
|
+
{:else if inspectorEntries.length > 0}
|
|
73
|
+
<div class="space-y-4">
|
|
74
|
+
{#each inspectorEntries as [fieldKey, fieldDefinition] (fieldKey)}
|
|
75
|
+
<BuilderFieldEditor
|
|
76
|
+
{fieldKey}
|
|
77
|
+
field={fieldDefinition}
|
|
78
|
+
path={fieldKey}
|
|
79
|
+
value={activeBlock.props[fieldKey]}
|
|
80
|
+
onChange={(path, value) => onFieldChange(activeBlock, path, value)}
|
|
81
|
+
onQueueFileEdit={(path) => onQueueFileEdit(activeBlock.id, path)}
|
|
82
|
+
onAddItem={(path) => onAddItem(activeBlock, path)}
|
|
83
|
+
onRemoveItem={(path, itemIndex) => onRemoveItem(activeBlock, path, itemIndex)}
|
|
84
|
+
onMoveItem={(path, itemIndex, direction) =>
|
|
85
|
+
onMoveItem(activeBlock, path, itemIndex, direction)}
|
|
86
|
+
/>
|
|
87
|
+
{/each}
|
|
88
|
+
</div>
|
|
89
|
+
{:else}
|
|
90
|
+
<p class="text-muted text-sm">Nothing to edit here!</p>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
93
|
+
{#if propsError}
|
|
94
|
+
<p class="text-error mt-3 text-sm">{propsError}</p>
|
|
95
|
+
{/if}
|
|
96
|
+
</section>
|
|
97
|
+
{:else}
|
|
98
|
+
<section class="p-4 dark:border-gray-700">
|
|
99
|
+
<h3 class="text-muted mb-4 text-[11px] font-semibold tracking-wide uppercase">Page</h3>
|
|
100
|
+
<div class="space-y-4">
|
|
101
|
+
<label class="block">
|
|
102
|
+
<span class="text-label mb-1 block text-sm font-medium">Page Title</span>
|
|
103
|
+
<input
|
|
104
|
+
value={title}
|
|
105
|
+
class="block w-full border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:ring-1 focus:ring-[#FDE047] focus:outline-none dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
106
|
+
placeholder="Page Title"
|
|
107
|
+
oninput={(event) => onTitleChange(event.currentTarget.value)}
|
|
108
|
+
/>
|
|
109
|
+
</label>
|
|
110
|
+
<label class="block">
|
|
111
|
+
<span class="text-label mb-1 block text-sm font-medium">Description</span>
|
|
112
|
+
<input
|
|
113
|
+
value={description}
|
|
114
|
+
class="block w-full border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 focus:border-[#FDE047] focus:ring-1 focus:ring-[#FDE047] focus:outline-none dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500 dark:focus:border-[#FACC15] dark:focus:ring-[#FACC15]"
|
|
115
|
+
placeholder="Description for the frontmatter"
|
|
116
|
+
oninput={(event) => onDescriptionChange(event.currentTarget.value)}
|
|
117
|
+
/>
|
|
118
|
+
</label>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
{/if}
|
|
122
|
+
</div>
|
|
123
|
+
</aside>
|