@platned/ui 0.6.0 → 0.7.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/README.md +7 -7
- package/dist/index.cjs +4961 -2822
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1441 -194
- package/dist/index.d.ts +1441 -194
- package/dist/index.js +4952 -2904
- package/dist/index.js.map +1 -1
- package/dist/sidebar.cjs +542 -876
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.d.cts +56 -0
- package/dist/sidebar.d.ts +56 -0
- package/dist/sidebar.js +539 -894
- package/dist/sidebar.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +5 -3
- package/tailwind-preset.cjs +79 -0
package/dist/sidebar.cjs
CHANGED
|
@@ -36,12 +36,63 @@ __export(sidebar_exports, {
|
|
|
36
36
|
SidebarUserCard: () => SidebarUserCard
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(sidebar_exports);
|
|
39
|
+
var import_react10 = require("react");
|
|
39
40
|
var import_react_router_dom2 = require("react-router-dom");
|
|
40
|
-
var
|
|
41
|
+
var import_lucide_react3 = require("lucide-react");
|
|
42
|
+
|
|
43
|
+
// src/lib/cn.ts
|
|
44
|
+
var import_clsx = require("clsx");
|
|
45
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
46
|
+
var FONT_SIZES = [
|
|
47
|
+
"body-micro",
|
|
48
|
+
"body-tiny",
|
|
49
|
+
"body-xs",
|
|
50
|
+
"body-sm",
|
|
51
|
+
"body",
|
|
52
|
+
"body-lg",
|
|
53
|
+
"heading-sm",
|
|
54
|
+
"heading",
|
|
55
|
+
"heading-lg",
|
|
56
|
+
"subheading-sm",
|
|
57
|
+
"subheading",
|
|
58
|
+
"subheading-lg",
|
|
59
|
+
"subtitle-sm",
|
|
60
|
+
"subtitle",
|
|
61
|
+
"subtitle-lg",
|
|
62
|
+
"title-sm",
|
|
63
|
+
"title",
|
|
64
|
+
"title-lg",
|
|
65
|
+
"title-hero",
|
|
66
|
+
"scale-01",
|
|
67
|
+
"scale-02",
|
|
68
|
+
"scale-03",
|
|
69
|
+
"scale-04",
|
|
70
|
+
"scale-05",
|
|
71
|
+
"scale-06",
|
|
72
|
+
"scale-07",
|
|
73
|
+
"scale-08",
|
|
74
|
+
"scale-09",
|
|
75
|
+
"scale-10",
|
|
76
|
+
"scale-12"
|
|
77
|
+
];
|
|
78
|
+
var twMerge = (0, import_tailwind_merge.extendTailwindMerge)({
|
|
79
|
+
extend: {
|
|
80
|
+
classGroups: {
|
|
81
|
+
"font-size": [{ text: [...FONT_SIZES] }]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
function cn(...inputs) {
|
|
86
|
+
return twMerge((0, import_clsx.clsx)(inputs));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/lib/focus-ring.ts
|
|
90
|
+
var FOCUS_RING = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-1 focus-visible:ring-offset-default";
|
|
91
|
+
var FOCUS_RING_INSET = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand";
|
|
41
92
|
|
|
42
93
|
// src/lib/slot.tsx
|
|
43
94
|
var import_react = require("react");
|
|
44
|
-
var
|
|
95
|
+
var import_clsx2 = __toESM(require("clsx"), 1);
|
|
45
96
|
var Slot = (0, import_react.forwardRef)(function Slot2({ children, wrapChildren, ...slotProps }, forwardedRef) {
|
|
46
97
|
if (!(0, import_react.isValidElement)(children)) return null;
|
|
47
98
|
const child = children;
|
|
@@ -66,7 +117,7 @@ function mergeProps(slotProps, childProps) {
|
|
|
66
117
|
merged[key] = childValue ?? slotValue;
|
|
67
118
|
}
|
|
68
119
|
} else if (key === "className") {
|
|
69
|
-
merged[key] = (0,
|
|
120
|
+
merged[key] = (0, import_clsx2.default)(slotValue, childValue);
|
|
70
121
|
} else if (key === "style") {
|
|
71
122
|
merged[key] = { ...slotValue, ...childValue };
|
|
72
123
|
} else {
|
|
@@ -103,27 +154,30 @@ function useControlSize(explicit) {
|
|
|
103
154
|
|
|
104
155
|
// src/components/forms/icon-button.tsx
|
|
105
156
|
var import_react3 = require("react");
|
|
106
|
-
var
|
|
157
|
+
var import_clsx3 = __toESM(require("clsx"), 1);
|
|
107
158
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
108
159
|
var variantStyles = {
|
|
109
|
-
primary: (0,
|
|
160
|
+
primary: (0, import_clsx3.default)(
|
|
110
161
|
"bg-brand border border-brand text-brand-on-brand",
|
|
111
162
|
"hover:bg-brand-hover",
|
|
112
|
-
"disabled:bg-disabled disabled:border-disabled disabled:text-disabled-on-disabled"
|
|
163
|
+
"disabled:bg-disabled disabled:border-disabled disabled:text-disabled-on-disabled",
|
|
164
|
+
"aria-disabled:bg-disabled aria-disabled:border-disabled aria-disabled:text-disabled-on-disabled"
|
|
113
165
|
),
|
|
114
166
|
// Neutral uses secondary background (light gray) for visual weight without a text label
|
|
115
|
-
neutral: (0,
|
|
167
|
+
neutral: (0, import_clsx3.default)(
|
|
116
168
|
"bg-default-secondary border border-default text-default",
|
|
117
169
|
// icon turns brand blue on hover (Figma spec)
|
|
118
170
|
"hover:bg-default-secondary-hover hover:text-brand",
|
|
119
|
-
"disabled:bg-disabled disabled:border-disabled disabled:text-disabled-on-disabled"
|
|
171
|
+
"disabled:bg-disabled disabled:border-disabled disabled:text-disabled-on-disabled",
|
|
172
|
+
"aria-disabled:bg-disabled aria-disabled:border-disabled aria-disabled:text-disabled-on-disabled"
|
|
120
173
|
),
|
|
121
|
-
subtle: (0,
|
|
174
|
+
subtle: (0, import_clsx3.default)(
|
|
122
175
|
"bg-transparent text-neutral",
|
|
123
176
|
// hover uses background-default-default-hover (#f5f5f5), icon turns brand blue (Figma spec)
|
|
124
177
|
"hover:bg-default-hover hover:text-brand",
|
|
125
178
|
// disabled needs explicit border since there is no base border
|
|
126
|
-
"disabled:bg-disabled disabled:border disabled:border-disabled disabled:text-disabled-on-disabled"
|
|
179
|
+
"disabled:bg-disabled disabled:border disabled:border-disabled disabled:text-disabled-on-disabled",
|
|
180
|
+
"aria-disabled:bg-disabled aria-disabled:border aria-disabled:border-disabled aria-disabled:text-disabled-on-disabled"
|
|
127
181
|
)
|
|
128
182
|
};
|
|
129
183
|
var sizeStyles = {
|
|
@@ -131,27 +185,52 @@ var sizeStyles = {
|
|
|
131
185
|
md: "size-9 rounded-lg [&_svg]:size-5",
|
|
132
186
|
sm: "size-6 rounded-full [&_svg]:size-4"
|
|
133
187
|
};
|
|
134
|
-
var IconButton = (0, import_react3.forwardRef)(function IconButton2({
|
|
135
|
-
|
|
188
|
+
var IconButton = (0, import_react3.forwardRef)(function IconButton2({
|
|
189
|
+
variant = "primary",
|
|
190
|
+
size: sizeProp,
|
|
191
|
+
asChild = false,
|
|
192
|
+
type,
|
|
193
|
+
disabled,
|
|
194
|
+
onClick,
|
|
195
|
+
children,
|
|
196
|
+
className,
|
|
197
|
+
...props
|
|
198
|
+
}, ref) {
|
|
199
|
+
const size = useControlSize(sizeProp);
|
|
200
|
+
const softDisabled = asChild && Boolean(disabled);
|
|
201
|
+
const handleClick = (event) => {
|
|
202
|
+
if (softDisabled) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
event.stopPropagation();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
onClick?.(event);
|
|
208
|
+
};
|
|
136
209
|
const rootProps = {
|
|
137
|
-
className
|
|
210
|
+
// `cn`, so a caller's `className` beats the size's radius and the variant's colours.
|
|
211
|
+
className: cn(
|
|
138
212
|
"inline-flex shrink-0 items-center justify-center transition-colors",
|
|
139
|
-
|
|
140
|
-
"disabled:cursor-not-allowed",
|
|
213
|
+
FOCUS_RING,
|
|
214
|
+
"disabled:cursor-not-allowed aria-disabled:cursor-not-allowed",
|
|
141
215
|
variantStyles[variant],
|
|
142
|
-
sizeStyles[
|
|
216
|
+
sizeStyles[size],
|
|
143
217
|
className
|
|
144
218
|
),
|
|
219
|
+
"data-variant": variant,
|
|
220
|
+
"data-size": size,
|
|
221
|
+
"aria-disabled": softDisabled || void 0,
|
|
222
|
+
onClick: handleClick,
|
|
145
223
|
...props
|
|
146
224
|
};
|
|
147
225
|
if (asChild) {
|
|
148
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Slot, { ref, ...rootProps, children });
|
|
226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Slot, { ref, tabIndex: disabled ? -1 : void 0, ...rootProps, children });
|
|
149
227
|
}
|
|
150
228
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
151
229
|
"button",
|
|
152
230
|
{
|
|
153
231
|
ref,
|
|
154
232
|
type: type ?? "button",
|
|
233
|
+
disabled,
|
|
155
234
|
...rootProps,
|
|
156
235
|
children
|
|
157
236
|
}
|
|
@@ -159,7 +238,7 @@ var IconButton = (0, import_react3.forwardRef)(function IconButton2({ variant =
|
|
|
159
238
|
});
|
|
160
239
|
|
|
161
240
|
// src/components/avatar/avatar.tsx
|
|
162
|
-
var
|
|
241
|
+
var import_clsx4 = __toESM(require("clsx"), 1);
|
|
163
242
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
164
243
|
var sizeStyles2 = {
|
|
165
244
|
lg: "size-10",
|
|
@@ -171,16 +250,16 @@ var initialTextStyles = {
|
|
|
171
250
|
md: "text-body-xs font-semibold",
|
|
172
251
|
sm: "text-body-tiny font-semibold"
|
|
173
252
|
};
|
|
174
|
-
function getShapeClass(shape,
|
|
253
|
+
function getShapeClass(shape, size) {
|
|
175
254
|
if (shape === "circle") return "rounded-full";
|
|
176
|
-
return
|
|
255
|
+
return size === "sm" ? "rounded" : "rounded-lg";
|
|
177
256
|
}
|
|
178
257
|
function Avatar(props) {
|
|
179
|
-
const { size
|
|
180
|
-
const base = (0,
|
|
258
|
+
const { size = "lg", shape = "circle", className } = props;
|
|
259
|
+
const base = (0, import_clsx4.default)(
|
|
181
260
|
"shrink-0 overflow-hidden",
|
|
182
|
-
sizeStyles2[
|
|
183
|
-
getShapeClass(shape,
|
|
261
|
+
sizeStyles2[size],
|
|
262
|
+
getShapeClass(shape, size),
|
|
184
263
|
className
|
|
185
264
|
);
|
|
186
265
|
if (props.type === "image") {
|
|
@@ -196,16 +275,16 @@ function Avatar(props) {
|
|
|
196
275
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
197
276
|
"div",
|
|
198
277
|
{
|
|
199
|
-
className: (0,
|
|
278
|
+
className: (0, import_clsx4.default)(base, "flex items-center justify-center"),
|
|
200
279
|
style: {
|
|
201
280
|
background: "linear-gradient(135deg, var(--color-graphic-blue) 30%, var(--color-graphic-red) 100%)"
|
|
202
281
|
},
|
|
203
282
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
204
283
|
"span",
|
|
205
284
|
{
|
|
206
|
-
className: (0,
|
|
285
|
+
className: (0, import_clsx4.default)(
|
|
207
286
|
"select-none leading-none text-brand-on-brand",
|
|
208
|
-
initialTextStyles[
|
|
287
|
+
initialTextStyles[size]
|
|
209
288
|
),
|
|
210
289
|
children: props.initial.charAt(0).toUpperCase()
|
|
211
290
|
}
|
|
@@ -214,52 +293,6 @@ function Avatar(props) {
|
|
|
214
293
|
);
|
|
215
294
|
}
|
|
216
295
|
|
|
217
|
-
// src/lib/cn.ts
|
|
218
|
-
var import_clsx4 = require("clsx");
|
|
219
|
-
var import_tailwind_merge = require("tailwind-merge");
|
|
220
|
-
var FONT_SIZES = [
|
|
221
|
-
"body-micro",
|
|
222
|
-
"body-tiny",
|
|
223
|
-
"body-xs",
|
|
224
|
-
"body-sm",
|
|
225
|
-
"body",
|
|
226
|
-
"body-lg",
|
|
227
|
-
"heading-sm",
|
|
228
|
-
"heading",
|
|
229
|
-
"heading-lg",
|
|
230
|
-
"subheading-sm",
|
|
231
|
-
"subheading",
|
|
232
|
-
"subheading-lg",
|
|
233
|
-
"subtitle-sm",
|
|
234
|
-
"subtitle",
|
|
235
|
-
"subtitle-lg",
|
|
236
|
-
"title-sm",
|
|
237
|
-
"title",
|
|
238
|
-
"title-lg",
|
|
239
|
-
"title-hero",
|
|
240
|
-
"scale-01",
|
|
241
|
-
"scale-02",
|
|
242
|
-
"scale-03",
|
|
243
|
-
"scale-04",
|
|
244
|
-
"scale-05",
|
|
245
|
-
"scale-06",
|
|
246
|
-
"scale-07",
|
|
247
|
-
"scale-08",
|
|
248
|
-
"scale-09",
|
|
249
|
-
"scale-10",
|
|
250
|
-
"scale-12"
|
|
251
|
-
];
|
|
252
|
-
var twMerge = (0, import_tailwind_merge.extendTailwindMerge)({
|
|
253
|
-
extend: {
|
|
254
|
-
classGroups: {
|
|
255
|
-
"font-size": [{ text: [...FONT_SIZES] }]
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
function cn(...inputs) {
|
|
260
|
-
return twMerge((0, import_clsx4.clsx)(inputs));
|
|
261
|
-
}
|
|
262
|
-
|
|
263
296
|
// src/lib/use-controllable-state.ts
|
|
264
297
|
var import_react4 = require("react");
|
|
265
298
|
function useControllableState({
|
|
@@ -285,189 +318,6 @@ function useControllableState({
|
|
|
285
318
|
return [current, setValue];
|
|
286
319
|
}
|
|
287
320
|
|
|
288
|
-
// src/lib/use-roving-focus.ts
|
|
289
|
-
var import_react5 = require("react");
|
|
290
|
-
function useRovingFocus({
|
|
291
|
-
orientation = "horizontal",
|
|
292
|
-
loop = true,
|
|
293
|
-
typeahead = false,
|
|
294
|
-
defaultIndex = 0,
|
|
295
|
-
onEscape
|
|
296
|
-
} = {}) {
|
|
297
|
-
const [activeIndex, setActiveIndex] = (0, import_react5.useState)(defaultIndex);
|
|
298
|
-
const items = (0, import_react5.useRef)([]);
|
|
299
|
-
const meta = (0, import_react5.useRef)([]);
|
|
300
|
-
const search = (0, import_react5.useRef)("");
|
|
301
|
-
const searchTimer = (0, import_react5.useRef)(null);
|
|
302
|
-
(0, import_react5.useEffect)(
|
|
303
|
-
() => () => {
|
|
304
|
-
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
305
|
-
},
|
|
306
|
-
[]
|
|
307
|
-
);
|
|
308
|
-
const focusIndex = (0, import_react5.useCallback)((index) => {
|
|
309
|
-
const node = items.current[index];
|
|
310
|
-
if (!node) return;
|
|
311
|
-
setActiveIndex(index);
|
|
312
|
-
node.focus();
|
|
313
|
-
}, []);
|
|
314
|
-
const move = (0, import_react5.useCallback)(
|
|
315
|
-
(start, step) => {
|
|
316
|
-
const total = items.current.length;
|
|
317
|
-
if (total === 0) return;
|
|
318
|
-
for (let i = 1; i <= total; i++) {
|
|
319
|
-
let next = start + step * i;
|
|
320
|
-
if (loop) {
|
|
321
|
-
next = (next % total + total) % total;
|
|
322
|
-
} else if (next < 0 || next >= total) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (items.current[next] && !meta.current[next]?.disabled) {
|
|
326
|
-
focusIndex(next);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
[loop, focusIndex]
|
|
332
|
-
);
|
|
333
|
-
const moveToEdge = (0, import_react5.useCallback)(
|
|
334
|
-
(from) => {
|
|
335
|
-
const total = items.current.length;
|
|
336
|
-
if (total === 0) return;
|
|
337
|
-
move(from === "start" ? -1 : total, from === "start" ? 1 : -1);
|
|
338
|
-
},
|
|
339
|
-
[move]
|
|
340
|
-
);
|
|
341
|
-
const runTypeahead = (0, import_react5.useCallback)(
|
|
342
|
-
(char) => {
|
|
343
|
-
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
344
|
-
search.current += char.toLowerCase();
|
|
345
|
-
searchTimer.current = setTimeout(() => {
|
|
346
|
-
search.current = "";
|
|
347
|
-
}, 500);
|
|
348
|
-
const total = items.current.length;
|
|
349
|
-
const query = search.current;
|
|
350
|
-
for (let i = 1; i <= total; i++) {
|
|
351
|
-
const index = (activeIndex + i) % total;
|
|
352
|
-
if (meta.current[index]?.disabled) continue;
|
|
353
|
-
const label = meta.current[index]?.label?.toLowerCase();
|
|
354
|
-
if (label?.startsWith(query)) {
|
|
355
|
-
focusIndex(index);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
[activeIndex, focusIndex]
|
|
361
|
-
);
|
|
362
|
-
const onKeyDown = (0, import_react5.useCallback)(
|
|
363
|
-
(event) => {
|
|
364
|
-
const { key } = event;
|
|
365
|
-
const vertical = orientation === "vertical" || orientation === "both";
|
|
366
|
-
const horizontal = orientation === "horizontal" || orientation === "both";
|
|
367
|
-
const next = vertical && key === "ArrowDown" || horizontal && key === "ArrowRight";
|
|
368
|
-
const prev = vertical && key === "ArrowUp" || horizontal && key === "ArrowLeft";
|
|
369
|
-
if (next || prev) {
|
|
370
|
-
event.preventDefault();
|
|
371
|
-
move(activeIndex, next ? 1 : -1);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
if (key === "Home") {
|
|
375
|
-
event.preventDefault();
|
|
376
|
-
moveToEdge("start");
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (key === "End") {
|
|
380
|
-
event.preventDefault();
|
|
381
|
-
moveToEdge("end");
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
if (key === "Escape" && onEscape) {
|
|
385
|
-
onEscape();
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
if (typeahead && key.length === 1 && key !== " " && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
389
|
-
event.preventDefault();
|
|
390
|
-
runTypeahead(key);
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
[orientation, activeIndex, move, moveToEdge, onEscape, typeahead, runTypeahead]
|
|
394
|
-
);
|
|
395
|
-
const itemProps = (0, import_react5.useCallback)(
|
|
396
|
-
(index, options = {}) => {
|
|
397
|
-
meta.current[index] = options;
|
|
398
|
-
return {
|
|
399
|
-
ref: (node) => {
|
|
400
|
-
items.current[index] = node;
|
|
401
|
-
},
|
|
402
|
-
// The roving part: exactly one item is in the tab sequence, so Tab enters the
|
|
403
|
-
// composite once and Tab again leaves it.
|
|
404
|
-
tabIndex: index === activeIndex ? 0 : -1,
|
|
405
|
-
onFocus: () => setActiveIndex(index),
|
|
406
|
-
"data-roving-index": index
|
|
407
|
-
};
|
|
408
|
-
},
|
|
409
|
-
[activeIndex]
|
|
410
|
-
);
|
|
411
|
-
return {
|
|
412
|
-
activeIndex,
|
|
413
|
-
setActiveIndex,
|
|
414
|
-
focusIndex,
|
|
415
|
-
moveToEdge,
|
|
416
|
-
itemProps,
|
|
417
|
-
containerProps: { onKeyDown }
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// src/components/overlays/floating.tsx
|
|
422
|
-
var import_react6 = require("react");
|
|
423
|
-
var import_react7 = require("@floating-ui/react");
|
|
424
|
-
function useFloatingPanel({
|
|
425
|
-
open,
|
|
426
|
-
onOpenChange,
|
|
427
|
-
side = "bottom",
|
|
428
|
-
align = "center",
|
|
429
|
-
sideOffset = 6,
|
|
430
|
-
alignOffset = 0,
|
|
431
|
-
collisionPadding = 8,
|
|
432
|
-
withArrow = false,
|
|
433
|
-
matchAnchorWidth = false
|
|
434
|
-
}) {
|
|
435
|
-
const arrowRef = (0, import_react6.useRef)(null);
|
|
436
|
-
const placement = align === "center" ? side : `${side}-${align}`;
|
|
437
|
-
const floating = (0, import_react7.useFloating)({
|
|
438
|
-
open,
|
|
439
|
-
onOpenChange,
|
|
440
|
-
placement,
|
|
441
|
-
// Only track while the panel is actually on screen. `autoUpdate` attaches scroll and
|
|
442
|
-
// resize listeners plus a ResizeObserver, so leaving it running on a closed panel
|
|
443
|
-
// would cost every consumer a listener per menu on the page.
|
|
444
|
-
whileElementsMounted: import_react7.autoUpdate,
|
|
445
|
-
middleware: [
|
|
446
|
-
(0, import_react7.offset)({ mainAxis: sideOffset, crossAxis: alignOffset }),
|
|
447
|
-
// `flip` before `shift`: try the opposite edge first, and only slide along the
|
|
448
|
-
// current one if neither edge fits. The reverse order produces panels that cling to
|
|
449
|
-
// the viewport edge when they had room to simply flip.
|
|
450
|
-
(0, import_react7.flip)({ padding: collisionPadding }),
|
|
451
|
-
(0, import_react7.shift)({ padding: collisionPadding }),
|
|
452
|
-
matchAnchorWidth && (0, import_react7.size)({
|
|
453
|
-
padding: collisionPadding,
|
|
454
|
-
apply({ rects, elements, availableHeight }) {
|
|
455
|
-
elements.floating.style.setProperty(
|
|
456
|
-
"--panel-anchor-width",
|
|
457
|
-
`${rects.reference.width}px`
|
|
458
|
-
);
|
|
459
|
-
elements.floating.style.setProperty(
|
|
460
|
-
"--panel-available-height",
|
|
461
|
-
`${availableHeight}px`
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
}),
|
|
465
|
-
withArrow && (0, import_react7.arrow)({ element: arrowRef, padding: 4 })
|
|
466
|
-
].filter(Boolean)
|
|
467
|
-
});
|
|
468
|
-
return { ...floating, arrowRef };
|
|
469
|
-
}
|
|
470
|
-
|
|
471
321
|
// src/components/overlays/menu-styles.ts
|
|
472
322
|
var import_clsx6 = __toESM(require("clsx"), 1);
|
|
473
323
|
|
|
@@ -499,248 +349,30 @@ var OVERLAY_ITEM_SIZE = {
|
|
|
499
349
|
md: "px-2.5 py-2 text-body-sm",
|
|
500
350
|
sm: "px-2 py-1.5 text-body-tiny"
|
|
501
351
|
};
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
return tone === "danger" ? "text-danger hover:bg-danger-tertiary focus:bg-danger-tertiary" : "text-default hover:bg-default-secondary focus:bg-default-secondary";
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// src/components/overlays/overlay.ts
|
|
508
|
-
var import_react9 = require("react");
|
|
509
|
-
var import_tabbable = require("tabbable");
|
|
510
|
-
|
|
511
|
-
// src/components/overlays/overlay-stack.ts
|
|
512
|
-
var import_react8 = require("react");
|
|
513
|
-
var layers = [];
|
|
514
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
515
|
-
function notify() {
|
|
516
|
-
for (const listener of listeners) listener();
|
|
517
|
-
}
|
|
518
|
-
function topmost() {
|
|
519
|
-
return layers[layers.length - 1]?.id;
|
|
520
|
-
}
|
|
521
|
-
var scrollLocks = 0;
|
|
522
|
-
var restoreBody = null;
|
|
523
|
-
function lockScroll() {
|
|
524
|
-
scrollLocks += 1;
|
|
525
|
-
if (scrollLocks > 1 || typeof document === "undefined") return;
|
|
526
|
-
const { body, documentElement } = document;
|
|
527
|
-
const previousOverflow = body.style.overflow;
|
|
528
|
-
const previousPaddingRight = body.style.paddingRight;
|
|
529
|
-
const scrollbarWidth = window.innerWidth - documentElement.clientWidth;
|
|
530
|
-
if (scrollbarWidth > 0) {
|
|
531
|
-
const current = parseFloat(window.getComputedStyle(body).paddingRight) || 0;
|
|
532
|
-
body.style.paddingRight = `${current + scrollbarWidth}px`;
|
|
533
|
-
}
|
|
534
|
-
body.style.overflow = "hidden";
|
|
535
|
-
restoreBody = () => {
|
|
536
|
-
body.style.overflow = previousOverflow;
|
|
537
|
-
body.style.paddingRight = previousPaddingRight;
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
function unlockScroll() {
|
|
541
|
-
scrollLocks = Math.max(0, scrollLocks - 1);
|
|
542
|
-
if (scrollLocks === 0 && restoreBody) {
|
|
543
|
-
restoreBody();
|
|
544
|
-
restoreBody = null;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
var inerted = /* @__PURE__ */ new Set();
|
|
548
|
-
function applyInert(node) {
|
|
549
|
-
if (typeof document === "undefined" || !node) return;
|
|
550
|
-
let root = node;
|
|
551
|
-
while (root.parentElement && root.parentElement !== document.body) {
|
|
552
|
-
root = root.parentElement;
|
|
553
|
-
}
|
|
554
|
-
for (const child of Array.from(document.body.children)) {
|
|
555
|
-
if (child === root || !(child instanceof HTMLElement)) continue;
|
|
556
|
-
if (child.hasAttribute("inert")) continue;
|
|
557
|
-
child.setAttribute("inert", "");
|
|
558
|
-
inerted.add(child);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
function releaseInert() {
|
|
562
|
-
for (const el of inerted) el.removeAttribute("inert");
|
|
563
|
-
inerted.clear();
|
|
564
|
-
}
|
|
565
|
-
function useOverlayLayer(active, {
|
|
566
|
-
lockScroll: shouldLockScroll = true,
|
|
567
|
-
inertBackground = true,
|
|
568
|
-
panelRef
|
|
569
|
-
} = {}) {
|
|
570
|
-
const id = (0, import_react8.useRef)();
|
|
571
|
-
if (!id.current) id.current = /* @__PURE__ */ Symbol("overlay-layer");
|
|
572
|
-
const [isTopmost, setIsTopmost] = (0, import_react8.useState)(false);
|
|
573
|
-
(0, import_react8.useEffect)(() => {
|
|
574
|
-
if (!active) return;
|
|
575
|
-
const self = id.current;
|
|
576
|
-
layers.push({ id: self });
|
|
577
|
-
const sync = () => setIsTopmost(topmost() === self);
|
|
578
|
-
listeners.add(sync);
|
|
579
|
-
sync();
|
|
580
|
-
notify();
|
|
581
|
-
if (shouldLockScroll) lockScroll();
|
|
582
|
-
return () => {
|
|
583
|
-
const index = layers.findIndex((layer) => layer.id === self);
|
|
584
|
-
if (index !== -1) layers.splice(index, 1);
|
|
585
|
-
listeners.delete(sync);
|
|
586
|
-
notify();
|
|
587
|
-
if (shouldLockScroll) unlockScroll();
|
|
588
|
-
};
|
|
589
|
-
}, [active, shouldLockScroll]);
|
|
590
|
-
(0, import_react8.useEffect)(() => {
|
|
591
|
-
if (!active || !inertBackground) return;
|
|
592
|
-
if (inerted.size === 0) applyInert(panelRef?.current ?? null);
|
|
593
|
-
return () => {
|
|
594
|
-
if (layers.length <= 1) releaseInert();
|
|
595
|
-
};
|
|
596
|
-
}, [active, inertBackground, panelRef]);
|
|
597
|
-
return { isTopmost };
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// src/components/overlays/overlay.ts
|
|
601
|
-
function useOverlayDismiss({
|
|
602
|
-
onDismiss,
|
|
603
|
-
panelRef,
|
|
604
|
-
active = true,
|
|
605
|
-
closeOnEscape = true,
|
|
606
|
-
closeOnOutsidePress = true,
|
|
607
|
-
trapFocus = true,
|
|
608
|
-
lockScroll: lockScroll2 = true,
|
|
609
|
-
inertBackground = true,
|
|
610
|
-
autoFocus = true,
|
|
611
|
-
initialFocus,
|
|
612
|
-
returnFocus,
|
|
613
|
-
onEscapeKeyDown,
|
|
614
|
-
onInteractOutside
|
|
615
|
-
}) {
|
|
616
|
-
const { isTopmost } = useOverlayLayer(active, {
|
|
617
|
-
lockScroll: lockScroll2,
|
|
618
|
-
inertBackground,
|
|
619
|
-
panelRef
|
|
620
|
-
});
|
|
621
|
-
const handlers = (0, import_react9.useRef)({ onDismiss, onEscapeKeyDown, onInteractOutside });
|
|
622
|
-
handlers.current = { onDismiss, onEscapeKeyDown, onInteractOutside };
|
|
623
|
-
const isTopmostRef = (0, import_react9.useRef)(isTopmost);
|
|
624
|
-
isTopmostRef.current = isTopmost;
|
|
625
|
-
(0, import_react9.useEffect)(() => {
|
|
626
|
-
if (!active || !closeOnEscape) return;
|
|
627
|
-
function onKeyDown(event) {
|
|
628
|
-
if (event.key !== "Escape") return;
|
|
629
|
-
if (!isTopmostRef.current) return;
|
|
630
|
-
handlers.current.onEscapeKeyDown?.(event);
|
|
631
|
-
if (event.defaultPrevented) return;
|
|
632
|
-
handlers.current.onDismiss();
|
|
633
|
-
}
|
|
634
|
-
document.addEventListener("keydown", onKeyDown);
|
|
635
|
-
return () => document.removeEventListener("keydown", onKeyDown);
|
|
636
|
-
}, [active, closeOnEscape]);
|
|
637
|
-
(0, import_react9.useEffect)(() => {
|
|
638
|
-
if (!active || !closeOnOutsidePress) return;
|
|
639
|
-
function onPointerDown(event) {
|
|
640
|
-
if (!isTopmostRef.current) return;
|
|
641
|
-
const panel = panelRef.current;
|
|
642
|
-
if (!panel || panel.contains(event.target)) return;
|
|
643
|
-
handlers.current.onInteractOutside?.(event);
|
|
644
|
-
if (event.defaultPrevented) return;
|
|
645
|
-
handlers.current.onDismiss();
|
|
646
|
-
}
|
|
647
|
-
document.addEventListener("pointerdown", onPointerDown);
|
|
648
|
-
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
649
|
-
}, [active, closeOnOutsidePress, panelRef]);
|
|
650
|
-
(0, import_react9.useEffect)(() => {
|
|
651
|
-
if (!active || !trapFocus) return;
|
|
652
|
-
function onKeyDown(event) {
|
|
653
|
-
if (event.key !== "Tab" || !isTopmostRef.current) return;
|
|
654
|
-
const panel = panelRef.current;
|
|
655
|
-
if (!panel) return;
|
|
656
|
-
const items = (0, import_tabbable.tabbable)(panel);
|
|
657
|
-
if (items.length === 0) {
|
|
658
|
-
event.preventDefault();
|
|
659
|
-
panel.focus({ preventScroll: true });
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
const first = items[0];
|
|
663
|
-
const last = items[items.length - 1];
|
|
664
|
-
const activeElement = document.activeElement;
|
|
665
|
-
const inside = panel.contains(activeElement);
|
|
666
|
-
if (event.shiftKey && (activeElement === first || !inside)) {
|
|
667
|
-
event.preventDefault();
|
|
668
|
-
last.focus();
|
|
669
|
-
} else if (!event.shiftKey && (activeElement === last || !inside)) {
|
|
670
|
-
event.preventDefault();
|
|
671
|
-
first.focus();
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
function onFocusIn(event) {
|
|
675
|
-
if (!isTopmostRef.current) return;
|
|
676
|
-
const panel = panelRef.current;
|
|
677
|
-
if (!panel) return;
|
|
678
|
-
if (panel.contains(event.target)) return;
|
|
679
|
-
const items = (0, import_tabbable.tabbable)(panel);
|
|
680
|
-
(items[0] ?? panel).focus({ preventScroll: true });
|
|
681
|
-
}
|
|
682
|
-
document.addEventListener("keydown", onKeyDown);
|
|
683
|
-
document.addEventListener("focusin", onFocusIn);
|
|
684
|
-
return () => {
|
|
685
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
686
|
-
document.removeEventListener("focusin", onFocusIn);
|
|
687
|
-
};
|
|
688
|
-
}, [active, trapFocus, panelRef]);
|
|
689
|
-
(0, import_react9.useEffect)(() => {
|
|
690
|
-
if (!active) return;
|
|
691
|
-
const previouslyFocused = document.activeElement;
|
|
692
|
-
const panel = panelRef.current;
|
|
693
|
-
let observer = null;
|
|
694
|
-
if (panel && autoFocus) {
|
|
695
|
-
if (!panel.hasAttribute("tabindex")) panel.setAttribute("tabindex", "-1");
|
|
696
|
-
const focusTarget = () => {
|
|
697
|
-
const explicit = initialFocus?.current;
|
|
698
|
-
if (explicit) {
|
|
699
|
-
explicit.focus({ preventScroll: true });
|
|
700
|
-
return true;
|
|
701
|
-
}
|
|
702
|
-
const items = (0, import_tabbable.tabbable)(panel);
|
|
703
|
-
if (items.length > 0) {
|
|
704
|
-
items[0].focus({ preventScroll: true });
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
return false;
|
|
708
|
-
};
|
|
709
|
-
if (!focusTarget()) {
|
|
710
|
-
panel.focus({ preventScroll: true });
|
|
711
|
-
observer = new MutationObserver(() => {
|
|
712
|
-
if (focusTarget()) {
|
|
713
|
-
observer?.disconnect();
|
|
714
|
-
observer = null;
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
observer.observe(panel, { childList: true, subtree: true });
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return () => {
|
|
721
|
-
observer?.disconnect();
|
|
722
|
-
const target = returnFocus ? returnFocus.current : previouslyFocused;
|
|
723
|
-
if (target?.isConnected) target.focus({ preventScroll: true });
|
|
724
|
-
};
|
|
725
|
-
}, [active, panelRef, initialFocus, returnFocus, autoFocus]);
|
|
726
|
-
}
|
|
352
|
+
var OVERLAY_HEADING = "px-2.5 py-1.5 text-body-tiny font-semibold uppercase tracking-wide text-default-tertiary";
|
|
353
|
+
var OVERLAY_SEPARATOR = "-mx-1 my-1 h-px bg-divider";
|
|
727
354
|
|
|
728
355
|
// src/components/overlays/portal.tsx
|
|
729
|
-
var
|
|
356
|
+
var import_react5 = require("react");
|
|
730
357
|
var import_react_dom = require("react-dom");
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
358
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
359
|
+
var PortalContainerContext = (0, import_react5.createContext)(null);
|
|
360
|
+
function usePortalContainer() {
|
|
361
|
+
return (0, import_react5.useContext)(PortalContainerContext);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/lib/compose-event-handlers.ts
|
|
365
|
+
function composeEventHandlers(theirs, ours) {
|
|
366
|
+
return (event) => {
|
|
367
|
+
theirs?.(event);
|
|
368
|
+
if (!event.defaultPrevented) ours(event);
|
|
369
|
+
};
|
|
738
370
|
}
|
|
739
371
|
|
|
740
372
|
// src/components/overlays/tooltip.tsx
|
|
741
|
-
var
|
|
742
|
-
var
|
|
743
|
-
var
|
|
373
|
+
var import_react6 = require("react");
|
|
374
|
+
var import_radix_ui = require("radix-ui");
|
|
375
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
744
376
|
function Tooltip({
|
|
745
377
|
children,
|
|
746
378
|
title,
|
|
@@ -748,224 +380,240 @@ function Tooltip({
|
|
|
748
380
|
side = "top",
|
|
749
381
|
align = "center",
|
|
750
382
|
openDelay = 150,
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
defaultOpen = false,
|
|
383
|
+
open,
|
|
384
|
+
defaultOpen,
|
|
754
385
|
onOpenChange,
|
|
755
386
|
association = "describedby",
|
|
756
387
|
className
|
|
757
388
|
}) {
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const schedule = (0, import_react11.useCallback)(
|
|
773
|
-
(next, delay) => {
|
|
774
|
-
clearTimer();
|
|
775
|
-
if (delay === 0) {
|
|
776
|
-
setOpen(next);
|
|
777
|
-
return;
|
|
389
|
+
const container = usePortalContainer();
|
|
390
|
+
const labelId = (0, import_react6.useId)();
|
|
391
|
+
const labelled = association === "labelledby";
|
|
392
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
393
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "font-semibold", children: title }),
|
|
394
|
+
body && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: cn(title && "mt-0.5 opacity-80"), children: body })
|
|
395
|
+
] });
|
|
396
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_radix_ui.Tooltip.Provider, { delayDuration: openDelay, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_radix_ui.Tooltip.Root, { open, defaultOpen, onOpenChange, children: [
|
|
397
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
398
|
+
import_radix_ui.Tooltip.Trigger,
|
|
399
|
+
{
|
|
400
|
+
asChild: true,
|
|
401
|
+
...labelled ? { "aria-labelledby": labelId, "aria-describedby": void 0 } : {},
|
|
402
|
+
children
|
|
778
403
|
}
|
|
779
|
-
timer.current = setTimeout(() => setOpen(next), delay);
|
|
780
|
-
},
|
|
781
|
-
[clearTimer, setOpen]
|
|
782
|
-
);
|
|
783
|
-
(0, import_react11.useEffect)(() => clearTimer, [clearTimer]);
|
|
784
|
-
const { refs, floatingStyles } = useFloatingPanel({
|
|
785
|
-
open,
|
|
786
|
-
onOpenChange: setOpen,
|
|
787
|
-
side,
|
|
788
|
-
align,
|
|
789
|
-
sideOffset: 6
|
|
790
|
-
});
|
|
791
|
-
(0, import_react11.useEffect)(() => {
|
|
792
|
-
refs.setReference(triggerNode);
|
|
793
|
-
}, [refs, triggerNode]);
|
|
794
|
-
(0, import_react11.useEffect)(() => {
|
|
795
|
-
if (!open) return;
|
|
796
|
-
function onKeyDown(event) {
|
|
797
|
-
if (event.key === "Escape") setOpen(false);
|
|
798
|
-
}
|
|
799
|
-
document.addEventListener("keydown", onKeyDown);
|
|
800
|
-
return () => document.removeEventListener("keydown", onKeyDown);
|
|
801
|
-
}, [open, setOpen]);
|
|
802
|
-
if (!(0, import_react11.isValidElement)(children)) return null;
|
|
803
|
-
const child = children;
|
|
804
|
-
const childProps = child.props;
|
|
805
|
-
const describedBy = association === "describedby" ? tooltipId : void 0;
|
|
806
|
-
const labelledBy = association === "labelledby" ? tooltipId : void 0;
|
|
807
|
-
const trigger = (0, import_react11.cloneElement)(child, {
|
|
808
|
-
ref: composeRefs(
|
|
809
|
-
(node) => setTriggerNode(node),
|
|
810
|
-
child.ref
|
|
811
404
|
),
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
childProps.onPointerLeave?.(event);
|
|
820
|
-
schedule(false, closeDelay);
|
|
821
|
-
},
|
|
822
|
-
// Focus, not just hover — this is the difference between a tooltip a keyboard user can
|
|
823
|
-
// read and one they cannot.
|
|
824
|
-
onFocus: (event) => {
|
|
825
|
-
childProps.onFocus?.(event);
|
|
826
|
-
schedule(true, 0);
|
|
827
|
-
},
|
|
828
|
-
onBlur: (event) => {
|
|
829
|
-
childProps.onBlur?.(event);
|
|
830
|
-
schedule(false, 0);
|
|
831
|
-
}
|
|
832
|
-
});
|
|
833
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
834
|
-
trigger,
|
|
835
|
-
(open || association === "labelledby") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
836
|
-
"div",
|
|
405
|
+
labelled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { id: labelId, hidden: true, children: [
|
|
406
|
+
title,
|
|
407
|
+
body ? " " : null,
|
|
408
|
+
body
|
|
409
|
+
] }),
|
|
410
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_radix_ui.Tooltip.Portal, { container: container ?? void 0, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
411
|
+
import_radix_ui.Tooltip.Content,
|
|
837
412
|
{
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
className: (
|
|
413
|
+
side,
|
|
414
|
+
align,
|
|
415
|
+
sideOffset: 6,
|
|
416
|
+
collisionPadding: 8,
|
|
417
|
+
className: cn(
|
|
843
418
|
"z-tooltip max-w-[260px] rounded-lg bg-neutral px-3 py-2 shadow-300",
|
|
844
419
|
"text-body-xs text-neutral-on-neutral",
|
|
845
|
-
!open && "sr-only",
|
|
846
420
|
className
|
|
847
421
|
),
|
|
848
|
-
children:
|
|
849
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "font-semibold", children: title }),
|
|
850
|
-
body && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: (0, import_clsx7.default)(title && "mt-0.5 opacity-80"), children: body })
|
|
851
|
-
]
|
|
422
|
+
children: content
|
|
852
423
|
}
|
|
853
424
|
) })
|
|
854
|
-
] });
|
|
425
|
+
] }) });
|
|
855
426
|
}
|
|
856
427
|
|
|
857
428
|
// src/components/overlays/menu-core.tsx
|
|
858
|
-
var
|
|
859
|
-
var
|
|
860
|
-
var
|
|
861
|
-
var
|
|
862
|
-
var
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
429
|
+
var import_react7 = require("react");
|
|
430
|
+
var import_clsx7 = __toESM(require("clsx"), 1);
|
|
431
|
+
var import_lucide_react = require("lucide-react");
|
|
432
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
433
|
+
var MENU_PANEL_CLASSES = cn("z-popover", OVERLAY_PANEL_MENU, "focus:outline-none");
|
|
434
|
+
var ITEM_BASE = (0, import_clsx7.default)(
|
|
435
|
+
OVERLAY_ITEM_BASE,
|
|
436
|
+
OVERLAY_ITEM_SIZE.md,
|
|
437
|
+
// Highlight follows Radix's `data-highlighted`, which tracks pointer and keyboard alike.
|
|
438
|
+
// The ring marks the keyboard position on top of it: the tint alone, shared with hover,
|
|
439
|
+
// cannot tell a keyboard user which of two tinted items has focus.
|
|
440
|
+
FOCUS_RING_INSET,
|
|
441
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:text-disabled"
|
|
442
|
+
);
|
|
443
|
+
var ITEM_TONE = {
|
|
444
|
+
default: "text-default data-[highlighted]:bg-default-secondary",
|
|
445
|
+
danger: "text-danger data-[highlighted]:bg-danger-tertiary"
|
|
446
|
+
};
|
|
447
|
+
function stopToAncestors(event) {
|
|
448
|
+
event.stopPropagation();
|
|
869
449
|
}
|
|
870
|
-
function
|
|
871
|
-
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
typeahead: true,
|
|
878
|
-
onEscape: () => setOpen(false)
|
|
450
|
+
function focusMenuEdge(event, edge) {
|
|
451
|
+
if (event.target !== event.currentTarget) return;
|
|
452
|
+
const panel = event.currentTarget;
|
|
453
|
+
queueMicrotask(() => {
|
|
454
|
+
const items = panel.querySelectorAll('[role^="menuitem"]:not([data-disabled])');
|
|
455
|
+
if (edge === "end") items[items.length - 1]?.focus();
|
|
456
|
+
else if (document.activeElement === panel) items[0]?.focus();
|
|
879
457
|
});
|
|
880
|
-
const claimIndex = (0, import_react12.useCallback)(() => cursor.current++, []);
|
|
881
|
-
const resetIndices = (0, import_react12.useCallback)(() => {
|
|
882
|
-
cursor.current = 0;
|
|
883
|
-
}, []);
|
|
884
|
-
return (0, import_react12.useMemo)(
|
|
885
|
-
() => ({
|
|
886
|
-
open,
|
|
887
|
-
setOpen,
|
|
888
|
-
menuId: `${baseId}-menu`,
|
|
889
|
-
triggerId: `${baseId}-trigger`,
|
|
890
|
-
claimIndex,
|
|
891
|
-
resetIndices,
|
|
892
|
-
pendingEdge,
|
|
893
|
-
roving
|
|
894
|
-
}),
|
|
895
|
-
[open, setOpen, baseId, claimIndex, resetIndices, roving]
|
|
896
|
-
);
|
|
897
458
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
459
|
+
function createMenuParts(P) {
|
|
460
|
+
const Item = (0, import_react7.forwardRef)(function MenuItem({ children, onSelect, tone = "default", disabled = false, icon, description, shortcut, textValue, className, ...rest }, ref) {
|
|
461
|
+
const descriptionId = (0, import_react7.useId)();
|
|
462
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
463
|
+
P.Item,
|
|
464
|
+
{
|
|
465
|
+
ref,
|
|
466
|
+
disabled,
|
|
467
|
+
textValue,
|
|
468
|
+
onSelect,
|
|
469
|
+
"data-tone": tone,
|
|
470
|
+
"aria-describedby": description ? descriptionId : void 0,
|
|
471
|
+
className: cn(ITEM_BASE, ITEM_TONE[tone], className),
|
|
472
|
+
...rest,
|
|
473
|
+
children: [
|
|
474
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: icon }),
|
|
475
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "min-w-0 flex-1", children: [
|
|
476
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block truncate", children }),
|
|
477
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { id: descriptionId, className: "mt-0.5 block text-body-tiny text-default-secondary", children: description })
|
|
478
|
+
] }),
|
|
479
|
+
shortcut && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ml-auto shrink-0 pl-4 text-body-tiny text-default-tertiary", children: shortcut })
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
);
|
|
483
|
+
});
|
|
484
|
+
const CheckboxItem = (0, import_react7.forwardRef)(function MenuCheckboxItem({ children, checked, onCheckedChange, disabled = false, tone = "default", shortcut, textValue, className, ...rest }, ref) {
|
|
485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
486
|
+
P.CheckboxItem,
|
|
487
|
+
{
|
|
488
|
+
ref,
|
|
489
|
+
checked,
|
|
490
|
+
onCheckedChange: (next) => onCheckedChange?.(next === true),
|
|
491
|
+
onSelect: (event) => event.preventDefault(),
|
|
492
|
+
disabled,
|
|
493
|
+
textValue,
|
|
494
|
+
"data-tone": tone,
|
|
495
|
+
className: cn(ITEM_BASE, "items-center", ITEM_TONE[tone], className),
|
|
496
|
+
...rest,
|
|
497
|
+
children: [
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, className: "flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react.CheckIcon, { className: "size-3.5", strokeWidth: 3 }) }) }),
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "min-w-0 flex-1 truncate", children }),
|
|
500
|
+
shortcut && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ml-auto shrink-0 pl-4 text-body-tiny text-default-tertiary", children: shortcut })
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
});
|
|
505
|
+
function RadioGroup({ children, value, onValueChange }) {
|
|
506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.RadioGroup, { value, onValueChange, children });
|
|
507
|
+
}
|
|
508
|
+
const RadioItem = (0, import_react7.forwardRef)(function MenuRadioItem({ children, value, disabled = false, tone = "default", textValue, className, ...rest }, ref) {
|
|
509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
510
|
+
P.RadioItem,
|
|
511
|
+
{
|
|
512
|
+
ref,
|
|
513
|
+
value,
|
|
514
|
+
disabled,
|
|
515
|
+
textValue,
|
|
516
|
+
"data-tone": tone,
|
|
517
|
+
className: cn(ITEM_BASE, "items-center", ITEM_TONE[tone], className),
|
|
518
|
+
...rest,
|
|
519
|
+
children: [
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, className: "flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block size-1.5 rounded-full bg-current" }) }) }),
|
|
521
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "min-w-0 flex-1 truncate", children })
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
const Group = (0, import_react7.forwardRef)(function MenuGroup({ children, label, className, ...rest }, ref) {
|
|
527
|
+
const labelId = (0, import_react7.useId)();
|
|
528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(P.Group, { ref, "aria-labelledby": label ? labelId : void 0, className, ...rest, children: [
|
|
529
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { id: labelId, className: OVERLAY_HEADING, children: label }),
|
|
530
|
+
children
|
|
531
|
+
] });
|
|
532
|
+
});
|
|
533
|
+
const Label = (0, import_react7.forwardRef)(function MenuLabel({ className, ...rest }, ref) {
|
|
534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.Label, { ref, className: cn(OVERLAY_HEADING, className), ...rest });
|
|
535
|
+
});
|
|
536
|
+
const Separator = (0, import_react7.forwardRef)(function MenuSeparator({ className, ...rest }, ref) {
|
|
537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.Separator, { ref, className: cn(OVERLAY_SEPARATOR, className), ...rest });
|
|
538
|
+
});
|
|
539
|
+
const Shortcut = (0, import_react7.forwardRef)(function MenuShortcut({ className, ...rest }, ref) {
|
|
540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref, className: cn("ml-auto pl-4 text-body-tiny text-default-tertiary", className), ...rest });
|
|
541
|
+
});
|
|
542
|
+
function Sub({ children, open, defaultOpen, onOpenChange }) {
|
|
543
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.Sub, { open, defaultOpen, onOpenChange, children });
|
|
544
|
+
}
|
|
545
|
+
const SubTrigger = (0, import_react7.forwardRef)(function MenuSubTrigger({ children, icon, disabled = false, textValue, className, ...rest }, ref) {
|
|
546
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
547
|
+
P.SubTrigger,
|
|
548
|
+
{
|
|
549
|
+
ref,
|
|
550
|
+
disabled,
|
|
551
|
+
textValue,
|
|
552
|
+
className: cn(
|
|
553
|
+
ITEM_BASE,
|
|
554
|
+
"items-center",
|
|
555
|
+
ITEM_TONE.default,
|
|
556
|
+
"data-[state=open]:bg-default-secondary",
|
|
557
|
+
className
|
|
558
|
+
),
|
|
559
|
+
...rest,
|
|
560
|
+
children: [
|
|
561
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, className: "flex size-4 shrink-0 items-center justify-center", children: icon }),
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "min-w-0 flex-1 truncate", children }),
|
|
563
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react.ChevronRightIcon, { "aria-hidden": true, className: "ml-auto size-4 shrink-0 text-default-tertiary" })
|
|
564
|
+
]
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
});
|
|
568
|
+
const SubContent = (0, import_react7.forwardRef)(function MenuSubContent({ className, onClick, onKeyDown, ...rest }, ref) {
|
|
569
|
+
const container = usePortalContainer();
|
|
570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(P.Portal, { container: container ?? void 0, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
571
|
+
P.SubContent,
|
|
572
|
+
{
|
|
573
|
+
ref,
|
|
574
|
+
sideOffset: 4,
|
|
575
|
+
collisionPadding: 8,
|
|
576
|
+
className: cn(MENU_PANEL_CLASSES, className),
|
|
577
|
+
onClick: (event) => {
|
|
578
|
+
onClick?.(event);
|
|
579
|
+
stopToAncestors(event);
|
|
580
|
+
},
|
|
581
|
+
onKeyDown: (event) => {
|
|
582
|
+
onKeyDown?.(event);
|
|
583
|
+
stopToAncestors(event);
|
|
584
|
+
},
|
|
585
|
+
...rest
|
|
586
|
+
}
|
|
587
|
+
) });
|
|
588
|
+
});
|
|
589
|
+
return {
|
|
590
|
+
Item,
|
|
591
|
+
CheckboxItem,
|
|
592
|
+
RadioGroup,
|
|
593
|
+
RadioItem,
|
|
594
|
+
Group,
|
|
595
|
+
Label,
|
|
596
|
+
Separator,
|
|
597
|
+
Shortcut,
|
|
598
|
+
Sub,
|
|
599
|
+
SubTrigger,
|
|
600
|
+
SubContent
|
|
922
601
|
};
|
|
923
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
924
|
-
"div",
|
|
925
|
-
{
|
|
926
|
-
role: "menuitem",
|
|
927
|
-
"aria-disabled": disabled || void 0,
|
|
928
|
-
"aria-describedby": description ? descriptionId : void 0,
|
|
929
|
-
...itemProps,
|
|
930
|
-
tabIndex: itemProps.tabIndex,
|
|
931
|
-
className: (0, import_clsx8.default)(ITEM_BASE, itemToneClasses(tone, disabled), className),
|
|
932
|
-
onClick: (event) => {
|
|
933
|
-
event.stopPropagation();
|
|
934
|
-
activate();
|
|
935
|
-
},
|
|
936
|
-
onKeyDown: (event) => {
|
|
937
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
938
|
-
event.preventDefault();
|
|
939
|
-
activate();
|
|
940
|
-
}
|
|
941
|
-
},
|
|
942
|
-
...rest,
|
|
943
|
-
children: [
|
|
944
|
-
icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": true, className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: icon }),
|
|
945
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "min-w-0 flex-1", children: [
|
|
946
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "block truncate", children }),
|
|
947
|
-
description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { id: descriptionId, className: "mt-0.5 block text-body-tiny text-default-secondary", children: description })
|
|
948
|
-
] }),
|
|
949
|
-
shortcut && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ml-auto shrink-0 pl-4 text-body-tiny text-default-tertiary", children: shortcut })
|
|
950
|
-
]
|
|
951
|
-
}
|
|
952
|
-
);
|
|
953
602
|
}
|
|
954
|
-
var MenuRadioContext = (0, import_react12.createContext)(null);
|
|
955
603
|
|
|
956
604
|
// src/components/cards/scroll-area.tsx
|
|
957
|
-
var
|
|
958
|
-
var
|
|
959
|
-
var
|
|
605
|
+
var import_react8 = require("react");
|
|
606
|
+
var import_clsx8 = __toESM(require("clsx"), 1);
|
|
607
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
960
608
|
var OVERFLOW = {
|
|
961
609
|
vertical: "overflow-y-auto overflow-x-hidden",
|
|
962
610
|
horizontal: "overflow-x-auto overflow-y-hidden",
|
|
963
611
|
both: "overflow-auto"
|
|
964
612
|
};
|
|
965
|
-
var ScrollArea = (0,
|
|
966
|
-
const localRef = (0,
|
|
967
|
-
const [overflows, setOverflows] = (0,
|
|
968
|
-
(0,
|
|
613
|
+
var ScrollArea = (0, import_react8.forwardRef)(function ScrollArea2({ children, axis = "vertical", label, className, ...rest }, ref) {
|
|
614
|
+
const localRef = (0, import_react8.useRef)(null);
|
|
615
|
+
const [overflows, setOverflows] = (0, import_react8.useState)(false);
|
|
616
|
+
(0, import_react8.useEffect)(() => {
|
|
969
617
|
const node = localRef.current;
|
|
970
618
|
if (!node) return;
|
|
971
619
|
const measure = () => {
|
|
@@ -982,7 +630,7 @@ var ScrollArea = (0, import_react13.forwardRef)(function ScrollArea2({ children,
|
|
|
982
630
|
if (node.firstElementChild) observer.observe(node.firstElementChild);
|
|
983
631
|
return () => observer.disconnect();
|
|
984
632
|
}, [axis, children]);
|
|
985
|
-
return /* @__PURE__ */ (0,
|
|
633
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
986
634
|
"div",
|
|
987
635
|
{
|
|
988
636
|
ref: composeRefs(localRef, ref),
|
|
@@ -990,7 +638,7 @@ var ScrollArea = (0, import_react13.forwardRef)(function ScrollArea2({ children,
|
|
|
990
638
|
"aria-label": overflows ? label : void 0,
|
|
991
639
|
tabIndex: overflows ? 0 : void 0,
|
|
992
640
|
"data-slot": "scroll-area",
|
|
993
|
-
className: (0,
|
|
641
|
+
className: (0, import_clsx8.default)(
|
|
994
642
|
OVERFLOW[axis],
|
|
995
643
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-1 focus-visible:ring-offset-default",
|
|
996
644
|
SCROLLBAR_CLASSES,
|
|
@@ -1003,151 +651,130 @@ var ScrollArea = (0, import_react13.forwardRef)(function ScrollArea2({ children,
|
|
|
1003
651
|
});
|
|
1004
652
|
|
|
1005
653
|
// src/components/overlays/dropdown-menu.tsx
|
|
1006
|
-
var
|
|
1007
|
-
var
|
|
1008
|
-
var
|
|
1009
|
-
var
|
|
1010
|
-
var
|
|
1011
|
-
function
|
|
1012
|
-
const value = (0,
|
|
1013
|
-
if (!value) throw new Error(
|
|
654
|
+
var import_react9 = require("react");
|
|
655
|
+
var import_radix_ui2 = require("radix-ui");
|
|
656
|
+
var import_lucide_react2 = require("lucide-react");
|
|
657
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
658
|
+
var DropdownMenuStateContext = (0, import_react9.createContext)(null);
|
|
659
|
+
function useDropdownMenuState(part) {
|
|
660
|
+
const value = (0, import_react9.useContext)(DropdownMenuStateContext);
|
|
661
|
+
if (!value) throw new Error(`<${part}> must be used inside <DropdownMenu>`);
|
|
1014
662
|
return value;
|
|
1015
663
|
}
|
|
1016
664
|
function DropdownMenu({
|
|
1017
665
|
children,
|
|
1018
666
|
open: openProp,
|
|
1019
667
|
defaultOpen = false,
|
|
1020
|
-
onOpenChange
|
|
668
|
+
onOpenChange,
|
|
669
|
+
modal = false
|
|
1021
670
|
}) {
|
|
1022
671
|
const [open, setOpen] = useControllableState({
|
|
1023
672
|
value: openProp,
|
|
1024
673
|
defaultValue: defaultOpen,
|
|
1025
674
|
onChange: onOpenChange
|
|
1026
675
|
});
|
|
1027
|
-
const
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuContext.Provider, { value: menu, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TriggerNodeContext.Provider, { value: anchor, children }) });
|
|
676
|
+
const pendingEdge = (0, import_react9.useRef)("start");
|
|
677
|
+
const state = (0, import_react9.useMemo)(() => ({ setOpen, pendingEdge }), [setOpen]);
|
|
678
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_radix_ui2.DropdownMenu.Root, { open, onOpenChange: setOpen, modal, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuStateContext.Provider, { value: state, children }) });
|
|
1031
679
|
}
|
|
1032
|
-
|
|
1033
|
-
children,
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
event.
|
|
1052
|
-
|
|
1053
|
-
|
|
680
|
+
var DropdownMenuTrigger = (0, import_react9.forwardRef)(
|
|
681
|
+
function DropdownMenuTrigger2({ children, asChild = false, label = "Open menu", onClick, onPointerDown, onMouseDown, onKeyDown, ...rest }, ref) {
|
|
682
|
+
const state = useDropdownMenuState("DropdownMenuTrigger");
|
|
683
|
+
const handlers = {
|
|
684
|
+
onClick: (event) => {
|
|
685
|
+
onClick?.(event);
|
|
686
|
+
stopToAncestors(event);
|
|
687
|
+
},
|
|
688
|
+
onPointerDown: (event) => {
|
|
689
|
+
onPointerDown?.(event);
|
|
690
|
+
event.stopPropagation();
|
|
691
|
+
},
|
|
692
|
+
onMouseDown: (event) => {
|
|
693
|
+
onMouseDown?.(event);
|
|
694
|
+
stopToAncestors(event);
|
|
695
|
+
},
|
|
696
|
+
onKeyDown: (event) => {
|
|
697
|
+
onKeyDown?.(event);
|
|
698
|
+
if (event.defaultPrevented) return;
|
|
699
|
+
if (event.key === "ArrowUp") {
|
|
700
|
+
event.preventDefault();
|
|
701
|
+
state.pendingEdge.current = "end";
|
|
702
|
+
state.setOpen(true);
|
|
703
|
+
}
|
|
704
|
+
if (event.key === "Enter" || event.key === " ") stopToAncestors(event);
|
|
1054
705
|
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
706
|
+
};
|
|
707
|
+
if (asChild) {
|
|
708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_radix_ui2.DropdownMenu.Trigger, { ref, asChild: true, ...rest, ...handlers, children });
|
|
709
|
+
}
|
|
710
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_radix_ui2.DropdownMenu.Trigger, { ref, asChild: true, ...rest, ...handlers, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconButton, { variant: "neutral", size: "sm", "aria-label": label, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.EllipsisIcon, { className: "size-4", "aria-hidden": true }) }) });
|
|
711
|
+
}
|
|
712
|
+
);
|
|
713
|
+
var DropdownMenuContent = (0, import_react9.forwardRef)(
|
|
714
|
+
function DropdownMenuContent2({
|
|
715
|
+
children,
|
|
716
|
+
side = "bottom",
|
|
717
|
+
align = "end",
|
|
718
|
+
sideOffset = 6,
|
|
719
|
+
container,
|
|
720
|
+
className,
|
|
721
|
+
onClick,
|
|
722
|
+
onKeyDown,
|
|
723
|
+
onFocus,
|
|
724
|
+
onCloseAutoFocus,
|
|
1059
725
|
...rest
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
|
|
726
|
+
}, ref) {
|
|
727
|
+
const state = useDropdownMenuState("DropdownMenuContent");
|
|
728
|
+
const inherited = usePortalContainer();
|
|
729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_radix_ui2.DropdownMenu.Portal, { container: container ?? inherited ?? void 0, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
730
|
+
import_radix_ui2.DropdownMenu.Content,
|
|
731
|
+
{
|
|
732
|
+
ref,
|
|
733
|
+
side,
|
|
734
|
+
align,
|
|
735
|
+
sideOffset,
|
|
736
|
+
collisionPadding: 8,
|
|
737
|
+
loop: true,
|
|
738
|
+
className: cn(MENU_PANEL_CLASSES, className),
|
|
739
|
+
onCloseAutoFocus: composeEventHandlers(onCloseAutoFocus, () => {
|
|
740
|
+
state.pendingEdge.current = "start";
|
|
741
|
+
}),
|
|
742
|
+
onFocus: composeEventHandlers(onFocus, (event) => {
|
|
743
|
+
const edge = state.pendingEdge.current;
|
|
744
|
+
state.pendingEdge.current = "start";
|
|
745
|
+
focusMenuEdge(event, edge);
|
|
746
|
+
}),
|
|
747
|
+
onClick: (event) => {
|
|
748
|
+
onClick?.(event);
|
|
749
|
+
stopToAncestors(event);
|
|
750
|
+
},
|
|
751
|
+
onKeyDown: (event) => {
|
|
752
|
+
onKeyDown?.(event);
|
|
753
|
+
stopToAncestors(event);
|
|
754
|
+
if (event.key === "Tab") state.setOpen(false);
|
|
755
|
+
},
|
|
756
|
+
...rest,
|
|
757
|
+
children
|
|
758
|
+
}
|
|
759
|
+
) });
|
|
1063
760
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
children,
|
|
1078
|
-
side = "bottom",
|
|
1079
|
-
align = "end",
|
|
1080
|
-
sideOffset = 6,
|
|
1081
|
-
className,
|
|
1082
|
-
...rest
|
|
1083
|
-
}) {
|
|
1084
|
-
const menu = useMenu("DropdownMenuContent");
|
|
1085
|
-
const anchor = useTriggerNode();
|
|
1086
|
-
const panelRef = (0, import_react14.useRef)(null);
|
|
1087
|
-
const [panelNode, setPanelNode] = (0, import_react14.useState)(null);
|
|
1088
|
-
menu.resetIndices();
|
|
1089
|
-
const { refs, floatingStyles } = useFloatingPanel({
|
|
1090
|
-
open: menu.open,
|
|
1091
|
-
onOpenChange: menu.setOpen,
|
|
1092
|
-
side,
|
|
1093
|
-
align,
|
|
1094
|
-
sideOffset
|
|
1095
|
-
});
|
|
1096
|
-
(0, import_react14.useEffect)(() => {
|
|
1097
|
-
refs.setReference(anchor.triggerNode);
|
|
1098
|
-
}, [refs, anchor.triggerNode]);
|
|
1099
|
-
useOverlayDismiss({
|
|
1100
|
-
onDismiss: () => menu.setOpen(false),
|
|
1101
|
-
panelRef,
|
|
1102
|
-
active: menu.open,
|
|
1103
|
-
// A menu is not modal: the page behind stays readable and interactive, so none of the
|
|
1104
|
-
// dialog-grade side effects apply. Only Escape, outside-press and focus restore.
|
|
1105
|
-
trapFocus: false,
|
|
1106
|
-
lockScroll: false,
|
|
1107
|
-
inertBackground: false,
|
|
1108
|
-
// The menu owns its own initial focus below; a generic "first tabbable element" scan
|
|
1109
|
-
// would fight the roving tabindex, which exposes exactly one item at a time.
|
|
1110
|
-
autoFocus: false
|
|
1111
|
-
});
|
|
1112
|
-
const wasOpen = (0, import_react14.useRef)(false);
|
|
1113
|
-
(0, import_react14.useEffect)(() => {
|
|
1114
|
-
if (menu.open && panelNode && !wasOpen.current) {
|
|
1115
|
-
menu.roving.moveToEdge(menu.pendingEdge.current);
|
|
1116
|
-
menu.pendingEdge.current = "start";
|
|
1117
|
-
wasOpen.current = true;
|
|
1118
|
-
}
|
|
1119
|
-
if (!menu.open) wasOpen.current = false;
|
|
1120
|
-
}, [menu.open, panelNode, menu.roving, menu.pendingEdge]);
|
|
1121
|
-
if (!menu.open) return null;
|
|
1122
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1123
|
-
"div",
|
|
1124
|
-
{
|
|
1125
|
-
ref: composeRefs(
|
|
1126
|
-
panelRef,
|
|
1127
|
-
(node) => setPanelNode(node),
|
|
1128
|
-
refs.setFloating
|
|
1129
|
-
),
|
|
1130
|
-
id: menu.menuId,
|
|
1131
|
-
role: "menu",
|
|
1132
|
-
tabIndex: -1,
|
|
1133
|
-
"aria-orientation": "vertical",
|
|
1134
|
-
"aria-labelledby": menu.triggerId,
|
|
1135
|
-
style: floatingStyles,
|
|
1136
|
-
className: (0, import_clsx10.default)("z-dropdown", MENU_PANEL_CLASSES, className),
|
|
1137
|
-
...menu.roving.containerProps,
|
|
1138
|
-
onKeyDown: (event) => {
|
|
1139
|
-
menu.roving.containerProps.onKeyDown(event);
|
|
1140
|
-
if (event.key === "Tab") menu.setOpen(false);
|
|
1141
|
-
},
|
|
1142
|
-
...rest,
|
|
1143
|
-
children
|
|
1144
|
-
}
|
|
1145
|
-
) });
|
|
1146
|
-
}
|
|
761
|
+
);
|
|
762
|
+
var parts = createMenuParts(import_radix_ui2.DropdownMenu);
|
|
763
|
+
var DropdownMenuItem = parts.Item;
|
|
764
|
+
var DropdownMenuCheckboxItem = parts.CheckboxItem;
|
|
765
|
+
var DropdownMenuRadioGroup = parts.RadioGroup;
|
|
766
|
+
var DropdownMenuRadioItem = parts.RadioItem;
|
|
767
|
+
var DropdownMenuGroup = parts.Group;
|
|
768
|
+
var DropdownMenuLabel = parts.Label;
|
|
769
|
+
var DropdownMenuSeparator = parts.Separator;
|
|
770
|
+
var DropdownMenuShortcut = parts.Shortcut;
|
|
771
|
+
var DropdownMenuSub = parts.Sub;
|
|
772
|
+
var DropdownMenuSubTrigger = parts.SubTrigger;
|
|
773
|
+
var DropdownMenuSubContent = parts.SubContent;
|
|
1147
774
|
|
|
1148
775
|
// src/components/chrome/nav-item.tsx
|
|
1149
|
-
var
|
|
1150
|
-
var
|
|
776
|
+
var import_clsx9 = __toESM(require("clsx"), 1);
|
|
777
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1151
778
|
var SIZE = {
|
|
1152
779
|
sm: { box: "h-9 gap-2 rounded-lg px-3 py-1.5", label: "text-body-xs", square: "w-9" },
|
|
1153
780
|
md: { box: "h-11 gap-3 rounded-xl px-3.5 py-2.5", label: "text-body-sm", square: "w-11" }
|
|
@@ -1174,12 +801,12 @@ var VARIANT = {
|
|
|
1174
801
|
// `border-transparent` holds the 1px so the icon and label do not shift outward when a
|
|
1175
802
|
// row becomes the current one — the same trick, for the same reason, as `button.tsx`.
|
|
1176
803
|
// `bg-brand` still paints edge to edge, since backgrounds clip to the border box.
|
|
1177
|
-
active: (0,
|
|
1178
|
-
inactive: (0,
|
|
804
|
+
active: (0, import_clsx9.default)("border border-transparent", STATE.active),
|
|
805
|
+
inactive: (0, import_clsx9.default)("border border-default bg-default text-default-secondary", STATE.hover)
|
|
1179
806
|
},
|
|
1180
807
|
plain: {
|
|
1181
808
|
active: STATE.active,
|
|
1182
|
-
inactive: (0,
|
|
809
|
+
inactive: (0, import_clsx9.default)("text-default-secondary", STATE.hover)
|
|
1183
810
|
}
|
|
1184
811
|
};
|
|
1185
812
|
function NavItem({
|
|
@@ -1188,14 +815,16 @@ function NavItem({
|
|
|
1188
815
|
isActive = false,
|
|
1189
816
|
indicator = false,
|
|
1190
817
|
collapsed = false,
|
|
1191
|
-
size
|
|
818
|
+
size = "md",
|
|
1192
819
|
variant = "card",
|
|
1193
820
|
asChild = false,
|
|
821
|
+
disabled = false,
|
|
1194
822
|
children,
|
|
1195
823
|
className,
|
|
824
|
+
onClick,
|
|
1196
825
|
...rest
|
|
1197
826
|
}) {
|
|
1198
|
-
const s = SIZE[
|
|
827
|
+
const s = SIZE[size];
|
|
1199
828
|
const v = VARIANT[variant];
|
|
1200
829
|
const rootProps = {
|
|
1201
830
|
// Only meaningful when `asChild` renders a real link — a decorative span with
|
|
@@ -1206,7 +835,9 @@ function NavItem({
|
|
|
1206
835
|
// you *are* (`aria-current="page"`), which is a different thing from a row you have
|
|
1207
836
|
// *picked* (`aria-selected`) — `TableRow` is the latter and says `selected`.
|
|
1208
837
|
"data-state": isActive ? "active" : void 0,
|
|
1209
|
-
|
|
838
|
+
"data-disabled": disabled || void 0,
|
|
839
|
+
"aria-disabled": disabled || void 0,
|
|
840
|
+
className: (0, import_clsx9.default)(
|
|
1210
841
|
"relative flex items-center transition-colors",
|
|
1211
842
|
// Unconditional: a row can also become focusable by the caller putting it inside a
|
|
1212
843
|
// link, which is how `src/sidebar/` uses it. A ring that only appears under `asChild`
|
|
@@ -1219,20 +850,31 @@ function NavItem({
|
|
|
1219
850
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-1 focus-visible:ring-offset-default-secondary",
|
|
1220
851
|
s.box,
|
|
1221
852
|
// Expanded rows fill the nav; a collapsed one is a centred square, not a stretched row.
|
|
1222
|
-
collapsed ? (0,
|
|
853
|
+
collapsed ? (0, import_clsx9.default)("justify-center", s.square) : "w-full",
|
|
1223
854
|
isActive ? v.active : v.inactive,
|
|
855
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-40 data-[disabled]:hover:bg-transparent data-[disabled]:hover:text-default-secondary",
|
|
1224
856
|
className
|
|
1225
857
|
),
|
|
1226
|
-
...rest
|
|
858
|
+
...rest,
|
|
859
|
+
// After the spread: a disabled row refuses the press (a router Link skips navigation when
|
|
860
|
+
// the event is prevented) and leaves the tab order, as a disabled control does.
|
|
861
|
+
...disabled ? { tabIndex: -1 } : {},
|
|
862
|
+
onClick: (event) => {
|
|
863
|
+
if (disabled) {
|
|
864
|
+
event.preventDefault();
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
onClick?.(event);
|
|
868
|
+
}
|
|
1227
869
|
};
|
|
1228
|
-
const content = /* @__PURE__ */ (0,
|
|
1229
|
-
icon != null ? /* @__PURE__ */ (0,
|
|
870
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
871
|
+
icon != null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": true, className: "shrink-0 [&_svg]:size-4", children: icon }) : (
|
|
1230
872
|
// Icons are optional, but the collapsed rail hides the label — so a row with neither
|
|
1231
873
|
// renders as an empty box. Through 0.5.0 that is exactly what a nav without icons
|
|
1232
874
|
// collapsed to, the docs site's own 36-row sidebar included. The monogram is
|
|
1233
875
|
// decorative in the same way the icon is: the `sr-only` label below still names the
|
|
1234
876
|
// row.
|
|
1235
|
-
collapsed && /* @__PURE__ */ (0,
|
|
877
|
+
collapsed && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1236
878
|
"span",
|
|
1237
879
|
{
|
|
1238
880
|
"aria-hidden": true,
|
|
@@ -1241,10 +883,10 @@ function NavItem({
|
|
|
1241
883
|
}
|
|
1242
884
|
)
|
|
1243
885
|
),
|
|
1244
|
-
/* @__PURE__ */ (0,
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1245
887
|
"span",
|
|
1246
888
|
{
|
|
1247
|
-
className: (0,
|
|
889
|
+
className: (0, import_clsx9.default)(
|
|
1248
890
|
"min-w-0 truncate font-regular leading-snug",
|
|
1249
891
|
s.label,
|
|
1250
892
|
collapsed ? "sr-only" : "flex-1"
|
|
@@ -1252,7 +894,7 @@ function NavItem({
|
|
|
1252
894
|
children: label
|
|
1253
895
|
}
|
|
1254
896
|
),
|
|
1255
|
-
indicator && /* @__PURE__ */ (0,
|
|
897
|
+
indicator && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1256
898
|
"span",
|
|
1257
899
|
{
|
|
1258
900
|
role: "status",
|
|
@@ -1262,32 +904,35 @@ function NavItem({
|
|
|
1262
904
|
)
|
|
1263
905
|
] });
|
|
1264
906
|
if (asChild) {
|
|
1265
|
-
return /* @__PURE__ */ (0,
|
|
907
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slot, { wrapChildren: () => content, ...rootProps, children });
|
|
1266
908
|
}
|
|
1267
|
-
return /* @__PURE__ */ (0,
|
|
909
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { ...rootProps, children: content });
|
|
1268
910
|
}
|
|
1269
911
|
|
|
912
|
+
// src/index.tsx
|
|
913
|
+
var import_react_table = require("@tanstack/react-table");
|
|
914
|
+
|
|
1270
915
|
// src/sidebar/nav-tabs.tsx
|
|
1271
916
|
var import_react_router_dom = require("react-router-dom");
|
|
1272
|
-
var
|
|
1273
|
-
var
|
|
917
|
+
var import_clsx10 = __toESM(require("clsx"), 1);
|
|
918
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1274
919
|
function NavTabs({ items, className }) {
|
|
1275
920
|
return (
|
|
1276
921
|
// The links inside are the tab stops.
|
|
1277
|
-
/* @__PURE__ */ (0,
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1278
923
|
"div",
|
|
1279
924
|
{
|
|
1280
|
-
className: (0,
|
|
925
|
+
className: (0, import_clsx10.default)(
|
|
1281
926
|
"flex gap-6 overflow-x-auto border-b border-default",
|
|
1282
927
|
SCROLLBAR_CLASSES,
|
|
1283
928
|
className
|
|
1284
929
|
),
|
|
1285
|
-
children: items.map((tab) => /* @__PURE__ */ (0,
|
|
930
|
+
children: items.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1286
931
|
import_react_router_dom.NavLink,
|
|
1287
932
|
{
|
|
1288
933
|
to: tab.to,
|
|
1289
934
|
end: tab.end,
|
|
1290
|
-
className: ({ isActive }) => (0,
|
|
935
|
+
className: ({ isActive }) => (0, import_clsx10.default)(
|
|
1291
936
|
"shrink-0 whitespace-nowrap border-b-2 pb-3 text-body-sm transition-colors",
|
|
1292
937
|
isActive ? "border-brand-emphasis font-semibold text-brand-emphasis" : "border-transparent font-medium text-default-secondary hover:text-default"
|
|
1293
938
|
),
|
|
@@ -1301,7 +946,7 @@ function NavTabs({ items, className }) {
|
|
|
1301
946
|
}
|
|
1302
947
|
|
|
1303
948
|
// src/sidebar/index.tsx
|
|
1304
|
-
var
|
|
949
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1305
950
|
function AppSidebar({
|
|
1306
951
|
groups,
|
|
1307
952
|
footerNav,
|
|
@@ -1310,14 +955,20 @@ function AppSidebar({
|
|
|
1310
955
|
collapsible = true,
|
|
1311
956
|
collapsed: collapsedProp = false,
|
|
1312
957
|
onToggle,
|
|
1313
|
-
size
|
|
958
|
+
size = "md",
|
|
1314
959
|
variant = "card",
|
|
1315
960
|
width = "w-80",
|
|
1316
961
|
collapsedWidth = "w-20",
|
|
1317
962
|
className
|
|
1318
963
|
}) {
|
|
1319
964
|
const collapsed = collapsible && collapsedProp;
|
|
1320
|
-
|
|
965
|
+
const hashTargets = (0, import_react10.useMemo)(
|
|
966
|
+
() => new Set(
|
|
967
|
+
[...groups.flatMap((group) => group.items), ...footerNav ?? []].map((item) => item.to).filter((to) => to.includes("#"))
|
|
968
|
+
),
|
|
969
|
+
[groups, footerNav]
|
|
970
|
+
);
|
|
971
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HashTargets.Provider, { value: hashTargets, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1321
972
|
"aside",
|
|
1322
973
|
{
|
|
1323
974
|
className: cn(
|
|
@@ -1327,7 +978,7 @@ function AppSidebar({
|
|
|
1327
978
|
className
|
|
1328
979
|
),
|
|
1329
980
|
children: [
|
|
1330
|
-
collapsible && onToggle && /* @__PURE__ */ (0,
|
|
981
|
+
collapsible && onToggle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1331
982
|
"button",
|
|
1332
983
|
{
|
|
1333
984
|
type: "button",
|
|
@@ -1343,10 +994,10 @@ function AppSidebar({
|
|
|
1343
994
|
"shadow-100 transition-colors hover:bg-brand-tertiary-hover hover:text-brand",
|
|
1344
995
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-1 focus-visible:ring-offset-default"
|
|
1345
996
|
),
|
|
1346
|
-
children: collapsed ? /* @__PURE__ */ (0,
|
|
997
|
+
children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.ChevronRightIcon, { className: "size-3", strokeWidth: 2.5, "aria-hidden": true }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.ChevronLeftIcon, { className: "size-3", strokeWidth: 2.5, "aria-hidden": true })
|
|
1347
998
|
}
|
|
1348
999
|
),
|
|
1349
|
-
brand && /* @__PURE__ */ (0,
|
|
1000
|
+
brand && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1350
1001
|
"div",
|
|
1351
1002
|
{
|
|
1352
1003
|
className: cn(
|
|
@@ -1356,24 +1007,24 @@ function AppSidebar({
|
|
|
1356
1007
|
children: typeof brand === "function" ? brand({ collapsed }) : brand
|
|
1357
1008
|
}
|
|
1358
1009
|
),
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollArea, { axis: "vertical", label: "Sidebar navigation", className: "min-h-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("nav", { className: "flex flex-col gap-4 px-4 pb-3 pt-2", children: groups.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1360
1011
|
SidebarGroup,
|
|
1361
1012
|
{
|
|
1362
1013
|
group,
|
|
1363
1014
|
showSeparator: groupIndex > 0,
|
|
1364
1015
|
collapsed,
|
|
1365
|
-
size
|
|
1016
|
+
size,
|
|
1366
1017
|
variant
|
|
1367
1018
|
},
|
|
1368
1019
|
group.title ?? `group-${groupIndex}`
|
|
1369
1020
|
)) }) }),
|
|
1370
|
-
(footerNav?.length || footer) && /* @__PURE__ */ (0,
|
|
1371
|
-
footerNav && footerNav.length > 0 && /* @__PURE__ */ (0,
|
|
1021
|
+
(footerNav?.length || footer) && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex shrink-0 flex-col", children: [
|
|
1022
|
+
footerNav && footerNav.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("nav", { className: "flex flex-col gap-3 px-4 pt-2", children: footerNav.map((item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1372
1023
|
SidebarLink,
|
|
1373
1024
|
{
|
|
1374
1025
|
item,
|
|
1375
1026
|
collapsed,
|
|
1376
|
-
size
|
|
1027
|
+
size,
|
|
1377
1028
|
variant
|
|
1378
1029
|
},
|
|
1379
1030
|
item.to
|
|
@@ -1382,22 +1033,22 @@ function AppSidebar({
|
|
|
1382
1033
|
] })
|
|
1383
1034
|
]
|
|
1384
1035
|
}
|
|
1385
|
-
);
|
|
1036
|
+
) });
|
|
1386
1037
|
}
|
|
1387
1038
|
function SidebarGroup({
|
|
1388
1039
|
group,
|
|
1389
1040
|
showSeparator,
|
|
1390
1041
|
collapsed,
|
|
1391
|
-
size
|
|
1042
|
+
size,
|
|
1392
1043
|
variant
|
|
1393
1044
|
}) {
|
|
1394
1045
|
return (
|
|
1395
1046
|
// 12px between rows, per the design — the rows read as separate pills rather than a
|
|
1396
1047
|
// stack. Through 0.5.0 this was `gap-1`, which packed them into a near-continuous block.
|
|
1397
1048
|
// The same gap spaces the separator and the heading, so neither needs a margin of its own.
|
|
1398
|
-
/* @__PURE__ */ (0,
|
|
1399
|
-
showSeparator && /* @__PURE__ */ (0,
|
|
1400
|
-
group.title && /* @__PURE__ */ (0,
|
|
1049
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
1050
|
+
showSeparator && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SidebarSeparator, {}),
|
|
1051
|
+
group.title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1401
1052
|
"p",
|
|
1402
1053
|
{
|
|
1403
1054
|
className: cn(
|
|
@@ -1410,12 +1061,12 @@ function SidebarGroup({
|
|
|
1410
1061
|
children: group.title
|
|
1411
1062
|
}
|
|
1412
1063
|
),
|
|
1413
|
-
group.items.map((item) => /* @__PURE__ */ (0,
|
|
1064
|
+
group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1414
1065
|
SidebarLink,
|
|
1415
1066
|
{
|
|
1416
1067
|
item,
|
|
1417
1068
|
collapsed,
|
|
1418
|
-
size
|
|
1069
|
+
size,
|
|
1419
1070
|
variant
|
|
1420
1071
|
},
|
|
1421
1072
|
item.to
|
|
@@ -1423,37 +1074,52 @@ function SidebarGroup({
|
|
|
1423
1074
|
] })
|
|
1424
1075
|
);
|
|
1425
1076
|
}
|
|
1077
|
+
var HashTargets = (0, import_react10.createContext)(/* @__PURE__ */ new Set());
|
|
1078
|
+
function useRowActive(item) {
|
|
1079
|
+
const location = (0, import_react_router_dom2.useLocation)();
|
|
1080
|
+
const hashTargets = (0, import_react10.useContext)(HashTargets);
|
|
1081
|
+
const [path, hash] = item.to.split("#");
|
|
1082
|
+
const pathMatches = item.end ? location.pathname === path : location.pathname === path || location.pathname.startsWith(`${path.replace(/\/$/, "")}/`);
|
|
1083
|
+
if (!pathMatches) return false;
|
|
1084
|
+
if (hash !== void 0) return location.hash === `#${hash}`;
|
|
1085
|
+
return !hashTargets.has(`${location.pathname}${location.hash}`);
|
|
1086
|
+
}
|
|
1426
1087
|
function SidebarLink({
|
|
1427
1088
|
item,
|
|
1428
1089
|
collapsed,
|
|
1429
|
-
size
|
|
1090
|
+
size,
|
|
1430
1091
|
variant
|
|
1431
1092
|
}) {
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1093
|
+
const active = useRowActive(item);
|
|
1094
|
+
const link = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1095
|
+
import_react_router_dom2.Link,
|
|
1434
1096
|
{
|
|
1435
1097
|
to: item.to,
|
|
1436
|
-
|
|
1098
|
+
"aria-current": active ? "page" : void 0,
|
|
1099
|
+
"aria-disabled": item.disabled || void 0,
|
|
1100
|
+
tabIndex: item.disabled ? -1 : void 0,
|
|
1101
|
+
onClick: item.disabled ? (event) => event.preventDefault() : void 0,
|
|
1437
1102
|
className: cn("block w-full no-underline", collapsed && "flex justify-center"),
|
|
1438
|
-
children:
|
|
1103
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1439
1104
|
NavItem,
|
|
1440
1105
|
{
|
|
1441
1106
|
icon: item.icon,
|
|
1442
1107
|
label: item.label,
|
|
1443
|
-
isActive,
|
|
1108
|
+
isActive: active,
|
|
1444
1109
|
indicator: item.indicator,
|
|
1110
|
+
disabled: item.disabled,
|
|
1445
1111
|
collapsed,
|
|
1446
|
-
size
|
|
1112
|
+
size,
|
|
1447
1113
|
variant
|
|
1448
1114
|
}
|
|
1449
1115
|
)
|
|
1450
1116
|
}
|
|
1451
1117
|
);
|
|
1452
1118
|
if (!collapsed) return link;
|
|
1453
|
-
return /* @__PURE__ */ (0,
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Tooltip, { title: item.label, side: "right", children: link });
|
|
1454
1120
|
}
|
|
1455
1121
|
function SidebarSeparator({ className, ...rest }) {
|
|
1456
|
-
return /* @__PURE__ */ (0,
|
|
1122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { "aria-hidden": true, className: cn("mx-1 h-px shrink-0 bg-divider", className), ...rest });
|
|
1457
1123
|
}
|
|
1458
1124
|
function SidebarUserCard({
|
|
1459
1125
|
name,
|
|
@@ -1464,40 +1130,40 @@ function SidebarUserCard({
|
|
|
1464
1130
|
className
|
|
1465
1131
|
}) {
|
|
1466
1132
|
const initial = name.trim().charAt(0);
|
|
1467
|
-
const items = /* @__PURE__ */ (0,
|
|
1133
|
+
const items = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1468
1134
|
menu,
|
|
1469
|
-
/* @__PURE__ */ (0,
|
|
1470
|
-
|
|
1135
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1136
|
+
DropdownMenuItem,
|
|
1471
1137
|
{
|
|
1472
|
-
icon: /* @__PURE__ */ (0,
|
|
1138
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.LogOutIcon, { className: "size-3.5", "aria-hidden": true }),
|
|
1473
1139
|
onSelect: onLogout,
|
|
1474
1140
|
children: "Sign out"
|
|
1475
1141
|
}
|
|
1476
1142
|
)
|
|
1477
1143
|
] });
|
|
1478
1144
|
if (collapsed) {
|
|
1479
|
-
return /* @__PURE__ */ (0,
|
|
1480
|
-
/* @__PURE__ */ (0,
|
|
1481
|
-
/* @__PURE__ */ (0,
|
|
1145
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex shrink-0 items-center justify-center p-4", className), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(DropdownMenu, { children: [
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DropdownMenuTrigger, { asChild: true, label: "User options", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-label": `${name} \u2014 user options`, className: "rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Avatar, { initial, size: "lg", shape: "circle" }) }) }),
|
|
1147
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DropdownMenuContent, { side: "right", align: "end", children: items })
|
|
1482
1148
|
] }) });
|
|
1483
1149
|
}
|
|
1484
|
-
return /* @__PURE__ */ (0,
|
|
1485
|
-
/* @__PURE__ */ (0,
|
|
1486
|
-
/* @__PURE__ */ (0,
|
|
1487
|
-
/* @__PURE__ */ (0,
|
|
1488
|
-
/* @__PURE__ */ (0,
|
|
1150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: cn("flex shrink-0 items-center gap-2.5 p-4", className), children: [
|
|
1151
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Avatar, { initial, size: "lg", shape: "circle" }),
|
|
1152
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
1153
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "truncate text-body-sm font-semibold leading-snug text-default", children: name }),
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "truncate text-body-tiny font-regular leading-none text-default-secondary", children: role })
|
|
1489
1155
|
] }),
|
|
1490
|
-
/* @__PURE__ */ (0,
|
|
1491
|
-
/* @__PURE__ */ (0,
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(DropdownMenu, { children: [
|
|
1157
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DropdownMenuTrigger, { asChild: true, label: "User options", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1492
1158
|
"button",
|
|
1493
1159
|
{
|
|
1494
1160
|
type: "button",
|
|
1495
1161
|
"aria-label": `${name} \u2014 user options`,
|
|
1496
1162
|
className: "flex size-6 shrink-0 items-center justify-center rounded-md text-default-secondary transition-colors hover:bg-brand-tertiary-hover hover:text-brand",
|
|
1497
|
-
children: /* @__PURE__ */ (0,
|
|
1163
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.EllipsisVerticalIcon, { className: "size-5", "aria-hidden": true })
|
|
1498
1164
|
}
|
|
1499
1165
|
) }),
|
|
1500
|
-
/* @__PURE__ */ (0,
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DropdownMenuContent, { side: "top", align: "end", children: items })
|
|
1501
1167
|
] })
|
|
1502
1168
|
] });
|
|
1503
1169
|
}
|