@kubex/zinc 1.1.92 → 1.1.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/custom-elements.json +2880 -453
  2. package/dist/vscode.html-custom-data.json +209 -22
  3. package/dist/web-types.json +452 -39
  4. package/dist/zn.d.ts +633 -61
  5. package/dist/zn.min.css +1 -1
  6. package/dist/zn.min.js +865 -560
  7. package/docs/pages/components/page-builder.md +143 -19
  8. package/docs/pages/components/schedule-builder.md +345 -0
  9. package/docs/pages/components/slash-menu.md +132 -6
  10. package/docs/pages/components/textarea.md +16 -0
  11. package/package.json +1 -1
  12. package/scss/_root.scss +7 -1
  13. package/src/components/alert/alert.scss +9 -13
  14. package/src/components/button/button.scss +5 -2
  15. package/src/components/chip/chip.scss +1 -1
  16. package/src/components/icon-picker/icon-picker.component.ts +1 -1
  17. package/src/components/inline-edit/inline-edit.component.ts +6 -1
  18. package/src/components/input/input.component.ts +12 -2
  19. package/src/components/linked-select/linked-select.component.ts +22 -5
  20. package/src/components/page/page.scss +20 -9
  21. package/src/components/page-builder/modules/page-section-card/page-section-card.component.ts +16 -9
  22. package/src/components/page-builder/modules/page-section-card/page-section-card.scss +13 -0
  23. package/src/components/page-builder/modules/page-section-card/page-section-card.test.ts +9 -0
  24. package/src/components/page-builder/page-builder.component.ts +535 -232
  25. package/src/components/page-builder/page-builder.scss +230 -19
  26. package/src/components/page-builder/page-builder.test.ts +790 -110
  27. package/src/components/page-builder/page-tree.test.ts +483 -0
  28. package/src/components/page-builder/page-tree.ts +329 -0
  29. package/src/components/page-builder/page.types.ts +98 -10
  30. package/src/components/page-nav/page-nav.scss +9 -1
  31. package/src/components/panel/panel.component.ts +5 -1
  32. package/src/components/priority-list/priority-list.component.ts +1 -0
  33. package/src/components/priority-list/priority-list.scss +2 -1
  34. package/src/components/remarkd-editor/remarkd-editor.component.ts +198 -9
  35. package/src/components/remarkd-editor/remarkd-editor.scss +81 -0
  36. package/src/components/remarkd-editor/remarkd-editor.test.ts +179 -0
  37. package/src/components/schedule-builder/index.ts +12 -0
  38. package/src/components/schedule-builder/schedule-builder.component.ts +1543 -0
  39. package/src/components/schedule-builder/schedule-builder.scss +448 -0
  40. package/src/components/schedule-builder/schedule-builder.test.ts +344 -0
  41. package/src/components/settings-container/settings-container.scss +2 -1
  42. package/src/components/slash-item/slash-item.component.ts +1 -1
  43. package/src/components/slash-menu/slash-menu-items.ts +48 -0
  44. package/src/components/slash-menu/slash-menu.component.ts +134 -27
  45. package/src/components/slash-menu/slash-menu.scss +90 -12
  46. package/src/components/slash-menu/slash-menu.test.ts +107 -0
  47. package/src/components/textarea/textarea.component.ts +12 -2
  48. package/src/components/textarea/textarea.test.ts +2 -2
  49. package/src/components/toggle/toggle.component.ts +2 -1
  50. package/src/components/translations/translations.component.ts +5 -1
  51. package/src/zinc.ts +1 -0
  52. package/docs/superpowers/plans/2026-08-03-theme-editor.md +0 -1536
  53. package/docs/superpowers/specs/2026-08-03-theme-editor-design.md +0 -327
package/dist/zn.d.ts CHANGED
@@ -1316,6 +1316,7 @@ declare module "components/toggle/toggle.component" {
1316
1316
  *
1317
1317
  * @csspart base - The component's base wrapper containing the toggle switch.
1318
1318
  * @csspart control - The toggle switch control (the circular button that slides).
1319
+ * @csspart label - The toggle's label.
1319
1320
  * @csspart description - The container that wraps the toggle's description.
1320
1321
  *
1321
1322
  * @cssproperty --zn-toggle-margin - The margin around the toggle switch. Defaults to `8px 0`.
@@ -1900,6 +1901,14 @@ declare module "components/slash-menu/slash-menu-items" {
1900
1901
  export function parseSlashItems(value: string | null | undefined): SlashMenuItem[];
1901
1902
  /** Filters and ranks items against a query. An empty query keeps every item in its declared order. */
1902
1903
  export function filterSlashItems(items: SlashMenuItem[], query: string): SlashMenuItem[];
1904
+ /** The identity an item is remembered by in a menu's recently used list. */
1905
+ export function slashItemKey(item: SlashMenuItem): string;
1906
+ /** The keys of the items most recently chosen from the menu stored under `key`, newest first. */
1907
+ export function readRecentSlashItems(key: string): string[];
1908
+ /** Moves an item to the front of the recently used list stored under `key`, and returns the list. */
1909
+ export function recordRecentSlashItem(key: string, item: SlashMenuItem): string[];
1910
+ /** Forgets the recently used items stored under `key`. */
1911
+ export function clearRecentSlashItems(key: string): void;
1903
1912
  }
1904
1913
  declare module "utilities/caret-position" {
1905
1914
  export interface CaretCoordinates {
@@ -1926,7 +1935,7 @@ declare module "utilities/caret-position" {
1926
1935
  declare module "components/slash-menu/slash-menu.component" {
1927
1936
  import ZincElement from "internal/zinc-element";
1928
1937
  import ZnIcon from "components/icon/index";
1929
- import type { CSSResultGroup, PropertyValues } from 'lit';
1938
+ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
1930
1939
  import type { Placement, VirtualElement } from '@floating-ui/dom';
1931
1940
  import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
1932
1941
  export const SLASH_ITEM_SELECT = "zn-slash-item-select";
@@ -1942,12 +1951,19 @@ declare module "components/slash-menu/slash-menu.component" {
1942
1951
  * component driving the menu (e.g. `zn-textarea`) re-emits it as `zn-slash-select`.
1943
1952
  *
1944
1953
  * @csspart panel - The floating panel that holds the list.
1945
- * @csspart heading - The panel's heading.
1954
+ * @csspart list - The scrolling list of items.
1946
1955
  * @csspart item - An item in the list.
1956
+ * @csspart icon - The chip holding an item's icon.
1947
1957
  * @csspart group-heading - A group heading between items.
1958
+ * @csspart divider - The rule closing the recently used section, when the items below it have no heading of their own.
1948
1959
  * @csspart footer - The truncation footer, shown when not every match fits.
1960
+ * @csspart hints - The pinned footer of keyboard hints.
1961
+ * @csspart hint - A single keyboard hint within the footer.
1962
+ * @csspart hint-key - The key shown against a hint.
1949
1963
  *
1950
1964
  * @cssproperty --slash-menu-width - The width of the panel.
1965
+ * @cssproperty --slash-menu-border-radius - The corner radius of the panel.
1966
+ * @cssproperty --slash-menu-item-border-radius - The corner radius of the items and their icon chips.
1951
1967
  * @cssproperty --slash-menu-max-height - The maximum height of the panel before it scrolls.
1952
1968
  */
1953
1969
  export default class ZnSlashMenu extends ZincElement {
@@ -1956,6 +1972,7 @@ declare module "components/slash-menu/slash-menu.component" {
1956
1972
  'zn-icon': typeof ZnIcon;
1957
1973
  };
1958
1974
  private panel;
1975
+ private list;
1959
1976
  private stopAutoUpdate?;
1960
1977
  /** Whether the menu is showing. */
1961
1978
  open: boolean;
@@ -1963,7 +1980,7 @@ declare module "components/slash-menu/slash-menu.component" {
1963
1980
  items: SlashMenuItem[];
1964
1981
  /** The query the items were matched against, shown in the heading. */
1965
1982
  query: string;
1966
- /** The heading shown when there is no query. */
1983
+ /** The name the list is announced by when there is no query. */
1967
1984
  heading: string;
1968
1985
  /** Shown in place of the list when there are no items. */
1969
1986
  emptyText: string;
@@ -1971,6 +1988,18 @@ declare module "components/slash-menu/slash-menu.component" {
1971
1988
  maxItems: number;
1972
1989
  /** Hides the insertion key (the item's value) normally shown against each item. */
1973
1990
  hideKeys: boolean;
1991
+ /** Hides the pinned footer of keyboard hints. */
1992
+ hideHints: boolean;
1993
+ /**
1994
+ * Remembers the items chosen here and lists the most recent of them first, under their own heading.
1995
+ * The key scopes the list to where the menu is used, so each place keeps its own history in
1996
+ * `localStorage`. Leave unset to offer no recently used section.
1997
+ */
1998
+ recentKey: string;
1999
+ /** The most recently used items to list. */
2000
+ maxRecent: number;
2001
+ /** The heading shown above the recently used items. */
2002
+ recentHeading: string;
1974
2003
  /** The element or caret rect the panel is positioned against. */
1975
2004
  anchor: Element | VirtualElement | null;
1976
2005
  /** The preferred placement of the panel. */
@@ -1978,11 +2007,22 @@ declare module "components/slash-menu/slash-menu.component" {
1978
2007
  /** The gap between the caret and the panel. */
1979
2008
  distance: number;
1980
2009
  private activeIndex;
2010
+ private recentKeys;
2011
+ /** How many recently used items the last update listed, to spot the list appearing or reordering. */
2012
+ private recentCount;
2013
+ private get listItems();
2014
+ /**
2015
+ * The remembered items that are in the current list, newest first. Only offered without a query —
2016
+ * once the user is searching, the ranked matches are the better answer.
2017
+ */
2018
+ private get recentItems();
1981
2019
  private get visibleItems();
1982
2020
  /** The item that Enter would insert. */
1983
2021
  get activeItem(): SlashMenuItem | undefined;
1984
2022
  show(): void;
1985
2023
  hide(): void;
2024
+ /** Forgets the items remembered under `recent-key`. */
2025
+ clearRecent(): void;
1986
2026
  /** Sets the active item by index, wrapping at both ends and skipping disabled items. */
1987
2027
  setActiveIndex(index: number): void;
1988
2028
  /** Moves the active item by `delta` places. */
@@ -2005,7 +2045,9 @@ declare module "components/slash-menu/slash-menu.component" {
2005
2045
  protected updated(changed: PropertyValues): void;
2006
2046
  private renderItem;
2007
2047
  private renderItems;
2008
- render(): import("lit-html").TemplateResult<1>;
2048
+ private renderHint;
2049
+ private renderHints;
2050
+ render(): TemplateResult<1>;
2009
2051
  }
2010
2052
  }
2011
2053
  declare module "components/slash-menu/slash-menu-controller" {
@@ -2079,10 +2121,22 @@ declare module "components/slash-menu/slash-menu-controller" {
2079
2121
  private replace;
2080
2122
  }
2081
2123
  }
2124
+ declare module "components/slash-menu/index" {
2125
+ import ZnSlashMenu from "components/slash-menu/slash-menu.component";
2126
+ export * from "components/slash-menu/slash-menu.component";
2127
+ export * from "components/slash-menu/slash-menu-controller";
2128
+ export * from "components/slash-menu/slash-menu-items";
2129
+ export default ZnSlashMenu;
2130
+ global {
2131
+ interface HTMLElementTagNameMap {
2132
+ 'zn-slash-menu': ZnSlashMenu;
2133
+ }
2134
+ }
2135
+ }
2082
2136
  declare module "components/slash-item/slash-item.component" {
2083
2137
  import ZincElement from "internal/zinc-element";
2084
2138
  import type { CSSResultGroup } from 'lit';
2085
- import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
2139
+ import type { SlashMenuItem } from "components/slash-menu/index";
2086
2140
  /**
2087
2141
  * @summary Declares a single insertion for a slash menu. Renders nothing itself — it describes an
2088
2142
  * entry for the component it is slotted into, e.g. `<zn-textarea>`'s `slash-items` slot.
@@ -2131,18 +2185,6 @@ declare module "components/slash-item/index" {
2131
2185
  }
2132
2186
  }
2133
2187
  }
2134
- declare module "components/slash-menu/index" {
2135
- import ZnSlashMenu from "components/slash-menu/slash-menu.component";
2136
- export * from "components/slash-menu/slash-menu.component";
2137
- export * from "components/slash-menu/slash-menu-controller";
2138
- export * from "components/slash-menu/slash-menu-items";
2139
- export default ZnSlashMenu;
2140
- global {
2141
- interface HTMLElementTagNameMap {
2142
- 'zn-slash-menu': ZnSlashMenu;
2143
- }
2144
- }
2145
- }
2146
2188
  declare module "components/input/input.component" {
2147
2189
  import { type SlashMenuItem } from "components/slash-menu/slash-menu-items";
2148
2190
  import ZincElement from "internal/zinc-element";
@@ -2336,10 +2378,15 @@ declare module "components/input/input.component" {
2336
2378
  slashPreset: string;
2337
2379
  /** The characters that open the slash menu. */
2338
2380
  slashTrigger: string;
2339
- /** The heading shown above the slash menu's items. */
2381
+ /** The name the slash menu's list is announced by. */
2340
2382
  slashHeading: string;
2341
2383
  /** Hides the insertion keys normally shown against the slash menu's items. */
2342
2384
  slashHideKeys: boolean;
2385
+ /**
2386
+ * Lists the items most recently chosen here above the rest, remembered in `localStorage` under this
2387
+ * key. Share a key between the fields that should share a history; leave unset to offer no such list.
2388
+ */
2389
+ slashRecentKey: string;
2343
2390
  /**
2344
2391
  * Resolves additional items each time the menu opens, for lists that come from elsewhere (e.g. an
2345
2392
  * API). Receives the current query and may return a promise. JavaScript only.
@@ -5232,10 +5279,12 @@ declare module "components/inline-edit/inline-edit.component" {
5232
5279
  slashPreset: string;
5233
5280
  /** The characters that open the slash menu. */
5234
5281
  slashTrigger: string;
5235
- /** The heading shown above the slash menu's items. */
5282
+ /** The name the slash menu's list is announced by. */
5236
5283
  slashHeading: string;
5237
5284
  /** Hides the insertion keys normally shown against the slash menu's items. */
5238
5285
  slashHideKeys: boolean;
5286
+ /** Lists the slash menu items most recently chosen here above the rest, remembered under this key. */
5287
+ slashRecentKey: string;
5239
5288
  /** Resolves additional slash menu items each time the menu opens. JavaScript only. */
5240
5289
  slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
5241
5290
  options: {
@@ -6679,10 +6728,15 @@ declare module "components/textarea/textarea.component" {
6679
6728
  slashPreset: string;
6680
6729
  /** The characters that open the slash menu. */
6681
6730
  slashTrigger: string;
6682
- /** The heading shown above the slash menu's items. */
6731
+ /** The name the slash menu's list is announced by. */
6683
6732
  slashHeading: string;
6684
6733
  /** Hides the insertion keys normally shown against the slash menu's items. */
6685
6734
  slashHideKeys: boolean;
6735
+ /**
6736
+ * Lists the items most recently chosen here above the rest, remembered in `localStorage` under this
6737
+ * key. Share a key between the fields that should share a history; leave unset to offer no such list.
6738
+ */
6739
+ slashRecentKey: string;
6686
6740
  /**
6687
6741
  * Resolves additional items each time the menu opens, for lists that come from elsewhere (e.g. an
6688
6742
  * API). Receives the current query and may return a promise. JavaScript only.
@@ -7149,7 +7203,7 @@ declare module "components/linked-select/linked-select.component" {
7149
7203
  [key: string]: string;
7150
7204
  }
7151
7205
  interface linkedSelectOptions {
7152
- [key: string]: linkedSelectOption[];
7206
+ [key: string]: linkedSelectOption;
7153
7207
  }
7154
7208
  /**
7155
7209
  * @summary Short summary of the component's intended use.
@@ -7177,6 +7231,8 @@ declare module "components/linked-select/linked-select.component" {
7177
7231
  linkedSelect: string;
7178
7232
  cacheKey: string;
7179
7233
  label: string;
7234
+ /** Automatically select the first option of the linked group when no value is set. */
7235
+ selectFirst: boolean;
7180
7236
  input: ZnSelect;
7181
7237
  private linkedSelectElement;
7182
7238
  private readonly formControlController;
@@ -7193,6 +7249,8 @@ declare module "components/linked-select/linked-select.component" {
7193
7249
  handleLinkedSelectChange: () => void;
7194
7250
  handleChange(e: Event): void;
7195
7251
  handleSelectChange: (e: ZnSelectEvent) => void;
7252
+ /** The options of the group the linked select currently points at. */
7253
+ private currentOptions;
7196
7254
  render(): import("lit-html").TemplateResult<1>;
7197
7255
  }
7198
7256
  }
@@ -8719,10 +8777,12 @@ declare module "components/translations/translations.component" {
8719
8777
  slashPreset: string;
8720
8778
  /** The characters that open the slash menu. */
8721
8779
  slashTrigger: string;
8722
- /** The heading shown above the slash menu's items. */
8780
+ /** The name the slash menu's list is announced by. */
8723
8781
  slashHeading: string;
8724
8782
  /** Hides the insertion keys normally shown against the slash menu's items. */
8725
8783
  slashHideKeys: boolean;
8784
+ /** Lists the slash menu items most recently chosen here above the rest, remembered under this key. */
8785
+ slashRecentKey: string;
8726
8786
  /** Resolves additional slash menu items each time the menu opens. JavaScript only. */
8727
8787
  slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
8728
8788
  /** When true, hides the individual language navbar and defers language control to a parent zn-translation-group. */
@@ -9020,6 +9080,7 @@ declare module "components/priority-list/priority-list.component" {
9020
9080
  *
9021
9081
  * @cssproperty --zn-priority-list-item-gap - The gap between list items. Defaults to `var(--zn-spacing-2x-small)`.
9022
9082
  * @cssproperty --zn-priority-list-item-padding - The padding inside each item. Defaults to `var(--zn-spacing-small) var(--zn-spacing-medium)`.
9083
+ * @cssproperty --zn-priority-list-actions-gap - The gap between an item's slotted actions. Defaults to `var(--zn-spacing-x-small)`.
9023
9084
  * @cssproperty --zn-priority-list-handle-color - The color of the drag handle. Defaults to `var(--zn-color-neutral-500)`.
9024
9085
  * @cssproperty --zn-priority-list-priority-color - The color of the priority number. Defaults to `var(--zn-color-neutral-600)`.
9025
9086
  */
@@ -9272,6 +9333,8 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9272
9333
  * @csspart raw - The full-document textarea shown in raw source mode.
9273
9334
  * @csspart slash-menu - The `zn-slash-menu` opened by typing "/" in an empty block.
9274
9335
  * @csspart image-controls - The caption / alignment / size panel shown when an image block is clicked.
9336
+ * @csspart include - The chip rendered in place of an `include::` directive.
9337
+ * @csspart include-picker - The inline Include picker opened from the toolbar or "/include".
9275
9338
  */
9276
9339
  export default class ZnRemarkdEditor extends ZincElement implements ZincFormControl {
9277
9340
  static styles: CSSResultGroup;
@@ -9281,6 +9344,7 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9281
9344
  private readonly formControlController;
9282
9345
  private readonly slashController;
9283
9346
  private editingDraft;
9347
+ private includeRequest;
9284
9348
  private rawEntryValue;
9285
9349
  private suppressValueSync;
9286
9350
  private suppressBlurCommit;
@@ -9288,6 +9352,11 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9288
9352
  private slashMenuElement;
9289
9353
  private blocks;
9290
9354
  private editingIndex;
9355
+ /**
9356
+ * Lists the blocks most recently inserted here above the rest of the slash menu, remembered in
9357
+ * `localStorage` under this key. Leave unset to offer no recently used section.
9358
+ */
9359
+ slashRecentKey: string;
9291
9360
  /** Renders the slash menu only once it has been needed. */
9292
9361
  private hasSlashMenu;
9293
9362
  private imagePickerIndex;
@@ -9296,6 +9365,9 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9296
9365
  private dragIndex;
9297
9366
  private editShell;
9298
9367
  private rawMode;
9368
+ private includeOptions;
9369
+ private includePickerIndex;
9370
+ private includeQuery;
9299
9371
  private pendingDragHandle;
9300
9372
  private dragStartX;
9301
9373
  private dragStartY;
@@ -9314,6 +9386,12 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9314
9386
  * to `uploadUrl` and the returned `uploadPath` is embedded as the image URL.
9315
9387
  */
9316
9388
  attachmentUrl: string;
9389
+ /**
9390
+ * Endpoint listing the Includes this document may embed, as
9391
+ * `{"items":[{id,title,description,scope,keywords,languages,url}]}`. Labels the
9392
+ * chips rendered for `include::` directives and feeds the include picker.
9393
+ */
9394
+ includeUrl: string;
9317
9395
  /** Adds a toolbar toggle that swaps the block view for the full remarkd source. */
9318
9396
  allowRaw: boolean;
9319
9397
  /** Makes the editor required for form submission. */
@@ -9335,6 +9413,7 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9335
9413
  protected firstUpdated(_changedProperties: PropertyValues): void;
9336
9414
  disconnectedCallback(): void;
9337
9415
  handleValueChange(): void;
9416
+ handleIncludeUrlChange(): void;
9338
9417
  /**
9339
9418
  * Splits remarkd source into blocks on blank lines, keeping fenced /
9340
9419
  * delimited containers (``` ==== !!!! .... ----) as single blocks.
@@ -9346,6 +9425,8 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9346
9425
  private handleRenderedClick;
9347
9426
  /** Parses a block that is purely an image (with optional caption/align lines). */
9348
9427
  private parseImageBlock;
9428
+ /** Parses a block that is nothing but an include directive. */
9429
+ private parseIncludeBlock;
9349
9430
  private serializeImageBlock;
9350
9431
  private toggleCheckbox;
9351
9432
  private startEdit;
@@ -9392,10 +9473,16 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9392
9473
  private createDragGhost;
9393
9474
  private moveDragGhost;
9394
9475
  private pickImage;
9476
+ private pickInclude;
9477
+ private closeIncludePicker;
9478
+ private insertInclude;
9395
9479
  private closeImagePicker;
9396
9480
  private handleImagePicked;
9397
9481
  private insertImage;
9398
9482
  private uploadImage;
9483
+ private hasIncludeBlock;
9484
+ /** Fetches the include list once; every later caller shares the same promise. */
9485
+ private loadIncludeOptions;
9399
9486
  private autosize;
9400
9487
  private toggleRawMode;
9401
9488
  private focusRaw;
@@ -9413,11 +9500,13 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
9413
9500
  private commitRaw;
9414
9501
  private handleToolbarInsert;
9415
9502
  private renderImageControls;
9503
+ private renderIncludeChip;
9416
9504
  private renderBlock;
9417
9505
  render(): import("lit-html").TemplateResult<1>;
9418
9506
  private renderRaw;
9419
9507
  /** The block views, with the inline image picker spliced in when active. */
9420
9508
  private renderBody;
9509
+ private renderIncludePicker;
9421
9510
  private renderImagePicker;
9422
9511
  }
9423
9512
  }
@@ -10526,9 +10615,23 @@ declare module "components/page-builder/page.types" {
10526
10615
  label?: string;
10527
10616
  /** Section content, keyed by field name (the inspector's `name` attributes). */
10528
10617
  data: Record<string, unknown>;
10529
- /** Slot contents for container sections, sized to the type's `slots`. Empty slots are null. */
10618
+ /** Container instances only: the editor-chosen layout. */
10619
+ layout?: PageContainerLayout;
10620
+ /** Container instances only: one ordered stack per cell, row-major. */
10621
+ cells?: PageSection[][];
10622
+ /**
10623
+ * @deprecated The pre-cells fixed-slot shape. Read on load and migrated to
10624
+ * `cells`; never written back.
10625
+ */
10530
10626
  children?: (PageSection | null)[];
10531
10627
  }
10628
+ /** A container instance's editor-chosen layout. */
10629
+ export interface PageContainerLayout {
10630
+ /** One weight per column; the array length IS the column count. */
10631
+ widths: number[];
10632
+ /** When true the builder offers a trailing empty row instead of a pinned cell count. */
10633
+ grow: boolean;
10634
+ }
10532
10635
  /** The complete serialisable state of a page. Order = render order. */
10533
10636
  export interface PageState {
10534
10637
  sections: PageSection[];
@@ -10554,16 +10657,48 @@ declare module "components/page-builder/page.types" {
10554
10657
  configTemplate?: HTMLTemplateElement;
10555
10658
  /** Programmatic inspector body — takes precedence over `configTemplate`. */
10556
10659
  renderConfig?: (section: PageSection, update: (data: Record<string, unknown>) => void) => TemplateResult;
10660
+ /** Marks this type a container: its card renders a grid of cells on the canvas. */
10661
+ container?: boolean;
10662
+ /**
10663
+ * Column weights a new instance of this container starts with. Stored as parsed, not
10664
+ * guaranteed sanitised — route through `sanitiseWidths`/`defaultLayout` before use.
10665
+ */
10666
+ defaultWidths?: number[];
10667
+ /** Whether a new instance of this container starts growable. */
10668
+ defaultGrow?: boolean;
10557
10669
  /**
10558
- * Number of child slots this section offers on the canvas (a container tile);
10559
- * rendered as a 3-column grid. Containers cannot be placed inside other containers.
10670
+ * @deprecated Use `container` with `columns`/`widths`. Read as a container of
10671
+ * `DEFAULT_WIDTHS` columns with the cell count pinned to this number.
10560
10672
  */
10561
10673
  slots?: number;
10562
- /** Section type keys allowed in this container's slots. Omit to allow any non-container type. */
10674
+ /**
10675
+ * Section type keys allowed in this container's cells. On a `container` type,
10676
+ * omitting it allows any type, subject to the nesting cap — that is how nesting
10677
+ * is reachable without enumerating types. The deprecated `slots=` alias keeps the
10678
+ * old rule instead: omitting it there allows any non-container type.
10679
+ */
10563
10680
  accepts?: string[];
10564
10681
  }
10565
- /** A container section's slot contents, padded/truncated to the type's slot count. */
10566
- export function sectionChildren(section: PageSection, type: PageSectionType): (PageSection | null)[];
10682
+ /** Beyond 6 columns a card is under ~170px on the 1024px canvas — unreadable. */
10683
+ export const MAX_COLUMNS = 6;
10684
+ /** A single weight beyond 12 makes the other columns unusably thin. */
10685
+ export const MAX_WIDTH = 12;
10686
+ /** Container nesting levels: a top-level container is 1, one inside a cell is 2. */
10687
+ export const MAX_CONTAINER_LEVELS = 2;
10688
+ /** Global section budget; also the sanity clamp on an incoming `cells` length. */
10689
+ export const MAX_SECTIONS = 500;
10690
+ /** What a bare `container` declaration seeds. */
10691
+ export const DEFAULT_WIDTHS: readonly number[];
10692
+ /** Container-ness is a property of the registered type, never of the instance. */
10693
+ export function isContainer(type: PageSectionType | undefined): boolean;
10694
+ /**
10695
+ * Coerces a weights list into a usable one: each entry a whole number from 1 to
10696
+ * MAX_WIDTH (anything unusable becomes 1), at most MAX_COLUMNS entries, falling
10697
+ * back to DEFAULT_WIDTHS only when nothing usable remains.
10698
+ */
10699
+ export function sanitiseWidths(raw: unknown): number[];
10700
+ /** The layout a newly placed instance of a container type starts with. */
10701
+ export function defaultLayout(type: PageSectionType | undefined): PageContainerLayout;
10567
10702
  /** Drag-and-drop MIME carrying a section type id from the palette to the canvas. */
10568
10703
  export const PAGE_TYPE_MIME = "application/x-zn-page-type";
10569
10704
  /** Drag-and-drop MIME carrying a placed section's id when reordering. */
@@ -10571,9 +10706,89 @@ declare module "components/page-builder/page.types" {
10571
10706
  export function emptyPageState(): PageState;
10572
10707
  /** Unique-enough id for a new section, stable across edits once assigned. */
10573
10708
  export function generateSectionId(): string;
10574
- /** Card summary: the section's first non-empty string value, else the type description. */
10709
+ /**
10710
+ * Card summary: the label of the first value chosen from a select, else the first
10711
+ * non-empty string value, else the type description. Options win over field order
10712
+ * so a tile reads as its linked item however its other fields were filled in.
10713
+ */
10575
10714
  export function sectionSummary(section: PageSection, type?: PageSectionType): string;
10576
10715
  }
10716
+ declare module "components/page-builder/page-tree" {
10717
+ import { type PageSection, type PageSectionType } from "components/page-builder/page.types";
10718
+ /** Drops trailing cells that hold nothing. Interior empties are meaningful and kept. */
10719
+ export function trimTrailingEmptyCells(cells: PageSection[][]): PageSection[][];
10720
+ /** Pads to a whole number of rows, always leaving at least one row to drop into. */
10721
+ export function padCells(cells: PageSection[][], columns: number): PageSection[][];
10722
+ /**
10723
+ * The canonical cell list for a container. A growable container never keeps a
10724
+ * trailing all-empty row (the renderer supplies the `+` row itself, so keeping
10725
+ * one would show two); a fixed container's trailing empty row is its layout and
10726
+ * survives untouched.
10727
+ */
10728
+ export function normaliseCells(cells: PageSection[][], columns: number, grow: boolean): PageSection[][];
10729
+ /**
10730
+ * Re-chunks the same ordered list of stacks into a different column count. Only
10731
+ * trailing empties are dropped, so no section can be lost by a column change.
10732
+ */
10733
+ export function recolumnCells(cells: PageSection[][], columns: number): PageSection[][];
10734
+ /** Row-major view of the flat cell list, for rendering. */
10735
+ export function cellRows(cells: PageSection[][], columns: number): PageSection[][][];
10736
+ export function containerWidths(section: PageSection, type?: PageSectionType): number[];
10737
+ export function containerColumns(section: PageSection, type?: PageSectionType): number;
10738
+ export function containerGrow(section: PageSection, type?: PageSectionType): boolean;
10739
+ /** A container's cells, normalised. Empty for a non-container. */
10740
+ export function containerCells(section: PageSection, type?: PageSectionType): PageSection[][];
10741
+ /** Depth-first search across top-level sections and every cell stack. */
10742
+ export function findSection(sections: PageSection[], id: string | null): PageSection | undefined;
10743
+ /** New section array with `id` replaced by `patch(section)`, wherever it lives. */
10744
+ export function patchSection(sections: PageSection[], id: string, patch: (section: PageSection) => PageSection): PageSection[];
10745
+ /** Detaches a section from wherever it lives, returning it and the remaining tree. */
10746
+ export function extractSection(sections: PageSection[], id: string): [PageSection | undefined, PageSection[]];
10747
+ /** Inserts a section into a container's cell at a stack position. */
10748
+ export function insertIntoCell(sections: PageSection[], containerId: string, cellIndex: number, insertIndex: number, section: PageSection, columns: number): PageSection[];
10749
+ /**
10750
+ * Nesting level of the container with this id: 1 at the top level, 2 inside a
10751
+ * cell, 0 when not found. Only containers have cells, so every cell level is a
10752
+ * container level.
10753
+ */
10754
+ export function containerDepth(sections: PageSection[], containerId: string, depth?: number): number;
10755
+ /** Deep copy with a fresh id for the section and every descendant. */
10756
+ export function cloneWithNewIds(section: PageSection): PageSection;
10757
+ /** Resolves a type key to its registered type — the registry's `get`. */
10758
+ export type TypeLookup = (type: string) => PageSectionType | undefined;
10759
+ /**
10760
+ * Height of a container's subtree: 1 for a container with no nested containers,
10761
+ * otherwise 1 + the tallest nested container's height. 0 for a non-container, so
10762
+ * it composes with containerDepth to bound how deep a moved subtree would reach:
10763
+ * dropping a section into a container at depth d puts the section's own deepest
10764
+ * descendant at d + containerHeight(section).
10765
+ */
10766
+ export function containerHeight(section: PageSection): number;
10767
+ /**
10768
+ * Re-applies each container's own grow/columns normalisation throughout the
10769
+ * tree, so a growable container never carries a trailing all-empty row once
10770
+ * committed — not only when read out via containerCells. A section whose
10771
+ * current type isn't a registered container is left untouched, preserving its
10772
+ * cells verbatim per the unknown-type contract.
10773
+ */
10774
+ export function normaliseGrowth(sections: PageSection[], lookup: TypeLookup): PageSection[];
10775
+ /**
10776
+ * Rewrites the pre-cells `children` shape as `cells`. The old grid was always
10777
+ * DEFAULT_WIDTHS wide, and the slot count was the layout, so cells are padded
10778
+ * out to the declared slot count, then rounded up to a whole number of
10779
+ * DEFAULT_WIDTHS-wide rows; never trimmed.
10780
+ */
10781
+ export function migrateSection(section: PageSection, type?: PageSectionType): PageSection;
10782
+ /**
10783
+ * Normalises externally supplied sections: migrates the old shape, gives every
10784
+ * section a unique id, drops malformed entries, sanitises layouts, caps nesting
10785
+ * and clamps sizes. Pure — warnings are returned for the caller to log.
10786
+ */
10787
+ export function normaliseSections(sections: unknown, lookup: TypeLookup): {
10788
+ sections: PageSection[];
10789
+ warnings: string[];
10790
+ };
10791
+ }
10577
10792
  declare module "components/page-builder/page-registry" {
10578
10793
  import type { PageSectionType } from "components/page-builder/page.types";
10579
10794
  /**
@@ -10673,6 +10888,8 @@ declare module "components/page-builder/modules/page-section-card/page-section-c
10673
10888
  selected: boolean;
10674
10889
  /** Set when the section's type has no registered template — renders greyed. */
10675
10890
  unknown: boolean;
10891
+ /** Set when the builder pins this section to the page — drops the remove action. */
10892
+ locked: boolean;
10676
10893
  protected updated(changed: PropertyValues): void;
10677
10894
  private _action;
10678
10895
  private _actionKeydown;
@@ -10690,7 +10907,7 @@ declare module "components/page-builder/modules/page-section-card/index" {
10690
10907
  }
10691
10908
  }
10692
10909
  declare module "components/page-builder/page-builder.component" {
10693
- import { type CSSResultGroup, type PropertyValues } from 'lit';
10910
+ import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
10694
10911
  import { type PageSection, type PageSectionType, type PageState } from "components/page-builder/page.types";
10695
10912
  import ZincElement from "internal/zinc-element";
10696
10913
  import ZnCollapsible from "components/collapsible/index";
@@ -10698,6 +10915,7 @@ declare module "components/page-builder/page-builder.component" {
10698
10915
  import ZnInput from "components/input/index";
10699
10916
  import ZnPagePaletteItem from "components/page-builder/modules/page-palette-item/index";
10700
10917
  import ZnPageSectionCard from "components/page-builder/modules/page-section-card/index";
10918
+ import ZnToggle from "components/toggle/index";
10701
10919
  /**
10702
10920
  * @summary A config-driven page composer: a palette of predefined section types, a linear
10703
10921
  * canvas of section cards, and an inspector for editing each section's content.
@@ -10715,10 +10933,14 @@ declare module "components/page-builder/page-builder.component" {
10715
10933
  * @event zn-page-selection-change - Emitted when the selected section changes. `event.detail.sectionId`.
10716
10934
  *
10717
10935
  * @slot config - `<template type="…">` declarations; never displayed. Each template's attributes
10718
- * (type, label, icon, icon-library, color, category, description, slots, accepts) declare a
10719
- * palette entry and its content declares the inspector form for that type. `slots` makes the
10720
- * section a container with that many child slots; `accepts` is a comma-separated list of the
10721
- * type keys its slots allow.
10936
+ * (type, label, icon, icon-library, color, category, description, container, columns, widths,
10937
+ * grow, slots, accepts) declare a palette entry and its content declares the inspector form for
10938
+ * that type. `container` makes the type a container whose editor-configurable layout starts from
10939
+ * `columns` (seeds equal widths) or `widths` (explicit weights, wins over `columns`); `grow` seeds
10940
+ * a growable instance. `accepts` is a comma-separated list of the type keys its cells allow —
10941
+ * omitted on a `container` type, any type is allowed subject to the nesting cap. `slots` is
10942
+ * @deprecated: it declares a fixed-slot container of `DEFAULT_WIDTHS` columns pinned to that many
10943
+ * cells, and keeps the old any-non-container-type rule when `accepts` is omitted.
10722
10944
  * @slot header-left - Actions shown on the left of the header bar.
10723
10945
  * @slot header-right - Actions shown on the right of the header bar.
10724
10946
  *
@@ -10727,6 +10949,8 @@ declare module "components/page-builder/page-builder.component" {
10727
10949
  * @csspart palette - The left palette panel.
10728
10950
  * @csspart canvas - The centre section-card canvas.
10729
10951
  * @csspart inspector - The right panel while a section is selected.
10952
+ * @csspart inspector-header - The inspector's fixed header (icon, section name, type, close).
10953
+ * @csspart inspector-body - The inspector's scrolling form area.
10730
10954
  */
10731
10955
  export default class ZnPageBuilder extends ZincElement {
10732
10956
  static styles: CSSResultGroup;
@@ -10736,6 +10960,7 @@ declare module "components/page-builder/page-builder.component" {
10736
10960
  'zn-input': typeof ZnInput;
10737
10961
  'zn-page-palette-item': typeof ZnPagePaletteItem;
10738
10962
  'zn-page-section-card': typeof ZnPageSectionCard;
10963
+ 'zn-toggle': typeof ZnToggle;
10739
10964
  };
10740
10965
  private readonly formControlController;
10741
10966
  /** The name of the control, submitted as a name/value pair with form data. */
@@ -10746,6 +10971,13 @@ declare module "components/page-builder/page-builder.component" {
10746
10971
  config: string;
10747
10972
  heading: string;
10748
10973
  subheading: string;
10974
+ /**
10975
+ * Section type key that must lead the page. The builder hoists an existing section of
10976
+ * that type to the top, or inserts an empty one, and pins it there: it can't be
10977
+ * removed, reordered or dragged into a slot, and nothing can be dropped above it.
10978
+ * Its content stays fully editable in the inspector.
10979
+ */
10980
+ requiredFirst: string;
10749
10981
  /** Section types to make available, registered into the internal registry. */
10750
10982
  sectionTypes: PageSectionType[];
10751
10983
  /** Collapses the left palette. Auto-set when the builder becomes narrow. */
@@ -10765,10 +10997,10 @@ declare module "components/page-builder/page-builder.component" {
10765
10997
  /** Index of the drop zone whose "+" type picker is open, if any. */
10766
10998
  private _pickerIndex;
10767
10999
  private _dragOverIndex;
10768
- /** The container slot a drag is currently over, if any. */
10769
- private _slotDragOver;
10770
- /** The container slot whose "+" type picker is open, if any. */
10771
- private _slotPicker;
11000
+ /** The cell a drag is currently over, if any. */
11001
+ private _cellDragOver;
11002
+ /** The cell whose "+" type picker is open, if any. */
11003
+ private _cellPicker;
10772
11004
  /** The stamped config form for the selected section; rebuilt on selection change. */
10773
11005
  private _form;
10774
11006
  private readonly _hasSlot;
@@ -10797,6 +11029,7 @@ declare module "components/page-builder/page-builder.component" {
10797
11029
  /** Sets a custom validation message. Pass an empty string to restore validity. */
10798
11030
  setCustomValidity(_message?: string): void;
10799
11031
  handleConfigChange(): void;
11032
+ handleRequiredFirstChange(): void;
10800
11033
  handleSectionTypesChange(): void;
10801
11034
  registerSectionType(type: PageSectionType): this;
10802
11035
  registerSectionTypes(types: PageSectionType[]): this;
@@ -10842,42 +11075,82 @@ declare module "components/page-builder/page-builder.component" {
10842
11075
  private _registerSlottedTemplates;
10843
11076
  /** Normalises and installs an externally provided state; resets selection. */
10844
11077
  private _applyExternalState;
10845
- /** Finds a section by id, searching top-level sections and slotted children. */
10846
- private _findSection;
10847
- /** New sections array with the section patched wherever it lives (top level or slot). */
10848
- private _patchSection;
10849
- /** Detaches a section wherever it lives: removed from the top level, or its slot nulled. */
10850
- private _extract;
10851
- /** Installs a new state from a user edit and notifies listeners. */
11078
+ /**
11079
+ * Id of the section pinned to the top of the page, or null when `required-first`
11080
+ * is unset. Derived from the state rather than stored on it, so nothing about the
11081
+ * lock leaks into the persisted config.
11082
+ */
11083
+ private get _pinnedId();
11084
+ private _isPinned;
11085
+ /** Lowest top-level index a section may be added or moved to. */
11086
+ private get _firstFreeIndex();
11087
+ /**
11088
+ * Sections reordered so `required-first` leads the page: an existing section of that
11089
+ * type is hoisted to the front, otherwise an empty one is prepended. Returns the
11090
+ * argument unchanged when there is nothing to do, so callers can compare by identity.
11091
+ */
11092
+ private _requireFirst;
11093
+ /**
11094
+ * Installs a new state from a user edit and notifies listeners. Re-applies
11095
+ * growth normalisation across the tree first, so a growable container never
11096
+ * carries a trailing empty row past this point — the read path (`containerCells`)
11097
+ * already hides it, but state/value/zn-page-change/auto-save must not diverge
11098
+ * from what the canvas renders.
11099
+ */
10852
11100
  private _commit;
10853
11101
  private _selectedSection;
10854
11102
  private _select;
10855
11103
  private _pushHistory;
10856
11104
  undo: () => void;
10857
11105
  redo: () => void;
11106
+ /** A fresh section of a registered type, seeded with layout/cells when it's a container. */
11107
+ private _newSection;
10858
11108
  /** Adds a section of a registered type at `index` (default: end). Returns null for unknown types. */
10859
11109
  addSection(type: string, index?: number): PageSection | null;
10860
- /** Adds a new section of a registered type into a container's slot. Returns null if not allowed. */
10861
- addSectionToSlot(type: string, containerId: string, slotIndex: number): PageSection | null;
10862
11110
  private _removeSection;
10863
11111
  private _duplicateSection;
10864
- /** Moves a section (top-level or slotted) to a top-level position. */
11112
+ /** Locates a section living inside a container cell. */
11113
+ private _findCellOwner;
11114
+ /** Moves a section (top-level or inside a cell) to a top-level position. */
10865
11115
  private _moveSection;
10866
- /**
10867
- * Moves a section into a container's slot. Dropping onto an occupied slot swaps
10868
- * the two children (slot-to-slot reordering); top-level sections and containers
10869
- * only enter empty slots / never enter slots respectively.
10870
- */
10871
- private _moveToSlot;
11116
+ /** Id of the section currently being dragged — dataTransfer is unreadable during dragover. */
11117
+ private _draggingId;
10872
11118
  private _onCardDragStart;
10873
11119
  /** Whether a drag carries one of the builder's own payloads. */
10874
11120
  private _isPageDrag;
10875
- private _onSlotDragOver;
10876
- private _onSlotDrop;
10877
11121
  private _onZoneDragOver;
10878
11122
  private _onZoneDrop;
10879
11123
  private _onCanvasDragOver;
10880
11124
  private _onCanvasDrop;
11125
+ /**
11126
+ * Whether `typeKey`'s registered type is allowed into this container by its
11127
+ * `accepts`/`slots=` rule — the nesting depth cap is a separate concern,
11128
+ * checked by each caller against its own notion of how tall the dropped
11129
+ * subtree is.
11130
+ */
11131
+ private _acceptsType;
11132
+ /**
11133
+ * Whether `typeKey` may be dropped into this container's cells from the
11134
+ * palette. A newly created section always has height 1, so the depth cap
11135
+ * only needs the target's own depth.
11136
+ */
11137
+ private _acceptsInCell;
11138
+ /**
11139
+ * Whether a dragged, already-placed section may land in this container's
11140
+ * cells. Unlike a palette drop, the moved subtree can already be several
11141
+ * containers tall (it may itself hold a nested container), so the cap has to
11142
+ * account for that height, not just the target's depth: dropping `moved` at
11143
+ * a new depth of `containerDepth(container) + 1` puts its own deepest
11144
+ * descendant at `containerDepth(container) + containerHeight(moved)`.
11145
+ */
11146
+ private _canMoveIntoCell;
11147
+ private _onCellDragOver;
11148
+ private _onCellDrop;
11149
+ /** Adds a new section of a registered type into a container cell. */
11150
+ addSectionToCell(type: string, containerId: string, cellIndex: number, insertIndex?: number): PageSection | null;
11151
+ private _moveToCell;
11152
+ /** Types offerable in a container's cells. */
11153
+ private _cellTypes;
10881
11154
  private _onCardKeydown;
10882
11155
  private _renderPalette;
10883
11156
  private _renderPaletteItem;
@@ -10887,10 +11160,17 @@ declare module "components/page-builder/page-builder.component" {
10887
11160
  /** The one card template both the page list and slot cells render. */
10888
11161
  private _renderSectionCard;
10889
11162
  private _renderCard;
10890
- private _renderSlot;
10891
- /** Types allowed in a container's slots: non-containers, filtered by its accepts list. */
10892
- private _slotTypes;
10893
- /** The one type-picker template both drop zones and slot cells render. */
11163
+ /**
11164
+ * A card, or — for a container its card plus its own cell grid. Shared by the
11165
+ * top-level section list and container-cell stacks, so nesting renders at every level.
11166
+ */
11167
+ private _renderNode;
11168
+ /** A container's cell grid: one track per width, one stack per cell. */
11169
+ private _renderCells;
11170
+ private _renderCell;
11171
+ /** Thin insertion target between two cards in a stack. */
11172
+ private _renderCellStrip;
11173
+ /** The one type-picker template both drop zones and container cells render. */
10894
11174
  private _renderTypePicker;
10895
11175
  private _renderDropZone;
10896
11176
  /**
@@ -10904,8 +11184,16 @@ declare module "components/page-builder/page-builder.component" {
10904
11184
  private _onInspectorInput;
10905
11185
  private _updateSectionData;
10906
11186
  private _renameSection;
11187
+ /** Replaces a container's layout, keeping cells consistent with it. */
11188
+ private _setLayout;
11189
+ private _setColumns;
11190
+ private _setWidth;
11191
+ private _setGrow;
11192
+ /** Pads with empty rows, or trims trailing empty rows down to the last occupied one. */
11193
+ private _setRows;
11194
+ private _renderLayoutGroup;
10907
11195
  private _renderInspector;
10908
- render(): import("lit-html").TemplateResult<1>;
11196
+ render(): TemplateResult<1>;
10909
11197
  }
10910
11198
  }
10911
11199
  declare module "components/page-builder/index" {
@@ -11362,6 +11650,289 @@ declare module "components/theme-editor/index" {
11362
11650
  }
11363
11651
  }
11364
11652
  }
11653
+ declare module "components/schedule-builder/schedule-builder.component" {
11654
+ import { type CSSResultGroup } from 'lit';
11655
+ import ZincElement from "internal/zinc-element";
11656
+ import ZnIcon from "components/icon/index";
11657
+ import ZnInput from "components/input/index";
11658
+ import ZnOption from "components/option/index";
11659
+ import ZnSelect from "components/select/index";
11660
+ import type { ZincFormControl } from "internal/zinc-element";
11661
+ /** The seven weekday keys used throughout the schedule. */
11662
+ export type ScheduleDay = 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
11663
+ /** A single opening period within a day. Times are `HH:MM` in the schedule's own timezone. */
11664
+ export interface ScheduleRange {
11665
+ start: string;
11666
+ end: string;
11667
+ }
11668
+ /**
11669
+ * A dated deviation from the weekly pattern. Exceptions are never edited by the builder, they only
11670
+ * annotate it — the surrounding application owns them.
11671
+ */
11672
+ export interface ScheduleException {
11673
+ id?: string;
11674
+ /** Human readable name, e.g. `Christmas Eve — early close`. */
11675
+ label?: string;
11676
+ /** A single calendar date (`YYYY-MM-DD`). */
11677
+ date?: string;
11678
+ /** Inclusive start of a multi-day exception (`YYYY-MM-DD`). */
11679
+ from?: string;
11680
+ /** Inclusive end of a multi-day exception (`YYYY-MM-DD`). */
11681
+ to?: string;
11682
+ /** Weekdays the exception applies to. Defaults to every weekday inside the date window. */
11683
+ days?: ScheduleDay[];
11684
+ /** When true the affected days close outright and `ranges` is ignored. */
11685
+ closed?: boolean;
11686
+ /** Replacement opening hours for the affected days. */
11687
+ ranges?: ScheduleRange[];
11688
+ }
11689
+ export type ScheduleDayMap = Record<ScheduleDay, ScheduleRange[]>;
11690
+ /** The shape serialised into the form value. */
11691
+ export interface ScheduleValue {
11692
+ timezone?: string;
11693
+ days: ScheduleDayMap;
11694
+ exceptions: ScheduleException[];
11695
+ }
11696
+ export type ScheduleView = 'calendar' | 'form';
11697
+ /** The named sets accepted by `timezones`, alongside explicit IANA names. */
11698
+ export type ScheduleTimezoneSet = 'en' | 'offsets' | 'common' | 'all';
11699
+ /**
11700
+ * @summary Builds a weekly opening-hours schedule as a drag-to-paint calendar or a compact list of
11701
+ * time ranges, and posts the result as JSON.
11702
+ * @documentation https://zinc.style/components/schedule-builder
11703
+ * @status experimental
11704
+ * @since 1.0
11705
+ *
11706
+ * @dependency zn-icon
11707
+ * @dependency zn-input
11708
+ * @dependency zn-option
11709
+ * @dependency zn-select
11710
+ *
11711
+ * @event zn-change - Emitted when the schedule changes.
11712
+ *
11713
+ * @slot label - The schedule's label. Alternatively, use the `label` attribute.
11714
+ * @slot help-text - Text that describes how to use the schedule. Alternatively, use the `help-text` attribute.
11715
+ *
11716
+ * @csspart form-control - The form control that wraps the builder, label and help text.
11717
+ * @csspart base - The component's base wrapper.
11718
+ * @csspart toolbar - The row above the builder holding the hint, legend and view toggle.
11719
+ * @csspart calendar - The calendar view wrapper.
11720
+ * @csspart list - The form (list) view wrapper.
11721
+ * @csspart summary - The summary panel beside the calendar.
11722
+ *
11723
+ * @cssproperty --slot-height - The height of a single time slot in the calendar. Defaults to `18px`.
11724
+ * @cssproperty --gutter-width - The width of the calendar's time gutter. Defaults to `64px`.
11725
+ * @cssproperty --open-color - The fill used for open hours.
11726
+ * @cssproperty --reduced-color - The fill used for hours an exception removes.
11727
+ */
11728
+ export default class ZnScheduleBuilder extends ZincElement implements ZincFormControl {
11729
+ static styles: CSSResultGroup;
11730
+ static formAssociated: boolean;
11731
+ static dependencies: {
11732
+ 'zn-icon': typeof ZnIcon;
11733
+ 'zn-input': typeof ZnInput;
11734
+ 'zn-option': typeof ZnOption;
11735
+ 'zn-select': typeof ZnSelect;
11736
+ };
11737
+ private readonly formControlController;
11738
+ private readonly hasSlotController;
11739
+ private readonly localize;
11740
+ private readonly internals;
11741
+ private canvas;
11742
+ private _days;
11743
+ private _exceptions;
11744
+ private _dragPreview;
11745
+ private _editing;
11746
+ private _dragMode;
11747
+ private _dragAnchor;
11748
+ private _dragPointerId;
11749
+ /** The name of the form control, submitted as a name/value pair with form data. */
11750
+ name: string;
11751
+ /** The schedule as a JSON string. This is what gets posted with the form. */
11752
+ value: string;
11753
+ /** The default value, used when resetting the containing form. */
11754
+ defaultValue: string;
11755
+ /** The schedule's label. If you need to display HTML, use the `label` slot instead. */
11756
+ label: string;
11757
+ /** The schedule's help text. If you need to display HTML, use the `help-text` slot instead. */
11758
+ helpText: string;
11759
+ /** Which view is showing. */
11760
+ view: ScheduleView;
11761
+ /** The word used for hours the schedule covers, in the legend and in labels. */
11762
+ openLabel: string;
11763
+ /** The word used for hours the schedule doesn't cover, in the legend and against empty days. */
11764
+ closedLabel: string;
11765
+ /** Hides the calendar/form view toggle. */
11766
+ noToggle: boolean;
11767
+ /** Hides the summary panel beside the calendar. */
11768
+ hideSummary: boolean;
11769
+ /** The first hour shown in the calendar. */
11770
+ startHour: number;
11771
+ /** The last hour shown in the calendar. */
11772
+ endHour: number;
11773
+ /** The granularity of the calendar grid and the time inputs, in minutes. */
11774
+ interval: number;
11775
+ /** The weekday the week starts on. */
11776
+ weekStart: ScheduleDay;
11777
+ /** Displays times as 12 or 24 hour. The serialised value is always 24 hour `HH:MM`. */
11778
+ timeFormat: '12' | '24';
11779
+ /**
11780
+ * The IANA timezone the hours are shown in. Accepts `auto` for the viewer's own timezone. Defaults
11781
+ * to `save-timezone`, so nothing is converted until you ask for it. Changing this only re-labels
11782
+ * the same underlying hours; the value never moves.
11783
+ */
11784
+ displayTimezone: string;
11785
+ /**
11786
+ * The IANA timezone the value is stored in. Defaults to `UTC` as soon as the schedule is
11787
+ * timezone-aware (a display timezone is set, or the picker is shown), and to no timezone at all
11788
+ * otherwise — in which case the times are stored exactly as they are shown.
11789
+ */
11790
+ saveTimezone: string;
11791
+ /** Shows the timezone picker, letting the user read the schedule in any timezone. */
11792
+ showTimezone: boolean;
11793
+ /**
11794
+ * The timezones offered by the picker, as IANA names or one of the named sets — `en` (the four US
11795
+ * zones, the UK and Australia, under those names), `offsets` (one zone per UTC offset, the
11796
+ * default), `common` (every offset plus the world's major centres) or `all` (the complete IANA
11797
+ * list). Names and sets can be mixed, e.g. `en Asia/Tokyo`.
11798
+ */
11799
+ timezones: string[];
11800
+ /**
11801
+ * The date (`YYYY-MM-DD`) used to resolve timezone offsets. A weekly pattern has no date of its
11802
+ * own, so one has to be picked to know whether daylight saving applies; today is used by default.
11803
+ */
11804
+ referenceDate: string;
11805
+ /** Disables the schedule. */
11806
+ disabled: boolean;
11807
+ /** Renders the schedule without any editing affordances. */
11808
+ readonly: boolean;
11809
+ /** Makes the schedule a required field, invalid until at least one period is open. */
11810
+ required: boolean;
11811
+ /** The id of the form to associate with, when the control sits outside of it. */
11812
+ form: string;
11813
+ constructor();
11814
+ /**
11815
+ * The schedule as a plain object, with `days` in the save timezone. Assigning to it replaces the
11816
+ * whole schedule.
11817
+ */
11818
+ get schedule(): ScheduleValue;
11819
+ set schedule(schedule: ScheduleValue | null | undefined);
11820
+ /** The exceptions annotating the schedule. Also readable from, and written into, the value. */
11821
+ get exceptions(): ScheduleException[];
11822
+ set exceptions(exceptions: ScheduleException[] | null | undefined);
11823
+ /** Gets the validity state object. */
11824
+ get validity(): ValidityState;
11825
+ /** Gets the validation message. */
11826
+ get validationMessage(): string;
11827
+ /** Whether the schedule carries a timezone at all. */
11828
+ private get _isZoned();
11829
+ /** The timezone the value is stored in. Empty means the times are stored exactly as shown. */
11830
+ private get _saveZone();
11831
+ /** The timezone the grid and list are drawn in. */
11832
+ private get _displayZone();
11833
+ /** The moment used to resolve daylight saving for both zones. */
11834
+ private get _reference();
11835
+ /** Minutes to add to a stored time to get the time shown. */
11836
+ private get _offsetDelta();
11837
+ private get _orderedDays();
11838
+ private get _interval();
11839
+ private get _startMinute();
11840
+ private get _endMinute();
11841
+ private get _slotCount();
11842
+ private get _slotsPerHour();
11843
+ private get _isEditable();
11844
+ private get _hasHours();
11845
+ connectedCallback(): void;
11846
+ firstUpdated(): void;
11847
+ handleValueChange(): void;
11848
+ handleValidationStateChange(): void;
11849
+ /** Checks validity but does not show a validation message. */
11850
+ checkValidity(): boolean;
11851
+ /** Gets the associated form, if one exists. */
11852
+ getForm(): HTMLFormElement | null;
11853
+ /** Checks for validity and shows the browser's validation message if the control is invalid. */
11854
+ reportValidity(): boolean;
11855
+ /** Sets a custom validation message. Pass an empty string to restore validity. */
11856
+ setCustomValidity(message: string): void;
11857
+ /** Replaces the hours for a single day, in the save timezone. */
11858
+ setDay(day: ScheduleDay, ranges: ScheduleRange[]): void;
11859
+ /** Reads the hours for a single day, in the save timezone. */
11860
+ getDay(day: ScheduleDay): ScheduleRange[];
11861
+ /** The hours as currently shown, in the display timezone. */
11862
+ get displayedDays(): ScheduleDayMap;
11863
+ /** Replaces the hours for a single day, given in the display timezone. */
11864
+ setDisplayDay(day: ScheduleDay, ranges: ScheduleRange[]): void;
11865
+ formResetCallback(): void;
11866
+ formStateRestoreCallback(restoredValue: string): void;
11867
+ private _customValidity;
11868
+ private _cloneDays;
11869
+ private _serialise;
11870
+ /** Accepts a JSON string, a full `ScheduleValue`, or a bare day map. */
11871
+ private _applySchedule;
11872
+ private _syncFormValue;
11873
+ private _commit;
11874
+ private _formatTime;
11875
+ private _formatRange;
11876
+ private _formatDate;
11877
+ private _summariseDay;
11878
+ /** The stored hours rotated into the display timezone. */
11879
+ private get _storedAsShown();
11880
+ /** The hours as drawn, which is the drag preview while a drag is in flight. */
11881
+ private get _shownDays();
11882
+ /** Stores hours that were edited in display coordinates, rotating them back to the save timezone. */
11883
+ private _commitShownDays;
11884
+ /** The exceptions that touch a given weekday and actually change its hours. */
11885
+ private _exceptionsForDay;
11886
+ /**
11887
+ * The week spans an exception takes away, in display coordinates. Computed as spans rather than
11888
+ * per-day ranges because a timezone rotation can move an exception's hours onto another weekday.
11889
+ */
11890
+ private get _shownReductionSpans();
11891
+ /** The week minute a calendar cell sits on, measured from Monday 00:00. */
11892
+ private _slotMinute;
11893
+ private _slotState;
11894
+ private _pointerPosition;
11895
+ /** Paints the rectangle between the drag anchor and the cursor onto a copy of the shown schedule. */
11896
+ private _buildDragPreview;
11897
+ private _handleCanvasPointerDown;
11898
+ private _handleCanvasPointerMove;
11899
+ private _handleCanvasPointerUp;
11900
+ private _handleCanvasPointerCancel;
11901
+ private _handleViewToggle;
11902
+ /** Picks a sensible slot for a newly added range: the first hour-wide gap in the day. */
11903
+ private _nextFreeRange;
11904
+ private _handleAddRange;
11905
+ private _handleRemoveRange;
11906
+ private _handleRangeEdit;
11907
+ private _handleEditorKeyDown;
11908
+ private _handleEditorFocusOut;
11909
+ private _renderToolbar;
11910
+ /** The picker's options: the configured list plus whatever zones are already in play. */
11911
+ private get _timezoneOptions();
11912
+ private _handleTimezoneChange;
11913
+ private _renderTimezonePicker;
11914
+ private _renderCalendar;
11915
+ private _renderCalendarColumn;
11916
+ private _renderSummary;
11917
+ private _renderList;
11918
+ private _renderListRow;
11919
+ private _renderRangeChip;
11920
+ private _renderRangeEditor;
11921
+ /** The exception annotation shown against a day in the form view. */
11922
+ private _dayNote;
11923
+ render(): import("lit-html").TemplateResult<1>;
11924
+ }
11925
+ }
11926
+ declare module "components/schedule-builder/index" {
11927
+ import ZnScheduleBuilder from "components/schedule-builder/schedule-builder.component";
11928
+ export * from "components/schedule-builder/schedule-builder.component";
11929
+ export default ZnScheduleBuilder;
11930
+ global {
11931
+ interface HTMLElementTagNameMap {
11932
+ 'zn-schedule-builder': ZnScheduleBuilder;
11933
+ }
11934
+ }
11935
+ }
11365
11936
  declare module "utilities/form" {
11366
11937
  export { clearFormStoreValues } from "internal/form";
11367
11938
  }
@@ -11722,6 +12293,7 @@ declare module "zinc" {
11722
12293
  export { default as ThemeEditor } from "components/theme-editor/index";
11723
12294
  export { default as SlashMenu } from "components/slash-menu/index";
11724
12295
  export { default as SlashItem } from "components/slash-item/index";
12296
+ export { default as ScheduleBuilder } from "components/schedule-builder/index";
11725
12297
  export { default as ZincElement } from "internal/zinc-element";
11726
12298
  export * from "utilities/on";
11727
12299
  export * from "utilities/query";