@kubex/zinc 1.1.64 → 1.1.65
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/dist/custom-elements.json +246 -27
- package/dist/vscode.html-custom-data.json +13 -3
- package/dist/web-types.json +44 -5
- package/dist/zn.d.ts +46 -11
- package/dist/zn.min.js +441 -389
- package/docs/pages/components/remarkd-editor.md +25 -9
- package/package.json +2 -2
- package/src/components/page-builder/page-builder.component.ts +118 -54
- package/src/components/query-builder/query-builder.component.ts +2 -1
- package/src/components/remarkd-editor/remarkd-editor.component.ts +240 -55
- package/src/components/remarkd-editor/remarkd-editor.scss +106 -3
- package/src/components/remarkd-editor/remarkd-editor.test.ts +40 -6
- package/web-test-runner.config.js +2 -2
|
@@ -30,10 +30,10 @@ NOTE: Click any block to edit its source."
|
|
|
30
30
|
|
|
31
31
|
### Adding and Moving Blocks
|
|
32
32
|
|
|
33
|
-
Hover a block to reveal its
|
|
34
|
-
|
|
35
|
-
area at the end of the document starts a new block. A
|
|
36
|
-
is removed.
|
|
33
|
+
Hover a block to reveal its actions: a drag handle with an add-block button
|
|
34
|
+
below it in the left gutter, and a delete button in its top-right corner.
|
|
35
|
+
Clicking the empty area at the end of the document starts a new block. A
|
|
36
|
+
block committed empty is removed.
|
|
37
37
|
|
|
38
38
|
```html:preview
|
|
39
39
|
<zn-remarkd-editor
|
|
@@ -44,12 +44,28 @@ is removed.
|
|
|
44
44
|
|
|
45
45
|
### Images
|
|
46
46
|
|
|
47
|
-
Adding an image
|
|
48
|
-
|
|
47
|
+
Adding an image shows a `zn-file` drop area inline, at the point in the
|
|
48
|
+
document where the image will go. Choosing a file uploads it straight away:
|
|
49
|
+
the file's metadata is POSTed to the `attachment-url` endpoint, which must
|
|
49
50
|
respond with `{uploadPath, uploadUrl}`; the file is then PUT to `uploadUrl`
|
|
50
|
-
and `uploadPath` is
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
and the returned `uploadPath` is embedded as the image URL. Dropping an image
|
|
52
|
+
file straight onto the editor uploads it directly. `attachment-url` is
|
|
53
|
+
required for image support.
|
|
54
|
+
|
|
55
|
+
Clicking an image block opens its controls: caption, alignment
|
|
56
|
+
(left / center / right), width, height, and alt text — plus edit-source and
|
|
57
|
+
delete actions. These write standard remarkd syntax back to the value: the
|
|
58
|
+
caption as a `.Caption` title line, alignment as an `[.align-center]` /
|
|
59
|
+
`[.align-right]` class, and size as `image::src[alt,width,height]`.
|
|
60
|
+
|
|
61
|
+
```html:preview
|
|
62
|
+
<zn-remarkd-editor
|
|
63
|
+
name="content"
|
|
64
|
+
value=".The Zinc logo
|
|
65
|
+
[.align-center]
|
|
66
|
+
image::/assets/images/watermark.svg[Zinc,220]"
|
|
67
|
+
></zn-remarkd-editor>
|
|
68
|
+
```
|
|
53
69
|
|
|
54
70
|
```html
|
|
55
71
|
<zn-remarkd-editor name="content" attachment-url="/upload"></zn-remarkd-editor>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubex/zinc",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.65",
|
|
4
4
|
"description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web components",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"lucide": "^1.17.0",
|
|
57
57
|
"marked": "^18.0.2",
|
|
58
58
|
"quill": "^2.0.3",
|
|
59
|
-
"remarkd-js": "github:packaged/remarkd
|
|
59
|
+
"remarkd-js": "github:packaged/remarkd"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@custom-elements-manifest/analyzer": "^0.10.3",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
|
|
1
|
+
import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
|
|
2
2
|
import {
|
|
3
3
|
emptyPageState,
|
|
4
4
|
generateSectionId,
|
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
sectionChildren,
|
|
11
11
|
sectionSummary,
|
|
12
12
|
} from './page.types';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
13
|
+
import { FormControlController, validValidityState } from '../../internal/form';
|
|
14
|
+
import { HasSlotController } from '../../internal/slot';
|
|
15
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
16
|
+
import { PageSectionRegistry } from './page-registry';
|
|
17
|
+
import { property, state } from 'lit/decorators.js';
|
|
18
|
+
import { watch } from '../../internal/watch';
|
|
18
19
|
import ZincElement from '../../internal/zinc-element';
|
|
19
20
|
import ZnCollapsible from '../collapsible';
|
|
20
21
|
import ZnIcon from '../icon';
|
|
@@ -88,20 +89,28 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
88
89
|
'zn-page-section-card': ZnPageSectionCard,
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
private readonly formControlController = new FormControlController(this);
|
|
93
|
+
|
|
94
|
+
/** The name of the control, submitted as a name/value pair with form data. */
|
|
95
|
+
@property() name = '';
|
|
96
|
+
|
|
97
|
+
/** Associates the control with a form by id. The form must be in the same document or shadow root. */
|
|
98
|
+
@property({ reflect: true }) form = '';
|
|
99
|
+
|
|
91
100
|
/** The page state as a JSON string. Parsed on set; invalid JSON is ignored with a warning. */
|
|
92
101
|
@property() config = '';
|
|
93
102
|
|
|
94
|
-
@property({reflect: true}) heading = '';
|
|
95
|
-
@property({reflect: true}) subheading = '';
|
|
103
|
+
@property({ reflect: true }) heading = '';
|
|
104
|
+
@property({ reflect: true }) subheading = '';
|
|
96
105
|
|
|
97
106
|
/** Section types to make available, registered into the internal registry. */
|
|
98
|
-
@property({attribute: false}) sectionTypes: PageSectionType[] = [];
|
|
107
|
+
@property({ attribute: false }) sectionTypes: PageSectionType[] = [];
|
|
99
108
|
|
|
100
109
|
/** Collapses the left palette. Auto-set when the builder becomes narrow. */
|
|
101
|
-
@property({type: Boolean, reflect: true, attribute: 'palette-collapsed'}) paletteCollapsed = false;
|
|
110
|
+
@property({ type: Boolean, reflect: true, attribute: 'palette-collapsed' }) paletteCollapsed = false;
|
|
102
111
|
|
|
103
112
|
/** Collapses the inspector while a section is selected. */
|
|
104
|
-
@property({type: Boolean, reflect: true, attribute: 'inspector-collapsed'}) inspectorCollapsed = false;
|
|
113
|
+
@property({ type: Boolean, reflect: true, attribute: 'inspector-collapsed' }) inspectorCollapsed = false;
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
116
|
* Auto-save the page to localStorage (1-day TTL). Omit to disable. A bare
|
|
@@ -129,9 +138,9 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
129
138
|
@state() private _pickerIndex: number | null = null;
|
|
130
139
|
@state() private _dragOverIndex: number | null = null;
|
|
131
140
|
/** The container slot a drag is currently over, if any. */
|
|
132
|
-
@state() private _slotDragOver: {containerId: string; index: number} | null = null;
|
|
141
|
+
@state() private _slotDragOver: { containerId: string; index: number } | null = null;
|
|
133
142
|
/** The container slot whose "+" type picker is open, if any. */
|
|
134
|
-
@state() private _slotPicker: {containerId: string; index: number} | null = null;
|
|
143
|
+
@state() private _slotPicker: { containerId: string; index: number } | null = null;
|
|
135
144
|
/** The stamped config form for the selected section; rebuilt on selection change. */
|
|
136
145
|
@state() private _form: HTMLDivElement | null = null;
|
|
137
146
|
|
|
@@ -154,6 +163,55 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
154
163
|
}
|
|
155
164
|
}
|
|
156
165
|
|
|
166
|
+
/** The page state as a JSON string — the value submitted with form data. */
|
|
167
|
+
get value(): string {
|
|
168
|
+
return JSON.stringify(this._state);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Replaces the page state from a JSON string (does not emit zn-page-change). */
|
|
172
|
+
set value(next: string) {
|
|
173
|
+
if (!next) return;
|
|
174
|
+
try {
|
|
175
|
+
this._applyExternalState(JSON.parse(next) as PageState);
|
|
176
|
+
} catch {
|
|
177
|
+
console.warn('<zn-page-builder> invalid value JSON');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** The serialised state of the last externally loaded page — restored on form reset. */
|
|
182
|
+
defaultValue = JSON.stringify(emptyPageState());
|
|
183
|
+
|
|
184
|
+
/** Gets the validity state object. */
|
|
185
|
+
get validity(): ValidityState {
|
|
186
|
+
return validValidityState;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Gets the validation message. */
|
|
190
|
+
get validationMessage(): string {
|
|
191
|
+
return '';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Checks for validity but does not show a validation message. */
|
|
195
|
+
checkValidity(): boolean {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Gets the associated form, if one exists. */
|
|
200
|
+
getForm(): HTMLFormElement | null {
|
|
201
|
+
return this.formControlController.getForm();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
|
205
|
+
reportValidity(): boolean {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Sets a custom validation message. Pass an empty string to restore validity. */
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
211
|
+
setCustomValidity(_message = '') {
|
|
212
|
+
// Always valid.
|
|
213
|
+
}
|
|
214
|
+
|
|
157
215
|
@watch('config')
|
|
158
216
|
handleConfigChange() {
|
|
159
217
|
if (!this.config) return;
|
|
@@ -227,7 +285,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
227
285
|
/** Re-render clock for the "last saved Xm ago" label. */
|
|
228
286
|
@state() private _statusNow = Date.now();
|
|
229
287
|
/** A fresh auto-save differing from the loaded page — offer to restore it. */
|
|
230
|
-
@state() private _restorePrompt: {savedAt: number} | null = null;
|
|
288
|
+
@state() private _restorePrompt: { savedAt: number } | null = null;
|
|
231
289
|
|
|
232
290
|
/** localStorage key for this builder's auto-saves — its id, else its heading. */
|
|
233
291
|
private get _autoSaveKey(): string {
|
|
@@ -262,7 +320,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
262
320
|
private _autoSaveTick = () => {
|
|
263
321
|
if (!this._state.sections.length || this._restorePrompt) return;
|
|
264
322
|
try {
|
|
265
|
-
localStorage.setItem(this._autoSaveKey, JSON.stringify({savedAt: Date.now(), state: this._state}));
|
|
323
|
+
localStorage.setItem(this._autoSaveKey, JSON.stringify({ savedAt: Date.now(), state: this._state }));
|
|
266
324
|
this._lastSavedAt = Date.now();
|
|
267
325
|
this._justSaved = true;
|
|
268
326
|
if (this._justSavedTimer !== null) clearTimeout(this._justSavedTimer);
|
|
@@ -273,11 +331,11 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
273
331
|
};
|
|
274
332
|
|
|
275
333
|
/** The stored auto-save, purging it when past its TTL (or unreadable). */
|
|
276
|
-
private _readAutoSave(): {savedAt: number; state: PageState} | null {
|
|
334
|
+
private _readAutoSave(): { savedAt: number; state: PageState } | null {
|
|
277
335
|
try {
|
|
278
336
|
const raw = localStorage.getItem(this._autoSaveKey);
|
|
279
337
|
if (!raw) return null;
|
|
280
|
-
const saved = JSON.parse(raw) as {savedAt: number; state: PageState};
|
|
338
|
+
const saved = JSON.parse(raw) as { savedAt: number; state: PageState };
|
|
281
339
|
if (!saved.state || Date.now() - saved.savedAt > AUTO_SAVE_TTL_MS) {
|
|
282
340
|
localStorage.removeItem(this._autoSaveKey);
|
|
283
341
|
return null;
|
|
@@ -310,7 +368,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
310
368
|
if (this.autoSave === null || this._restoring) return;
|
|
311
369
|
const saved = this._readAutoSave();
|
|
312
370
|
this._restorePrompt = saved && JSON.stringify(saved.state) !== JSON.stringify(this._state)
|
|
313
|
-
? {savedAt: saved.savedAt}
|
|
371
|
+
? { savedAt: saved.savedAt }
|
|
314
372
|
: null;
|
|
315
373
|
}
|
|
316
374
|
|
|
@@ -320,6 +378,11 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
320
378
|
if (changed.has('autoSave')) this._restartAutoSave();
|
|
321
379
|
}
|
|
322
380
|
|
|
381
|
+
protected firstUpdated(changed: PropertyValues) {
|
|
382
|
+
super.firstUpdated(changed);
|
|
383
|
+
this.formControlController.updateValidity();
|
|
384
|
+
}
|
|
385
|
+
|
|
323
386
|
// --- Slotted templates define section types --------------------------------
|
|
324
387
|
|
|
325
388
|
private _typeFromTemplate(el: HTMLTemplateElement): PageSectionType | null {
|
|
@@ -371,7 +434,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
371
434
|
label: s.label,
|
|
372
435
|
data: structuredClone(s.data ?? {}),
|
|
373
436
|
...(s.children && depth === 0
|
|
374
|
-
? {children: s.children.slice(0, MAX_CHILDREN).map(c => (c && typeof c.type === 'string' ? normalise(c, depth + 1) : null))}
|
|
437
|
+
? { children: s.children.slice(0, MAX_CHILDREN).map(c => (c && typeof c.type === 'string' ? normalise(c, depth + 1) : null)) }
|
|
375
438
|
: {}),
|
|
376
439
|
};
|
|
377
440
|
};
|
|
@@ -385,7 +448,8 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
385
448
|
}
|
|
386
449
|
this._history = [];
|
|
387
450
|
this._redoStack = [];
|
|
388
|
-
this._state = {sections};
|
|
451
|
+
this._state = { sections };
|
|
452
|
+
this.defaultValue = JSON.stringify(this._state);
|
|
389
453
|
this._selectedId = null;
|
|
390
454
|
this._pickerIndex = null;
|
|
391
455
|
this._offerRestoreIfNewer();
|
|
@@ -407,7 +471,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
407
471
|
return this._state.sections.map(s => {
|
|
408
472
|
if (s.id === id) return patch(s);
|
|
409
473
|
if (s.children?.some(c => c?.id === id)) {
|
|
410
|
-
return {...s, children: s.children.map(c => (c?.id === id ? patch(c) : c))};
|
|
474
|
+
return { ...s, children: s.children.map(c => (c?.id === id ? patch(c) : c)) };
|
|
411
475
|
}
|
|
412
476
|
return s;
|
|
413
477
|
});
|
|
@@ -425,7 +489,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
425
489
|
const slot = s.children?.findIndex(c => c?.id === id) ?? -1;
|
|
426
490
|
if (slot !== -1) {
|
|
427
491
|
removed = s.children![slot] ?? undefined;
|
|
428
|
-
sections.push({...s, children: s.children!.map((c, i) => (i === slot ? null : c))});
|
|
492
|
+
sections.push({ ...s, children: s.children!.map((c, i) => (i === slot ? null : c)) });
|
|
429
493
|
} else {
|
|
430
494
|
sections.push(s);
|
|
431
495
|
}
|
|
@@ -436,7 +500,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
436
500
|
/** Installs a new state from a user edit and notifies listeners. */
|
|
437
501
|
private _commit(next: PageState) {
|
|
438
502
|
this._state = next;
|
|
439
|
-
this.emit('zn-page-change', {detail: {state: this.state}});
|
|
503
|
+
this.emit('zn-page-change', { detail: { state: this.state } });
|
|
440
504
|
}
|
|
441
505
|
|
|
442
506
|
private _selectedSection(): PageSection | undefined {
|
|
@@ -448,7 +512,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
448
512
|
this._selectedId = id;
|
|
449
513
|
this._pickerIndex = null;
|
|
450
514
|
this._slotPicker = null;
|
|
451
|
-
this.emit('zn-page-selection-change', {detail: {sectionId: id}});
|
|
515
|
+
this.emit('zn-page-selection-change', { detail: { sectionId: id } });
|
|
452
516
|
}
|
|
453
517
|
|
|
454
518
|
// --- History ----------------------------------------------------------------
|
|
@@ -483,10 +547,10 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
483
547
|
addSection(type: string, index?: number): PageSection | null {
|
|
484
548
|
if (!this.registry.has(type)) return null;
|
|
485
549
|
this._pushHistory();
|
|
486
|
-
const section: PageSection = {id: generateSectionId(), type, data: {}};
|
|
550
|
+
const section: PageSection = { id: generateSectionId(), type, data: {} };
|
|
487
551
|
const sections = [...this._state.sections];
|
|
488
552
|
sections.splice(index ?? sections.length, 0, section);
|
|
489
|
-
this._commit({sections});
|
|
553
|
+
this._commit({ sections });
|
|
490
554
|
this._select(section.id);
|
|
491
555
|
return section;
|
|
492
556
|
}
|
|
@@ -501,10 +565,10 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
501
565
|
if (containerType.accepts && !containerType.accepts.includes(type)) return null;
|
|
502
566
|
const children = sectionChildren(container, containerType);
|
|
503
567
|
if (children[slotIndex]) return null;
|
|
504
|
-
const section: PageSection = {id: generateSectionId(), type, data: {}};
|
|
568
|
+
const section: PageSection = { id: generateSectionId(), type, data: {} };
|
|
505
569
|
children[slotIndex] = section;
|
|
506
570
|
this._pushHistory();
|
|
507
|
-
this._commit({sections: this._patchSection(containerId, s => ({...s, children}))});
|
|
571
|
+
this._commit({ sections: this._patchSection(containerId, s => ({ ...s, children })) });
|
|
508
572
|
this._select(section.id);
|
|
509
573
|
return section;
|
|
510
574
|
}
|
|
@@ -517,7 +581,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
517
581
|
if (this._selectedId === id || removed.children?.some(c => c?.id === this._selectedId)) {
|
|
518
582
|
this._select(null);
|
|
519
583
|
}
|
|
520
|
-
this._commit({sections});
|
|
584
|
+
this._commit({ sections });
|
|
521
585
|
}
|
|
522
586
|
|
|
523
587
|
private _duplicateSection(id: string) {
|
|
@@ -526,10 +590,10 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
526
590
|
this._pushHistory();
|
|
527
591
|
const copy = structuredClone(this._state.sections[index]);
|
|
528
592
|
copy.id = generateSectionId();
|
|
529
|
-
copy.children = copy.children?.map(c => (c ? {...c, id: generateSectionId()} : null));
|
|
593
|
+
copy.children = copy.children?.map(c => (c ? { ...c, id: generateSectionId() } : null));
|
|
530
594
|
const sections = [...this._state.sections];
|
|
531
595
|
sections.splice(index + 1, 0, copy);
|
|
532
|
-
this._commit({sections});
|
|
596
|
+
this._commit({ sections });
|
|
533
597
|
this._select(copy.id);
|
|
534
598
|
return;
|
|
535
599
|
}
|
|
@@ -546,7 +610,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
546
610
|
copy.id = generateSectionId();
|
|
547
611
|
children[empty] = copy;
|
|
548
612
|
this._pushHistory();
|
|
549
|
-
this._commit({sections: this._patchSection(s.id, x => ({...x, children}))});
|
|
613
|
+
this._commit({ sections: this._patchSection(s.id, x => ({ ...x, children })) });
|
|
550
614
|
this._select(copy.id);
|
|
551
615
|
return;
|
|
552
616
|
}
|
|
@@ -562,14 +626,14 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
562
626
|
const sections = [...this._state.sections];
|
|
563
627
|
const [moved] = sections.splice(from, 1);
|
|
564
628
|
sections.splice(to, 0, moved);
|
|
565
|
-
this._commit({sections});
|
|
629
|
+
this._commit({ sections });
|
|
566
630
|
return;
|
|
567
631
|
}
|
|
568
632
|
const [moved, sections] = this._extract(id);
|
|
569
633
|
if (!moved) return;
|
|
570
634
|
this._pushHistory();
|
|
571
635
|
sections.splice(index, 0, moved);
|
|
572
|
-
this._commit({sections});
|
|
636
|
+
this._commit({ sections });
|
|
573
637
|
}
|
|
574
638
|
|
|
575
639
|
/**
|
|
@@ -594,7 +658,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
594
658
|
this._pushHistory();
|
|
595
659
|
const sections = structuredClone(this._state.sections);
|
|
596
660
|
const containerRef = sections.find(s => s.id === containerId)!;
|
|
597
|
-
containerRef.children = Array.from({length: containerType.slots}, (_, i) => containerRef.children?.[i] ?? null);
|
|
661
|
+
containerRef.children = Array.from({ length: containerType.slots }, (_, i) => containerRef.children?.[i] ?? null);
|
|
598
662
|
const movedCopy = structuredClone(moved);
|
|
599
663
|
|
|
600
664
|
if (fromTop) {
|
|
@@ -610,7 +674,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
610
674
|
}
|
|
611
675
|
}
|
|
612
676
|
containerRef.children[slotIndex] = movedCopy;
|
|
613
|
-
this._commit({sections});
|
|
677
|
+
this._commit({ sections });
|
|
614
678
|
}
|
|
615
679
|
|
|
616
680
|
// --- Canvas drag & drop -----------------------------------------------------
|
|
@@ -633,7 +697,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
633
697
|
e.preventDefault();
|
|
634
698
|
e.stopPropagation();
|
|
635
699
|
this._dragOverIndex = null;
|
|
636
|
-
this._slotDragOver = {containerId, index};
|
|
700
|
+
this._slotDragOver = { containerId, index };
|
|
637
701
|
}
|
|
638
702
|
|
|
639
703
|
private _onSlotDrop(e: DragEvent, containerId: string, index: number) {
|
|
@@ -689,7 +753,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
689
753
|
const query = this._search.trim().toLowerCase();
|
|
690
754
|
const categories = new Map<string, PageSectionType[]>();
|
|
691
755
|
for (const [category, types] of this.registry.categories()) {
|
|
692
|
-
const matches = query ? types.filter(t=>t.label.toLowerCase().includes(query)) : types;
|
|
756
|
+
const matches = query ? types.filter(t => t.label.toLowerCase().includes(query)) : types;
|
|
693
757
|
if (matches.length) categories.set(category, matches);
|
|
694
758
|
}
|
|
695
759
|
|
|
@@ -708,7 +772,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
708
772
|
placeholder="Search sections"
|
|
709
773
|
clearable
|
|
710
774
|
.value="${this._search}"
|
|
711
|
-
@zn-input="${(e: Event)=>(this._search = String((e.target as ZnInput).value ?? ''))}"></zn-input>
|
|
775
|
+
@zn-input="${(e: Event) => (this._search = String((e.target as ZnInput).value ?? ''))}"></zn-input>
|
|
712
776
|
<div class="palette__scroll">
|
|
713
777
|
${[...categories.entries()].map(([category, types]) => category
|
|
714
778
|
? html`
|
|
@@ -795,14 +859,14 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
795
859
|
@click="${() => this._select(null)}"
|
|
796
860
|
@dragover="${this._onCanvasDragOver}"
|
|
797
861
|
@drop="${this._onCanvasDrop}">
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
862
|
+
${sections.length === 0 ? html`
|
|
863
|
+
<div class="canvas__empty" ?hidden="${this._dragOverIndex !== null}">
|
|
864
|
+
Drag sections here to build your page
|
|
865
|
+
</div>` : ''}
|
|
866
|
+
${sections.map((section, i) => html`
|
|
867
|
+
${this._renderDropZone(i)}
|
|
868
|
+
${this._renderCard(section, i)}
|
|
869
|
+
`)}
|
|
806
870
|
${this._renderDropZone(sections.length)}
|
|
807
871
|
</main>
|
|
808
872
|
</div>`;
|
|
@@ -811,7 +875,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
811
875
|
/** The one card template both the page list and slot cells render. */
|
|
812
876
|
private _renderSectionCard(
|
|
813
877
|
section: PageSection,
|
|
814
|
-
drop: {over: (e: DragEvent) => void; drop: (e: DragEvent) => void},
|
|
878
|
+
drop: { over: (e: DragEvent) => void; drop: (e: DragEvent) => void },
|
|
815
879
|
extraClass = ''
|
|
816
880
|
) {
|
|
817
881
|
const type = this.registry.get(section.type);
|
|
@@ -876,7 +940,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
876
940
|
@drop="${(e: DragEvent) => this._onSlotDrop(e, container.id, index)}"
|
|
877
941
|
@click="${(e: Event) => {
|
|
878
942
|
e.stopPropagation();
|
|
879
|
-
this._slotPicker = pickerOpen ? null : {containerId: container.id, index};
|
|
943
|
+
this._slotPicker = pickerOpen ? null : { containerId: container.id, index };
|
|
880
944
|
}}">
|
|
881
945
|
<zn-icon src="add" size="16"></zn-icon>
|
|
882
946
|
${pickerOpen
|
|
@@ -960,7 +1024,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
960
1024
|
(control as HTMLInputElement).checked = Boolean(value);
|
|
961
1025
|
} else if (Array.isArray(value)) {
|
|
962
1026
|
// Multi-value controls (e.g. zn-select[multiple]) take the array as-is.
|
|
963
|
-
(control as unknown as {value: unknown}).value = value;
|
|
1027
|
+
(control as unknown as { value: unknown }).value = value;
|
|
964
1028
|
} else if (value !== undefined && value !== null) {
|
|
965
1029
|
(control as HTMLInputElement).value = String(value);
|
|
966
1030
|
}
|
|
@@ -977,7 +1041,7 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
977
1041
|
private _onInspectorInput = (e: Event) => {
|
|
978
1042
|
const section = this._selectedSection();
|
|
979
1043
|
if (!section) return;
|
|
980
|
-
const control = e.target as HTMLElement & {value?: unknown; checked?: boolean};
|
|
1044
|
+
const control = e.target as HTMLElement & { value?: unknown; checked?: boolean };
|
|
981
1045
|
const name = control.getAttribute?.('name');
|
|
982
1046
|
if (!name) return;
|
|
983
1047
|
let value: unknown = this._isBooleanControl(control, section.data[name])
|
|
@@ -988,17 +1052,17 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
988
1052
|
}
|
|
989
1053
|
if (typeof value === 'number' && Number.isNaN(value)) return;
|
|
990
1054
|
if (section.data[name] === value) return;
|
|
991
|
-
this._updateSectionData(section.id, {[name]: value});
|
|
1055
|
+
this._updateSectionData(section.id, { [name]: value });
|
|
992
1056
|
};
|
|
993
1057
|
|
|
994
1058
|
private _updateSectionData(id: string, patch: Record<string, unknown>) {
|
|
995
1059
|
this._pushHistory();
|
|
996
|
-
this._commit({sections: this._patchSection(id, s => ({...s, data: {...s.data, ...patch}}))});
|
|
1060
|
+
this._commit({ sections: this._patchSection(id, s => ({ ...s, data: { ...s.data, ...patch } })) });
|
|
997
1061
|
}
|
|
998
1062
|
|
|
999
1063
|
private _renameSection(id: string, label: string) {
|
|
1000
1064
|
this._pushHistory();
|
|
1001
|
-
this._commit({sections: this._patchSection(id, s => ({...s, label: label || undefined}))});
|
|
1065
|
+
this._commit({ sections: this._patchSection(id, s => ({ ...s, label: label || undefined })) });
|
|
1002
1066
|
}
|
|
1003
1067
|
|
|
1004
1068
|
private _renderInspector() {
|
|
@@ -426,7 +426,8 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
|
|
|
426
426
|
multiple=${multiSelect || nothing}
|
|
427
427
|
clearable=${multiSelect || nothing}
|
|
428
428
|
max-options-visible=${filter.maxOptionsVisible || nothing}
|
|
429
|
-
selectedItems=${(multiSelect ? JSON.stringify(options) : undefined) || nothing}
|
|
429
|
+
selectedItems=${(multiSelect ? JSON.stringify(options) : undefined) || nothing}
|
|
430
|
+
search>
|
|
430
431
|
${Object.keys(options).map(key => html`
|
|
431
432
|
<zn-option value="${key}">
|
|
432
433
|
${options[key]}
|