@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/core.d.ts +103 -0
  4. package/dist/core.d.ts.map +1 -0
  5. package/dist/core.js +758 -0
  6. package/dist/core.js.map +1 -0
  7. package/dist/editor/BuilderApp.svelte +1299 -0
  8. package/dist/editor/BuilderFieldEditor.svelte +274 -0
  9. package/dist/editor/BuilderInspector.svelte +123 -0
  10. package/dist/editor/BuilderPreviewFrame.svelte +661 -0
  11. package/dist/editor/ComponentPreviewThumbnail.svelte +197 -0
  12. package/dist/editor/PageFlowSidebar.svelte +198 -0
  13. package/dist/editor/PreviewBlockInserter.svelte +35 -0
  14. package/dist/editor/PreviewIconEditor.svelte +213 -0
  15. package/dist/editor/PreviewImageEditor.svelte +221 -0
  16. package/dist/editor/PreviewTextEditor.svelte +246 -0
  17. package/dist/editor/RichTextEditor.svelte +234 -0
  18. package/dist/editor/contracts.d.ts +57 -0
  19. package/dist/editor/contracts.d.ts.map +1 -0
  20. package/dist/editor/contracts.js +2 -0
  21. package/dist/editor/contracts.js.map +1 -0
  22. package/dist/editor/index.d.ts +3 -0
  23. package/dist/editor/index.d.ts.map +1 -0
  24. package/dist/editor/index.js +2 -0
  25. package/dist/editor/index.js.map +1 -0
  26. package/dist/editor/shortcuts.d.ts +28 -0
  27. package/dist/editor/shortcuts.d.ts.map +1 -0
  28. package/dist/editor/shortcuts.js +28 -0
  29. package/dist/editor/shortcuts.js.map +1 -0
  30. package/dist/editor-controller.d.ts +50 -0
  31. package/dist/editor-controller.d.ts.map +1 -0
  32. package/dist/editor-controller.js +157 -0
  33. package/dist/editor-controller.js.map +1 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +6 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/preview/field-edit-debug.d.ts +5 -0
  39. package/dist/preview/field-edit-debug.d.ts.map +1 -0
  40. package/dist/preview/field-edit-debug.js +36 -0
  41. package/dist/preview/field-edit-debug.js.map +1 -0
  42. package/dist/preview/interactive-content.d.ts +8 -0
  43. package/dist/preview/interactive-content.d.ts.map +1 -0
  44. package/dist/preview/interactive-content.js +62 -0
  45. package/dist/preview/interactive-content.js.map +1 -0
  46. package/dist/preview-dom.d.ts +67 -0
  47. package/dist/preview-dom.d.ts.map +1 -0
  48. package/dist/preview-dom.js +191 -0
  49. package/dist/preview-dom.js.map +1 -0
  50. package/dist/svelte/SveltePreviewRenderer.svelte +490 -0
  51. package/dist/svelte/adapter.d.ts +7 -0
  52. package/dist/svelte/adapter.d.ts.map +1 -0
  53. package/dist/svelte/adapter.js +66 -0
  54. package/dist/svelte/adapter.js.map +1 -0
  55. package/dist/svelte/index.d.ts +3 -0
  56. package/dist/svelte/index.d.ts.map +1 -0
  57. package/dist/svelte/index.js +3 -0
  58. package/dist/svelte/index.js.map +1 -0
  59. package/dist/svelte/markup-schema.d.ts +5 -0
  60. package/dist/svelte/markup-schema.d.ts.map +1 -0
  61. package/dist/svelte/markup-schema.js +177 -0
  62. package/dist/svelte/markup-schema.js.map +1 -0
  63. package/package.json +56 -0
@@ -0,0 +1,490 @@
1
+ <script lang="ts">
2
+ import { untrack } from 'svelte';
3
+ import { getBuilderDefinition } from '../editor-controller.js';
4
+ import {
5
+ createBuilderFallbackProps,
6
+ normalizeBuilderPropsForRender,
7
+ getFieldByPath,
8
+ inferBuilderFieldKind
9
+ } from '../core.js';
10
+ import PreviewBlockInserter from '../editor/PreviewBlockInserter.svelte';
11
+ import { attachPreviewInteractionGuard } from '../preview/block-preview-interactions.js';
12
+ import type { BuilderAppPreviewProps } from '../editor/contracts.js';
13
+ import type { PreviewOverlay, PreviewCollectionOverlay } from '../preview-dom.js';
14
+ import type { BrikDefinition } from './adapter.js';
15
+
16
+ let {
17
+ definitions,
18
+ blocks,
19
+ propsErrors,
20
+ previewOverlays,
21
+ previewCollectionOverlays,
22
+ activeBlockId,
23
+ activeFieldEdit,
24
+ previewContainer,
25
+ onPreviewClick,
26
+ onPreviewKeydown,
27
+ onSelectBlock,
28
+ onDeselectBlock,
29
+ onAddBlockBefore,
30
+ onAddBlockAfter,
31
+ onAddItem,
32
+ onRemoveItem,
33
+ onMoveItem,
34
+ onOpenReorderModal,
35
+ onOpenInserterModal,
36
+ previewMode = false
37
+ }: BuilderAppPreviewProps & { definitions: BrikDefinition[] } = $props();
38
+
39
+ let hoveredCollectionItem = $state<string | null>(null);
40
+ let hoveredCollection = $state<string | null>(null);
41
+
42
+ let blockRenderSnapshotsCache: Record<string, Record<string, unknown>> = {};
43
+ let lastActiveBlockId: string | null = null;
44
+ let lastActivePath: string | null = null;
45
+
46
+ const blockRenderSnapshots = $derived.by(() => {
47
+ const edit = activeFieldEdit;
48
+ if (!edit) {
49
+ blockRenderSnapshotsCache = {};
50
+ lastActiveBlockId = null;
51
+ lastActivePath = null;
52
+ return {} as Record<string, Record<string, unknown>>;
53
+ }
54
+
55
+ if (lastActiveBlockId === edit.blockId && lastActivePath === edit.path) {
56
+ return blockRenderSnapshotsCache;
57
+ }
58
+
59
+ const block = blocks.find((entry) => entry.id === edit.blockId);
60
+ if (!block) {
61
+ return {} as Record<string, Record<string, unknown>>;
62
+ }
63
+
64
+ const definition = getBuilderDefinition(block.type, definitions);
65
+ const field = getFieldByPath(definition.fields, edit.path);
66
+ const kind = field ? inferBuilderFieldKind(field) : null;
67
+
68
+ if (kind === 'image' || kind === 'icon') {
69
+ blockRenderSnapshotsCache = {};
70
+ lastActiveBlockId = edit.blockId;
71
+ lastActivePath = edit.path;
72
+ return {} as Record<string, Record<string, unknown>>;
73
+ }
74
+
75
+ lastActiveBlockId = edit.blockId;
76
+ lastActivePath = edit.path;
77
+ blockRenderSnapshotsCache = untrack(() => {
78
+ return {
79
+ [edit.blockId]: normalizeBuilderPropsForRender(
80
+ createBuilderFallbackProps(definition, block.props)
81
+ ) as Record<string, unknown>
82
+ };
83
+ });
84
+
85
+ return blockRenderSnapshotsCache;
86
+ });
87
+
88
+ function getRenderProps(block: (typeof blocks)[number]): Record<string, unknown> {
89
+ const definition = getBuilderDefinition(block.type, definitions);
90
+ if (previewMode) {
91
+ return normalizeBuilderPropsForRender(block.props) as Record<string, unknown>;
92
+ }
93
+
94
+ const liveProps = normalizeBuilderPropsForRender(
95
+ createBuilderFallbackProps(definition, block.props)
96
+ ) as Record<string, unknown>;
97
+ if (activeFieldEdit?.blockId === block.id && blockRenderSnapshots[block.id]) {
98
+ return blockRenderSnapshots[block.id];
99
+ }
100
+
101
+ return liveProps;
102
+ }
103
+
104
+ function getCollectionItemKey(blockId: string, collectionPath: string, index: number): string {
105
+ return `${blockId}:${collectionPath}:${index}`;
106
+ }
107
+
108
+ function updateHoverStates(
109
+ blockId: string,
110
+ itemOverlays: PreviewOverlay[],
111
+ collectionOverlays: PreviewCollectionOverlay[],
112
+ event: MouseEvent
113
+ ): void {
114
+ if (
115
+ isElement(event.target) &&
116
+ event.target.closest('.collection-item-toolbar, .collection-add-button')
117
+ ) {
118
+ return;
119
+ }
120
+
121
+ const container = event.currentTarget || (isElement(event.target) ? event.target.closest('[data-builder-preview-block]') : null);
122
+ if (!isHTMLElement(container)) {
123
+ return;
124
+ }
125
+
126
+ // 1. Try DOM target matching first (100% accurate, no coordinate issues)
127
+ if (isElement(event.target)) {
128
+ const itemElement = event.target.closest('[data-builder-collection-item]');
129
+ if (itemElement) {
130
+ const collectionPath = itemElement.getAttribute('data-builder-collection-item');
131
+ if (collectionPath) {
132
+ const items = Array.from(container.querySelectorAll(`[data-builder-collection-item="${collectionPath}"]`));
133
+ const index = items.indexOf(itemElement);
134
+ if (index !== -1) {
135
+ hoveredCollectionItem = getCollectionItemKey(blockId, collectionPath, index);
136
+ hoveredCollection = `${blockId}:${collectionPath}`;
137
+ return;
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ // 2. Coordinate fallback (in case mouse is over padding or empty space of collection)
144
+ const containerRect = container.getBoundingClientRect();
145
+ const pointerX = event.clientX - containerRect.left;
146
+ const pointerY = event.clientY - containerRect.top;
147
+
148
+ const itemMatch = itemOverlays.find((overlay) => {
149
+ const top = Math.max(0, overlay.top + 36);
150
+ const bottom = top + overlay.height;
151
+ const left = overlay.left;
152
+ const right = left + overlay.width;
153
+ return pointerX >= left && pointerX <= right && pointerY >= top && pointerY <= bottom;
154
+ });
155
+
156
+ hoveredCollectionItem = itemMatch
157
+ ? getCollectionItemKey(blockId, itemMatch.collectionPath, itemMatch.index)
158
+ : null;
159
+
160
+ const collectionMatch = collectionOverlays.find((overlay) => {
161
+ const top = overlay.top;
162
+ const bottom = top + overlay.height + 45;
163
+ const left = overlay.left;
164
+ const right = left + overlay.width;
165
+ return pointerX >= left && pointerX <= right && pointerY >= top && pointerY <= bottom;
166
+ });
167
+
168
+ hoveredCollection = collectionMatch
169
+ ? `${blockId}:${collectionMatch.collectionPath}`
170
+ : null;
171
+ }
172
+
173
+ function isElement(value: unknown): value is Element {
174
+ return typeof value === 'object' && value !== null && (value as Node).nodeType === 1;
175
+ }
176
+
177
+ function isHTMLElement(value: unknown): value is HTMLElement {
178
+ return isElement(value);
179
+ }
180
+
181
+ function getEditingContext(
182
+ blockId: string,
183
+ rawProps: Record<string, unknown>,
184
+ hasPreviewBindings: boolean
185
+ ) {
186
+ return {
187
+ active: previewMode ? false : hasPreviewBindings,
188
+ focusPath: activeFieldEdit?.blockId === blockId ? activeFieldEdit.path : null,
189
+ caretOffset:
190
+ activeFieldEdit?.blockId === blockId ? (activeFieldEdit.caretOffset ?? null) : null,
191
+ previewProps: rawProps
192
+ };
193
+ }
194
+
195
+ $effect(() => attachPreviewInteractionGuard(document));
196
+ </script>
197
+
198
+ <div onclick={(event) => {
199
+ if (!(event.target as Element).closest('[data-builder-preview-block]')) {
200
+ onDeselectBlock();
201
+ }
202
+ }}>
203
+ {#each blocks as block, blockIndex (block.id)}
204
+ {@const definition = getBuilderDefinition(block.type, definitions)}
205
+ {#if !propsErrors[block.id]}
206
+ {@const BlockComponent = definition.component}
207
+ {@const renderProps = getRenderProps(block)}
208
+ {@const liveProps = normalizeBuilderPropsForRender(
209
+ createBuilderFallbackProps(definition, block.props)
210
+ ) as Record<string, unknown>}
211
+ {@const hasPreviewBindings = definition.previewBindings.length > 0}
212
+ {#if hasPreviewBindings}
213
+ <div
214
+ data-builder-preview-block={block.id}
215
+ use:previewContainer={{
216
+ block,
217
+ definition,
218
+ editing: getEditingContext(block.id, block.props, hasPreviewBindings)
219
+ }}
220
+ class="group relative scroll-mt-0.5 scroll-mb-0.5 transition"
221
+ class:cursor-pointer={!previewMode}
222
+ role={previewMode ? undefined : "button"}
223
+ tabindex={previewMode ? undefined : 0}
224
+ aria-label={previewMode ? undefined : `Modifica elementi del brik ${definition.type}`}
225
+ onclick={previewMode ? undefined : (event: MouseEvent) => onPreviewClick(block, event)}
226
+ onkeydown={previewMode ? undefined : (event: KeyboardEvent) => onPreviewKeydown(block, event)}
227
+ onmousemove={previewMode ? undefined : (event: MouseEvent) =>
228
+ updateHoverStates(
229
+ block.id,
230
+ previewOverlays[block.id] ?? [],
231
+ previewCollectionOverlays[block.id] ?? [],
232
+ event
233
+ )}
234
+ onmouseleave={previewMode ? undefined : () => {
235
+ hoveredCollectionItem = null;
236
+ hoveredCollection = null;
237
+ }}
238
+ >
239
+ {#if !previewMode}
240
+ <PreviewBlockInserter
241
+ placement="before"
242
+ edgeInset={blockIndex === 0}
243
+ onToggle={() => onOpenInserterModal(block.id, 'before')}
244
+ />
245
+ {/if}
246
+ <div data-builder-preview-content>
247
+ <BlockComponent {...renderProps} />
248
+ </div>
249
+ {#if activeBlockId === block.id && !previewMode}
250
+ <div
251
+ class="pointer-events-none absolute inset-px z-30 border-2 border-[#FDE047] dark:border-[#FACC15]"
252
+ ></div>
253
+ {/if}
254
+
255
+ {#if definition.collections.length > 0 && !previewMode}
256
+ <div class="pointer-events-none absolute inset-0">
257
+ {#each previewCollectionOverlays[block.id] ?? [] as overlay (overlay.collectionPath)}
258
+ <div
259
+ class="collection-overlay pointer-events-none absolute z-10"
260
+ style={`top:${overlay.top}px; left:${overlay.left}px; width:${overlay.width}px; height:${overlay.height}px;`}
261
+ >
262
+ <div
263
+ class="collection-outline absolute inset-0 outline outline-1 outline-[#FDE047] transition outline-dashed dark:outline-[#FACC15] {hoveredCollection === `${block.id}:${overlay.collectionPath}` ? 'opacity-100' : 'opacity-0'}"
264
+ ></div>
265
+ <button
266
+ type="button"
267
+ class="collection-add-button btn-brutal-icon pointer-events-auto absolute top-full left-1/2 flex h-7 w-7 -translate-x-1/2 translate-y-2 items-center justify-center text-lg leading-none transition {hoveredCollection === `${block.id}:${overlay.collectionPath}` ? 'opacity-100' : 'opacity-0'}"
268
+ aria-label={`Aggiungi ${overlay.label}`}
269
+ onclick={(event) => {
270
+ event.stopPropagation();
271
+ onAddItem(block, overlay.collectionPath);
272
+ }}
273
+ >
274
+ +
275
+ </button>
276
+ </div>
277
+ {/each}
278
+
279
+ {#each previewOverlays[block.id] ?? [] as overlay (`${overlay.collectionPath}-${overlay.index}`)}
280
+ <div
281
+ class="collection-item-overlay pointer-events-none absolute z-20"
282
+ style={`top:${Math.max(0, overlay.top + 36)}px; left:${overlay.left}px; width:${overlay.width}px; height:${overlay.height}px;`}
283
+ >
284
+ <div
285
+ class={hoveredCollectionItem ===
286
+ getCollectionItemKey(block.id, overlay.collectionPath, overlay.index)
287
+ ? 'collection-item-outline absolute inset-0 opacity-100 outline outline-1 outline-[#FDE047] transition dark:outline-[#FACC15]'
288
+ : 'collection-item-outline absolute inset-0 opacity-0 outline outline-1 outline-[#FDE047] transition dark:outline-[#FACC15]'}
289
+ ></div>
290
+ <div
291
+ class={hoveredCollectionItem ===
292
+ getCollectionItemKey(block.id, overlay.collectionPath, overlay.index)
293
+ ? 'collection-item-toolbar pointer-events-auto absolute top-0 left-0 flex h-8 -translate-y-full items-center overflow-hidden border border-gray-300 bg-white text-xs text-gray-900 opacity-100 shadow-[0_2px_8px_rgba(0,0,0,0.18)] transition dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100'
294
+ : 'collection-item-toolbar pointer-events-auto absolute top-0 left-0 flex h-8 -translate-y-full items-center overflow-hidden border border-gray-300 bg-white text-xs text-gray-900 opacity-0 shadow-[0_2px_8px_rgba(0,0,0,0.18)] transition dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100'}
295
+ >
296
+ <button
297
+ type="button"
298
+ class="h-full border-r border-gray-200 px-2.5 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:hover:bg-gray-700"
299
+ onclick={(event) => {
300
+ event.stopPropagation();
301
+ onMoveItem(block, overlay.collectionPath, overlay.index, -1);
302
+ }}
303
+ >
304
+
305
+ </button>
306
+ <button
307
+ type="button"
308
+ class="h-full border-r border-gray-200 px-2.5 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:hover:bg-gray-700"
309
+ onclick={(event) => {
310
+ event.stopPropagation();
311
+ onMoveItem(block, overlay.collectionPath, overlay.index, 1);
312
+ }}
313
+ >
314
+
315
+ </button>
316
+ <button
317
+ type="button"
318
+ class="h-full px-2.5 text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950"
319
+ onclick={(event) => {
320
+ event.stopPropagation();
321
+ onRemoveItem(block, overlay.collectionPath, overlay.index);
322
+ }}
323
+ >
324
+ ×
325
+ </button>
326
+ </div>
327
+ </div>
328
+ {/each}
329
+ </div>
330
+ {/if}
331
+
332
+ {#if !previewMode}
333
+ <PreviewBlockInserter
334
+ placement="after"
335
+ edgeInset={blockIndex === blocks.length - 1}
336
+ onToggle={() => onOpenInserterModal(block.id, 'after')}
337
+ />
338
+ {/if}
339
+ </div>
340
+ {:else}
341
+ <div
342
+ data-builder-preview-block={block.id}
343
+ use:previewContainer={{
344
+ block,
345
+ definition,
346
+ editing: getEditingContext(block.id, block.props, hasPreviewBindings)
347
+ }}
348
+ class="group relative scroll-mt-0.5 scroll-mb-0.5 transition"
349
+ class:cursor-pointer={!previewMode}
350
+ role={previewMode ? undefined : "button"}
351
+ tabindex={previewMode ? undefined : 0}
352
+ aria-label={previewMode ? undefined : `Seleziona brik ${definition.type}`}
353
+ onclick={previewMode ? undefined : () => onSelectBlock(block.id)}
354
+ onkeydown={previewMode ? undefined : (event: KeyboardEvent) => {
355
+ if (event.key === 'Enter' || event.key === ' ') {
356
+ event.preventDefault();
357
+ onSelectBlock(block.id);
358
+ }
359
+ }}
360
+ onmousemove={previewMode ? undefined : (event: MouseEvent) =>
361
+ updateHoverStates(
362
+ block.id,
363
+ previewOverlays[block.id] ?? [],
364
+ previewCollectionOverlays[block.id] ?? [],
365
+ event
366
+ )}
367
+ onmouseleave={previewMode ? undefined : () => {
368
+ hoveredCollectionItem = null;
369
+ hoveredCollection = null;
370
+ }}
371
+ >
372
+ {#if !previewMode}
373
+ <PreviewBlockInserter
374
+ placement="before"
375
+ edgeInset={blockIndex === 0}
376
+ onToggle={() => onOpenInserterModal(block.id, 'before')}
377
+ />
378
+ {/if}
379
+ <div data-builder-preview-content>
380
+ <BlockComponent {...renderProps} />
381
+ </div>
382
+ {#if activeBlockId === block.id && !previewMode}
383
+ <div
384
+ class="pointer-events-none absolute inset-px z-30 border-2 border-[#FDE047] dark:border-[#FACC15]"
385
+ ></div>
386
+ {/if}
387
+
388
+ {#if definition.collections.length > 0 && !previewMode}
389
+ <div class="pointer-events-none absolute inset-0">
390
+ {#each previewCollectionOverlays[block.id] ?? [] as overlay (overlay.collectionPath)}
391
+ <div
392
+ class="collection-overlay pointer-events-none absolute z-10"
393
+ style={`top:${overlay.top}px; left:${overlay.left}px; width:${overlay.width}px; height:${overlay.height}px;`}
394
+ >
395
+ <div
396
+ class="collection-outline absolute inset-0 outline outline-1 outline-[#FDE047] transition outline-dashed dark:outline-[#FACC15] {hoveredCollection === `${block.id}:${overlay.collectionPath}` ? 'opacity-100' : 'opacity-0'}"
397
+ ></div>
398
+ <button
399
+ type="button"
400
+ class="collection-add-button btn-brutal-icon pointer-events-auto absolute top-full left-1/2 flex h-7 w-7 -translate-x-1/2 translate-y-2 items-center justify-center text-lg leading-none transition {hoveredCollection === `${block.id}:${overlay.collectionPath}` ? 'opacity-100' : 'opacity-0'}"
401
+ aria-label={`Aggiungi ${overlay.label}`}
402
+ onclick={(event) => {
403
+ event.stopPropagation();
404
+ onAddItem(block, overlay.collectionPath);
405
+ }}
406
+ >
407
+ +
408
+ </button>
409
+ </div>
410
+ {/each}
411
+
412
+ {#each previewOverlays[block.id] ?? [] as overlay (`${overlay.collectionPath}-${overlay.index}`)}
413
+ <div
414
+ class="collection-item-overlay pointer-events-none absolute z-20"
415
+ style={`top:${Math.max(0, overlay.top + 36)}px; left:${overlay.left}px; width:${overlay.width}px; height:${overlay.height}px;`}
416
+ >
417
+ <div
418
+ class={hoveredCollectionItem ===
419
+ getCollectionItemKey(block.id, overlay.collectionPath, overlay.index)
420
+ ? 'collection-item-outline absolute inset-0 opacity-100 outline outline-1 outline-[#FDE047] transition dark:outline-[#FACC15]'
421
+ : 'collection-item-outline absolute inset-0 opacity-0 outline outline-1 outline-[#FDE047] transition dark:outline-[#FACC15]'}
422
+ ></div>
423
+ <div
424
+ class={hoveredCollectionItem ===
425
+ getCollectionItemKey(block.id, overlay.collectionPath, overlay.index)
426
+ ? 'collection-item-toolbar pointer-events-auto absolute top-0 left-0 flex h-8 -translate-y-full items-center overflow-hidden border border-gray-300 bg-white text-xs text-gray-900 opacity-100 shadow-[0_2px_8px_rgba(0,0,0,0.18)] transition dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100'
427
+ : 'collection-item-toolbar pointer-events-auto absolute top-0 left-0 flex h-8 -translate-y-full items-center overflow-hidden border border-gray-300 bg-white text-xs text-gray-900 opacity-0 shadow-[0_2px_8px_rgba(0,0,0,0.18)] transition dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100'}
428
+ >
429
+ <button
430
+ type="button"
431
+ class="h-full border-r border-gray-200 px-2.5 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:hover:bg-gray-700"
432
+ onclick={(event) => {
433
+ event.stopPropagation();
434
+ onMoveItem(block, overlay.collectionPath, overlay.index, -1);
435
+ }}
436
+ >
437
+
438
+ </button>
439
+ <button
440
+ type="button"
441
+ class="h-full border-r border-gray-200 px-2.5 transition-colors hover:bg-gray-100 dark:border-gray-700 dark:hover:bg-gray-700"
442
+ onclick={(event) => {
443
+ event.stopPropagation();
444
+ onMoveItem(block, overlay.collectionPath, overlay.index, 1);
445
+ }}
446
+ >
447
+
448
+ </button>
449
+ <button
450
+ type="button"
451
+ class="h-full px-2.5 text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950"
452
+ onclick={(event) => {
453
+ event.stopPropagation();
454
+ onRemoveItem(block, overlay.collectionPath, overlay.index);
455
+ }}
456
+ >
457
+ ×
458
+ </button>
459
+ </div>
460
+ </div>
461
+ {/each}
462
+ </div>
463
+ {/if}
464
+
465
+ {#if !previewMode}
466
+ <PreviewBlockInserter
467
+ placement="after"
468
+ edgeInset={blockIndex === blocks.length - 1}
469
+ onToggle={() => onOpenInserterModal(block.id, 'after')}
470
+ />
471
+ {/if}
472
+ </div>
473
+ {/if}
474
+ {:else}
475
+ <div
476
+ class="border border-dashed border-red-300 bg-red-50 px-4 py-3 text-sm text-red-600 dark:border-red-800 dark:bg-red-950 dark:text-red-400"
477
+ >
478
+ Correggi i contenuti di questo brik per vedere di nuovo la preview.
479
+ </div>
480
+ {/if}
481
+ {/each}
482
+ </div>
483
+
484
+
485
+ <style>
486
+ .collection-overlay:focus-within .collection-outline,
487
+ .collection-overlay:focus-within .collection-add-button {
488
+ opacity: 1;
489
+ }
490
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { Component } from 'svelte';
2
+ import type { BuilderDefinition } from '../core.js';
3
+ export interface BrikDefinition extends BuilderDefinition {
4
+ component: Component<Record<string, unknown>>;
5
+ }
6
+ export declare function createBrixDefinitions(brikModules: Record<string, unknown>, brikSources?: Record<string, string>): BrikDefinition[];
7
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../svelte/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAEX,iBAAiB,EAIjB,MAAM,YAAY,CAAC;AAkBpB,MAAM,WAAW,cAAe,SAAQ,iBAAiB;IACxD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9C;AAED,wBAAgB,qBAAqB,CACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACtC,cAAc,EAAE,CAYlB"}
@@ -0,0 +1,66 @@
1
+ import { createBuilderCollectionsFromFields, createBuilderDefaultsFromFields, createBuilderPreviewBindingsFromFields } from '../core.js';
2
+ import { createBrikSchemaFromMarkup, mergeBuilderFields } from './markup-schema.js';
3
+ export function createBrixDefinitions(brikModules, brikSources = {}) {
4
+ return Object.entries(brikModules)
5
+ .map(([path, module]) => createDefinition(path, module, brikSources[path] ?? ''))
6
+ .sort((a, b) => {
7
+ if (a.mode !== b.mode) {
8
+ return a.mode === 'markdown' ? -1 : 1;
9
+ }
10
+ return a.type.localeCompare(b.type);
11
+ });
12
+ }
13
+ function createDefinition(path, module, source) {
14
+ const type = path.split('/').pop()?.replace('.svelte', '') ?? path;
15
+ const markupFields = source ? createBrikSchemaFromMarkup(source) : {};
16
+ const fields = mergeBuilderFields(markupFields, cloneValue(module.brikFields ?? {}));
17
+ const defaults = Object.keys(fields).length > 0
18
+ ? mergeDefaults(createBuilderDefaultsFromFields(fields), cloneValue(module.brikDefaults ?? {}))
19
+ : cloneValue(module.brikDefaults ?? {});
20
+ const previewBindings = Object.keys(fields).length > 0
21
+ ? createBuilderPreviewBindingsFromFields(fields)
22
+ : cloneValue(module.brikPreviewBindings ?? []);
23
+ const collections = Object.keys(fields).length > 0
24
+ ? createBuilderCollectionsFromFields(fields)
25
+ : cloneValue(module.brikCollections ?? []);
26
+ return {
27
+ type,
28
+ path: toLibImportPath(type),
29
+ description: module.brikDescription ?? `Brik ${humanizeType(type)}.`,
30
+ mode: module.brikMode ?? 'component',
31
+ component: module.default,
32
+ defaults,
33
+ previewBindings,
34
+ collections,
35
+ fields
36
+ };
37
+ }
38
+ function toLibImportPath(type) {
39
+ return `$lib/brixter/brix/${type}.svelte`;
40
+ }
41
+ function humanizeType(type) {
42
+ return type
43
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
44
+ .split(/[-_ ]/)
45
+ .filter(Boolean)
46
+ .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
47
+ .join(' ');
48
+ }
49
+ function cloneValue(value) {
50
+ return JSON.parse(JSON.stringify(value));
51
+ }
52
+ function mergeDefaults(baseDefaults, overrideDefaults) {
53
+ const merged = { ...baseDefaults };
54
+ for (const [key, value] of Object.entries(overrideDefaults)) {
55
+ const baseValue = merged[key];
56
+ merged[key] =
57
+ isRecord(baseValue) && isRecord(value)
58
+ ? mergeDefaults(baseValue, value)
59
+ : cloneValue(value);
60
+ }
61
+ return merged;
62
+ }
63
+ function isRecord(value) {
64
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
65
+ }
66
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../svelte/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { createBrikSchemaFromMarkup } from './markup-schema.js';
2
+ export { createBrixDefinitions, type BrikDefinition } from './adapter.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createBrikSchemaFromMarkup } from './markup-schema.js';
2
+ export { createBrixDefinitions } from './adapter.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAuB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { BuilderFields } from '../core.js';
2
+ export declare function createBuilderFieldsFromMarkup(source: string): BuilderFields;
3
+ export declare function createBrikSchemaFromMarkup(source: string): BuilderFields;
4
+ export declare function mergeBuilderFields(baseFields: BuilderFields, overrideFields: BuilderFields): BuilderFields;
5
+ //# sourceMappingURL=markup-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markup-schema.d.ts","sourceRoot":"","sources":["../../svelte/markup-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkC,aAAa,EAAE,MAAM,YAAY,CAAC;AAahF,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CA0B3E;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAExE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,GAAG,aAAa,CAS1G"}