ajo-ui 0.1.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 (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
@@ -0,0 +1,263 @@
1
+ import { defaultResultsLabel, flag, matchesTokens, resolveFilter, text } from "./utils.js";
2
+ import { t as collection } from "./chunks/collection-DtRB63U4.js";
3
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "./dialog.js";
4
+ import { announce, callHandler, controlled, dom, id, listen, roving, statefulRootAttrs } from "ajo-cloves";
5
+ import { context } from "ajo/context";
6
+ import { Fragment, jsx, jsxs } from "ajo/jsx-runtime";
7
+ //#region packages/ajo-ui/src/command.tsx
8
+ var CommandContext = context(null);
9
+ var defaultFilter = (value, search, keywords) => matchesTokens(search, [value, ...keywords].join(" "));
10
+ var commandItems = collection("command");
11
+ var CommandRoot = function* ({ defaultSearch, defaultValue, search, value }) {
12
+ const commandId = id("command");
13
+ let announceResults = false;
14
+ let disabled = false;
15
+ let lastResultCount = -1;
16
+ let loop = true;
17
+ let onSearchChange;
18
+ let onValueChange;
19
+ let resultsLabel;
20
+ const valueState = controlled(this, {
21
+ fallback: String(value ?? defaultValue ?? ""),
22
+ onChange: (next, event) => onValueChange?.(next, event)
23
+ });
24
+ const searchState = controlled(this, {
25
+ fallback: String(search ?? defaultSearch ?? ""),
26
+ onChange: (next, event) => onSearchChange?.(next, event)
27
+ });
28
+ const live = announce(this);
29
+ const itemId = (itemValue) => `${commandId}-item-${encodeURIComponent(itemValue)}`;
30
+ const setValue = (next, event) => {
31
+ if (next === valueState.value) return;
32
+ valueState.set(next, event);
33
+ };
34
+ const setSearch = (next, event) => {
35
+ if (next === searchState.value) return;
36
+ searchState.set(next, event);
37
+ announceResults = true;
38
+ };
39
+ const nav = roving(this, {
40
+ items: () => commandItems.items(this),
41
+ loop: () => loop,
42
+ current: () => commandItems.items(this).find((item) => item.dataset.value === valueState.value),
43
+ onMove: (target, event) => {
44
+ setValue(target.dataset.value ?? "", event);
45
+ target.scrollIntoView({ block: "nearest" });
46
+ }
47
+ });
48
+ listen(this, "keydown", (event) => {
49
+ if (!event.target?.closest("[data-slot=\"command\"]")) return;
50
+ if (nav.handle(event)) return;
51
+ if (event.key === "Enter") {
52
+ const item = commandItems.items(this).find((candidate) => candidate.dataset.value === valueState.value);
53
+ if (!item) return;
54
+ event.preventDefault();
55
+ item.click();
56
+ } else if (event.key === "Escape") {
57
+ if (!searchState.value) return;
58
+ event.preventDefault();
59
+ setSearch("", event);
60
+ }
61
+ });
62
+ for (const args of this) {
63
+ disabled = Boolean(args.disabled);
64
+ loop = args.loop !== false;
65
+ onSearchChange = args.onSearchChange;
66
+ onValueChange = args.onValueChange;
67
+ resultsLabel = args.resultsLabel;
68
+ searchState.sync(args.search != null ? String(args.search) : void 0);
69
+ valueState.sync(args.value != null ? String(args.value) : void 0);
70
+ const filter = resolveFilter(args.filter, defaultFilter);
71
+ const matches = (itemValue, keywords = []) => filter(itemValue, searchState.value, keywords);
72
+ CommandContext({
73
+ activeId: valueState.value ? itemId(valueState.value) : "",
74
+ disabled,
75
+ itemId,
76
+ listId: `${commandId}-list`,
77
+ matches,
78
+ search: searchState.value,
79
+ setValue,
80
+ setSearch,
81
+ value: valueState.value
82
+ });
83
+ if (dom(this)) queueMicrotask(() => {
84
+ const visible = commandItems.sweep(this);
85
+ if (!valueState.controlled) {
86
+ const nextValue = (visible.find((item) => item.dataset.value === valueState.value) ?? visible[0])?.dataset.value ?? "";
87
+ if (nextValue !== valueState.value) valueState.init(nextValue);
88
+ }
89
+ if (announceResults && visible.length !== lastResultCount) live.polite((resultsLabel ?? defaultResultsLabel)(visible.length));
90
+ lastResultCount = visible.length;
91
+ announceResults = false;
92
+ });
93
+ yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
94
+ }
95
+ };
96
+ /** Searchable command menu. */
97
+ var Command = ({ children, class: classes, defaultSearch, defaultValue, disabled, filter, loop, onSearchChange, onValueChange, resultsLabel, search, value, ...attrs }) => /* @__PURE__ */ jsx(CommandRoot, {
98
+ ...statefulRootAttrs(attrs),
99
+ defaultSearch,
100
+ defaultValue,
101
+ disabled,
102
+ filter,
103
+ loop,
104
+ onSearchChange,
105
+ onValueChange,
106
+ resultsLabel,
107
+ search,
108
+ value,
109
+ "attr:class": classes,
110
+ "attr:data-slot": "command",
111
+ children
112
+ });
113
+ /** Native dialog wrapper for a Command palette. */
114
+ var CommandDialog = ({ children, class: classes, defaultOpen, description, commandClass, onOpenChange, open, showCloseButton, closeClass, closeIconClass, closeLabel = "Close", descriptionClass, onEscapeKeyDown, title, titleClass, onPointerDownOutside, ...attrs }) => /* @__PURE__ */ jsx(Dialog, {
115
+ defaultOpen,
116
+ onOpenChange,
117
+ open,
118
+ class: "contents",
119
+ children: /* @__PURE__ */ jsxs(DialogContent, {
120
+ ...attrs,
121
+ class: classes,
122
+ "data-slot": "command-dialog",
123
+ onEscapeKeyDown,
124
+ onPointerDownOutside,
125
+ children: [
126
+ /* @__PURE__ */ jsxs("div", {
127
+ class: titleClass,
128
+ children: [/* @__PURE__ */ jsx(DialogTitle, { children: title ?? "Command Palette" }), /* @__PURE__ */ jsx(DialogDescription, {
129
+ class: descriptionClass,
130
+ children: description ?? "Search for a command to run..."
131
+ })]
132
+ }),
133
+ showCloseButton !== false ? /* @__PURE__ */ jsx(DialogClose, {
134
+ "aria-label": closeLabel,
135
+ class: closeClass,
136
+ children: /* @__PURE__ */ jsx("span", {
137
+ "aria-hidden": "true",
138
+ class: closeIconClass
139
+ })
140
+ }) : null,
141
+ /* @__PURE__ */ jsx(Command, {
142
+ class: commandClass,
143
+ children
144
+ })
145
+ ]
146
+ })
147
+ });
148
+ /** Search input for a Command menu. */
149
+ var CommandInput = ({ class: classes, disabled, iconClass, onValueChange, placeholder = "Type a command or search...", type: _type, value, wrapperClass, "set:oninput": onInput, ...attrs }) => {
150
+ const command = CommandContext();
151
+ const disabledFlag = Boolean(disabled ?? command?.disabled);
152
+ const shown = value == null ? command?.search ?? "" : String(value);
153
+ return /* @__PURE__ */ jsxs("div", {
154
+ class: wrapperClass,
155
+ "data-slot": "command-input-wrapper",
156
+ children: [/* @__PURE__ */ jsx("span", {
157
+ "aria-hidden": "true",
158
+ class: iconClass
159
+ }), /* @__PURE__ */ jsx("input", {
160
+ ...attrs,
161
+ "aria-activedescendant": command?.activeId || void 0,
162
+ "aria-autocomplete": "list",
163
+ "aria-controls": command?.listId,
164
+ "aria-expanded": "true",
165
+ class: classes,
166
+ "data-slot": "command-input",
167
+ disabled: disabledFlag,
168
+ placeholder,
169
+ role: "combobox",
170
+ "set:oninput": (event) => {
171
+ const next = event.target.value;
172
+ callHandler(onInput, event);
173
+ if (event.defaultPrevented) return;
174
+ onValueChange?.(next, event);
175
+ command?.setSearch(next, event);
176
+ },
177
+ "set:value": shown,
178
+ type: "search"
179
+ })]
180
+ });
181
+ };
182
+ /** Scrollable list for command options. */
183
+ var CommandList = ({ children, class: classes, ...attrs }) => {
184
+ const command = CommandContext();
185
+ return /* @__PURE__ */ jsx("div", {
186
+ ...attrs,
187
+ class: classes,
188
+ "data-slot": "command-list",
189
+ id: command?.listId,
190
+ role: "listbox",
191
+ children
192
+ });
193
+ };
194
+ /** Empty state shown when filtering hides every command item. */
195
+ var CommandEmpty = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
196
+ ...attrs,
197
+ class: classes,
198
+ "data-slot": "command-empty",
199
+ children
200
+ });
201
+ /** Group of related command items. */
202
+ var CommandGroup = ({ children, class: classes, forceMount, heading, headingClass, ...attrs }) => /* @__PURE__ */ jsxs("div", {
203
+ ...attrs,
204
+ class: classes,
205
+ "data-force-mount": flag(forceMount),
206
+ "data-slot": "command-group",
207
+ role: "group",
208
+ children: [heading ? /* @__PURE__ */ jsx("div", {
209
+ class: headingClass,
210
+ "data-slot": "command-group-heading",
211
+ children: heading
212
+ }) : null, children]
213
+ });
214
+ /** Visual separator between command groups. */
215
+ var CommandSeparator = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
216
+ ...attrs,
217
+ class: classes,
218
+ "data-slot": "command-separator",
219
+ role: "separator"
220
+ });
221
+ /** Selectable command option. */
222
+ var CommandItem = ({ children, class: classes, disabled, forceMount, id: idArg, keywords = [], onSelect, value, ...attrs }) => {
223
+ const command = CommandContext();
224
+ const itemValue = String(value ?? text(children));
225
+ const disabledFlag = Boolean(disabled ?? command?.disabled);
226
+ const hidden = !forceMount && command ? !command.matches(itemValue, keywords) : false;
227
+ const highlighted = command?.value === itemValue;
228
+ return /* @__PURE__ */ jsx("div", {
229
+ ...attrs,
230
+ ...commandItems.attrs({
231
+ disabled: disabledFlag,
232
+ value: itemValue
233
+ }),
234
+ "aria-disabled": flag(disabledFlag),
235
+ "aria-selected": highlighted ? "true" : "false",
236
+ class: classes,
237
+ "data-highlighted": flag(highlighted),
238
+ "data-slot": "command-item",
239
+ hidden: hidden || void 0,
240
+ id: idArg ?? command?.itemId(itemValue),
241
+ role: "option",
242
+ "set:onclick": (event) => {
243
+ if (disabledFlag) return;
244
+ onSelect?.(itemValue, event);
245
+ if (event.defaultPrevented) return;
246
+ command?.setValue(itemValue, event);
247
+ },
248
+ "set:onpointermove": (event) => {
249
+ if (disabledFlag) return;
250
+ command?.setValue(itemValue, event);
251
+ },
252
+ children
253
+ });
254
+ };
255
+ /** Right-aligned shortcut hint inside a CommandItem. */
256
+ var CommandShortcut = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("span", {
257
+ ...attrs,
258
+ class: classes,
259
+ "data-slot": "command-shortcut",
260
+ children
261
+ });
262
+ //#endregion
263
+ export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut };
@@ -0,0 +1,112 @@
1
+ import { withSlot } from "./utils.js";
2
+ import { t as pointReference } from "./chunks/position-D6_i_SRn.js";
3
+ import { _ as provideContextMenuComposition, a as MenuItem, c as MenuRadioItem, d as MenuSub, f as MenuSubContent, g as menuInvocation, i as MenuGroup, l as MenuSeparator, n as MenuCheckboxItem, o as MenuLabel, p as MenuSubTrigger, r as MenuContent, s as MenuRadioGroup, t as Menu, u as MenuShortcut } from "./chunks/menu-B45IyHHC.js";
4
+ import { callHandler, listen, restore, statefulRootAttrs } from "ajo-cloves";
5
+ import { jsx } from "ajo/jsx-runtime";
6
+ //#region packages/ajo-ui/src/context-menu.tsx
7
+ var ContextMenuRoot = function* () {
8
+ const focus = restore(this);
9
+ let invoker = null;
10
+ let point = {
11
+ x: 0,
12
+ y: 0
13
+ };
14
+ let reference = null;
15
+ const invoke = (controller, x, y, event, source, focusIntent) => {
16
+ point.x = x;
17
+ point.y = y;
18
+ if (source !== invoker || !reference) {
19
+ invoker = source;
20
+ point = {
21
+ x,
22
+ y
23
+ };
24
+ reference = pointReference(source, () => point);
25
+ }
26
+ focus.capture(source);
27
+ controller(reference, source, event, focusIntent);
28
+ };
29
+ listen(this, "contextmenu", (event) => {
30
+ if (event.target?.closest("[data-menu-sub-content=\"true\"],[data-menu-content=\"true\"]")) event.preventDefault();
31
+ });
32
+ for (const args of this) {
33
+ provideContextMenuComposition(invoke, focus.restore);
34
+ yield /* @__PURE__ */ jsx(Menu, {
35
+ disabled: args.disabled,
36
+ onOpenChange: args.onOpenChange,
37
+ children: args.children
38
+ });
39
+ }
40
+ };
41
+ /** Root provider for a context menu. */
42
+ var ContextMenu = ({ children, class: classes, disabled, onOpenChange, ...attrs }) => /* @__PURE__ */ jsx(ContextMenuRoot, {
43
+ ...statefulRootAttrs(attrs),
44
+ disabled,
45
+ onOpenChange,
46
+ "attr:class": classes,
47
+ "attr:data-slot": "context-menu",
48
+ children
49
+ });
50
+ /** The single region that invokes its ContextMenu by pointer or keyboard. */
51
+ var ContextMenuTrigger = ({ children, class: classes, disabled, id, ref, tabindex, "set:oncontextmenu": onContextMenu, "set:onkeydown": onKeydown, ...attrs }) => {
52
+ const menu = menuInvocation();
53
+ const disabledFlag = Boolean(disabled ?? menu?.disabled);
54
+ const adoptedId = menu?.adoptTriggerId(id);
55
+ return /* @__PURE__ */ jsx("div", {
56
+ ...attrs,
57
+ "aria-controls": menu?.contentId,
58
+ "aria-disabled": disabledFlag ? "true" : void 0,
59
+ "aria-expanded": menu?.open ? "true" : "false",
60
+ "aria-haspopup": "menu",
61
+ class: classes,
62
+ "data-disabled": disabledFlag ? "true" : void 0,
63
+ "data-slot": "context-menu-trigger",
64
+ "data-state": menu?.open ? "open" : "closed",
65
+ id: adoptedId ?? id,
66
+ ref,
67
+ "set:oncontextmenu": (event) => {
68
+ const element = event.currentTarget;
69
+ callHandler(onContextMenu, event);
70
+ if (event.defaultPrevented || disabledFlag) return;
71
+ event.preventDefault();
72
+ menu?.invoke(event.clientX, event.clientY, event, element, "content");
73
+ },
74
+ "set:onkeydown": (event) => {
75
+ const element = event.currentTarget;
76
+ callHandler(onKeydown, event);
77
+ if (event.defaultPrevented || disabledFlag) return;
78
+ if (event.key !== "ContextMenu" && !(event.key === "F10" && event.shiftKey)) return;
79
+ event.preventDefault();
80
+ const rect = element.getBoundingClientRect();
81
+ menu?.invoke(rect.left, rect.bottom, event, element, "first");
82
+ },
83
+ tabindex: disabledFlag ? void 0 : tabindex ?? 0,
84
+ children
85
+ });
86
+ };
87
+ /** Popover menu content opened by a ContextMenuTrigger. */
88
+ var ContextMenuContent = withSlot(MenuContent, "context-menu-content");
89
+ /** Standard context menu action item. */
90
+ var ContextMenuItem = withSlot(MenuItem, "context-menu-item");
91
+ /** Checkable context menu item. */
92
+ var ContextMenuCheckboxItem = withSlot(MenuCheckboxItem, "context-menu-checkbox-item");
93
+ /** Radio group inside a context menu. */
94
+ var ContextMenuRadioGroup = withSlot(MenuRadioGroup, "context-menu-radio-group");
95
+ /** Radio item inside a context menu radio group. */
96
+ var ContextMenuRadioItem = withSlot(MenuRadioItem, "context-menu-radio-item");
97
+ /** Group of context menu items. */
98
+ var ContextMenuGroup = withSlot(MenuGroup, "context-menu-group");
99
+ /** Non-interactive label inside a context menu. */
100
+ var ContextMenuLabel = withSlot(MenuLabel, "context-menu-label");
101
+ /** Visual separator between context menu groups. */
102
+ var ContextMenuSeparator = withSlot(MenuSeparator, "context-menu-separator");
103
+ /** Right-aligned shortcut hint inside a context menu item. */
104
+ var ContextMenuShortcut = withSlot(MenuShortcut, "context-menu-shortcut");
105
+ /** Root provider for a context submenu. */
106
+ var ContextMenuSub = withSlot(MenuSub, "context-menu-sub");
107
+ /** Trigger item that opens a context submenu. */
108
+ var ContextMenuSubTrigger = withSlot(MenuSubTrigger, "context-menu-sub-trigger");
109
+ /** Content for a context submenu. */
110
+ var ContextMenuSubContent = withSlot(MenuSubContent, "context-menu-sub-content");
111
+ //#endregion
112
+ export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger };
@@ -0,0 +1,5 @@
1
+ import "./checkbox.js";
2
+ import { t as DataTable } from "./chunks/data-table-DpkWv4y4.js";
3
+ import "./select.js";
4
+ import "./toolbar.js";
5
+ export { DataTable, DataTable as default };
package/dist/dialog.js ADDED
@@ -0,0 +1,207 @@
1
+ import { triggerAttrs } from "./utils.js";
2
+ import { callHandler, callRef, controlled, id, statefulRootAttrs } from "ajo-cloves";
3
+ import { context } from "ajo/context";
4
+ import { Fragment, jsx, jsxs } from "ajo/jsx-runtime";
5
+ //#region packages/ajo-ui/src/dialog.tsx
6
+ var DialogContext = context(null);
7
+ var dialog = () => {
8
+ const value = DialogContext();
9
+ if (!value) throw new Error("Dialog component must be used within Dialog.");
10
+ return value;
11
+ };
12
+ var outside = (element, event) => {
13
+ const rect = element.getBoundingClientRect();
14
+ return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
15
+ };
16
+ var DialogRoot = function* ({ defaultOpen, open }) {
17
+ const dialogId = id("dialog");
18
+ const watched = /* @__PURE__ */ new WeakSet();
19
+ let content = null;
20
+ let modal = true;
21
+ let onOpenChange;
22
+ let syncing = false;
23
+ let trigger = null;
24
+ const state = controlled(this, {
25
+ fallback: Boolean(open ?? defaultOpen),
26
+ onChange: (next, event) => onOpenChange?.(next, event)
27
+ });
28
+ let current = state.value;
29
+ const focusTrigger = () => queueMicrotask(() => trigger?.focus());
30
+ const sync = () => queueMicrotask(() => {
31
+ if (!content) return;
32
+ syncing = true;
33
+ try {
34
+ if (current && !content.open) if (modal) content.showModal();
35
+ else content.show();
36
+ else if (!current && content.open) content.close();
37
+ } finally {
38
+ queueMicrotask(() => syncing = false);
39
+ }
40
+ });
41
+ const setOpen = (next, event) => {
42
+ if (next === current) return;
43
+ state.set(next, event);
44
+ current = state.value;
45
+ sync();
46
+ if (!next) focusTrigger();
47
+ };
48
+ const setContent = (element) => {
49
+ content = element;
50
+ if (!element || watched.has(element)) return;
51
+ watched.add(element);
52
+ element.addEventListener("close", (event) => {
53
+ if (syncing || current === false) return;
54
+ state.accept(false, event);
55
+ current = state.value;
56
+ focusTrigger();
57
+ }, { signal: this.signal });
58
+ };
59
+ for (const args of this) {
60
+ modal = args.modal !== false;
61
+ onOpenChange = args.onOpenChange;
62
+ current = state.sync(args.open == null ? void 0 : Boolean(args.open));
63
+ DialogContext({
64
+ close: (event) => setOpen(false, event),
65
+ contentId: `${dialogId}-content`,
66
+ descriptionId: `${dialogId}-description`,
67
+ modal,
68
+ open: current,
69
+ setContent,
70
+ setOpen,
71
+ setTrigger: (element) => trigger = element,
72
+ titleId: `${dialogId}-title`,
73
+ triggerId: `${dialogId}-trigger`
74
+ });
75
+ sync();
76
+ yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
77
+ }
78
+ };
79
+ /** Unstyled root provider for a native dialog. */
80
+ var Dialog = ({ children, class: classes, "data-slot": slot = "dialog", defaultOpen, modal, onOpenChange, open, ...attrs }) => /* @__PURE__ */ jsx(DialogRoot, {
81
+ ...statefulRootAttrs(attrs),
82
+ defaultOpen,
83
+ modal,
84
+ onOpenChange,
85
+ open,
86
+ "attr:class": classes,
87
+ "attr:data-slot": slot,
88
+ children
89
+ });
90
+ /** Unstyled button that opens the nearest Dialog. */
91
+ var DialogTrigger = ({ children, "data-slot": slot = "dialog-trigger", disabled, id, ref, type = "button", "set:onclick": onClick, ...attrs }) => {
92
+ const ctx = dialog();
93
+ return /* @__PURE__ */ jsx("button", {
94
+ ...attrs,
95
+ ...triggerAttrs({
96
+ controls: ctx.contentId,
97
+ expanded: ctx.open,
98
+ id,
99
+ open: ctx.open,
100
+ ref,
101
+ setTrigger: ctx.setTrigger,
102
+ triggerId: ctx.triggerId
103
+ }),
104
+ "data-slot": slot,
105
+ disabled,
106
+ type,
107
+ "set:onclick": (event) => {
108
+ callHandler(onClick, event);
109
+ if (event.defaultPrevented || disabled) return;
110
+ ctx.setOpen(true, event);
111
+ },
112
+ children
113
+ });
114
+ };
115
+ /** Unstyled button that closes the nearest Dialog. */
116
+ var DialogClose = ({ children, "data-slot": slot = "dialog-close", disabled, type = "button", "set:onclick": onClick, ...attrs }) => {
117
+ const ctx = dialog();
118
+ return /* @__PURE__ */ jsx("button", {
119
+ ...attrs,
120
+ "data-slot": slot,
121
+ disabled,
122
+ type,
123
+ "set:onclick": (event) => {
124
+ callHandler(onClick, event);
125
+ if (event.defaultPrevented || disabled) return;
126
+ ctx.close(event);
127
+ },
128
+ children
129
+ });
130
+ };
131
+ /** Unstyled native dialog panel. */
132
+ var DialogContent = ({ children, class: classes, "data-slot": slot = "dialog-content", onEscapeKeyDown, onPointerDownOutside, ref, "aria-describedby": describedBy, "aria-labelledby": labelledBy, "set:oncancel": onCancel, "set:onclick": onClick, "set:onkeydown": onKeydown, ...attrs }) => {
133
+ const ctx = dialog();
134
+ const reference = (element) => {
135
+ ctx.setContent(element);
136
+ callRef(ref, element);
137
+ };
138
+ return /* @__PURE__ */ jsxs("dialog", {
139
+ ...attrs,
140
+ "aria-describedby": describedBy ?? ctx.descriptionId,
141
+ "aria-labelledby": labelledBy ?? ctx.titleId,
142
+ "aria-modal": ctx.modal ? "true" : void 0,
143
+ class: classes,
144
+ "data-slot": slot,
145
+ "data-state": ctx.open ? "open" : "closed",
146
+ id: ctx.contentId,
147
+ ref: reference,
148
+ "set:oncancel": (event) => {
149
+ callHandler(onCancel, event);
150
+ if (event.defaultPrevented) return;
151
+ if (event.cancelable) event.preventDefault();
152
+ ctx.close(event);
153
+ },
154
+ "set:onclick": (event) => {
155
+ callHandler(onClick, event);
156
+ if (event.defaultPrevented) return;
157
+ const target = event.currentTarget;
158
+ if (event.target === target && outside(target, event)) {
159
+ onPointerDownOutside?.(event);
160
+ if (!event.defaultPrevented) ctx.close(event);
161
+ }
162
+ },
163
+ "set:onkeydown": (event) => {
164
+ if (event.key === "Escape") onEscapeKeyDown?.(event);
165
+ callHandler(onKeydown, event);
166
+ },
167
+ children: [children, /* @__PURE__ */ jsx("div", {
168
+ "data-slot": "dialog-portal",
169
+ skip: true,
170
+ style: "display:contents"
171
+ }, "dialog-portal")]
172
+ });
173
+ };
174
+ /** Unstyled header area for dialog title and description. */
175
+ var DialogHeader = ({ children, "data-slot": slot = "dialog-header", ...attrs }) => /* @__PURE__ */ jsx("div", {
176
+ ...attrs,
177
+ "data-slot": slot,
178
+ children
179
+ });
180
+ /** Unstyled footer area for dialog actions. */
181
+ var DialogFooter = ({ children, "data-slot": slot = "dialog-footer", ...attrs }) => /* @__PURE__ */ jsx("div", {
182
+ ...attrs,
183
+ "data-slot": slot,
184
+ children
185
+ });
186
+ /** Accessible title for DialogContent. */
187
+ var DialogTitle = ({ children, "data-slot": slot = "dialog-title", id, ...attrs }) => {
188
+ const ctx = DialogContext();
189
+ return /* @__PURE__ */ jsx("h2", {
190
+ ...attrs,
191
+ "data-slot": slot,
192
+ id: id ?? ctx?.titleId,
193
+ children
194
+ });
195
+ };
196
+ /** Accessible description for DialogContent. */
197
+ var DialogDescription = ({ children, "data-slot": slot = "dialog-description", id, ...attrs }) => {
198
+ const ctx = DialogContext();
199
+ return /* @__PURE__ */ jsx("p", {
200
+ ...attrs,
201
+ "data-slot": slot,
202
+ id: id ?? ctx?.descriptionId,
203
+ children
204
+ });
205
+ };
206
+ //#endregion
207
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger };
@@ -0,0 +1,22 @@
1
+ import { statefulRootAttrs } from "ajo-cloves";
2
+ import { context } from "ajo/context";
3
+ import { Fragment, jsx } from "ajo/jsx-runtime";
4
+ //#region packages/ajo-ui/src/direction.tsx
5
+ /** Direction inherited by direction-aware components. */
6
+ var DirectionContext = context("ltr");
7
+ var DirectionRoot = function* () {
8
+ for (const { children, dir } of this) {
9
+ DirectionContext(dir);
10
+ yield /* @__PURE__ */ jsx(Fragment, { children });
11
+ }
12
+ };
13
+ /** Unstyled provider that sets direction context and an inherited HTML `dir` attribute. */
14
+ var DirectionProvider = ({ children, dir = "ltr", ...attrs }) => /* @__PURE__ */ jsx(DirectionRoot, {
15
+ ...statefulRootAttrs(attrs),
16
+ dir,
17
+ "attr:data-slot": "direction-provider",
18
+ "attr:dir": dir,
19
+ children
20
+ });
21
+ //#endregion
22
+ export { DirectionContext, DirectionProvider };