@seekalfred/primitives 0.7.4 → 0.8.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 +255 -3
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +1 -1
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.js +1 -1
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/avatar/avatar.d.ts +1 -1
- package/dist/badge/badge.d.ts +16 -1
- package/dist/badge/badge.js +12 -5
- package/dist/badge/badge.js.map +1 -1
- package/dist/button/button.d.ts +24 -2
- package/dist/button/button.js +62 -13
- package/dist/button/button.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/checkbox/checkbox.d.ts +25 -1
- package/dist/checkbox/checkbox.js +103 -28
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/checkbox/index.d.ts +2 -2
- package/dist/checkbox/index.js +2 -2
- package/dist/code-block/code-block.d.ts +45 -0
- package/dist/code-block/code-block.js +74 -0
- package/dist/code-block/code-block.js.map +1 -0
- package/dist/code-block/index.d.ts +2 -0
- package/dist/code-block/index.js +2 -0
- package/dist/command-palette/command-palette.d.ts +128 -0
- package/dist/command-palette/command-palette.js +371 -0
- package/dist/command-palette/command-palette.js.map +1 -0
- package/dist/command-palette/index.d.ts +2 -0
- package/dist/command-palette/index.js +2 -0
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/empty-state/empty-state.d.ts +17 -0
- package/dist/empty-state/empty-state.js +9 -7
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +12 -8
- package/dist/info-tooltip/index.d.ts +2 -0
- package/dist/info-tooltip/index.js +2 -0
- package/dist/info-tooltip/info-tooltip.d.ts +35 -0
- package/dist/info-tooltip/info-tooltip.js +49 -0
- package/dist/info-tooltip/info-tooltip.js.map +1 -0
- package/dist/input/input.js +1 -1
- package/dist/lib/selection-control.js +32 -1
- package/dist/lib/selection-control.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +2 -1
- package/dist/line-chart/line-chart.js +2 -2
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/mention-input/mention-input.js +1 -1
- package/dist/money-input/money-input.js +1 -1
- package/dist/multi-select/multi-select.js +1 -1
- package/dist/number-field/number-field.js +1 -1
- package/dist/password-field/password-field.js +1 -1
- package/dist/password-strength/password-strength.js +1 -1
- package/dist/phone-input/phone-input.js +1 -1
- package/dist/popover/popover.js +1 -1
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +3 -3
- package/dist/radio-group/radio-group.d.ts +12 -3
- package/dist/radio-group/radio-group.js +62 -52
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/rich-text-editor/rich-text-editor.js +1 -1
- package/dist/scroll-area/scroll-area.d.ts +1 -1
- package/dist/search-field/search-field.js +1 -1
- package/dist/select/select.d.ts +1 -1
- package/dist/select/select.js +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/shortcut-recorder/shortcut-recorder.js +1 -1
- package/dist/stat-card/stat-card.d.ts +2 -2
- package/dist/styles.css +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +99 -1
- package/dist/table/table.js +123 -23
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.d.ts +4 -2
- package/dist/tabs/tabs.js +7 -5
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/tag-input.js +1 -1
- package/dist/textarea/textarea.js +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/toggle-button/index.d.ts +2 -0
- package/dist/toggle-button/index.js +2 -0
- package/dist/toggle-button/toggle-button.d.ts +93 -0
- package/dist/toggle-button/toggle-button.js +199 -0
- package/dist/toggle-button/toggle-button.js.map +1 -0
- package/package.json +19 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/cn.js";
|
|
3
|
-
import { selectionLabelVariants, selectionRingVariants } from "../lib/selection-control.js";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { selectionCardVariants, selectionLabelVariants, selectionRingVariants } from "../lib/selection-control.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
|
-
import { forwardRef } from "react";
|
|
5
|
+
import { forwardRef, useId } from "react";
|
|
6
6
|
import { Checkbox } from "react-aria-components";
|
|
7
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/checkbox/checkbox.tsx
|
|
@@ -42,7 +42,8 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
42
42
|
* legacy pair that styled one surface twice and silently drifted.
|
|
43
43
|
*/
|
|
44
44
|
const checkboxVariants = cva([selectionLabelVariants()]);
|
|
45
|
-
|
|
45
|
+
/** The square, shared by both variants. Only the default one rings the box itself: a card shows focus on its own border. */
|
|
46
|
+
const checkboxBox = [
|
|
46
47
|
"inline-flex size-4.5 shrink-0 items-center justify-center",
|
|
47
48
|
"rounded-checkbox border-(length:--border-width)",
|
|
48
49
|
"box-border",
|
|
@@ -50,37 +51,111 @@ const checkboxBoxVariants = cva([
|
|
|
50
51
|
"checkbox-box",
|
|
51
52
|
"group-data-[selected]:border-primary-strong",
|
|
52
53
|
"group-data-[indeterminate]:border-primary-strong",
|
|
53
|
-
"group-data-[hovered]:not-group-data-[selected]:not-group-data-[indeterminate]:border-muted-foreground"
|
|
54
|
-
|
|
55
|
-
]);
|
|
56
|
-
|
|
54
|
+
"group-data-[hovered]:not-group-data-[selected]:not-group-data-[indeterminate]:border-muted-foreground"
|
|
55
|
+
];
|
|
56
|
+
const checkboxBoxVariants = cva([...checkboxBox, selectionRingVariants()]);
|
|
57
|
+
/** A card's checkbox sits beside the title, aligned to its first line. */
|
|
58
|
+
const checkboxCardBoxVariants = cva([...checkboxBox, "mt-0.5"]);
|
|
59
|
+
const checkboxCardVariants = selectionCardVariants;
|
|
60
|
+
/** The box's fill and mark. One mark, always rendered: the state decides what it shows. */
|
|
61
|
+
function CheckboxMark({ isSelected, isIndeterminate }) {
|
|
62
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
63
|
+
"data-slot": "checkbox-fill",
|
|
64
|
+
className: "checkbox-fill"
|
|
65
|
+
}), /* @__PURE__ */ jsx("svg", {
|
|
66
|
+
"data-slot": "checkbox-mark",
|
|
67
|
+
"data-mark": isIndeterminate ? "indeterminate" : isSelected ? "check" : void 0,
|
|
68
|
+
viewBox: "0 0 18 18",
|
|
69
|
+
fill: "none",
|
|
70
|
+
className: "absolute -inset-px size-4.5 overflow-visible",
|
|
71
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
72
|
+
className: "checkbox-mark",
|
|
73
|
+
pathLength: "1",
|
|
74
|
+
stroke: "currentColor",
|
|
75
|
+
strokeWidth: "2",
|
|
76
|
+
strokeLinecap: "round",
|
|
77
|
+
strokeLinejoin: "round"
|
|
78
|
+
})
|
|
79
|
+
})] });
|
|
80
|
+
}
|
|
81
|
+
const Checkbox$1 = forwardRef(function Checkbox(props, ref) {
|
|
82
|
+
return props.variant === "card" ? /* @__PURE__ */ jsx(CheckboxCard, {
|
|
83
|
+
...props,
|
|
84
|
+
ref
|
|
85
|
+
}) : /* @__PURE__ */ jsx(CheckboxDefault, {
|
|
86
|
+
...props,
|
|
87
|
+
ref
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
const CheckboxCard = forwardRef(function CheckboxCard({ className, children, description, icon, media, variant: _variant, ...props }, ref) {
|
|
91
|
+
const id = useId();
|
|
92
|
+
const titleId = `${id}-title`;
|
|
93
|
+
const descriptionId = `${id}-description`;
|
|
94
|
+
return /* @__PURE__ */ jsx(Checkbox, {
|
|
95
|
+
"aria-labelledby": children == null || props["aria-label"] != null ? void 0 : titleId,
|
|
96
|
+
"aria-describedby": description == null ? void 0 : descriptionId,
|
|
97
|
+
...props,
|
|
98
|
+
inputRef: ref,
|
|
99
|
+
"data-slot": "checkbox",
|
|
100
|
+
"data-variant": "card",
|
|
101
|
+
className: cn(cnAll(checkboxCardVariants(), media != null && "flex-col items-stretch"), className),
|
|
102
|
+
children: ({ isSelected, isIndeterminate }) => {
|
|
103
|
+
const row = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
104
|
+
icon == null ? null : /* @__PURE__ */ jsx("span", {
|
|
105
|
+
"aria-hidden": "true",
|
|
106
|
+
"data-slot": "checkbox-icon",
|
|
107
|
+
className: "mt-0.5 inline-flex shrink-0 text-text-secondary [&_svg]:size-4",
|
|
108
|
+
children: icon
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ jsxs("span", {
|
|
111
|
+
className: "flex min-w-0 flex-1 flex-col gap-1",
|
|
112
|
+
children: [children == null ? null : /* @__PURE__ */ jsx("span", {
|
|
113
|
+
id: titleId,
|
|
114
|
+
"data-slot": "checkbox-label",
|
|
115
|
+
className: "font-medium",
|
|
116
|
+
children
|
|
117
|
+
}), description == null ? null : /* @__PURE__ */ jsx("span", {
|
|
118
|
+
id: descriptionId,
|
|
119
|
+
"data-slot": "checkbox-description",
|
|
120
|
+
className: "text-text-secondary",
|
|
121
|
+
children: description
|
|
122
|
+
})]
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ jsx("span", {
|
|
125
|
+
"aria-hidden": "true",
|
|
126
|
+
"data-slot": "checkbox-box",
|
|
127
|
+
className: checkboxCardBoxVariants(),
|
|
128
|
+
children: /* @__PURE__ */ jsx(CheckboxMark, {
|
|
129
|
+
isSelected,
|
|
130
|
+
isIndeterminate
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
] });
|
|
134
|
+
return media == null ? row : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
135
|
+
"data-slot": "checkbox-media",
|
|
136
|
+
className: "block w-full min-w-0",
|
|
137
|
+
children: media
|
|
138
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
139
|
+
className: "flex w-full items-start gap-3",
|
|
140
|
+
children: row
|
|
141
|
+
})] });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
const CheckboxDefault = forwardRef(function CheckboxDefault({ className, children, variant: _variant, description: _description, icon: _icon, media: _media, ...props }, ref) {
|
|
57
146
|
return /* @__PURE__ */ jsx(Checkbox, {
|
|
58
147
|
...props,
|
|
59
148
|
inputRef: ref,
|
|
60
149
|
"data-slot": "checkbox",
|
|
61
150
|
className: cn(checkboxVariants(), className),
|
|
62
|
-
children: ({ isSelected, isIndeterminate }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */
|
|
151
|
+
children: ({ isSelected, isIndeterminate }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
63
152
|
"aria-hidden": "true",
|
|
64
153
|
"data-slot": "checkbox-box",
|
|
65
154
|
className: checkboxBoxVariants(),
|
|
66
|
-
children:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
70
|
-
"data-slot": "checkbox-mark",
|
|
71
|
-
"data-mark": isIndeterminate ? "indeterminate" : isSelected ? "check" : void 0,
|
|
72
|
-
viewBox: "0 0 18 18",
|
|
73
|
-
fill: "none",
|
|
74
|
-
className: "absolute -inset-px size-4.5 overflow-visible",
|
|
75
|
-
children: /* @__PURE__ */ jsx("path", {
|
|
76
|
-
className: "checkbox-mark",
|
|
77
|
-
pathLength: "1",
|
|
78
|
-
stroke: "currentColor",
|
|
79
|
-
strokeWidth: "2",
|
|
80
|
-
strokeLinecap: "round",
|
|
81
|
-
strokeLinejoin: "round"
|
|
82
|
-
})
|
|
83
|
-
})]
|
|
155
|
+
children: /* @__PURE__ */ jsx(CheckboxMark, {
|
|
156
|
+
isSelected,
|
|
157
|
+
isIndeterminate
|
|
158
|
+
})
|
|
84
159
|
}), children == null ? null : /* @__PURE__ */ jsx("span", {
|
|
85
160
|
"data-slot": "checkbox-label",
|
|
86
161
|
children
|
|
@@ -88,6 +163,6 @@ const Checkbox$1 = forwardRef(function Checkbox$2({ className, children, ...prop
|
|
|
88
163
|
});
|
|
89
164
|
});
|
|
90
165
|
//#endregion
|
|
91
|
-
export { Checkbox$1 as Checkbox, checkboxBoxVariants, checkboxVariants };
|
|
166
|
+
export { Checkbox$1 as Checkbox, checkboxBoxVariants, checkboxCardVariants, checkboxVariants };
|
|
92
167
|
|
|
93
168
|
//# sourceMappingURL=checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.js","names":["Checkbox","AriaCheckbox"],"sources":["../../src/checkbox/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Checkbox as AriaCheckbox,\n type CheckboxProps as AriaCheckboxProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * Checkbox, rebuilt on React Aria.\n *\n * THE CENSUS FOUND ZERO CALL SITES. It is ported anyway because forms and\n * table row selection are unbuildable without it, but that fact changes what\n * this is: with no usage to be faithful to and no call site to break, it is a\n * rewrite rather than a restyling. Four things the legacy component got\n * wrong are therefore simply fixed rather than carried:\n *\n * - **No accessible name.** It rendered a bare `<input type=\"checkbox\">` with\n * no label, no `aria-label` and no `id`, so every use was an axe `label`\n * violation. Here the label is `children`, and React Aria wraps the input\n * in it.\n * - **The fill never showed.** `checked:bg-primary` on a native input with no\n * `appearance-none` is painted over by the user-agent checkbox.\n * - **`border-primary` cannot be a boundary.** Brand orange is 2.44:1 on\n * white, under even the 3:1 non-text floor (WCAG 1.4.11).\n * - **`focus-visible:ring-2` is a box-shadow**, which forced-colors discards,\n * taking the only focus indication with it.\n *\n * React Aria renders a `<label>` around a visually-hidden real `<input>` and\n * NOTHING else -- there is no visual box in the box. Drawing one, and keeping\n * it in step with `data-selected` / `data-indeterminate`, is what this\n * component owns.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\n/**\n * The label geometry now lives in lib/selection-control.ts, shared with\n * Radio and Switch. Three controls with the same 24px target and the same\n * typography is the Rule of Three, and this migration has twice found a\n * legacy pair that styled one surface twice and silently drifted.\n */\nexport const checkboxVariants = cva([selectionLabelVariants()]);\n\nexport const checkboxBoxVariants = cva([\n \"inline-flex size-4.5 shrink-0 items-center justify-center\",\n \"rounded-checkbox border-(length:--border-width)\",\n\n // box-border: the size is the whole square, border included.\n \"box-border\",\n\n \"bg-surface border-control-edge text-primary-foreground\",\n\n // Border colour eases, the square presses to .95 and springs back (see checkbox-box).\n \"checkbox-box\",\n\n // The border takes the strong tone once chosen; the colour itself is the fill below.\n \"group-data-[selected]:border-primary-strong\",\n \"group-data-[indeterminate]:border-primary-strong\",\n // Hover darkens an unchosen box's border.\n \"group-data-[hovered]:not-group-data-[selected]:not-group-data-[indeterminate]:border-muted-foreground\",\n\n selectionRingVariants(),\n]);\n\nexport interface CheckboxProps\n extends Omit<AriaCheckboxProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The label, and the control's accessible name.\n *\n * Omit it only for a genuinely label-less checkbox -- a table row selector\n * -- and pass `aria-label` instead. A checkbox with neither has no\n * accessible name, which is what every legacy call site would have been.\n */\n children?: ReactNode;\n /** Extra classes for the label. Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Checkbox(\n { className, children, ...props },\n ref,\n) {\n return (\n <AriaCheckbox\n {...props}\n // inputRef, not ref: AriaCheckbox's own ref targets the label it\n // renders around a visually-hidden native input, and a label cannot\n // be focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"checkbox\"\n // `group` is load-bearing: React Aria puts data-selected,\n // data-indeterminate and data-focus-visible on the LABEL, and CSS\n // cannot look upward from the box to them.\n className={cn(checkboxVariants(), className)}\n >\n {({ isSelected, isIndeterminate }) => (\n <>\n {/*\n aria-hidden: the real <input> already carries the state. A\n decorative box announced as well would say everything twice.\n */}\n <span aria-hidden=\"true\" data-slot=\"checkbox-box\" className={checkboxBoxVariants()}>\n <span data-slot=\"checkbox-fill\" className=\"checkbox-fill\" />\n {/*\n One mark, always rendered: the state decides what it shows. Both\n states can be true at once (a \"select all\" box that has been\n partially cleared) and indeterminate wins, which the path's\n `d` carries in CSS -- the same precedence the old two-glyph\n branch pinned in JS.\n */}\n <svg\n data-slot=\"checkbox-mark\"\n data-mark={isIndeterminate ? \"indeterminate\" : isSelected ? \"check\" : undefined}\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n className=\"absolute -inset-px size-4.5 overflow-visible\"\n >\n <path\n className=\"checkbox-mark\"\n pathLength=\"1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n {children == null ? null : <span data-slot=\"checkbox-label\">{children}</span>}\n </>\n )}\n </AriaCheckbox>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAa,mBAAmB,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAE9D,MAAa,sBAAsB,IAAI;CACrC;CACA;CAGA;CAEA;CAGA;CAGA;CACA;CAEA;CAEA,sBAAsB;AACxB,CAAC;AAgBD,MAAaA,aAAW,WAA4C,SAASA,WAC3E,EAAE,WAAW,UAAU,GAAG,SAC1B,KACA;CACA,OACE,oBAACC,UAAD;EACE,GAAI;EAKJ,UAAU;EACV,aAAU;EAIV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,sBACd,qBAAA,YAAA,EAAA,UAAA,CAKE,qBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAe,WAAW,oBAAoB;GAAjF,UAAA,CACE,oBAAC,QAAD;IAAM,aAAU;IAAgB,WAAU;GAAiB,CAAA,GAQ3D,oBAAC,OAAD;IACE,aAAU;IACV,aAAW,kBAAkB,kBAAkB,aAAa,UAAU,KAAA;IACtE,SAAQ;IACR,MAAK;IACL,WAAU;IAEV,UAAA,oBAAC,QAAD;KACE,WAAU;KACV,YAAW;KACX,QAAO;KACP,aAAY;KACZ,eAAc;KACd,gBAAe;IAChB,CAAA;GACE,CAAA,CACD;EACL,CAAA,GAAA,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAkB;EAAe,CAAA,CAC5E,EAAA,CAAA;CAEQ,CAAA;AAElB,CAAC"}
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":["Checkbox","AriaCheckbox"],"sources":["../../src/checkbox/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useId, type ReactNode } from \"react\";\nimport {\n Checkbox as AriaCheckbox,\n type CheckboxProps as AriaCheckboxProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport {\n selectionCardVariants,\n selectionLabelVariants,\n selectionRingVariants,\n} from \"../lib/selection-control.js\";\n\n/**\n * Checkbox, rebuilt on React Aria.\n *\n * THE CENSUS FOUND ZERO CALL SITES. It is ported anyway because forms and\n * table row selection are unbuildable without it, but that fact changes what\n * this is: with no usage to be faithful to and no call site to break, it is a\n * rewrite rather than a restyling. Four things the legacy component got\n * wrong are therefore simply fixed rather than carried:\n *\n * - **No accessible name.** It rendered a bare `<input type=\"checkbox\">` with\n * no label, no `aria-label` and no `id`, so every use was an axe `label`\n * violation. Here the label is `children`, and React Aria wraps the input\n * in it.\n * - **The fill never showed.** `checked:bg-primary` on a native input with no\n * `appearance-none` is painted over by the user-agent checkbox.\n * - **`border-primary` cannot be a boundary.** Brand orange is 2.44:1 on\n * white, under even the 3:1 non-text floor (WCAG 1.4.11).\n * - **`focus-visible:ring-2` is a box-shadow**, which forced-colors discards,\n * taking the only focus indication with it.\n *\n * React Aria renders a `<label>` around a visually-hidden real `<input>` and\n * NOTHING else -- there is no visual box in the box. Drawing one, and keeping\n * it in step with `data-selected` / `data-indeterminate`, is what this\n * component owns.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\n/**\n * The label geometry now lives in lib/selection-control.ts, shared with\n * Radio and Switch. Three controls with the same 24px target and the same\n * typography is the Rule of Three, and this migration has twice found a\n * legacy pair that styled one surface twice and silently drifted.\n */\nexport const checkboxVariants = cva([selectionLabelVariants()]);\n\n/** The square, shared by both variants. Only the default one rings the box itself: a card shows focus on its own border. */\nconst checkboxBox = [\n \"inline-flex size-4.5 shrink-0 items-center justify-center\",\n \"rounded-checkbox border-(length:--border-width)\",\n\n // box-border: the size is the whole square, border included.\n \"box-border\",\n\n \"bg-surface border-control-edge text-primary-foreground\",\n\n // Border colour eases, the square presses to .95 and springs back (see checkbox-box).\n \"checkbox-box\",\n\n // The border takes the strong tone once chosen; the colour itself is the fill below.\n \"group-data-[selected]:border-primary-strong\",\n \"group-data-[indeterminate]:border-primary-strong\",\n // Hover darkens an unchosen box's border.\n \"group-data-[hovered]:not-group-data-[selected]:not-group-data-[indeterminate]:border-muted-foreground\",\n];\n\nexport const checkboxBoxVariants = cva([...checkboxBox, selectionRingVariants()]);\n\n/** A card's checkbox sits beside the title, aligned to its first line. */\nconst checkboxCardBoxVariants = cva([...checkboxBox, \"mt-0.5\"]);\n\nexport const checkboxCardVariants = selectionCardVariants;\n\nexport interface CheckboxProps\n extends Omit<AriaCheckboxProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The label, and the control's accessible name.\n *\n * Omit it only for a genuinely label-less checkbox -- a table row selector\n * -- and pass `aria-label` instead. A checkbox with neither has no\n * accessible name, which is what every legacy call site would have been.\n */\n children?: ReactNode;\n /**\n * `card` draws the option as a selectable card: a bordered surface that is\n * all hit target, with a `description`, an `icon` and `media`, and the box at\n * its end. Selected shows a `primary-strong` edge AND the tick, so it never\n * rests on colour alone. Focus is the card's own border turning dark, with no\n * ring. The default variant renders exactly as before.\n *\n * @default \"default\"\n */\n variant?: \"default\" | \"card\";\n /**\n * A line under the title, read as the option's description\n * (`aria-describedby`), not part of its name. Card variant only.\n */\n description?: ReactNode;\n /** A decorative icon before the title. Card variant only; hidden from assistive tech. */\n icon?: ReactNode;\n /**\n * Content above the title, edge to edge in the card: a thumbnail, a preview.\n * Card variant only. Give an `<img>` its own `alt`, or `alt=\"\"` when the\n * title says it all.\n */\n media?: ReactNode;\n /** Extra classes for the label (the whole card in the card variant). Merged last, so they win conflicts. */\n className?: string;\n}\n\n/** The box's fill and mark. One mark, always rendered: the state decides what it shows. */\nfunction CheckboxMark({ isSelected, isIndeterminate }: { isSelected: boolean; isIndeterminate: boolean }) {\n return (\n <>\n <span data-slot=\"checkbox-fill\" className=\"checkbox-fill\" />\n {/*\n Both states can be true at once (a \"select all\" box that has been\n partially cleared) and indeterminate wins, which the path's `d` carries\n in CSS -- the same precedence the old two-glyph branch pinned in JS.\n */}\n <svg\n data-slot=\"checkbox-mark\"\n data-mark={isIndeterminate ? \"indeterminate\" : isSelected ? \"check\" : undefined}\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n className=\"absolute -inset-px size-4.5 overflow-visible\"\n >\n <path\n className=\"checkbox-mark\"\n pathLength=\"1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </>\n );\n}\n\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Checkbox(props, ref) {\n return props.variant === \"card\" ? <CheckboxCard {...props} ref={ref} /> : <CheckboxDefault {...props} ref={ref} />;\n});\n\nconst CheckboxCard = forwardRef<HTMLInputElement, CheckboxProps>(function CheckboxCard(\n { className, children, description, icon, media, variant: _variant, ...props },\n ref,\n) {\n const id = useId();\n const titleId = `${id}-title`;\n const descriptionId = `${id}-description`;\n return (\n <AriaCheckbox\n // The title alone names the option; the description describes it. Left to\n // the wrapping <label>, the name would be title and description run\n // together. A caller's own labelling wins, as on Radio.\n aria-labelledby={children == null || props[\"aria-label\"] != null ? undefined : titleId}\n aria-describedby={description == null ? undefined : descriptionId}\n {...props}\n inputRef={ref}\n data-slot=\"checkbox\"\n data-variant=\"card\"\n className={cn(cnAll(checkboxCardVariants(), media != null && \"flex-col items-stretch\"), className)}\n >\n {({ isSelected, isIndeterminate }) => {\n const row = (\n <>\n {icon == null ? null : (\n <span\n aria-hidden=\"true\"\n data-slot=\"checkbox-icon\"\n className=\"mt-0.5 inline-flex shrink-0 text-text-secondary [&_svg]:size-4\"\n >\n {icon}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {children == null ? null : (\n <span id={titleId} data-slot=\"checkbox-label\" className=\"font-medium\">\n {children}\n </span>\n )}\n {description == null ? null : (\n <span id={descriptionId} data-slot=\"checkbox-description\" className=\"text-text-secondary\">\n {description}\n </span>\n )}\n </span>\n <span aria-hidden=\"true\" data-slot=\"checkbox-box\" className={checkboxCardBoxVariants()}>\n <CheckboxMark isSelected={isSelected} isIndeterminate={isIndeterminate} />\n </span>\n </>\n );\n return media == null ? (\n row\n ) : (\n <>\n <span data-slot=\"checkbox-media\" className=\"block w-full min-w-0\">\n {media}\n </span>\n <span className=\"flex w-full items-start gap-3\">{row}</span>\n </>\n );\n }}\n </AriaCheckbox>\n );\n});\n\nconst CheckboxDefault = forwardRef<HTMLInputElement, CheckboxProps>(function CheckboxDefault(\n // description, icon and media belong to the card; pulled out so they never reach the DOM.\n { className, children, variant: _variant, description: _description, icon: _icon, media: _media, ...props },\n ref,\n) {\n return (\n <AriaCheckbox\n {...props}\n // inputRef, not ref: AriaCheckbox's own ref targets the label it\n // renders around a visually-hidden native input, and a label cannot\n // be focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"checkbox\"\n // `group` is load-bearing: React Aria puts data-selected,\n // data-indeterminate and data-focus-visible on the LABEL, and CSS\n // cannot look upward from the box to them.\n className={cn(checkboxVariants(), className)}\n >\n {({ isSelected, isIndeterminate }) => (\n <>\n {/*\n aria-hidden: the real <input> already carries the state. A\n decorative box announced as well would say everything twice.\n */}\n <span aria-hidden=\"true\" data-slot=\"checkbox-box\" className={checkboxBoxVariants()}>\n <CheckboxMark isSelected={isSelected} isIndeterminate={isIndeterminate} />\n </span>\n {children == null ? null : <span data-slot=\"checkbox-label\">{children}</span>}\n </>\n )}\n </AriaCheckbox>\n );\n});\n"],"mappings":"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"}
|
package/dist/checkbox/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Checkbox, CheckboxProps, checkboxBoxVariants, checkboxVariants } from "./checkbox.js";
|
|
2
|
-
export { Checkbox, type CheckboxProps, checkboxBoxVariants, checkboxVariants };
|
|
1
|
+
import { Checkbox, CheckboxProps, checkboxBoxVariants, checkboxCardVariants, checkboxVariants } from "./checkbox.js";
|
|
2
|
+
export { Checkbox, type CheckboxProps, checkboxBoxVariants, checkboxCardVariants, checkboxVariants };
|
package/dist/checkbox/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Checkbox, checkboxBoxVariants, checkboxVariants } from "./checkbox.js";
|
|
2
|
-
export { Checkbox, checkboxBoxVariants, checkboxVariants };
|
|
1
|
+
import { Checkbox, checkboxBoxVariants, checkboxCardVariants, checkboxVariants } from "./checkbox.js";
|
|
2
|
+
export { Checkbox, checkboxBoxVariants, checkboxCardVariants, checkboxVariants };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
//#region src/code-block/code-block.d.ts
|
|
3
|
+
export declare const codeBlockVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
4
|
+
export interface CodeBlockProps extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
|
|
5
|
+
/** The code. It is what is shown (unless `children` is given) and what goes on the clipboard. */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Show this instead of `value` (for highlighted markup). `value` is still what is copied. */
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Break long lines instead of scrolling sideways. Off by default: a line of code
|
|
11
|
+
* scrolls, and the scroll area takes keyboard focus (only while it actually
|
|
12
|
+
* overflows) so it can be read.
|
|
13
|
+
*
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
wrap?: boolean;
|
|
17
|
+
/** The visible label of the copy button. @default "Copy" */
|
|
18
|
+
copyLabel?: string;
|
|
19
|
+
/** The copy button's accessible name; keep `copyLabel` inside it. @default "Copy code" */
|
|
20
|
+
copyAriaLabel?: string;
|
|
21
|
+
/** Only the icon on the copy button. @default false */
|
|
22
|
+
iconOnly?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Names the code group for assistive technology. It is a `group`, not a landmark,
|
|
25
|
+
* so several blocks on one page do not each add a region to the landmark list.
|
|
26
|
+
* Give each a distinct label when it helps ("Install command").
|
|
27
|
+
*
|
|
28
|
+
* @default "Code"
|
|
29
|
+
*/
|
|
30
|
+
codeLabel?: string;
|
|
31
|
+
/** Called once the code has reached the clipboard. */
|
|
32
|
+
onCopied?: () => void;
|
|
33
|
+
/** Called when the clipboard refused the text. */
|
|
34
|
+
onCopyError?: (error: unknown) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A snippet of code with a copy button, built on CopyButton (which owns the
|
|
38
|
+
* copy, its feedback and its live-region announcement).
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* <CodeBlock value="npm install @seekalfred/primitives" onCopied={() => toast("Copied")} />
|
|
42
|
+
*/
|
|
43
|
+
export declare const CodeBlock: import("react").ForwardRefExoticComponent<CodeBlockProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
44
|
+
//#endregion
|
|
45
|
+
//# sourceMappingURL=code-block.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { CopyButton } from "../copy-button/copy-button.js";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { forwardRef, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/code-block/code-block.tsx
|
|
8
|
+
const codeBlockVariants = cva([
|
|
9
|
+
"flex items-start justify-between gap-2",
|
|
10
|
+
"rounded-control bg-muted px-3 py-2 box-border",
|
|
11
|
+
"font-sans text-foreground"
|
|
12
|
+
]);
|
|
13
|
+
/**
|
|
14
|
+
* A snippet of code with a copy button, built on CopyButton (which owns the
|
|
15
|
+
* copy, its feedback and its live-region announcement).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <CodeBlock value="npm install @seekalfred/primitives" onCopied={() => toast("Copied")} />
|
|
19
|
+
*/
|
|
20
|
+
const CodeBlock = forwardRef(function CodeBlock({ value, children, wrap = false, copyLabel = "Copy", copyAriaLabel = "Copy code", iconOnly = false, codeLabel = "Code", onCopied, onCopyError, className, ...props }, ref) {
|
|
21
|
+
const preRef = useRef(null);
|
|
22
|
+
const [scrolls, setScrolls] = useState(false);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const pre = preRef.current;
|
|
25
|
+
if (wrap || !pre) {
|
|
26
|
+
setScrolls(false);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const measure = () => setScrolls(pre.scrollWidth > pre.clientWidth);
|
|
30
|
+
measure();
|
|
31
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
32
|
+
const observer = new ResizeObserver(measure);
|
|
33
|
+
observer.observe(pre);
|
|
34
|
+
if (pre.firstElementChild) observer.observe(pre.firstElementChild);
|
|
35
|
+
return () => observer.disconnect();
|
|
36
|
+
}, [
|
|
37
|
+
wrap,
|
|
38
|
+
value,
|
|
39
|
+
children
|
|
40
|
+
]);
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
ref,
|
|
43
|
+
...props,
|
|
44
|
+
"data-slot": "code-block",
|
|
45
|
+
"data-wrap": wrap || void 0,
|
|
46
|
+
className: cn(codeBlockVariants(), className),
|
|
47
|
+
children: [/* @__PURE__ */ jsx("pre", {
|
|
48
|
+
ref: preRef,
|
|
49
|
+
"data-slot": "code-block-pre",
|
|
50
|
+
...wrap ? {} : {
|
|
51
|
+
role: "group",
|
|
52
|
+
"aria-label": codeLabel,
|
|
53
|
+
tabIndex: scrolls ? 0 : void 0
|
|
54
|
+
},
|
|
55
|
+
className: cnAll("m-0 min-w-0 flex-1 font-mono text-sm leading-(--leading-normal)", wrap ? "whitespace-pre-wrap break-all" : "overflow-x-auto outline-none focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-(--color-ring)"),
|
|
56
|
+
children: /* @__PURE__ */ jsx("code", {
|
|
57
|
+
"data-slot": "code-block-code",
|
|
58
|
+
children: children ?? value
|
|
59
|
+
})
|
|
60
|
+
}), /* @__PURE__ */ jsx(CopyButton, {
|
|
61
|
+
value,
|
|
62
|
+
variant: "plain",
|
|
63
|
+
iconOnly,
|
|
64
|
+
"aria-label": copyAriaLabel,
|
|
65
|
+
onCopied,
|
|
66
|
+
onError: onCopyError,
|
|
67
|
+
children: copyLabel
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
//#endregion
|
|
72
|
+
export { CodeBlock, codeBlockVariants };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.js","names":[],"sources":["../../src/code-block/code-block.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useEffect, useRef, useState, type ComponentPropsWithoutRef, type ReactNode } from \"react\";\n\nimport { CopyButton } from \"../copy-button/copy-button.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\n\nexport const codeBlockVariants = cva([\n \"flex items-start justify-between gap-2\",\n \"rounded-control bg-muted px-3 py-2 box-border\",\n \"font-sans text-foreground\",\n]);\n\nexport interface CodeBlockProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /** The code. It is what is shown (unless `children` is given) and what goes on the clipboard. */\n value: string;\n /** Show this instead of `value` (for highlighted markup). `value` is still what is copied. */\n children?: ReactNode;\n /**\n * Break long lines instead of scrolling sideways. Off by default: a line of code\n * scrolls, and the scroll area takes keyboard focus (only while it actually\n * overflows) so it can be read.\n *\n * @default false\n */\n wrap?: boolean;\n /** The visible label of the copy button. @default \"Copy\" */\n copyLabel?: string;\n /** The copy button's accessible name; keep `copyLabel` inside it. @default \"Copy code\" */\n copyAriaLabel?: string;\n /** Only the icon on the copy button. @default false */\n iconOnly?: boolean;\n /**\n * Names the code group for assistive technology. It is a `group`, not a landmark,\n * so several blocks on one page do not each add a region to the landmark list.\n * Give each a distinct label when it helps (\"Install command\").\n *\n * @default \"Code\"\n */\n codeLabel?: string;\n /** Called once the code has reached the clipboard. */\n onCopied?: () => void;\n /** Called when the clipboard refused the text. */\n onCopyError?: (error: unknown) => void;\n}\n\n/**\n * A snippet of code with a copy button, built on CopyButton (which owns the\n * copy, its feedback and its live-region announcement).\n *\n * @example\n * <CodeBlock value=\"npm install @seekalfred/primitives\" onCopied={() => toast(\"Copied\")} />\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n value,\n children,\n wrap = false,\n copyLabel = \"Copy\",\n copyAriaLabel = \"Copy code\",\n iconOnly = false,\n codeLabel = \"Code\",\n onCopied,\n onCopyError,\n className,\n ...props\n },\n ref,\n) {\n const preRef = useRef<HTMLPreElement>(null);\n const [scrolls, setScrolls] = useState(false);\n\n // A scroll area must be a tab stop (axe: scrollable-region-focusable) but a snippet\n // that fits is not one, so a page of short snippets adds no empty tab stops.\n useEffect(() => {\n const pre = preRef.current;\n if (wrap || !pre) {\n setScrolls(false);\n return;\n }\n const measure = () => setScrolls(pre.scrollWidth > pre.clientWidth);\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n // The pre's own box changes with its container; the code's with its content.\n observer.observe(pre);\n if (pre.firstElementChild) observer.observe(pre.firstElementChild);\n return () => observer.disconnect();\n }, [wrap, value, children]);\n\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"code-block\"\n data-wrap={wrap || undefined}\n className={cn(codeBlockVariants(), className)}\n >\n <pre\n ref={preRef}\n data-slot=\"code-block-pre\"\n // Scrolling code must be reachable by keyboard; code that fits or wraps never needs to be.\n {...(wrap ? {} : { role: \"group\", \"aria-label\": codeLabel, tabIndex: scrolls ? 0 : undefined })}\n className={cnAll(\n \"m-0 min-w-0 flex-1 font-mono text-sm leading-(--leading-normal)\",\n wrap\n ? \"whitespace-pre-wrap break-all\"\n : \"overflow-x-auto outline-none focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-(--color-ring)\",\n )}\n >\n <code data-slot=\"code-block-code\">{children ?? value}</code>\n </pre>\n <CopyButton\n value={value}\n variant=\"plain\"\n iconOnly={iconOnly}\n aria-label={copyAriaLabel}\n onCopied={onCopied}\n onError={onCopyError}\n >\n {copyLabel}\n </CopyButton>\n </div>\n );\n});\n"],"mappings":";;;;;;;AAQA,MAAa,oBAAoB,IAAI;CACnC;CACA;CACA;AACF,CAAC;;;;;;;;AA0CD,MAAa,YAAY,WAA2C,SAAS,UAC3E,EACE,OACA,UACA,OAAO,OACP,YAAY,QACZ,gBAAgB,aAChB,WAAW,OACX,YAAY,QACZ,UACA,aACA,WACA,GAAG,SAEL,KACA;CACA,MAAM,SAAS,OAAuB,IAAI;CAC1C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAI5C,gBAAgB;EACd,MAAM,MAAM,OAAO;EACnB,IAAI,QAAQ,CAAC,KAAK;GAChB,WAAW,KAAK;GAChB;EACF;EACA,MAAM,gBAAgB,WAAW,IAAI,cAAc,IAAI,WAAW;EAClE,QAAQ;EACR,IAAI,OAAO,mBAAmB,aAAa;EAC3C,MAAM,WAAW,IAAI,eAAe,OAAO;EAE3C,SAAS,QAAQ,GAAG;EACpB,IAAI,IAAI,mBAAmB,SAAS,QAAQ,IAAI,iBAAiB;EACjE,aAAa,SAAS,WAAW;CACnC,GAAG;EAAC;EAAM;EAAO;CAAQ,CAAC;CAE1B,OACE,qBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,aAAW,QAAQ,KAAA;EACnB,WAAW,GAAG,kBAAkB,GAAG,SAAS;EAL9C,UAAA,CAOE,oBAAC,OAAD;GACE,KAAK;GACL,aAAU;GAEV,GAAK,OAAO,CAAC,IAAI;IAAE,MAAM;IAAS,cAAc;IAAW,UAAU,UAAU,IAAI,KAAA;GAAU;GAC7F,WAAW,MACT,mEACA,OACI,kCACA,iJACN;GAEA,UAAA,oBAAC,QAAD;IAAM,aAAU;IAAmB,UAAA,YAAY;GAAY,CAAA;EACxD,CAAA,GACL,oBAAC,YAAD;GACS;GACP,SAAQ;GACE;GACV,cAAY;GACF;GACV,SAAS;GAER,UAAA;EACS,CAAA,CACT;;AAET,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/command-palette/command-palette.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* CommandPalette: a modal search-and-run list opened with Cmd+K or Ctrl+K.
|
|
5
|
+
*
|
|
6
|
+
* Built on the existing Dialog (React Aria's modal: focus trap, focus return, Escape, scrim,
|
|
7
|
+
* entrance motion that already honours prefers-reduced-motion), Input and ShortcutKeys, so it adds
|
|
8
|
+
* only the combobox + listbox wiring.
|
|
9
|
+
*
|
|
10
|
+
* The pattern is the WAI-ARIA "combobox with listbox popup": DOM focus never leaves the input,
|
|
11
|
+
* and the highlighted option is named by `aria-activedescendant`. Options are not tabbable.
|
|
12
|
+
*
|
|
13
|
+
* Every string in the cva blocks is a STATIC literal (Tailwind scans source text).
|
|
14
|
+
*/
|
|
15
|
+
/** One entry in the palette. */
|
|
16
|
+
export interface CommandPaletteItem {
|
|
17
|
+
/**
|
|
18
|
+
* Stable identity. Unique within `items`. A `recent` entry is a copy of an item, so it uses
|
|
19
|
+
* that item's `id` and `recent` need not be unique against `items`; only each list on its own.
|
|
20
|
+
*/
|
|
21
|
+
id: string;
|
|
22
|
+
/** The text shown, and the main thing the query is matched against. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** The heading it is listed under. Items without one share an unlabelled group. */
|
|
25
|
+
group?: string;
|
|
26
|
+
/** A decorative glyph before the label. Mark it `aria-hidden`; it is sized by the palette. */
|
|
27
|
+
icon?: ReactNode;
|
|
28
|
+
/** A key combination shown as key caps, such as `"mod+shift+p"`. A hint only: it binds nothing. */
|
|
29
|
+
shortcut?: string;
|
|
30
|
+
/** Plain text at the trailing edge, such as "Go to" or "Run". Shown when there is no `shortcut`. */
|
|
31
|
+
hint?: string;
|
|
32
|
+
/** Extra words that also match the query but are not shown. */
|
|
33
|
+
keywords?: readonly string[];
|
|
34
|
+
/** Listed but skipped by the arrow keys and ignored when pressed. @default false */
|
|
35
|
+
isDisabled?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare const commandPaletteListVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
38
|
+
export declare const commandPaletteOptionVariants: (props?: ({} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
39
|
+
export interface CommandPaletteShortcutOptions {
|
|
40
|
+
/**
|
|
41
|
+
* The chord. `mod` matches Command or Control on any platform, so the default opens on both
|
|
42
|
+
* Cmd+K and Ctrl+K. Other modifiers: `ctrl`, `meta`, `alt`, `shift`. Modifiers you do not name
|
|
43
|
+
* must not be held.
|
|
44
|
+
*
|
|
45
|
+
* The last part is the key. Supported: a letter (`k`), a digit (`1`, also with `shift`), `space`,
|
|
46
|
+
* and any other name equal to `KeyboardEvent.key` such as `escape`, `enter`, `/`. A letter is
|
|
47
|
+
* read from what the key types, so it follows Dvorak and similar layouts. On a non-Latin layout
|
|
48
|
+
* it falls back to the physical key.
|
|
49
|
+
* @default "mod+k"
|
|
50
|
+
*/
|
|
51
|
+
shortcut?: string;
|
|
52
|
+
/** Turn the listener off. @default true */
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
}
|
|
55
|
+
type KeyLike = Pick<KeyboardEvent, "key" | "code" | "ctrlKey" | "metaKey" | "altKey" | "shiftKey">;
|
|
56
|
+
/** True when a keydown is this chord. `mod` is either Command or Control (not both required). */
|
|
57
|
+
export declare function matchesCommandPaletteShortcut(event: KeyLike, shortcut: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Calls `onTrigger` when the chord is pressed anywhere in the document, and prevents the
|
|
60
|
+
* browser's own use of it (Ctrl+K focuses the address bar in some browsers).
|
|
61
|
+
*
|
|
62
|
+
* `CommandPalette` installs this itself, so use the hook only to open a palette from a
|
|
63
|
+
* different chord than the one the palette listens for, or to drive one you render conditionally.
|
|
64
|
+
*/
|
|
65
|
+
export declare function useCommandPaletteShortcut(onTrigger: () => void, { shortcut, enabled }?: CommandPaletteShortcutOptions): void;
|
|
66
|
+
export interface CommandPaletteProps {
|
|
67
|
+
/** Everything that can be run or opened. */
|
|
68
|
+
items: readonly CommandPaletteItem[];
|
|
69
|
+
/**
|
|
70
|
+
* A group of recent or suggested items, listed first while the search box is empty. It
|
|
71
|
+
* disappears once the user types, when the search runs over `items` alone.
|
|
72
|
+
*/
|
|
73
|
+
recent?: readonly CommandPaletteItem[];
|
|
74
|
+
/** Heading of the `recent` group. @default "Recent" */
|
|
75
|
+
recentLabel?: string;
|
|
76
|
+
/** Called with the chosen item, after the palette has asked to close. */
|
|
77
|
+
onSelect?: (item: CommandPaletteItem) => void;
|
|
78
|
+
/** Whether it is open. Pass with `onOpenChange` to control it. */
|
|
79
|
+
isOpen?: boolean;
|
|
80
|
+
/** Whether it starts open when uncontrolled. @default false */
|
|
81
|
+
defaultOpen?: boolean;
|
|
82
|
+
/** Called when it wants to open or close: the shortcut, Escape, the scrim, a selection. */
|
|
83
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
84
|
+
/**
|
|
85
|
+
* The chord that toggles it, installed on the document while the component is mounted.
|
|
86
|
+
* `false` removes the listener, for an app that binds it elsewhere (see
|
|
87
|
+
* `useCommandPaletteShortcut`). `mod` is Command or Control.
|
|
88
|
+
* @default "mod+k"
|
|
89
|
+
*/
|
|
90
|
+
shortcut?: string | false;
|
|
91
|
+
/** The dialog's accessible name. A prop so it can be translated. @default "Search and commands" */
|
|
92
|
+
label?: string;
|
|
93
|
+
/** Accessible name of the search box. @default "Search" */
|
|
94
|
+
searchLabel?: string;
|
|
95
|
+
/** Placeholder of the search box. @default "Search pages and actions" */
|
|
96
|
+
placeholder?: string;
|
|
97
|
+
/** Replaces the default filter. Receives the item and the trimmed query; the query is never empty. */
|
|
98
|
+
filter?: (item: CommandPaletteItem, query: string) => boolean;
|
|
99
|
+
/** What to show when nothing matches. @default ``No matches for “query”.`` */
|
|
100
|
+
emptyMessage?: ReactNode | ((query: string) => ReactNode);
|
|
101
|
+
/** The polite result-count announcement. @default "1 result" / "n results" / "No results" */
|
|
102
|
+
resultsLabel?: (count: number) => string;
|
|
103
|
+
/**
|
|
104
|
+
* The strip of key hints under the list. `undefined` shows the default ("↑ ↓ to move",
|
|
105
|
+
* "Enter to open"); `null` removes it; anything else replaces it, which is how to translate it.
|
|
106
|
+
*/
|
|
107
|
+
footer?: ReactNode;
|
|
108
|
+
/** Extra classes for the panel. Merged last. */
|
|
109
|
+
className?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A command palette.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const [open, setOpen] = useState(false);
|
|
116
|
+
* <CommandPalette
|
|
117
|
+
* items={[
|
|
118
|
+
* { id: "home", group: "Pages", label: "Home", hint: "Go to" },
|
|
119
|
+
* { id: "theme", group: "Actions", label: "Switch theme", keywords: ["dark"], shortcut: "mod+shift+l" },
|
|
120
|
+
* ]}
|
|
121
|
+
* isOpen={open}
|
|
122
|
+
* onOpenChange={setOpen}
|
|
123
|
+
* onSelect={(item) => run(item.id)}
|
|
124
|
+
* />
|
|
125
|
+
*/
|
|
126
|
+
export declare function CommandPalette({ items, recent, recentLabel, onSelect, isOpen: isOpenProp, defaultOpen, onOpenChange, shortcut, label, searchLabel, placeholder, filter, emptyMessage, resultsLabel, footer, className }: CommandPaletteProps): import("react").JSX.Element;
|
|
127
|
+
//#endregion
|
|
128
|
+
//# sourceMappingURL=command-palette.d.ts.map
|