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.
- package/LICENSE +15 -0
- package/README.md +126 -0
- package/dist/accordion.js +130 -0
- package/dist/avatar.js +54 -0
- package/dist/calendar.js +2 -0
- package/dist/carousel.js +239 -0
- package/dist/chart.js +790 -0
- package/dist/checkbox-group.js +70 -0
- package/dist/checkbox.js +77 -0
- package/dist/chunks/bar-CVafh6C1.js +99 -0
- package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
- package/dist/chunks/collection-DtRB63U4.js +111 -0
- package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
- package/dist/chunks/menu-B45IyHHC.js +704 -0
- package/dist/chunks/native-BJdhd9XJ.js +20 -0
- package/dist/chunks/popup-C8Bb3l_g.js +459 -0
- package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
- package/dist/chunks/position-D6_i_SRn.js +434 -0
- package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
- package/dist/collapsible.js +106 -0
- package/dist/command.js +263 -0
- package/dist/context-menu.js +112 -0
- package/dist/data-table.js +5 -0
- package/dist/dialog.js +207 -0
- package/dist/direction.js +22 -0
- package/dist/drawer.js +139 -0
- package/dist/field.js +26 -0
- package/dist/index.js +38 -0
- package/dist/input-date.js +994 -0
- package/dist/input-group.js +52 -0
- package/dist/input-otp.js +179 -0
- package/dist/menu.js +2 -0
- package/dist/menubar.js +236 -0
- package/dist/message-scroller.js +446 -0
- package/dist/navigation-menu.js +330 -0
- package/dist/popover.js +307 -0
- package/dist/progress.js +39 -0
- package/dist/radio-group.js +107 -0
- package/dist/resizable.js +172 -0
- package/dist/select.js +961 -0
- package/dist/sidebar.js +343 -0
- package/dist/slider.js +259 -0
- package/dist/switch.js +53 -0
- package/dist/tabs.js +182 -0
- package/dist/toast.js +492 -0
- package/dist/toggle-group.js +111 -0
- package/dist/toggle.js +52 -0
- package/dist/toolbar.js +127 -0
- package/dist/tooltip.js +196 -0
- package/dist/utils.js +104 -0
- package/dist/virtual-list.js +2 -0
- package/package.json +250 -0
- package/src/accordion.tsx +261 -0
- package/src/availability.ts +261 -0
- package/src/avatar.tsx +99 -0
- package/src/bar.ts +156 -0
- package/src/calendar.tsx +1441 -0
- package/src/carousel.tsx +424 -0
- package/src/chart.tsx +1194 -0
- package/src/checkbox-group.tsx +132 -0
- package/src/checkbox.tsx +130 -0
- package/src/collapsible.tsx +188 -0
- package/src/collection.ts +154 -0
- package/src/command.tsx +511 -0
- package/src/context-menu.tsx +233 -0
- package/src/data-table-contract.ts +143 -0
- package/src/data-table-model.ts +760 -0
- package/src/data-table.tsx +475 -0
- package/src/dialog.tsx +393 -0
- package/src/direction.tsx +45 -0
- package/src/drawer.tsx +251 -0
- package/src/field.tsx +61 -0
- package/src/index.ts +37 -0
- package/src/input-date.tsx +1539 -0
- package/src/input-group.tsx +142 -0
- package/src/input-otp.tsx +324 -0
- package/src/menu-cluster.ts +124 -0
- package/src/menu.tsx +1095 -0
- package/src/menubar.tsx +459 -0
- package/src/message-scroller.tsx +732 -0
- package/src/native.ts +26 -0
- package/src/navigation-menu.tsx +578 -0
- package/src/popover.tsx +519 -0
- package/src/popup-surface.tsx +31 -0
- package/src/popup.ts +569 -0
- package/src/position.ts +523 -0
- package/src/progress.tsx +70 -0
- package/src/radio-group.tsx +186 -0
- package/src/resizable.tsx +310 -0
- package/src/segments.ts +922 -0
- package/src/select.tsx +1501 -0
- package/src/sidebar.tsx +683 -0
- package/src/slider.tsx +424 -0
- package/src/switch.tsx +104 -0
- package/src/tabs.tsx +314 -0
- package/src/toast.tsx +923 -0
- package/src/toggle-group.tsx +249 -0
- package/src/toggle.tsx +91 -0
- package/src/toolbar.tsx +212 -0
- package/src/tooltip.tsx +359 -0
- package/src/utils.ts +204 -0
- package/src/virtual-list.tsx +205 -0
- package/src/virtual.ts +385 -0
package/dist/command.js
ADDED
|
@@ -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 };
|
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 };
|