@financedistrict/apps-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +119 -0
- package/dist/Radio-BYWz1n7p.d.cts +32 -0
- package/dist/Radio-BYWz1n7p.d.ts +32 -0
- package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
- package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
- package/dist/accordion.cjs +322 -0
- package/dist/accordion.d.cts +156 -0
- package/dist/accordion.d.ts +156 -0
- package/dist/accordion.js +2 -0
- package/dist/apps-ui.umd.js +1 -0
- package/dist/button.cjs +366 -0
- package/dist/button.d.cts +52 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +3 -0
- package/dist/checkbox.cjs +74 -0
- package/dist/checkbox.d.cts +63 -0
- package/dist/checkbox.d.ts +63 -0
- package/dist/checkbox.js +2 -0
- package/dist/chunk-3RU7JMFE.js +27 -0
- package/dist/chunk-5TWRK4NW.js +783 -0
- package/dist/chunk-7EOSDFS3.js +9 -0
- package/dist/chunk-AIQ4UOGK.js +142 -0
- package/dist/chunk-B3MMGTPV.js +200 -0
- package/dist/chunk-DAHQYLUI.js +146 -0
- package/dist/chunk-DGBXADSS.js +1 -0
- package/dist/chunk-E6KPNTWW.js +295 -0
- package/dist/chunk-EOAB6G2D.js +149 -0
- package/dist/chunk-FAOVKDVM.js +32 -0
- package/dist/chunk-FV76Z4Z2.js +78 -0
- package/dist/chunk-GLTLBUZ7.js +285 -0
- package/dist/chunk-GTZFV43T.js +154 -0
- package/dist/chunk-HRGZNL4A.js +88 -0
- package/dist/chunk-IEVN3J2F.js +98 -0
- package/dist/chunk-IRS3T6LF.js +352 -0
- package/dist/chunk-IYYHCQXG.js +146 -0
- package/dist/chunk-JIXW3455.js +89 -0
- package/dist/chunk-N4VAP2GB.js +77 -0
- package/dist/chunk-SJ2ZRYVV.js +167 -0
- package/dist/chunk-SNU4YXOR.js +66 -0
- package/dist/chunk-TT6JQATV.js +99 -0
- package/dist/chunk-UPQ3ZPCO.js +66 -0
- package/dist/chunk-UVUSVFOH.js +264 -0
- package/dist/chunk-WJFPGGJB.js +255 -0
- package/dist/chunk-WXEBQA7A.js +371 -0
- package/dist/chunk-Y2O4YYZQ.js +184 -0
- package/dist/cut-corner.cjs +111 -0
- package/dist/cut-corner.d.cts +49 -0
- package/dist/cut-corner.d.ts +49 -0
- package/dist/cut-corner.js +1 -0
- package/dist/dialog.cjs +441 -0
- package/dist/dialog.d.cts +240 -0
- package/dist/dialog.d.ts +240 -0
- package/dist/dialog.js +3 -0
- package/dist/feature-card.cjs +182 -0
- package/dist/feature-card.d.cts +54 -0
- package/dist/feature-card.d.ts +54 -0
- package/dist/feature-card.js +2 -0
- package/dist/filter.cjs +873 -0
- package/dist/filter.d.cts +129 -0
- package/dist/filter.d.ts +129 -0
- package/dist/filter.js +6 -0
- package/dist/index.cjs +4583 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +27 -0
- package/dist/inline-select.cjs +856 -0
- package/dist/inline-select.d.cts +92 -0
- package/dist/inline-select.d.ts +92 -0
- package/dist/inline-select.js +6 -0
- package/dist/input.cjs +263 -0
- package/dist/input.d.cts +157 -0
- package/dist/input.d.ts +157 -0
- package/dist/input.js +4 -0
- package/dist/panel.cjs +185 -0
- package/dist/panel.d.cts +103 -0
- package/dist/panel.d.ts +103 -0
- package/dist/panel.js +2 -0
- package/dist/property-grid.cjs +110 -0
- package/dist/property-grid.d.cts +56 -0
- package/dist/property-grid.d.ts +56 -0
- package/dist/property-grid.js +2 -0
- package/dist/radio.cjs +74 -0
- package/dist/radio.d.cts +39 -0
- package/dist/radio.d.ts +39 -0
- package/dist/radio.js +2 -0
- package/dist/scrollbar.cjs +34 -0
- package/dist/scrollbar.d.cts +35 -0
- package/dist/scrollbar.d.ts +35 -0
- package/dist/scrollbar.js +1 -0
- package/dist/search-bar.cjs +408 -0
- package/dist/search-bar.d.cts +42 -0
- package/dist/search-bar.d.ts +42 -0
- package/dist/search-bar.js +4 -0
- package/dist/select.cjs +998 -0
- package/dist/select.d.cts +357 -0
- package/dist/select.d.ts +357 -0
- package/dist/select.js +5 -0
- package/dist/selectable-feature-card.cjs +159 -0
- package/dist/selectable-feature-card.d.cts +74 -0
- package/dist/selectable-feature-card.d.ts +74 -0
- package/dist/selectable-feature-card.js +3 -0
- package/dist/sidebar-item.cjs +175 -0
- package/dist/sidebar-item.d.cts +75 -0
- package/dist/sidebar-item.d.ts +75 -0
- package/dist/sidebar-item.js +2 -0
- package/dist/switch.cjs +86 -0
- package/dist/switch.d.cts +72 -0
- package/dist/switch.d.ts +72 -0
- package/dist/switch.js +2 -0
- package/dist/table-cell.cjs +196 -0
- package/dist/table-cell.d.cts +76 -0
- package/dist/table-cell.d.ts +76 -0
- package/dist/table-cell.js +2 -0
- package/dist/table-title.cjs +34 -0
- package/dist/table-title.d.cts +16 -0
- package/dist/table-title.d.ts +16 -0
- package/dist/table-title.js +2 -0
- package/dist/tabs.cjs +299 -0
- package/dist/tabs.d.cts +116 -0
- package/dist/tabs.d.ts +116 -0
- package/dist/tabs.js +2 -0
- package/dist/toast.cjs +87 -0
- package/dist/toast.d.cts +48 -0
- package/dist/toast.d.ts +48 -0
- package/dist/toast.js +2 -0
- package/dist/token-input.cjs +415 -0
- package/dist/token-input.d.cts +90 -0
- package/dist/token-input.d.ts +90 -0
- package/dist/token-input.js +3 -0
- package/dist/tooltip.cjs +384 -0
- package/dist/tooltip.d.cts +142 -0
- package/dist/tooltip.d.ts +142 -0
- package/dist/tooltip.js +2 -0
- package/package.json +200 -0
- package/src/fonts.css +33 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { useCutCornerClipPath, BUTTON_CUT } from './chunk-JIXW3455.js';
|
|
2
|
+
import { cn } from './chunk-7EOSDFS3.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { CircleNotchIcon } from '@phosphor-icons/react';
|
|
5
|
+
import { cva } from 'class-variance-authority';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var buttonVariants = cva(
|
|
9
|
+
[
|
|
10
|
+
// Layout
|
|
11
|
+
"inline-flex shrink-0 items-center justify-center gap-1",
|
|
12
|
+
// Type — the Figma `button-01` text style (Archivo Medium 14/20)
|
|
13
|
+
"button-01",
|
|
14
|
+
// Shape — cut corners are applied as a clip-path in <Button>; keep the box square
|
|
15
|
+
"rounded-none",
|
|
16
|
+
// Transition
|
|
17
|
+
"transition-colors cursor-pointer",
|
|
18
|
+
// Suppress the native ring. The Figma focus ring is drawn OUTSIDE the
|
|
19
|
+
// clip-path (which would swallow an outline here), so cut-corner variants
|
|
20
|
+
// render it on the wrapper in <Button>; ghost renders it directly (below).
|
|
21
|
+
"outline-none",
|
|
22
|
+
// Disabled
|
|
23
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed",
|
|
24
|
+
// Icons
|
|
25
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
variation: {
|
|
30
|
+
primary: [
|
|
31
|
+
"bg-button-primary-background text-button-primary-foreground",
|
|
32
|
+
"hover:bg-button-primary-accent active:bg-button-primary-accent",
|
|
33
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
34
|
+
],
|
|
35
|
+
brand: [
|
|
36
|
+
"bg-button-brand-primary-background text-button-brand-primary-foreground",
|
|
37
|
+
"hover:bg-button-brand-primary-accent active:bg-button-brand-primary-accent",
|
|
38
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
39
|
+
],
|
|
40
|
+
destructive: [
|
|
41
|
+
"bg-button-destructive-background text-button-destructive-primary-foreground",
|
|
42
|
+
"hover:bg-button-destructive-accent active:bg-button-destructive-accent",
|
|
43
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
44
|
+
],
|
|
45
|
+
// Outline — background fill + an SVG stroke border drawn in <Button>.
|
|
46
|
+
secondary: [
|
|
47
|
+
"bg-button-secondary-background text-button-secondary-foreground",
|
|
48
|
+
"hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
|
|
49
|
+
"disabled:text-button-secondary-foreground-muted"
|
|
50
|
+
],
|
|
51
|
+
// No background, no cut corner — so no wrapper; ring on the element.
|
|
52
|
+
// `outline-solid` restores the style that base `outline-none` zeroed.
|
|
53
|
+
ghost: [
|
|
54
|
+
"bg-transparent text-button-secondary-foreground",
|
|
55
|
+
"hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
|
|
56
|
+
"disabled:text-button-secondary-foreground-muted",
|
|
57
|
+
// `rounded-xs` (2px) keeps the ghost focus ring as sharp as the other
|
|
58
|
+
// variants' corners (which now use sharp chamfers + 2px square corners).
|
|
59
|
+
"rounded-xs focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
size: {
|
|
63
|
+
sm: "h-8 px-2.5 [&_svg:not([class*='size-'])]:size-4",
|
|
64
|
+
md: "h-10 px-2.5 [&_svg:not([class*='size-'])]:size-4",
|
|
65
|
+
lg: "h-12 px-3 [&_svg:not([class*='size-'])]:size-4"
|
|
66
|
+
},
|
|
67
|
+
iconOnly: {
|
|
68
|
+
true: "",
|
|
69
|
+
false: ""
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
compoundVariants: [
|
|
73
|
+
// Icon-only: square footprint, no horizontal padding.
|
|
74
|
+
{ iconOnly: true, size: "sm", className: "w-8 px-0" },
|
|
75
|
+
{ iconOnly: true, size: "md", className: "w-10 px-0" },
|
|
76
|
+
{ iconOnly: true, size: "lg", className: "w-12 px-0" },
|
|
77
|
+
// Ghost has no chamfer/background, so it sits flush with no inset padding.
|
|
78
|
+
{ variation: "ghost", iconOnly: false, className: "px-0" }
|
|
79
|
+
],
|
|
80
|
+
defaultVariants: {
|
|
81
|
+
variation: "primary",
|
|
82
|
+
size: "md",
|
|
83
|
+
iconOnly: false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
var FOCUS_RING_GAP = 3;
|
|
88
|
+
var FOCUS_RING_WIDTH = 2;
|
|
89
|
+
function setRef(ref, node) {
|
|
90
|
+
if (typeof ref === "function") ref(node);
|
|
91
|
+
else if (ref) ref.current = node;
|
|
92
|
+
}
|
|
93
|
+
function Button({
|
|
94
|
+
className,
|
|
95
|
+
variation = "primary",
|
|
96
|
+
size = "md",
|
|
97
|
+
iconOnly = false,
|
|
98
|
+
asChild = false,
|
|
99
|
+
loading = false,
|
|
100
|
+
leftSlot,
|
|
101
|
+
rightSlot,
|
|
102
|
+
wrapperClassName,
|
|
103
|
+
disabled,
|
|
104
|
+
style,
|
|
105
|
+
children,
|
|
106
|
+
onClick,
|
|
107
|
+
ref,
|
|
108
|
+
...props
|
|
109
|
+
}) {
|
|
110
|
+
const {
|
|
111
|
+
ref: cutRef,
|
|
112
|
+
clipPath,
|
|
113
|
+
pathD,
|
|
114
|
+
outsetPathD
|
|
115
|
+
} = useCutCornerClipPath(BUTTON_CUT.cut, {
|
|
116
|
+
radius: BUTTON_CUT.radius,
|
|
117
|
+
radiusCuts: BUTTON_CUT.radiusCuts,
|
|
118
|
+
outset: FOCUS_RING_GAP
|
|
119
|
+
});
|
|
120
|
+
const setRootRef = React.useCallback(
|
|
121
|
+
(node) => {
|
|
122
|
+
cutRef.current = node;
|
|
123
|
+
setRef(ref, node);
|
|
124
|
+
},
|
|
125
|
+
[cutRef, ref]
|
|
126
|
+
);
|
|
127
|
+
const isDisabled = disabled;
|
|
128
|
+
const hasCutCorner = variation !== "ghost";
|
|
129
|
+
const hasSvgBorder = variation === "secondary";
|
|
130
|
+
const handleClick = loading ? (event) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
event.stopPropagation();
|
|
133
|
+
} : onClick;
|
|
134
|
+
const rootClassName = cn(
|
|
135
|
+
buttonVariants({ variation, size, iconOnly }),
|
|
136
|
+
hasSvgBorder && "border-0",
|
|
137
|
+
loading && "pointer-events-none",
|
|
138
|
+
className
|
|
139
|
+
);
|
|
140
|
+
const rootStyle = hasCutCorner ? { ...style, clipPath } : style;
|
|
141
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
142
|
+
loading ? /* @__PURE__ */ jsx(CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : leftSlot,
|
|
143
|
+
!iconOnly && children,
|
|
144
|
+
iconOnly && !loading && children,
|
|
145
|
+
!iconOnly && rightSlot
|
|
146
|
+
] });
|
|
147
|
+
let rootEl;
|
|
148
|
+
if (asChild) {
|
|
149
|
+
if (!React.isValidElement(children)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"Button: `asChild` requires a single React element child"
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const child = children;
|
|
155
|
+
const childProps = child.props;
|
|
156
|
+
const childOnClick = childProps.onClick;
|
|
157
|
+
rootEl = React.cloneElement(
|
|
158
|
+
child,
|
|
159
|
+
{
|
|
160
|
+
...props,
|
|
161
|
+
...childProps,
|
|
162
|
+
"data-slot": "button",
|
|
163
|
+
"data-loading": loading || void 0,
|
|
164
|
+
"aria-busy": loading || void 0,
|
|
165
|
+
"aria-disabled": loading || void 0,
|
|
166
|
+
// Block activation while loading; otherwise run both handlers.
|
|
167
|
+
onClick: loading ? (event) => {
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
event.stopPropagation();
|
|
170
|
+
} : (event) => {
|
|
171
|
+
onClick?.(event);
|
|
172
|
+
childOnClick?.(event);
|
|
173
|
+
},
|
|
174
|
+
ref: (node) => {
|
|
175
|
+
setRootRef(node);
|
|
176
|
+
setRef(childProps.ref, node);
|
|
177
|
+
},
|
|
178
|
+
className: cn(
|
|
179
|
+
rootClassName,
|
|
180
|
+
childProps.className
|
|
181
|
+
),
|
|
182
|
+
style: {
|
|
183
|
+
...rootStyle,
|
|
184
|
+
...childProps.style
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
loading ? /* @__PURE__ */ jsx(CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : null,
|
|
188
|
+
childProps.children
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
rootEl = /* @__PURE__ */ jsx(
|
|
192
|
+
"button",
|
|
193
|
+
{
|
|
194
|
+
"data-slot": "button",
|
|
195
|
+
"data-loading": loading || void 0,
|
|
196
|
+
"aria-busy": loading || void 0,
|
|
197
|
+
"aria-disabled": loading || void 0,
|
|
198
|
+
className: rootClassName,
|
|
199
|
+
style: rootStyle,
|
|
200
|
+
disabled: isDisabled,
|
|
201
|
+
...props,
|
|
202
|
+
onClick: handleClick,
|
|
203
|
+
ref: setRootRef,
|
|
204
|
+
children: content
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (!hasCutCorner) return rootEl;
|
|
209
|
+
return /* @__PURE__ */ jsxs("span", { className: cn("group relative inline-block", wrapperClassName), children: [
|
|
210
|
+
rootEl,
|
|
211
|
+
outsetPathD && /* @__PURE__ */ jsx(
|
|
212
|
+
"svg",
|
|
213
|
+
{
|
|
214
|
+
className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible opacity-0 group-has-[:focus-visible]:opacity-100",
|
|
215
|
+
"aria-hidden": "true",
|
|
216
|
+
children: /* @__PURE__ */ jsx(
|
|
217
|
+
"path",
|
|
218
|
+
{
|
|
219
|
+
d: outsetPathD,
|
|
220
|
+
fill: "none",
|
|
221
|
+
strokeWidth: FOCUS_RING_WIDTH,
|
|
222
|
+
className: "stroke-focus"
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
hasSvgBorder && pathD && /* @__PURE__ */ jsx(
|
|
228
|
+
"svg",
|
|
229
|
+
{
|
|
230
|
+
className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible",
|
|
231
|
+
"aria-hidden": "true",
|
|
232
|
+
children: /* @__PURE__ */ jsx(
|
|
233
|
+
"path",
|
|
234
|
+
{
|
|
235
|
+
d: pathD,
|
|
236
|
+
fill: "none",
|
|
237
|
+
strokeWidth: 1,
|
|
238
|
+
className: cn(
|
|
239
|
+
// Disabled locks the muted stroke; the hover/active accent
|
|
240
|
+
// classes must not be present at all (a `group-hover:` variant
|
|
241
|
+
// out-specifies the plain muted class, so it would still
|
|
242
|
+
// recolor the border on hover of a disabled button).
|
|
243
|
+
isDisabled ? "stroke-button-secondary-border-muted" : [
|
|
244
|
+
"stroke-button-secondary-border",
|
|
245
|
+
"group-hover:stroke-button-secondary-border-accent group-active:stroke-button-secondary-border-accent"
|
|
246
|
+
]
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { scrollbarVariants } from './chunk-FAOVKDVM.js';
|
|
2
|
+
import { cn } from './chunk-7EOSDFS3.js';
|
|
3
|
+
import * as React2 from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { IconContext, XIcon } from '@phosphor-icons/react';
|
|
6
|
+
import { cva } from 'class-variance-authority';
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var FOCUSABLE_SELECTOR = [
|
|
10
|
+
"a[href]",
|
|
11
|
+
"button:not([disabled])",
|
|
12
|
+
"textarea:not([disabled])",
|
|
13
|
+
"input:not([disabled])",
|
|
14
|
+
"select:not([disabled])",
|
|
15
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
16
|
+
].join(",");
|
|
17
|
+
function getFocusableElements(container) {
|
|
18
|
+
return Array.from(
|
|
19
|
+
container.querySelectorAll(FOCUSABLE_SELECTOR)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function useFocusTrap({
|
|
23
|
+
active,
|
|
24
|
+
containerRef
|
|
25
|
+
}) {
|
|
26
|
+
React2.useEffect(() => {
|
|
27
|
+
if (!active) return;
|
|
28
|
+
const container = containerRef.current;
|
|
29
|
+
if (!container) return;
|
|
30
|
+
const previouslyFocused = document.activeElement;
|
|
31
|
+
const initial = getFocusableElements(container)[0] ?? container;
|
|
32
|
+
initial.focus();
|
|
33
|
+
const body = document.body;
|
|
34
|
+
const previousOverflow = body.style.overflow;
|
|
35
|
+
body.style.overflow = "hidden";
|
|
36
|
+
const handleKeyDown = (event) => {
|
|
37
|
+
if (event.key !== "Tab") return;
|
|
38
|
+
const focusables = getFocusableElements(container);
|
|
39
|
+
if (focusables.length === 0) {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
container.focus();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const first = focusables[0];
|
|
45
|
+
const last = focusables[focusables.length - 1];
|
|
46
|
+
const activeEl = document.activeElement;
|
|
47
|
+
if (event.shiftKey) {
|
|
48
|
+
if (activeEl === first || !container.contains(activeEl)) {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
last.focus();
|
|
51
|
+
}
|
|
52
|
+
} else if (activeEl === last || !container.contains(activeEl)) {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
first.focus();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
58
|
+
return () => {
|
|
59
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
60
|
+
body.style.overflow = previousOverflow;
|
|
61
|
+
previouslyFocused?.focus?.();
|
|
62
|
+
};
|
|
63
|
+
}, [active, containerRef]);
|
|
64
|
+
}
|
|
65
|
+
var dialogOverlayVariants = cva([
|
|
66
|
+
"fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
67
|
+
"bg-overlay"
|
|
68
|
+
]);
|
|
69
|
+
var dialogContentVariants = cva([
|
|
70
|
+
"relative flex w-[438px] max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)] flex-col gap-6",
|
|
71
|
+
"rounded-md border border-border bg-card-background p-6 shadow-s",
|
|
72
|
+
// Programmatic-focus fallback target (no visible ring when focused as a last
|
|
73
|
+
// resort by the focus trap — the card is not an interactive control).
|
|
74
|
+
"outline-none"
|
|
75
|
+
]);
|
|
76
|
+
var dialogHeaderVariants = cva([
|
|
77
|
+
"flex shrink-0 flex-col items-start gap-4"
|
|
78
|
+
]);
|
|
79
|
+
var dialogHeaderIconVariants = cva([
|
|
80
|
+
"flex items-center text-foreground [&_svg]:size-8 [&_svg]:shrink-0"
|
|
81
|
+
]);
|
|
82
|
+
var dialogHeaderTextVariants = cva(["flex w-full flex-col gap-2"]);
|
|
83
|
+
var dialogCloseButtonVariants = cva([
|
|
84
|
+
"absolute right-6 top-6 flex cursor-pointer items-center justify-center rounded-xs",
|
|
85
|
+
"text-foreground-muted transition-colors hover:text-foreground",
|
|
86
|
+
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
87
|
+
// TW4 needs `outline-solid` to render a solid ring (a bare `outline` won't).
|
|
88
|
+
"outline-none focus-visible:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
|
|
89
|
+
]);
|
|
90
|
+
var dialogBodyVariants = cva([
|
|
91
|
+
"min-h-0 flex-1 overflow-y-auto text-foreground"
|
|
92
|
+
]);
|
|
93
|
+
var dialogFooterVariants = cva([
|
|
94
|
+
"flex w-full shrink-0 gap-2 [&>*]:flex-1 [&_button]:w-full"
|
|
95
|
+
]);
|
|
96
|
+
var DialogContext = React2.createContext(null);
|
|
97
|
+
function useDialogContext(part) {
|
|
98
|
+
const context = React2.useContext(DialogContext);
|
|
99
|
+
if (context === null) {
|
|
100
|
+
throw new Error(`${part} must be used within <Dialog.Root>`);
|
|
101
|
+
}
|
|
102
|
+
return context;
|
|
103
|
+
}
|
|
104
|
+
function DialogRoot({
|
|
105
|
+
open: openProp,
|
|
106
|
+
defaultOpen = false,
|
|
107
|
+
onOpenChange,
|
|
108
|
+
dismissable = true,
|
|
109
|
+
children
|
|
110
|
+
}) {
|
|
111
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React2.useState(defaultOpen);
|
|
112
|
+
const [hasDescription, setHasDescription] = React2.useState(false);
|
|
113
|
+
const isControlled = openProp !== void 0;
|
|
114
|
+
const open = isControlled ? openProp : uncontrolledOpen;
|
|
115
|
+
const reactId = React2.useId();
|
|
116
|
+
const titleId = `${reactId}-title`;
|
|
117
|
+
const descriptionId = `${reactId}-description`;
|
|
118
|
+
const setOpen = React2.useCallback(
|
|
119
|
+
(next) => {
|
|
120
|
+
if (!isControlled) setUncontrolledOpen(next);
|
|
121
|
+
onOpenChange?.(next);
|
|
122
|
+
},
|
|
123
|
+
[isControlled, onOpenChange]
|
|
124
|
+
);
|
|
125
|
+
const value = {
|
|
126
|
+
open,
|
|
127
|
+
setOpen,
|
|
128
|
+
dismissable,
|
|
129
|
+
titleId,
|
|
130
|
+
descriptionId,
|
|
131
|
+
hasDescription,
|
|
132
|
+
setHasDescription
|
|
133
|
+
};
|
|
134
|
+
return /* @__PURE__ */ jsx(DialogContext.Provider, { value, children });
|
|
135
|
+
}
|
|
136
|
+
function setRef(ref, node) {
|
|
137
|
+
if (typeof ref === "function") ref(node);
|
|
138
|
+
else if (ref) ref.current = node;
|
|
139
|
+
}
|
|
140
|
+
function TriggerLike({
|
|
141
|
+
asChild = false,
|
|
142
|
+
onActivate,
|
|
143
|
+
dataSlot,
|
|
144
|
+
children,
|
|
145
|
+
className,
|
|
146
|
+
type,
|
|
147
|
+
...props
|
|
148
|
+
}) {
|
|
149
|
+
if (asChild) {
|
|
150
|
+
if (!React2.isValidElement(children)) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`${dataSlot}: \`asChild\` requires a single React element child`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const child = children;
|
|
156
|
+
const childProps = child.props;
|
|
157
|
+
const childOnClick = childProps.onClick;
|
|
158
|
+
return React2.cloneElement(child, {
|
|
159
|
+
...props,
|
|
160
|
+
...childProps,
|
|
161
|
+
"data-slot": dataSlot,
|
|
162
|
+
onClick: (event) => {
|
|
163
|
+
childOnClick?.(event);
|
|
164
|
+
if (!event.defaultPrevented) onActivate();
|
|
165
|
+
},
|
|
166
|
+
className: cn(className, childProps.className),
|
|
167
|
+
ref: (node) => {
|
|
168
|
+
setRef(childProps.ref, node);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return /* @__PURE__ */ jsx(
|
|
173
|
+
"button",
|
|
174
|
+
{
|
|
175
|
+
type: type ?? "button",
|
|
176
|
+
"data-slot": dataSlot,
|
|
177
|
+
className,
|
|
178
|
+
onClick: () => onActivate(),
|
|
179
|
+
...props,
|
|
180
|
+
children
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function DialogTrigger({ asChild = false, ...props }) {
|
|
185
|
+
const { open, setOpen } = useDialogContext("Dialog.Trigger");
|
|
186
|
+
return /* @__PURE__ */ jsx(
|
|
187
|
+
TriggerLike,
|
|
188
|
+
{
|
|
189
|
+
asChild,
|
|
190
|
+
dataSlot: "dialog-trigger",
|
|
191
|
+
onActivate: () => setOpen(true),
|
|
192
|
+
"aria-haspopup": "dialog",
|
|
193
|
+
"aria-expanded": open,
|
|
194
|
+
...props
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function DialogClose({ asChild = false, ...props }) {
|
|
199
|
+
const { setOpen } = useDialogContext("Dialog.Close");
|
|
200
|
+
return /* @__PURE__ */ jsx(
|
|
201
|
+
TriggerLike,
|
|
202
|
+
{
|
|
203
|
+
asChild,
|
|
204
|
+
dataSlot: "dialog-close",
|
|
205
|
+
onActivate: () => setOpen(false),
|
|
206
|
+
...props
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
function DialogContent({
|
|
211
|
+
className,
|
|
212
|
+
children,
|
|
213
|
+
ref,
|
|
214
|
+
onKeyDown,
|
|
215
|
+
...props
|
|
216
|
+
}) {
|
|
217
|
+
const { open, setOpen, dismissable, titleId, descriptionId, hasDescription } = useDialogContext("Dialog.Content");
|
|
218
|
+
const cardRef = React2.useRef(null);
|
|
219
|
+
useFocusTrap({ active: open, containerRef: cardRef });
|
|
220
|
+
React2.useEffect(() => {
|
|
221
|
+
if (!open || !dismissable) return;
|
|
222
|
+
const handleEscape = (event) => {
|
|
223
|
+
if (event.key === "Escape") {
|
|
224
|
+
event.stopPropagation();
|
|
225
|
+
setOpen(false);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
document.addEventListener("keydown", handleEscape);
|
|
229
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
230
|
+
}, [open, dismissable, setOpen]);
|
|
231
|
+
if (!open) return null;
|
|
232
|
+
const setCardRef = (node) => {
|
|
233
|
+
cardRef.current = node;
|
|
234
|
+
setRef(ref, node);
|
|
235
|
+
};
|
|
236
|
+
return createPortal(
|
|
237
|
+
/* @__PURE__ */ jsx(
|
|
238
|
+
"div",
|
|
239
|
+
{
|
|
240
|
+
"data-slot": "dialog-overlay",
|
|
241
|
+
className: dialogOverlayVariants(),
|
|
242
|
+
onClick: (event) => {
|
|
243
|
+
if (dismissable && event.target === event.currentTarget) {
|
|
244
|
+
setOpen(false);
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
children: /* @__PURE__ */ jsx(
|
|
248
|
+
"div",
|
|
249
|
+
{
|
|
250
|
+
ref: setCardRef,
|
|
251
|
+
role: "dialog",
|
|
252
|
+
"aria-modal": "true",
|
|
253
|
+
"aria-labelledby": titleId,
|
|
254
|
+
"aria-describedby": hasDescription ? descriptionId : void 0,
|
|
255
|
+
tabIndex: -1,
|
|
256
|
+
"data-slot": "dialog-content",
|
|
257
|
+
className: cn(dialogContentVariants(), className),
|
|
258
|
+
onKeyDown,
|
|
259
|
+
...props,
|
|
260
|
+
children
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
document.body
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
function DialogHeader({
|
|
269
|
+
icon,
|
|
270
|
+
title,
|
|
271
|
+
description,
|
|
272
|
+
showClose = true,
|
|
273
|
+
className,
|
|
274
|
+
ref,
|
|
275
|
+
...props
|
|
276
|
+
}) {
|
|
277
|
+
const { titleId, descriptionId, setHasDescription, setOpen } = useDialogContext("Dialog.Header");
|
|
278
|
+
const hasDescription = description != null;
|
|
279
|
+
React2.useEffect(() => {
|
|
280
|
+
if (!hasDescription) return;
|
|
281
|
+
setHasDescription(true);
|
|
282
|
+
return () => setHasDescription(false);
|
|
283
|
+
}, [hasDescription, setHasDescription]);
|
|
284
|
+
return /* @__PURE__ */ jsxs(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
ref,
|
|
288
|
+
"data-slot": "dialog-header",
|
|
289
|
+
className: cn(dialogHeaderVariants(), className),
|
|
290
|
+
...props,
|
|
291
|
+
children: [
|
|
292
|
+
icon != null && /* @__PURE__ */ jsx(
|
|
293
|
+
"span",
|
|
294
|
+
{
|
|
295
|
+
"data-slot": "dialog-header-icon",
|
|
296
|
+
"aria-hidden": "true",
|
|
297
|
+
className: dialogHeaderIconVariants(),
|
|
298
|
+
children: /* @__PURE__ */ jsx(IconContext.Provider, { value: { weight: "fill" }, children: icon })
|
|
299
|
+
}
|
|
300
|
+
),
|
|
301
|
+
/* @__PURE__ */ jsxs(
|
|
302
|
+
"div",
|
|
303
|
+
{
|
|
304
|
+
className: cn(
|
|
305
|
+
dialogHeaderTextVariants(),
|
|
306
|
+
// Reserve right space for the corner X only when it would collide: an
|
|
307
|
+
// icon-less header puts the title on the X's row. With an icon the
|
|
308
|
+
// title sits below the X, so no reservation is needed.
|
|
309
|
+
showClose && icon == null && "pr-8"
|
|
310
|
+
),
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ jsx("h2", { id: titleId, className: "display-04 text-foreground", children: title }),
|
|
313
|
+
hasDescription && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "body-03 text-foreground-muted", children: description })
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
),
|
|
317
|
+
showClose && /* @__PURE__ */ jsx(
|
|
318
|
+
"button",
|
|
319
|
+
{
|
|
320
|
+
type: "button",
|
|
321
|
+
"data-slot": "dialog-header-close",
|
|
322
|
+
"aria-label": "Close",
|
|
323
|
+
className: dialogCloseButtonVariants(),
|
|
324
|
+
onClick: () => setOpen(false),
|
|
325
|
+
children: /* @__PURE__ */ jsx(XIcon, { weight: "regular" })
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
function DialogBody({ className, children, ref, ...props }) {
|
|
333
|
+
return /* @__PURE__ */ jsx(
|
|
334
|
+
"div",
|
|
335
|
+
{
|
|
336
|
+
ref,
|
|
337
|
+
"data-slot": "dialog-body",
|
|
338
|
+
className: cn(dialogBodyVariants(), scrollbarVariants(), className),
|
|
339
|
+
...props,
|
|
340
|
+
children
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
function DialogFooter({
|
|
345
|
+
className,
|
|
346
|
+
children,
|
|
347
|
+
ref,
|
|
348
|
+
...props
|
|
349
|
+
}) {
|
|
350
|
+
return /* @__PURE__ */ jsx(
|
|
351
|
+
"div",
|
|
352
|
+
{
|
|
353
|
+
ref,
|
|
354
|
+
"data-slot": "dialog-footer",
|
|
355
|
+
className: cn(dialogFooterVariants(), className),
|
|
356
|
+
...props,
|
|
357
|
+
children
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
var Dialog = {
|
|
362
|
+
Root: DialogRoot,
|
|
363
|
+
Trigger: DialogTrigger,
|
|
364
|
+
Content: DialogContent,
|
|
365
|
+
Header: DialogHeader,
|
|
366
|
+
Body: DialogBody,
|
|
367
|
+
Footer: DialogFooter,
|
|
368
|
+
Close: DialogClose
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
export { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTrigger, dialogBodyVariants, dialogCloseButtonVariants, dialogContentVariants, dialogFooterVariants, dialogHeaderIconVariants, dialogHeaderTextVariants, dialogHeaderVariants, dialogOverlayVariants, getFocusableElements, useFocusTrap };
|