@code-sherpas/pharos-react 0.19.0 → 0.20.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/dist/components/Avatar.js +138 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/Avatar.module.js +15 -0
- package/dist/components/Avatar.module.js.map +1 -0
- package/dist/components/Badge.js +30 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Badge.module.js +14 -0
- package/dist/components/Badge.module.js.map +1 -0
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Button.js +49 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Button.module.js +14 -0
- package/dist/components/Button.module.js.map +1 -0
- package/dist/components/Card.js +66 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Card.module.js +15 -0
- package/dist/components/Card.module.js.map +1 -0
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +47 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/Checkbox.module.js +11 -0
- package/dist/components/Checkbox.module.js.map +1 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +212 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/Combobox.module.js +25 -0
- package/dist/components/Combobox.module.js.map +1 -0
- package/dist/components/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog.js +66 -0
- package/dist/components/Dialog.js.map +1 -0
- package/dist/components/Dialog.module.js +12 -0
- package/dist/components/Dialog.module.js.map +1 -0
- package/dist/components/DropdownMenu.d.ts.map +1 -1
- package/dist/components/DropdownMenu.js +64 -0
- package/dist/components/DropdownMenu.js.map +1 -0
- package/dist/components/DropdownMenu.module.js +12 -0
- package/dist/components/DropdownMenu.module.js.map +1 -0
- package/dist/components/IconButton.d.ts.map +1 -1
- package/dist/components/IconButton.js +53 -0
- package/dist/components/IconButton.js.map +1 -0
- package/dist/components/IconButton.module.js +14 -0
- package/dist/components/IconButton.module.js.map +1 -0
- package/dist/components/Input.js +28 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Input.module.js +10 -0
- package/dist/components/Input.module.js.map +1 -0
- package/dist/components/Popover.d.ts.map +1 -1
- package/dist/components/Popover.js +57 -0
- package/dist/components/Popover.js.map +1 -0
- package/dist/components/Popover.module.js +10 -0
- package/dist/components/Popover.module.js.map +1 -0
- package/dist/components/RadioGroup.d.ts.map +1 -1
- package/dist/components/RadioGroup.js +28 -0
- package/dist/components/RadioGroup.js.map +1 -0
- package/dist/components/RadioGroup.module.js +9 -0
- package/dist/components/RadioGroup.module.js.map +1 -0
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js +130 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Select.module.js +19 -0
- package/dist/components/Select.module.js.map +1 -0
- package/dist/components/Separator.js +18 -0
- package/dist/components/Separator.js.map +1 -0
- package/dist/components/Separator.module.js +9 -0
- package/dist/components/Separator.module.js.map +1 -0
- package/dist/components/Sheet.d.ts.map +1 -1
- package/dist/components/Sheet.js +77 -0
- package/dist/components/Sheet.js.map +1 -0
- package/dist/components/Sheet.module.js +16 -0
- package/dist/components/Sheet.module.js.map +1 -0
- package/dist/components/Spinner.js +51 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Spinner.module.js +13 -0
- package/dist/components/Spinner.module.js.map +1 -0
- package/dist/components/Switch.d.ts.map +1 -1
- package/dist/components/Switch.js +19 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Switch.module.js +8 -0
- package/dist/components/Switch.module.js.map +1 -0
- package/dist/components/Textarea.js +27 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/Textarea.module.js +10 -0
- package/dist/components/Textarea.module.js.map +1 -0
- package/dist/index.js +20 -1218
- package/dist/lib/cn.js +9 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +76 -3
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import t from "./Combobox.module.js";
|
|
4
|
+
import { cva as n } from "class-variance-authority";
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
import { createContext as a, useCallback as o, useContext as s, useMemo as c, useState as l } from "react";
|
|
7
|
+
import { Combobox as u } from "@base-ui/react/combobox";
|
|
8
|
+
//#region src/components/Combobox.tsx
|
|
9
|
+
var d = a(null);
|
|
10
|
+
function f(e) {
|
|
11
|
+
let [t, n] = l(null), i = c(() => ({
|
|
12
|
+
anchorElement: t,
|
|
13
|
+
setAnchorElement: n
|
|
14
|
+
}), [t]);
|
|
15
|
+
return /* @__PURE__ */ r(d.Provider, {
|
|
16
|
+
value: i,
|
|
17
|
+
children: /* @__PURE__ */ r(u.Root, { ...e })
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
f.displayName = "Combobox";
|
|
21
|
+
function p() {
|
|
22
|
+
return /* @__PURE__ */ r("svg", {
|
|
23
|
+
viewBox: "0 0 24 24",
|
|
24
|
+
fill: "none",
|
|
25
|
+
stroke: "currentColor",
|
|
26
|
+
strokeWidth: "2",
|
|
27
|
+
"aria-hidden": "true",
|
|
28
|
+
children: /* @__PURE__ */ r("path", {
|
|
29
|
+
d: "m6 9 6 6 6-6",
|
|
30
|
+
strokeLinecap: "round",
|
|
31
|
+
strokeLinejoin: "round"
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function m() {
|
|
36
|
+
return /* @__PURE__ */ r("svg", {
|
|
37
|
+
viewBox: "0 0 24 24",
|
|
38
|
+
fill: "none",
|
|
39
|
+
stroke: "currentColor",
|
|
40
|
+
strokeWidth: "2",
|
|
41
|
+
"aria-hidden": "true",
|
|
42
|
+
children: /* @__PURE__ */ r("path", {
|
|
43
|
+
d: "M20 6 9 17l-5-5",
|
|
44
|
+
strokeLinecap: "round",
|
|
45
|
+
strokeLinejoin: "round"
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function h() {
|
|
50
|
+
return /* @__PURE__ */ r("svg", {
|
|
51
|
+
viewBox: "0 0 24 24",
|
|
52
|
+
fill: "none",
|
|
53
|
+
stroke: "currentColor",
|
|
54
|
+
strokeWidth: "2",
|
|
55
|
+
"aria-hidden": "true",
|
|
56
|
+
children: /* @__PURE__ */ r("path", {
|
|
57
|
+
d: "M18 6 6 18M6 6l12 12",
|
|
58
|
+
strokeLinecap: "round",
|
|
59
|
+
strokeLinejoin: "round"
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function g({ className: n, ...i }) {
|
|
64
|
+
return /* @__PURE__ */ r(u.InputGroup, {
|
|
65
|
+
"data-pharos-slot": "combobox-control",
|
|
66
|
+
className: e(t.control, n),
|
|
67
|
+
...i
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
g.displayName = "ComboboxControl";
|
|
71
|
+
function _({ className: n, ref: i, ...a }) {
|
|
72
|
+
let c = s(d), l = o((e) => {
|
|
73
|
+
c?.setAnchorElement(e), typeof i == "function" ? i(e) : i && (i.current = e);
|
|
74
|
+
}, [c, i]);
|
|
75
|
+
return /* @__PURE__ */ r(u.Chips, {
|
|
76
|
+
"data-pharos-slot": "combobox-chips",
|
|
77
|
+
className: e(t.control, t.chips, n),
|
|
78
|
+
ref: l,
|
|
79
|
+
...a
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
_.displayName = "ComboboxChips";
|
|
83
|
+
var v = n(t.input, {
|
|
84
|
+
variants: { size: {
|
|
85
|
+
sm: t.inputSizeSm,
|
|
86
|
+
md: t.inputSizeMd,
|
|
87
|
+
lg: t.inputSizeLg
|
|
88
|
+
} },
|
|
89
|
+
defaultVariants: { size: "md" }
|
|
90
|
+
});
|
|
91
|
+
function y({ size: n, inset: i = !1, className: a, ...o }) {
|
|
92
|
+
let s = n ?? "md";
|
|
93
|
+
return /* @__PURE__ */ r(u.Input, {
|
|
94
|
+
"data-pharos-slot": "combobox-input",
|
|
95
|
+
"data-pharos-size": s,
|
|
96
|
+
className: e(i ? e(t.input, t.chipsInput) : v({ size: s }), a),
|
|
97
|
+
...o
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
y.displayName = "ComboboxInput";
|
|
101
|
+
function b({ className: n, children: i, ...a }) {
|
|
102
|
+
return /* @__PURE__ */ r(u.Trigger, {
|
|
103
|
+
"data-pharos-slot": "combobox-trigger",
|
|
104
|
+
className: e(t.trigger, n),
|
|
105
|
+
...a,
|
|
106
|
+
children: i ?? /* @__PURE__ */ r(p, {})
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
b.displayName = "ComboboxTrigger";
|
|
110
|
+
function x({ className: n, children: i, ...a }) {
|
|
111
|
+
return /* @__PURE__ */ r(u.Clear, {
|
|
112
|
+
"data-pharos-slot": "combobox-clear",
|
|
113
|
+
className: e(t.clear, n),
|
|
114
|
+
...a,
|
|
115
|
+
children: i ?? /* @__PURE__ */ r(h, {})
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
x.displayName = "ComboboxClear";
|
|
119
|
+
function S({ className: n, ...i }) {
|
|
120
|
+
return /* @__PURE__ */ r(u.Chip, {
|
|
121
|
+
"data-pharos-slot": "combobox-chip",
|
|
122
|
+
className: e(t.chip, n),
|
|
123
|
+
...i
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
S.displayName = "ComboboxChip";
|
|
127
|
+
function C({ className: n, children: i, "aria-label": a = "Remove", ...o }) {
|
|
128
|
+
return /* @__PURE__ */ r(u.ChipRemove, {
|
|
129
|
+
"data-pharos-slot": "combobox-chip-remove",
|
|
130
|
+
"aria-label": a,
|
|
131
|
+
className: e(t.chipRemove, n),
|
|
132
|
+
...o,
|
|
133
|
+
children: i ?? /* @__PURE__ */ r(h, {})
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
C.displayName = "ComboboxChipRemove";
|
|
137
|
+
function w({ side: n = "bottom", align: i = "start", sideOffset: a = 8, alignOffset: o = 0, className: c, children: l, ...f }) {
|
|
138
|
+
let p = s(d)?.anchorElement ?? void 0;
|
|
139
|
+
return /* @__PURE__ */ r(u.Portal, { children: /* @__PURE__ */ r(u.Positioner, {
|
|
140
|
+
className: t.positioner,
|
|
141
|
+
anchor: p,
|
|
142
|
+
side: n,
|
|
143
|
+
align: i,
|
|
144
|
+
sideOffset: a,
|
|
145
|
+
alignOffset: o,
|
|
146
|
+
children: /* @__PURE__ */ r(u.Popup, {
|
|
147
|
+
"data-pharos-slot": "combobox-content",
|
|
148
|
+
className: e(t.content, c),
|
|
149
|
+
...f,
|
|
150
|
+
children: l
|
|
151
|
+
})
|
|
152
|
+
}) });
|
|
153
|
+
}
|
|
154
|
+
w.displayName = "ComboboxContent";
|
|
155
|
+
function T({ className: e, ...t }) {
|
|
156
|
+
return /* @__PURE__ */ r(u.List, {
|
|
157
|
+
"data-pharos-slot": "combobox-list",
|
|
158
|
+
className: e,
|
|
159
|
+
...t
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
T.displayName = "ComboboxList";
|
|
163
|
+
function E({ className: n, children: a, ...o }) {
|
|
164
|
+
return /* @__PURE__ */ i(u.Item, {
|
|
165
|
+
"data-pharos-slot": "combobox-item",
|
|
166
|
+
className: e(t.item, n),
|
|
167
|
+
...o,
|
|
168
|
+
children: [/* @__PURE__ */ r("span", {
|
|
169
|
+
className: t.itemText,
|
|
170
|
+
children: a
|
|
171
|
+
}), /* @__PURE__ */ r(u.ItemIndicator, {
|
|
172
|
+
className: t.itemIndicator,
|
|
173
|
+
children: /* @__PURE__ */ r(m, {})
|
|
174
|
+
})]
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
E.displayName = "ComboboxItem";
|
|
178
|
+
function D({ className: n, ...i }) {
|
|
179
|
+
return /* @__PURE__ */ r(u.Empty, {
|
|
180
|
+
"data-pharos-slot": "combobox-empty",
|
|
181
|
+
className: e(t.empty, n),
|
|
182
|
+
...i
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
D.displayName = "ComboboxEmpty";
|
|
186
|
+
function O(e) {
|
|
187
|
+
return /* @__PURE__ */ r(u.Group, {
|
|
188
|
+
"data-pharos-slot": "combobox-group",
|
|
189
|
+
...e
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
O.displayName = "ComboboxGroup";
|
|
193
|
+
function k({ className: n, ...i }) {
|
|
194
|
+
return /* @__PURE__ */ r(u.GroupLabel, {
|
|
195
|
+
"data-pharos-slot": "combobox-group-label",
|
|
196
|
+
className: e(t.label, n),
|
|
197
|
+
...i
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
k.displayName = "ComboboxGroupLabel";
|
|
201
|
+
function A({ className: n, ...i }) {
|
|
202
|
+
return /* @__PURE__ */ r(u.Separator, {
|
|
203
|
+
"data-pharos-slot": "combobox-separator",
|
|
204
|
+
className: e(t.separator, n),
|
|
205
|
+
...i
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
A.displayName = "ComboboxSeparator";
|
|
209
|
+
//#endregion
|
|
210
|
+
export { f as Combobox, S as ComboboxChip, C as ComboboxChipRemove, _ as ComboboxChips, x as ComboboxClear, w as ComboboxContent, g as ComboboxControl, D as ComboboxEmpty, O as ComboboxGroup, k as ComboboxGroupLabel, y as ComboboxInput, E as ComboboxItem, T as ComboboxList, A as ComboboxSeparator, b as ComboboxTrigger };
|
|
211
|
+
|
|
212
|
+
//# sourceMappingURL=Combobox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.js","names":[],"sources":["../../src/components/Combobox.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ComponentProps,\n type Ref,\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { Combobox as BaseCombobox } from '@base-ui/react/combobox';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn';\nimport styles from './Combobox.module.css';\n\n/**\n * Single- or multi-select with a **text input that filters the options**\n * (Decision D17). Wraps Base UI's `Combobox.*` parts, which implement the\n * ARIA APG **combobox** pattern: an `<input role=\"combobox\">` with\n * `aria-expanded` / `aria-controls` pointing at a popup listbox, typeahead\n * filtering, and arrow-key navigation of the results.\n *\n * This is deliberately distinct from the `Select` atom (also D17). A Select\n * has no input — the user picks from a closed list. A Combobox is for sets\n * large enough to need filtering, or where the user expects to type. shadcn,\n * Base UI and ARIA APG separate the two; Pharos ships both rather than a\n * single component with a `searchable` axis. (Free-text entry — typing a\n * value that is not in the list — is Base UI's separate `Autocomplete`\n * primitive and is deferred; every known Alexandria call-site filters a known\n * set.) See `NAMING-decisions.md` § Combobox.\n *\n * Multi-select is the `multiple` axis on the root (not a separate atom),\n * matching Base UI / shadcn / React Aria. With `multiple`, selections render\n * as removable chips inside a `ComboboxChips` box and the popup stays open so\n * they accumulate; single-select uses a `ComboboxControl` input box and\n * closes on pick.\n *\n * Naming follows Base UI (`Combobox*`; shadcn has no canonical primitive — it\n * composes a recipe). The control chrome is split into composable parts\n * because a combobox is inherently more compositional than a select:\n * `ComboboxControl` / `ComboboxChips` own the bordered box, `ComboboxInput`\n * is the bare field, `ComboboxTrigger` the chevron. `ComboboxContent`\n * collapses `Portal` + `Positioner` + `Popup`; `ComboboxList` and\n * `ComboboxEmpty` sit inside it. The popup takes `width:\n * var(--anchor-width)` so it matches the control width exactly (long options\n * truncate) — the anchor-width affordance the picker call-sites needed.\n *\n * Per the form-control composition rule (Escuela 1, D11) the atom owns no\n * label / helper / error message; the consumer composes those and conveys\n * error via `aria-invalid`.\n *\n * @example single-select\n * <Combobox items={fruits} onValueChange={setValue}>\n * <ComboboxControl>\n * <ComboboxInput placeholder=\"Search fruit…\" />\n * <ComboboxTrigger />\n * </ComboboxControl>\n * <ComboboxContent>\n * <ComboboxEmpty>No fruit found</ComboboxEmpty>\n * <ComboboxList>\n * {(item) => (\n * <ComboboxItem key={item.value} value={item}>\n * {item.label}\n * </ComboboxItem>\n * )}\n * </ComboboxList>\n * </ComboboxContent>\n * </Combobox>\n */\nexport type ComboboxProps<\n Value,\n Multiple extends boolean | undefined = false,\n> = BaseCombobox.Root.Props<Value, Multiple>;\n\n/**\n * Lets `ComboboxChips` publish its DOM node so `ComboboxContent` can anchor\n * the floating listbox to the control box instead of the bare input.\n *\n * Why this exists: Base UI resolves the popup anchor as\n * `inputGroupElement ?? inputElement`. In single-select Pharos wraps Base UI's\n * `InputGroup` (`ComboboxControl`), so the popup anchors to the full-width\n * control. In multi-select Pharos uses `Combobox.Chips` (`ComboboxChips`) as\n * the bordered box — and `Chips` does NOT register an `inputGroupElement`, so\n * the anchor falls back to the `Input`, which floats at the end of the chip\n * row. The popup then opens narrow and shifted right. Capturing the chips box\n * here and passing it as the Positioner's `anchor` keeps the panel the full\n * control width, left-aligned, regardless of where the input sits.\n */\ninterface ComboboxAnchorContextValue {\n anchorElement: HTMLElement | null;\n setAnchorElement: (element: HTMLElement | null) => void;\n}\n\nconst ComboboxAnchorContext = createContext<ComboboxAnchorContextValue | null>(null);\n\nexport function Combobox<Value, Multiple extends boolean | undefined = false>(\n props: ComboboxProps<Value, Multiple>,\n) {\n const [anchorElement, setAnchorElement] = useState<HTMLElement | null>(null);\n const anchorContext = useMemo<ComboboxAnchorContextValue>(\n () => ({ anchorElement, setAnchorElement }),\n [anchorElement],\n );\n return (\n <ComboboxAnchorContext.Provider value={anchorContext}>\n <BaseCombobox.Root {...props} />\n </ComboboxAnchorContext.Provider>\n );\n}\n\nCombobox.displayName = 'Combobox';\n\n/** A downward chevron, inlined as an SVG (Pharos bundles no icon dependency; D4). */\nfunction ChevronDownIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\n/** A check mark for the selected option, inlined for the same reason. */\nfunction CheckIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\n/** A small ✕ for clearing / removing, inlined for the same reason. */\nfunction XIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"M18 6 6 18M6 6l12 12\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\n/**\n * The bordered control box for **single-select**. Wraps Base UI's\n * `Combobox.InputGroup` and carries the Input chrome (border tone, focus\n * ring). Put a `ComboboxInput` and optionally a `ComboboxTrigger` /\n * `ComboboxClear` inside it.\n */\nexport type ComboboxControlProps = ComponentProps<typeof BaseCombobox.InputGroup>;\n\nexport function ComboboxControl({ className, ...rest }: ComboboxControlProps) {\n return (\n <BaseCombobox.InputGroup\n data-pharos-slot=\"combobox-control\"\n className={cn(styles.control, className)}\n {...rest}\n />\n );\n}\n\nComboboxControl.displayName = 'ComboboxControl';\n\n/**\n * The bordered control box for **multi-select**. Wraps Base UI's\n * `Combobox.Chips`; render `ComboboxChip`s for the current selections\n * followed by a `ComboboxInput` to keep typing. Shares the `ComboboxControl`\n * chrome.\n */\nexport type ComboboxChipsProps = ComponentProps<typeof BaseCombobox.Chips>;\n\nexport function ComboboxChips({ className, ref, ...rest }: ComboboxChipsProps) {\n const anchorContext = useContext(ComboboxAnchorContext);\n // Publish the chips box as the popup anchor (see ComboboxAnchorContext)\n // while still honouring any ref the consumer forwards.\n const setRef = useCallback(\n (element: HTMLDivElement | null) => {\n anchorContext?.setAnchorElement(element);\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n (ref as { current: HTMLDivElement | null }).current = element;\n }\n },\n [anchorContext, ref],\n );\n return (\n <BaseCombobox.Chips\n data-pharos-slot=\"combobox-chips\"\n className={cn(styles.control, styles.chips, className)}\n ref={setRef as Ref<HTMLDivElement>}\n {...rest}\n />\n );\n}\n\nComboboxChips.displayName = 'ComboboxChips';\n\nconst inputVariants = cva(styles.input, {\n variants: {\n size: {\n sm: styles.inputSizeSm,\n md: styles.inputSizeMd,\n lg: styles.inputSizeLg,\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\ntype InputVariantProps = VariantProps<typeof inputVariants>;\n\n/**\n * The text field. Wraps Base UI's `Combobox.Input` (`role=\"combobox\"`),\n * which owns the filtering. Renders bare — the surrounding `ComboboxControl`\n * / `ComboboxChips` box owns the border. The `size` axis (sm / md / lg)\n * matches Input/Select and applies when the input is used standalone in a\n * `ComboboxControl`; inside a `ComboboxChips` box pass `inset` so it fills\n * the chip row instead of imposing its own height.\n */\nexport interface ComboboxInputProps\n extends Omit<ComponentProps<typeof BaseCombobox.Input>, 'size'>, InputVariantProps {\n /**\n * Render as a flexible filler that shares the row with chips, instead of a\n * fixed-height field. Use inside `ComboboxChips`.\n * @default false\n */\n inset?: boolean;\n}\n\nexport function ComboboxInput({ size, inset = false, className, ...rest }: ComboboxInputProps) {\n const resolvedSize = size ?? 'md';\n return (\n <BaseCombobox.Input\n data-pharos-slot=\"combobox-input\"\n data-pharos-size={resolvedSize}\n className={cn(\n inset ? cn(styles.input, styles.chipsInput) : inputVariants({ size: resolvedSize }),\n className,\n )}\n {...rest}\n />\n );\n}\n\nComboboxInput.displayName = 'ComboboxInput';\n\n/**\n * The chevron button that toggles the popup. Wraps Base UI's\n * `Combobox.Trigger`; the chevron flips when the popup is open.\n */\nexport type ComboboxTriggerProps = ComponentProps<typeof BaseCombobox.Trigger>;\n\nexport function ComboboxTrigger({ className, children, ...rest }: ComboboxTriggerProps) {\n return (\n <BaseCombobox.Trigger\n data-pharos-slot=\"combobox-trigger\"\n className={cn(styles.trigger, className)}\n {...rest}\n >\n {children ?? <ChevronDownIcon />}\n </BaseCombobox.Trigger>\n );\n}\n\nComboboxTrigger.displayName = 'ComboboxTrigger';\n\n/** A button that clears the current value(s). Wraps Base UI's `Combobox.Clear`. */\nexport type ComboboxClearProps = ComponentProps<typeof BaseCombobox.Clear>;\n\nexport function ComboboxClear({ className, children, ...rest }: ComboboxClearProps) {\n return (\n <BaseCombobox.Clear\n data-pharos-slot=\"combobox-clear\"\n className={cn(styles.clear, className)}\n {...rest}\n >\n {children ?? <XIcon />}\n </BaseCombobox.Clear>\n );\n}\n\nComboboxClear.displayName = 'ComboboxClear';\n\n/** A selected-value chip for multi-select. Wraps Base UI's `Combobox.Chip`. */\nexport type ComboboxChipProps = ComponentProps<typeof BaseCombobox.Chip>;\n\nexport function ComboboxChip({ className, ...rest }: ComboboxChipProps) {\n return (\n <BaseCombobox.Chip\n data-pharos-slot=\"combobox-chip\"\n className={cn(styles.chip, className)}\n {...rest}\n />\n );\n}\n\nComboboxChip.displayName = 'ComboboxChip';\n\n/** The ✕ that removes a chip's value. Wraps Base UI's `Combobox.ChipRemove`. */\nexport type ComboboxChipRemoveProps = ComponentProps<typeof BaseCombobox.ChipRemove>;\n\nexport function ComboboxChipRemove({\n className,\n children,\n 'aria-label': ariaLabel = 'Remove',\n ...rest\n}: ComboboxChipRemoveProps) {\n return (\n <BaseCombobox.ChipRemove\n data-pharos-slot=\"combobox-chip-remove\"\n aria-label={ariaLabel}\n className={cn(styles.chipRemove, className)}\n {...rest}\n >\n {children ?? <XIcon />}\n </BaseCombobox.ChipRemove>\n );\n}\n\nComboboxChipRemove.displayName = 'ComboboxChipRemove';\n\n/**\n * The floating listbox surface. Collapses Base UI's `Portal` + `Positioner` +\n * `Popup`. Put `ComboboxEmpty` and `ComboboxList` inside it. Defaults: opens\n * below the control, aligned to its start edge, 8px away, and matches the\n * control width (`width: var(--anchor-width)`).\n */\nexport interface ComboboxContentProps extends ComponentProps<typeof BaseCombobox.Popup> {\n /**\n * Which side of the control the listbox opens on.\n * @default 'bottom'\n */\n side?: ComponentProps<typeof BaseCombobox.Positioner>['side'];\n /**\n * Alignment of the listbox relative to the control along the chosen side.\n * @default 'start'\n */\n align?: ComponentProps<typeof BaseCombobox.Positioner>['align'];\n /**\n * Gap in pixels between the control and the listbox.\n * @default 8\n */\n sideOffset?: ComponentProps<typeof BaseCombobox.Positioner>['sideOffset'];\n /**\n * Offset in pixels along the alignment axis.\n * @default 0\n */\n alignOffset?: ComponentProps<typeof BaseCombobox.Positioner>['alignOffset'];\n}\n\nexport function ComboboxContent({\n side = 'bottom',\n align = 'start',\n sideOffset = 8,\n alignOffset = 0,\n className,\n children,\n ...rest\n}: ComboboxContentProps) {\n const anchorContext = useContext(ComboboxAnchorContext);\n // Anchor to the chips control box in multi-select (see\n // ComboboxAnchorContext); single-select leaves this undefined so Base UI\n // anchors to the `ComboboxControl` (InputGroup) as before.\n const anchor = anchorContext?.anchorElement ?? undefined;\n return (\n <BaseCombobox.Portal>\n <BaseCombobox.Positioner\n className={styles.positioner}\n anchor={anchor}\n side={side}\n align={align}\n sideOffset={sideOffset}\n alignOffset={alignOffset}\n >\n <BaseCombobox.Popup\n data-pharos-slot=\"combobox-content\"\n className={cn(styles.content, className)}\n {...rest}\n >\n {children}\n </BaseCombobox.Popup>\n </BaseCombobox.Positioner>\n </BaseCombobox.Portal>\n );\n}\n\nComboboxContent.displayName = 'ComboboxContent';\n\n/**\n * The list of results. Wraps Base UI's `Combobox.List`, which accepts either\n * static children or a render function `(item, index) => ReactNode` that maps\n * over the (already filtered) `items` passed to the root.\n */\nexport type ComboboxListProps = ComponentProps<typeof BaseCombobox.List>;\n\nexport function ComboboxList({ className, ...rest }: ComboboxListProps) {\n return <BaseCombobox.List data-pharos-slot=\"combobox-list\" className={className} {...rest} />;\n}\n\nComboboxList.displayName = 'ComboboxList';\n\n/**\n * A single result row. Folds Base UI's `Item` + the selected-state\n * `ItemIndicator` check. The `value` prop is what `onValueChange` reports.\n */\nexport interface ComboboxItemProps extends ComponentProps<typeof BaseCombobox.Item> {}\n\nexport function ComboboxItem({ className, children, ...rest }: ComboboxItemProps) {\n return (\n <BaseCombobox.Item\n data-pharos-slot=\"combobox-item\"\n className={cn(styles.item, className)}\n {...rest}\n >\n <span className={styles.itemText}>{children}</span>\n <BaseCombobox.ItemIndicator className={styles.itemIndicator}>\n <CheckIcon />\n </BaseCombobox.ItemIndicator>\n </BaseCombobox.Item>\n );\n}\n\nComboboxItem.displayName = 'ComboboxItem';\n\n/**\n * Shown in place of the list when the filter matches nothing. Wraps Base UI's\n * `Combobox.Empty`, which is only rendered when there are no results.\n */\nexport type ComboboxEmptyProps = ComponentProps<typeof BaseCombobox.Empty>;\n\nexport function ComboboxEmpty({ className, ...rest }: ComboboxEmptyProps) {\n return (\n <BaseCombobox.Empty\n data-pharos-slot=\"combobox-empty\"\n className={cn(styles.empty, className)}\n {...rest}\n />\n );\n}\n\nComboboxEmpty.displayName = 'ComboboxEmpty';\n\n/** Groups related results under an optional `ComboboxGroupLabel` (`role=\"group\"`). */\nexport type ComboboxGroupProps = ComponentProps<typeof BaseCombobox.Group>;\n\nexport function ComboboxGroup(props: ComboboxGroupProps) {\n return <BaseCombobox.Group data-pharos-slot=\"combobox-group\" {...props} />;\n}\n\nComboboxGroup.displayName = 'ComboboxGroup';\n\n/** A non-interactive heading for a `ComboboxGroup`. Wraps `Combobox.GroupLabel`. */\nexport type ComboboxGroupLabelProps = ComponentProps<typeof BaseCombobox.GroupLabel>;\n\nexport function ComboboxGroupLabel({ className, ...rest }: ComboboxGroupLabelProps) {\n return (\n <BaseCombobox.GroupLabel\n data-pharos-slot=\"combobox-group-label\"\n className={cn(styles.label, className)}\n {...rest}\n />\n );\n}\n\nComboboxGroupLabel.displayName = 'ComboboxGroupLabel';\n\n/** A thin divider between groups of results. Wraps `Combobox.Separator`. */\nexport type ComboboxSeparatorProps = ComponentProps<typeof BaseCombobox.Separator>;\n\nexport function ComboboxSeparator({ className, ...rest }: ComboboxSeparatorProps) {\n return (\n <BaseCombobox.Separator\n data-pharos-slot=\"combobox-separator\"\n className={cn(styles.separator, className)}\n {...rest}\n />\n );\n}\n\nComboboxSeparator.displayName = 'ComboboxSeparator';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
control: "_control_1dd5w_5",
|
|
3
|
+
chips: "_chips_1dd5w_61",
|
|
4
|
+
input: "_input_1dd5w_70",
|
|
5
|
+
inputSizeSm: "_inputSizeSm_1dd5w_99",
|
|
6
|
+
inputSizeMd: "_inputSizeMd_1dd5w_104",
|
|
7
|
+
inputSizeLg: "_inputSizeLg_1dd5w_109",
|
|
8
|
+
chipsInput: "_chipsInput_1dd5w_117",
|
|
9
|
+
trigger: "_trigger_1dd5w_124",
|
|
10
|
+
clear: "_clear_1dd5w_125",
|
|
11
|
+
chip: "_chip_1dd5w_61",
|
|
12
|
+
chipRemove: "_chipRemove_1dd5w_169",
|
|
13
|
+
positioner: "_positioner_1dd5w_203",
|
|
14
|
+
content: "_content_1dd5w_193",
|
|
15
|
+
item: "_item_1dd5w_14",
|
|
16
|
+
itemText: "_itemText_1dd5w_14",
|
|
17
|
+
itemIndicator: "_itemIndicator_1dd5w_272",
|
|
18
|
+
empty: "_empty_1dd5w_294",
|
|
19
|
+
label: "_label_1dd5w_304",
|
|
20
|
+
separator: "_separator_1dd5w_311"
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { e as default };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=Combobox.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.module.js","names":[],"sources":["../../src/components/Combobox.module.css"],"sourcesContent":["/* Combobox.module.css\n *\n * Per-component CSS Modules definition (Decision D9). Hashed into\n * `dist/styles.css`. Styles the bordered control (single-select input group\n * and multi-select chips container share the `.control` chrome), the chips,\n * the floating listbox surface, and the option rows.\n *\n * The control border tone is `neutral-500` for the same WCAG 1.4.11 reason\n * Input and Select use it (D10). The popup surface chrome and the option /\n * group / separator styling mirror Select exactly — Select and Combobox are\n * the two halves of the selection family and read as one system.\n *\n * Anchor-width: the popup takes `width: var(--anchor-width)` so the listbox\n * matches the control exactly — long options truncate (see `.itemText`)\n * rather than ballooning wider than the field (the Ant / Mantine / MUI /\n * shadcn-combobox default). `max-height: var(--available-height)` clamps it\n * to the viewport. Both vars come from Base UI's Positioner. Consumers\n * needing a wider panel override `width` via `className`. */\n\n/* === Control (Combobox.InputGroup, single) and chips box (Combobox.Chips,\n * multiple) — same bordered chrome === */\n.control {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--pharos-spacing-2);\n width: 100%;\n\n border: 1px solid var(--pharos-color-neutral-500);\n border-radius: var(--pharos-radius-md);\n background-color: var(--pharos-color-base-white);\n\n /* Set the Pharos sans stack on the box so everything inside it — chips,\n * the input — shares one typography. Without this the chips fall back to\n * the user-agent serif/system font (the bundle ships no global font), so\n * they read in a different typeface than the input. Same reason Input sets\n * font-family explicitly. */\n font-family: var(--pharos-font-family-sans);\n\n transition-property: border-color, box-shadow;\n transition-duration: var(--pharos-duration-normal);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n/* Lift the focus ring to the control when the inner input is focused, so the\n * whole bordered box reads as the focused control (the input itself carries\n * no border). */\n.control:focus-within {\n border-color: var(--pharos-color-primary-600);\n box-shadow:\n 0 0 0 2px var(--pharos-color-base-white),\n 0 0 0 4px var(--pharos-color-primary-600);\n}\n\n.control[aria-invalid='true'],\n.control:has([aria-invalid='true']) {\n border-color: var(--pharos-color-semantic-error-fg);\n}\n\n/* The multi-select chips box wraps its chips and grows vertically. */\n.chips {\n flex-wrap: wrap;\n gap: var(--pharos-spacing-1);\n padding: var(--pharos-spacing-1) var(--pharos-spacing-2);\n}\n\n/* === Bare input (Combobox.Input) ===\n * Carries no border of its own — the surrounding `.control` / `.chips` box\n * owns the chrome. Grows to fill the row. */\n.input {\n appearance: none;\n -webkit-appearance: none;\n\n flex: 1 1 auto;\n min-width: 0;\n margin: 0;\n border: none;\n outline: none;\n background: transparent;\n color: var(--pharos-color-neutral-900);\n\n font-family: var(--pharos-font-family-sans);\n font-size: var(--pharos-font-size-sm);\n font-weight: var(--pharos-font-weight-regular);\n line-height: 1.25;\n}\n\n.input::placeholder {\n color: var(--pharos-color-neutral-500);\n opacity: 1;\n}\n\n.input:disabled {\n cursor: not-allowed;\n}\n\n/* Standalone single-select input gets the same padding/height grid as Input\n * so it sits flush with other fields. */\n.inputSizeSm {\n height: var(--pharos-spacing-8);\n padding: 0 var(--pharos-spacing-3);\n}\n\n.inputSizeMd {\n height: var(--pharos-spacing-10);\n padding: 0 var(--pharos-spacing-4);\n}\n\n.inputSizeLg {\n height: var(--pharos-spacing-12);\n padding: 0 var(--pharos-spacing-6);\n font-size: var(--pharos-font-size-base);\n}\n\n/* Inside the chips box the input is a flexible filler — no fixed height, it\n * shares the row with the chips. */\n.chipsInput {\n height: var(--pharos-spacing-7);\n padding: 0 var(--pharos-spacing-1);\n flex: 1 1 4rem;\n}\n\n/* === Trigger (chevron) and clear button === */\n.trigger,\n.clear {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--pharos-spacing-6);\n height: var(--pharos-spacing-6);\n margin-inline-end: var(--pharos-spacing-1);\n padding: 0;\n border: none;\n background: transparent;\n color: var(--pharos-color-neutral-500);\n cursor: default;\n}\n\n.trigger svg,\n.clear svg {\n width: 1rem;\n height: 1rem;\n}\n\n.trigger[data-popup-open] svg {\n transform: rotate(180deg);\n transition: transform var(--pharos-duration-fast) var(--pharos-easing-out);\n}\n\n/* === Chip (Combobox.Chip) === */\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--pharos-spacing-1);\n padding: 0 var(--pharos-spacing-1) 0 var(--pharos-spacing-2);\n height: var(--pharos-spacing-6);\n\n border-radius: var(--pharos-radius-sm);\n background-color: var(--pharos-color-neutral-100);\n color: var(--pharos-color-neutral-900);\n font-size: var(--pharos-font-size-xs);\n}\n\n.chip[data-highlighted] {\n background-color: var(--pharos-color-neutral-200);\n}\n\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--pharos-spacing-4);\n height: var(--pharos-spacing-4);\n padding: 0;\n border: none;\n border-radius: var(--pharos-radius-sm);\n background: transparent;\n color: var(--pharos-color-neutral-500);\n cursor: default;\n}\n\n.chipRemove:hover {\n color: var(--pharos-color-neutral-900);\n}\n\n.chipRemove svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n/* === Positioner (Combobox.Positioner) ===\n * z-index lives here, not on `.content` (the Popup). Base UI's Positioner\n * anchors with `transform`, which creates a stacking context — so a z-index\n * on the inner Popup is trapped inside it and cannot rise above a host-app\n * overlay (e.g. a drawer with its own z-index). The z-index must sit on the\n * Positioner itself.\n *\n * Sourced from `--pharos-z-index-popover` (no hardcoded value — NON-NEGOTIABLE\n * rule 1). The fallback suits apps with no high z-index scale; an app whose\n * own overlays sit higher (drawers, modals) raises every Pharos popup at once\n * by overriding `--pharos-z-index-popover` in `:root`. */\n.positioner {\n z-index: var(--pharos-z-index-popover, 1000);\n}\n\n/* === Floating surface (Combobox.Popup) — mirrors Select === */\n.content {\n box-sizing: border-box;\n width: var(--anchor-width);\n max-height: var(--available-height);\n overflow-y: auto;\n padding: var(--pharos-spacing-1);\n\n background-color: var(--pharos-color-base-white);\n border: 1px solid var(--pharos-color-neutral-200);\n border-radius: var(--pharos-radius-md);\n box-shadow: var(--pharos-shadow-lg);\n\n font-family: var(--pharos-font-family-sans);\n font-size: var(--pharos-font-size-sm);\n color: var(--pharos-color-neutral-900);\n\n outline: none;\n\n transform-origin: var(--transform-origin);\n transition-property: opacity, transform;\n transition-duration: var(--pharos-duration-fast);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n.content[data-starting-style],\n.content[data-ending-style] {\n opacity: 0;\n transform: scale(0.96);\n}\n\n/* === Option (Combobox.Item) === */\n.item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--pharos-spacing-2);\n\n padding: var(--pharos-spacing-2) var(--pharos-spacing-3);\n border-radius: var(--pharos-radius-sm);\n\n color: var(--pharos-color-neutral-900);\n cursor: default;\n user-select: none;\n outline: none;\n scroll-margin: var(--pharos-spacing-1);\n}\n\n.item[data-highlighted] {\n background-color: var(--pharos-color-neutral-100);\n}\n\n.item[data-disabled] {\n color: var(--pharos-color-neutral-400);\n pointer-events: none;\n}\n\n.itemText {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.itemIndicator {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--pharos-color-primary-600);\n}\n\n.itemIndicator svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* === Empty state (Combobox.Empty) ===\n * Base UI keeps this element mounted even when there ARE results (it is a\n * `role=\"status\"` live region and must not be removed / `display:none`-d, or\n * screen readers miss the \"no results\" announcement) — it only nulls its\n * children. So when results exist the element is present but childless;\n * `:not(:empty)` applies the padding only when a message is actually shown,\n * collapsing the box to zero height otherwise. Without this the empty live\n * region adds a block of padding above the list and the popup looks\n * top-heavy / asymmetric against its bottom edge. */\n.empty {\n color: var(--pharos-color-neutral-500);\n text-align: center;\n}\n\n.empty:not(:empty) {\n padding: var(--pharos-spacing-3);\n}\n\n/* === Group + label + separator (mirror Select) === */\n.label {\n padding: var(--pharos-spacing-2) var(--pharos-spacing-3) var(--pharos-spacing-1);\n font-size: var(--pharos-font-size-xs);\n font-weight: var(--pharos-font-weight-semibold);\n color: var(--pharos-color-neutral-600);\n}\n\n.separator {\n height: 1px;\n margin: var(--pharos-spacing-1) 0;\n background-color: var(--pharos-color-neutral-200);\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/components/Dialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/components/Dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAC;AACtE,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAI7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;AAEjE,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAExC;yBAFe,MAAM;;;AAMtB;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;AAE3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,+BAEtD;yBAFe,aAAa;;;AAM7B;;;;;GAKG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC;CAAG;AAEtF,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,kBAAkB,+BAajF;yBAbe,aAAa;;;AAiB7B,8DAA8D;AAC9D,MAAM,WAAW,iBAAkB,SAAQ,wBAAwB,CAAC,KAAK,CAAC;CAAG;AAE7E,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,+BAIrE;yBAJe,YAAY;;;AAQ5B;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvE,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,+BAQnE;yBARe,WAAW;;;AAY3B;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,WAAW,CAAC,CAAC;AAEnF,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,sBAAsB,+BAQ/E;yBARe,iBAAiB;;;AAYjC,oDAAoD;AACpD,MAAM,WAAW,iBAAkB,SAAQ,wBAAwB,CAAC,KAAK,CAAC;CAAG;AAE7E,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,+BAIrE;yBAJe,YAAY;;;AAQ5B;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAElD;yBAFe,WAAW"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import t from "./Dialog.module.js";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
import { Dialog as i } from "@base-ui/react/dialog";
|
|
6
|
+
//#region src/components/Dialog.tsx
|
|
7
|
+
function a(e) {
|
|
8
|
+
return /* @__PURE__ */ n(i.Root, { ...e });
|
|
9
|
+
}
|
|
10
|
+
a.displayName = "Dialog";
|
|
11
|
+
function o(e) {
|
|
12
|
+
return /* @__PURE__ */ n(i.Trigger, { ...e });
|
|
13
|
+
}
|
|
14
|
+
o.displayName = "DialogTrigger";
|
|
15
|
+
function s({ className: a, children: o, ...s }) {
|
|
16
|
+
return /* @__PURE__ */ r(i.Portal, { children: [/* @__PURE__ */ n(i.Backdrop, { className: t.backdrop }), /* @__PURE__ */ n(i.Popup, {
|
|
17
|
+
"data-pharos-slot": "dialog-content",
|
|
18
|
+
className: e(t.content, a),
|
|
19
|
+
...s,
|
|
20
|
+
children: o
|
|
21
|
+
})] });
|
|
22
|
+
}
|
|
23
|
+
s.displayName = "DialogContent";
|
|
24
|
+
function c({ className: r, ...i }) {
|
|
25
|
+
return /* @__PURE__ */ n("div", {
|
|
26
|
+
"data-pharos-slot": "dialog-header",
|
|
27
|
+
className: e(t.header, r),
|
|
28
|
+
...i
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
c.displayName = "DialogHeader";
|
|
32
|
+
function l({ className: r, ...a }) {
|
|
33
|
+
return /* @__PURE__ */ n(i.Title, {
|
|
34
|
+
"data-pharos-slot": "dialog-title",
|
|
35
|
+
className: e(t.title, r),
|
|
36
|
+
...a
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
l.displayName = "DialogTitle";
|
|
40
|
+
function u({ className: r, ...a }) {
|
|
41
|
+
return /* @__PURE__ */ n(i.Description, {
|
|
42
|
+
"data-pharos-slot": "dialog-description",
|
|
43
|
+
className: e(t.description, r),
|
|
44
|
+
...a
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
u.displayName = "DialogDescription";
|
|
48
|
+
function d({ className: r, ...i }) {
|
|
49
|
+
return /* @__PURE__ */ n("div", {
|
|
50
|
+
"data-pharos-slot": "dialog-footer",
|
|
51
|
+
className: e(t.footer, r),
|
|
52
|
+
...i
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
d.displayName = "DialogFooter";
|
|
56
|
+
function f(e) {
|
|
57
|
+
return /* @__PURE__ */ n(i.Close, {
|
|
58
|
+
"data-pharos-slot": "dialog-close",
|
|
59
|
+
...e
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
f.displayName = "DialogClose";
|
|
63
|
+
//#endregion
|
|
64
|
+
export { a as Dialog, f as DialogClose, s as DialogContent, u as DialogDescription, d as DialogFooter, c as DialogHeader, l as DialogTitle, o as DialogTrigger };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=Dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.js","names":[],"sources":["../../src/components/Dialog.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentProps, ComponentPropsWithoutRef } from 'react';\nimport { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport { cn } from '../lib/cn';\nimport styles from './Dialog.module.css';\n\n/**\n * Centered modal panel (the shadcn **Dialog** contract). Wraps Base UI's\n * `Dialog.*`, which implements the ARIA **dialog** pattern as a **modal**\n * disclosure: `role=\"dialog\"` + `aria-modal`, focus trapped inside the panel,\n * scroll locked, Escape and backdrop-click dismiss, focus returns to the\n * trigger on close.\n *\n * Sibling of `Sheet` (D18): both wrap the same Base UI `Dialog` primitive.\n * Dialog is the **centered** modal (confirmations, short forms, focused\n * tasks); Sheet is the **edge-docked** panel. Distinct from `Popover` (D16,\n * non-modal, anchored) and `DropdownMenu` (D15, menu-button). Naming follows\n * shadcn (`Dialog*`); `DialogContent` collapses Base UI's `Portal` +\n * `Backdrop` + `Popup` into one part, mirroring the other Pharos overlays.\n *\n * @example\n * <Dialog>\n * <DialogTrigger render={<Button intent=\"destructive\">Delete</Button>} />\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Delete project?</DialogTitle>\n * <DialogDescription>This cannot be undone.</DialogDescription>\n * </DialogHeader>\n * <DialogFooter>\n * <DialogClose render={<Button intent=\"ghost\">Cancel</Button>} />\n * <Button intent=\"destructive\" onClick={onDelete}>Delete</Button>\n * </DialogFooter>\n * </DialogContent>\n * </Dialog>\n */\nexport type DialogProps = ComponentProps<typeof BaseDialog.Root>;\n\nexport function Dialog(props: DialogProps) {\n return <BaseDialog.Root {...props} />;\n}\n\nDialog.displayName = 'Dialog';\n\n/**\n * The element that opens the dialog. Renders a native `<button>` by default;\n * pass `render` to compose any control (`render={<Button…/>}`).\n */\nexport type DialogTriggerProps = ComponentProps<typeof BaseDialog.Trigger>;\n\nexport function DialogTrigger(props: DialogTriggerProps) {\n return <BaseDialog.Trigger {...props} />;\n}\n\nDialogTrigger.displayName = 'DialogTrigger';\n\n/**\n * The centered panel. Collapses Base UI's `Portal` + `Backdrop` + `Popup`. The\n * backdrop and panel share the `--pharos-z-index-popover` overlay layer so a\n * Select / Popover opened from inside the dialog still stacks above it (portal\n * open order).\n */\nexport interface DialogContentProps extends ComponentProps<typeof BaseDialog.Popup> {}\n\nexport function DialogContent({ className, children, ...rest }: DialogContentProps) {\n return (\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className={styles.backdrop} />\n <BaseDialog.Popup\n data-pharos-slot=\"dialog-content\"\n className={cn(styles.content, className)}\n {...rest}\n >\n {children}\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n );\n}\n\nDialogContent.displayName = 'DialogContent';\n\n/** Groups the title + description at the top of the panel. */\nexport interface DialogHeaderProps extends ComponentPropsWithoutRef<'div'> {}\n\nexport function DialogHeader({ className, ...rest }: DialogHeaderProps) {\n return (\n <div data-pharos-slot=\"dialog-header\" className={cn(styles.header, className)} {...rest} />\n );\n}\n\nDialogHeader.displayName = 'DialogHeader';\n\n/**\n * The dialog's accessible name. Renders Base UI's `Dialog.Title`, which wires\n * `aria-labelledby` on the popup — required for the dialog to be announced.\n * Renders as an `<h2>` by default.\n */\nexport type DialogTitleProps = ComponentProps<typeof BaseDialog.Title>;\n\nexport function DialogTitle({ className, ...rest }: DialogTitleProps) {\n return (\n <BaseDialog.Title\n data-pharos-slot=\"dialog-title\"\n className={cn(styles.title, className)}\n {...rest}\n />\n );\n}\n\nDialogTitle.displayName = 'DialogTitle';\n\n/**\n * Supporting copy under the title. Renders Base UI's `Dialog.Description`,\n * which wires `aria-describedby` on the popup. Renders as a `<p>` by default.\n */\nexport type DialogDescriptionProps = ComponentProps<typeof BaseDialog.Description>;\n\nexport function DialogDescription({ className, ...rest }: DialogDescriptionProps) {\n return (\n <BaseDialog.Description\n data-pharos-slot=\"dialog-description\"\n className={cn(styles.description, className)}\n {...rest}\n />\n );\n}\n\nDialogDescription.displayName = 'DialogDescription';\n\n/** Bottom action row, pushed to the panel's end. */\nexport interface DialogFooterProps extends ComponentPropsWithoutRef<'div'> {}\n\nexport function DialogFooter({ className, ...rest }: DialogFooterProps) {\n return (\n <div data-pharos-slot=\"dialog-footer\" className={cn(styles.footer, className)} {...rest} />\n );\n}\n\nDialogFooter.displayName = 'DialogFooter';\n\n/**\n * A button that closes the dialog. Renders Base UI's `Dialog.Close` (a native\n * `<button>`); compose any control via `render`.\n */\nexport type DialogCloseProps = ComponentProps<typeof BaseDialog.Close>;\n\nexport function DialogClose(props: DialogCloseProps) {\n return <BaseDialog.Close data-pharos-slot=\"dialog-close\" {...props} />;\n}\n\nDialogClose.displayName = 'DialogClose';\n"],"mappings":";;;;;;AAsCA,SAAgB,EAAO,GAAoB;CACzC,OAAO,kBAAC,EAAW,MAAZ,EAAiB,GAAI,EAAQ,CAAA;AACtC;AAEA,EAAO,cAAc;AAQrB,SAAgB,EAAc,GAA2B;CACvD,OAAO,kBAAC,EAAW,SAAZ,EAAoB,GAAI,EAAQ,CAAA;AACzC;AAEA,EAAc,cAAc;AAU5B,SAAgB,EAAc,EAAE,cAAW,aAAU,GAAG,KAA4B;CAClF,OACE,kBAAC,EAAW,QAAZ,EAAA,UAAA,CACE,kBAAC,EAAW,UAAZ,EAAqB,WAAW,EAAO,SAAW,CAAA,GAClD,kBAAC,EAAW,OAAZ;EACE,oBAAiB;EACjB,WAAW,EAAG,EAAO,SAAS,CAAS;EACvC,GAAI;EAEH;CACe,CAAA,CACD,EAAA,CAAA;AAEvB;AAEA,EAAc,cAAc;AAK5B,SAAgB,EAAa,EAAE,cAAW,GAAG,KAA2B;CACtE,OACE,kBAAC,OAAD;EAAK,oBAAiB;EAAgB,WAAW,EAAG,EAAO,QAAQ,CAAS;EAAG,GAAI;CAAO,CAAA;AAE9F;AAEA,EAAa,cAAc;AAS3B,SAAgB,EAAY,EAAE,cAAW,GAAG,KAA0B;CACpE,OACE,kBAAC,EAAW,OAAZ;EACE,oBAAiB;EACjB,WAAW,EAAG,EAAO,OAAO,CAAS;EACrC,GAAI;CACL,CAAA;AAEL;AAEA,EAAY,cAAc;AAQ1B,SAAgB,EAAkB,EAAE,cAAW,GAAG,KAAgC;CAChF,OACE,kBAAC,EAAW,aAAZ;EACE,oBAAiB;EACjB,WAAW,EAAG,EAAO,aAAa,CAAS;EAC3C,GAAI;CACL,CAAA;AAEL;AAEA,EAAkB,cAAc;AAKhC,SAAgB,EAAa,EAAE,cAAW,GAAG,KAA2B;CACtE,OACE,kBAAC,OAAD;EAAK,oBAAiB;EAAgB,WAAW,EAAG,EAAO,QAAQ,CAAS;EAAG,GAAI;CAAO,CAAA;AAE9F;AAEA,EAAa,cAAc;AAQ3B,SAAgB,EAAY,GAAyB;CACnD,OAAO,kBAAC,EAAW,OAAZ;EAAkB,oBAAiB;EAAe,GAAI;CAAQ,CAAA;AACvE;AAEA,EAAY,cAAc"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
backdrop: "_backdrop_mozml_18",
|
|
3
|
+
content: "_content_mozml_32",
|
|
4
|
+
header: "_header_mozml_75",
|
|
5
|
+
title: "_title_mozml_81",
|
|
6
|
+
description: "_description_mozml_89",
|
|
7
|
+
footer: "_footer_mozml_96"
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=Dialog.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.module.js","names":[],"sources":["../../src/components/Dialog.module.css"],"sourcesContent":["/* Dialog.module.css\n *\n * Per-component CSS Modules definition (Decision D9). Hashed into\n * `dist/styles.css`. Styles a centered modal panel — the shadcn \"Dialog\"\n * contract over Base UI's `Dialog` (modal: focus trap, scroll lock, backdrop).\n * Sibling of Sheet (D18): same Base UI primitive, but centered with a\n * fade-and-scale transition instead of an edge slide. Motion is driven by Base\n * UI's `data-starting-style` / `data-ending-style` state attributes.\n *\n * z-index: backdrop and panel share `--pharos-z-index-popover` — the SAME\n * overlay layer DropdownMenu / Popover / Select / Combobox / Sheet use. Nesting\n * resolves by portal open order (DOM order), so a Select or Popover opened from\n * INSIDE the dialog still stacks above it. Consumer-overridable (fallback 1000).\n * See NAMING-decisions.md § Sheet for the full rationale.\n */\n\n/* === Backdrop (Dialog.Backdrop) === */\n.backdrop {\n position: fixed;\n inset: 0;\n z-index: var(--pharos-z-index-popover, 1000);\n background-color: color-mix(in srgb, var(--pharos-color-base-black) 50%, transparent);\n transition: opacity var(--pharos-duration-normal) var(--pharos-easing-out);\n}\n\n.backdrop[data-starting-style],\n.backdrop[data-ending-style] {\n opacity: 0;\n}\n\n/* === Panel (Dialog.Popup) === */\n.content {\n position: fixed;\n top: 50%;\n left: 50%;\n /* Centering lives in the transform; the scale for the open/close motion is\n * composed on top of the same translate (see starting/ending styles). */\n transform: translate(-50%, -50%);\n transform-origin: center;\n z-index: var(--pharos-z-index-popover, 1000);\n\n box-sizing: border-box;\n width: calc(100% - 2 * var(--pharos-spacing-4));\n max-width: 32rem;\n max-height: calc(100dvh - 2 * var(--pharos-spacing-6));\n overflow-y: auto;\n\n display: flex;\n flex-direction: column;\n gap: var(--pharos-spacing-4);\n padding: var(--pharos-spacing-6);\n\n background-color: var(--pharos-color-base-white);\n border-radius: var(--pharos-radius-lg);\n box-shadow: var(--pharos-shadow-xl);\n\n font-family: var(--pharos-font-family-sans);\n font-size: var(--pharos-font-size-sm);\n color: var(--pharos-color-neutral-900);\n\n outline: none;\n\n transition-property: opacity, transform;\n transition-duration: var(--pharos-duration-fast);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n.content[data-starting-style],\n.content[data-ending-style] {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.96);\n}\n\n/* === Slots === */\n.header {\n display: flex;\n flex-direction: column;\n gap: var(--pharos-spacing-1);\n}\n\n.title {\n margin: 0;\n font-size: var(--pharos-font-size-lg);\n font-weight: var(--pharos-font-weight-semibold);\n line-height: 1.4;\n color: var(--pharos-color-neutral-900);\n}\n\n.description {\n margin: 0;\n font-size: var(--pharos-font-size-sm);\n line-height: 1.5;\n color: var(--pharos-color-neutral-600);\n}\n\n.footer {\n display: flex;\n gap: var(--pharos-spacing-2);\n justify-content: flex-end;\n margin-top: auto;\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/components/DropdownMenu.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/components/DropdownMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;AAEjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAEpD;yBAFe,YAAY;;;AAM5B;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;AAE3E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,+BAElE;yBAFe,mBAAmB;;;AAMnC;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC;IACjF;;;OAGG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD;;;OAGG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC;CACnE;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAe,EACf,KAAe,EACf,UAAc,EACd,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,wBAAwB,+BAmB1B;yBA1Be,mBAAmB;;;AA8BnC;;;;;;GAMG;AACH,MAAM,WAAW,qBAAsB,SAAQ,cAAc,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC;IAC7E;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;CACrC;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAmB,EACnB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,qBAAqB,+BAQvB;yBAZe,gBAAgB;;;AAgBhC,8CAA8C;AAC9C,MAAM,MAAM,0BAA0B,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;AAE/E,wBAAgB,qBAAqB,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,0BAA0B,+BAQvF;yBARe,qBAAqB;;;AAYrC;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC;AAE5E,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,sBAAsB,+BAQ/E;yBARe,iBAAiB;;;AAYjC;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AAEvE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAE9D;yBAFe,iBAAiB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import t from "./DropdownMenu.module.js";
|
|
4
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
5
|
+
import { Menu as r } from "@base-ui/react/menu";
|
|
6
|
+
//#region src/components/DropdownMenu.tsx
|
|
7
|
+
function i(e) {
|
|
8
|
+
return /* @__PURE__ */ n(r.Root, { ...e });
|
|
9
|
+
}
|
|
10
|
+
i.displayName = "DropdownMenu";
|
|
11
|
+
function a(e) {
|
|
12
|
+
return /* @__PURE__ */ n(r.Trigger, { ...e });
|
|
13
|
+
}
|
|
14
|
+
a.displayName = "DropdownMenuTrigger";
|
|
15
|
+
function o({ side: i = "bottom", align: a = "start", sideOffset: o = 8, className: s, children: c, ...l }) {
|
|
16
|
+
return /* @__PURE__ */ n(r.Portal, { children: /* @__PURE__ */ n(r.Positioner, {
|
|
17
|
+
className: t.positioner,
|
|
18
|
+
side: i,
|
|
19
|
+
align: a,
|
|
20
|
+
sideOffset: o,
|
|
21
|
+
children: /* @__PURE__ */ n(r.Popup, {
|
|
22
|
+
"data-pharos-slot": "dropdown-menu-content",
|
|
23
|
+
className: e(t.content, s),
|
|
24
|
+
...l,
|
|
25
|
+
children: c
|
|
26
|
+
})
|
|
27
|
+
}) });
|
|
28
|
+
}
|
|
29
|
+
o.displayName = "DropdownMenuContent";
|
|
30
|
+
function s({ variant: i = "default", className: a, ...o }) {
|
|
31
|
+
return /* @__PURE__ */ n(r.Item, {
|
|
32
|
+
"data-pharos-variant": i,
|
|
33
|
+
className: e(t.item, i === "destructive" && t.itemDestructive, a),
|
|
34
|
+
...o
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
s.displayName = "DropdownMenuItem";
|
|
38
|
+
function c({ className: i, ...a }) {
|
|
39
|
+
return /* @__PURE__ */ n(r.Separator, {
|
|
40
|
+
"data-pharos-slot": "dropdown-menu-separator",
|
|
41
|
+
className: e(t.separator, i),
|
|
42
|
+
...a
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
c.displayName = "DropdownMenuSeparator";
|
|
46
|
+
function l({ className: i, ...a }) {
|
|
47
|
+
return /* @__PURE__ */ n(r.GroupLabel, {
|
|
48
|
+
"data-pharos-slot": "dropdown-menu-label",
|
|
49
|
+
className: e(t.label, i),
|
|
50
|
+
...a
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
l.displayName = "DropdownMenuLabel";
|
|
54
|
+
function u(e) {
|
|
55
|
+
return /* @__PURE__ */ n(r.Group, {
|
|
56
|
+
"data-pharos-slot": "dropdown-menu-group",
|
|
57
|
+
...e
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
u.displayName = "DropdownMenuGroup";
|
|
61
|
+
//#endregion
|
|
62
|
+
export { i as DropdownMenu, o as DropdownMenuContent, u as DropdownMenuGroup, s as DropdownMenuItem, l as DropdownMenuLabel, c as DropdownMenuSeparator, a as DropdownMenuTrigger };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=DropdownMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../src/components/DropdownMenu.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentProps } from 'react';\nimport { Menu } from '@base-ui/react/menu';\nimport { cn } from '../lib/cn';\nimport styles from './DropdownMenu.module.css';\n\n/**\n * Action menu anchored to a trigger (Decision D15). Wraps Base UI's\n * `Menu.*` parts, which implement the ARIA APG **menu button** pattern:\n * `role=\"menu\"` / `menuitem`, roving focus, arrow-key navigation,\n * typeahead, and Escape-to-close with focus return to the trigger.\n *\n * This is deliberately distinct from a future `Popover` atom. A\n * DropdownMenu is for **commands** (the menu-button contract); free-form\n * anchored content (forms, navigation links) belongs in a Popover (the\n * disclosure/dialog contract). 7 of 8 surveyed design systems separate\n * the two for exactly this reason — putting `role=\"menu\"` around\n * arbitrary content breaks screen-reader expectations. See\n * `NAMING-decisions.md` § DropdownMenu.\n *\n * Naming follows shadcn (`DropdownMenu*`, not Base UI's `Menu`). The\n * compound surface collapses Base UI's `Portal` + `Positioner` + `Popup`\n * into a single `DropdownMenuContent` with `side` / `align` /\n * `sideOffset` props — the same ergonomics shadcn exposes over Radix.\n *\n * v1 ships the parts the known consumers need (kebab row menus, the\n * MobileHeader user actions): Trigger, Content, Item, Separator, Label,\n * Group. `CheckboxItem`, `RadioItem`, submenus and `LinkItem` are\n * deliberately deferred — no Alexandria call-site exercises them, and the\n * compound API makes them additive without a breaking change.\n *\n * @example\n * <DropdownMenu>\n * <DropdownMenuTrigger render={<IconButton aria-label=\"Options\"><MoreIcon /></IconButton>} />\n * <DropdownMenuContent>\n * <DropdownMenuItem onClick={onRename}>Rename</DropdownMenuItem>\n * <DropdownMenuSeparator />\n * <DropdownMenuItem variant=\"destructive\" onClick={onDelete}>Delete</DropdownMenuItem>\n * </DropdownMenuContent>\n * </DropdownMenu>\n */\nexport type DropdownMenuProps = ComponentProps<typeof Menu.Root>;\n\nexport function DropdownMenu(props: DropdownMenuProps) {\n return <Menu.Root {...props} />;\n}\n\nDropdownMenu.displayName = 'DropdownMenu';\n\n/**\n * The element that opens the menu. Renders a native `<button>` by\n * default; pass `render` to compose any control while keeping the menu\n * wiring — the dominant case is the kebab `IconButton` (D13):\n * `<DropdownMenuTrigger render={<IconButton aria-label=\"Options\"><MoreIcon /></IconButton>} />`.\n */\nexport type DropdownMenuTriggerProps = ComponentProps<typeof Menu.Trigger>;\n\nexport function DropdownMenuTrigger(props: DropdownMenuTriggerProps) {\n return <Menu.Trigger {...props} />;\n}\n\nDropdownMenuTrigger.displayName = 'DropdownMenuTrigger';\n\n/**\n * The floating surface. Collapses Base UI's `Portal` + `Positioner` +\n * `Popup` so the consumer positions the menu with the three props that\n * actually matter at the call site. Defaults match the common dropdown:\n * opens below the trigger, aligned to its start edge, 8px away.\n */\nexport interface DropdownMenuContentProps extends ComponentProps<typeof Menu.Popup> {\n /**\n * Which side of the trigger the menu opens on.\n * @default 'bottom'\n */\n side?: ComponentProps<typeof Menu.Positioner>['side'];\n /**\n * Alignment of the menu relative to the trigger along the chosen side.\n * @default 'start'\n */\n align?: ComponentProps<typeof Menu.Positioner>['align'];\n /**\n * Gap in pixels between the trigger and the menu.\n * @default 8\n */\n sideOffset?: ComponentProps<typeof Menu.Positioner>['sideOffset'];\n}\n\nexport function DropdownMenuContent({\n side = 'bottom',\n align = 'start',\n sideOffset = 8,\n className,\n children,\n ...rest\n}: DropdownMenuContentProps) {\n return (\n <Menu.Portal>\n <Menu.Positioner\n className={styles.positioner}\n side={side}\n align={align}\n sideOffset={sideOffset}\n >\n <Menu.Popup\n data-pharos-slot=\"dropdown-menu-content\"\n className={cn(styles.content, className)}\n {...rest}\n >\n {children}\n </Menu.Popup>\n </Menu.Positioner>\n </Menu.Portal>\n );\n}\n\nDropdownMenuContent.displayName = 'DropdownMenuContent';\n\n/**\n * A single action row. `variant=\"destructive\"` tints the row with the\n * error foreground for delete / remove actions — the same tone the\n * destructive Button uses, and the equivalent of shadcn's destructive\n * item variant. By default a click closes the menu (`closeOnClick`);\n * pass `closeOnClick={false}` for rows that should keep it open.\n */\nexport interface DropdownMenuItemProps extends ComponentProps<typeof Menu.Item> {\n /**\n * Visual tone of the row.\n * @default 'default'\n */\n variant?: 'default' | 'destructive';\n}\n\nexport function DropdownMenuItem({\n variant = 'default',\n className,\n ...rest\n}: DropdownMenuItemProps) {\n return (\n <Menu.Item\n data-pharos-variant={variant}\n className={cn(styles.item, variant === 'destructive' && styles.itemDestructive, className)}\n {...rest}\n />\n );\n}\n\nDropdownMenuItem.displayName = 'DropdownMenuItem';\n\n/** A thin divider between groups of items. */\nexport type DropdownMenuSeparatorProps = ComponentProps<typeof Menu.Separator>;\n\nexport function DropdownMenuSeparator({ className, ...rest }: DropdownMenuSeparatorProps) {\n return (\n <Menu.Separator\n data-pharos-slot=\"dropdown-menu-separator\"\n className={cn(styles.separator, className)}\n {...rest}\n />\n );\n}\n\nDropdownMenuSeparator.displayName = 'DropdownMenuSeparator';\n\n/**\n * A non-interactive heading for a group of items. Renders Base UI's\n * `Menu.GroupLabel`, which wires `aria-labelledby` on the surrounding\n * `DropdownMenuGroup` — so use the two together when a section needs an\n * accessible name.\n */\nexport type DropdownMenuLabelProps = ComponentProps<typeof Menu.GroupLabel>;\n\nexport function DropdownMenuLabel({ className, ...rest }: DropdownMenuLabelProps) {\n return (\n <Menu.GroupLabel\n data-pharos-slot=\"dropdown-menu-label\"\n className={cn(styles.label, className)}\n {...rest}\n />\n );\n}\n\nDropdownMenuLabel.displayName = 'DropdownMenuLabel';\n\n/**\n * Groups related items under an optional `DropdownMenuLabel` for\n * accessible sectioning (`role=\"group\"`).\n */\nexport type DropdownMenuGroupProps = ComponentProps<typeof Menu.Group>;\n\nexport function DropdownMenuGroup(props: DropdownMenuGroupProps) {\n return <Menu.Group data-pharos-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nDropdownMenuGroup.displayName = 'DropdownMenuGroup';\n"],"mappings":";;;;;;AA4CA,SAAgB,EAAa,GAA0B;CACrD,OAAO,kBAAC,EAAK,MAAN,EAAW,GAAI,EAAQ,CAAA;AAChC;AAEA,EAAa,cAAc;AAU3B,SAAgB,EAAoB,GAAiC;CACnE,OAAO,kBAAC,EAAK,SAAN,EAAc,GAAI,EAAQ,CAAA;AACnC;AAEA,EAAoB,cAAc;AA0BlC,SAAgB,EAAoB,EAClC,UAAO,UACP,WAAQ,SACR,gBAAa,GACb,cACA,aACA,GAAG,KACwB;CAC3B,OACE,kBAAC,EAAK,QAAN,EAAA,UACE,kBAAC,EAAK,YAAN;EACE,WAAW,EAAO;EACZ;EACC;EACK;YAEZ,kBAAC,EAAK,OAAN;GACE,oBAAiB;GACjB,WAAW,EAAG,EAAO,SAAS,CAAS;GACvC,GAAI;GAEH;EACS,CAAA;CACG,CAAA,EACN,CAAA;AAEjB;AAEA,EAAoB,cAAc;AAiBlC,SAAgB,EAAiB,EAC/B,aAAU,WACV,cACA,GAAG,KACqB;CACxB,OACE,kBAAC,EAAK,MAAN;EACE,uBAAqB;EACrB,WAAW,EAAG,EAAO,MAAM,MAAY,iBAAiB,EAAO,iBAAiB,CAAS;EACzF,GAAI;CACL,CAAA;AAEL;AAEA,EAAiB,cAAc;AAK/B,SAAgB,EAAsB,EAAE,cAAW,GAAG,KAAoC;CACxF,OACE,kBAAC,EAAK,WAAN;EACE,oBAAiB;EACjB,WAAW,EAAG,EAAO,WAAW,CAAS;EACzC,GAAI;CACL,CAAA;AAEL;AAEA,EAAsB,cAAc;AAUpC,SAAgB,EAAkB,EAAE,cAAW,GAAG,KAAgC;CAChF,OACE,kBAAC,EAAK,YAAN;EACE,oBAAiB;EACjB,WAAW,EAAG,EAAO,OAAO,CAAS;EACrC,GAAI;CACL,CAAA;AAEL;AAEA,EAAkB,cAAc;AAQhC,SAAgB,EAAkB,GAA+B;CAC/D,OAAO,kBAAC,EAAK,OAAN;EAAY,oBAAiB;EAAsB,GAAI;CAAQ,CAAA;AACxE;AAEA,EAAkB,cAAc"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
positioner: "_positioner_ua6x3_9",
|
|
3
|
+
content: "_content_ua6x3_4",
|
|
4
|
+
item: "_item_ua6x3_5",
|
|
5
|
+
itemDestructive: "_itemDestructive_ua6x3_113",
|
|
6
|
+
separator: "_separator_ua6x3_6",
|
|
7
|
+
label: "_label_ua6x3_6"
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=DropdownMenu.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.module.js","names":[],"sources":["../../src/components/DropdownMenu.module.css"],"sourcesContent":["/* DropdownMenu.module.css\n *\n * Per-component CSS Modules definition (Decision D9). Hashed into\n * `dist/styles.css`. Styles the floating surface (`.content`), the\n * action rows (`.item`, with a `destructive` tone), the structural\n * parts (`.separator`, `.label`, `.group`), and the open/close\n * transition driven by Base UI's `data-*` state attributes.\n *\n * z-index lives on `.positioner` (see below). Base UI portals the menu to the\n * end of `<body>`; that floats it above in-flow content, but not above a\n * host-app overlay (drawer/modal) with its own z-index — and a z-index on the\n * Popup is trapped by the Positioner's transform stacking context. The\n * Positioner therefore carries `z-index: var(--pharos-z-index-popover, …)`,\n * the shared scale Combobox / Select / Popover all use now. */\n\n/* === Positioner (Menu.Positioner) ===\n * z-index lives here, not on `.content` (the Popup). Base UI's Positioner\n * anchors with `transform`, which creates a stacking context — so a z-index\n * on the inner Popup is trapped inside it and cannot rise above a host-app\n * overlay (e.g. a drawer with its own z-index). The z-index must sit on the\n * Positioner itself.\n *\n * Sourced from `--pharos-z-index-popover` (no hardcoded value — NON-NEGOTIABLE\n * rule 1). The fallback suits apps with no high z-index scale; an app whose\n * own overlays sit higher (drawers, modals) raises every Pharos popup at once\n * by overriding `--pharos-z-index-popover` in `:root`. */\n.positioner {\n z-index: var(--pharos-z-index-popover, 1000);\n}\n\n/* === Floating surface (Menu.Popup) === */\n.content {\n box-sizing: border-box;\n min-width: 12rem;\n padding: var(--pharos-spacing-1);\n\n background-color: var(--pharos-color-base-white);\n border: 1px solid var(--pharos-color-neutral-200);\n border-radius: var(--pharos-radius-md);\n box-shadow: var(--pharos-shadow-lg);\n\n font-family: var(--pharos-font-family-sans);\n color: var(--pharos-color-neutral-900);\n\n /* Open/close transition keyed off Base UI's `data-starting-style` /\n * `data-ending-style`. The popup fades and lifts a few pixels from\n * the anchored side — the same motion shadcn / Radix ship. */\n transform-origin: var(--transform-origin);\n transition-property: opacity, transform;\n transition-duration: var(--pharos-duration-fast);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n.content[data-starting-style],\n.content[data-ending-style] {\n opacity: 0;\n transform: scale(0.96);\n}\n\n/* === Action row (Menu.Item) === */\n.item {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--pharos-spacing-2);\n width: 100%;\n padding: var(--pharos-spacing-1-5) var(--pharos-spacing-2);\n\n border-radius: var(--pharos-radius-sm);\n font-size: var(--pharos-font-size-sm);\n font-weight: var(--pharos-font-weight-medium);\n line-height: 1.25;\n color: var(--pharos-color-neutral-700);\n\n cursor: default;\n user-select: none;\n outline: none;\n scroll-margin: var(--pharos-spacing-1);\n\n transition-property: background-color, color;\n transition-duration: var(--pharos-duration-fast);\n transition-timing-function: var(--pharos-easing-out);\n}\n\n/* Base UI marks the active row (keyboard navigation or pointer, when\n * `highlightItemOnHover` is on) with `data-highlighted`. A single\n * highlight state covers both pointer hover and arrow-key focus, so\n * the menu reads identically whichever input drives it. */\n.item[data-highlighted] {\n background-color: var(--pharos-color-neutral-100);\n color: var(--pharos-color-neutral-900);\n}\n\n.item[data-disabled] {\n color: var(--pharos-color-neutral-500);\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* Icons composed inside the row inherit the row colour and size to the\n * text cap height, matching the IconButton `> svg` convention. */\n.item > svg {\n width: 1rem;\n height: 1rem;\n flex-shrink: 0;\n}\n\n/* === Destructive tone ===\n * shadcn ships a `destructive` variant on DropdownMenuItem; Alexandria\n * has several delete / remove actions (career-ladder delete, lesson\n * delete, RemoveBox) that map onto it. The error foreground darkens on\n * highlight the same way the destructive Button does. */\n.itemDestructive {\n color: var(--pharos-color-semantic-error-fg);\n}\n\n.itemDestructive[data-highlighted] {\n background-color: var(--pharos-color-semantic-error-bg);\n color: var(--pharos-color-semantic-error-fg-hover);\n}\n\n/* === Separator (Menu.Separator) === */\n.separator {\n height: 1px;\n margin: var(--pharos-spacing-1) calc(var(--pharos-spacing-1) * -1);\n background-color: var(--pharos-color-neutral-200);\n}\n\n/* === Group label (Menu.GroupLabel) === */\n.label {\n padding: var(--pharos-spacing-1-5) var(--pharos-spacing-2);\n font-size: var(--pharos-font-size-sm);\n font-weight: var(--pharos-font-weight-semibold);\n color: var(--pharos-color-neutral-500);\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconButton.d.ts","sourceRoot":"","sources":["../../src/components/IconButton.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IconButton.d.ts","sourceRoot":"","sources":["../../src/components/IconButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE;;;;;;;GAOG;AACH,QAAA,MAAM,kBAAkB;;;8EAkBtB,CAAC;AAEH,KAAK,sBAAsB,GAAG,YAAY,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,KAAK,sBAAsB,GACvB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,KAAK,CAAA;CAAE,GACnD;IAAE,YAAY,CAAC,EAAE,KAAK,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,EAClC,YAAY,GAAG,iBAAiB,CACjC,GACC,sBAAsB,GACtB,sBAAsB,GAAG;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEJ,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,SAAS,EACT,MAAM,EACN,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,IAAe,EACf,QAAQ,EACR,GAAG,EACH,GAAG,IAAI,EACR,EAAE,eAAe,8FAmBjB;yBA9Be,UAAU;;;AAkC1B,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { Spinner as t } from "./Spinner.js";
|
|
4
|
+
import n from "./IconButton.module.js";
|
|
5
|
+
import { useRender as r } from "@base-ui/react/use-render";
|
|
6
|
+
import { cva as i } from "class-variance-authority";
|
|
7
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/IconButton.tsx
|
|
9
|
+
var o = i(n.button, {
|
|
10
|
+
variants: {
|
|
11
|
+
intent: {
|
|
12
|
+
primary: n.primary,
|
|
13
|
+
secondary: n.secondary,
|
|
14
|
+
ghost: n.ghost,
|
|
15
|
+
destructive: n.destructive
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
sm: n.sizeSm,
|
|
19
|
+
md: n.sizeMd,
|
|
20
|
+
lg: n.sizeLg
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
intent: "ghost",
|
|
25
|
+
size: "md"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
function s({ render: n, className: i, intent: s, size: c, isLoading: l, disabled: u, type: d = "button", children: f, ref: p, ...m }) {
|
|
29
|
+
let h = s ?? "ghost", g = c ?? "md";
|
|
30
|
+
return r({
|
|
31
|
+
render: n ?? /* @__PURE__ */ a("button", {}),
|
|
32
|
+
ref: p,
|
|
33
|
+
defaultTagName: "button",
|
|
34
|
+
props: {
|
|
35
|
+
type: d,
|
|
36
|
+
"data-pharos-intent": h,
|
|
37
|
+
"data-pharos-size": g,
|
|
38
|
+
"aria-busy": l ? !0 : void 0,
|
|
39
|
+
disabled: !!u || !!l,
|
|
40
|
+
className: e(o({
|
|
41
|
+
intent: h,
|
|
42
|
+
size: g
|
|
43
|
+
}), i),
|
|
44
|
+
...m,
|
|
45
|
+
children: l ? /* @__PURE__ */ a(t, { size: g }) : f
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
s.displayName = "IconButton";
|
|
50
|
+
//#endregion
|
|
51
|
+
export { s as IconButton, o as iconButtonVariants };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=IconButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IconButton.js","names":[],"sources":["../../src/components/IconButton.tsx"],"sourcesContent":["'use client';\n\nimport { useRender } from '@base-ui/react/use-render';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn';\nimport { Spinner } from './Spinner';\nimport styles from './IconButton.module.css';\n\n/**\n * Maps the two public variant axes (`intent`, `size`) to the per-class names\n * inside `IconButton.module.css`. Mirrors the Button atom so an IconButton\n * sits next to a Button of the same size with matching height (32 / 40 /\n * 48 px) and shares the same intent vocabulary — `ghost` is the dominant\n * intent for icon-only controls (close, expand, dismiss…) so it is the\n * default, unlike Button where `primary` defaults.\n */\nconst iconButtonVariants = cva(styles.button, {\n variants: {\n intent: {\n primary: styles.primary,\n secondary: styles.secondary,\n ghost: styles.ghost,\n destructive: styles.destructive,\n },\n size: {\n sm: styles.sizeSm,\n md: styles.sizeMd,\n lg: styles.sizeLg,\n },\n },\n defaultVariants: {\n intent: 'ghost',\n size: 'md',\n },\n});\n\ntype IconButtonVariantProps = VariantProps<typeof iconButtonVariants>;\n\n/**\n * Either `aria-label` or `aria-labelledby` is required at the type level.\n * An icon-only control with no accessible name is unusable for assistive\n * tech (WCAG 4.1.2). The atom enforces the constraint by construction —\n * the equivalent runtime check Chakra / Mantine emit as a console warning\n * is here a TypeScript error, so a missing label cannot ship.\n */\ntype IconButtonRequiredA11y =\n | { 'aria-label': string; 'aria-labelledby'?: never }\n | { 'aria-label'?: never; 'aria-labelledby': string };\n\n/**\n * Props accepted by `<IconButton>`. Extends Base UI's\n * `useRender.ComponentProps<'button'>` minus the optional `aria-label` /\n * `aria-labelledby` (re-added as required), plus the variant axes and an\n * `isLoading` toggle that swaps the icon for a `<Spinner>` of the same\n * size.\n *\n * The `render` prop composes the IconButton as a different element while\n * keeping its styles and behaviour — same shape as Button, useful for\n * `<IconButton render={<Link to=\"/next\" />} aria-label=\"Next\">…</IconButton>`\n * call-sites (e.g. circle navigation buttons).\n */\nexport type IconButtonProps = Omit<\n useRender.ComponentProps<'button'>,\n 'aria-label' | 'aria-labelledby'\n> &\n IconButtonVariantProps &\n IconButtonRequiredA11y & {\n /**\n * When `true`, replaces the icon with a `<Spinner>` of matching size,\n * sets `disabled` so the action cannot fire mid-flight, and exposes\n * `aria-busy=\"true\"` so screen readers announce the in-progress state.\n */\n isLoading?: boolean;\n };\n\nexport function IconButton({\n render,\n className,\n intent,\n size,\n isLoading,\n disabled,\n type = 'button',\n children,\n ref,\n ...rest\n}: IconButtonProps) {\n const resolvedIntent = intent ?? 'ghost';\n const resolvedSize = size ?? 'md';\n const isDisabled = Boolean(disabled) || Boolean(isLoading);\n return useRender({\n render: render ?? <button />,\n ref,\n defaultTagName: 'button',\n props: {\n type,\n 'data-pharos-intent': resolvedIntent,\n 'data-pharos-size': resolvedSize,\n 'aria-busy': isLoading ? true : undefined,\n disabled: isDisabled,\n className: cn(iconButtonVariants({ intent: resolvedIntent, size: resolvedSize }), className),\n ...rest,\n children: isLoading ? <Spinner size={resolvedSize} /> : children,\n },\n });\n}\n\nIconButton.displayName = 'IconButton';\n\nexport { iconButtonVariants };\n"],"mappings":";;;;;;;;AAgBA,IAAM,IAAqB,EAAI,EAAO,QAAQ;CAC5C,UAAU;EACR,QAAQ;GACN,SAAS,EAAO;GAChB,WAAW,EAAO;GAClB,OAAO,EAAO;GACd,aAAa,EAAO;EACtB;EACA,MAAM;GACJ,IAAI,EAAO;GACX,IAAI,EAAO;GACX,IAAI,EAAO;EACb;CACF;CACA,iBAAiB;EACf,QAAQ;EACR,MAAM;CACR;AACF,CAAC;AAyCD,SAAgB,EAAW,EACzB,WACA,cACA,WACA,SACA,cACA,aACA,UAAO,UACP,aACA,QACA,GAAG,KACe;CAClB,IAAM,IAAiB,KAAU,SAC3B,IAAe,KAAQ;CAE7B,OAAO,EAAU;EACf,QAAQ,KAAU,kBAAC,UAAD,CAAS,CAAA;EAC3B;EACA,gBAAgB;EAChB,OAAO;GACL;GACA,sBAAsB;GACtB,oBAAoB;GACpB,aAAa,IAAY,KAAO,KAAA;GAChC,UAVe,EAAQ,KAAa,EAAQ;GAW5C,WAAW,EAAG,EAAmB;IAAE,QAAQ;IAAgB,MAAM;GAAa,CAAC,GAAG,CAAS;GAC3F,GAAG;GACH,UAAU,IAAY,kBAAC,GAAD,EAAS,MAAM,EAAe,CAAA,IAAI;EAC1D;CACF,CAAC;AACH;AAEA,EAAW,cAAc"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
button: "_button_ltuh2_14",
|
|
3
|
+
primary: "_primary_ltuh2_74",
|
|
4
|
+
secondary: "_secondary_ltuh2_88",
|
|
5
|
+
ghost: "_ghost_ltuh2_107",
|
|
6
|
+
destructive: "_destructive_ltuh2_121",
|
|
7
|
+
sizeSm: "_sizeSm_ltuh2_150",
|
|
8
|
+
sizeMd: "_sizeMd_ltuh2_160",
|
|
9
|
+
sizeLg: "_sizeLg_ltuh2_170"
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { e as default };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=IconButton.module.js.map
|