@photon-ai/pho-ui 0.4.0 → 0.6.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.
@@ -0,0 +1,365 @@
1
+ "use client";
2
+ import { t as p } from "./cn-7uGUzCOM.js";
3
+ import { n as A, r as w } from "./link-provider-9TFNyDqO.js";
4
+ import { t as v } from "./resolve-variant-CcD8iG5l.js";
5
+ import { isValidElement as T } from "react";
6
+ import { Fragment as V, jsx as r, jsxs as k } from "react/jsx-runtime";
7
+ import { motion as x } from "motion/react";
8
+ var q = p("group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2", "disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"), B = {
9
+ sm: {
10
+ classes: p("gap-1 px-3.5 py-1.5 text-sm font-medium data-svg-only:p-2", "has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2"),
11
+ description: "Small — compact rows, tables, and toolbars"
12
+ },
13
+ md: {
14
+ classes: p("gap-1 px-4 py-2 text-sm font-medium data-svg-only:p-2.5", "has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5"),
15
+ description: "Medium — the default"
16
+ },
17
+ lg: {
18
+ classes: p("gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3", "has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3"),
19
+ description: "Large — prominent and marketing actions"
20
+ }
21
+ }, M = {
22
+ rounded: {
23
+ classes: "rounded-full",
24
+ description: "Pill — the default; every button on photon.codes"
25
+ },
26
+ square: {
27
+ classes: "rounded-lg",
28
+ description: "Gently rounded corners for dense app chrome"
29
+ },
30
+ circle: {
31
+ classes: "rounded-full",
32
+ description: "Circular — pair with svgOnly for round icon buttons"
33
+ }
34
+ }, D = "squircle rounded-xl", H = {
35
+ primary: {
36
+ classes: "bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover *:data-icon:opacity-60 hover:*:data-icon:opacity-75",
37
+ description: "High-emphasis primary action (default)"
38
+ },
39
+ secondary: {
40
+ classes: "bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset hover:bg-pho-primary-hover hover:text-pho-secondary-hover data-loading:bg-pho-primary-hover *:data-icon:text-fg-pho-quaternary hover:*:data-icon:text-fg-pho-quaternary-hover",
41
+ description: "Neutral, supporting action"
42
+ },
43
+ tertiary: {
44
+ classes: "text-pho-tertiary hover:bg-pho-ghost-hover hover:text-pho-tertiary-hover data-loading:bg-pho-ghost-hover *:data-icon:text-fg-pho-quaternary hover:*:data-icon:text-fg-pho-quaternary-hover",
45
+ description: "Low-emphasis, borderless action"
46
+ },
47
+ error: {
48
+ classes: "bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover *:data-icon:opacity-70 hover:*:data-icon:opacity-85",
49
+ description: "Destructive confirmation"
50
+ }
51
+ };
52
+ function y(e) {
53
+ const { variant: a = "primary", size: t = "md", shape: o = "rounded", className: i } = e ?? {};
54
+ return p(q, v(M, o), o === "square" && t === "lg" && D, v(B, t), v(H, a), i);
55
+ }
56
+ var U = "transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0";
57
+ function g({ position: e, children: a }) {
58
+ return /* @__PURE__ */ r("span", {
59
+ "data-icon": e,
60
+ className: U,
61
+ children: a
62
+ });
63
+ }
64
+ function N() {
65
+ return /* @__PURE__ */ k("svg", {
66
+ fill: "none",
67
+ viewBox: "0 0 20 20",
68
+ "aria-hidden": "true",
69
+ children: [/* @__PURE__ */ r("circle", {
70
+ className: "stroke-current opacity-30",
71
+ cx: "10",
72
+ cy: "10",
73
+ r: "8",
74
+ fill: "none",
75
+ strokeWidth: "2"
76
+ }), /* @__PURE__ */ r("circle", {
77
+ className: "origin-center animate-spin stroke-current",
78
+ cx: "10",
79
+ cy: "10",
80
+ r: "8",
81
+ fill: "none",
82
+ strokeWidth: "2",
83
+ strokeDasharray: "12.5 50",
84
+ strokeLinecap: "round"
85
+ })]
86
+ });
87
+ }
88
+ function _(e, a) {
89
+ return e == null || e === !1 ? null : w(e) ? /* @__PURE__ */ r(g, {
90
+ position: a,
91
+ children: /* @__PURE__ */ r(e, {})
92
+ }) : T(e) ? /* @__PURE__ */ r(g, {
93
+ position: a,
94
+ children: e
95
+ }) : null;
96
+ }
97
+ function C({ svgOnly: e, loading: a, prefix: t, suffix: o, children: i }) {
98
+ return e ? a ? /* @__PURE__ */ r(N, {}) : i : /* @__PURE__ */ k(V, { children: [
99
+ a ? /* @__PURE__ */ r(g, {
100
+ position: "loading",
101
+ children: /* @__PURE__ */ r(N, {})
102
+ }) : _(t, "prefix"),
103
+ i != null && i !== !1 && /* @__PURE__ */ r("span", {
104
+ "data-text": !0,
105
+ className: "transition-inherit-all px-0.5",
106
+ children: i
107
+ }),
108
+ _(o, "suffix")
109
+ ] });
110
+ }
111
+ function F(e, a) {
112
+ e && !a && typeof process < "u" && process.env.NODE_ENV !== "production" && console.warn("Button: `svgOnly` buttons require an `aria-label` that names the action.");
113
+ }
114
+ function b({ variant: e, size: a, shape: t, prefix: o, suffix: i, svgOnly: n, loading: s, children: h, className: l, ref: c, ...d }) {
115
+ return F(n, d["aria-label"]), /* @__PURE__ */ r("button", {
116
+ ref: c,
117
+ type: "button",
118
+ ...d,
119
+ "aria-busy": s || void 0,
120
+ "data-loading": s ? "true" : void 0,
121
+ "data-svg-only": n ? "true" : void 0,
122
+ className: p(y({
123
+ variant: e,
124
+ size: a,
125
+ shape: t
126
+ }), s && "pointer-events-none", n && "[&>svg]:size-[1.25em] [&>svg]:shrink-0", l),
127
+ children: C({
128
+ svgOnly: n,
129
+ loading: s,
130
+ prefix: o,
131
+ suffix: i,
132
+ children: h
133
+ })
134
+ });
135
+ }
136
+ b.displayName = "Button";
137
+ function E({ variant: e, size: a, shape: t, prefix: o, suffix: i, svgOnly: n, loading: s, disabled: h, href: l, children: c, className: d, ...u }) {
138
+ return F(n, u["aria-label"]), /* @__PURE__ */ r(A(), {
139
+ href: l,
140
+ ...u,
141
+ "aria-disabled": h || s || void 0,
142
+ "aria-busy": s || void 0,
143
+ "data-loading": s ? "true" : void 0,
144
+ "data-svg-only": n ? "true" : void 0,
145
+ className: p(y({
146
+ variant: e,
147
+ size: a,
148
+ shape: t
149
+ }), s && "pointer-events-none", n && "[&>svg]:size-[1.25em] [&>svg]:shrink-0", d),
150
+ children: C({
151
+ svgOnly: n,
152
+ loading: s,
153
+ prefix: o,
154
+ suffix: i,
155
+ children: c
156
+ })
157
+ });
158
+ }
159
+ E.displayName = "ButtonLink";
160
+ var L = {
161
+ type: "spring",
162
+ stiffness: 200,
163
+ damping: 30,
164
+ mass: 0.1
165
+ }, O = p("squircle overflow-hidden", "hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid", "*:data-icon:opacity-100 hover:*:data-icon:opacity-100", "transition-none"), P = p("[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]", "shadow-[inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]"), j = {
166
+ "--pho-sheen-y": "0px",
167
+ "--pho-sheen-blur": "5px",
168
+ "--pho-sheen-spread": "2px"
169
+ }, G = {
170
+ sm: {
171
+ "--pho-sheen-y": "-3px",
172
+ "--pho-sheen-blur": "8px",
173
+ "--pho-sheen-spread": "2px",
174
+ gap: 0
175
+ },
176
+ md: {
177
+ "--pho-sheen-y": "-3px",
178
+ "--pho-sheen-blur": "8px",
179
+ "--pho-sheen-spread": "2px",
180
+ gap: 0
181
+ },
182
+ lg: {
183
+ "--pho-sheen-y": "-6px",
184
+ "--pho-sheen-blur": "14px",
185
+ "--pho-sheen-spread": "1px",
186
+ gap: 4
187
+ }
188
+ }, te = B;
189
+ function ne(e) {
190
+ const { size: a = "md", fullWidth: t, className: o } = e ?? {};
191
+ return y({
192
+ variant: "primary",
193
+ size: a,
194
+ shape: "rounded",
195
+ className: f(t, o)
196
+ });
197
+ }
198
+ function f(e, a) {
199
+ return p(O, P, e && "w-full", a);
200
+ }
201
+ var R = {
202
+ sm: "has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5",
203
+ md: "has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4",
204
+ lg: "has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5"
205
+ };
206
+ function Z(e) {
207
+ return {
208
+ rest: {
209
+ ...j,
210
+ gap: 0
211
+ },
212
+ hover: { ...G[e] }
213
+ };
214
+ }
215
+ var Q = {
216
+ sm: {
217
+ rest: {
218
+ opacity: 0,
219
+ width: 1,
220
+ height: 9
221
+ },
222
+ hover: {
223
+ opacity: 1,
224
+ width: 17,
225
+ height: 18
226
+ }
227
+ },
228
+ md: {
229
+ rest: {
230
+ opacity: 0,
231
+ width: 1,
232
+ height: 9
233
+ },
234
+ hover: {
235
+ opacity: 1,
236
+ width: 17,
237
+ height: 18
238
+ }
239
+ },
240
+ lg: {
241
+ rest: {
242
+ opacity: 0,
243
+ width: 1,
244
+ height: 10
245
+ },
246
+ hover: {
247
+ opacity: 1,
248
+ width: 20,
249
+ height: 21
250
+ }
251
+ }
252
+ };
253
+ function S({ position: e, size: a, children: t }) {
254
+ return /* @__PURE__ */ r(x.span, {
255
+ "data-icon": e,
256
+ variants: Q[a],
257
+ transition: L,
258
+ style: {
259
+ display: "inline-flex",
260
+ flex: "none",
261
+ alignItems: "center",
262
+ overflow: "hidden"
263
+ },
264
+ className: "pointer-events-none [&>svg]:block [&>svg]:size-full",
265
+ children: t
266
+ });
267
+ }
268
+ function m(e, a, t) {
269
+ if (!(e == null || e === !1)) {
270
+ if (w(e)) return /* @__PURE__ */ r(S, {
271
+ position: a,
272
+ size: t,
273
+ children: /* @__PURE__ */ r(e, {})
274
+ });
275
+ if (T(e)) return /* @__PURE__ */ r(S, {
276
+ position: a,
277
+ size: t,
278
+ children: e
279
+ });
280
+ }
281
+ }
282
+ function I(e, a) {
283
+ return {
284
+ initial: !1,
285
+ animate: "rest",
286
+ whileHover: a ? void 0 : "hover",
287
+ variants: Z(e),
288
+ transition: L
289
+ };
290
+ }
291
+ var W = x.create(b), J = x.create(E);
292
+ function K({ size: e = "md", fullWidth: a, animated: t = !0, prefix: o, suffix: i, svgOnly: n, loading: s, children: h, className: l, ref: c, ...d }) {
293
+ return t ? /* @__PURE__ */ r(W, {
294
+ ref: c,
295
+ variant: "primary",
296
+ size: e,
297
+ shape: "rounded",
298
+ prefix: n ? o : m(o, "prefix", e),
299
+ suffix: n ? i : m(i, "suffix", e),
300
+ svgOnly: n,
301
+ loading: s,
302
+ ...I(e, d.disabled || s),
303
+ ...d,
304
+ className: f(a, p(R[e], l)),
305
+ children: h
306
+ }) : /* @__PURE__ */ r(b, {
307
+ ref: c,
308
+ variant: "primary",
309
+ size: e,
310
+ shape: "rounded",
311
+ prefix: o,
312
+ suffix: i,
313
+ svgOnly: n,
314
+ loading: s,
315
+ className: f(a, l),
316
+ ...d,
317
+ children: h
318
+ });
319
+ }
320
+ K.displayName = "EffectButton";
321
+ function X({ size: e = "md", fullWidth: a, animated: t = !0, prefix: o, suffix: i, svgOnly: n, loading: s, disabled: h, href: l, children: c, className: d, ...u }) {
322
+ return t ? /* @__PURE__ */ r(J, {
323
+ href: l,
324
+ variant: "primary",
325
+ size: e,
326
+ shape: "rounded",
327
+ prefix: n ? o : m(o, "prefix", e),
328
+ suffix: n ? i : m(i, "suffix", e),
329
+ svgOnly: n,
330
+ loading: s,
331
+ disabled: h,
332
+ ...I(e, h || s),
333
+ ...u,
334
+ className: f(a, p(R[e], d)),
335
+ children: c
336
+ }) : /* @__PURE__ */ r(E, {
337
+ href: l,
338
+ variant: "primary",
339
+ size: e,
340
+ shape: "rounded",
341
+ prefix: o,
342
+ suffix: i,
343
+ svgOnly: n,
344
+ loading: s,
345
+ disabled: h,
346
+ className: f(a, d),
347
+ ...u,
348
+ children: c
349
+ });
350
+ }
351
+ X.displayName = "EffectButtonLink";
352
+ export {
353
+ E as a,
354
+ X as c,
355
+ b as i,
356
+ y as l,
357
+ B as n,
358
+ te as o,
359
+ H as r,
360
+ K as s,
361
+ M as t,
362
+ ne as u
363
+ };
364
+
365
+ //# sourceMappingURL=button-CQ4cZvkC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-CQ4cZvkC.js","names":[],"sources":["../../src/components/button/button.tsx"],"sourcesContent":["import {\n isValidElement,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type FC,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\n\n/**\n * Design notes (Geist-aligned):\n * - ONE emphasis axis, kept deliberately small. `variant` is a closed,\n * semantic set — a single high-emphasis default plus the supporting and\n * intent variants you actually reach for. There is no `ghost`, no `link`,\n * and no second destructive: navigation belongs to `ButtonLink`, and an\n * inline text link is an anchor, not a button.\n * - THREE sizes (`sm` / `md` / `lg`, default `md`). No `xs` / `xl`.\n * - `prefix` / `suffix` are generic slots (icon, spinner, count…), not\n * icon-only props. `svgOnly` makes an icon button and *requires* an\n * `aria-label`. `loading` is declarative. There is no `shadow`: buttons\n * are flat on photon.codes — elevation belongs to floating layers.\n */\n\nconst COMMON_ROOT = cn(\n \"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n // Disabled (native `disabled` on <button>, `aria-disabled` on links).\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none\",\n);\n\n/**\n * Size axis — padding, text size, and icon-only (svgOnly) padding.\n *\n * Icon-side tuck (LiftKit-derived optical correction): a side that carries an\n * icon gets its padding divided by the golden ratio — an icon's rounded form\n * brings its own whitespace, so the full text-edge padding reads as a hole.\n * LiftKit: `--button-padX-sideWithIcon: calc(font-size / 1.618)`. Applied to\n * Pho's paddings: sm 14px/φ ≈ 8px · md 16px/φ ≈ 10px · lg 20px/φ ≈ 12px,\n * selected structurally via `:has()` on the slot markers (`data-icon` is\n * `prefix`, `suffix`, or `loading` — the spinner is an icon too).\n */\nexport const BUTTON_SIZES = {\n sm: {\n classes: cn(\n \"gap-1 px-3.5 py-1.5 text-sm font-medium data-svg-only:p-2\",\n \"has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2\",\n ),\n description: \"Small — compact rows, tables, and toolbars\",\n },\n md: {\n classes: cn(\n \"gap-1 px-4 py-2 text-sm font-medium data-svg-only:p-2.5\",\n \"has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5\",\n ),\n description: \"Medium — the default\",\n },\n lg: {\n classes: cn(\n \"gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3\",\n \"has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3\",\n ),\n description: \"Large — prominent and marketing actions\",\n },\n} satisfies VariantMap;\n\n/** Shape axis — corner treatment. Pills are the photon.codes default. */\nexport const BUTTON_SHAPES = {\n rounded: {\n classes: \"rounded-full\",\n description: \"Pill — the default; every button on photon.codes\",\n },\n square: {\n classes: \"rounded-lg\",\n description: \"Gently rounded corners for dense app chrome\",\n },\n circle: {\n classes: \"rounded-full\",\n description: \"Circular — pair with svgOnly for round icon buttons\",\n },\n} satisfies VariantMap;\n\n/**\n * Radius follows size (LiftKit: corner radius derives from the element's own\n * scale, never a fixed constant). `square` keeps 8px at `sm`/`md` and steps up\n * to the 12px squircle at `lg` — the exact treatment Input's `lg` field uses\n * (44px control → `squircle rounded-xl`), so lg buttons and lg fields share a\n * corner language. 12/44 ≈ 0.272 — LiftKit's half-step decimal (√φ − 1).\n */\nconst SQUARE_LG_RADIUS = \"squircle rounded-xl\";\n\n/** Emphasis axis — a tight, semantic set. `primary` is the default. */\nexport const BUTTON_VARIANTS = {\n primary: {\n classes:\n \"bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover *:data-icon:opacity-60 hover:*:data-icon:opacity-75\",\n description: \"High-emphasis primary action (default)\",\n },\n secondary: {\n classes:\n \"bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset hover:bg-pho-primary-hover hover:text-pho-secondary-hover data-loading:bg-pho-primary-hover *:data-icon:text-fg-pho-quaternary hover:*:data-icon:text-fg-pho-quaternary-hover\",\n description: \"Neutral, supporting action\",\n },\n tertiary: {\n classes:\n \"text-pho-tertiary hover:bg-pho-ghost-hover hover:text-pho-tertiary-hover data-loading:bg-pho-ghost-hover *:data-icon:text-fg-pho-quaternary hover:*:data-icon:text-fg-pho-quaternary-hover\",\n description: \"Low-emphasis, borderless action\",\n },\n error: {\n classes:\n \"bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover *:data-icon:opacity-70 hover:*:data-icon:opacity-85\",\n description: \"Destructive confirmation\",\n },\n} satisfies VariantMap;\n\nexport type ButtonSize = keyof typeof BUTTON_SIZES;\nexport type ButtonShape = keyof typeof BUTTON_SHAPES;\nexport type ButtonVariant = keyof typeof BUTTON_VARIANTS;\n\n/** Compose the full class string for a button — exported for reuse/testing. */\nexport function buttonVariants(opts?: {\n variant?: ButtonVariant;\n size?: ButtonSize;\n shape?: ButtonShape;\n className?: string;\n}): string {\n const {\n variant = \"primary\",\n size = \"md\",\n shape = \"rounded\",\n className,\n } = opts ?? {};\n return cn(\n COMMON_ROOT,\n resolveVariant(BUTTON_SHAPES, shape),\n shape === \"square\" && size === \"lg\" && SQUARE_LG_RADIUS,\n resolveVariant(BUTTON_SIZES, size),\n resolveVariant(BUTTON_VARIANTS, variant),\n className,\n );\n}\n\n/** An icon/element slot rendered before or after the label. */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\ninterface ButtonBaseProps {\n /** Emphasis variant. @default \"primary\" */\n variant?: ButtonVariant;\n /** Size variant. @default \"md\" */\n size?: ButtonSize;\n /** Corner shape. @default \"rounded\" */\n shape?: ButtonShape;\n /** Slot before the label — a component (`Plus`) or element (`<Plus />`). */\n prefix?: Slottable;\n /** Slot after the label. */\n suffix?: Slottable;\n /** Render as an icon-only button. Requires an `aria-label`. */\n svgOnly?: boolean;\n /** Show a spinner and block interaction while staying readable. */\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface ButtonProps\n extends\n ButtonBaseProps,\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonBaseProps> {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface ButtonLinkProps\n extends\n ButtonBaseProps,\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof ButtonBaseProps> {\n /** Navigate to this URL (rendered via the host's `LinkProvider`). */\n href: string;\n /** Visually and semantically disable the link. */\n disabled?: boolean;\n}\n\nconst SLOT_CLASS =\n // Icons scale with the label: 1.125em tracks the button's font size\n // (16px icon beside 14px text, 18px beside 16px) so small buttons never\n // carry an oversized glyph.\n \"transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\nfunction Slot({\n position,\n children,\n}: {\n position: \"prefix\" | \"suffix\" | \"loading\";\n children: ReactNode;\n}) {\n return (\n <span data-icon={position} className={SLOT_CLASS}>\n {children}\n </span>\n );\n}\n\nfunction Spinner() {\n return (\n <svg fill=\"none\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <circle\n className=\"stroke-current opacity-30\"\n cx=\"10\"\n cy=\"10\"\n r=\"8\"\n fill=\"none\"\n strokeWidth=\"2\"\n />\n <circle\n className=\"origin-center animate-spin stroke-current\"\n cx=\"10\"\n cy=\"10\"\n r=\"8\"\n fill=\"none\"\n strokeWidth=\"2\"\n strokeDasharray=\"12.5 50\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n\nfunction renderSlot(icon: Slottable, position: \"prefix\" | \"suffix\") {\n if (icon == null || icon === false) return null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <Slot position={position}>\n <Icon />\n </Slot>\n );\n }\n if (isValidElement(icon)) return <Slot position={position}>{icon}</Slot>;\n return null;\n}\n\nfunction buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n}: Pick<\n ButtonBaseProps,\n \"svgOnly\" | \"loading\" | \"prefix\" | \"suffix\" | \"children\"\n>) {\n if (svgOnly) {\n return loading ? <Spinner /> : children;\n }\n return (\n <>\n {loading ? (\n <Slot position=\"loading\">\n <Spinner />\n </Slot>\n ) : (\n renderSlot(prefix, \"prefix\")\n )}\n {children != null && children !== false && (\n <span data-text className=\"transition-inherit-all px-0.5\">\n {children}\n </span>\n )}\n {renderSlot(suffix, \"suffix\")}\n </>\n );\n}\n\nfunction warnMissingLabel(svgOnly: boolean | undefined, label: unknown) {\n if (\n svgOnly &&\n !label &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.warn(\n \"Button: `svgOnly` buttons require an `aria-label` that names the action.\",\n );\n }\n}\n\n/**\n * Button — triggers an action that mutates state (submit, save, delete).\n * For navigation that changes the URL, use {@link ButtonLink}.\n */\nexport function Button({\n variant,\n size,\n shape,\n prefix,\n suffix,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: ButtonProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n return (\n <button\n ref={ref}\n type=\"button\"\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n className={cn(\n buttonVariants({ variant, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\n )}\n >\n {buttonInner({ svgOnly, loading, prefix, suffix, children })}\n </button>\n );\n}\n\nButton.displayName = \"Button\";\n\n/**\n * ButtonLink — a link styled as a button, for navigation that changes the URL.\n * Shares every visual prop with {@link Button}; renders the host router's link\n * via `LinkProvider`.\n */\nexport function ButtonLink({\n variant,\n size,\n shape,\n prefix,\n suffix,\n svgOnly,\n loading,\n disabled,\n href,\n children,\n className,\n ...props\n}: ButtonLinkProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const Link = useLinkComponent();\n return (\n <Link\n href={href}\n {...props}\n aria-disabled={disabled || loading || undefined}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n className={cn(\n buttonVariants({ variant, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\n )}\n >\n {buttonInner({ svgOnly, loading, prefix, suffix, children })}\n </Link>\n );\n}\n\nButtonLink.displayName = \"ButtonLink\";\n\n/* ============================= Effect button ============================= */\n\n/**\n * EffectButton is {@link Button}, not a parallel primitive: it renders the\n * real `Button` component (Motion-wrapped via `motion.create` when animated)\n * with `variant=\"primary\"` / `shape=\"rounded\"` and layers ONLY the effect\n * treatment on top through `className`. That means it shares Button's size\n * scale 1:1 — `sm` / `md` / `lg` at 32 / 36 / 44px — so effect pills pair with\n * plain Buttons at equal heights.\n *\n * The effect itself is the Motion port of Photon's Framer \"Button - Effect\"\n * (the same engine Framer renders with, so every frame matches the source).\n * On hover, three things spring together on the *identical* transition\n * (`spring-physics 200 30 0.1` → stiffness 200 / damping 30 / mass 0.1): a\n * glossy inset sheen (mode-aware: white gloss on the black pill, dark shading\n * on dark mode's white pill) reshapes toward the bottom edge, the gap eases\n * open (lg), and any icon grows and fades in from nothing. Because it's a real\n * spring — not a fixed-duration tween — it's velocity-correct: hovering out\n * mid-animation continues from the current velocity, exactly like Framer.\n *\n * `animated` (default `true`) gates the whole Motion treatment. When `false`,\n * the pill renders as a plain static `Button` with the effect styling: no\n * springs at all, and a passed icon is simply visible from the start in\n * Button's normal slot instead of hiding until hover.\n *\n * Fill and stroke are STATIC (the source keeps them identical between the base\n * and hover variants): the contrast pill (black in light mode, white in dark —\n * photon.codes' button fill) with the glossy sheen. There is deliberately no\n * variant axis — an EffectButton without its sheen is just a pill-shaped\n * `Button`; reach for `<Button variant=\"secondary\">` for supporting/OAuth\n * actions.\n */\n\n/** The exact source transition: `spring-physics 200 30 0.1` (stiffness/damping/mass). */\nconst EFFECT_SPRING = {\n type: \"spring\",\n stiffness: 200,\n damping: 30,\n mass: 0.1,\n} as const;\n\n/**\n * The effect treatment, layered over Button's `primary` pill (tailwind-merge\n * lets the later class win within each group):\n * - `squircle` corners + `overflow-hidden` so the sheen clips to the pill.\n * - The fill is pinned static: the source keeps fill identical between base\n * and hover, so primary's hover/loading darken is overridden back to the\n * resting brand fill, and primary's icon dimming is lifted to full strength.\n * - `transition-none`: Motion drives every animated property; Button's CSS\n * transition would chase the spring's box-shadow each frame.\n */\nconst EFFECT_OVERRIDES = cn(\n \"squircle overflow-hidden\",\n \"hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid\",\n \"*:data-icon:opacity-100 hover:*:data-icon:opacity-100\",\n \"transition-none\",\n);\n\n/**\n * The sheen is drawn by a CSS box-shadow that reads three custom properties\n * (offset-y / blur / spread) — Motion springs the *properties*, not an inline\n * box-shadow string, so the sheen COLOR can stay mode-aware in CSS: white\n * gloss on the black pill in light mode, a soft dark shading on the white pill\n * in dark mode (where a white glow would be invisible). The `var()` fallbacks\n * ARE the rest geometry, so a non-animated pill shows the resting sheen with\n * no inline styles at all.\n *\n * The first shadow layer is the pill's 1px on-brand hairline. It used to be a\n * layout `border`, which added 2px to the height and broke equal-height\n * pairing with `Button`; as an inset shadow ring it reads identically but\n * keeps Button's exact 32/36/44 geometry.\n */\nconst EFFECT_SHEEN_CLASSES = cn(\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]\",\n \"shadow-[inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n);\n\nconst EFFECT_SHEEN_REST = {\n \"--pho-sheen-y\": \"0px\",\n \"--pho-sheen-blur\": \"5px\",\n \"--pho-sheen-spread\": \"2px\",\n} as const;\n\n/**\n * Per-size hover targets (sheen geometry + gap), sampled from the Framer\n * source variants. `sm`/`md` carry the compact treatment (the source's 14px-\n * label variant); `lg` carries the large one (16px label), including its\n * gap easing open on hover.\n */\nconst EFFECT_HOVER = {\n sm: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n gap: 0,\n },\n md: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n gap: 0,\n },\n lg: {\n \"--pho-sheen-y\": \"-6px\",\n \"--pho-sheen-blur\": \"14px\",\n \"--pho-sheen-spread\": \"1px\",\n gap: 4,\n },\n} as const;\n\n/**\n * EffectButton shares Button's size axis verbatim — same padding, text size,\n * and svgOnly geometry, so effect pills line up with plain Buttons.\n * Kept as a named export for API compatibility.\n */\nexport const EFFECT_BUTTON_SIZES = BUTTON_SIZES;\n\nexport type EffectButtonSize = ButtonSize;\n\n/** Compose the full class string for an effect button — exported for reuse. */\nexport function effectButtonVariants(opts?: {\n size?: EffectButtonSize;\n fullWidth?: boolean;\n className?: string;\n}): string {\n const { size = \"md\", fullWidth, className } = opts ?? {};\n return buttonVariants({\n variant: \"primary\",\n size,\n shape: \"rounded\",\n className: effectClassName(fullWidth, className),\n });\n}\n\n/** The effect layer handed to Button's `className` (merged after its own). */\nfunction effectClassName(fullWidth?: boolean, className?: string): string {\n return cn(\n EFFECT_OVERRIDES,\n EFFECT_SHEEN_CLASSES,\n fullWidth && \"w-full\",\n className,\n );\n}\n\n/**\n * The ANIMATED effect pill opts back out of Button's icon-side padding tuck:\n * its reveal icon is collapsed and invisible at rest, so tucked padding would\n * read as a lopsided pill while nothing is showing. Full symmetric padding is\n * restored per size with the same `:has()` variants (so tailwind-merge\n * collapses the tuck cleanly). The static pill (`animated={false}`) shows its\n * icon from the start and keeps the tuck like any other Button.\n */\nconst EFFECT_ICON_PAD_RESTORE: Record<EffectButtonSize, string> = {\n sm: \"has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5\",\n md: \"has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4\",\n lg: \"has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5\",\n};\n\n/** Motion variants for the pill root — the sheen (+ gap on lg) spring on hover. */\nfunction effectRootVariants(size: EffectButtonSize) {\n return {\n // Rest closes the gap so the hidden icon leaves no hole beside the label.\n rest: { ...EFFECT_SHEEN_REST, gap: 0 },\n hover: { ...EFFECT_HOVER[size] },\n };\n}\n\n/**\n * Icon reveal — a port of the source \"Arrow Right\" instance: collapsed and\n * invisible at rest, grown to full size on hover. The child inherits the\n * pill's `rest`/`hover` variant label through Motion context (the plain Slot\n * span Button wraps it in doesn't break propagation), so it springs in perfect\n * lockstep with the sheen and gap. `lg` keeps the frame-exact 20×21 from the\n * Framer source (16px label); `sm`/`md` scale the target down with their 14px\n * label so the icon stays visually matched to the text.\n */\nconst EFFECT_ICON_VARIANTS = {\n sm: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n md: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n lg: {\n rest: { opacity: 0, width: 1, height: 10 },\n hover: { opacity: 1, width: 20, height: 21 },\n },\n} as const;\n\nfunction EffectSlot({\n position,\n size,\n children,\n}: {\n position: \"prefix\" | \"suffix\";\n size: EffectButtonSize;\n children: ReactNode;\n}) {\n return (\n <motion.span\n data-icon={position}\n variants={EFFECT_ICON_VARIANTS[size]}\n transition={EFFECT_SPRING}\n style={{\n display: \"inline-flex\",\n flex: \"none\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n className=\"pointer-events-none [&>svg]:block [&>svg]:size-full\"\n >\n {children}\n </motion.span>\n );\n}\n\n/**\n * Pre-wrap an icon in its reveal slot so Button's own slot plumbing renders\n * it as a regular element prefix/suffix. Only used on the animated path — a\n * non-animated pill hands the icon to Button untouched, so it's simply\n * visible from the start.\n */\nfunction wrapEffectIcon(\n icon: Slottable,\n position: \"prefix\" | \"suffix\",\n size: EffectButtonSize,\n): ReactNode | undefined {\n if (icon == null || icon === false) return undefined;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <EffectSlot position={position} size={size}>\n <Icon />\n </EffectSlot>\n );\n }\n if (isValidElement(icon))\n return (\n <EffectSlot position={position} size={size}>\n {icon}\n </EffectSlot>\n );\n return undefined;\n}\n\n/** Motion props (initial/animate/hover/variants/transition) shared by both roots. */\nfunction effectMotionProps(size: EffectButtonSize, inert: boolean | undefined) {\n return {\n initial: false as const,\n animate: \"rest\",\n // Browsers still dispatch pointerenter on a disabled <button>, so Motion's\n // hover gesture would run the sheen / gap / icon-reveal springs on a pill\n // that must read as inert. Omitting `whileHover` while disabled or loading\n // is Motion's intended off-switch: the gesture never engages (no inline\n // styles mutate), an in-flight hover springs back to `rest`, and restoring\n // the prop re-arms hover in place — no remount, no rest-state change.\n whileHover: inert ? undefined : \"hover\",\n variants: effectRootVariants(size),\n transition: EFFECT_SPRING,\n };\n}\n\n// Motion redefines these DOM handlers; drop them from our public props so\n// spreading onto a motion element type-checks cleanly.\ntype MotionConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\"\n | \"style\";\n\ninterface EffectButtonBaseProps {\n /** Size variant — Button's scale (32 / 36 / 44px). @default \"md\" */\n size?: EffectButtonSize;\n /** Stretch to the container's full width (stacked login / OAuth pills). */\n fullWidth?: boolean;\n /**\n * Play the Motion treatment. When `true` (the default), hovering springs the\n * sheen, gap, and icon reveal — at rest the pill reads as a normal button\n * with the icon hidden. When `false` there is no animation at all: the pill\n * is a plain static `Button` with the effect styling, and a passed icon is\n * simply displayed from the start.\n * @default true\n */\n animated?: boolean;\n /** Slot before the label — a component (`Plus`) or element (`<Plus />`). */\n prefix?: Slottable;\n /** Slot after the label. */\n suffix?: Slottable;\n /** Render as an icon-only button. Requires an `aria-label`. */\n svgOnly?: boolean;\n /** Show a spinner and block interaction while staying readable. */\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface EffectButtonProps\n extends\n EffectButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface EffectButtonLinkProps\n extends\n EffectButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n /** Navigate to this URL (rendered via the host's `LinkProvider`). */\n href: string;\n /** Visually and semantically disable the link. */\n disabled?: boolean;\n}\n\n// The Motion-driven Button/ButtonLink: `motion.create` extracts the motion\n// props (variants/whileHover/transition/…) and forwards everything else, so\n// the effect pill IS a Button — same size presets, slots, loading, and\n// disabled plumbing — with Motion animating the underlying DOM node.\nconst MotionButton = motion.create(Button);\nconst MotionButtonLink = motion.create(ButtonLink);\n\n/**\n * EffectButton — {@link Button} wearing the glossy, springy effect treatment\n * ported frame-for-frame from Photon's Framer \"Button - Effect\". Shares\n * Button's size scale (`sm`/`md`/`lg` at 32/36/44px) so it pairs with plain\n * Buttons at equal heights. `animated` (default `true`) gates the Motion\n * treatment: off, it's a static effect-styled Button whose icon is always\n * visible. `fullWidth` stretches the pill across its container. There is no\n * variant axis — for a supporting/OAuth pill without the sheen, use\n * `<Button variant=\"secondary\">`. Triggers an action; for navigation use\n * {@link EffectButtonLink}.\n */\nexport function EffectButton({\n size = \"md\",\n fullWidth,\n animated = true,\n prefix,\n suffix,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: EffectButtonProps) {\n if (!animated) {\n return (\n <Button\n ref={ref}\n variant=\"primary\"\n size={size}\n shape=\"rounded\"\n prefix={prefix}\n suffix={suffix}\n svgOnly={svgOnly}\n loading={loading}\n className={effectClassName(fullWidth, className)}\n {...props}\n >\n {children}\n </Button>\n );\n }\n return (\n <MotionButton\n ref={ref}\n variant=\"primary\"\n size={size}\n shape=\"rounded\"\n // svgOnly keeps its icon always visible (nothing to reveal), so only\n // labeled pills get the reveal-wrapped slots.\n prefix={svgOnly ? prefix : wrapEffectIcon(prefix, \"prefix\", size)}\n suffix={svgOnly ? suffix : wrapEffectIcon(suffix, \"suffix\", size)}\n svgOnly={svgOnly}\n loading={loading}\n {...effectMotionProps(size, props.disabled || loading)}\n {...props}\n className={effectClassName(\n fullWidth,\n cn(EFFECT_ICON_PAD_RESTORE[size], className),\n )}\n >\n {children}\n </MotionButton>\n );\n}\n\nEffectButton.displayName = \"EffectButton\";\n\n/**\n * EffectButtonLink — {@link EffectButton} as a link, for navigation that\n * changes the URL. Composes {@link ButtonLink} (Motion-wrapped when animated),\n * which renders the host router's link via `LinkProvider`, so the pill\n * animates identically to the button.\n */\nexport function EffectButtonLink({\n size = \"md\",\n fullWidth,\n animated = true,\n prefix,\n suffix,\n svgOnly,\n loading,\n disabled,\n href,\n children,\n className,\n ...props\n}: EffectButtonLinkProps) {\n if (!animated) {\n return (\n <ButtonLink\n href={href}\n variant=\"primary\"\n size={size}\n shape=\"rounded\"\n prefix={prefix}\n suffix={suffix}\n svgOnly={svgOnly}\n loading={loading}\n disabled={disabled}\n className={effectClassName(fullWidth, className)}\n {...props}\n >\n {children}\n </ButtonLink>\n );\n }\n return (\n <MotionButtonLink\n href={href}\n variant=\"primary\"\n size={size}\n shape=\"rounded\"\n prefix={svgOnly ? prefix : wrapEffectIcon(prefix, \"prefix\", size)}\n suffix={svgOnly ? suffix : wrapEffectIcon(suffix, \"suffix\", size)}\n svgOnly={svgOnly}\n loading={loading}\n disabled={disabled}\n {...effectMotionProps(size, disabled || loading)}\n {...props}\n className={effectClassName(\n fullWidth,\n cn(EFFECT_ICON_PAD_RESTORE[size], className),\n )}\n >\n {children}\n </MotionButtonLink>\n );\n}\n\nEffectButtonLink.displayName = \"EffectButtonLink\";\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { t as i } from "./cn-7uGUzCOM.js";
3
+ import { t as n } from "./resolve-variant-CcD8iG5l.js";
4
+ import { t as a } from "./IconX-pZ9vrtSr.js";
5
+ import "react";
6
+ import { jsx as e } from "react/jsx-runtime";
7
+ var l = {
8
+ sm: { classes: "size-9 rounded-lg *:size-5" },
9
+ md: { classes: "size-10 rounded-lg *:size-5" },
10
+ lg: { classes: "size-11 rounded-xl *:size-6" }
11
+ };
12
+ function u({ size: s = "sm", label: t, className: o, ...r }) {
13
+ return /* @__PURE__ */ e("button", {
14
+ type: "button",
15
+ "aria-label": t || "Close",
16
+ ...r,
17
+ className: i("squircle text-fg-pho-quaternary outline-pho-brand hover:bg-pho-ghost-hover hover:text-fg-pho-quaternary-hover inline-flex cursor-pointer items-center justify-center p-2 transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2", n(l, s), o),
18
+ children: /* @__PURE__ */ e(a, {
19
+ "aria-hidden": "true",
20
+ className: "transition-inherit-all shrink-0"
21
+ })
22
+ });
23
+ }
24
+ u.displayName = "CloseButton";
25
+ export {
26
+ u as t
27
+ };
28
+
29
+ //# sourceMappingURL=close-button-CNhn8z0w.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-button-CNhn8z0w.js","names":[],"sources":["../../src/components/button/close-button.tsx"],"sourcesContent":["import { type ButtonHTMLAttributes } from \"react\";\nimport { IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n// Radius follows size (LiftKit: corner radius derives from the element's own\n// scale): 8px on the 36/40px chips, stepping up to Input-lg's 12px squircle on\n// the 44px chip — the shared corner language for 44px controls.\nconst CLOSE_SIZES = {\n sm: { classes: \"size-9 rounded-lg *:size-5\" },\n md: { classes: \"size-10 rounded-lg *:size-5\" },\n lg: { classes: \"size-11 rounded-xl *:size-6\" },\n} satisfies VariantMap;\n\nexport interface CloseButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"color\"\n> {\n /** Size variant. @default \"sm\" */\n size?: keyof typeof CLOSE_SIZES;\n /** Accessible label. @default \"Close\" */\n label?: string;\n}\n\n/**\n * Icon-only dismiss control — a squircle chip that fills on hover. Adapts to\n * light/dark automatically via semantic tokens. Used by Dialog and dismissible\n * surfaces.\n */\nexport function CloseButton({\n size = \"sm\",\n label,\n className,\n ...props\n}: CloseButtonProps) {\n return (\n <button\n type=\"button\"\n aria-label={label || \"Close\"}\n {...props}\n className={cn(\n \"squircle text-fg-pho-quaternary outline-pho-brand hover:bg-pho-ghost-hover hover:text-fg-pho-quaternary-hover inline-flex cursor-pointer items-center justify-center p-2 transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n resolveVariant(CLOSE_SIZES, size),\n className,\n )}\n >\n <IconX aria-hidden=\"true\" className=\"transition-inherit-all shrink-0\" />\n </button>\n );\n}\n\nCloseButton.displayName = \"CloseButton\";\n"],"mappings":";;;;;;AAQA,IAAM,IAAc;AAAA,EAClB,IAAI,EAAE,SAAS,6BAA6B;AAAA,EAC5C,IAAI,EAAE,SAAS,8BAA8B;AAAA,EAC7C,IAAI,EAAE,SAAS,8BAA8B;AAC/C;AAiBA,SAAgB,EAAY,EAC1B,MAAA,IAAO,MACP,OAAA,GACA,WAAA,GACA,GAAG,EAAA,GACgB;AACnB,SACE,gBAAA,EAAC,UAAD;AAAA,IACE,MAAK;AAAA,IACL,cAAY,KAAS;AAAA,IACrB,GAAI;AAAA,IACJ,WAAW,EACT,uQACA,EAAe,GAAa,CAAI,GAChC,CACF;AAAA,cAEA,gBAAA,EAAC,GAAD;AAAA,MAAO,eAAY;AAAA,MAAO,WAAU;AAAA,IAAmC,CAAA;AAAA,EACjE,CAAA;AAEZ;AAEA,EAAY,cAAc"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as n } from "./cn-7uGUzCOM.js";
3
- import { t as f } from "./close-button-CRiiwy6K.js";
3
+ import { t as f } from "./close-button-CNhn8z0w.js";
4
4
  import "react";
5
5
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
6
  import { Dialog as r } from "@base-ui/react/dialog";
@@ -60,4 +60,4 @@ export {
60
60
  T as t
61
61
  };
62
62
 
63
- //# sourceMappingURL=dialog-sTOaOEB2.js.map
63
+ //# sourceMappingURL=dialog-7oOe-eaO.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-sTOaOEB2.js","names":[],"sources":["../../src/components/dialog/dialog.tsx"],"sourcesContent":["import { type ComponentProps, type ElementType, type ReactNode } from \"react\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { cn } from \"../../utils/cn\";\nimport { CloseButton } from \"../button/close-button\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\nconst Root = BaseDialog.Root;\nconst Trigger = BaseDialog.Trigger;\nconst Portal = BaseDialog.Portal;\nconst Close = BaseDialog.Close;\n\nfunction Backdrop({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Backdrop>) {\n return (\n <BaseDialog.Backdrop\n className={cn(\n \"bg-pho-overlay fixed inset-0 z-50 backdrop-blur-[2px] transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Popup({ className, ...props }: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n // Floating layer: bg-pho-primary + border-pho-secondary hairline, elevation kept.\n \"squircle border-pho-secondary bg-pho-primary shadow-pho-xl fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-2xl border outline-none\",\n \"transition-all duration-200 ease-out\",\n \"data-[starting-style]:scale-95 data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-95 data-[ending-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Title({ className, ...props }: StyledProps<typeof BaseDialog.Title>) {\n return (\n <BaseDialog.Title\n className={cn(\"text-pho-primary text-lg font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction Description({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Description>) {\n return (\n <BaseDialog.Description\n className={cn(\"text-pho-tertiary text-sm\", className)}\n {...props}\n />\n );\n}\n\ninterface ContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /** Show the top-right close button. @default true */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience panel: renders the portal, backdrop, and styled popup with an\n * optional header (title/description + close). For full control, compose the\n * parts directly (`Dialog.Portal` / `Dialog.Backdrop` / `Dialog.Popup`).\n */\nfunction Content({\n title,\n description,\n showClose = true,\n children,\n className,\n ...props\n}: ContentProps) {\n const hasHeader = Boolean(title || description || showClose);\n return (\n <Portal>\n <Backdrop />\n <Popup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex items-start justify-between gap-4 px-6 pt-5\">\n <div className=\"flex flex-col gap-1\">\n {title && <Title>{title}</Title>}\n {description && <Description>{description}</Description>}\n </div>\n {showClose && <Close render={<CloseButton />} />}\n </div>\n )}\n <div className=\"px-6 py-5\">{children}</div>\n </Popup>\n </Portal>\n );\n}\n\n/**\n * Modal dialog built on Base UI `Dialog`. Controlled or uncontrolled via\n * `Dialog.Root` (`open` / `onOpenChange` / `defaultOpen`).\n */\nexport const Dialog = {\n Root,\n Trigger,\n Portal,\n Backdrop,\n Popup,\n Title,\n Description,\n Close,\n Content,\n};\n"],"mappings":";;;;;;AAaA,IAAM,IAAO,EAAW,MAClB,IAAU,EAAW,SACrB,IAAS,EAAW,QACpB,IAAQ,EAAW;AAEzB,SAAS,EAAS,EAChB,WAAA,GACA,GAAG,EAAA,GACuC;AAC1C,SACE,gBAAA,EAAC,EAAW,UAAZ;AAAA,IACE,WAAW,EACT,kGACA,iEACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAET,yMACA,wCACA,kEACA,8DACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAAG,0CAA0C,CAAS;AAAA,IACjE,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAY,EACnB,WAAA,GACA,GAAG,EAAA,GAC0C;AAC7C,SACE,gBAAA,EAAC,EAAW,aAAZ;AAAA,IACE,WAAW,EAAG,6BAA6B,CAAS;AAAA,IACpD,GAAI;AAAA,EACL,CAAA;AAEL;AAoBA,SAAS,EAAQ,EACf,OAAA,GACA,aAAA,GACA,WAAA,IAAY,IACZ,UAAA,GACA,WAAA,GACA,GAAG,EAAA,GACY;AAEf,SACE,gBAAA,EAAC,GAAD,EAAA,UAAA,CACE,gBAAA,EAAC,GAAD,CAAW,CAAA,GACX,gBAAA,EAAC,GAAD;AAAA,IAAkB,WAAA;AAAA,IAAW,GAAI;AAAA,cAAjC,CAJc,GAAQ,KAAS,KAAe,MAM1C,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,gBAAf,CACE,gBAAA,EAAC,OAAD;AAAA,QAAK,WAAU;AAAA,kBAAf,CACG,KAAS,gBAAA,EAAC,GAAD,EAAA,UAAQ,EAAa,CAAA,GAC9B,KAAe,gBAAA,EAAC,GAAD,EAAA,UAAc,EAAyB,CAAA,CACpD;AAAA,UACJ,KAAa,gBAAA,EAAC,GAAD,EAAO,QAAQ,gBAAA,EAAC,GAAD,CAAc,CAAA,EAAI,CAAA,CAC5C;AAAA,QAEP,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,MAAa,UAAA;AAAA,IAAc,CAAA,CACrC;AAAA,IACD,EAAA,CAAA;AAEZ;AAMA,IAAa,IAAS;AAAA,EACpB,MAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AACF"}
1
+ {"version":3,"file":"dialog-7oOe-eaO.js","names":[],"sources":["../../src/components/dialog/dialog.tsx"],"sourcesContent":["import { type ComponentProps, type ElementType, type ReactNode } from \"react\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { cn } from \"../../utils/cn\";\nimport { CloseButton } from \"../button/close-button\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\nconst Root = BaseDialog.Root;\nconst Trigger = BaseDialog.Trigger;\nconst Portal = BaseDialog.Portal;\nconst Close = BaseDialog.Close;\n\nfunction Backdrop({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Backdrop>) {\n return (\n <BaseDialog.Backdrop\n className={cn(\n \"bg-pho-overlay fixed inset-0 z-50 backdrop-blur-[2px] transition-opacity duration-200 ease-out\",\n \"data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Popup({ className, ...props }: StyledProps<typeof BaseDialog.Popup>) {\n return (\n <BaseDialog.Popup\n className={cn(\n // Floating layer: bg-pho-primary + border-pho-secondary hairline, elevation kept.\n \"squircle border-pho-secondary bg-pho-primary shadow-pho-xl fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-2xl border outline-none\",\n \"transition-all duration-200 ease-out\",\n \"data-[starting-style]:scale-95 data-[starting-style]:opacity-0\",\n \"data-[ending-style]:scale-95 data-[ending-style]:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Title({ className, ...props }: StyledProps<typeof BaseDialog.Title>) {\n return (\n <BaseDialog.Title\n className={cn(\"text-pho-primary text-lg font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction Description({\n className,\n ...props\n}: StyledProps<typeof BaseDialog.Description>) {\n return (\n <BaseDialog.Description\n className={cn(\"text-pho-tertiary text-sm\", className)}\n {...props}\n />\n );\n}\n\ninterface ContentProps extends Omit<\n StyledProps<typeof BaseDialog.Popup>,\n \"title\"\n> {\n /** Heading rendered in the panel header. */\n title?: ReactNode;\n /** Supporting text under the title. */\n description?: ReactNode;\n /** Show the top-right close button. @default true */\n showClose?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Convenience panel: renders the portal, backdrop, and styled popup with an\n * optional header (title/description + close). For full control, compose the\n * parts directly (`Dialog.Portal` / `Dialog.Backdrop` / `Dialog.Popup`).\n */\nfunction Content({\n title,\n description,\n showClose = true,\n children,\n className,\n ...props\n}: ContentProps) {\n const hasHeader = Boolean(title || description || showClose);\n return (\n <Portal>\n <Backdrop />\n <Popup className={className} {...props}>\n {hasHeader && (\n <div className=\"flex items-start justify-between gap-4 px-6 pt-5\">\n <div className=\"flex flex-col gap-1\">\n {title && <Title>{title}</Title>}\n {description && <Description>{description}</Description>}\n </div>\n {showClose && <Close render={<CloseButton />} />}\n </div>\n )}\n <div className=\"px-6 py-5\">{children}</div>\n </Popup>\n </Portal>\n );\n}\n\n/**\n * Modal dialog built on Base UI `Dialog`. Controlled or uncontrolled via\n * `Dialog.Root` (`open` / `onOpenChange` / `defaultOpen`).\n */\nexport const Dialog = {\n Root,\n Trigger,\n Portal,\n Backdrop,\n Popup,\n Title,\n Description,\n Close,\n Content,\n};\n"],"mappings":";;;;;;AAaA,IAAM,IAAO,EAAW,MAClB,IAAU,EAAW,SACrB,IAAS,EAAW,QACpB,IAAQ,EAAW;AAEzB,SAAS,EAAS,EAChB,WAAA,GACA,GAAG,EAAA,GACuC;AAC1C,SACE,gBAAA,EAAC,EAAW,UAAZ;AAAA,IACE,WAAW,EACT,kGACA,iEACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAET,yMACA,wCACA,kEACA,8DACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAA+C;AAC5E,SACE,gBAAA,EAAC,EAAW,OAAZ;AAAA,IACE,WAAW,EAAG,0CAA0C,CAAS;AAAA,IACjE,GAAI;AAAA,EACL,CAAA;AAEL;AAEA,SAAS,EAAY,EACnB,WAAA,GACA,GAAG,EAAA,GAC0C;AAC7C,SACE,gBAAA,EAAC,EAAW,aAAZ;AAAA,IACE,WAAW,EAAG,6BAA6B,CAAS;AAAA,IACpD,GAAI;AAAA,EACL,CAAA;AAEL;AAoBA,SAAS,EAAQ,EACf,OAAA,GACA,aAAA,GACA,WAAA,IAAY,IACZ,UAAA,GACA,WAAA,GACA,GAAG,EAAA,GACY;AAEf,SACE,gBAAA,EAAC,GAAD,EAAA,UAAA,CACE,gBAAA,EAAC,GAAD,CAAW,CAAA,GACX,gBAAA,EAAC,GAAD;AAAA,IAAkB,WAAA;AAAA,IAAW,GAAI;AAAA,cAAjC,CAJc,GAAQ,KAAS,KAAe,MAM1C,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,gBAAf,CACE,gBAAA,EAAC,OAAD;AAAA,QAAK,WAAU;AAAA,kBAAf,CACG,KAAS,gBAAA,EAAC,GAAD,EAAA,UAAQ,EAAa,CAAA,GAC9B,KAAe,gBAAA,EAAC,GAAD,EAAA,UAAc,EAAyB,CAAA,CACpD;AAAA,UACJ,KAAa,gBAAA,EAAC,GAAD,EAAO,QAAQ,gBAAA,EAAC,GAAD,CAAc,CAAA,EAAI,CAAA,CAC5C;AAAA,QAEP,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,MAAa,UAAA;AAAA,IAAc,CAAA,CACrC;AAAA,IACD,EAAA,CAAA;AAEZ;AAMA,IAAa,IAAS;AAAA,EACpB,MAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AACF"}