@evinvest/uikit 0.30.0 → 0.31.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 +266 -72
- package/dist/{chunk-3IPB4QQJ.js → chunk-35SPIU4U.js} +10 -6
- package/dist/chunk-35SPIU4U.js.map +1 -0
- package/dist/components/account-list.js +128 -0
- package/dist/components/account-list.js.map +1 -0
- package/dist/components/account-menu-profile.js +114 -0
- package/dist/components/account-menu-profile.js.map +1 -0
- package/dist/components/account-menu.js +50 -23
- package/dist/components/account-menu.js.map +1 -1
- package/dist/components/account-settings-dialog.js +84 -0
- package/dist/components/account-settings-dialog.js.map +1 -0
- package/dist/components/alert-dialog.js +3 -2
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/animated-number-columns.js +105 -0
- package/dist/components/animated-number-columns.js.map +1 -0
- package/dist/components/animated-number.js +157 -0
- package/dist/components/animated-number.js.map +1 -0
- package/dist/components/app-shell.js +9 -0
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/aside.js +140 -0
- package/dist/components/aside.js.map +1 -0
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.js +3 -11
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-composer.js +141 -0
- package/dist/components/chat-composer.js.map +1 -0
- package/dist/components/chat-message.js +195 -0
- package/dist/components/chat-message.js.map +1 -0
- package/dist/components/chat-panel.js +137 -0
- package/dist/components/chat-panel.js.map +1 -0
- package/dist/components/chat-stream-text.js +69 -0
- package/dist/components/chat-stream-text.js.map +1 -0
- package/dist/components/chat-typing.js +26 -0
- package/dist/components/chat-typing.js.map +1 -0
- package/dist/components/command-palette.js +119 -0
- package/dist/components/command-palette.js.map +1 -0
- package/dist/components/command.js +3 -1
- package/dist/components/command.js.map +1 -1
- package/dist/components/confirm-dialog.js +123 -0
- package/dist/components/confirm-dialog.js.map +1 -0
- package/dist/components/context-menu.js +53 -5
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +3 -2
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.js +3 -1
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/dropdown-menu.js +64 -5
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/form-row.js +51 -0
- package/dist/components/form-row.js.map +1 -0
- package/dist/components/header-controls.js +149 -0
- package/dist/components/header-controls.js.map +1 -0
- package/dist/components/icon-button.js +35 -0
- package/dist/components/icon-button.js.map +1 -0
- package/dist/components/key-value.js +68 -0
- package/dist/components/key-value.js.map +1 -0
- package/dist/components/menubar.js +39 -5
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/native-select.js +3 -4
- package/dist/components/native-select.js.map +1 -1
- package/dist/components/nav-collapse.js +142 -0
- package/dist/components/nav-collapse.js.map +1 -0
- package/dist/components/nav-model.js.map +1 -1
- package/dist/components/navigation-menu.js +4 -5
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/page-frame.js +2 -5
- package/dist/components/page-frame.js.map +1 -1
- package/dist/components/popover-form.js +60 -0
- package/dist/components/popover-form.js.map +1 -0
- package/dist/components/resource-error.js +1 -1
- package/dist/components/resource-error.js.map +1 -1
- package/dist/components/section-nav.js +73 -20
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/select.js +16 -6
- package/dist/components/select.js.map +1 -1
- package/dist/components/settings.js +94 -0
- package/dist/components/settings.js.map +1 -0
- package/dist/components/sheet.js +3 -2
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/shell-nav.js +80 -38
- package/dist/components/shell-nav.js.map +1 -1
- package/dist/components/smart-header.js +53 -0
- package/dist/components/smart-header.js.map +1 -0
- package/dist/components/status-item.js +42 -0
- package/dist/components/status-item.js.map +1 -0
- package/dist/components/system-banner.js +22 -12
- package/dist/components/system-banner.js.map +1 -1
- package/dist/components/top-bar.js +4 -3
- package/dist/components/top-bar.js.map +1 -1
- package/dist/components/ui-icons.js +77 -0
- package/dist/components/ui-icons.js.map +1 -0
- package/dist/components/use-count-tween.js +42 -0
- package/dist/components/use-count-tween.js.map +1 -0
- package/dist/components/workspace-tab.js +140 -0
- package/dist/components/workspace-tab.js.map +1 -0
- package/dist/generated/accent.js +2 -2
- package/dist/generated/accent.js.map +1 -1
- package/dist/generated/account-list.js +35 -0
- package/dist/generated/account-list.js.map +1 -0
- package/dist/generated/account-settings-dialog.js +27 -0
- package/dist/generated/account-settings-dialog.js.map +1 -0
- package/dist/generated/alert-dialog.js +1 -1
- package/dist/generated/alert-dialog.js.map +1 -1
- package/dist/generated/alert.js +2 -2
- package/dist/generated/alert.js.map +1 -1
- package/dist/generated/aside.js +36 -0
- package/dist/generated/aside.js.map +1 -0
- package/dist/generated/badge.js +7 -6
- package/dist/generated/badge.js.map +1 -1
- package/dist/generated/band.js +1 -1
- package/dist/generated/band.js.map +1 -1
- package/dist/generated/breadcrumb.js +2 -2
- package/dist/generated/breadcrumb.js.map +1 -1
- package/dist/generated/button-group.js +2 -2
- package/dist/generated/button-group.js.map +1 -1
- package/dist/generated/button.js +4 -4
- package/dist/generated/button.js.map +1 -1
- package/dist/generated/card.js +6 -6
- package/dist/generated/card.js.map +1 -1
- package/dist/generated/chat-composer.js +21 -0
- package/dist/generated/chat-composer.js.map +1 -0
- package/dist/generated/chat-message.js +86 -0
- package/dist/generated/chat-message.js.map +1 -0
- package/dist/generated/chat-typing.js +9 -0
- package/dist/generated/chat-typing.js.map +1 -0
- package/dist/generated/command-palette.js +17 -0
- package/dist/generated/command-palette.js.map +1 -0
- package/dist/generated/command.js +2 -2
- package/dist/generated/command.js.map +1 -1
- package/dist/generated/confirm-dialog.js +28 -0
- package/dist/generated/confirm-dialog.js.map +1 -0
- package/dist/generated/context-menu.js +1 -1
- package/dist/generated/context-menu.js.map +1 -1
- package/dist/generated/date-time-picker.js +1 -1
- package/dist/generated/date-time-picker.js.map +1 -1
- package/dist/generated/dialog.js +1 -1
- package/dist/generated/dialog.js.map +1 -1
- package/dist/generated/drawer.js +1 -1
- package/dist/generated/drawer.js.map +1 -1
- package/dist/generated/dropdown-menu.js +1 -1
- package/dist/generated/dropdown-menu.js.map +1 -1
- package/dist/generated/form-row.js +17 -0
- package/dist/generated/form-row.js.map +1 -0
- package/dist/generated/header-controls.js +56 -0
- package/dist/generated/header-controls.js.map +1 -0
- package/dist/generated/icon-button.js +13 -0
- package/dist/generated/icon-button.js.map +1 -0
- package/dist/generated/key-value.js +15 -0
- package/dist/generated/key-value.js.map +1 -0
- package/dist/generated/menubar.js +1 -1
- package/dist/generated/menubar.js.map +1 -1
- package/dist/generated/navigation-menu.js +1 -1
- package/dist/generated/navigation-menu.js.map +1 -1
- package/dist/generated/popover-form.js +13 -0
- package/dist/generated/popover-form.js.map +1 -0
- package/dist/generated/section-label.js +10 -0
- package/dist/generated/section-label.js.map +1 -0
- package/dist/generated/select.js +15 -3
- package/dist/generated/select.js.map +1 -1
- package/dist/generated/settings.js +36 -0
- package/dist/generated/settings.js.map +1 -0
- package/dist/generated/sheet.js +1 -1
- package/dist/generated/sheet.js.map +1 -1
- package/dist/generated/slider.js +1 -1
- package/dist/generated/slider.js.map +1 -1
- package/dist/generated/smart-header.js +11 -0
- package/dist/generated/smart-header.js.map +1 -0
- package/dist/generated/sonner.js +4 -4
- package/dist/generated/sonner.js.map +1 -1
- package/dist/generated/status-item.js +17 -0
- package/dist/generated/status-item.js.map +1 -0
- package/dist/generated/system-banner.js +33 -0
- package/dist/generated/system-banner.js.map +1 -0
- package/dist/generated/top-bar.js +9 -0
- package/dist/generated/top-bar.js.map +1 -0
- package/dist/generated/workspace-tab.js +17 -0
- package/dist/generated/workspace-tab.js.map +1 -0
- package/dist/index.d.ts +852 -156
- package/dist/index.js +21 -1
- package/dist/lib/cn.js +8 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/number-parts.js +90 -0
- package/dist/lib/number-parts.js.map +1 -0
- package/dist/palette-cli.js +1 -1
- package/dist/palette.d.ts +6 -4
- package/dist/palette.js +3 -1
- package/dist/primitives/dismissable-layer.js +76 -4
- package/dist/primitives/dismissable-layer.js.map +1 -1
- package/dist/primitives/focus-scope.js +20 -5
- package/dist/primitives/focus-scope.js.map +1 -1
- package/dist/primitives/use-floating.js +16 -1
- package/dist/primitives/use-floating.js.map +1 -1
- package/dist/primitives/use-reduced-motion.js +20 -0
- package/dist/primitives/use-reduced-motion.js.map +1 -0
- package/package.json +1 -1
- package/styles/ev.css +281 -49
- package/styles/theme.css +335 -39
- package/styles/tokens.css +643 -103
- package/dist/chunk-3IPB4QQJ.js.map +0 -1
- package/dist/components/terminal.js +0 -206
- package/dist/components/terminal.js.map +0 -1
- package/dist/generated/terminal.js +0 -70
- package/dist/generated/terminal.js.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import {
|
|
5
|
+
FORM_ROW_BASE,
|
|
6
|
+
FORM_ROW_CONTROL,
|
|
7
|
+
FORM_ROW_DESCRIPTION,
|
|
8
|
+
FORM_ROW_DIVIDER,
|
|
9
|
+
FORM_ROW_HINT,
|
|
10
|
+
FORM_ROW_LABEL,
|
|
11
|
+
FORM_ROW_LABEL_COLUMN
|
|
12
|
+
} from "../generated/form-row.js";
|
|
13
|
+
function FormRow({
|
|
14
|
+
label,
|
|
15
|
+
htmlFor,
|
|
16
|
+
description,
|
|
17
|
+
hint,
|
|
18
|
+
divider = true,
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
...props
|
|
22
|
+
}) {
|
|
23
|
+
const Label = htmlFor !== void 0 ? "label" : "span";
|
|
24
|
+
return /* @__PURE__ */ jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
"data-slot": "form-row",
|
|
28
|
+
"data-divider": divider,
|
|
29
|
+
className: cn(
|
|
30
|
+
FORM_ROW_BASE,
|
|
31
|
+
divider && FORM_ROW_DIVIDER,
|
|
32
|
+
className
|
|
33
|
+
),
|
|
34
|
+
...props,
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "form-row-label", className: FORM_ROW_LABEL_COLUMN, children: [
|
|
37
|
+
/* @__PURE__ */ jsx(Label, { ...htmlFor !== void 0 ? { htmlFor } : {}, className: FORM_ROW_LABEL, children: label }),
|
|
38
|
+
description != null && /* @__PURE__ */ jsx("span", { className: FORM_ROW_DESCRIPTION, children: description })
|
|
39
|
+
] }),
|
|
40
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "form-row-control", className: FORM_ROW_CONTROL, children: [
|
|
41
|
+
children,
|
|
42
|
+
hint != null && /* @__PURE__ */ jsx("span", { className: FORM_ROW_HINT, children: hint })
|
|
43
|
+
] })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
FormRow
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=form-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/form-row.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n FORM_ROW_BASE,\n FORM_ROW_CONTROL,\n FORM_ROW_DESCRIPTION,\n FORM_ROW_DIVIDER,\n FORM_ROW_HINT,\n FORM_ROW_LABEL,\n FORM_ROW_LABEL_COLUMN,\n} from \"../generated/form-row\";\n\nexport interface FormRowProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n label: React.ReactNode;\n /** The id of the control in `children`; renders the label as a `<label for>`. */\n htmlFor?: string;\n /** A line under the label. */\n description?: React.ReactNode;\n /** A caption under the control, e.g. a format hint. */\n hint?: React.ReactNode;\n /** Hairline under the row. Default true; the last row of a group turns it off. */\n divider?: boolean;\n}\n\n/**\n * Label on the left, the value or its control on the right; they stack on a\n * phone. A plain `div`, not `ListRow`: it is a form field row, not a list item,\n * and its label column has a fixed width so controls line up down the page.\n */\nexport function FormRow({\n label,\n htmlFor,\n description,\n hint,\n divider = true,\n className,\n children,\n ...props\n}: FormRowProps) {\n const Label = htmlFor !== undefined ? \"label\" : \"span\";\n return (\n <div\n data-slot=\"form-row\"\n data-divider={divider}\n className={cn(\n FORM_ROW_BASE,\n divider && FORM_ROW_DIVIDER,\n className,\n )}\n {...props}\n >\n <div data-slot=\"form-row-label\" className={FORM_ROW_LABEL_COLUMN}>\n <Label {...(htmlFor !== undefined ? { htmlFor } : {})} className={FORM_ROW_LABEL}>\n {label}\n </Label>\n {description != null && <span className={FORM_ROW_DESCRIPTION}>{description}</span>}\n </div>\n <div data-slot=\"form-row-control\" className={FORM_ROW_CONTROL}>\n {children}\n {hint != null && <span className={FORM_ROW_HINT}>{hint}</span>}\n </div>\n </div>\n );\n}\n"],"mappings":"AAmDM,SACE,KADF;AAnDN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAmBA,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiB;AACf,QAAM,QAAQ,YAAY,SAAY,UAAU;AAChD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,6BAAC,SAAI,aAAU,kBAAiB,WAAW,uBACzC;AAAA,8BAAC,SAAO,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC,GAAI,WAAW,gBAC/D,iBACH;AAAA,UACC,eAAe,QAAQ,oBAAC,UAAK,WAAW,sBAAuB,uBAAY;AAAA,WAC9E;AAAA,QACA,qBAAC,SAAI,aAAU,oBAAmB,WAAW,kBAC1C;AAAA;AAAA,UACA,QAAQ,QAAQ,oBAAC,UAAK,WAAW,eAAgB,gBAAK;AAAA,WACzD;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import {
|
|
6
|
+
HEADER_BALANCE_BASE,
|
|
7
|
+
HEADER_BALANCE_CHEVRON,
|
|
8
|
+
HEADER_BALANCE_FIGURE,
|
|
9
|
+
HEADER_BALANCE_INTERACTIVE,
|
|
10
|
+
HEADER_BALANCE_LABEL,
|
|
11
|
+
HEADER_BALANCE_SETTLED,
|
|
12
|
+
HEADER_BALANCE_SKELETON,
|
|
13
|
+
HEADER_SEARCH_BASE,
|
|
14
|
+
HEADER_SEARCH_PLACEHOLDER,
|
|
15
|
+
NOTIFICATION_BELL_CONTENT,
|
|
16
|
+
NOTIFICATION_BELL_EMPTY,
|
|
17
|
+
NOTIFICATION_BELL_FOOTER,
|
|
18
|
+
NOTIFICATION_BELL_ITEM,
|
|
19
|
+
NOTIFICATION_BELL_ITEM_BODY,
|
|
20
|
+
NOTIFICATION_BELL_ITEM_DESCRIPTION,
|
|
21
|
+
NOTIFICATION_BELL_ITEM_DOT,
|
|
22
|
+
NOTIFICATION_BELL_ITEM_DOT_READ,
|
|
23
|
+
NOTIFICATION_BELL_ITEM_DOT_UNREAD,
|
|
24
|
+
NOTIFICATION_BELL_ITEM_TIME,
|
|
25
|
+
NOTIFICATION_BELL_ITEM_TITLE,
|
|
26
|
+
NOTIFICATION_BELL_ITEM_TITLE_UNREAD,
|
|
27
|
+
NOTIFICATION_BELL_LIST,
|
|
28
|
+
NOTIFICATION_BELL_TRIGGER,
|
|
29
|
+
NOTIFICATION_DOT,
|
|
30
|
+
headerBalanceToneClasses
|
|
31
|
+
} from "../generated/header-controls.js";
|
|
32
|
+
import { AnimatedNumber } from "./animated-number.js";
|
|
33
|
+
import { IconButton } from "./icon-button.js";
|
|
34
|
+
import { Kbd } from "./kbd.js";
|
|
35
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
36
|
+
import { Settled } from "./settled.js";
|
|
37
|
+
import { Skeleton } from "./skeleton.js";
|
|
38
|
+
import { UiIcon } from "./ui-icons.js";
|
|
39
|
+
const USD = { style: "currency", currency: "USD" };
|
|
40
|
+
function HeaderBalance({
|
|
41
|
+
label = "Balance:",
|
|
42
|
+
value,
|
|
43
|
+
amount,
|
|
44
|
+
format = USD,
|
|
45
|
+
locales,
|
|
46
|
+
loading = false,
|
|
47
|
+
icon,
|
|
48
|
+
tone = "default",
|
|
49
|
+
onClick,
|
|
50
|
+
disabled,
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}) {
|
|
54
|
+
const face = cn(HEADER_BALANCE_BASE, onClick && HEADER_BALANCE_INTERACTIVE, className);
|
|
55
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
56
|
+
icon,
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: HEADER_BALANCE_LABEL, children: label }),
|
|
58
|
+
/* @__PURE__ */ jsx(
|
|
59
|
+
Settled,
|
|
60
|
+
{
|
|
61
|
+
loading,
|
|
62
|
+
skeleton: /* @__PURE__ */ jsx(Skeleton, { className: HEADER_BALANCE_SKELETON }),
|
|
63
|
+
className: HEADER_BALANCE_SETTLED,
|
|
64
|
+
children: /* @__PURE__ */ jsx("span", { className: cn(HEADER_BALANCE_FIGURE, headerBalanceToneClasses[tone]), children: amount === void 0 ? value : /* @__PURE__ */ jsx(AnimatedNumber, { value: amount, format, locales }) })
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
onClick && /* @__PURE__ */ jsx(UiIcon, { name: "chevron-down", className: HEADER_BALANCE_CHEVRON })
|
|
68
|
+
] });
|
|
69
|
+
if (!onClick) {
|
|
70
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "header-balance", className: face, ...props, children: body });
|
|
71
|
+
}
|
|
72
|
+
return /* @__PURE__ */ jsx("button", { type: "button", "data-slot": "header-balance", className: face, onClick, disabled, ...props, children: body });
|
|
73
|
+
}
|
|
74
|
+
function NotificationBell({ notifications, onSelect, labels = {}, footer, className }) {
|
|
75
|
+
const unread = notifications.filter((n) => n.unread).length;
|
|
76
|
+
const name = labels.trigger ?? "Notifications";
|
|
77
|
+
const spoken = unread > 0 ? `${name}, ${(labels.unread ?? ((n) => `${n} unread`))(unread)}` : name;
|
|
78
|
+
return /* @__PURE__ */ jsxs(Popover, { children: [
|
|
79
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(IconButton, { "aria-label": spoken, variant: "outline", className: cn(NOTIFICATION_BELL_TRIGGER, className), children: [
|
|
80
|
+
/* @__PURE__ */ jsx(UiIcon, { name: "bell" }),
|
|
81
|
+
unread > 0 && /* @__PURE__ */ jsx("span", { "data-slot": "notification-dot", "aria-hidden": true, className: NOTIFICATION_DOT })
|
|
82
|
+
] }) }),
|
|
83
|
+
/* @__PURE__ */ jsxs(PopoverContent, { align: "end", className: NOTIFICATION_BELL_CONTENT, children: [
|
|
84
|
+
notifications.length === 0 ? /* @__PURE__ */ jsx("p", { className: NOTIFICATION_BELL_EMPTY, children: labels.empty ?? "No notifications" }) : /* @__PURE__ */ jsx("ul", { className: NOTIFICATION_BELL_LIST, children: notifications.map((n) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
85
|
+
"button",
|
|
86
|
+
{
|
|
87
|
+
type: "button",
|
|
88
|
+
onClick: () => onSelect?.(n.id),
|
|
89
|
+
className: NOTIFICATION_BELL_ITEM,
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
"span",
|
|
93
|
+
{
|
|
94
|
+
"aria-hidden": true,
|
|
95
|
+
className: cn(
|
|
96
|
+
NOTIFICATION_BELL_ITEM_DOT,
|
|
97
|
+
n.unread ? NOTIFICATION_BELL_ITEM_DOT_UNREAD : NOTIFICATION_BELL_ITEM_DOT_READ
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ jsxs("span", { className: NOTIFICATION_BELL_ITEM_BODY, children: [
|
|
102
|
+
/* @__PURE__ */ jsx("span", { className: cn(NOTIFICATION_BELL_ITEM_TITLE, n.unread && NOTIFICATION_BELL_ITEM_TITLE_UNREAD), children: n.title }),
|
|
103
|
+
n.description != null && /* @__PURE__ */ jsx("span", { className: NOTIFICATION_BELL_ITEM_DESCRIPTION, children: n.description })
|
|
104
|
+
] }),
|
|
105
|
+
n.time != null && /* @__PURE__ */ jsx("span", { className: NOTIFICATION_BELL_ITEM_TIME, children: n.time })
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
) }, n.id)) }),
|
|
109
|
+
footer != null && /* @__PURE__ */ jsx("div", { className: NOTIFICATION_BELL_FOOTER, children: footer })
|
|
110
|
+
] })
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
113
|
+
const APPLE = /Mac|iPhone|iPad|iPod/;
|
|
114
|
+
function usePlatformShortcut() {
|
|
115
|
+
const [shortcut, setShortcut] = React.useState("Ctrl K");
|
|
116
|
+
React.useEffect(() => {
|
|
117
|
+
const nav = navigator;
|
|
118
|
+
if (APPLE.test(nav.userAgentData?.platform ?? nav.platform ?? "")) setShortcut("\u2318K");
|
|
119
|
+
}, []);
|
|
120
|
+
return shortcut;
|
|
121
|
+
}
|
|
122
|
+
function HeaderSearch({
|
|
123
|
+
placeholder = "Search or jump to\u2026",
|
|
124
|
+
shortcut,
|
|
125
|
+
className,
|
|
126
|
+
...props
|
|
127
|
+
}) {
|
|
128
|
+
const platformShortcut = usePlatformShortcut();
|
|
129
|
+
return /* @__PURE__ */ jsxs(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
type: "button",
|
|
133
|
+
"data-slot": "header-search",
|
|
134
|
+
className: cn(HEADER_SEARCH_BASE, className),
|
|
135
|
+
...props,
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsx(UiIcon, { name: "search" }),
|
|
138
|
+
/* @__PURE__ */ jsx("span", { className: HEADER_SEARCH_PLACEHOLDER, children: placeholder }),
|
|
139
|
+
/* @__PURE__ */ jsx(Kbd, { children: shortcut ?? platformShortcut })
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
HeaderBalance,
|
|
146
|
+
HeaderSearch,
|
|
147
|
+
NotificationBell
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=header-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/header-controls.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n HEADER_BALANCE_BASE,\n HEADER_BALANCE_CHEVRON,\n HEADER_BALANCE_FIGURE,\n HEADER_BALANCE_INTERACTIVE,\n HEADER_BALANCE_LABEL,\n HEADER_BALANCE_SETTLED,\n HEADER_BALANCE_SKELETON,\n HEADER_SEARCH_BASE,\n HEADER_SEARCH_PLACEHOLDER,\n NOTIFICATION_BELL_CONTENT,\n NOTIFICATION_BELL_EMPTY,\n NOTIFICATION_BELL_FOOTER,\n NOTIFICATION_BELL_ITEM,\n NOTIFICATION_BELL_ITEM_BODY,\n NOTIFICATION_BELL_ITEM_DESCRIPTION,\n NOTIFICATION_BELL_ITEM_DOT,\n NOTIFICATION_BELL_ITEM_DOT_READ,\n NOTIFICATION_BELL_ITEM_DOT_UNREAD,\n NOTIFICATION_BELL_ITEM_TIME,\n NOTIFICATION_BELL_ITEM_TITLE,\n NOTIFICATION_BELL_ITEM_TITLE_UNREAD,\n NOTIFICATION_BELL_LIST,\n NOTIFICATION_BELL_TRIGGER,\n NOTIFICATION_DOT,\n headerBalanceToneClasses,\n type HeaderBalanceTone,\n} from \"../generated/header-controls\";\nimport { AnimatedNumber } from \"./animated-number\";\nimport { IconButton } from \"./icon-button\";\nimport { Kbd } from \"./kbd\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\nimport { Settled } from \"./settled\";\nimport { Skeleton } from \"./skeleton\";\nimport { UiIcon } from \"./ui-icons\";\n\nexport interface HeaderBalanceProps extends Omit<React.HTMLAttributes<HTMLElement>, \"children\"> {\n /** Only meaningful with `onClick`. */\n disabled?: boolean;\n label?: React.ReactNode;\n /** The figure, already formatted: \"$0.00\". Ignored when `amount` is given. */\n value?: React.ReactNode;\n /** The figure as a number: it rolls to each new amount, formatted by `format` and `locales`. */\n amount?: number;\n /** Formatting for `amount`. Default: USD currency. */\n format?: Intl.NumberFormatOptions;\n locales?: Intl.LocalesArgument;\n /** True until the balance is known; the figure then reveals in place of the skeleton. */\n loading?: boolean;\n /** A glyph before the label. */\n icon?: React.ReactNode;\n /** `warn` colours the figure as a low-funds signal. */\n tone?: HeaderBalanceTone;\n}\n\nconst USD: Intl.NumberFormatOptions = { style: \"currency\", currency: \"USD\" };\n\n/** \"Balance: $0.00\" in the header; a button only when given `onClick`. */\nexport function HeaderBalance({\n label = \"Balance:\",\n value,\n amount,\n format = USD,\n locales,\n loading = false,\n icon,\n tone = \"default\",\n onClick,\n disabled,\n className,\n ...props\n}: HeaderBalanceProps) {\n const face = cn(HEADER_BALANCE_BASE, onClick && HEADER_BALANCE_INTERACTIVE, className);\n const body = (\n <>\n {icon}\n <span className={HEADER_BALANCE_LABEL}>{label}</span>\n <Settled\n loading={loading}\n skeleton={<Skeleton className={HEADER_BALANCE_SKELETON} />}\n className={HEADER_BALANCE_SETTLED}\n >\n <span className={cn(HEADER_BALANCE_FIGURE, headerBalanceToneClasses[tone])}>\n {amount === undefined ? value : <AnimatedNumber value={amount} format={format} locales={locales} />}\n </span>\n </Settled>\n {onClick && <UiIcon name=\"chevron-down\" className={HEADER_BALANCE_CHEVRON} />}\n </>\n );\n if (!onClick) {\n return (\n <div data-slot=\"header-balance\" className={face} {...props}>\n {body}\n </div>\n );\n }\n return (\n <button type=\"button\" data-slot=\"header-balance\" className={face} onClick={onClick} disabled={disabled} {...props}>\n {body}\n </button>\n );\n}\n\nexport interface HeaderNotification {\n id: string;\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Already formatted: \"2m ago\". */\n time?: React.ReactNode;\n unread?: boolean;\n}\n\nexport interface NotificationBellLabels {\n /** The bell's name. Default `\"Notifications\"`. */\n trigger?: string;\n /** Spoken after the name when something is unread; gets the count. Default `\"{n} unread\"`. */\n unread?: (count: number) => string;\n /** Default `\"No notifications\"`. */\n empty?: string;\n}\n\nexport interface NotificationBellProps {\n notifications: readonly HeaderNotification[];\n onSelect?: (id: string) => void;\n labels?: NotificationBellLabels;\n /** Under the list: \"View all\". */\n footer?: React.ReactNode;\n className?: string;\n}\n\n/** The bell with an unread dot; the list opens in a popover. */\nexport function NotificationBell({ notifications, onSelect, labels = {}, footer, className }: NotificationBellProps) {\n const unread = notifications.filter((n) => n.unread).length;\n const name = labels.trigger ?? \"Notifications\";\n const spoken = unread > 0 ? `${name}, ${(labels.unread ?? ((n) => `${n} unread`))(unread)}` : name;\n return (\n <Popover>\n <PopoverTrigger asChild>\n <IconButton aria-label={spoken} variant=\"outline\" className={cn(NOTIFICATION_BELL_TRIGGER, className)}>\n <UiIcon name=\"bell\" />\n {unread > 0 && (\n <span data-slot=\"notification-dot\" aria-hidden className={NOTIFICATION_DOT} />\n )}\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className={NOTIFICATION_BELL_CONTENT}>\n {notifications.length === 0 ? (\n <p className={NOTIFICATION_BELL_EMPTY}>{labels.empty ?? \"No notifications\"}</p>\n ) : (\n <ul className={NOTIFICATION_BELL_LIST}>\n {notifications.map((n) => (\n <li key={n.id}>\n <button\n type=\"button\"\n onClick={() => onSelect?.(n.id)}\n className={NOTIFICATION_BELL_ITEM}\n >\n <span\n aria-hidden\n className={cn(\n NOTIFICATION_BELL_ITEM_DOT,\n n.unread ? NOTIFICATION_BELL_ITEM_DOT_UNREAD : NOTIFICATION_BELL_ITEM_DOT_READ,\n )}\n />\n <span className={NOTIFICATION_BELL_ITEM_BODY}>\n <span className={cn(NOTIFICATION_BELL_ITEM_TITLE, n.unread && NOTIFICATION_BELL_ITEM_TITLE_UNREAD)}>{n.title}</span>\n {n.description != null && <span className={NOTIFICATION_BELL_ITEM_DESCRIPTION}>{n.description}</span>}\n </span>\n {n.time != null && <span className={NOTIFICATION_BELL_ITEM_TIME}>{n.time}</span>}\n </button>\n </li>\n ))}\n </ul>\n )}\n {footer != null && <div className={NOTIFICATION_BELL_FOOTER}>{footer}</div>}\n </PopoverContent>\n </Popover>\n );\n}\n\nexport interface HeaderSearchProps extends React.ComponentProps<\"button\"> {\n placeholder?: string;\n /** The shortcut hint at the end. Default: `⌘K` on Apple platforms, `Ctrl K` elsewhere. */\n shortcut?: string;\n}\n\nconst APPLE = /Mac|iPhone|iPad|iPod/;\n\n// Read after mount, so the server render and the first client render agree.\nfunction usePlatformShortcut(): string {\n const [shortcut, setShortcut] = React.useState(\"Ctrl K\");\n React.useEffect(() => {\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n if (APPLE.test(nav.userAgentData?.platform ?? nav.platform ?? \"\")) setShortcut(\"⌘K\");\n }, []);\n return shortcut;\n}\n\n/** A button dressed as the search field; it opens the palette rather than taking input. */\nexport function HeaderSearch({\n placeholder = \"Search or jump to…\",\n shortcut,\n className,\n ...props\n}: HeaderSearchProps) {\n const platformShortcut = usePlatformShortcut();\n return (\n <button\n type=\"button\"\n data-slot=\"header-search\"\n className={cn(HEADER_SEARCH_BASE, className)}\n {...props}\n >\n <UiIcon name=\"search\" />\n <span className={HEADER_SEARCH_PLACEHOLDER}>{placeholder}</span>\n <Kbd>{shortcut ?? platformShortcut}</Kbd>\n </button>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { ICON_BUTTON_BASE, ICON_BUTTON_PRESSED, iconButtonSizeClasses } from "../generated/icon-button.js";
|
|
5
|
+
import { buttonVariants } from "./button.js";
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
7
|
+
const IconButton = React.forwardRef(function IconButton2({ variant = "ghost", size = "md", pressed, tooltip, className, type = "button", ...props }, ref) {
|
|
8
|
+
const button = /* @__PURE__ */ jsx(
|
|
9
|
+
"button",
|
|
10
|
+
{
|
|
11
|
+
ref,
|
|
12
|
+
type,
|
|
13
|
+
"data-slot": "icon-button",
|
|
14
|
+
"data-variant": variant,
|
|
15
|
+
"aria-pressed": pressed,
|
|
16
|
+
className: cn(
|
|
17
|
+
buttonVariants({ variant, size, icon: true }),
|
|
18
|
+
iconButtonSizeClasses[size],
|
|
19
|
+
ICON_BUTTON_BASE,
|
|
20
|
+
pressed && ICON_BUTTON_PRESSED,
|
|
21
|
+
className
|
|
22
|
+
),
|
|
23
|
+
...props
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
if (tooltip === void 0) return button;
|
|
27
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
28
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: button }),
|
|
29
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: tooltip })
|
|
30
|
+
] });
|
|
31
|
+
});
|
|
32
|
+
export {
|
|
33
|
+
IconButton
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=icon-button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/icon-button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { ICON_BUTTON_BASE, ICON_BUTTON_PRESSED, iconButtonSizeClasses, type IconButtonSize } from \"../generated/icon-button\";\nimport { buttonVariants } from \"./button\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"./tooltip\";\n\nexport type { IconButtonSize };\nexport type IconButtonVariant = \"outline\" | \"ghost\";\n\nexport interface IconButtonProps extends Omit<React.ComponentPropsWithoutRef<\"button\">, \"aria-label\" | \"children\"> {\n /** Required: a glyph alone names nothing. */\n \"aria-label\": string;\n /** The glyph, 16 px. */\n children: React.ReactNode;\n variant?: IconButtonVariant;\n size?: IconButtonSize;\n /** Toggled on: the active fill, and `aria-pressed` for assistive tech. */\n pressed?: boolean;\n /** Shown in a Tooltip on hover and focus. */\n tooltip?: React.ReactNode;\n}\n\n/**\n * A square button holding one glyph. It forwards its ref (also on React 18,\n * where function components drop `ref`) so it can be the child of any\n * `asChild` trigger; with a `tooltip` the same element anchors the tooltip.\n */\nexport const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n { variant = \"ghost\", size = \"md\", pressed, tooltip, className, type = \"button\", ...props },\n ref,\n) {\n const button = (\n <button\n ref={ref}\n type={type}\n data-slot=\"icon-button\"\n data-variant={variant}\n aria-pressed={pressed}\n className={cn(\n buttonVariants({ variant, size, icon: true }),\n iconButtonSizeClasses[size],\n ICON_BUTTON_BASE,\n pressed && ICON_BUTTON_PRESSED,\n className,\n )}\n {...props}\n />\n );\n if (tooltip === undefined) return button;\n return (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>{tooltip}</TooltipContent>\n </Tooltip>\n );\n});\n"],"mappings":"AAgCI,cAkBA,YAlBA;AAhCJ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,kBAAkB,qBAAqB,6BAAkD;AAClG,SAAS,sBAAsB;AAC/B,SAAS,SAAS,gBAAgB,sBAAsB;AAuBjD,MAAM,aAAa,MAAM,WAA+C,SAASA,YACtF,EAAE,UAAU,SAAS,OAAO,MAAM,SAAS,SAAS,WAAW,OAAO,UAAU,GAAG,MAAM,GACzF,KACA;AACA,QAAM,SACJ;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,gBAAc;AAAA,MACd,WAAW;AAAA,QACT,eAAe,EAAE,SAAS,MAAM,MAAM,KAAK,CAAC;AAAA,QAC5C,sBAAsB,IAAI;AAAA,QAC1B;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEF,MAAI,YAAY,OAAW,QAAO;AAClC,SACE,qBAAC,WACC;AAAA,wBAAC,kBAAe,SAAO,MAAE,kBAAO;AAAA,IAChC,oBAAC,kBAAgB,mBAAQ;AAAA,KAC3B;AAEJ,CAAC;","names":["IconButton"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import {
|
|
6
|
+
KEY_VALUE_BASE,
|
|
7
|
+
KEY_VALUE_COPY,
|
|
8
|
+
KEY_VALUE_LABEL,
|
|
9
|
+
KEY_VALUE_ROW,
|
|
10
|
+
KEY_VALUE_STATUS,
|
|
11
|
+
KEY_VALUE_VALUE
|
|
12
|
+
} from "../generated/key-value.js";
|
|
13
|
+
import { UiIcon } from "./ui-icons.js";
|
|
14
|
+
const COPIED_MS = 2e3;
|
|
15
|
+
async function writeClipboard(text) {
|
|
16
|
+
try {
|
|
17
|
+
if (navigator.clipboard?.writeText) {
|
|
18
|
+
await navigator.clipboard.writeText(text);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
const host = document.activeElement?.closest('[role="dialog"]') ?? document.body;
|
|
24
|
+
const area = document.createElement("textarea");
|
|
25
|
+
area.value = text;
|
|
26
|
+
area.setAttribute("readonly", "");
|
|
27
|
+
area.style.position = "fixed";
|
|
28
|
+
area.style.opacity = "0";
|
|
29
|
+
host.append(area);
|
|
30
|
+
area.select();
|
|
31
|
+
try {
|
|
32
|
+
return document.execCommand("copy");
|
|
33
|
+
} finally {
|
|
34
|
+
area.remove();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function KeyValue({ label, value, copyText, labels = {}, className, ...props }) {
|
|
38
|
+
const [copied, setCopied] = React.useState(false);
|
|
39
|
+
const timer = React.useRef(void 0);
|
|
40
|
+
React.useEffect(() => () => clearTimeout(timer.current), []);
|
|
41
|
+
const copy = async () => {
|
|
42
|
+
if (copyText === void 0 || !await writeClipboard(copyText)) return;
|
|
43
|
+
setCopied(true);
|
|
44
|
+
clearTimeout(timer.current);
|
|
45
|
+
timer.current = setTimeout(() => setCopied(false), COPIED_MS);
|
|
46
|
+
};
|
|
47
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "key-value", className: cn(KEY_VALUE_BASE, className), ...props, children: [
|
|
48
|
+
/* @__PURE__ */ jsx("span", { className: KEY_VALUE_LABEL, children: label }),
|
|
49
|
+
/* @__PURE__ */ jsxs("div", { className: KEY_VALUE_ROW, children: [
|
|
50
|
+
/* @__PURE__ */ jsx("span", { className: KEY_VALUE_VALUE, children: value }),
|
|
51
|
+
copyText !== void 0 && /* @__PURE__ */ jsx(
|
|
52
|
+
"button",
|
|
53
|
+
{
|
|
54
|
+
type: "button",
|
|
55
|
+
"aria-label": labels.copy ?? (typeof label === "string" ? `Copy ${label}` : "Copy"),
|
|
56
|
+
onClick: copy,
|
|
57
|
+
className: KEY_VALUE_COPY,
|
|
58
|
+
children: /* @__PURE__ */ jsx(UiIcon, { name: copied ? "check" : "copy" })
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
] }),
|
|
62
|
+
/* @__PURE__ */ jsx("span", { role: "status", "aria-live": "polite", className: KEY_VALUE_STATUS, children: copied ? labels.copied ?? "Copied" : "" })
|
|
63
|
+
] });
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
KeyValue
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=key-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/key-value.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n KEY_VALUE_BASE,\n KEY_VALUE_COPY,\n KEY_VALUE_LABEL,\n KEY_VALUE_ROW,\n KEY_VALUE_STATUS,\n KEY_VALUE_VALUE,\n} from \"../generated/key-value\";\nimport { UiIcon } from \"./ui-icons\";\n\nexport interface KeyValueLabels {\n /** The copy button. Default `\"Copy {label}\"` (just `\"Copy\"` when the label is not text). */\n copy?: string;\n /** The polite announcement after a copy. Default `\"Copied\"`. */\n copied?: string;\n}\n\nexport interface KeyValueProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n label: React.ReactNode;\n value: React.ReactNode;\n /** Adds a copy button for this text. */\n copyText?: string;\n labels?: KeyValueLabels;\n}\n\nconst COPIED_MS = 2000;\n\n// The async clipboard API needs a secure context and a user gesture; an\n// insecure origin or an old webview falls back to a selection + execCommand.\nasync function writeClipboard(text: string): Promise<boolean> {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return true;\n }\n } catch {\n // Denied permission: try the selection route below.\n }\n // Inside a modal the focus scope pulls focus back to the dialog, which would drop a selection\n // made in <body>; the helper has to live inside the dialog the user is in.\n const host = document.activeElement?.closest('[role=\"dialog\"]') ?? document.body;\n const area = document.createElement(\"textarea\");\n area.value = text;\n area.setAttribute(\"readonly\", \"\");\n area.style.position = \"fixed\";\n area.style.opacity = \"0\";\n host.append(area);\n area.select();\n try {\n return document.execCommand(\"copy\");\n } finally {\n area.remove();\n }\n}\n\n/** A small label over its value, with an optional copy button. */\nexport function KeyValue({ label, value, copyText, labels = {}, className, ...props }: KeyValueProps) {\n const [copied, setCopied] = React.useState(false);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n React.useEffect(() => () => clearTimeout(timer.current), []);\n\n const copy = async () => {\n if (copyText === undefined || !(await writeClipboard(copyText))) return;\n setCopied(true);\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), COPIED_MS);\n };\n\n return (\n <div data-slot=\"key-value\" className={cn(KEY_VALUE_BASE, className)} {...props}>\n <span className={KEY_VALUE_LABEL}>{label}</span>\n <div className={KEY_VALUE_ROW}>\n <span className={KEY_VALUE_VALUE}>{value}</span>\n {copyText !== undefined && (\n <button\n type=\"button\"\n aria-label={labels.copy ?? (typeof label === \"string\" ? `Copy ${label}` : \"Copy\")}\n onClick={copy}\n className={KEY_VALUE_COPY}\n >\n <UiIcon name={copied ? \"check\" : \"copy\"} />\n </button>\n )}\n </div>\n <span role=\"status\" aria-live=\"polite\" className={KEY_VALUE_STATUS}>\n {copied ? (labels.copied ?? \"Copied\") : \"\"}\n </span>\n </div>\n );\n}\n"],"mappings":";AA0EM,cACA,YADA;AAxEN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AAiBvB,MAAM,YAAY;AAIlB,eAAe,eAAe,MAAgC;AAC5D,MAAI;AACF,QAAI,UAAU,WAAW,WAAW;AAClC,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,OAAO,SAAS,eAAe,QAAQ,iBAAiB,KAAK,SAAS;AAC5E,QAAM,OAAO,SAAS,cAAc,UAAU;AAC9C,OAAK,QAAQ;AACb,OAAK,aAAa,YAAY,EAAE;AAChC,OAAK,MAAM,WAAW;AACtB,OAAK,MAAM,UAAU;AACrB,OAAK,OAAO,IAAI;AAChB,OAAK,OAAO;AACZ,MAAI;AACF,WAAO,SAAS,YAAY,MAAM;AAAA,EACpC,UAAE;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAGO,SAAS,SAAS,EAAE,OAAO,OAAO,UAAU,SAAS,CAAC,GAAG,WAAW,GAAG,MAAM,GAAkB;AACpG,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,QAAQ,MAAM,OAAkD,MAAS;AAC/E,QAAM,UAAU,MAAM,MAAM,aAAa,MAAM,OAAO,GAAG,CAAC,CAAC;AAE3D,QAAM,OAAO,YAAY;AACvB,QAAI,aAAa,UAAa,CAAE,MAAM,eAAe,QAAQ,EAAI;AACjE,cAAU,IAAI;AACd,iBAAa,MAAM,OAAO;AAC1B,UAAM,UAAU,WAAW,MAAM,UAAU,KAAK,GAAG,SAAS;AAAA,EAC9D;AAEA,SACE,qBAAC,SAAI,aAAU,aAAY,WAAW,GAAG,gBAAgB,SAAS,GAAI,GAAG,OACvE;AAAA,wBAAC,UAAK,WAAW,iBAAkB,iBAAM;AAAA,IACzC,qBAAC,SAAI,WAAW,eACd;AAAA,0BAAC,UAAK,WAAW,iBAAkB,iBAAM;AAAA,MACxC,aAAa,UACZ;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY,OAAO,SAAS,OAAO,UAAU,WAAW,QAAQ,KAAK,KAAK;AAAA,UAC1E,SAAS;AAAA,UACT,WAAW;AAAA,UAEX,8BAAC,UAAO,MAAM,SAAS,UAAU,QAAQ;AAAA;AAAA,MAC3C;AAAA,OAEJ;AAAA,IACA,oBAAC,UAAK,MAAK,UAAS,aAAU,UAAS,WAAW,kBAC/C,mBAAU,OAAO,UAAU,WAAY,IAC1C;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -8,6 +8,7 @@ import { useFloating } from "../primitives/use-floating.js";
|
|
|
8
8
|
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
9
9
|
import { usePresence } from "../primitives/presence.js";
|
|
10
10
|
import { useRovingFocus } from "../primitives/use-roving-focus.js";
|
|
11
|
+
import { useHoverIntent } from "../primitives/use-hover-intent.js";
|
|
11
12
|
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
12
13
|
import {
|
|
13
14
|
MENUBAR_CHECKBOX_ITEM,
|
|
@@ -263,19 +264,29 @@ function MenubarSub({ open, defaultOpen = false, onOpenChange, children }) {
|
|
|
263
264
|
defaultValue: defaultOpen,
|
|
264
265
|
onChange: onOpenChange
|
|
265
266
|
});
|
|
266
|
-
|
|
267
|
+
const triggerRef = React.useRef(null);
|
|
268
|
+
const focusFirst = React.useRef(false);
|
|
269
|
+
const hover = useHoverIntent({
|
|
270
|
+
onOpen: () => setOpen(true),
|
|
271
|
+
onClose: () => setOpen(false),
|
|
272
|
+
openDelay: 100,
|
|
273
|
+
closeDelay: 150
|
|
274
|
+
});
|
|
275
|
+
return /* @__PURE__ */ jsx(MenubarSubContext.Provider, { value: { open: isOpen, setOpen, triggerRef, focusFirst }, children: /* @__PURE__ */ jsx("div", { "data-slot": "menubar-sub", className: "relative", ...hover, children }) });
|
|
267
276
|
}
|
|
268
277
|
function MenubarSubTrigger({
|
|
269
278
|
className,
|
|
270
279
|
inset,
|
|
271
280
|
children,
|
|
272
281
|
onClick,
|
|
282
|
+
onKeyDown,
|
|
273
283
|
...props
|
|
274
284
|
}) {
|
|
275
|
-
const { open, setOpen } = useSub();
|
|
285
|
+
const { open, setOpen, triggerRef, focusFirst } = useSub();
|
|
276
286
|
return /* @__PURE__ */ jsxs(
|
|
277
287
|
"div",
|
|
278
288
|
{
|
|
289
|
+
ref: triggerRef,
|
|
279
290
|
"data-slot": "menubar-sub-trigger",
|
|
280
291
|
"data-inset": inset,
|
|
281
292
|
"data-state": open ? "open" : "closed",
|
|
@@ -286,6 +297,14 @@ function MenubarSubTrigger({
|
|
|
286
297
|
onClick?.(e);
|
|
287
298
|
setOpen(!open);
|
|
288
299
|
},
|
|
300
|
+
onKeyDown: (e) => {
|
|
301
|
+
onKeyDown?.(e);
|
|
302
|
+
if (e.key !== "ArrowRight" && e.key !== "Enter" && e.key !== " ") return;
|
|
303
|
+
e.preventDefault();
|
|
304
|
+
e.stopPropagation();
|
|
305
|
+
focusFirst.current = true;
|
|
306
|
+
setOpen(true);
|
|
307
|
+
},
|
|
289
308
|
...props,
|
|
290
309
|
children: [
|
|
291
310
|
children,
|
|
@@ -306,14 +325,29 @@ function MenubarSubTrigger({
|
|
|
306
325
|
}
|
|
307
326
|
);
|
|
308
327
|
}
|
|
309
|
-
function MenubarSubContent({ className, children, ...props }) {
|
|
310
|
-
const { open } = useSub();
|
|
328
|
+
function MenubarSubContent({ className, children, onKeyDown, ...props }) {
|
|
329
|
+
const { open, setOpen, triggerRef, focusFirst } = useSub();
|
|
311
330
|
const { isPresent, ref } = usePresence(open);
|
|
331
|
+
const inner = React.useRef(null);
|
|
332
|
+
React.useEffect(() => {
|
|
333
|
+
if (!open || !focusFirst.current) return;
|
|
334
|
+
focusFirst.current = false;
|
|
335
|
+
inner.current?.querySelector("[role^='menuitem']")?.focus();
|
|
336
|
+
}, [open, focusFirst]);
|
|
312
337
|
if (!isPresent) return null;
|
|
313
338
|
return /* @__PURE__ */ jsx(
|
|
314
339
|
"div",
|
|
315
340
|
{
|
|
316
|
-
ref,
|
|
341
|
+
ref: mergeRefs(ref, inner),
|
|
342
|
+
onKeyDown: (e) => {
|
|
343
|
+
onKeyDown?.(e);
|
|
344
|
+
if (e.key === "ArrowLeft" || e.key === "Escape") {
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
e.stopPropagation();
|
|
347
|
+
setOpen(false);
|
|
348
|
+
triggerRef.current?.focus();
|
|
349
|
+
}
|
|
350
|
+
},
|
|
317
351
|
"data-slot": "menubar-sub-content",
|
|
318
352
|
"data-state": open ? "open" : "closed",
|
|
319
353
|
role: "menu",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/menubar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { Portal } from \"../primitives/portal\";\nimport { useFloating } from \"../primitives/use-floating\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { useRovingFocus } from \"../primitives/use-roving-focus\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport {\n MENUBAR_CHECKBOX_ITEM,\n MENUBAR_CONTENT,\n MENUBAR_ITEM,\n MENUBAR_ITEM_INDICATOR,\n MENUBAR_LABEL,\n MENUBAR_RADIO_ITEM,\n MENUBAR_ROOT,\n MENUBAR_SEPARATOR,\n MENUBAR_SHORTCUT,\n MENUBAR_SUB_CONTENT,\n MENUBAR_SUB_TRIGGER,\n MENUBAR_TRIGGER,\n} from \"../generated/menubar\";\n\ninterface MenuCtx {\n open: boolean;\n setOpen: (open: boolean) => void;\n triggerRef: React.RefObject<HTMLButtonElement | null>;\n}\nconst MenubarMenuContext = React.createContext<MenuCtx | null>(null);\nfunction useMenu(): MenuCtx {\n const ctx = React.useContext(MenubarMenuContext);\n if (!ctx) throw new Error(\"Menubar parts must be used within <MenubarMenu>\");\n return ctx;\n}\n\nconst MenubarRadioGroupContext = React.createContext<{\n value: string;\n onValueChange: (value: string) => void;\n} | null>(null);\n\nexport function Menubar({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"menubar\"\n role=\"menubar\"\n className={cn(MENUBAR_ROOT, className)}\n {...props}\n />\n );\n}\n\nexport interface MenubarMenuProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function MenubarMenu({ open, defaultOpen = false, onOpenChange, children }: MenubarMenuProps) {\n const [isOpen, setOpen] = useControllableState({\n value: open,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n return (\n <MenubarMenuContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n <div data-slot=\"menubar-menu\" className=\"relative\">\n {children}\n </div>\n </MenubarMenuContext.Provider>\n );\n}\n\nexport function MenubarTrigger({ className, onClick, ...props }: React.ComponentProps<\"button\">) {\n const { open, setOpen, triggerRef } = useMenu();\n return (\n <button\n ref={triggerRef}\n type=\"button\"\n data-slot=\"menubar-trigger\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(MENUBAR_TRIGGER, className)}\n onClick={(e) => {\n onClick?.(e);\n setOpen(!open);\n }}\n {...props}\n />\n );\n}\n\nexport function MenubarContent({\n className,\n children,\n align = \"start\",\n ...props\n}: React.ComponentProps<\"div\"> & { align?: \"start\" | \"center\" | \"end\" }) {\n const { open, setOpen, triggerRef } = useMenu();\n const { isPresent, ref: presenceRef } = usePresence(open);\n const { floatingRef, style, side } = useFloating({\n anchorRef: triggerRef,\n open,\n side: \"bottom\",\n align,\n offset: 8,\n });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n });\n\n // Count the mounted menu items, not the children: separators, labels and\n // groups are children too, and an inline sub-menu adds items when it opens.\n const contentRef = React.useRef<HTMLDivElement | null>(null);\n const items = () =>\n Array.from(\n contentRef.current?.querySelectorAll<HTMLElement>(\"[role^='menuitem']\") ?? [],\n ).filter((el) => el.getAttribute(\"data-disabled\") === null);\n const { activeIndex, setActiveIndex, onKeyDown } = useRovingFocus({\n count: () => items().length,\n orientation: \"vertical\",\n });\n // Reset on close: reopen on the first item, never on an index past the end.\n React.useEffect(() => {\n if (!open) setActiveIndex(0);\n }, [open, setActiveIndex]);\n React.useEffect(() => {\n if (!open) return;\n items()[activeIndex]?.focus();\n }, [open, activeIndex]);\n\n if (!isPresent) return null;\n return (\n <Portal>\n <div\n ref={mergeRefs(presenceRef, floatingRef, dismissRef, contentRef)}\n data-slot=\"menubar-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side={side}\n role=\"menu\"\n style={style}\n className={cn(MENUBAR_CONTENT, className)}\n onKeyDown={onKeyDown}\n {...props}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </div>\n </Portal>\n );\n}\n\nexport function MenubarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div data-slot=\"menubar-group\" role=\"group\" className={className} {...props} />;\n}\n\nexport function MenubarItem({\n className,\n inset,\n variant = \"neutral\",\n ...props\n}: React.ComponentProps<\"div\"> & {\n inset?: boolean;\n variant?: \"neutral\" | \"destructive\";\n}) {\n return (\n <div\n data-slot=\"menubar-item\"\n data-inset={inset}\n data-variant={variant}\n role=\"menuitem\"\n tabIndex={-1}\n className={cn(MENUBAR_ITEM, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<\"div\"> & { checked?: boolean }) {\n return (\n <div\n data-slot=\"menubar-checkbox-item\"\n role=\"menuitemcheckbox\"\n aria-checked={checked}\n tabIndex={-1}\n className={cn(MENUBAR_CHECKBOX_ITEM, className)}\n {...props}\n >\n <span className={MENUBAR_ITEM_INDICATOR}>\n {checked && (\n <svg\n className=\"size-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )}\n </span>\n {children}\n </div>\n );\n}\n\nexport interface MenubarRadioGroupProps {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function MenubarRadioGroup({\n value,\n defaultValue = \"\",\n onValueChange,\n className,\n children,\n}: MenubarRadioGroupProps) {\n const [current, setValue] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n return (\n <MenubarRadioGroupContext.Provider value={{ value: current, onValueChange: setValue }}>\n <div data-slot=\"menubar-radio-group\" role=\"group\" className={className}>\n {children}\n </div>\n </MenubarRadioGroupContext.Provider>\n );\n}\n\nexport function MenubarRadioItem({\n className,\n children,\n value,\n ...props\n}: React.ComponentProps<\"div\"> & { value: string }) {\n const ctx = React.useContext(MenubarRadioGroupContext);\n const checked = ctx?.value === value;\n return (\n <div\n data-slot=\"menubar-radio-item\"\n role=\"menuitemradio\"\n aria-checked={checked}\n tabIndex={-1}\n className={cn(MENUBAR_RADIO_ITEM, className)}\n onClick={() => ctx?.onValueChange(value)}\n {...props}\n >\n <span className={MENUBAR_ITEM_INDICATOR}>\n {checked && (\n <svg className=\"size-2 fill-current\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n )}\n </span>\n {children}\n </div>\n );\n}\n\nexport function MenubarLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<\"div\"> & { inset?: boolean }) {\n return (\n <div\n data-slot=\"menubar-label\"\n data-inset={inset}\n className={cn(MENUBAR_LABEL, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"menubar-separator\"\n role=\"separator\"\n className={cn(MENUBAR_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"menubar-shortcut\"\n className={cn(MENUBAR_SHORTCUT, className)}\n {...props}\n />\n );\n}\n\ninterface SubCtx {\n open: boolean;\n setOpen: (open: boolean) => void;\n}\nconst MenubarSubContext = React.createContext<SubCtx | null>(null);\nfunction useSub(): SubCtx {\n const ctx = React.useContext(MenubarSubContext);\n if (!ctx) throw new Error(\"MenubarSub parts must be used within <MenubarSub>\");\n return ctx;\n}\n\nexport interface MenubarSubProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function MenubarSub({ open, defaultOpen = false, onOpenChange, children }: MenubarSubProps) {\n const [isOpen, setOpen] = useControllableState({\n value: open,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n return (\n <MenubarSubContext.Provider value={{ open: isOpen, setOpen }}>\n <div data-slot=\"menubar-sub\" className=\"relative\">\n {children}\n </div>\n </MenubarSubContext.Provider>\n );\n}\n\nexport function MenubarSubTrigger({\n className,\n inset,\n children,\n onClick,\n ...props\n}: React.ComponentProps<\"div\"> & { inset?: boolean }) {\n const { open, setOpen } = useSub();\n return (\n <div\n data-slot=\"menubar-sub-trigger\"\n data-inset={inset}\n data-state={open ? \"open\" : \"closed\"}\n role=\"menuitem\"\n tabIndex={-1}\n className={cn(MENUBAR_SUB_TRIGGER, className)}\n onClick={(e) => {\n onClick?.(e);\n setOpen(!open);\n }}\n {...props}\n >\n {children}\n <svg\n className=\"ml-auto h-4 w-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </div>\n );\n}\n\nexport function MenubarSubContent({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { open } = useSub();\n const { isPresent, ref } = usePresence(open);\n if (!isPresent) return null;\n return (\n <div\n ref={ref as React.RefObject<HTMLDivElement>}\n data-slot=\"menubar-sub-content\"\n data-state={open ? \"open\" : \"closed\"}\n role=\"menu\"\n className={cn(MENUBAR_SUB_CONTENT, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/menubar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { Portal } from \"../primitives/portal\";\nimport { useFloating } from \"../primitives/use-floating\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { usePresence } from \"../primitives/presence\";\nimport { useRovingFocus } from \"../primitives/use-roving-focus\";\nimport { useHoverIntent } from \"../primitives/use-hover-intent\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport {\n MENUBAR_CHECKBOX_ITEM,\n MENUBAR_CONTENT,\n MENUBAR_ITEM,\n MENUBAR_ITEM_INDICATOR,\n MENUBAR_LABEL,\n MENUBAR_RADIO_ITEM,\n MENUBAR_ROOT,\n MENUBAR_SEPARATOR,\n MENUBAR_SHORTCUT,\n MENUBAR_SUB_CONTENT,\n MENUBAR_SUB_TRIGGER,\n MENUBAR_TRIGGER,\n} from \"../generated/menubar\";\n\ninterface MenuCtx {\n open: boolean;\n setOpen: (open: boolean) => void;\n triggerRef: React.RefObject<HTMLButtonElement | null>;\n}\nconst MenubarMenuContext = React.createContext<MenuCtx | null>(null);\nfunction useMenu(): MenuCtx {\n const ctx = React.useContext(MenubarMenuContext);\n if (!ctx) throw new Error(\"Menubar parts must be used within <MenubarMenu>\");\n return ctx;\n}\n\nconst MenubarRadioGroupContext = React.createContext<{\n value: string;\n onValueChange: (value: string) => void;\n} | null>(null);\n\nexport function Menubar({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"menubar\"\n role=\"menubar\"\n className={cn(MENUBAR_ROOT, className)}\n {...props}\n />\n );\n}\n\nexport interface MenubarMenuProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function MenubarMenu({ open, defaultOpen = false, onOpenChange, children }: MenubarMenuProps) {\n const [isOpen, setOpen] = useControllableState({\n value: open,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n return (\n <MenubarMenuContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n <div data-slot=\"menubar-menu\" className=\"relative\">\n {children}\n </div>\n </MenubarMenuContext.Provider>\n );\n}\n\nexport function MenubarTrigger({ className, onClick, ...props }: React.ComponentProps<\"button\">) {\n const { open, setOpen, triggerRef } = useMenu();\n return (\n <button\n ref={triggerRef}\n type=\"button\"\n data-slot=\"menubar-trigger\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(MENUBAR_TRIGGER, className)}\n onClick={(e) => {\n onClick?.(e);\n setOpen(!open);\n }}\n {...props}\n />\n );\n}\n\nexport function MenubarContent({\n className,\n children,\n align = \"start\",\n ...props\n}: React.ComponentProps<\"div\"> & { align?: \"start\" | \"center\" | \"end\" }) {\n const { open, setOpen, triggerRef } = useMenu();\n const { isPresent, ref: presenceRef } = usePresence(open);\n const { floatingRef, style, side } = useFloating({\n anchorRef: triggerRef,\n open,\n side: \"bottom\",\n align,\n offset: 8,\n });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n });\n\n // Count the mounted menu items, not the children: separators, labels and\n // groups are children too, and an inline sub-menu adds items when it opens.\n const contentRef = React.useRef<HTMLDivElement | null>(null);\n const items = () =>\n Array.from(\n contentRef.current?.querySelectorAll<HTMLElement>(\"[role^='menuitem']\") ?? [],\n ).filter((el) => el.getAttribute(\"data-disabled\") === null);\n const { activeIndex, setActiveIndex, onKeyDown } = useRovingFocus({\n count: () => items().length,\n orientation: \"vertical\",\n });\n // Reset on close: reopen on the first item, never on an index past the end.\n React.useEffect(() => {\n if (!open) setActiveIndex(0);\n }, [open, setActiveIndex]);\n React.useEffect(() => {\n if (!open) return;\n items()[activeIndex]?.focus();\n }, [open, activeIndex]);\n\n if (!isPresent) return null;\n return (\n <Portal>\n <div\n ref={mergeRefs(presenceRef, floatingRef, dismissRef, contentRef)}\n data-slot=\"menubar-content\"\n data-state={open ? \"open\" : \"closed\"}\n data-side={side}\n role=\"menu\"\n style={style}\n className={cn(MENUBAR_CONTENT, className)}\n onKeyDown={onKeyDown}\n {...props}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </div>\n </Portal>\n );\n}\n\nexport function MenubarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div data-slot=\"menubar-group\" role=\"group\" className={className} {...props} />;\n}\n\nexport function MenubarItem({\n className,\n inset,\n variant = \"neutral\",\n ...props\n}: React.ComponentProps<\"div\"> & {\n inset?: boolean;\n variant?: \"neutral\" | \"destructive\";\n}) {\n return (\n <div\n data-slot=\"menubar-item\"\n data-inset={inset}\n data-variant={variant}\n role=\"menuitem\"\n tabIndex={-1}\n className={cn(MENUBAR_ITEM, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<\"div\"> & { checked?: boolean }) {\n return (\n <div\n data-slot=\"menubar-checkbox-item\"\n role=\"menuitemcheckbox\"\n aria-checked={checked}\n tabIndex={-1}\n className={cn(MENUBAR_CHECKBOX_ITEM, className)}\n {...props}\n >\n <span className={MENUBAR_ITEM_INDICATOR}>\n {checked && (\n <svg\n className=\"size-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )}\n </span>\n {children}\n </div>\n );\n}\n\nexport interface MenubarRadioGroupProps {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function MenubarRadioGroup({\n value,\n defaultValue = \"\",\n onValueChange,\n className,\n children,\n}: MenubarRadioGroupProps) {\n const [current, setValue] = useControllableState({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n return (\n <MenubarRadioGroupContext.Provider value={{ value: current, onValueChange: setValue }}>\n <div data-slot=\"menubar-radio-group\" role=\"group\" className={className}>\n {children}\n </div>\n </MenubarRadioGroupContext.Provider>\n );\n}\n\nexport function MenubarRadioItem({\n className,\n children,\n value,\n ...props\n}: React.ComponentProps<\"div\"> & { value: string }) {\n const ctx = React.useContext(MenubarRadioGroupContext);\n const checked = ctx?.value === value;\n return (\n <div\n data-slot=\"menubar-radio-item\"\n role=\"menuitemradio\"\n aria-checked={checked}\n tabIndex={-1}\n className={cn(MENUBAR_RADIO_ITEM, className)}\n onClick={() => ctx?.onValueChange(value)}\n {...props}\n >\n <span className={MENUBAR_ITEM_INDICATOR}>\n {checked && (\n <svg className=\"size-2 fill-current\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n )}\n </span>\n {children}\n </div>\n );\n}\n\nexport function MenubarLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<\"div\"> & { inset?: boolean }) {\n return (\n <div\n data-slot=\"menubar-label\"\n data-inset={inset}\n className={cn(MENUBAR_LABEL, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"menubar-separator\"\n role=\"separator\"\n className={cn(MENUBAR_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function MenubarShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"menubar-shortcut\"\n className={cn(MENUBAR_SHORTCUT, className)}\n {...props}\n />\n );\n}\n\ninterface SubCtx {\n open: boolean;\n setOpen: (open: boolean) => void;\n triggerRef: React.RefObject<HTMLDivElement | null>;\n /** Set by a keyboard open, so the first item takes focus; a pointer open leaves focus alone. */\n focusFirst: React.RefObject<boolean>;\n}\nconst MenubarSubContext = React.createContext<SubCtx | null>(null);\nfunction useSub(): SubCtx {\n const ctx = React.useContext(MenubarSubContext);\n if (!ctx) throw new Error(\"MenubarSub parts must be used within <MenubarSub>\");\n return ctx;\n}\n\nexport interface MenubarSubProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function MenubarSub({ open, defaultOpen = false, onOpenChange, children }: MenubarSubProps) {\n const [isOpen, setOpen] = useControllableState({\n value: open,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n const triggerRef = React.useRef<HTMLDivElement | null>(null);\n const focusFirst = React.useRef(false);\n const hover = useHoverIntent({\n onOpen: () => setOpen(true),\n onClose: () => setOpen(false),\n openDelay: 100,\n closeDelay: 150,\n });\n return (\n <MenubarSubContext.Provider value={{ open: isOpen, setOpen, triggerRef, focusFirst }}>\n <div data-slot=\"menubar-sub\" className=\"relative\" {...hover}>\n {children}\n </div>\n </MenubarSubContext.Provider>\n );\n}\n\nexport function MenubarSubTrigger({\n className,\n inset,\n children,\n onClick,\n onKeyDown,\n ...props\n}: React.ComponentProps<\"div\"> & { inset?: boolean }) {\n const { open, setOpen, triggerRef, focusFirst } = useSub();\n return (\n <div\n ref={triggerRef}\n data-slot=\"menubar-sub-trigger\"\n data-inset={inset}\n data-state={open ? \"open\" : \"closed\"}\n role=\"menuitem\"\n tabIndex={-1}\n className={cn(MENUBAR_SUB_TRIGGER, className)}\n onClick={(e) => {\n onClick?.(e);\n setOpen(!open);\n }}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (e.key !== \"ArrowRight\" && e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n e.stopPropagation();\n focusFirst.current = true;\n setOpen(true);\n }}\n {...props}\n >\n {children}\n <svg\n className=\"ml-auto h-4 w-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </div>\n );\n}\n\nexport function MenubarSubContent({ className, children, onKeyDown, ...props }: React.ComponentProps<\"div\">) {\n const { open, setOpen, triggerRef, focusFirst } = useSub();\n const { isPresent, ref } = usePresence(open);\n const inner = React.useRef<HTMLDivElement | null>(null);\n React.useEffect(() => {\n if (!open || !focusFirst.current) return;\n focusFirst.current = false;\n inner.current?.querySelector<HTMLElement>(\"[role^='menuitem']\")?.focus();\n }, [open, focusFirst]);\n if (!isPresent) return null;\n return (\n <div\n ref={mergeRefs(ref as React.RefObject<HTMLDivElement>, inner)}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (e.key === \"ArrowLeft\" || e.key === \"Escape\") {\n // Closes this flyout only; Escape would otherwise reach the whole menu's dismissal.\n e.preventDefault();\n e.stopPropagation();\n setOpen(false);\n triggerRef.current?.focus();\n }\n }}\n data-slot=\"menubar-sub-content\"\n data-state={open ? \"open\" : \"closed\"}\n role=\"menu\"\n className={cn(MENUBAR_SUB_CONTENT, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
|
-
import { nativeSelectSizeClasses } from "../generated/select.js";
|
|
5
|
+
import { NATIVE_SELECT_BASE, nativeSelectSizeClasses } from "../generated/select.js";
|
|
6
6
|
import { markLabelable } from "../primitives/labelable.js";
|
|
7
7
|
import { useFieldControlId } from "./field-context.js";
|
|
8
8
|
import { SelectChevron } from "./select-chevron.js";
|
|
9
|
-
const NATIVE_SELECT = "border-input text-ink h-9 w-full min-w-0 appearance-none rounded-[var(--control-radius)] border bg-transparent py-1 pr-9 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error has-[option[value='']:checked]:text-ink-soft [&_option]:bg-popover [&_option]:text-ink [&_optgroup]:bg-popover [&_optgroup]:text-ink-soft [color-scheme:var(--scheme,normal)]";
|
|
10
9
|
const NativeSelect = React.forwardRef(
|
|
11
10
|
function NativeSelect2({ className, wrapperClassName, size = "md", placeholder, id, children, value, defaultValue, ...props }, ref) {
|
|
12
11
|
const controlId = useFieldControlId(id);
|
|
@@ -15,7 +14,7 @@ const NativeSelect = React.forwardRef(
|
|
|
15
14
|
"span",
|
|
16
15
|
{
|
|
17
16
|
"data-slot": "native-select-wrapper",
|
|
18
|
-
className: cn("relative inline-flex w-full has-[select:disabled]:opacity-
|
|
17
|
+
className: cn("relative inline-flex w-full has-[select:disabled]:opacity-40", wrapperClassName),
|
|
19
18
|
children: [
|
|
20
19
|
/* @__PURE__ */ jsxs(
|
|
21
20
|
"select",
|
|
@@ -24,7 +23,7 @@ const NativeSelect = React.forwardRef(
|
|
|
24
23
|
id: controlId,
|
|
25
24
|
"data-slot": "native-select",
|
|
26
25
|
"data-size": size,
|
|
27
|
-
className: cn(
|
|
26
|
+
className: cn(NATIVE_SELECT_BASE, nativeSelectSizeClasses[size], className),
|
|
28
27
|
...value !== void 0 ? { value } : {},
|
|
29
28
|
...initial !== void 0 ? { defaultValue: initial } : {},
|
|
30
29
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/native-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { nativeSelectSizeClasses, type NativeSelectSize } from \"../generated/select\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\n\nexport type { NativeSelectSize };\n\n/**\n * A real `<select>` wearing the kit's control: the form posts its `name` with no\n * script at all, and the popup is the platform's own (keyboard, type-ahead, the\n * mobile picker). `Select` is the scripted combobox for everything a native\n * popup cannot draw; this is for a form that has to submit before hydration.\n *\n * A component of its own rather than a `native` switch on `Select`: none of\n * `SelectTrigger` / `SelectValue` / `SelectContent` has a native counterpart, so\n * a flag would leave most of the compound API meaning nothing in one mode.\n *\n * `className` styles the `<select>`; `wrapperClassName` the box that holds it\n * and the arrow — that box is what a layout sizes. It is `w-full` like `Input`,\n * so the two line up in a form column; narrow it with `wrapperClassName`.\n */\nexport interface NativeSelectProps\n extends Omit<React.ComponentPropsWithoutRef<\"select\">, \"size\" | \"multiple\"> {\n size?: NativeSelectSize;\n /** An empty, unpickable first option shown until a value is chosen. */\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n// `appearance-none` drops the OS arrow. The popup is drawn by the platform:\n// the option colours reach it where the platform honours them, and `--scheme`\n// (a palette's polarity, `light` or `dark`) picks the platform's own dark\n// menu where it does not. The placeholder is the empty option being the\n// checked one — CSS can see that without script.\
|
|
1
|
+
{"version":3,"sources":["../../src/components/native-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { NATIVE_SELECT_BASE, nativeSelectSizeClasses, type NativeSelectSize } from \"../generated/select\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\n\nexport type { NativeSelectSize };\n\n/**\n * A real `<select>` wearing the kit's control: the form posts its `name` with no\n * script at all, and the popup is the platform's own (keyboard, type-ahead, the\n * mobile picker). `Select` is the scripted combobox for everything a native\n * popup cannot draw; this is for a form that has to submit before hydration.\n *\n * A component of its own rather than a `native` switch on `Select`: none of\n * `SelectTrigger` / `SelectValue` / `SelectContent` has a native counterpart, so\n * a flag would leave most of the compound API meaning nothing in one mode.\n *\n * `className` styles the `<select>`; `wrapperClassName` the box that holds it\n * and the arrow — that box is what a layout sizes. It is `w-full` like `Input`,\n * so the two line up in a form column; narrow it with `wrapperClassName`.\n */\nexport interface NativeSelectProps\n extends Omit<React.ComponentPropsWithoutRef<\"select\">, \"size\" | \"multiple\"> {\n size?: NativeSelectSize;\n /** An empty, unpickable first option shown until a value is chosen. */\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n// `appearance-none` drops the OS arrow. The popup is drawn by the platform:\n// the option colours reach it where the platform honours them, and `--scheme`\n// (a palette's polarity, `light` or `dark`) picks the platform's own dark\n// menu where it does not. The placeholder is the empty option being the\n// checked one — CSS can see that without script.\n\nexport const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(\n function NativeSelect(\n { className, wrapperClassName, size = \"md\", placeholder, id, children, value, defaultValue, ...props },\n ref,\n ) {\n const controlId = useFieldControlId(id);\n // Without a checked option the browser picks the first enabled one, which\n // would skip a disabled placeholder; start on it unless told otherwise.\n const initial = value === undefined && defaultValue === undefined && placeholder !== undefined ? \"\" : defaultValue;\n return (\n <span\n data-slot=\"native-select-wrapper\"\n className={cn(\"relative inline-flex w-full has-[select:disabled]:opacity-40\", wrapperClassName)}\n >\n <select\n ref={ref}\n id={controlId}\n data-slot=\"native-select\"\n data-size={size}\n className={cn(NATIVE_SELECT_BASE, nativeSelectSizeClasses[size], className)}\n {...(value !== undefined ? { value } : {})}\n {...(initial !== undefined ? { defaultValue: initial } : {})}\n {...props}\n >\n {placeholder !== undefined && (\n <option value=\"\" disabled hidden>\n {placeholder}\n </option>\n )}\n {children}\n </select>\n <SelectChevron\n className={cn(\n \"text-ink-soft pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2\",\n // keeps the arrow as far in from the edge as the large inset keeps the text\n size === \"lg\" && \"right-4\",\n )}\n />\n </span>\n );\n },\n);\n\nmarkLabelable(NativeSelect);\n\nexport function NativeSelectOption(props: React.ComponentProps<\"option\">) {\n return <option data-slot=\"native-select-option\" {...props} />;\n}\n\nexport function NativeSelectGroup(props: React.ComponentProps<\"optgroup\">) {\n return <optgroup data-slot=\"native-select-group\" {...props} />;\n}\n"],"mappings":";AAqDQ,SAWI,KAXJ;AAnDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,oBAAoB,+BAAsD;AACnF,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAgCvB,MAAM,eAAe,MAAM;AAAA,EAChC,SAASA,cACP,EAAE,WAAW,kBAAkB,OAAO,MAAM,aAAa,IAAI,UAAU,OAAO,cAAc,GAAG,MAAM,GACrG,KACA;AACA,UAAM,YAAY,kBAAkB,EAAE;AAGtC,UAAM,UAAU,UAAU,UAAa,iBAAiB,UAAa,gBAAgB,SAAY,KAAK;AACtG,WACE;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,WAAW,GAAG,gEAAgE,gBAAgB;AAAA,QAE9F;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,IAAI;AAAA,cACJ,aAAU;AAAA,cACV,aAAW;AAAA,cACX,WAAW,GAAG,oBAAoB,wBAAwB,IAAI,GAAG,SAAS;AAAA,cACzE,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,cACvC,GAAI,YAAY,SAAY,EAAE,cAAc,QAAQ,IAAI,CAAC;AAAA,cACzD,GAAG;AAAA,cAEH;AAAA,gCAAgB,UACf,oBAAC,YAAO,OAAM,IAAG,UAAQ,MAAC,QAAM,MAC7B,uBACH;AAAA,gBAED;AAAA;AAAA;AAAA,UACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA;AAAA,gBAEA,SAAS,QAAQ;AAAA,cACnB;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,cAAc,YAAY;AAEnB,SAAS,mBAAmB,OAAuC;AACxE,SAAO,oBAAC,YAAO,aAAU,wBAAwB,GAAG,OAAO;AAC7D;AAEO,SAAS,kBAAkB,OAAyC;AACzE,SAAO,oBAAC,cAAS,aAAU,uBAAuB,GAAG,OAAO;AAC9D;","names":["NativeSelect"]}
|