@peektravel/app-utilities 0.5.3 → 0.6.0

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.
@@ -71,6 +71,25 @@ declare abstract class OdyElement extends HTMLElement {
71
71
  * to call on every attribute change without losing the original children.
72
72
  */
73
73
  protected mount(chrome: string): void;
74
+ /**
75
+ * Move slotted content out of a portaled chrome node (dialog/panel/bubble)
76
+ * back into the captured slot fragment, so it survives `disconnectedCallback`
77
+ * tearing that node down and is re-slotted by the re-render on reconnect.
78
+ * Portal components call this before `removePortal(...)`; without it, moving
79
+ * the element in the DOM would permanently lose the consumer's content.
80
+ */
81
+ protected reclaimPortaledSlot(node: ParentNode | null | undefined): void;
82
+ appendChild<T extends Node>(node: T): T;
83
+ insertBefore<T extends Node>(node: T, child: Node | null): T;
84
+ removeChild<T extends Node>(child: T): T;
85
+ replaceChild<T extends Node>(node: Node, child: T): T;
86
+ /**
87
+ * Append an element the component owns (chrome / portaled nodes) as a direct
88
+ * host child, bypassing the slot-forwarding {@link appendChild} override.
89
+ * Portal-based components use this to re-home a portaled node before a
90
+ * re-render rebuilds the chrome.
91
+ */
92
+ protected adopt(node: Node): void;
74
93
  /** Read a string attribute with a fallback. */
75
94
  protected attr(name: string, fallback?: string): string;
76
95
  /** Whether a boolean attribute is present and not explicitly `"false"`. */
@@ -92,6 +111,13 @@ declare function escapeHtml(value: string): string;
92
111
  /**
93
112
  * Join class-name fragments, dropping falsy entries. Keeps component templates
94
113
  * readable when classes are conditional.
114
+ *
115
+ * The result is HTML-escaped because it is always interpolated into a
116
+ * `class="…"` attribute (or assigned to `el.className`). Many components build
117
+ * a class fragment from a raw, unvalidated attribute (`ody-tag--${attr('color')}`,
118
+ * `--size-${attr('size')}`, …); without escaping, a value containing a `"` would
119
+ * break out of the attribute and inject markup (DOM XSS). Legitimate class tokens
120
+ * contain none of the escaped characters, so this is a no-op for real class names.
95
121
  */
96
122
  declare function classes(...parts: Array<string | false | null | undefined>): string;
97
123
  /**
@@ -433,6 +459,8 @@ interface OdyToggleOption {
433
459
  * Attributes:
434
460
  * - `options` — JSON array of {@link OdyToggleOption} (e.g.
435
461
  * `options='[{"value":"day","label":"Day"},{"value":"week","label":"Week"}]'`).
462
+ * Also settable as a JS **property** (`el.options = [...]`) — accepts an array
463
+ * or a JSON string, so a framework binding (`options={items}`) works directly.
436
464
  * - `selected` — the currently-selected option value.
437
465
  * - `size` — `base` | `small` (default `base`).
438
466
  * - `disabled` — disables the whole group.
@@ -440,6 +468,9 @@ interface OdyToggleOption {
440
468
  declare class OdyToggleButton extends OdyElement {
441
469
  #private;
442
470
  static observedAttributes: string[];
471
+ /** Options: the JS property wins, else the JSON `options` attribute. */
472
+ get options(): OdyToggleOption[];
473
+ set options(next: OdyToggleOption[] | string);
443
474
  connectedCallback(): void;
444
475
  disconnectedCallback(): void;
445
476
  protected render(): void;
@@ -760,6 +791,13 @@ declare class OdyMoneyInput extends OdyElement {
760
791
  set value(next: string);
761
792
  /** Decimal precision for the configured currency. */
762
793
  get precision(): number;
794
+ /**
795
+ * Reflect a `value` change into the live control in place instead of a full
796
+ * re-render, so a framework-controlled `value` binding never drops focus or
797
+ * caret while the user is typing. Other observed attributes change the chrome
798
+ * and still re-render via the base implementation.
799
+ */
800
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
763
801
  protected render(): void;
764
802
  }
765
803
 
@@ -783,6 +821,13 @@ declare class OdyPercentageInput extends OdyElement {
783
821
  /** Current percentage value. */
784
822
  get value(): string;
785
823
  set value(next: string);
824
+ /**
825
+ * Reflect a `value` change into the live control in place instead of a full
826
+ * re-render, so a framework-controlled `value` binding never drops focus or
827
+ * caret while the user is typing. Other observed attributes change the chrome
828
+ * and still re-render via the base implementation.
829
+ */
830
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
786
831
  protected render(): void;
787
832
  }
788
833
 
@@ -810,6 +855,12 @@ declare class OdyCheckbox extends OdyElement {
810
855
  /** Convenience alias for {@link checked}. */
811
856
  get value(): boolean;
812
857
  set value(next: boolean);
858
+ /**
859
+ * Reflect `checked` / `indeterminate` in place instead of a full re-render, so
860
+ * selecting the box (mouse or keyboard) never rebuilds the native `<input>`
861
+ * and drops focus. Other observed attributes still re-render via the base.
862
+ */
863
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
813
864
  protected render(): void;
814
865
  }
815
866
 
@@ -838,8 +889,15 @@ declare class OdyRadioButtonGroup extends OdyElement {
838
889
  /** The selected option value. */
839
890
  get value(): string;
840
891
  set value(next: string);
841
- /** Parsed `options`; malformed JSON yields an empty list. */
892
+ /** Options: the JS property wins, else the JSON `options` attribute. */
842
893
  get options(): OdyRadioOption[];
894
+ set options(next: OdyRadioOption[] | string);
895
+ /**
896
+ * Reflect a `value` change by re-checking the matching radio in place instead
897
+ * of rebuilding the fieldset — a full re-render would replace the focused
898
+ * radio and break keyboard (arrow-key) navigation between options.
899
+ */
900
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
843
901
  protected render(): void;
844
902
  }
845
903
 
@@ -858,9 +916,13 @@ interface OdyCheckboxOption {
858
916
  * indeterminate when some are, and toggling it selects/clears every child.
859
917
  *
860
918
  * Attributes:
861
- * - `options` — JSON array, e.g. `options='[{"label":"A","value":"a"}]'`.
862
- * - `value` — comma-separated selected values (also a JS array property,
863
- * {@link value}).
919
+ * - `options` — JSON array, e.g. `options='[{"label":"A","value":"a"}]'`. Also
920
+ * a settable JS property ({@link options}): `el.options = [...]` (array or JSON
921
+ * string) mirrors `<ody-dropdown-*>`; the property is the source of truth and
922
+ * is not reflected back onto the attribute.
923
+ * - `value` — selected values (also a JS array property, {@link value}). The
924
+ * reflected attribute is a JSON array (values with commas round-trip); a
925
+ * legacy comma-separated attribute is still accepted when read.
864
926
  * - `select-all-label` — when present, renders the parent select-all row.
865
927
  * - `size` — `base` | `small` (default `small`).
866
928
  * - `disabled` — boolean flag applied to every checkbox.
@@ -870,11 +932,27 @@ interface OdyCheckboxOption {
870
932
  declare class OdyCheckboxGroup extends OdyElement {
871
933
  #private;
872
934
  static observedAttributes: string[];
873
- /** The selected option values. */
935
+ /**
936
+ * The selected option values. The reflected `value` attribute is serialized
937
+ * as a JSON array so values containing commas round-trip; a legacy
938
+ * comma-separated attribute is still read for backwards compatibility.
939
+ */
874
940
  get value(): string[];
875
941
  set value(next: string[]);
876
- /** Parsed `options`; malformed JSON yields an empty list. */
942
+ /** Options: the JS property wins, else the JSON `options` attribute. */
877
943
  get options(): OdyCheckboxOption[];
944
+ /**
945
+ * Accepts an array of `{ label, value }` (or a JSON string) and stores it as
946
+ * the source of truth. Mirrors `<ody-dropdown-*>` so `el.options = [...]`
947
+ * works, without reflecting a large JSON blob onto the DOM attribute.
948
+ */
949
+ set options(next: OdyCheckboxOption[] | string);
950
+ /**
951
+ * Reflect a `value` change by updating each checkbox (and the select-all
952
+ * parent) in place, so toggling an option keeps focus instead of rebuilding
953
+ * the group. Other observed attributes still re-render via the base.
954
+ */
955
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
878
956
  protected render(): void;
879
957
  }
880
958
 
@@ -958,6 +1036,7 @@ declare class OdyCopyButton extends OdyElement {
958
1036
  #private;
959
1037
  static observedAttributes: string[];
960
1038
  protected render(): void;
1039
+ disconnectedCallback(): void;
961
1040
  }
962
1041
 
963
1042
  /**
@@ -1164,11 +1243,13 @@ interface OdyToastOptions {
1164
1243
  * {@link toast} lazily creates and portals one for you.
1165
1244
  */
1166
1245
  declare class OdyToastHost extends OdyElement {
1246
+ #private;
1167
1247
  protected render(): void;
1168
1248
  /** Render and stack a toast, returning a handle to dismiss it early. */
1169
1249
  push(message: string, options?: OdyToastOptions): {
1170
1250
  dismiss: () => void;
1171
1251
  };
1252
+ disconnectedCallback(): void;
1172
1253
  }
1173
1254
  /**
1174
1255
  * Show a toast notification, lazily creating an {@link OdyToastHost} portaled to
@@ -1232,6 +1313,7 @@ declare abstract class OdySelectBase extends OdyElement {
1232
1313
  protected abstract triggerContent(): string;
1233
1314
  /** ARIA `aria-multiselectable` value for the listbox (`'true'`/`'false'`). */
1234
1315
  protected abstract get multiselectable(): boolean;
1316
+ connectedCallback(): void;
1235
1317
  protected render(): void;
1236
1318
  /** Open the panel and start listening for outside clicks. */
1237
1319
  protected openPanel(): void;
@@ -1344,7 +1426,8 @@ type OdyDateFormatter = (date: Date) => string;
1344
1426
  * - `range` — boolean; enables start/end range selection.
1345
1427
  * - `first-day-of-week` — `0`–`6` (0 = Sunday, default `0`).
1346
1428
  * - `placeholder` — trigger text shown when no value is selected.
1347
- * - `presets` — JSON `[{ "label", "value" }]` shortcut buttons.
1429
+ * - `presets` — JSON `[{ "label", "value" }]` shortcut buttons. Also settable
1430
+ * as a JS **property** (`el.presets = [...]`) — accepts an array or JSON string.
1348
1431
  * - `display-format` — how the selected date is shown to the user:
1349
1432
  * `short` | `medium` (default) | `long` | `full`. The displayed text is
1350
1433
  * localized via `Intl.DateTimeFormat` for the element's `lang`; the `value`
@@ -1362,10 +1445,15 @@ type OdyDateFormatter = (date: Date) => string;
1362
1445
  declare class OdyDatePicker extends OdyElement {
1363
1446
  #private;
1364
1447
  static observedAttributes: string[];
1448
+ /** Preset list: the JS property wins, else the JSON `presets` attribute. */
1449
+ get presets(): OdyDatePreset[];
1450
+ set presets(next: OdyDatePreset[] | string);
1365
1451
  /** Disable arbitrary days; not an attribute (functions can't serialize). */
1366
- isDateDisallowed?: OdyDateDisallowed;
1452
+ get isDateDisallowed(): OdyDateDisallowed | undefined;
1453
+ set isDateDisallowed(fn: OdyDateDisallowed | undefined);
1367
1454
  /** Custom trigger-label formatter; wins over `display-format`/`Intl`. */
1368
- formatDate?: OdyDateFormatter;
1455
+ get formatDate(): OdyDateFormatter | undefined;
1456
+ set formatDate(fn: OdyDateFormatter | undefined);
1369
1457
  /** Whether the popover is currently shown. */
1370
1458
  get isOpen(): boolean;
1371
1459
  /** Show the popover and focus the roving day. */
@@ -71,6 +71,25 @@ declare abstract class OdyElement extends HTMLElement {
71
71
  * to call on every attribute change without losing the original children.
72
72
  */
73
73
  protected mount(chrome: string): void;
74
+ /**
75
+ * Move slotted content out of a portaled chrome node (dialog/panel/bubble)
76
+ * back into the captured slot fragment, so it survives `disconnectedCallback`
77
+ * tearing that node down and is re-slotted by the re-render on reconnect.
78
+ * Portal components call this before `removePortal(...)`; without it, moving
79
+ * the element in the DOM would permanently lose the consumer's content.
80
+ */
81
+ protected reclaimPortaledSlot(node: ParentNode | null | undefined): void;
82
+ appendChild<T extends Node>(node: T): T;
83
+ insertBefore<T extends Node>(node: T, child: Node | null): T;
84
+ removeChild<T extends Node>(child: T): T;
85
+ replaceChild<T extends Node>(node: Node, child: T): T;
86
+ /**
87
+ * Append an element the component owns (chrome / portaled nodes) as a direct
88
+ * host child, bypassing the slot-forwarding {@link appendChild} override.
89
+ * Portal-based components use this to re-home a portaled node before a
90
+ * re-render rebuilds the chrome.
91
+ */
92
+ protected adopt(node: Node): void;
74
93
  /** Read a string attribute with a fallback. */
75
94
  protected attr(name: string, fallback?: string): string;
76
95
  /** Whether a boolean attribute is present and not explicitly `"false"`. */
@@ -92,6 +111,13 @@ declare function escapeHtml(value: string): string;
92
111
  /**
93
112
  * Join class-name fragments, dropping falsy entries. Keeps component templates
94
113
  * readable when classes are conditional.
114
+ *
115
+ * The result is HTML-escaped because it is always interpolated into a
116
+ * `class="…"` attribute (or assigned to `el.className`). Many components build
117
+ * a class fragment from a raw, unvalidated attribute (`ody-tag--${attr('color')}`,
118
+ * `--size-${attr('size')}`, …); without escaping, a value containing a `"` would
119
+ * break out of the attribute and inject markup (DOM XSS). Legitimate class tokens
120
+ * contain none of the escaped characters, so this is a no-op for real class names.
95
121
  */
96
122
  declare function classes(...parts: Array<string | false | null | undefined>): string;
97
123
  /**
@@ -433,6 +459,8 @@ interface OdyToggleOption {
433
459
  * Attributes:
434
460
  * - `options` — JSON array of {@link OdyToggleOption} (e.g.
435
461
  * `options='[{"value":"day","label":"Day"},{"value":"week","label":"Week"}]'`).
462
+ * Also settable as a JS **property** (`el.options = [...]`) — accepts an array
463
+ * or a JSON string, so a framework binding (`options={items}`) works directly.
436
464
  * - `selected` — the currently-selected option value.
437
465
  * - `size` — `base` | `small` (default `base`).
438
466
  * - `disabled` — disables the whole group.
@@ -440,6 +468,9 @@ interface OdyToggleOption {
440
468
  declare class OdyToggleButton extends OdyElement {
441
469
  #private;
442
470
  static observedAttributes: string[];
471
+ /** Options: the JS property wins, else the JSON `options` attribute. */
472
+ get options(): OdyToggleOption[];
473
+ set options(next: OdyToggleOption[] | string);
443
474
  connectedCallback(): void;
444
475
  disconnectedCallback(): void;
445
476
  protected render(): void;
@@ -760,6 +791,13 @@ declare class OdyMoneyInput extends OdyElement {
760
791
  set value(next: string);
761
792
  /** Decimal precision for the configured currency. */
762
793
  get precision(): number;
794
+ /**
795
+ * Reflect a `value` change into the live control in place instead of a full
796
+ * re-render, so a framework-controlled `value` binding never drops focus or
797
+ * caret while the user is typing. Other observed attributes change the chrome
798
+ * and still re-render via the base implementation.
799
+ */
800
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
763
801
  protected render(): void;
764
802
  }
765
803
 
@@ -783,6 +821,13 @@ declare class OdyPercentageInput extends OdyElement {
783
821
  /** Current percentage value. */
784
822
  get value(): string;
785
823
  set value(next: string);
824
+ /**
825
+ * Reflect a `value` change into the live control in place instead of a full
826
+ * re-render, so a framework-controlled `value` binding never drops focus or
827
+ * caret while the user is typing. Other observed attributes change the chrome
828
+ * and still re-render via the base implementation.
829
+ */
830
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
786
831
  protected render(): void;
787
832
  }
788
833
 
@@ -810,6 +855,12 @@ declare class OdyCheckbox extends OdyElement {
810
855
  /** Convenience alias for {@link checked}. */
811
856
  get value(): boolean;
812
857
  set value(next: boolean);
858
+ /**
859
+ * Reflect `checked` / `indeterminate` in place instead of a full re-render, so
860
+ * selecting the box (mouse or keyboard) never rebuilds the native `<input>`
861
+ * and drops focus. Other observed attributes still re-render via the base.
862
+ */
863
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
813
864
  protected render(): void;
814
865
  }
815
866
 
@@ -838,8 +889,15 @@ declare class OdyRadioButtonGroup extends OdyElement {
838
889
  /** The selected option value. */
839
890
  get value(): string;
840
891
  set value(next: string);
841
- /** Parsed `options`; malformed JSON yields an empty list. */
892
+ /** Options: the JS property wins, else the JSON `options` attribute. */
842
893
  get options(): OdyRadioOption[];
894
+ set options(next: OdyRadioOption[] | string);
895
+ /**
896
+ * Reflect a `value` change by re-checking the matching radio in place instead
897
+ * of rebuilding the fieldset — a full re-render would replace the focused
898
+ * radio and break keyboard (arrow-key) navigation between options.
899
+ */
900
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
843
901
  protected render(): void;
844
902
  }
845
903
 
@@ -858,9 +916,13 @@ interface OdyCheckboxOption {
858
916
  * indeterminate when some are, and toggling it selects/clears every child.
859
917
  *
860
918
  * Attributes:
861
- * - `options` — JSON array, e.g. `options='[{"label":"A","value":"a"}]'`.
862
- * - `value` — comma-separated selected values (also a JS array property,
863
- * {@link value}).
919
+ * - `options` — JSON array, e.g. `options='[{"label":"A","value":"a"}]'`. Also
920
+ * a settable JS property ({@link options}): `el.options = [...]` (array or JSON
921
+ * string) mirrors `<ody-dropdown-*>`; the property is the source of truth and
922
+ * is not reflected back onto the attribute.
923
+ * - `value` — selected values (also a JS array property, {@link value}). The
924
+ * reflected attribute is a JSON array (values with commas round-trip); a
925
+ * legacy comma-separated attribute is still accepted when read.
864
926
  * - `select-all-label` — when present, renders the parent select-all row.
865
927
  * - `size` — `base` | `small` (default `small`).
866
928
  * - `disabled` — boolean flag applied to every checkbox.
@@ -870,11 +932,27 @@ interface OdyCheckboxOption {
870
932
  declare class OdyCheckboxGroup extends OdyElement {
871
933
  #private;
872
934
  static observedAttributes: string[];
873
- /** The selected option values. */
935
+ /**
936
+ * The selected option values. The reflected `value` attribute is serialized
937
+ * as a JSON array so values containing commas round-trip; a legacy
938
+ * comma-separated attribute is still read for backwards compatibility.
939
+ */
874
940
  get value(): string[];
875
941
  set value(next: string[]);
876
- /** Parsed `options`; malformed JSON yields an empty list. */
942
+ /** Options: the JS property wins, else the JSON `options` attribute. */
877
943
  get options(): OdyCheckboxOption[];
944
+ /**
945
+ * Accepts an array of `{ label, value }` (or a JSON string) and stores it as
946
+ * the source of truth. Mirrors `<ody-dropdown-*>` so `el.options = [...]`
947
+ * works, without reflecting a large JSON blob onto the DOM attribute.
948
+ */
949
+ set options(next: OdyCheckboxOption[] | string);
950
+ /**
951
+ * Reflect a `value` change by updating each checkbox (and the select-all
952
+ * parent) in place, so toggling an option keeps focus instead of rebuilding
953
+ * the group. Other observed attributes still re-render via the base.
954
+ */
955
+ attributeChangedCallback(name?: string, oldValue?: string | null, newValue?: string | null): void;
878
956
  protected render(): void;
879
957
  }
880
958
 
@@ -958,6 +1036,7 @@ declare class OdyCopyButton extends OdyElement {
958
1036
  #private;
959
1037
  static observedAttributes: string[];
960
1038
  protected render(): void;
1039
+ disconnectedCallback(): void;
961
1040
  }
962
1041
 
963
1042
  /**
@@ -1164,11 +1243,13 @@ interface OdyToastOptions {
1164
1243
  * {@link toast} lazily creates and portals one for you.
1165
1244
  */
1166
1245
  declare class OdyToastHost extends OdyElement {
1246
+ #private;
1167
1247
  protected render(): void;
1168
1248
  /** Render and stack a toast, returning a handle to dismiss it early. */
1169
1249
  push(message: string, options?: OdyToastOptions): {
1170
1250
  dismiss: () => void;
1171
1251
  };
1252
+ disconnectedCallback(): void;
1172
1253
  }
1173
1254
  /**
1174
1255
  * Show a toast notification, lazily creating an {@link OdyToastHost} portaled to
@@ -1232,6 +1313,7 @@ declare abstract class OdySelectBase extends OdyElement {
1232
1313
  protected abstract triggerContent(): string;
1233
1314
  /** ARIA `aria-multiselectable` value for the listbox (`'true'`/`'false'`). */
1234
1315
  protected abstract get multiselectable(): boolean;
1316
+ connectedCallback(): void;
1235
1317
  protected render(): void;
1236
1318
  /** Open the panel and start listening for outside clicks. */
1237
1319
  protected openPanel(): void;
@@ -1344,7 +1426,8 @@ type OdyDateFormatter = (date: Date) => string;
1344
1426
  * - `range` — boolean; enables start/end range selection.
1345
1427
  * - `first-day-of-week` — `0`–`6` (0 = Sunday, default `0`).
1346
1428
  * - `placeholder` — trigger text shown when no value is selected.
1347
- * - `presets` — JSON `[{ "label", "value" }]` shortcut buttons.
1429
+ * - `presets` — JSON `[{ "label", "value" }]` shortcut buttons. Also settable
1430
+ * as a JS **property** (`el.presets = [...]`) — accepts an array or JSON string.
1348
1431
  * - `display-format` — how the selected date is shown to the user:
1349
1432
  * `short` | `medium` (default) | `long` | `full`. The displayed text is
1350
1433
  * localized via `Intl.DateTimeFormat` for the element's `lang`; the `value`
@@ -1362,10 +1445,15 @@ type OdyDateFormatter = (date: Date) => string;
1362
1445
  declare class OdyDatePicker extends OdyElement {
1363
1446
  #private;
1364
1447
  static observedAttributes: string[];
1448
+ /** Preset list: the JS property wins, else the JSON `presets` attribute. */
1449
+ get presets(): OdyDatePreset[];
1450
+ set presets(next: OdyDatePreset[] | string);
1365
1451
  /** Disable arbitrary days; not an attribute (functions can't serialize). */
1366
- isDateDisallowed?: OdyDateDisallowed;
1452
+ get isDateDisallowed(): OdyDateDisallowed | undefined;
1453
+ set isDateDisallowed(fn: OdyDateDisallowed | undefined);
1367
1454
  /** Custom trigger-label formatter; wins over `display-format`/`Intl`. */
1368
- formatDate?: OdyDateFormatter;
1455
+ get formatDate(): OdyDateFormatter | undefined;
1456
+ set formatDate(fn: OdyDateFormatter | undefined);
1369
1457
  /** Whether the popover is currently shown. */
1370
1458
  get isOpen(): boolean;
1371
1459
  /** Show the popover and focus the roving day. */