@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
package/dist/button.cjs
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var react = require('@phosphor-icons/react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
29
|
+
|
|
30
|
+
// src/button/Button.tsx
|
|
31
|
+
function cn(...inputs) {
|
|
32
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
33
|
+
}
|
|
34
|
+
function buildCutPath({
|
|
35
|
+
W,
|
|
36
|
+
H,
|
|
37
|
+
cut,
|
|
38
|
+
rTR,
|
|
39
|
+
rBL,
|
|
40
|
+
rCut,
|
|
41
|
+
ox = 0,
|
|
42
|
+
oy = 0
|
|
43
|
+
}) {
|
|
44
|
+
const rCutSq = rCut / Math.SQRT2;
|
|
45
|
+
const x = (v) => v + ox;
|
|
46
|
+
const y = (v) => v + oy;
|
|
47
|
+
return [
|
|
48
|
+
`M ${x(cut + rCut)} ${y(0)}`,
|
|
49
|
+
`L ${x(W - rTR)} ${y(0)}`,
|
|
50
|
+
`Q ${x(W)} ${y(0)} ${x(W)} ${y(rTR)}`,
|
|
51
|
+
`L ${x(W)} ${y(H - cut - rCut)}`,
|
|
52
|
+
`Q ${x(W)} ${y(H - cut)} ${x(W - rCutSq)} ${y(H - cut + rCutSq)}`,
|
|
53
|
+
`L ${x(W - cut + rCutSq)} ${y(H - rCutSq)}`,
|
|
54
|
+
`Q ${x(W - cut)} ${y(H)} ${x(W - cut - rCut)} ${y(H)}`,
|
|
55
|
+
`L ${x(rBL)} ${y(H)}`,
|
|
56
|
+
`Q ${x(0)} ${y(H)} ${x(0)} ${y(H - rBL)}`,
|
|
57
|
+
`L ${x(0)} ${y(cut + rCut)}`,
|
|
58
|
+
`Q ${x(0)} ${y(cut)} ${x(rCutSq)} ${y(cut - rCutSq)}`,
|
|
59
|
+
`L ${x(cut - rCutSq)} ${y(rCutSq)}`,
|
|
60
|
+
`Q ${x(cut)} ${y(0)} ${x(cut + rCut)} ${y(0)}`,
|
|
61
|
+
`Z`
|
|
62
|
+
].join(" ");
|
|
63
|
+
}
|
|
64
|
+
function useCutCornerClipPath(cut, {
|
|
65
|
+
radius = 2,
|
|
66
|
+
radiusCuts,
|
|
67
|
+
radiusTopRight,
|
|
68
|
+
radiusBottomLeft,
|
|
69
|
+
outset
|
|
70
|
+
} = {}) {
|
|
71
|
+
const ref = React__namespace.useRef(null);
|
|
72
|
+
const [clipPath, setClipPath] = React__namespace.useState();
|
|
73
|
+
const [pathD, setPathD] = React__namespace.useState();
|
|
74
|
+
const [outsetPathD, setOutsetPathD] = React__namespace.useState();
|
|
75
|
+
React__namespace.useLayoutEffect(() => {
|
|
76
|
+
const el = ref.current;
|
|
77
|
+
if (!el) return;
|
|
78
|
+
const rTR = radiusTopRight ?? radius;
|
|
79
|
+
const rBL = radiusBottomLeft ?? radius;
|
|
80
|
+
const rCut = radiusCuts ?? radius;
|
|
81
|
+
function compute(W, H) {
|
|
82
|
+
const d = buildCutPath({ W, H, cut, rTR, rBL, rCut });
|
|
83
|
+
setClipPath(`path('${d}')`);
|
|
84
|
+
setPathD(d);
|
|
85
|
+
if (outset != null) {
|
|
86
|
+
const g = outset;
|
|
87
|
+
setOutsetPathD(
|
|
88
|
+
buildCutPath({
|
|
89
|
+
W: W + 2 * g,
|
|
90
|
+
H: H + 2 * g,
|
|
91
|
+
cut: cut + (2 - Math.SQRT2) * g,
|
|
92
|
+
rTR: rTR + g,
|
|
93
|
+
rBL: rBL + g,
|
|
94
|
+
rCut: rCut + g,
|
|
95
|
+
ox: -g,
|
|
96
|
+
oy: -g
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const initial = el.getBoundingClientRect();
|
|
102
|
+
compute(initial.width, initial.height);
|
|
103
|
+
const ro = new ResizeObserver((entries) => {
|
|
104
|
+
const box = entries[entries.length - 1]?.borderBoxSize?.[0];
|
|
105
|
+
if (box) {
|
|
106
|
+
compute(box.inlineSize, box.blockSize);
|
|
107
|
+
} else {
|
|
108
|
+
const rect = el.getBoundingClientRect();
|
|
109
|
+
compute(rect.width, rect.height);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
ro.observe(el);
|
|
113
|
+
return () => ro.disconnect();
|
|
114
|
+
}, [cut, radius, radiusCuts, radiusTopRight, radiusBottomLeft, outset]);
|
|
115
|
+
return { ref, clipPath, pathD, outsetPathD };
|
|
116
|
+
}
|
|
117
|
+
var BUTTON_CUT = { cut: 8, radius: 2, radiusCuts: 0 };
|
|
118
|
+
var buttonVariants = classVarianceAuthority.cva(
|
|
119
|
+
[
|
|
120
|
+
// Layout
|
|
121
|
+
"inline-flex shrink-0 items-center justify-center gap-1",
|
|
122
|
+
// Type — the Figma `button-01` text style (Archivo Medium 14/20)
|
|
123
|
+
"button-01",
|
|
124
|
+
// Shape — cut corners are applied as a clip-path in <Button>; keep the box square
|
|
125
|
+
"rounded-none",
|
|
126
|
+
// Transition
|
|
127
|
+
"transition-colors cursor-pointer",
|
|
128
|
+
// Suppress the native ring. The Figma focus ring is drawn OUTSIDE the
|
|
129
|
+
// clip-path (which would swallow an outline here), so cut-corner variants
|
|
130
|
+
// render it on the wrapper in <Button>; ghost renders it directly (below).
|
|
131
|
+
"outline-none",
|
|
132
|
+
// Disabled
|
|
133
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed",
|
|
134
|
+
// Icons
|
|
135
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
136
|
+
],
|
|
137
|
+
{
|
|
138
|
+
variants: {
|
|
139
|
+
variation: {
|
|
140
|
+
primary: [
|
|
141
|
+
"bg-button-primary-background text-button-primary-foreground",
|
|
142
|
+
"hover:bg-button-primary-accent active:bg-button-primary-accent",
|
|
143
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
144
|
+
],
|
|
145
|
+
brand: [
|
|
146
|
+
"bg-button-brand-primary-background text-button-brand-primary-foreground",
|
|
147
|
+
"hover:bg-button-brand-primary-accent active:bg-button-brand-primary-accent",
|
|
148
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
149
|
+
],
|
|
150
|
+
destructive: [
|
|
151
|
+
"bg-button-destructive-background text-button-destructive-primary-foreground",
|
|
152
|
+
"hover:bg-button-destructive-accent active:bg-button-destructive-accent",
|
|
153
|
+
"disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
|
|
154
|
+
],
|
|
155
|
+
// Outline — background fill + an SVG stroke border drawn in <Button>.
|
|
156
|
+
secondary: [
|
|
157
|
+
"bg-button-secondary-background text-button-secondary-foreground",
|
|
158
|
+
"hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
|
|
159
|
+
"disabled:text-button-secondary-foreground-muted"
|
|
160
|
+
],
|
|
161
|
+
// No background, no cut corner — so no wrapper; ring on the element.
|
|
162
|
+
// `outline-solid` restores the style that base `outline-none` zeroed.
|
|
163
|
+
ghost: [
|
|
164
|
+
"bg-transparent text-button-secondary-foreground",
|
|
165
|
+
"hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
|
|
166
|
+
"disabled:text-button-secondary-foreground-muted",
|
|
167
|
+
// `rounded-xs` (2px) keeps the ghost focus ring as sharp as the other
|
|
168
|
+
// variants' corners (which now use sharp chamfers + 2px square corners).
|
|
169
|
+
"rounded-xs focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
size: {
|
|
173
|
+
sm: "h-8 px-2.5 [&_svg:not([class*='size-'])]:size-4",
|
|
174
|
+
md: "h-10 px-2.5 [&_svg:not([class*='size-'])]:size-4",
|
|
175
|
+
lg: "h-12 px-3 [&_svg:not([class*='size-'])]:size-4"
|
|
176
|
+
},
|
|
177
|
+
iconOnly: {
|
|
178
|
+
true: "",
|
|
179
|
+
false: ""
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
compoundVariants: [
|
|
183
|
+
// Icon-only: square footprint, no horizontal padding.
|
|
184
|
+
{ iconOnly: true, size: "sm", className: "w-8 px-0" },
|
|
185
|
+
{ iconOnly: true, size: "md", className: "w-10 px-0" },
|
|
186
|
+
{ iconOnly: true, size: "lg", className: "w-12 px-0" },
|
|
187
|
+
// Ghost has no chamfer/background, so it sits flush with no inset padding.
|
|
188
|
+
{ variation: "ghost", iconOnly: false, className: "px-0" }
|
|
189
|
+
],
|
|
190
|
+
defaultVariants: {
|
|
191
|
+
variation: "primary",
|
|
192
|
+
size: "md",
|
|
193
|
+
iconOnly: false
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
var FOCUS_RING_GAP = 3;
|
|
198
|
+
var FOCUS_RING_WIDTH = 2;
|
|
199
|
+
function setRef(ref, node) {
|
|
200
|
+
if (typeof ref === "function") ref(node);
|
|
201
|
+
else if (ref) ref.current = node;
|
|
202
|
+
}
|
|
203
|
+
function Button({
|
|
204
|
+
className,
|
|
205
|
+
variation = "primary",
|
|
206
|
+
size = "md",
|
|
207
|
+
iconOnly = false,
|
|
208
|
+
asChild = false,
|
|
209
|
+
loading = false,
|
|
210
|
+
leftSlot,
|
|
211
|
+
rightSlot,
|
|
212
|
+
wrapperClassName,
|
|
213
|
+
disabled,
|
|
214
|
+
style,
|
|
215
|
+
children,
|
|
216
|
+
onClick,
|
|
217
|
+
ref,
|
|
218
|
+
...props
|
|
219
|
+
}) {
|
|
220
|
+
const {
|
|
221
|
+
ref: cutRef,
|
|
222
|
+
clipPath,
|
|
223
|
+
pathD,
|
|
224
|
+
outsetPathD
|
|
225
|
+
} = useCutCornerClipPath(BUTTON_CUT.cut, {
|
|
226
|
+
radius: BUTTON_CUT.radius,
|
|
227
|
+
radiusCuts: BUTTON_CUT.radiusCuts,
|
|
228
|
+
outset: FOCUS_RING_GAP
|
|
229
|
+
});
|
|
230
|
+
const setRootRef = React__namespace.useCallback(
|
|
231
|
+
(node) => {
|
|
232
|
+
cutRef.current = node;
|
|
233
|
+
setRef(ref, node);
|
|
234
|
+
},
|
|
235
|
+
[cutRef, ref]
|
|
236
|
+
);
|
|
237
|
+
const isDisabled = disabled;
|
|
238
|
+
const hasCutCorner = variation !== "ghost";
|
|
239
|
+
const hasSvgBorder = variation === "secondary";
|
|
240
|
+
const handleClick = loading ? (event) => {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
event.stopPropagation();
|
|
243
|
+
} : onClick;
|
|
244
|
+
const rootClassName = cn(
|
|
245
|
+
buttonVariants({ variation, size, iconOnly }),
|
|
246
|
+
hasSvgBorder && "border-0",
|
|
247
|
+
loading && "pointer-events-none",
|
|
248
|
+
className
|
|
249
|
+
);
|
|
250
|
+
const rootStyle = hasCutCorner ? { ...style, clipPath } : style;
|
|
251
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
252
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx(react.CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : leftSlot,
|
|
253
|
+
!iconOnly && children,
|
|
254
|
+
iconOnly && !loading && children,
|
|
255
|
+
!iconOnly && rightSlot
|
|
256
|
+
] });
|
|
257
|
+
let rootEl;
|
|
258
|
+
if (asChild) {
|
|
259
|
+
if (!React__namespace.isValidElement(children)) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
"Button: `asChild` requires a single React element child"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const child = children;
|
|
265
|
+
const childProps = child.props;
|
|
266
|
+
const childOnClick = childProps.onClick;
|
|
267
|
+
rootEl = React__namespace.cloneElement(
|
|
268
|
+
child,
|
|
269
|
+
{
|
|
270
|
+
...props,
|
|
271
|
+
...childProps,
|
|
272
|
+
"data-slot": "button",
|
|
273
|
+
"data-loading": loading || void 0,
|
|
274
|
+
"aria-busy": loading || void 0,
|
|
275
|
+
"aria-disabled": loading || void 0,
|
|
276
|
+
// Block activation while loading; otherwise run both handlers.
|
|
277
|
+
onClick: loading ? (event) => {
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
event.stopPropagation();
|
|
280
|
+
} : (event) => {
|
|
281
|
+
onClick?.(event);
|
|
282
|
+
childOnClick?.(event);
|
|
283
|
+
},
|
|
284
|
+
ref: (node) => {
|
|
285
|
+
setRootRef(node);
|
|
286
|
+
setRef(childProps.ref, node);
|
|
287
|
+
},
|
|
288
|
+
className: cn(
|
|
289
|
+
rootClassName,
|
|
290
|
+
childProps.className
|
|
291
|
+
),
|
|
292
|
+
style: {
|
|
293
|
+
...rootStyle,
|
|
294
|
+
...childProps.style
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx(react.CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : null,
|
|
298
|
+
childProps.children
|
|
299
|
+
);
|
|
300
|
+
} else {
|
|
301
|
+
rootEl = /* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
+
"button",
|
|
303
|
+
{
|
|
304
|
+
"data-slot": "button",
|
|
305
|
+
"data-loading": loading || void 0,
|
|
306
|
+
"aria-busy": loading || void 0,
|
|
307
|
+
"aria-disabled": loading || void 0,
|
|
308
|
+
className: rootClassName,
|
|
309
|
+
style: rootStyle,
|
|
310
|
+
disabled: isDisabled,
|
|
311
|
+
...props,
|
|
312
|
+
onClick: handleClick,
|
|
313
|
+
ref: setRootRef,
|
|
314
|
+
children: content
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (!hasCutCorner) return rootEl;
|
|
319
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("group relative inline-block", wrapperClassName), children: [
|
|
320
|
+
rootEl,
|
|
321
|
+
outsetPathD && /* @__PURE__ */ jsxRuntime.jsx(
|
|
322
|
+
"svg",
|
|
323
|
+
{
|
|
324
|
+
className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible opacity-0 group-has-[:focus-visible]:opacity-100",
|
|
325
|
+
"aria-hidden": "true",
|
|
326
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
327
|
+
"path",
|
|
328
|
+
{
|
|
329
|
+
d: outsetPathD,
|
|
330
|
+
fill: "none",
|
|
331
|
+
strokeWidth: FOCUS_RING_WIDTH,
|
|
332
|
+
className: "stroke-focus"
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
),
|
|
337
|
+
hasSvgBorder && pathD && /* @__PURE__ */ jsxRuntime.jsx(
|
|
338
|
+
"svg",
|
|
339
|
+
{
|
|
340
|
+
className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible",
|
|
341
|
+
"aria-hidden": "true",
|
|
342
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
343
|
+
"path",
|
|
344
|
+
{
|
|
345
|
+
d: pathD,
|
|
346
|
+
fill: "none",
|
|
347
|
+
strokeWidth: 1,
|
|
348
|
+
className: cn(
|
|
349
|
+
// Disabled locks the muted stroke; the hover/active accent
|
|
350
|
+
// classes must not be present at all (a `group-hover:` variant
|
|
351
|
+
// out-specifies the plain muted class, so it would still
|
|
352
|
+
// recolor the border on hover of a disabled button).
|
|
353
|
+
isDisabled ? "stroke-button-secondary-border-muted" : [
|
|
354
|
+
"stroke-button-secondary-border",
|
|
355
|
+
"group-hover:stroke-button-secondary-border-accent group-active:stroke-button-secondary-border-accent"
|
|
356
|
+
]
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
] });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
exports.Button = Button;
|
|
366
|
+
exports.buttonVariants = buttonVariants;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Button styling — mirrors the Figma `button` component set
|
|
7
|
+
* (`variation` × `size` × `state` × `iconOnly`) and binds ONLY to the semantic
|
|
8
|
+
* `--button-*` token layer (never primitives). Theme switching is automatic via
|
|
9
|
+
* the `.dark` class, so no `dark:` variants are needed.
|
|
10
|
+
*
|
|
11
|
+
* Notes on states:
|
|
12
|
+
* - `hover` / `active` are handled by the `:hover` / `:active` utilities here.
|
|
13
|
+
* - `focus` is the shared focus ring (`outline-focus`), applied in the base.
|
|
14
|
+
* - `disabled` is a shared NEUTRAL-muted look for every filled variant (Figma
|
|
15
|
+
* greys out primary/brand/destructive identically), hence they all reuse
|
|
16
|
+
* `button-primary-muted` / `button-primary-foreground-muted`.
|
|
17
|
+
* - `loading` is a runtime prop on <Button>, not a style variant.
|
|
18
|
+
*
|
|
19
|
+
* Shape (cut corners) and the secondary border are handled in <Button>: the
|
|
20
|
+
* chamfer is a measured `clip-path` and the secondary outline is an SVG stroke
|
|
21
|
+
* that follows that shape (a CSS border would clip square across the chamfer).
|
|
22
|
+
*/
|
|
23
|
+
declare const buttonVariants: (props?: ({
|
|
24
|
+
variation?: "primary" | "brand" | "destructive" | "secondary" | "ghost" | null | undefined;
|
|
25
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
26
|
+
iconOnly?: boolean | null | undefined;
|
|
27
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
28
|
+
type ButtonVariantsProps = VariantProps<typeof buttonVariants>;
|
|
29
|
+
|
|
30
|
+
interface ButtonProps extends Omit<React.ComponentProps<"button">, "color">, ButtonVariantsProps {
|
|
31
|
+
/**
|
|
32
|
+
* Render the single child element (e.g. an `<a>` / Next `<Link>`) as the root
|
|
33
|
+
* instead of a `<button>`, keeping all button styling. Keeps a button-styled
|
|
34
|
+
* link a real anchor (valid HTML, middle-click, open-in-new-tab).
|
|
35
|
+
*/
|
|
36
|
+
asChild?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Show a spinner and mark the button busy (`aria-busy`). Keeps the ACTIVE
|
|
39
|
+
* styling (not the greyed disabled look) but is not actionable while busy —
|
|
40
|
+
* clicks and keyboard activation are blocked and it reports `aria-disabled`.
|
|
41
|
+
*/
|
|
42
|
+
loading?: boolean;
|
|
43
|
+
/** Leading content (usually a Phosphor icon). Replaced by the spinner when loading. */
|
|
44
|
+
leftSlot?: React.ReactNode;
|
|
45
|
+
/** Trailing content (usually a Phosphor icon). Stays visible when loading (per Figma). */
|
|
46
|
+
rightSlot?: React.ReactNode;
|
|
47
|
+
/** Class names for the outer wrapper span (present only for the secondary border). */
|
|
48
|
+
wrapperClassName?: string;
|
|
49
|
+
}
|
|
50
|
+
declare function Button({ className, variation, size, iconOnly, asChild, loading, leftSlot, rightSlot, wrapperClassName, disabled, style, children, onClick, ref, ...props }: ButtonProps): React.JSX.Element;
|
|
51
|
+
|
|
52
|
+
export { Button, type ButtonProps, type ButtonVariantsProps, buttonVariants };
|
package/dist/button.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Button styling — mirrors the Figma `button` component set
|
|
7
|
+
* (`variation` × `size` × `state` × `iconOnly`) and binds ONLY to the semantic
|
|
8
|
+
* `--button-*` token layer (never primitives). Theme switching is automatic via
|
|
9
|
+
* the `.dark` class, so no `dark:` variants are needed.
|
|
10
|
+
*
|
|
11
|
+
* Notes on states:
|
|
12
|
+
* - `hover` / `active` are handled by the `:hover` / `:active` utilities here.
|
|
13
|
+
* - `focus` is the shared focus ring (`outline-focus`), applied in the base.
|
|
14
|
+
* - `disabled` is a shared NEUTRAL-muted look for every filled variant (Figma
|
|
15
|
+
* greys out primary/brand/destructive identically), hence they all reuse
|
|
16
|
+
* `button-primary-muted` / `button-primary-foreground-muted`.
|
|
17
|
+
* - `loading` is a runtime prop on <Button>, not a style variant.
|
|
18
|
+
*
|
|
19
|
+
* Shape (cut corners) and the secondary border are handled in <Button>: the
|
|
20
|
+
* chamfer is a measured `clip-path` and the secondary outline is an SVG stroke
|
|
21
|
+
* that follows that shape (a CSS border would clip square across the chamfer).
|
|
22
|
+
*/
|
|
23
|
+
declare const buttonVariants: (props?: ({
|
|
24
|
+
variation?: "primary" | "brand" | "destructive" | "secondary" | "ghost" | null | undefined;
|
|
25
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
26
|
+
iconOnly?: boolean | null | undefined;
|
|
27
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
28
|
+
type ButtonVariantsProps = VariantProps<typeof buttonVariants>;
|
|
29
|
+
|
|
30
|
+
interface ButtonProps extends Omit<React.ComponentProps<"button">, "color">, ButtonVariantsProps {
|
|
31
|
+
/**
|
|
32
|
+
* Render the single child element (e.g. an `<a>` / Next `<Link>`) as the root
|
|
33
|
+
* instead of a `<button>`, keeping all button styling. Keeps a button-styled
|
|
34
|
+
* link a real anchor (valid HTML, middle-click, open-in-new-tab).
|
|
35
|
+
*/
|
|
36
|
+
asChild?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Show a spinner and mark the button busy (`aria-busy`). Keeps the ACTIVE
|
|
39
|
+
* styling (not the greyed disabled look) but is not actionable while busy —
|
|
40
|
+
* clicks and keyboard activation are blocked and it reports `aria-disabled`.
|
|
41
|
+
*/
|
|
42
|
+
loading?: boolean;
|
|
43
|
+
/** Leading content (usually a Phosphor icon). Replaced by the spinner when loading. */
|
|
44
|
+
leftSlot?: React.ReactNode;
|
|
45
|
+
/** Trailing content (usually a Phosphor icon). Stays visible when loading (per Figma). */
|
|
46
|
+
rightSlot?: React.ReactNode;
|
|
47
|
+
/** Class names for the outer wrapper span (present only for the secondary border). */
|
|
48
|
+
wrapperClassName?: string;
|
|
49
|
+
}
|
|
50
|
+
declare function Button({ className, variation, size, iconOnly, asChild, loading, leftSlot, rightSlot, wrapperClassName, disabled, style, children, onClick, ref, ...props }: ButtonProps): React.JSX.Element;
|
|
51
|
+
|
|
52
|
+
export { Button, type ButtonProps, type ButtonVariantsProps, buttonVariants };
|
package/dist/button.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('@phosphor-icons/react');
|
|
4
|
+
var clsx = require('clsx');
|
|
5
|
+
var tailwindMerge = require('tailwind-merge');
|
|
6
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
// src/checkbox/Checkbox.tsx
|
|
10
|
+
function cn(...inputs) {
|
|
11
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
var checkboxVariants = classVarianceAuthority.cva([
|
|
14
|
+
// Box — 20×20 (`size-5`), 2px radius (Figma `radius/xs`), 1px border.
|
|
15
|
+
// `peer` lets the sibling indicator react to :checked / :disabled.
|
|
16
|
+
"peer size-5 shrink-0 appearance-none rounded-xs border",
|
|
17
|
+
"cursor-pointer transition-colors",
|
|
18
|
+
// Default
|
|
19
|
+
"bg-background border-border",
|
|
20
|
+
// Hover (unselected) — focus keeps the DEFAULT border; only the ring signals
|
|
21
|
+
// focus (so focus doesn't visually read as hover).
|
|
22
|
+
"hover:border-border-accent",
|
|
23
|
+
// Focus ring — suppress the native ring, draw the shared `--focus` ring the
|
|
24
|
+
// same way ghost Button does: 2px stroke, 2px offset (`outline-solid`
|
|
25
|
+
// restores what `outline-none` zeroed).
|
|
26
|
+
"outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-focus",
|
|
27
|
+
// Selected
|
|
28
|
+
"checked:bg-brand-primary-background checked:border-brand-primary-border",
|
|
29
|
+
"checked:hover:bg-brand-primary-accent checked:hover:border-brand-primary-border",
|
|
30
|
+
"checked:focus-visible:bg-brand-primary-accent checked:focus-visible:border-brand-primary-border",
|
|
31
|
+
// Disabled — shared neutral-muted look whether selected or not (per Figma)
|
|
32
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-border-muted",
|
|
33
|
+
"checked:disabled:bg-background checked:disabled:border-border-muted"
|
|
34
|
+
]);
|
|
35
|
+
function Checkbox({
|
|
36
|
+
className,
|
|
37
|
+
wrapperClassName,
|
|
38
|
+
onChange,
|
|
39
|
+
onCheckedChange,
|
|
40
|
+
...props
|
|
41
|
+
}) {
|
|
42
|
+
const handleChange = (event) => {
|
|
43
|
+
onChange?.(event);
|
|
44
|
+
onCheckedChange?.(event.currentTarget.checked);
|
|
45
|
+
};
|
|
46
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("relative inline-flex shrink-0", wrapperClassName), children: [
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
+
"input",
|
|
49
|
+
{
|
|
50
|
+
type: "checkbox",
|
|
51
|
+
"data-slot": "checkbox",
|
|
52
|
+
className: cn(checkboxVariants(), className),
|
|
53
|
+
...props,
|
|
54
|
+
onChange: handleChange
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
58
|
+
react.CheckIcon,
|
|
59
|
+
{
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
weight: "regular",
|
|
62
|
+
"data-slot": "checkbox-indicator",
|
|
63
|
+
className: cn(
|
|
64
|
+
"pointer-events-none invisible absolute inset-0 m-auto size-4",
|
|
65
|
+
"text-brand-primary-foreground",
|
|
66
|
+
"peer-checked:visible peer-disabled:text-foreground-muted"
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
exports.Checkbox = Checkbox;
|
|
74
|
+
exports.checkboxVariants = checkboxVariants;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
interface CheckboxProps extends Omit<React.ComponentProps<"input">, "type" | "size"> {
|
|
6
|
+
/**
|
|
7
|
+
* Convenience change callback with the next checked state already
|
|
8
|
+
* extracted. Fires alongside (not instead of) the native `onChange`.
|
|
9
|
+
* Controlled/uncontrolled usage is the standard input contract:
|
|
10
|
+
* `defaultChecked` (uncontrolled, the default) or `checked` + a change
|
|
11
|
+
* handler (controlled).
|
|
12
|
+
*/
|
|
13
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
14
|
+
/** Class names for the outer wrapper span (positions the ✓ indicator). */
|
|
15
|
+
wrapperClassName?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checkbox control — the Figma `checkbox-control` set (node 5066:9964),
|
|
19
|
+
* rendered as a real `<input type="checkbox">` so keyboard (Space), forms,
|
|
20
|
+
* and label association all come from the platform.
|
|
21
|
+
*
|
|
22
|
+
* Ships without a label (the Figma set is the bare 20×20 control): associate
|
|
23
|
+
* one by wrapping in a `<label>` or via `htmlFor`/`id`; icon-style usages
|
|
24
|
+
* need an `aria-label`.
|
|
25
|
+
*
|
|
26
|
+
* The FD Figma set has no indeterminate variant; if a consumer ever needs the
|
|
27
|
+
* native `indeterminate` flag (DOM-only, not an attribute), set it on the
|
|
28
|
+
* node via `ref`.
|
|
29
|
+
*/
|
|
30
|
+
declare function Checkbox({ className, wrapperClassName, onChange, onCheckedChange, ...props }: CheckboxProps): React.JSX.Element;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Checkbox styling — mirrors the Figma `checkbox-control` component set
|
|
34
|
+
* (node 5066:9964: `state` × `selected`, one 20×20 size) and binds ONLY to the
|
|
35
|
+
* semantic token layer (never primitives). Theme switching is automatic via
|
|
36
|
+
* the `.dark` class, so no `dark:` variants are needed.
|
|
37
|
+
*
|
|
38
|
+
* Unlike Button, every Figma axis here is an *interaction* state, so there is
|
|
39
|
+
* no prop-driven variant table: `selected` maps to the native `:checked`
|
|
40
|
+
* pseudo-class and `state` maps to `:hover` / `:focus-visible` / `:disabled`.
|
|
41
|
+
*
|
|
42
|
+
* State → token map (from the Figma variants):
|
|
43
|
+
* - default: `bg-background` + `border-border`
|
|
44
|
+
* - hover (unselected): border strengthens to `border-accent`
|
|
45
|
+
* - focused: border stays at the default; the ring alone marks focus
|
|
46
|
+
* - selected: `bg-brand-primary-background` + `border-brand-primary-border`
|
|
47
|
+
* - selected hover/focused: bg strengthens to `brand-primary-accent`
|
|
48
|
+
* - disabled (either): shared neutral look — `bg-background` + `border-border-muted`
|
|
49
|
+
* - focused additionally draws the shared 2px `--focus` ring, offset 2px from
|
|
50
|
+
* the box — the same treatment as ghost Button (design decision 2026-07-07,
|
|
51
|
+
* overriding the flush ring the Figma node draws).
|
|
52
|
+
*
|
|
53
|
+
* Checked × hover/focus/disabled combinations are written as compound
|
|
54
|
+
* utilities (`checked:hover:`, `checked:disabled:`) so precedence is decided
|
|
55
|
+
* by selector specificity, not by Tailwind's stylesheet ordering.
|
|
56
|
+
*
|
|
57
|
+
* The ✓ indicator is a sibling of the input (an `<input>` can't have
|
|
58
|
+
* children), revealed via `peer-checked:` in <Checkbox>.
|
|
59
|
+
*/
|
|
60
|
+
declare const checkboxVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
|
|
61
|
+
type CheckboxVariantsProps = VariantProps<typeof checkboxVariants>;
|
|
62
|
+
|
|
63
|
+
export { Checkbox, type CheckboxProps, type CheckboxVariantsProps, checkboxVariants };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
interface CheckboxProps extends Omit<React.ComponentProps<"input">, "type" | "size"> {
|
|
6
|
+
/**
|
|
7
|
+
* Convenience change callback with the next checked state already
|
|
8
|
+
* extracted. Fires alongside (not instead of) the native `onChange`.
|
|
9
|
+
* Controlled/uncontrolled usage is the standard input contract:
|
|
10
|
+
* `defaultChecked` (uncontrolled, the default) or `checked` + a change
|
|
11
|
+
* handler (controlled).
|
|
12
|
+
*/
|
|
13
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
14
|
+
/** Class names for the outer wrapper span (positions the ✓ indicator). */
|
|
15
|
+
wrapperClassName?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checkbox control — the Figma `checkbox-control` set (node 5066:9964),
|
|
19
|
+
* rendered as a real `<input type="checkbox">` so keyboard (Space), forms,
|
|
20
|
+
* and label association all come from the platform.
|
|
21
|
+
*
|
|
22
|
+
* Ships without a label (the Figma set is the bare 20×20 control): associate
|
|
23
|
+
* one by wrapping in a `<label>` or via `htmlFor`/`id`; icon-style usages
|
|
24
|
+
* need an `aria-label`.
|
|
25
|
+
*
|
|
26
|
+
* The FD Figma set has no indeterminate variant; if a consumer ever needs the
|
|
27
|
+
* native `indeterminate` flag (DOM-only, not an attribute), set it on the
|
|
28
|
+
* node via `ref`.
|
|
29
|
+
*/
|
|
30
|
+
declare function Checkbox({ className, wrapperClassName, onChange, onCheckedChange, ...props }: CheckboxProps): React.JSX.Element;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Checkbox styling — mirrors the Figma `checkbox-control` component set
|
|
34
|
+
* (node 5066:9964: `state` × `selected`, one 20×20 size) and binds ONLY to the
|
|
35
|
+
* semantic token layer (never primitives). Theme switching is automatic via
|
|
36
|
+
* the `.dark` class, so no `dark:` variants are needed.
|
|
37
|
+
*
|
|
38
|
+
* Unlike Button, every Figma axis here is an *interaction* state, so there is
|
|
39
|
+
* no prop-driven variant table: `selected` maps to the native `:checked`
|
|
40
|
+
* pseudo-class and `state` maps to `:hover` / `:focus-visible` / `:disabled`.
|
|
41
|
+
*
|
|
42
|
+
* State → token map (from the Figma variants):
|
|
43
|
+
* - default: `bg-background` + `border-border`
|
|
44
|
+
* - hover (unselected): border strengthens to `border-accent`
|
|
45
|
+
* - focused: border stays at the default; the ring alone marks focus
|
|
46
|
+
* - selected: `bg-brand-primary-background` + `border-brand-primary-border`
|
|
47
|
+
* - selected hover/focused: bg strengthens to `brand-primary-accent`
|
|
48
|
+
* - disabled (either): shared neutral look — `bg-background` + `border-border-muted`
|
|
49
|
+
* - focused additionally draws the shared 2px `--focus` ring, offset 2px from
|
|
50
|
+
* the box — the same treatment as ghost Button (design decision 2026-07-07,
|
|
51
|
+
* overriding the flush ring the Figma node draws).
|
|
52
|
+
*
|
|
53
|
+
* Checked × hover/focus/disabled combinations are written as compound
|
|
54
|
+
* utilities (`checked:hover:`, `checked:disabled:`) so precedence is decided
|
|
55
|
+
* by selector specificity, not by Tailwind's stylesheet ordering.
|
|
56
|
+
*
|
|
57
|
+
* The ✓ indicator is a sibling of the input (an `<input>` can't have
|
|
58
|
+
* children), revealed via `peer-checked:` in <Checkbox>.
|
|
59
|
+
*/
|
|
60
|
+
declare const checkboxVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
|
|
61
|
+
type CheckboxVariantsProps = VariantProps<typeof checkboxVariants>;
|
|
62
|
+
|
|
63
|
+
export { Checkbox, type CheckboxProps, type CheckboxVariantsProps, checkboxVariants };
|
package/dist/checkbox.js
ADDED