@zeyos/zx 4.2.0 → 4.3.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +5 -0
  3. package/dist/types/components/calendar/calendar.d.ts +19 -2
  4. package/dist/types/components/dialog/dialog.d.ts +6 -1
  5. package/dist/types/components/dropdown/dropdown.d.ts +5 -0
  6. package/dist/types/components/launcher/launcher.d.ts +5 -0
  7. package/dist/types/components/message/message.d.ts +9 -1
  8. package/dist/types/components/modal/modal.d.ts +44 -7
  9. package/dist/types/components/sheet/sheet.d.ts +22 -12
  10. package/dist/types/core/overlay-host.d.ts +12 -0
  11. package/dist/zx-zeyos.esm.js +138 -32
  12. package/dist/zx-zeyos.esm.min.js +1 -1
  13. package/dist/zx.css +259 -132
  14. package/dist/zx.esm.js +277 -82
  15. package/dist/zx.esm.min.js +3 -3
  16. package/dist/zx.global.js +277 -82
  17. package/dist/zx.global.min.js +3 -3
  18. package/dist/zx.min.css +1 -1
  19. package/docs/api.json +36 -0
  20. package/docs/llms.md +37 -18
  21. package/docs/llms.txt +1 -1
  22. package/package.json +1 -1
  23. package/src/components/account-menu/account-menu.css +1 -1
  24. package/src/components/app-rail/app-rail.css +1 -1
  25. package/src/components/calendar/calendar.css +66 -24
  26. package/src/components/calendar/calendar.js +114 -19
  27. package/src/components/context-menu/context-menu.css +2 -2
  28. package/src/components/context-menu/context-menu.js +5 -1
  29. package/src/components/date-picker/date-picker.css +18 -11
  30. package/src/components/datebox/date-range-box.css +4 -2
  31. package/src/components/datebox/datebox.css +4 -2
  32. package/src/components/dialog/dialog.css +4 -3
  33. package/src/components/dialog/dialog.js +2 -1
  34. package/src/components/dropdown/dropdown.css +4 -2
  35. package/src/components/dropdown/dropdown.js +7 -2
  36. package/src/components/launcher/launcher.css +10 -8
  37. package/src/components/launcher/launcher.js +9 -2
  38. package/src/components/menu-button/menu-button.css +2 -2
  39. package/src/components/message/message.css +7 -16
  40. package/src/components/message/message.js +17 -11
  41. package/src/components/modal/modal.css +6 -4
  42. package/src/components/modal/modal.js +99 -25
  43. package/src/components/select/select.css +9 -7
  44. package/src/components/sheet/sheet.css +25 -7
  45. package/src/components/sheet/sheet.js +43 -23
  46. package/src/components/table/table.css +5 -3
  47. package/src/components/tag-picker/tag-picker.css +5 -3
  48. package/src/components/tooltip/tooltip.css +4 -2
  49. package/src/components/tooltip/tooltip.js +4 -1
  50. package/src/core/overlay-host.js +23 -0
  51. package/styles/tokens/dark.css +4 -0
  52. package/styles/tokens/semantic.css +83 -8
package/CHANGELOG.md CHANGED
@@ -8,6 +8,43 @@ and friends remain the complete record.
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 4.3.0 — 2026-08-26
12
+
13
+ ### Added
14
+
15
+ - **Scoped top-layer ownership.** Dropdown, Modal, Dialog, Sheet, Launcher, and floating Message
16
+ regions accept an optional `scope` context and otherwise resolve the opener's nearest Zx theme
17
+ scope. Native popovers and dialogs still use the browser top layer, while material, density,
18
+ product-token, and nested-theme inheritance remain intact.
19
+
20
+ ### Changed
21
+
22
+ - **Liquid glass is now a real overlay material.** Compact overlays and reading panels use
23
+ separate translucent bases, restrained specular and Aurora-response layers, blur/saturation,
24
+ shadows, borders, dividers, and interaction-state tokens. Menus, Select, TagPicker, date
25
+ popovers, tooltips, table column controls, and Toast use compact glass; Launcher, Modal, Dialog,
26
+ and floating Sheets use the calmer reading material. Docked Sheets remain opaque.
27
+ - **Theme Studio makes optical differences visible.** Its overlay specimen now sits on a
28
+ multicolour Aurora field, while Flat, Glass, and Deep glass expose materially different alpha,
29
+ refraction, and elevation in both light and dark themes.
30
+ - **Accessible material fallbacks are deterministic.** Reduced-transparency, increased-contrast,
31
+ forced-colour, and unsupported-filter environments remove reflection and backdrop effects and
32
+ restore solid surfaces; blurred Sheet backdrops follow the same rule.
33
+
34
+ ### Fixed
35
+
36
+ - Toast intent colours no longer turn the surface effectively opaque, modal backdrops no longer
37
+ erase the content meant to refract through the panel, scoped modals lock page scrolling again,
38
+ and DatePicker controls no longer tile the owning surface's reflection gradient.
39
+ - Floating non-modal Sheets now remain viewport-anchored inside transformed or clipped theme
40
+ scopes, and generation-aware lifecycle handling prevents queued close events from invalidating
41
+ a reopened Sheet during rapid dock/release handoffs.
42
+ - Documentation search now consumes the same compact overlay material, interaction states, and
43
+ accessibility fallbacks as library popovers; fallback tokens cover every supported theme scope.
44
+ - Calendar drag operations preserve the grabbed time/day offset, resize previews track their
45
+ proposed duration, pointer capture is resilient, and agenda/month layouts keep their event and
46
+ day controls aligned across desktop and mobile views.
47
+
11
48
  ## 4.2.0 — 2026-08-26
12
49
 
13
50
  ### Added
package/README.md CHANGED
@@ -238,6 +238,11 @@ the document. Select a built-in theme and density on any ancestor (normally `<ht
238
238
  <html data-zx-theme="dark" data-zx-density="compact">
239
239
  ```
240
240
 
241
+ Top-layer menus, dialogs, tooltips, launchers, and toast regions stay under the nearest Zx theme
242
+ scope in the DOM, so a theme or product-token override on a nested application shell also applies
243
+ to its overlays. Pass the optional `scope` context to Modal, Dialog, Sheet, Launcher, or static
244
+ Message calls when the opener is not inside the intended scope.
245
+
241
246
  Theme values are `light`, `dark`, and `auto`; density values are `cozy` and `compact`. `auto`
242
247
  follows the operating-system color preference.
243
248
 
@@ -283,6 +283,8 @@ export declare class Calendar extends Component<CalendarOptions> {
283
283
  event: import("./calendar-model.js").CalendarEvent;
284
284
  action: string;
285
285
  control: any;
286
+ timeOffsetMinutes: number;
287
+ dayOffset: number;
286
288
  moved: boolean;
287
289
  start?: undefined;
288
290
  end?: undefined;
@@ -290,6 +292,8 @@ export declare class Calendar extends Component<CalendarOptions> {
290
292
  oldEvent?: undefined;
291
293
  event?: undefined;
292
294
  action?: undefined;
295
+ timeOffsetMinutes?: undefined;
296
+ dayOffset?: undefined;
293
297
  type: string;
294
298
  pointerId: number;
295
299
  startX: number;
@@ -424,10 +428,23 @@ export declare class Calendar extends Component<CalendarOptions> {
424
428
  _cancelPointer(event: PointerEvent): void;
425
429
  /** @param {CalendarEvent} event @param {'move'|'resize'} action @param {number} x @param {number} y @returns {CalendarEvent|null} */
426
430
  _proposalAtPoint(event: CalendarEvent, action: 'move' | 'resize', x: number, y: number): CalendarEvent | null;
427
- /** @param {number} x @param {number} y @returns {Date|null} */
428
- _timeAtPoint(x: number, y: number): Date | null;
431
+ /** @param {number} x @param {number} y @param {Date|null} [fixedDay=null] @returns {Date|null} */
432
+ _timeAtPoint(x: number, y: number, fixedDay?: Date | null): Date | null;
429
433
  /** @param {number} x @param {number} y @returns {Date|null} */
430
434
  _dayAtPoint(x: number, y: number): Date | null;
435
+ /** @param {CalendarEvent} event @param {number} x @param {number} y @returns {{minutes: number, days: number}} */
436
+ _dragOffsetForEvent(event: CalendarEvent, x: number, y: number): {
437
+ minutes: number;
438
+ days: number;
439
+ };
440
+ /** @param {Date} day @returns {number} */
441
+ _timeGridDayIndex(day: Date): number;
442
+ /** @param {CalendarEvent} event @returns {string|null} */
443
+ _timedPreviewHeight(event: CalendarEvent): string | null;
444
+ /** @param {number} pointerId @returns {void} */
445
+ _capturePointer(pointerId: number): void;
446
+ /** @param {number} pointerId @returns {void} */
447
+ _releasePointerCapture(pointerId: number): void;
431
448
  /** @param {Date} start @param {Date} end @returns {void} */
432
449
  _previewSelection(start: Date, end: Date): void;
433
450
  /** @returns {void} */
@@ -41,6 +41,10 @@ export type DialogOptions = {
41
41
  content?: Node | string | number | {
42
42
  toElement: () => Node | null;
43
43
  } | null;
44
+ /**
45
+ * Element whose nearest Zx theme scope owns the overlay; defaults to the opener.
46
+ */
47
+ scope?: Element | string | null;
44
48
  /**
45
49
  * Open event listener.
46
50
  */
@@ -159,6 +163,7 @@ export type PromptOptions = {
159
163
  * @property {DialogButton[]} [buttons=[]] Footer buttons.
160
164
  * @property {boolean} [closable=true] Whether Escape and the header button may close the dialog.
161
165
  * @property {Node|string|number|{toElement: () => Node|null}|null} [content=null] Body content.
166
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns the overlay; defaults to the opener.
162
167
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
163
168
  * @property {(event: CustomEvent<{result: unknown}>) => void} [onclose] Close event listener.
164
169
  * @property {(event: CustomEvent<Record<string, never>>) => void} [oncancel] Cancel event listener.
@@ -209,7 +214,7 @@ export declare class Dialog extends Modal {
209
214
  /** @type {Readonly<DialogOptions>} */
210
215
  static defaults: Readonly<DialogOptions>;
211
216
  /**
212
- * Creates a structured dialog appended to `document.body`.
217
+ * Creates a structured dialog in the configured theme scope, or at document level when unscoped.
213
218
  * @param {Element|string|null} [_target=null] Ignored; Dialog always owns its root.
214
219
  * @param {DialogOptions} [options={}] Dialog options.
215
220
  */
@@ -28,6 +28,10 @@ export type DropdownOptions = {
28
28
  * Whether a click within the panel closes it.
29
29
  */
30
30
  closeOnSelect?: boolean;
31
+ /**
32
+ * Element whose nearest Zx theme scope owns the panel; defaults to the anchor.
33
+ */
34
+ scope?: Element | string | null;
31
35
  /**
32
36
  * Open event listener.
33
37
  */
@@ -45,6 +49,7 @@ export type DropdownOptions = {
45
49
  * @property {boolean} [matchWidth=false] Whether the panel matches the anchor width.
46
50
  * @property {'click'|'manual'} [openOn='click'] Trigger behavior.
47
51
  * @property {boolean} [closeOnSelect=false] Whether a click within the panel closes it.
52
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns the panel; defaults to the anchor.
48
53
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
49
54
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onclose] Close event listener.
50
55
  */
@@ -162,6 +162,10 @@ export type LauncherOptions = {
162
162
  open?: string;
163
163
  close?: string;
164
164
  };
165
+ /**
166
+ * Element whose nearest Zx theme scope owns an internally created launcher; defaults to the opener.
167
+ */
168
+ scope?: Element | string | null;
165
169
  /**
166
170
  * Preventable selection listener.
167
171
  */
@@ -244,6 +248,7 @@ export type RankedLauncherItem = LauncherItem & {
244
248
  * @property {string} [loadingText='Searching…'] Loading-state text.
245
249
  * @property {'mod+k'|false} [shortcut='mod+k'] Optional global keyboard shortcut.
246
250
  * @property {false|{move?: string, open?: string, close?: string}} [hints] Visible keyboard hints, or false.
251
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns an internally created launcher; defaults to the opener.
247
252
  * @property {(event: CustomEvent<LauncherSelectDetail>) => void} [onselect] Preventable selection listener.
248
253
  * @property {(event: CustomEvent<{query: string}>) => void} [onquery] Query-change listener.
249
254
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open listener.
@@ -13,6 +13,10 @@ export type MessageShowOptions = {
13
13
  * Whether to render a close button.
14
14
  */
15
15
  closable?: boolean;
16
+ /**
17
+ * Element whose nearest Zx theme scope owns a floating toast.
18
+ */
19
+ scope?: Element | string | null;
16
20
  };
17
21
  export type MessageOptions = {
18
22
  /**
@@ -55,6 +59,7 @@ export type ProgressHandle = {
55
59
  * @property {MessageKind|'danger'} [kind='info'] Message intent (`danger` aliases `error`).
56
60
  * @property {number} [timeout=4000] Auto-dismiss delay in milliseconds; zero persists.
57
61
  * @property {boolean} [closable=true] Whether to render a close button.
62
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns a floating toast.
58
63
  */
59
64
  /**
60
65
  * @typedef {Object} MessageOptions
@@ -176,9 +181,12 @@ export declare class Message extends Component<MessageOptions> {
176
181
  /**
177
182
  * Shows a floating progress status.
178
183
  * @param {string} text Initial status text.
184
+ * @param {{scope?: Element|string|null}} [options={}] Floating-region scope.
179
185
  * @returns {ProgressHandle}
180
186
  */
181
- static progress(text: string): ProgressHandle;
187
+ static progress(text: string, options?: {
188
+ scope?: Element | string | null;
189
+ }): ProgressHandle;
182
190
  /** @returns {void} */
183
191
  destroy(): void;
184
192
  /** @param {string} text @returns {ProgressHandle} */
@@ -22,6 +22,10 @@ export type ModalOptions = {
22
22
  * Whether to destroy the modal after it closes.
23
23
  */
24
24
  destroyOnClose?: boolean;
25
+ /**
26
+ * Element whose nearest Zx theme scope owns the overlay; defaults to the opener.
27
+ */
28
+ scope?: Element | string | null;
25
29
  /**
26
30
  * Open event listener.
27
31
  */
@@ -44,6 +48,7 @@ export type ModalOptions = {
44
48
  * @property {boolean} [closable=true] Whether Escape may close the modal.
45
49
  * @property {boolean} [lightDismiss=false] Whether a backdrop click closes the modal.
46
50
  * @property {boolean} [destroyOnClose=false] Whether to destroy the modal after it closes.
51
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns the overlay; defaults to the opener.
47
52
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
48
53
  * @property {(event: CustomEvent<{result: unknown}>) => void} [onclose] Close event listener.
49
54
  * @property {(event: CustomEvent<Record<string, never>>) => void} [oncancel] Cancel event listener.
@@ -62,7 +67,7 @@ export declare class Modal extends Component<ModalOptions> {
62
67
  /** @type {Readonly<ModalOptions>} */
63
68
  static defaults: Readonly<ModalOptions>;
64
69
  /**
65
- * Creates a modal appended to `document.body`.
70
+ * Creates a modal in the configured theme scope, or at document level when none is present.
66
71
  * @param {Element|string|null} [_target=null] Ignored; Modal always owns its root dialog.
67
72
  * @param {ModalOptions} [options={}] Modal options.
68
73
  */
@@ -81,17 +86,49 @@ export declare class Modal extends Component<ModalOptions> {
81
86
  * @returns {void}
82
87
  */
83
88
  _show(): void;
89
+ /**
90
+ * Removes the dialog from its current presentation layer.
91
+ * Subclasses may replace native dialog presentation while preserving Modal lifecycle events.
92
+ * @param {string} [result=''] Native string return value.
93
+ * @param {number} [presentation=this.#presentation] Presentation generation being hidden.
94
+ * @returns {void}
95
+ */
96
+ _hide(result?: string, presentation?: number): void;
97
+ /**
98
+ * Shows a new presentation and gives it a generation of its own. Close events are queued by the
99
+ * platform, so the generation lets an event from an earlier presentation expire harmlessly if
100
+ * the same overlay has already reopened.
101
+ * @returns {void}
102
+ */
103
+ _present(): void;
104
+ /**
105
+ * Hides the current presentation and records which generation its queued close event belongs to.
106
+ * @param {string} [result=''] Native string return value.
107
+ * @returns {void}
108
+ */
109
+ _dismiss(result?: string): void;
110
+ /**
111
+ * Reports whether the current presentation layer is visible.
112
+ * @returns {boolean}
113
+ */
114
+ _isShown(): boolean;
84
115
  /**
85
116
  * Whether an incoming `close` event is a real dismissal.
86
117
  *
87
- * A dialog cannot move between the top layer and the page flow while it is open, so a subclass
88
- * that re-hosts itself has to close and immediately reopen. `close` is dispatched in a queued
89
- * task, not synchronously, so that stale event arrives once the dialog is already open again —
90
- * late enough to clobber `data-state`, fire a `close` nobody asked for, and trip
91
- * `destroyOnClose`. Returning false from an override drops it at the source instead.
118
+ * `close` is dispatched in a queued task, not synchronously. By the time it arrives the overlay
119
+ * may have reopened or crossed presentation layers, so only the event for the current hidden
120
+ * generation may settle state, emit lifecycle events, or trigger `destroyOnClose`.
121
+ * @param {number|undefined} closingPresentation Presentation that produced the event, if known.
92
122
  * @returns {boolean}
93
123
  */
94
- _isRealClose(): boolean;
124
+ _isRealClose(closingPresentation: number | undefined): boolean;
125
+ /**
126
+ * Settles one presentation's close without allowing a queued event to invalidate a newer one.
127
+ * Popover-based subclasses call this directly; native dialog events arrive through the listener.
128
+ * @param {number|undefined} closingPresentation Presentation that was hidden, if known.
129
+ * @returns {boolean} Whether this event closed the current presentation.
130
+ */
131
+ _settleClose(closingPresentation: number | undefined): boolean;
95
132
  /**
96
133
  * Creates the owned native dialog.
97
134
  * @returns {HTMLDialogElement}
@@ -80,6 +80,10 @@ export type SheetOptions = {
80
80
  * Whether to destroy the sheet after it closes.
81
81
  */
82
82
  destroyOnClose?: boolean;
83
+ /**
84
+ * Element whose nearest Zx theme scope owns a floating sheet; defaults to the opener.
85
+ */
86
+ scope?: Element | string | null;
83
87
  /**
84
88
  * Open event listener.
85
89
  */
@@ -138,6 +142,7 @@ export type SheetOptions = {
138
142
  * @property {boolean} [lightDismiss=true] Whether a click outside closes the sheet — the backdrop
139
143
  * when modal, a document-level pointer press when not.
140
144
  * @property {boolean} [destroyOnClose=false] Whether to destroy the sheet after it closes.
145
+ * @property {Element|string|null} [scope=null] Element whose nearest Zx theme scope owns a floating sheet; defaults to the opener.
141
146
  * @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open event listener.
142
147
  * @property {(event: CustomEvent<{result: unknown}>) => void} [onclose] Close event listener.
143
148
  * @property {(event: CustomEvent<Record<string, never>>) => void} [oncancel] Cancel event listener.
@@ -153,9 +158,11 @@ export type SheetOptions = {
153
158
  *
154
159
  * Modality is a three-way choice rather than a boolean because the three real behaviours do not
155
160
  * collapse into two: a modal sheet delegates focus containment, page inertness, and Escape to the
156
- * browser via `showModal()`; the other two open with `show()` and re-implement only what they still
157
- * want. That is also why `backdrop` applies only to `modal: true` — `::backdrop` does not render
158
- * for a non-modal dialog, and painting a fake one would make a sheet look blocking while it is not.
161
+ * browser via `showModal()`; floating non-modal sheets use the manual Popover top layer so a
162
+ * transformed or clipped application scope cannot displace them, and re-implement only what they
163
+ * still want. Docked sheets remain native non-modal dialogs in flow. That is also why `backdrop`
164
+ * applies only to `modal: true`: painting one for either non-modal mode would make the sheet look
165
+ * blocking while it is not.
159
166
  * @fires Sheet#open
160
167
  * @fires Sheet#close
161
168
  * @fires Sheet#cancel
@@ -165,7 +172,6 @@ export type SheetOptions = {
165
172
  export declare class Sheet extends Dialog {
166
173
  #private;
167
174
  _dock: any;
168
- _handoff: boolean;
169
175
  static cssName: string;
170
176
  /** @type {Readonly<SheetOptions>} */
171
177
  static defaults: Readonly<SheetOptions>;
@@ -217,17 +223,12 @@ export declare class Sheet extends Dialog {
217
223
  /**
218
224
  * Moves the dialog to its current mount and, when it was open, reopens it there.
219
225
  *
220
- * The close is unavoidable — a dialog cannot cross between the top layer and the flow while
221
- * open — but it is invisible: `_isRealClose()` drops the stale event, and reopening bypasses
222
- * `open()` so no lifecycle event is emitted for what is only a change of address.
226
+ * The close is unavoidable — a dialog cannot cross between presentation layers while open —
227
+ * but it is invisible: Modal's presentation generation drops the stale event, and reopening
228
+ * bypasses `open()` so no lifecycle event is emitted for what is only a change of address.
223
229
  * @returns {void}
224
230
  */
225
231
  _rehost(): void;
226
- /**
227
- * Drops the stale `close` a re-hosting handoff leaves behind.
228
- * @returns {boolean}
229
- */
230
- _isRealClose(): boolean;
231
232
  /**
232
233
  * Opens the sheet, trapping focus first where the modality asks for it.
233
234
  * @returns {this}
@@ -276,6 +277,15 @@ export declare class Sheet extends Dialog {
276
277
  * @returns {void}
277
278
  */
278
279
  _show(): void;
280
+ /**
281
+ * Hides a native dialog or a non-modal Sheet promoted through the popover top layer.
282
+ * @param {string} [result=''] Native string return value.
283
+ * @param {number} [presentation] Presentation generation being hidden.
284
+ * @returns {void}
285
+ */
286
+ _hide(result?: string, presentation?: number): void;
287
+ /** @returns {boolean} */
288
+ _isShown(): boolean;
279
289
  /**
280
290
  * Releases the focus trap and the owned dialog.
281
291
  * @returns {void}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Finds the closest styling scope for a top-layer element.
3
+ *
4
+ * Popovers and dialogs escape clipping even when their DOM parent is inside an application shell,
5
+ * so keeping them below that parent preserves inherited theme, density, material, and product
6
+ * token overrides without sacrificing top-layer behaviour. The document body remains the safe
7
+ * fallback for unscoped applications.
8
+ *
9
+ * @param {Element|string|null} [source=null] Element or selector whose styling scope should own the overlay.
10
+ * @returns {Element}
11
+ */
12
+ export declare function overlayHost(source?: Element | string | null): Element;