@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.
@@ -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 gutter actions: add a block below it, or grab the
34
- handle to drag the block somewhere else in the document. Clicking the empty
35
- area at the end of the document starts a new block. A block committed empty
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 opens a dialog. With `attachment-url` set it contains a
48
- `zn-file` drop area; the file's metadata is POSTed to the endpoint, which must
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 inserted as an image block. Without `attachment-url` the
51
- dialog asks for an image URL instead. Dropping an image file straight onto the
52
- editor uploads it directly.
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.64",
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#fc482a4260740e0d61ab1cd50b056545fe195d94"
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 {HasSlotController} from '../../internal/slot';
14
- import {ifDefined} from 'lit/directives/if-defined.js';
15
- import {PageSectionRegistry} from './page-registry';
16
- import {property, state} from 'lit/decorators.js';
17
- import {watch} from '../../internal/watch';
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
- ${sections.length === 0 ? html`
799
- <div class="canvas__empty" ?hidden="${this._dragOverIndex !== null}">
800
- Drag sections here to build your page
801
- </div>` : ''}
802
- ${sections.map((section, i) => html`
803
- ${this._renderDropZone(i)}
804
- ${this._renderCard(section, i)}
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]}