@kubex/zinc 1.1.76 → 1.1.77

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.
@@ -3023,7 +3023,7 @@
3023
3023
  },
3024
3024
  {
3025
3025
  "name": "interactive",
3026
- "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).",
3026
+ "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).\n\nSet, the frame becomes a real viewport instead: it stays the panel's own\nheight and the embed scrolls itself, so there is one scrollbar rather than\na panel scrolling an oversized frame, and the embed's viewport-relative\nlayout (`100vh`, `position: fixed`, sticky headers) sizes to what's on\nscreen. _contentHeight is ignored while this is set.",
3027
3027
  "values": []
3028
3028
  }
3029
3029
  ],
@@ -3961,6 +3961,36 @@
3961
3961
  }
3962
3962
  ]
3963
3963
  },
3964
+ {
3965
+ "name": "zn-tabs",
3966
+ "description": "Short summary of the component's intended use.\n---\n\n\n### **Events:**\n - **zn-event-name** - Emitted as an example.\n\n### **Methods:**\n \n\n\n### **Slots:**\n - _default_ - The default slot.\n- **example** - An example slot.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
3967
+ "attributes": [
3968
+ { "name": "master-id", "values": [] },
3969
+ { "name": "default-uri", "values": [] },
3970
+ { "name": "active", "values": [] },
3971
+ { "name": "split", "values": [] },
3972
+ { "name": "split-min", "values": [] },
3973
+ { "name": "split-min-secondary", "values": [] },
3974
+ { "name": "split-max", "values": [] },
3975
+ { "name": "primary-caption", "values": [] },
3976
+ { "name": "secondary-caption", "values": [] },
3977
+ { "name": "no-prefetch", "values": [] },
3978
+ { "name": "no-cache", "values": [] },
3979
+ { "name": "local-storage", "values": [] },
3980
+ { "name": "store-key", "values": [] },
3981
+ { "name": "store-ttl", "values": [] },
3982
+ { "name": "padded", "values": [] },
3983
+ { "name": "fetch-style", "values": [] },
3984
+ { "name": "full-width", "values": [] },
3985
+ { "name": "padded-right", "values": [] },
3986
+ { "name": "monitor", "values": [] },
3987
+ { "name": "caption", "values": [] },
3988
+ { "name": "description", "values": [] }
3989
+ ],
3990
+ "references": [
3991
+ { "name": "Documentation", "url": "https://zinc.style/components/tabs" }
3992
+ ]
3993
+ },
3964
3994
  {
3965
3995
  "name": "zn-textarea",
3966
3996
  "description": "Textareas collect data from the user and allow multiple lines of text.\n---\n\n\n### **Events:**\n - **zn-blur** - Emitted when the control loses focus.\n- **zn-change** - Emitted when an alteration to the control's value is committed by the user.\n- **zn-focus** - Emitted when the control gains focus.\n- **zn-input** - Emitted when the control receives input.\n- **zn-invalid** - Emitted when the form control has been checked for validity and its constraints aren't satisfied.\n\n### **Methods:**\n - **focus(options: _FocusOptions_)** - Sets focus on the textarea.\n- **blur()** - Removes focus from the textarea.\n- **select()** - Selects all the text in the textarea.\n- **scrollPosition(position: _{ top?: number; left?: number }_): _{ top: number; left: number } | undefined_** - Gets or sets the textarea scroll position.\n- **setSelectionRange(selectionStart: _number_, selectionEnd: _number_, selectionDirection: _'forward' | 'backward' | 'none'_)** - Sets the start and end positions of the text selection (0-based).\n- **setRangeText(replacement: _string_, start: _number_, end: _number_, selectMode: _'select' | 'start' | 'end' | 'preserve'_)** - Replaces a range of text with a new string.\n- **checkValidity()** - Checks for validity but does not show a validation message. Returns `true` when valid and `false` when invalid.\n- **getForm(): _HTMLFormElement | null_** - Gets the associated form, if one exists.\n- **reportValidity()** - Checks for validity and shows the browser's validation message if the control is invalid.\n- **setCustomValidity(message: _string_)** - Sets a custom validation message. Pass an empty string to restore validity.\n\n### **Slots:**\n - **label** - The textareas label. Alternatively, you can use the `label` attribute.\n- **label-tooltip** - Used to add text that is displayed in a tooltip next to the label. Alternatively, you can use the `label-tooltip` attribute.\n- **context-note** - Used to add contextual text that is displayed above the textarea, on the right. Alternatively, you can use the `context-note` attribute.\n- **help-text** - Text that describes how to use the input. Alternatively, you can use the `help-text` attribute.\n\n### **CSS Parts:**\n - **form-control** - The form control that wraps the label, input, and help text.\n- **form-control-label** - The label's wrapper.\n- **form-control-input** - The input's wrapper.\n- **form-control-help-text** - The help text's wrapper.\n- **base** - The component's base wrapper.\n- **textarea** - The internal `<textarea>` control.",
@@ -4123,36 +4153,6 @@
4123
4153
  }
4124
4154
  ]
4125
4155
  },
4126
- {
4127
- "name": "zn-tabs",
4128
- "description": "Short summary of the component's intended use.\n---\n\n\n### **Events:**\n - **zn-event-name** - Emitted as an example.\n\n### **Methods:**\n \n\n\n### **Slots:**\n - _default_ - The default slot.\n- **example** - An example slot.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
4129
- "attributes": [
4130
- { "name": "master-id", "values": [] },
4131
- { "name": "default-uri", "values": [] },
4132
- { "name": "active", "values": [] },
4133
- { "name": "split", "values": [] },
4134
- { "name": "split-min", "values": [] },
4135
- { "name": "split-min-secondary", "values": [] },
4136
- { "name": "split-max", "values": [] },
4137
- { "name": "primary-caption", "values": [] },
4138
- { "name": "secondary-caption", "values": [] },
4139
- { "name": "no-prefetch", "values": [] },
4140
- { "name": "no-cache", "values": [] },
4141
- { "name": "local-storage", "values": [] },
4142
- { "name": "store-key", "values": [] },
4143
- { "name": "store-ttl", "values": [] },
4144
- { "name": "padded", "values": [] },
4145
- { "name": "fetch-style", "values": [] },
4146
- { "name": "full-width", "values": [] },
4147
- { "name": "padded-right", "values": [] },
4148
- { "name": "monitor", "values": [] },
4149
- { "name": "caption", "values": [] },
4150
- { "name": "description", "values": [] }
4151
- ],
4152
- "references": [
4153
- { "name": "Documentation", "url": "https://zinc.style/components/tabs" }
4154
- ]
4155
- },
4156
4156
  {
4157
4157
  "name": "zn-theme-editor",
4158
4158
  "description": "A theme editor: slotted form controls drive a live preview frame,\nwith a toolbar for the preview's light/dark mode and device width.\n---\n\n\n### **Events:**\n - **zn-theme-change** - Emitted when the values, mode or device change.\n- **zn-theme-submit** - Emitted on submit (button click), carrying the current values. With `action` set, only fires after a successful save.\n- **zn-error** - Emitted when a save fails. Also seen for preview render failures: the frame's zn-error is composed and not stopped, so it bubbles out through the editor too.\n\n### **Slots:**\n - _default_ - Ungrouped theme controls, rendered above any sections. Controls assigned `slot=\"<name>\"` matching a `sections` entry (or, when nested, a `groups` entry) render inside that section/group instead. Harvesting and change detection walk every slot's full assigned subtree, not just direct children. With `sections` left unset, the structure is instead derived from the controls' own attributes: `group=\"<label>\"` becomes a tab and `category=\"<label>\"` a collapsible within it, and the control is slotted into that collapsible automatically. Either attribute works alone - a control with only `group` sits directly in its tab, and one with only `category` becomes its own top-level section. Setting `sections` explicitly disables the derivation entirely.\n- **toolbar** - Actions in the toolbar, right-aligned beside the device controls. Where a save button belongs.\n- **footer** - Actions pinned beneath the controls. The built-in submit button lives in the toolbar, not here.\n\n### **CSS Properties:**\n - **--zn-theme-editor-controls-width** - Width of the controls column. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.\n- **controls** - The left-hand controls column, full height.\n- **controls-header** - The controls column's header row: `controls-caption` on the left, the light/dark mode toggle on the right.\n- **toolbar** - The preview column's header row: `preview-caption` on the left, the device switcher (and sources/submit) on the right. Spans the preview column only.\n- **section** - A rendered section's or group's collapsible (`section-layout=\"collapsible\"`, or any nested group).\n- **footer** - The footer wrapper beneath the controls.\n- **preview** - The preview column.\n- **error** - The inline error strip.\n- **preview__base** - The frame's base wrapper (forwarded from zn-preview-frame).\n- **preview__stage** - The frame's device-width wrapper (forwarded from zn-preview-frame).\n- **preview__iframe** - The frame's iframe (forwarded from zn-preview-frame).\n- **preview__error** - The frame's own error overlay (forwarded from zn-preview-frame).",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "@kubex/zinc",
4
- "version": "1.1.76",
4
+ "version": "1.1.77",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -6999,7 +6999,7 @@
6999
6999
  },
7000
7000
  {
7001
7001
  "name": "interactive",
7002
- "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).",
7002
+ "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).\n\nSet, the frame becomes a real viewport instead: it stays the panel's own\nheight and the embed scrolls itself, so there is one scrollbar rather than\na panel scrolling an oversized frame, and the embed's viewport-relative\nlayout (`100vh`, `position: fixed`, sticky headers) sizes to what's on\nscreen. _contentHeight is ignored while this is set.",
7003
7003
  "value": { "type": "boolean", "default": "false" }
7004
7004
  }
7005
7005
  ],
@@ -7068,7 +7068,7 @@
7068
7068
  },
7069
7069
  {
7070
7070
  "name": "interactive",
7071
- "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).",
7071
+ "description": "Lets pointer input through to the embedded page. The preview is inert by\ndefault: clicks never reach the frame, so the previewed page can't be\nnavigated or submitted from inside the preview. Cross-origin content can't\nbe reached from here to cancel its own handlers, so this blocks pointer\ninput entirely — hover goes with it. Scrolling doesn't: an overflowing\npage is scrolled by the panel rather than by the frame (see _contentHeight).\n\nSet, the frame becomes a real viewport instead: it stays the panel's own\nheight and the embed scrolls itself, so there is one scrollbar rather than\na panel scrolling an oversized frame, and the embed's viewport-relative\nlayout (`100vh`, `position: fixed`, sticky headers) sizes to what's on\nscreen. _contentHeight is ignored while this is set.",
7072
7072
  "type": "boolean"
7073
7073
  },
7074
7074
  { "name": "frame", "type": "HTMLIFrameElement" }
@@ -9364,6 +9364,110 @@
9364
9364
  ]
9365
9365
  }
9366
9366
  },
9367
+ {
9368
+ "name": "zn-tabs",
9369
+ "description": "Short summary of the component's intended use.\n---\n\n\n### **Events:**\n - **zn-event-name** - Emitted as an example.\n\n### **Methods:**\n \n\n\n### **Slots:**\n - _default_ - The default slot.\n- **example** - An example slot.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
9370
+ "doc-url": "",
9371
+ "attributes": [
9372
+ { "name": "master-id", "value": { "type": "string" } },
9373
+ {
9374
+ "name": "default-uri",
9375
+ "value": { "type": "string", "default": "''" }
9376
+ },
9377
+ {
9378
+ "name": "active",
9379
+ "value": { "type": "string", "default": "''" }
9380
+ },
9381
+ { "name": "split", "value": { "type": "number" } },
9382
+ {
9383
+ "name": "split-min",
9384
+ "value": { "type": "number", "default": "60" }
9385
+ },
9386
+ { "name": "split-min-secondary", "value": { "type": "number" } },
9387
+ { "name": "split-max", "value": { "type": "number" } },
9388
+ {
9389
+ "name": "primary-caption",
9390
+ "value": { "type": "string", "default": "'Navigation'" }
9391
+ },
9392
+ {
9393
+ "name": "secondary-caption",
9394
+ "value": { "type": "string", "default": "'Content'" }
9395
+ },
9396
+ {
9397
+ "name": "no-prefetch",
9398
+ "value": { "type": "boolean", "default": "false" }
9399
+ },
9400
+ {
9401
+ "name": "no-cache",
9402
+ "value": { "type": "boolean", "default": "false" }
9403
+ },
9404
+ { "name": "local-storage", "value": { "type": "boolean" } },
9405
+ { "name": "store-key", "value": { "type": "string" } },
9406
+ {
9407
+ "name": "store-ttl",
9408
+ "value": { "type": "number", "default": "0" }
9409
+ },
9410
+ {
9411
+ "name": "padded",
9412
+ "value": { "type": "boolean", "default": "false" }
9413
+ },
9414
+ {
9415
+ "name": "fetch-style",
9416
+ "value": { "type": "string", "default": "\"\"" }
9417
+ },
9418
+ {
9419
+ "name": "full-width",
9420
+ "value": { "type": "boolean", "default": "false" }
9421
+ },
9422
+ {
9423
+ "name": "padded-right",
9424
+ "value": { "type": "boolean", "default": "false" }
9425
+ },
9426
+ { "name": "monitor", "value": { "type": "string" } },
9427
+ { "name": "caption", "value": { "type": "string" } },
9428
+ { "name": "description", "value": { "type": "string" } }
9429
+ ],
9430
+ "slots": [
9431
+ { "name": "", "description": "The default slot." },
9432
+ { "name": "example", "description": "An example slot." }
9433
+ ],
9434
+ "events": [
9435
+ { "name": "zn-event-name", "description": "Emitted as an example." }
9436
+ ],
9437
+ "js": {
9438
+ "properties": [
9439
+ { "name": "masterId", "type": "string" },
9440
+ { "name": "defaultUri", "type": "string" },
9441
+ { "name": "_current", "type": "string" },
9442
+ { "name": "_split", "type": "number" },
9443
+ { "name": "_splitMin", "type": "number" },
9444
+ { "name": "_splitMinSecondary", "type": "number" },
9445
+ { "name": "_splitMax", "type": "number" },
9446
+ { "name": "primaryCaption", "type": "string" },
9447
+ { "name": "secondaryCaption", "type": "string" },
9448
+ { "name": "noPrefetch", "type": "boolean" },
9449
+ { "name": "noCache", "type": "boolean" },
9450
+ { "name": "localStorage", "type": "boolean" },
9451
+ { "name": "storeKey", "type": "string" },
9452
+ { "name": "storeTtl", "type": "number" },
9453
+ { "name": "padded", "type": "boolean" },
9454
+ { "name": "fetchStyle", "type": "string" },
9455
+ { "name": "fullWidth", "type": "boolean" },
9456
+ { "name": "paddedRight", "type": "boolean" },
9457
+ { "name": "monitor", "type": "string" },
9458
+ { "name": "caption", "type": "string" },
9459
+ { "name": "description", "type": "string" },
9460
+ { "name": "reRegisterTabs" },
9461
+ { "name": "_registerTabs" }
9462
+ ],
9463
+ "events": [
9464
+ {
9465
+ "name": "zn-event-name",
9466
+ "description": "Emitted as an example."
9467
+ }
9468
+ ]
9469
+ }
9470
+ },
9367
9471
  {
9368
9472
  "name": "zn-textarea",
9369
9473
  "description": "Textareas collect data from the user and allow multiple lines of text.\n---\n\n\n### **Events:**\n - **zn-blur** - Emitted when the control loses focus.\n- **zn-change** - Emitted when an alteration to the control's value is committed by the user.\n- **zn-focus** - Emitted when the control gains focus.\n- **zn-input** - Emitted when the control receives input.\n- **zn-invalid** - Emitted when the form control has been checked for validity and its constraints aren't satisfied.\n\n### **Methods:**\n - **focus(options: _FocusOptions_)** - Sets focus on the textarea.\n- **blur()** - Removes focus from the textarea.\n- **select()** - Selects all the text in the textarea.\n- **scrollPosition(position: _{ top?: number; left?: number }_): _{ top: number; left: number } | undefined_** - Gets or sets the textarea scroll position.\n- **setSelectionRange(selectionStart: _number_, selectionEnd: _number_, selectionDirection: _'forward' | 'backward' | 'none'_)** - Sets the start and end positions of the text selection (0-based).\n- **setRangeText(replacement: _string_, start: _number_, end: _number_, selectMode: _'select' | 'start' | 'end' | 'preserve'_)** - Replaces a range of text with a new string.\n- **checkValidity()** - Checks for validity but does not show a validation message. Returns `true` when valid and `false` when invalid.\n- **getForm(): _HTMLFormElement | null_** - Gets the associated form, if one exists.\n- **reportValidity()** - Checks for validity and shows the browser's validation message if the control is invalid.\n- **setCustomValidity(message: _string_)** - Sets a custom validation message. Pass an empty string to restore validity.\n\n### **Slots:**\n - **label** - The textareas label. Alternatively, you can use the `label` attribute.\n- **label-tooltip** - Used to add text that is displayed in a tooltip next to the label. Alternatively, you can use the `label-tooltip` attribute.\n- **context-note** - Used to add contextual text that is displayed above the textarea, on the right. Alternatively, you can use the `context-note` attribute.\n- **help-text** - Text that describes how to use the input. Alternatively, you can use the `help-text` attribute.\n\n### **CSS Parts:**\n - **form-control** - The form control that wraps the label, input, and help text.\n- **form-control-label** - The label's wrapper.\n- **form-control-input** - The input's wrapper.\n- **form-control-help-text** - The help text's wrapper.\n- **base** - The component's base wrapper.\n- **textarea** - The internal `<textarea>` control.",
@@ -9707,110 +9811,6 @@
9707
9811
  ]
9708
9812
  }
9709
9813
  },
9710
- {
9711
- "name": "zn-tabs",
9712
- "description": "Short summary of the component's intended use.\n---\n\n\n### **Events:**\n - **zn-event-name** - Emitted as an example.\n\n### **Methods:**\n \n\n\n### **Slots:**\n - _default_ - The default slot.\n- **example** - An example slot.\n\n### **CSS Properties:**\n - **--example** - An example CSS custom property. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.",
9713
- "doc-url": "",
9714
- "attributes": [
9715
- { "name": "master-id", "value": { "type": "string" } },
9716
- {
9717
- "name": "default-uri",
9718
- "value": { "type": "string", "default": "''" }
9719
- },
9720
- {
9721
- "name": "active",
9722
- "value": { "type": "string", "default": "''" }
9723
- },
9724
- { "name": "split", "value": { "type": "number" } },
9725
- {
9726
- "name": "split-min",
9727
- "value": { "type": "number", "default": "60" }
9728
- },
9729
- { "name": "split-min-secondary", "value": { "type": "number" } },
9730
- { "name": "split-max", "value": { "type": "number" } },
9731
- {
9732
- "name": "primary-caption",
9733
- "value": { "type": "string", "default": "'Navigation'" }
9734
- },
9735
- {
9736
- "name": "secondary-caption",
9737
- "value": { "type": "string", "default": "'Content'" }
9738
- },
9739
- {
9740
- "name": "no-prefetch",
9741
- "value": { "type": "boolean", "default": "false" }
9742
- },
9743
- {
9744
- "name": "no-cache",
9745
- "value": { "type": "boolean", "default": "false" }
9746
- },
9747
- { "name": "local-storage", "value": { "type": "boolean" } },
9748
- { "name": "store-key", "value": { "type": "string" } },
9749
- {
9750
- "name": "store-ttl",
9751
- "value": { "type": "number", "default": "0" }
9752
- },
9753
- {
9754
- "name": "padded",
9755
- "value": { "type": "boolean", "default": "false" }
9756
- },
9757
- {
9758
- "name": "fetch-style",
9759
- "value": { "type": "string", "default": "\"\"" }
9760
- },
9761
- {
9762
- "name": "full-width",
9763
- "value": { "type": "boolean", "default": "false" }
9764
- },
9765
- {
9766
- "name": "padded-right",
9767
- "value": { "type": "boolean", "default": "false" }
9768
- },
9769
- { "name": "monitor", "value": { "type": "string" } },
9770
- { "name": "caption", "value": { "type": "string" } },
9771
- { "name": "description", "value": { "type": "string" } }
9772
- ],
9773
- "slots": [
9774
- { "name": "", "description": "The default slot." },
9775
- { "name": "example", "description": "An example slot." }
9776
- ],
9777
- "events": [
9778
- { "name": "zn-event-name", "description": "Emitted as an example." }
9779
- ],
9780
- "js": {
9781
- "properties": [
9782
- { "name": "masterId", "type": "string" },
9783
- { "name": "defaultUri", "type": "string" },
9784
- { "name": "_current", "type": "string" },
9785
- { "name": "_split", "type": "number" },
9786
- { "name": "_splitMin", "type": "number" },
9787
- { "name": "_splitMinSecondary", "type": "number" },
9788
- { "name": "_splitMax", "type": "number" },
9789
- { "name": "primaryCaption", "type": "string" },
9790
- { "name": "secondaryCaption", "type": "string" },
9791
- { "name": "noPrefetch", "type": "boolean" },
9792
- { "name": "noCache", "type": "boolean" },
9793
- { "name": "localStorage", "type": "boolean" },
9794
- { "name": "storeKey", "type": "string" },
9795
- { "name": "storeTtl", "type": "number" },
9796
- { "name": "padded", "type": "boolean" },
9797
- { "name": "fetchStyle", "type": "string" },
9798
- { "name": "fullWidth", "type": "boolean" },
9799
- { "name": "paddedRight", "type": "boolean" },
9800
- { "name": "monitor", "type": "string" },
9801
- { "name": "caption", "type": "string" },
9802
- { "name": "description", "type": "string" },
9803
- { "name": "reRegisterTabs" },
9804
- { "name": "_registerTabs" }
9805
- ],
9806
- "events": [
9807
- {
9808
- "name": "zn-event-name",
9809
- "description": "Emitted as an example."
9810
- }
9811
- ]
9812
- }
9813
- },
9814
9814
  {
9815
9815
  "name": "zn-theme-editor",
9816
9816
  "description": "A theme editor: slotted form controls drive a live preview frame,\nwith a toolbar for the preview's light/dark mode and device width.\n---\n\n\n### **Events:**\n - **zn-theme-change** - Emitted when the values, mode or device change.\n- **zn-theme-submit** - Emitted on submit (button click), carrying the current values. With `action` set, only fires after a successful save.\n- **zn-error** - Emitted when a save fails. Also seen for preview render failures: the frame's zn-error is composed and not stopped, so it bubbles out through the editor too.\n\n### **Slots:**\n - _default_ - Ungrouped theme controls, rendered above any sections. Controls assigned `slot=\"<name>\"` matching a `sections` entry (or, when nested, a `groups` entry) render inside that section/group instead. Harvesting and change detection walk every slot's full assigned subtree, not just direct children. With `sections` left unset, the structure is instead derived from the controls' own attributes: `group=\"<label>\"` becomes a tab and `category=\"<label>\"` a collapsible within it, and the control is slotted into that collapsible automatically. Either attribute works alone - a control with only `group` sits directly in its tab, and one with only `category` becomes its own top-level section. Setting `sections` explicitly disables the derivation entirely.\n- **toolbar** - Actions in the toolbar, right-aligned beside the device controls. Where a save button belongs.\n- **footer** - Actions pinned beneath the controls. The built-in submit button lives in the toolbar, not here.\n\n### **CSS Properties:**\n - **--zn-theme-editor-controls-width** - Width of the controls column. _(default: undefined)_\n\n### **CSS Parts:**\n - **base** - The component's base wrapper.\n- **controls** - The left-hand controls column, full height.\n- **controls-header** - The controls column's header row: `controls-caption` on the left, the light/dark mode toggle on the right.\n- **toolbar** - The preview column's header row: `preview-caption` on the left, the device switcher (and sources/submit) on the right. Spans the preview column only.\n- **section** - A rendered section's or group's collapsible (`section-layout=\"collapsible\"`, or any nested group).\n- **footer** - The footer wrapper beneath the controls.\n- **preview** - The preview column.\n- **error** - The inline error strip.\n- **preview__base** - The frame's base wrapper (forwarded from zn-preview-frame).\n- **preview__stage** - The frame's device-width wrapper (forwarded from zn-preview-frame).\n- **preview__iframe** - The frame's iframe (forwarded from zn-preview-frame).\n- **preview__error** - The frame's own error overlay (forwarded from zn-preview-frame).",
package/dist/zn.d.ts CHANGED
@@ -10556,6 +10556,12 @@ declare module "components/preview-frame/preview-frame.component" {
10556
10556
  * be reached from here to cancel its own handlers, so this blocks pointer
10557
10557
  * input entirely — hover goes with it. Scrolling doesn't: an overflowing
10558
10558
  * page is scrolled by the panel rather than by the frame (see _contentHeight).
10559
+ *
10560
+ * Set, the frame becomes a real viewport instead: it stays the panel's own
10561
+ * height and the embed scrolls itself, so there is one scrollbar rather than
10562
+ * a panel scrolling an oversized frame, and the embed's viewport-relative
10563
+ * layout (`100vh`, `position: fixed`, sticky headers) sizes to what's on
10564
+ * screen. _contentHeight is ignored while this is set.
10559
10565
  */
10560
10566
  interactive: boolean;
10561
10567
  frame: HTMLIFrameElement;
@@ -10567,7 +10573,8 @@ declare module "components/preview-frame/preview-frame.component" {
10567
10573
  * height rather than the panel's, so the page never scrolls inside the frame
10568
10574
  * — the panel scrolls instead, which is what makes an overflowing preview
10569
10575
  * reachable while pointer input to the frame is blocked. 0 = unknown, and the
10570
- * frame falls back to filling the panel.
10576
+ * frame falls back to filling the panel. Kept up to date either way, but only
10577
+ * laid out when the frame is inert: an `interactive` frame scrolls itself.
10571
10578
  */
10572
10579
  private _contentHeight;
10573
10580
  private _generation;
package/dist/zn.min.js CHANGED
@@ -5478,7 +5478,7 @@ ${n}`:""}</li></ol>`};return t(0)}parseTable(o,t){this.index++;let r=[],n=[];for
5478
5478
  ${this._renderInspector()}
5479
5479
  <slot name="config" class="declarations" @slotchange="${this._registerSlottedTemplates}"></slot>
5480
5480
  </div>
5481
- `}};At.styles=E(y_),At.dependencies={"zn-collapsible":oa,"zn-icon":ct,"zn-input":ke,"zn-page-palette-item":Rf,"zn-page-section-card":Of},c([p()],At.prototype,"name",2),c([p({reflect:!0})],At.prototype,"form",2),c([p()],At.prototype,"config",2),c([p({reflect:!0})],At.prototype,"heading",2),c([p({reflect:!0})],At.prototype,"subheading",2),c([p({attribute:!1})],At.prototype,"sectionTypes",2),c([p({type:Boolean,reflect:!0,attribute:"palette-collapsed"})],At.prototype,"paletteCollapsed",2),c([p({type:Boolean,reflect:!0,attribute:"inspector-collapsed"})],At.prototype,"inspectorCollapsed",2),c([p({attribute:"auto-save",converter:{fromAttribute:t=>{if(t===null)return null;let r=parseFloat(t);return Number.isFinite(r)&&r>0?r:uq},toAttribute:t=>t===null?null:String(t)}})],At.prototype,"autoSave",2),c([O()],At.prototype,"_state",2),c([O()],At.prototype,"_selectedId",2),c([O()],At.prototype,"_search",2),c([O()],At.prototype,"_pickerIndex",2),c([O()],At.prototype,"_dragOverIndex",2),c([O()],At.prototype,"_slotDragOver",2),c([O()],At.prototype,"_slotPicker",2),c([O()],At.prototype,"_form",2),c([K("config")],At.prototype,"handleConfigChange",1),c([K("sectionTypes")],At.prototype,"handleSectionTypesChange",1),c([O()],At.prototype,"_lastSavedAt",2),c([O()],At.prototype,"_justSaved",2),c([O()],At.prototype,"_statusNow",2),c([O()],At.prototype,"_restorePrompt",2);var fq=At;At.define("zn-page-builder");var w_=S`:host{display:block}:host([fill]){flex:1 1 auto;min-height:0}.preview{position:relative;width:100%;overflow-x:hidden;overflow-y:auto;background:radial-gradient(circle,rgba(var(--zn-text),var(--zn-preview-frame-dot-opacity,0.08)) 1px,transparent 1px) 0 0/var(--zn-preview-frame-dot-spacing,20px) var(--zn-preview-frame-dot-spacing,20px),rgb(var(--zn-body))}:host([backdrop=panel]) .preview{background:rgb(var(--zn-panel))}.preview__stage{height:100%;max-width:100%;margin:0 auto;overflow:hidden}iframe{display:block;width:100%;border:0;pointer-events:none;background:rgb(var(--zn-body))}:host([interactive]) iframe{pointer-events:auto}.preview__error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:1rem;text-align:center;background:rgba(0,0,0,0.65);color:#fff}`;var __={desktop:"100%",tablet:"768px",mobile:"390px"},ye=class extends C{constructor(){super(...arguments);this.src="";this.frameOrigin="";this.dataUri="";this.watch="form[data-auto-save]";this.refreshOn="form";this.debounce=400;this.zoom=1;this.minHeight=480;this.fill=!1;this.device="desktop";this.backdrop="dots";this.interactive=!1;this.error="";this._contentHeight=0;this._generation=0;this._watchedForms=new Set;this._refreshForms=new Set;this._debounceTimers=new Map;this._formObserver=new Lt(this,{target:null,config:{subtree:!0,childList:!0},callback:()=>this._attachForms()});this._onMessage=t=>{if(t.origin!==this.frameOrigin||!this.frame||t.source!==this.frame.contentWindow)return;let r=t.data;switch(r?.type){case"hp-preview:ready":this._sendConfig().then(()=>this._postTheme());break;case"hp-preview:rendered":this.error="",this._applyContentHeight(r.height);break;case"hp-preview:height":this._applyContentHeight(r.height);break;case"hp-preview:error":this._fail(String(r.message??"Preview failed to render"));break}};this._onFrameLoad=()=>{this._contentObserver?.disconnect(),this._contentObserver=void 0;let t;try{t=this.frame?.contentDocument?.documentElement}catch{return}if(!t)return;let r=()=>{let n=t.getBoundingClientRect().height;n>(this.frame?.clientHeight??0)&&this._applyContentHeight(n)};r(),this._contentObserver=new ResizeObserver(r),this._contentObserver.observe(t)};this._onShellSave=()=>{this._sendConfig()};this._onChange=t=>{let r=t.currentTarget,n=this._debounceTimers.get(r);n&&window.clearTimeout(n),this._debounceTimers.set(r,window.setTimeout(()=>{this._debounceTimers.delete(r),r.isConnected&&r.requestSubmit()},this.debounce))};this._onSubmit=t=>{t.preventDefault(),t.stopImmediatePropagation(),this._save(t.currentTarget)}}connectedCallback(){super.connectedCallback(),window.addEventListener("message",this._onMessage),this._attachForms();let t=this.getRootNode();t instanceof Document?this._formObserver.observe(t.body):t instanceof ShadowRoot&&this._formObserver.observe(t)}willUpdate(t){t.has("src")&&this._resetContentHeight()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("message",this._onMessage),this._contentObserver?.disconnect(),this._contentObserver=void 0,this._watchedForms.forEach(t=>this._detachForm(t)),this._watchedForms.clear(),this._refreshForms.forEach(t=>t.removeEventListener("complete",this._onShellSave)),this._refreshForms.clear()}_applyContentHeight(t){let r=Math.round(Number(t));!Number.isFinite(r)||r<=0||(this._contentHeight=r)}_resetContentHeight(){this._contentHeight=0,this._contentObserver?.disconnect(),this._contentObserver=void 0}refresh(){return this._sendConfig()}setTheme(t){this._theme=t,this._postTheme()}_postTheme(){!this._theme||!this.frameOrigin||this.frame?.contentWindow?.postMessage({type:"hp-preview:theme",...this._theme},this.frameOrigin)}async _sendConfig(){if(!this.dataUri||!this.frameOrigin)return;let t=++this._generation;try{let r=await fetch(this.dataUri,{credentials:"same-origin",headers:{"x-kx-fetch-style":"download"}});if(!r.ok)throw new Error(await r.text()||r.statusText);let n=await r.json();if(t!==this._generation)return;this.frame?.contentWindow?.postMessage({type:"hp-preview:config",...n},this.frameOrigin)}catch(r){t===this._generation&&this._fail(r instanceof Error?r.message:String(r))}}_attachForms(){let t=this.getRootNode(),r=new Set;t.querySelectorAll(this.watch).forEach(a=>{a instanceof HTMLFormElement&&r.add(a)}),this._watchedForms.forEach(a=>{r.has(a)||(this._detachForm(a),this._watchedForms.delete(a))}),r.forEach(a=>{this._watchedForms.has(a)||(this._watchedForms.add(a),a.addEventListener("submit",this._onSubmit,{capture:!0}),a.addEventListener("zn-change",this._onChange),a.addEventListener("zn-input",this._onChange),a.addEventListener("change",this._onChange))});let n=new Set;this.refreshOn&&t.querySelectorAll(this.refreshOn).forEach(a=>{a instanceof HTMLFormElement&&!r.has(a)&&n.add(a)}),this._refreshForms.forEach(a=>{n.has(a)||(a.removeEventListener("complete",this._onShellSave),this._refreshForms.delete(a))}),n.forEach(a=>{this._refreshForms.has(a)||(this._refreshForms.add(a),a.addEventListener("complete",this._onShellSave))})}_detachForm(t){t.removeEventListener("submit",this._onSubmit,{capture:!0}),t.removeEventListener("zn-change",this._onChange),t.removeEventListener("zn-input",this._onChange),t.removeEventListener("change",this._onChange);let r=this._debounceTimers.get(t);r&&window.clearTimeout(r),this._debounceTimers.delete(t)}async _save(t){try{let r=await fetch(t.getAttribute("action")||"",{method:"POST",credentials:"same-origin",headers:{"x-kx-fetch-style":"download"},body:new FormData(t)}),n=r.headers.get("x-kubex-alert-danger");if(n)throw new Error(n);if(!r.ok)throw new Error(await r.text()||r.statusText);await this._sendConfig()}catch(r){this._fail(r instanceof Error?r.message:String(r))}}_fail(t){this.error=t,this.emit("zn-error",{detail:{message:t}})}render(){let t=this.zoom>0&&this.zoom<=1?this.zoom:1,r=this.error?0:this._contentHeight,n=this.fill?{width:"100%",height:r?`max(${r}px, 100%)`:"100%"}:{width:`${100/t}%`,height:`${Math.max(r,this.minHeight/t)}px`,transform:`scale(${t})`,transformOrigin:"0 0"},a=this.fill?{height:"100%",minHeight:`${this.minHeight}px`}:{height:`${this.minHeight}px`},s={width:__[this.device]??__.desktop};return r&&(s.height=this.fill?`max(${r}px, 100%)`:`${Math.max(r*t,this.minHeight)}px`),u`
5481
+ `}};At.styles=E(y_),At.dependencies={"zn-collapsible":oa,"zn-icon":ct,"zn-input":ke,"zn-page-palette-item":Rf,"zn-page-section-card":Of},c([p()],At.prototype,"name",2),c([p({reflect:!0})],At.prototype,"form",2),c([p()],At.prototype,"config",2),c([p({reflect:!0})],At.prototype,"heading",2),c([p({reflect:!0})],At.prototype,"subheading",2),c([p({attribute:!1})],At.prototype,"sectionTypes",2),c([p({type:Boolean,reflect:!0,attribute:"palette-collapsed"})],At.prototype,"paletteCollapsed",2),c([p({type:Boolean,reflect:!0,attribute:"inspector-collapsed"})],At.prototype,"inspectorCollapsed",2),c([p({attribute:"auto-save",converter:{fromAttribute:t=>{if(t===null)return null;let r=parseFloat(t);return Number.isFinite(r)&&r>0?r:uq},toAttribute:t=>t===null?null:String(t)}})],At.prototype,"autoSave",2),c([O()],At.prototype,"_state",2),c([O()],At.prototype,"_selectedId",2),c([O()],At.prototype,"_search",2),c([O()],At.prototype,"_pickerIndex",2),c([O()],At.prototype,"_dragOverIndex",2),c([O()],At.prototype,"_slotDragOver",2),c([O()],At.prototype,"_slotPicker",2),c([O()],At.prototype,"_form",2),c([K("config")],At.prototype,"handleConfigChange",1),c([K("sectionTypes")],At.prototype,"handleSectionTypesChange",1),c([O()],At.prototype,"_lastSavedAt",2),c([O()],At.prototype,"_justSaved",2),c([O()],At.prototype,"_statusNow",2),c([O()],At.prototype,"_restorePrompt",2);var fq=At;At.define("zn-page-builder");var w_=S`:host{display:block}:host([fill]){flex:1 1 auto;min-height:0}.preview{position:relative;width:100%;overflow-x:hidden;overflow-y:auto;background:radial-gradient(circle,rgba(var(--zn-text),var(--zn-preview-frame-dot-opacity,0.08)) 1px,transparent 1px) 0 0/var(--zn-preview-frame-dot-spacing,20px) var(--zn-preview-frame-dot-spacing,20px),rgb(var(--zn-body))}:host([backdrop=panel]) .preview{background:rgb(var(--zn-panel))}.preview__stage{height:100%;max-width:100%;margin:0 auto;overflow:hidden}iframe{display:block;width:100%;border:0;pointer-events:none;background:rgb(var(--zn-body))}:host([interactive]) iframe{pointer-events:auto}.preview__error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:1rem;text-align:center;background:rgba(0,0,0,0.65);color:#fff}`;var __={desktop:"100%",tablet:"768px",mobile:"390px"},ye=class extends C{constructor(){super(...arguments);this.src="";this.frameOrigin="";this.dataUri="";this.watch="form[data-auto-save]";this.refreshOn="form";this.debounce=400;this.zoom=1;this.minHeight=480;this.fill=!1;this.device="desktop";this.backdrop="dots";this.interactive=!1;this.error="";this._contentHeight=0;this._generation=0;this._watchedForms=new Set;this._refreshForms=new Set;this._debounceTimers=new Map;this._formObserver=new Lt(this,{target:null,config:{subtree:!0,childList:!0},callback:()=>this._attachForms()});this._onMessage=t=>{if(t.origin!==this.frameOrigin||!this.frame||t.source!==this.frame.contentWindow)return;let r=t.data;switch(r?.type){case"hp-preview:ready":this._sendConfig().then(()=>this._postTheme());break;case"hp-preview:rendered":this.error="",this._applyContentHeight(r.height);break;case"hp-preview:height":this._applyContentHeight(r.height);break;case"hp-preview:error":this._fail(String(r.message??"Preview failed to render"));break}};this._onFrameLoad=()=>{this._contentObserver?.disconnect(),this._contentObserver=void 0;let t;try{t=this.frame?.contentDocument?.documentElement}catch{return}if(!t)return;let r=()=>{let n=t.getBoundingClientRect().height;n>(this.frame?.clientHeight??0)&&this._applyContentHeight(n)};r(),this._contentObserver=new ResizeObserver(r),this._contentObserver.observe(t)};this._onShellSave=()=>{this._sendConfig()};this._onChange=t=>{let r=t.currentTarget,n=this._debounceTimers.get(r);n&&window.clearTimeout(n),this._debounceTimers.set(r,window.setTimeout(()=>{this._debounceTimers.delete(r),r.isConnected&&r.requestSubmit()},this.debounce))};this._onSubmit=t=>{t.preventDefault(),t.stopImmediatePropagation(),this._save(t.currentTarget)}}connectedCallback(){super.connectedCallback(),window.addEventListener("message",this._onMessage),this._attachForms();let t=this.getRootNode();t instanceof Document?this._formObserver.observe(t.body):t instanceof ShadowRoot&&this._formObserver.observe(t)}willUpdate(t){t.has("src")&&this._resetContentHeight()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("message",this._onMessage),this._contentObserver?.disconnect(),this._contentObserver=void 0,this._watchedForms.forEach(t=>this._detachForm(t)),this._watchedForms.clear(),this._refreshForms.forEach(t=>t.removeEventListener("complete",this._onShellSave)),this._refreshForms.clear()}_applyContentHeight(t){let r=Math.round(Number(t));!Number.isFinite(r)||r<=0||(this._contentHeight=r)}_resetContentHeight(){this._contentHeight=0,this._contentObserver?.disconnect(),this._contentObserver=void 0}refresh(){return this._sendConfig()}setTheme(t){this._theme=t,this._postTheme()}_postTheme(){!this._theme||!this.frameOrigin||this.frame?.contentWindow?.postMessage({type:"hp-preview:theme",...this._theme},this.frameOrigin)}async _sendConfig(){if(!this.dataUri||!this.frameOrigin)return;let t=++this._generation;try{let r=await fetch(this.dataUri,{credentials:"same-origin",headers:{"x-kx-fetch-style":"download"}});if(!r.ok)throw new Error(await r.text()||r.statusText);let n=await r.json();if(t!==this._generation)return;this.frame?.contentWindow?.postMessage({type:"hp-preview:config",...n},this.frameOrigin)}catch(r){t===this._generation&&this._fail(r instanceof Error?r.message:String(r))}}_attachForms(){let t=this.getRootNode(),r=new Set;t.querySelectorAll(this.watch).forEach(a=>{a instanceof HTMLFormElement&&r.add(a)}),this._watchedForms.forEach(a=>{r.has(a)||(this._detachForm(a),this._watchedForms.delete(a))}),r.forEach(a=>{this._watchedForms.has(a)||(this._watchedForms.add(a),a.addEventListener("submit",this._onSubmit,{capture:!0}),a.addEventListener("zn-change",this._onChange),a.addEventListener("zn-input",this._onChange),a.addEventListener("change",this._onChange))});let n=new Set;this.refreshOn&&t.querySelectorAll(this.refreshOn).forEach(a=>{a instanceof HTMLFormElement&&!r.has(a)&&n.add(a)}),this._refreshForms.forEach(a=>{n.has(a)||(a.removeEventListener("complete",this._onShellSave),this._refreshForms.delete(a))}),n.forEach(a=>{this._refreshForms.has(a)||(this._refreshForms.add(a),a.addEventListener("complete",this._onShellSave))})}_detachForm(t){t.removeEventListener("submit",this._onSubmit,{capture:!0}),t.removeEventListener("zn-change",this._onChange),t.removeEventListener("zn-input",this._onChange),t.removeEventListener("change",this._onChange);let r=this._debounceTimers.get(t);r&&window.clearTimeout(r),this._debounceTimers.delete(t)}async _save(t){try{let r=await fetch(t.getAttribute("action")||"",{method:"POST",credentials:"same-origin",headers:{"x-kx-fetch-style":"download"},body:new FormData(t)}),n=r.headers.get("x-kubex-alert-danger");if(n)throw new Error(n);if(!r.ok)throw new Error(await r.text()||r.statusText);await this._sendConfig()}catch(r){this._fail(r instanceof Error?r.message:String(r))}}_fail(t){this.error=t,this.emit("zn-error",{detail:{message:t}})}render(){let t=this.zoom>0&&this.zoom<=1?this.zoom:1,r=this.error||this.interactive?0:this._contentHeight,n=this.fill?{width:"100%",height:r?`max(${r}px, 100%)`:"100%"}:{width:`${100/t}%`,height:`${Math.max(r,this.minHeight/t)}px`,transform:`scale(${t})`,transformOrigin:"0 0"},a=this.fill?{height:"100%",minHeight:`${this.minHeight}px`}:{height:`${this.minHeight}px`},s={width:__[this.device]??__.desktop};return r&&(s.height=this.fill?`max(${r}px, 100%)`:`${Math.max(r*t,this.minHeight)}px`),u`
5482
5482
  <div part="base" class="preview" style="${_r(a)}">
5483
5483
  <div part="stage" class="preview__stage" style="${_r(s)}">
5484
5484
  <iframe part="iframe"
@@ -5490,7 +5490,7 @@ ${n}`:""}</li></ol>`};return t(0)}parseTable(o,t){this.index++;let r=[],n=[];for
5490
5490
  </div>
5491
5491
  ${this.error?u`
5492
5492
  <div part="error" class="preview__error">${this.error}</div>`:""}
5493
- </div>`}};ye.styles=E(w_),c([p()],ye.prototype,"src",2),c([p({attribute:"frame-origin"})],ye.prototype,"frameOrigin",2),c([p({attribute:"data-uri"})],ye.prototype,"dataUri",2),c([p()],ye.prototype,"watch",2),c([p({attribute:"refresh-on"})],ye.prototype,"refreshOn",2),c([p({type:Number})],ye.prototype,"debounce",2),c([p({type:Number})],ye.prototype,"zoom",2),c([p({type:Number,attribute:"min-height"})],ye.prototype,"minHeight",2),c([p({type:Boolean})],ye.prototype,"fill",2),c([p({reflect:!0})],ye.prototype,"device",2),c([p({reflect:!0})],ye.prototype,"backdrop",2),c([p({type:Boolean,reflect:!0})],ye.prototype,"interactive",2),c([F("iframe")],ye.prototype,"frame",2),c([O()],ye.prototype,"error",2),c([O()],ye.prototype,"_contentHeight",2);var Ff=ye;ye.define("zn-preview-frame");var k_=S`@keyframes rotate-gradient{from{--rotate:0deg}to{--rotate:360deg}}:host{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-transform:none}:host *,:host *::before,:host *::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgb(var(--zn-border-color))}[hidden]{display:none !important}.width-container,.form-container,.wide-form-container{height:100%;width:100%;margin:var(--zn-default-margin,0 auto)}.width-container{max-width:var(--zn-container)}.form-container{max-width:var(--zn-container-ph)}.wide-form-container{max-width:var(--zn-container-lg)}:host{display:block;--zn-theme-editor-controls-width:343px;height:100%}.editor{--controls-col:var(--zn-theme-editor-controls-width);display:flex;align-items:stretch;height:100%}.editor--controls-collapsed{--controls-col:0px}.editor--controls-collapsed .editor__controls{border-right:none}.editor--controls-collapsed .editor__controls-header,.editor--controls-collapsed .editor__controls-body{padding:0}:host([standalone]) .editor{background:rgb(var(--zn-panel));border:1px solid rgb(var(--zn-border-color));border-radius:8px;overflow:hidden}.editor__toolbar{display:flex;align-items:center;flex:0 0 auto;min-height:44px;gap:var(--zn-spacing-small);padding:var(--zn-spacing-small) var(--zn-spacing-medium);border-bottom:1px solid rgb(var(--zn-border-color))}.editor__toolbar-actions{display:flex;align-items:center;gap:var(--zn-spacing-small);margin-left:auto}.editor__caption{font-size:14px;font-weight:600;line-height:16px;color:rgb(var(--zn-text))}.editor__controls{position:relative;display:flex;flex-direction:column;flex:0 0 var(--controls-col);min-width:0;max-width:100%;background:rgb(var(--zn-panel));border-right:1px solid rgb(var(--zn-border-color));overflow:hidden;transition:flex-basis 0.2s ease}.editor__controls-inner{position:absolute;inset:0;display:flex;flex-direction:column}.editor__controls-header{display:flex;align-items:center;flex:0 0 auto;min-height:44px;padding:var(--zn-spacing-small) var(--zn-spacing-medium)}.editor__controls-body{display:flex;flex-direction:column;gap:var(--zn-spacing-medium);flex:1 1 auto;min-height:0}.editor__main{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0}.editor__fields{display:flex;flex-direction:column;gap:var(--zn-spacing-medium);flex:1 1 auto;min-height:0;overflow-y:auto}::slotted(zn-collapsible){width:100%}.editor__fields--sections-only .editor__section:first-of-type{border-top:none}.editor__section{display:block;border-top:1px solid rgba(var(--zn-border-color),0.5)}.editor__section::part(header){align-items:center !important;padding-block:calc(var(--zn-base-px) * 2) !important}.editor__section::part(caption){font-size:14px;font-weight:600;line-height:16px;color:rgb(var(--zn-text))}.editor__section-fields{display:flex;flex-direction:column;gap:var(--zn-spacing-medium)}.editor__tabs{display:block}.editor__section-slot{padding-top:var(--zn-spacing-sm)}.editor__tab-panel{display:flex;flex-direction:column}.editor__footer{display:flex;flex:0 0 auto;gap:var(--zn-spacing-small);padding-top:var(--zn-spacing-small);border-top:1px solid rgb(var(--zn-border-color));margin-top:auto}.editor__preview{overflow:auto;flex:1 1 auto;min-width:0;min-height:0;display:flex;flex-direction:column}.panel-toggle{position:absolute;top:50%;left:0;z-index:6;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;width:18px;height:44px;padding:0;border:1px solid rgb(var(--zn-border-color));border-left:0;border-radius:0 6px 6px 0;color:rgb(var(--zn-color-muted-text));background:rgb(var(--zn-panel));cursor:pointer;transition:left 0.2s ease,color 0.12s ease,border-color 0.12s ease}.panel-toggle:hover{color:rgb(var(--zn-text));background:rgba(var(--zn-border-color),0.4)}.panel-toggle--tucked{left:var(--zn-spacing-small);border-left:1px solid rgb(var(--zn-border-color));border-radius:6px}.editor__devices{display:flex;border:1px solid rgb(var(--zn-border-color));border-radius:4px;overflow:hidden}.editor__device,.editor__mode{display:flex;align-items:center;justify-content:center;width:30px;height:28px;padding:0;border:0;background:transparent;color:rgb(var(--zn-text));cursor:pointer}.editor__device+.editor__device{border-left:1px solid rgb(var(--zn-border-color))}.editor__device:hover,.editor__mode:hover{background:rgba(var(--zn-border-color),0.5)}.editor__device[aria-pressed=true]{background:rgb(var(--zn-primary));color:white}.editor__mode{border:1px solid rgb(var(--zn-border-color));border-radius:4px;margin-left:auto}.editor__sources{width:auto;max-width:160px}.editor__sources::part(form-control-label){position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.editor__error{padding:var(--zn-spacing-small);margin-bottom:var(--zn-spacing-small);border-radius:4px;background:rgba(var(--zn-color-error),0.1);border:1px solid rgb(var(--zn-color-error));color:rgb(var(--zn-color-error));font-size:0.8125rem}@media (max-width:768px){:host{height:auto}.editor{flex-direction:column;height:auto}.editor__fields,.editor__preview{overflow:visible}.editor__controls{flex-basis:auto;width:100%;border-right:none;border-bottom:1px solid rgb(var(--zn-border-color))}.editor__controls-inner{position:static}.editor--controls-collapsed .editor__controls{display:none}.editor__main{width:100%;padding-top:var(--zn-spacing-medium)}.panel-toggle{top:0;left:50%;width:44px;height:18px;border:1px solid rgb(var(--zn-border-color));border-top:0;border-radius:0 0 6px 6px;transform:translateX(-50%)}.panel-toggle--tucked{top:var(--zn-spacing-small);left:50%;border-top:1px solid rgb(var(--zn-border-color));border-radius:6px}}`;var gq=new Set(["zn-checkbox","zn-toggle"]),bq="(max-width: 768px)",vq=[{id:"desktop",icon:"monitor",label:"Desktop"},{id:"tablet",icon:"tablet",label:"Tablet"},{id:"mobile",icon:"smartphone",label:"Mobile"}];function xq(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var Et=class extends C{constructor(){super(...arguments);this.src="";this.frameOrigin="";this.dataUri="";this.mode="light";this.device="desktop";this.minHeight=480;this.debounce=150;this.action="";this.saveDebounce=1e3;this.sections=[];this.sectionLayout="collapsible";this.sources=[];this.controlsCollapsed=!1;this.standalone=!1;this.controlsCaption="";this.previewCaption="";this.submitLabel="";this.manual=!1;this.error="";this._submitting=!1;this._sourceIndex=0;this.hasSlotController=new Q(this,"footer","[default]");this._saving=!1;this._saveQueued=!1;this._saveWaiters=[];this._narrowQuery=window.matchMedia(bq);this._wasNarrow=!1;this._mounted=!1;this._lastControls=[];this._controlsObserverConfig={childList:!0,subtree:!0};this._controlsObserver=new Lt(this,{target:null,config:this._controlsObserverConfig,callback:()=>this._pushIfControlsChanged()});this._modeValues={light:{},dark:{}};this._suppressDepth=0;this._openedInitialCollapsible=!1;this._onNarrowChange=t=>{t.matches&&!this._wasNarrow&&(this.controlsCollapsed=!0),this._wasNarrow=t.matches};this._onSubmit=()=>{if(!this._submitting){if(this._flushPendingEdit(),this._saveTimer&&(window.clearTimeout(this._saveTimer),this._saveTimer=void 0),!this.action){this.emit("zn-theme-submit",{detail:{values:this.values}});return}this._submitting=!0,this._awaitSave().then(t=>{t&&this.emit("zn-theme-submit",{detail:{values:this.values}})}).finally(()=>{this._submitting=!1})}};this._onControlChange=()=>{this._suppressDepth>0||(this._pushTimer&&window.clearTimeout(this._pushTimer),this._pushTimer=window.setTimeout(()=>{this._pushTimer=void 0,this._harvestInto(this.mode),this._push(),this._queueSave()},this.debounce))};this._onSlotChange=()=>{this._pushIfControlsChanged()};this._setDevice=t=>{this.device!==t&&(this.device=t,this._announce())};this._toggleMode=()=>{this._flushPendingEdit(),this.mode=this.mode==="dark"?"light":"dark",this._writeBack(),this._push()};this._onFrameError=t=>{this.error=t.detail.message??"Preview failed to render"};this._onSourceChange=t=>{let r=Number(t.target.value);Number.isNaN(r)||(this._sourceIndex=r)}}get values(){return{light:{...this._modeValues.light},dark:{...this._modeValues.dark}}}get activeValues(){return{...this._modeValues[this.mode]}}_controlSlots(){return[this.controlsSlot,...Array.from(this.sectionSlots??[])].filter(t=>!!t)}_harvestNamed(){let t=[];for(let r of this._controlSlots()){let n=r.assignedElements({flatten:!0});for(let a of n){let s=[a,...Array.from(a.querySelectorAll("[name]"))];for(let l of s){let d=l;!d.getAttribute?.("name")||d.disabled||t.push({name:d.getAttribute("name"),control:d})}}}return t}_hasAssignedControls(t){return Array.from(this.children).some(r=>r.getAttribute("slot")===t)}_isBooleanControl(t){return gq.has(t.tagName.toLowerCase())||t.type==="checkbox"}_readControlValue(t){return this._isBooleanControl(t)?!!t.checked:t.value}_seed(){for(let{name:t,control:r}of this._harvestNamed()){if(t in this._modeValues.light)continue;let n=this._readControlValue(r);this._modeValues.light[t]=n;let a=r.getAttribute("dark-value");this._modeValues.dark[t]=a===null?n:this._isBooleanControl(r)?a==="1"||a==="true":a}}_writeBack(t=this.mode){let r=!1;for(let{name:n,control:a}of this._harvestNamed()){if(!(n in this._modeValues[t]))continue;let s=this._modeValues[t][n],l=this._isBooleanControl(a),d=l?!!a.checked:a.value;(l?d===s:String(d)===String(s))||(r=!0,l?a.checked=!!s:a.value=s)}r&&(this._suppressDepth++,window.setTimeout(()=>{this._suppressDepth--},0))}_harvestInto(t){for(let{name:r,control:n}of this._harvestNamed())this._modeValues[t][r]=this._readControlValue(n)}connectedCallback(){super.connectedCallback(),this._controlsObserver.observe(this),this._narrowQuery.addEventListener("change",this._onNarrowChange),this._onNarrowChange(this._narrowQuery)}disconnectedCallback(){this._pushTimer&&window.clearTimeout(this._pushTimer),this._saveTimer&&window.clearTimeout(this._saveTimer),this._narrowQuery.removeEventListener("change",this._onNarrowChange),super.disconnectedCallback()}firstUpdated(){this._pushIfControlsChanged()}_push(){this.error="",this.frame?.setTheme({mode:this.mode,values:this.activeValues}),this._announce()}_queueSave(){!this.action||this.manual||(this._saveTimer&&window.clearTimeout(this._saveTimer),this._saveTimer=window.setTimeout(()=>{this._saveTimer=void 0,this._save()},this.saveDebounce))}async _save(){if(this._saving){this._saveQueued=!0;return}this._saving=!0;let t=!1;try{let r=new FormData;for(let a of["light","dark"])for(let[s,l]of Object.entries(this._modeValues[a]))r.append(`${a}[${s}]`,typeof l=="boolean"?l?"1":"":String(l??""));let n=await fetch(this.action,{method:"POST",credentials:"same-origin",body:r});if(!n.ok)throw new Error(await n.text()||n.statusText);this.error="",t=!0}catch(r){this._fail(r instanceof Error?r.message:String(r))}finally{if(this._saving=!1,this._saveQueued)this._saveQueued=!1,this._save();else{let r=this._saveWaiters;this._saveWaiters=[],r.forEach(n=>n(t))}}}_awaitSave(){return new Promise(t=>{this._saveWaiters.push(t),this._save()})}_pushIfControlsChanged(){this.requestUpdate();let t=this._harvestNamed().map(({control:n})=>n);(!this._mounted||t.length!==this._lastControls.length||t.some((n,a)=>n!==this._lastControls[a]))&&(this._flushPendingEdit(),this._mounted=!0,this._lastControls=t,this._seed(),this._writeBack(),this._push())}_flushPendingEdit(){this._pushTimer&&(window.clearTimeout(this._pushTimer),this._pushTimer=void 0,this._harvestInto(this.mode),this._queueSave())}_announce(){this.emit("zn-theme-change",{detail:{values:this.values,mode:this.mode,device:this.device}})}_fail(t){this.error=t,this.emit("zn-error",{detail:{message:t}})}_sourcesSafe(){return Array.isArray(this.sources)?this.sources:[]}_frameSrc(){let t=this._sourcesSafe();return t.length>0?(t[this._sourceIndex]??t[0]).src:this.src}_sectionsSafe(){return Array.isArray(this.sections)?this.sections:[]}_groupsFor(t){return Array.isArray(t?.groups)?t.groups:[]}_hasStructureAttributes(){return Array.from(this.children).some(t=>t.hasAttribute("group")||t.hasAttribute("category"))}_usesDerivedSections(){return this._sectionsSafe().length===0&&this._hasStructureAttributes()}_derive(){let t=new Set,r=l=>{let d=xq(l)||"group",h=d;for(let m=2;t.has(h);m++)h=`${d}-${m}`;return t.add(h),h},n=new Map,a=new Map,s=new Map;for(let l of Array.from(this.children)){let d=l.getAttribute("group")?.trim()??"",h=l.getAttribute("category")?.trim()??"";if(!d&&!h)continue;let m=d||h,f=n.get(m);if(f||(f={name:r(m),caption:m,groups:[]},n.set(m,f)),!d||!h){s.set(l,f.name);continue}let b=`${m}\0${h}`,g=a.get(b);g||(g=r(`${m}-${h}`),a.set(b,g),f.groups.push({name:g,caption:h})),s.set(l,g)}return{sections:Array.from(n.values()),assignments:s}}_effectiveSections(){return this._usesDerivedSections()?this._derive().sections:this._sectionsSafe()}_assignDerivedSlots(){if(this._usesDerivedSections())for(let[t,r]of this._derive().assignments)t.getAttribute("slot")!==r&&t.setAttribute("slot",r)}willUpdate(t){super.willUpdate(t),this._assignDerivedSlots()}_hasNestedGroups(){return this._effectiveSections().some(t=>this._groupsFor(t).length>0)}_visibleGroups(t){return this._groupsFor(t).filter(r=>this._hasAssignedControls(r.name))}_visibleSections(){let t=this._effectiveSections();return this._hasNestedGroups()?t.filter(r=>this._visibleGroups(r).length>0):t.filter(r=>this._hasAssignedControls(r.name))}_renderSections(){return this._visibleSections().map(t=>u`
5493
+ </div>`}};ye.styles=E(w_),c([p()],ye.prototype,"src",2),c([p({attribute:"frame-origin"})],ye.prototype,"frameOrigin",2),c([p({attribute:"data-uri"})],ye.prototype,"dataUri",2),c([p()],ye.prototype,"watch",2),c([p({attribute:"refresh-on"})],ye.prototype,"refreshOn",2),c([p({type:Number})],ye.prototype,"debounce",2),c([p({type:Number})],ye.prototype,"zoom",2),c([p({type:Number,attribute:"min-height"})],ye.prototype,"minHeight",2),c([p({type:Boolean})],ye.prototype,"fill",2),c([p({reflect:!0})],ye.prototype,"device",2),c([p({reflect:!0})],ye.prototype,"backdrop",2),c([p({type:Boolean,reflect:!0})],ye.prototype,"interactive",2),c([F("iframe")],ye.prototype,"frame",2),c([O()],ye.prototype,"error",2),c([O()],ye.prototype,"_contentHeight",2);var Ff=ye;ye.define("zn-preview-frame");var k_=S`@keyframes rotate-gradient{from{--rotate:0deg}to{--rotate:360deg}}:host{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-transform:none}:host *,:host *::before,:host *::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgb(var(--zn-border-color))}[hidden]{display:none !important}.width-container,.form-container,.wide-form-container{height:100%;width:100%;margin:var(--zn-default-margin,0 auto)}.width-container{max-width:var(--zn-container)}.form-container{max-width:var(--zn-container-ph)}.wide-form-container{max-width:var(--zn-container-lg)}:host{display:block;--zn-theme-editor-controls-width:343px;height:100%}.editor{--controls-col:var(--zn-theme-editor-controls-width);display:flex;align-items:stretch;height:100%}.editor--controls-collapsed{--controls-col:0px}.editor--controls-collapsed .editor__controls{border-right:none}.editor--controls-collapsed .editor__controls-header,.editor--controls-collapsed .editor__controls-body{padding:0}:host([standalone]) .editor{background:rgb(var(--zn-panel));border:1px solid rgb(var(--zn-border-color));border-radius:8px;overflow:hidden}.editor__toolbar{display:flex;align-items:center;flex:0 0 auto;min-height:44px;gap:var(--zn-spacing-small);padding:var(--zn-spacing-small) var(--zn-spacing-medium);border-bottom:1px solid rgb(var(--zn-border-color))}.editor__toolbar-actions{display:flex;align-items:center;gap:var(--zn-spacing-small);margin-left:auto}.editor__caption{font-size:14px;font-weight:600;line-height:16px;color:rgb(var(--zn-text))}.editor__controls{position:relative;display:flex;flex-direction:column;flex:0 0 var(--controls-col);min-width:0;max-width:100%;background:rgb(var(--zn-panel));border-right:1px solid rgb(var(--zn-border-color));overflow:hidden;transition:flex-basis 0.2s ease}.editor__controls-inner{position:absolute;inset:0;display:flex;flex-direction:column}.editor__controls-header{display:flex;align-items:center;flex:0 0 auto;min-height:44px;padding:var(--zn-spacing-small) var(--zn-spacing-medium)}.editor__controls-body{display:flex;flex-direction:column;gap:var(--zn-spacing-medium);flex:1 1 auto;min-height:0}.editor__main{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0}.editor__fields{display:flex;flex-direction:column;gap:var(--zn-spacing-medium);flex:1 1 auto;min-height:0;overflow-y:auto}::slotted(zn-collapsible){width:100%}.editor__fields--sections-only .editor__section:first-of-type{border-top:none}.editor__section{display:block;border-top:1px solid rgba(var(--zn-border-color),0.5)}.editor__section::part(header){align-items:center !important;padding-block:calc(var(--zn-base-px) * 2) !important}.editor__section::part(caption){font-size:14px;font-weight:600;line-height:16px;color:rgb(var(--zn-text))}.editor__section-fields{display:flex;flex-direction:column;gap:var(--zn-spacing-medium)}.editor__tabs{display:block}.editor__section-slot{padding-top:var(--zn-spacing-sm)}.editor__tab-panel{display:flex;flex-direction:column}.editor__footer{display:flex;flex:0 0 auto;gap:var(--zn-spacing-small);padding-top:var(--zn-spacing-small);border-top:1px solid rgb(var(--zn-border-color));margin-top:auto}.editor__preview{overflow:hidden;flex:1 1 auto;min-width:0;min-height:0;display:flex;flex-direction:column}.panel-toggle{position:absolute;top:50%;left:0;z-index:6;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;width:18px;height:44px;padding:0;border:1px solid rgb(var(--zn-border-color));border-left:0;border-radius:0 6px 6px 0;color:rgb(var(--zn-color-muted-text));background:rgb(var(--zn-panel));cursor:pointer;transition:left 0.2s ease,color 0.12s ease,border-color 0.12s ease}.panel-toggle:hover{color:rgb(var(--zn-text));background:rgba(var(--zn-border-color),0.4)}.panel-toggle--tucked{left:var(--zn-spacing-small);border-left:1px solid rgb(var(--zn-border-color));border-radius:6px}.editor__devices{display:flex;border:1px solid rgb(var(--zn-border-color));border-radius:4px;overflow:hidden}.editor__device,.editor__mode{display:flex;align-items:center;justify-content:center;width:30px;height:28px;padding:0;border:0;background:transparent;color:rgb(var(--zn-text));cursor:pointer}.editor__device+.editor__device{border-left:1px solid rgb(var(--zn-border-color))}.editor__device:hover,.editor__mode:hover{background:rgba(var(--zn-border-color),0.5)}.editor__device[aria-pressed=true]{background:rgb(var(--zn-primary));color:white}.editor__mode{border:1px solid rgb(var(--zn-border-color));border-radius:4px;margin-left:auto}.editor__sources{width:auto;max-width:160px}.editor__sources::part(form-control-label){position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.editor__error{padding:var(--zn-spacing-small);margin-bottom:var(--zn-spacing-small);border-radius:4px;background:rgba(var(--zn-color-error),0.1);border:1px solid rgb(var(--zn-color-error));color:rgb(var(--zn-color-error));font-size:0.8125rem}@media (max-width:768px){:host{height:auto}.editor{flex-direction:column;height:auto}.editor__fields,.editor__preview{overflow:visible}.editor__controls{flex-basis:auto;width:100%;border-right:none;border-bottom:1px solid rgb(var(--zn-border-color))}.editor__controls-inner{position:static}.editor--controls-collapsed .editor__controls{display:none}.editor__main{width:100%;padding-top:var(--zn-spacing-medium)}.panel-toggle{top:0;left:50%;width:44px;height:18px;border:1px solid rgb(var(--zn-border-color));border-top:0;border-radius:0 0 6px 6px;transform:translateX(-50%)}.panel-toggle--tucked{top:var(--zn-spacing-small);left:50%;border-top:1px solid rgb(var(--zn-border-color));border-radius:6px}}`;var gq=new Set(["zn-checkbox","zn-toggle"]),bq="(max-width: 768px)",vq=[{id:"desktop",icon:"monitor",label:"Desktop"},{id:"tablet",icon:"tablet",label:"Tablet"},{id:"mobile",icon:"smartphone",label:"Mobile"}];function xq(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var Et=class extends C{constructor(){super(...arguments);this.src="";this.frameOrigin="";this.dataUri="";this.mode="light";this.device="desktop";this.minHeight=480;this.debounce=150;this.action="";this.saveDebounce=1e3;this.sections=[];this.sectionLayout="collapsible";this.sources=[];this.controlsCollapsed=!1;this.standalone=!1;this.controlsCaption="";this.previewCaption="";this.submitLabel="";this.manual=!1;this.error="";this._submitting=!1;this._sourceIndex=0;this.hasSlotController=new Q(this,"footer","[default]");this._saving=!1;this._saveQueued=!1;this._saveWaiters=[];this._narrowQuery=window.matchMedia(bq);this._wasNarrow=!1;this._mounted=!1;this._lastControls=[];this._controlsObserverConfig={childList:!0,subtree:!0};this._controlsObserver=new Lt(this,{target:null,config:this._controlsObserverConfig,callback:()=>this._pushIfControlsChanged()});this._modeValues={light:{},dark:{}};this._suppressDepth=0;this._openedInitialCollapsible=!1;this._onNarrowChange=t=>{t.matches&&!this._wasNarrow&&(this.controlsCollapsed=!0),this._wasNarrow=t.matches};this._onSubmit=()=>{if(!this._submitting){if(this._flushPendingEdit(),this._saveTimer&&(window.clearTimeout(this._saveTimer),this._saveTimer=void 0),!this.action){this.emit("zn-theme-submit",{detail:{values:this.values}});return}this._submitting=!0,this._awaitSave().then(t=>{t&&this.emit("zn-theme-submit",{detail:{values:this.values}})}).finally(()=>{this._submitting=!1})}};this._onControlChange=()=>{this._suppressDepth>0||(this._pushTimer&&window.clearTimeout(this._pushTimer),this._pushTimer=window.setTimeout(()=>{this._pushTimer=void 0,this._harvestInto(this.mode),this._push(),this._queueSave()},this.debounce))};this._onSlotChange=()=>{this._pushIfControlsChanged()};this._setDevice=t=>{this.device!==t&&(this.device=t,this._announce())};this._toggleMode=()=>{this._flushPendingEdit(),this.mode=this.mode==="dark"?"light":"dark",this._writeBack(),this._push()};this._onFrameError=t=>{this.error=t.detail.message??"Preview failed to render"};this._onSourceChange=t=>{let r=Number(t.target.value);Number.isNaN(r)||(this._sourceIndex=r)}}get values(){return{light:{...this._modeValues.light},dark:{...this._modeValues.dark}}}get activeValues(){return{...this._modeValues[this.mode]}}_controlSlots(){return[this.controlsSlot,...Array.from(this.sectionSlots??[])].filter(t=>!!t)}_harvestNamed(){let t=[];for(let r of this._controlSlots()){let n=r.assignedElements({flatten:!0});for(let a of n){let s=[a,...Array.from(a.querySelectorAll("[name]"))];for(let l of s){let d=l;!d.getAttribute?.("name")||d.disabled||t.push({name:d.getAttribute("name"),control:d})}}}return t}_hasAssignedControls(t){return Array.from(this.children).some(r=>r.getAttribute("slot")===t)}_isBooleanControl(t){return gq.has(t.tagName.toLowerCase())||t.type==="checkbox"}_readControlValue(t){return this._isBooleanControl(t)?!!t.checked:t.value}_seed(){for(let{name:t,control:r}of this._harvestNamed()){if(t in this._modeValues.light)continue;let n=this._readControlValue(r);this._modeValues.light[t]=n;let a=r.getAttribute("dark-value");this._modeValues.dark[t]=a===null?n:this._isBooleanControl(r)?a==="1"||a==="true":a}}_writeBack(t=this.mode){let r=!1;for(let{name:n,control:a}of this._harvestNamed()){if(!(n in this._modeValues[t]))continue;let s=this._modeValues[t][n],l=this._isBooleanControl(a),d=l?!!a.checked:a.value;(l?d===s:String(d)===String(s))||(r=!0,l?a.checked=!!s:a.value=s)}r&&(this._suppressDepth++,window.setTimeout(()=>{this._suppressDepth--},0))}_harvestInto(t){for(let{name:r,control:n}of this._harvestNamed())this._modeValues[t][r]=this._readControlValue(n)}connectedCallback(){super.connectedCallback(),this._controlsObserver.observe(this),this._narrowQuery.addEventListener("change",this._onNarrowChange),this._onNarrowChange(this._narrowQuery)}disconnectedCallback(){this._pushTimer&&window.clearTimeout(this._pushTimer),this._saveTimer&&window.clearTimeout(this._saveTimer),this._narrowQuery.removeEventListener("change",this._onNarrowChange),super.disconnectedCallback()}firstUpdated(){this._pushIfControlsChanged()}_push(){this.error="",this.frame?.setTheme({mode:this.mode,values:this.activeValues}),this._announce()}_queueSave(){!this.action||this.manual||(this._saveTimer&&window.clearTimeout(this._saveTimer),this._saveTimer=window.setTimeout(()=>{this._saveTimer=void 0,this._save()},this.saveDebounce))}async _save(){if(this._saving){this._saveQueued=!0;return}this._saving=!0;let t=!1;try{let r=new FormData;for(let a of["light","dark"])for(let[s,l]of Object.entries(this._modeValues[a]))r.append(`${a}[${s}]`,typeof l=="boolean"?l?"1":"":String(l??""));let n=await fetch(this.action,{method:"POST",credentials:"same-origin",body:r});if(!n.ok)throw new Error(await n.text()||n.statusText);this.error="",t=!0}catch(r){this._fail(r instanceof Error?r.message:String(r))}finally{if(this._saving=!1,this._saveQueued)this._saveQueued=!1,this._save();else{let r=this._saveWaiters;this._saveWaiters=[],r.forEach(n=>n(t))}}}_awaitSave(){return new Promise(t=>{this._saveWaiters.push(t),this._save()})}_pushIfControlsChanged(){this.requestUpdate();let t=this._harvestNamed().map(({control:n})=>n);(!this._mounted||t.length!==this._lastControls.length||t.some((n,a)=>n!==this._lastControls[a]))&&(this._flushPendingEdit(),this._mounted=!0,this._lastControls=t,this._seed(),this._writeBack(),this._push())}_flushPendingEdit(){this._pushTimer&&(window.clearTimeout(this._pushTimer),this._pushTimer=void 0,this._harvestInto(this.mode),this._queueSave())}_announce(){this.emit("zn-theme-change",{detail:{values:this.values,mode:this.mode,device:this.device}})}_fail(t){this.error=t,this.emit("zn-error",{detail:{message:t}})}_sourcesSafe(){return Array.isArray(this.sources)?this.sources:[]}_frameSrc(){let t=this._sourcesSafe();return t.length>0?(t[this._sourceIndex]??t[0]).src:this.src}_sectionsSafe(){return Array.isArray(this.sections)?this.sections:[]}_groupsFor(t){return Array.isArray(t?.groups)?t.groups:[]}_hasStructureAttributes(){return Array.from(this.children).some(t=>t.hasAttribute("group")||t.hasAttribute("category"))}_usesDerivedSections(){return this._sectionsSafe().length===0&&this._hasStructureAttributes()}_derive(){let t=new Set,r=l=>{let d=xq(l)||"group",h=d;for(let m=2;t.has(h);m++)h=`${d}-${m}`;return t.add(h),h},n=new Map,a=new Map,s=new Map;for(let l of Array.from(this.children)){let d=l.getAttribute("group")?.trim()??"",h=l.getAttribute("category")?.trim()??"";if(!d&&!h)continue;let m=d||h,f=n.get(m);if(f||(f={name:r(m),caption:m,groups:[]},n.set(m,f)),!d||!h){s.set(l,f.name);continue}let b=`${m}\0${h}`,g=a.get(b);g||(g=r(`${m}-${h}`),a.set(b,g),f.groups.push({name:g,caption:h})),s.set(l,g)}return{sections:Array.from(n.values()),assignments:s}}_effectiveSections(){return this._usesDerivedSections()?this._derive().sections:this._sectionsSafe()}_assignDerivedSlots(){if(this._usesDerivedSections())for(let[t,r]of this._derive().assignments)t.getAttribute("slot")!==r&&t.setAttribute("slot",r)}willUpdate(t){super.willUpdate(t),this._assignDerivedSlots()}_hasNestedGroups(){return this._effectiveSections().some(t=>this._groupsFor(t).length>0)}_visibleGroups(t){return this._groupsFor(t).filter(r=>this._hasAssignedControls(r.name))}_visibleSections(){let t=this._effectiveSections();return this._hasNestedGroups()?t.filter(r=>this._visibleGroups(r).length>0):t.filter(r=>this._hasAssignedControls(r.name))}_renderSections(){return this._visibleSections().map(t=>u`
5494
5494
  <zn-collapsible
5495
5495
  class="editor__section"
5496
5496
  part="section"
@@ -5542,10 +5542,12 @@ ${n}`:""}</li></ol>`};return t(0)}parseTable(o,t){this.index++;let r=[],n=[];for
5542
5542
  @input="${this._onControlChange}">
5543
5543
  <slot @slotchange="${this._onSlotChange}"></slot>
5544
5544
  ${this._hasNestedGroups()?this._renderTabs(t=>u`
5545
- ${this._hasAssignedControls(t.name)?u`
5546
- <slot name="${t.name}" class="editor__section-slot" @slotchange="${this._onSlotChange}"></slot>`:V}
5547
- ${this._renderGroups(t)}`):this.sectionLayout==="tabs"?this._renderTabs(t=>u`
5548
- <slot name="${t.name}" class="editor__section-slot" @slotchange="${this._onSlotChange}"></slot>`):this._renderSections()}
5545
+ ${this._hasAssignedControls(t.name)?u`
5546
+ <slot name="${t.name}" class="editor__section-slot"
5547
+ @slotchange="${this._onSlotChange}"></slot>`:V}
5548
+ ${this._renderGroups(t)}`):this.sectionLayout==="tabs"?this._renderTabs(t=>u`
5549
+ <slot name="${t.name}" class="editor__section-slot"
5550
+ @slotchange="${this._onSlotChange}"></slot>`):this._renderSections()}
5549
5551
  </div>
5550
5552
  ${this.hasSlotController.test("footer")?u`
5551
5553
  <div part="footer" class="editor__footer">
@@ -5609,6 +5611,7 @@ ${n}`:""}</li></ol>`};return t(0)}parseTable(o,t){this.index++;let r=[],n=[];for
5609
5611
  device="${this.device}"
5610
5612
  min-height="${this.minHeight}"
5611
5613
  fill
5614
+ interactive
5612
5615
  backdrop="${this.standalone?"panel":"dots"}"
5613
5616
  exportparts="base:preview__base,stage:preview__stage,iframe:preview__iframe,error:preview__error"
5614
5617
  @zn-error="${this._onFrameError}"></zn-preview-frame>
@@ -56,7 +56,13 @@ handlers can't be cancelled from out here — blocking pointer input is the only
56
56
  way to stop them, and hover goes with it. Scrolling doesn't: an overflowing page
57
57
  is scrolled by the panel instead, as below.
58
58
 
59
- Set `interactive` when the embed is meant to be used rather than looked at.
59
+ Set `interactive` when the embed is meant to be used rather than looked at. The
60
+ frame then behaves as a viewport: it stays the panel's own height and the embed
61
+ scrolls itself, so there's a single scrollbar and the embed's `100vh`,
62
+ `position: fixed` and sticky content size to what's actually on screen. The
63
+ [reported content height](#overflowing-content) is ignored while `interactive`
64
+ is set — it exists to make an *inert* frame's overflow reachable, which an
65
+ interactive one does for itself.
60
66
 
61
67
  ```html:preview
62
68
  <zn-preview-frame
@@ -73,11 +79,13 @@ Set `interactive` when the embed is meant to be used rather than looked at.
73
79
 
74
80
  ## Overflowing Content
75
81
 
76
- A page taller than the panel is scrolled by the panel, not inside the frame. The
77
- frame can't do it itself: a cross-origin document can't be scrolled from the host
78
- (`contentWindow.scrollTo` is blocked), and with pointer input off the wheel never
79
- reaches it anyway. So the frame is instead laid out at its full content height —
80
- nothing scrolls inside it — and the panel scrolls that.
82
+ An inert page taller than the panel is scrolled by the panel, not inside the
83
+ frame. The frame can't do it itself: a cross-origin document can't be scrolled
84
+ from the host (`contentWindow.scrollTo` is blocked), and with pointer input off
85
+ the wheel never reaches it anyway. So the frame is instead laid out at its full
86
+ content height — nothing scrolls inside it — and the panel scrolls that. An
87
+ [`interactive`](#interactivity) frame doesn't need any of this and opts out of
88
+ it: it keeps the panel's height and the embed scrolls itself.
81
89
 
82
90
  For the frame to be sized that way, the embed reports its height alongside
83
91
  `hp-preview:rendered`:
@@ -24,6 +24,10 @@ Set `controls-caption` and `preview-caption` to label each column's header
24
24
  row — both are empty by default, rendering no text (the controls column's
25
25
  header row still renders either way, so the two columns stay aligned).
26
26
 
27
+ The preview is [interactive](/components/preview-frame/#interactivity): the
28
+ embedded page can be clicked and hovered, and it scrolls itself, so the preview
29
+ column never adds a second scrollbar beside the embed's own.
30
+
27
31
  The preview always fills its column, leaving no dead space beneath it.
28
32
  `min-height` (default `480`) is a floor for that column, not a fixed height —
29
33
  it's still forwarded to the [preview frame](/components/preview-frame/), which
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.76",
3
+ "version": "1.1.77",
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",
@@ -111,6 +111,12 @@ export default class ZnPreviewFrame extends ZincElement {
111
111
  * be reached from here to cancel its own handlers, so this blocks pointer
112
112
  * input entirely — hover goes with it. Scrolling doesn't: an overflowing
113
113
  * page is scrolled by the panel rather than by the frame (see _contentHeight).
114
+ *
115
+ * Set, the frame becomes a real viewport instead: it stays the panel's own
116
+ * height and the embed scrolls itself, so there is one scrollbar rather than
117
+ * a panel scrolling an oversized frame, and the embed's viewport-relative
118
+ * layout (`100vh`, `position: fixed`, sticky headers) sizes to what's on
119
+ * screen. _contentHeight is ignored while this is set.
114
120
  */
115
121
  @property({type: Boolean, reflect: true}) interactive = false;
116
122
 
@@ -125,7 +131,8 @@ export default class ZnPreviewFrame extends ZincElement {
125
131
  * height rather than the panel's, so the page never scrolls inside the frame
126
132
  * — the panel scrolls instead, which is what makes an overflowing preview
127
133
  * reachable while pointer input to the frame is blocked. 0 = unknown, and the
128
- * frame falls back to filling the panel.
134
+ * frame falls back to filling the panel. Kept up to date either way, but only
135
+ * laid out when the frame is inert: an `interactive` frame scrolls itself.
129
136
  */
130
137
  @state() private _contentHeight = 0;
131
138
 
@@ -396,7 +403,10 @@ export default class ZnPreviewFrame extends ZincElement {
396
403
  // transformed back down, so the frame fills the panel while the content
397
404
  // renders smaller and more of the page is visible. Percentage width means
398
405
  // nothing is measured — no layout feedback loop.
399
- const content = this.error ? 0 : this._contentHeight;
406
+ // Growing the frame past the panel is the workaround for an inert frame not
407
+ // being scrollable; interactive, that trades one scrollbar for two and hands
408
+ // the embed a viewport taller than the panel it's shown in.
409
+ const content = this.error || this.interactive ? 0 : this._contentHeight;
400
410
  const iframeStyles = this.fill
401
411
  ? {width: '100%', height: content ? `max(${content}px, 100%)` : '100%'}
402
412
  : {