@photon-ai/pho-ui 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/IconCheck-D2WuculQ.js +11 -0
- package/dist/chunks/IconCheck-D2WuculQ.js.map +1 -0
- package/dist/chunks/card-9cGnv-VD.js +56 -0
- package/dist/chunks/card-9cGnv-VD.js.map +1 -0
- package/dist/chunks/{close-button-8f-oRJXt.js → close-button-Bq9f-H15.js} +256 -181
- package/dist/chunks/close-button-Bq9f-H15.js.map +1 -0
- package/dist/chunks/{dialog-Bgb2Z1_v.js → dialog-DolPKFGS.js} +2 -2
- package/dist/chunks/{dialog-Bgb2Z1_v.js.map → dialog-DolPKFGS.js.map} +1 -1
- package/dist/chunks/{input-BSJGBUbf.js → input-CxS7ndpb.js} +7 -7
- package/dist/chunks/input-CxS7ndpb.js.map +1 -0
- package/dist/chunks/input-group-D6dlma8k.js +85 -0
- package/dist/chunks/input-group-D6dlma8k.js.map +1 -0
- package/dist/chunks/{menu-Dan0oRvL.js → menu-BSXds3Rt.js} +2 -2
- package/dist/chunks/{menu-Dan0oRvL.js.map → menu-BSXds3Rt.js.map} +1 -1
- package/dist/chunks/settings-B0vhLoMi.js +134 -0
- package/dist/chunks/settings-B0vhLoMi.js.map +1 -0
- package/dist/chunks/sidebar-BCqq58hV.js +177 -0
- package/dist/chunks/sidebar-BCqq58hV.js.map +1 -0
- package/dist/chunks/use-press-pulse-CMwPTOR2.js +44 -0
- package/dist/chunks/use-press-pulse-CMwPTOR2.js.map +1 -0
- package/dist/components/button.js +1 -1
- package/dist/components/card.js +6 -0
- package/dist/components/checkbox.js +33 -28
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/context-menu.js +2 -2
- package/dist/components/dialog.js +1 -1
- package/dist/components/field.js +17 -17
- package/dist/components/field.js.map +1 -1
- package/dist/components/input-group.js +5 -0
- package/dist/components/input.js +1 -1
- package/dist/components/menu.js +1 -1
- package/dist/components/select.js +83 -39
- package/dist/components/select.js.map +1 -1
- package/dist/components/skeleton.js +18 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/index.js +44 -27
- package/dist/primitives.js +1 -1
- package/dist/sections/settings.js +8 -0
- package/dist/sections/sidebar.js +8 -5
- package/dist/src/components/card/card.d.ts +75 -0
- package/dist/src/components/card/index.d.ts +1 -0
- package/dist/src/components/checkbox/checkbox.d.ts +5 -1
- package/dist/src/components/input-group/index.d.ts +1 -0
- package/dist/src/components/input-group/input-group.d.ts +57 -0
- package/dist/src/components/select/index.d.ts +1 -1
- package/dist/src/components/select/select.d.ts +29 -10
- package/dist/src/components/skeleton/index.d.ts +1 -0
- package/dist/src/components/skeleton/skeleton.d.ts +11 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/sections/settings/index.d.ts +1 -0
- package/dist/src/sections/settings/settings.d.ts +170 -0
- package/dist/src/sections/sidebar/index.d.ts +1 -1
- package/dist/src/sections/sidebar/sidebar.d.ts +25 -6
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/use-press-pulse.d.ts +37 -0
- package/dist/utils.js +12 -8
- package/package.json +17 -1
- package/src/styles/theme.css +18 -4
- package/dist/chunks/close-button-8f-oRJXt.js.map +0 -1
- package/dist/chunks/input-BSJGBUbf.js.map +0 -1
- package/dist/chunks/sidebar-Buhva5xa.js +0 -164
- package/dist/chunks/sidebar-Buhva5xa.js.map +0 -1
|
@@ -1,25 +1,44 @@
|
|
|
1
|
-
import { ComponentProps, ElementType } from 'react';
|
|
1
|
+
import { ComponentProps, ElementType, ReactNode } from 'react';
|
|
2
2
|
import { Select as BaseSelect } from '@base-ui/react/select';
|
|
3
3
|
/** Override Base UI's `string | (state) => string` className with plain string. */
|
|
4
4
|
type StyledProps<T extends ElementType> = Omit<ComponentProps<T>, "className"> & {
|
|
5
5
|
className?: string;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
export type SelectTriggerVariant = "default" | "inline";
|
|
8
|
+
declare function Root({ multiple, ...props }: ComponentProps<typeof BaseSelect.Root>): import("react").JSX.Element;
|
|
9
|
+
declare function Trigger({ className, disabled, variant, onPointerDown, ...props }: StyledProps<typeof BaseSelect.Trigger> & {
|
|
10
|
+
/**
|
|
11
|
+
* `inline` drops the field chrome (ring, background, padding, min-width)
|
|
12
|
+
* to ride a host surface — an Input Group addon, a toolbar — which then
|
|
13
|
+
* carries the focus/open treatment. The press pulse moves to the arrow.
|
|
14
|
+
* @default "default"
|
|
15
|
+
*/
|
|
16
|
+
variant?: SelectTriggerVariant;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
8
18
|
declare function Icon({ className, ...props }: StyledProps<typeof BaseSelect.Icon>): import("react").JSX.Element;
|
|
9
|
-
declare function Positioner({ className, sideOffset, ...props }: StyledProps<typeof BaseSelect.Positioner>): import("react").JSX.Element;
|
|
19
|
+
declare function Positioner({ className, sideOffset, align, alignItemWithTrigger, ...props }: StyledProps<typeof BaseSelect.Positioner>): import("react").JSX.Element;
|
|
10
20
|
declare function Popup({ className, ...props }: StyledProps<typeof BaseSelect.Popup>): import("react").JSX.Element;
|
|
11
21
|
declare function Item({ className, ...props }: StyledProps<typeof BaseSelect.Item>): import("react").JSX.Element;
|
|
12
|
-
declare function
|
|
22
|
+
declare function ItemText({ className, ...props }: StyledProps<typeof BaseSelect.ItemText>): import("react").JSX.Element;
|
|
23
|
+
/**
|
|
24
|
+
* Leading selection mark, mode-aware. Single: a brand check that appears in
|
|
25
|
+
* the reserved column. Multiple: an always-visible checkbox box that fills
|
|
26
|
+
* with the same quiet fill/stroke ease as the standalone Checkbox.
|
|
27
|
+
*/
|
|
28
|
+
declare function ItemIndicator({ className, children, ...props }: StyledProps<typeof BaseSelect.ItemIndicator> & {
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
}): import("react").JSX.Element;
|
|
13
31
|
declare function GroupLabel({ className, ...props }: StyledProps<typeof BaseSelect.GroupLabel>): import("react").JSX.Element;
|
|
14
32
|
/**
|
|
15
33
|
* Select built on Base UI `Select` — pick one option (or several with
|
|
16
|
-
* `multiple`) from a list that opens on click
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* use `
|
|
34
|
+
* `multiple`) from a list that opens below the trigger on click; the pattern
|
|
35
|
+
* other design systems call a picker. Pass `items` to `Select.Root`, render an
|
|
36
|
+
* `Item` per option, and show the selection with `Select.Value`. For a short,
|
|
37
|
+
* always-visible set use `RadioGroup`; to filter a long list by typing use
|
|
38
|
+
* `Combobox`.
|
|
20
39
|
*/
|
|
21
40
|
export declare const Select: {
|
|
22
|
-
Root: typeof
|
|
41
|
+
Root: typeof Root;
|
|
23
42
|
Trigger: typeof Trigger;
|
|
24
43
|
Value: import('react').ForwardRefExoticComponent<Omit<import('@base-ui/react').SelectValueProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
|
|
25
44
|
Icon: typeof Icon;
|
|
@@ -28,7 +47,7 @@ export declare const Select: {
|
|
|
28
47
|
Popup: typeof Popup;
|
|
29
48
|
List: import('react').ForwardRefExoticComponent<Omit<import('@base-ui/react').SelectListProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
|
|
30
49
|
Item: typeof Item;
|
|
31
|
-
ItemText:
|
|
50
|
+
ItemText: typeof ItemText;
|
|
32
51
|
ItemIndicator: typeof ItemIndicator;
|
|
33
52
|
Group: import('react').ForwardRefExoticComponent<Omit<import('@base-ui/react').SelectGroupProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
|
|
34
53
|
GroupLabel: typeof GroupLabel;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Skeleton, type SkeletonProps } from './skeleton';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
export interface SkeletonProps extends ComponentProps<"div"> {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* A pulsing placeholder bone for loading surfaces. Size and shape come from
|
|
6
|
+
* `className` (`h-*`, `w-*`, `rounded-full`, …) — the component only supplies
|
|
7
|
+
* the wash and motion. Decorative by default (`aria-hidden`); mark the
|
|
8
|
+
* surrounding region with `aria-busy` / `aria-label` instead of labeling each
|
|
9
|
+
* bone. Prefer `Spinner` when a single control is pending.
|
|
10
|
+
*/
|
|
11
|
+
export declare const Skeleton: import('react').ForwardRefExoticComponent<Omit<SkeletonProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -8,5 +8,8 @@
|
|
|
8
8
|
export * from './utils';
|
|
9
9
|
export * from './components/button';
|
|
10
10
|
export * from './components/input';
|
|
11
|
+
export * from './components/input-group';
|
|
12
|
+
export * from './components/card';
|
|
11
13
|
export * from './components/dialog';
|
|
12
14
|
export * from './sections/sidebar';
|
|
15
|
+
export * from './sections/settings';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Settings, SETTINGS_CARD, SETTINGS_CALLOUT, SETTINGS_ROW, type SettingsRowProps, } from './settings';
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ComponentProps, ReactNode, Ref } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Settings — Pho's settings-page section: a page title, section titles, and
|
|
4
|
+
* bordered cards of label/control rows.
|
|
5
|
+
*
|
|
6
|
+
* Spacing: wrap the page in `Page` (`max-w-2xl`, vertical padding). `Stack`
|
|
7
|
+
* separates blocks (`gap-10`). Inside a block, children share `gap-3`. Wrap a
|
|
8
|
+
* section title + description in `Header` (`gap-0.5`) so that pair stays tight
|
|
9
|
+
* — don't put them as loose siblings. Parts already carry `px-5`.
|
|
10
|
+
*/
|
|
11
|
+
/** Bordered card that stacks rows with hairline dividers. */
|
|
12
|
+
export declare const SETTINGS_CARD = "divide-y divide-pho-secondary rounded-base border border-pho-secondary bg-pho-primary";
|
|
13
|
+
/**
|
|
14
|
+
* Standalone callout surface — featured block (invite form, notice, etc.).
|
|
15
|
+
* Don't wrap in `Card`; this is the outer chrome.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SETTINGS_CALLOUT = "flex flex-col gap-3 rounded-base border border-pho-secondary bg-pho-page p-5";
|
|
18
|
+
/**
|
|
19
|
+
* One settings row — label (+ optional description) left, control right.
|
|
20
|
+
* `min-h-[4.25rem]` (68px) keeps avatar/control rows from collapsing shorter
|
|
21
|
+
* than a comfortable touch target.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SETTINGS_ROW = "flex min-h-[4.25rem] flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6";
|
|
24
|
+
/**
|
|
25
|
+
* Page chrome — centered column at the settings content width. Hosts
|
|
26
|
+
* `PageTitle` + `Stack` (and any route-level siblings). Prefer this over
|
|
27
|
+
* ad-hoc `max-w-*` wrappers in the app shell.
|
|
28
|
+
*/
|
|
29
|
+
declare function Page({ className, ...props }: ComponentProps<"div"> & {
|
|
30
|
+
ref?: Ref<HTMLDivElement>;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
/** Page title — largest heading on the surface. */
|
|
33
|
+
declare function PageTitle({ className, children, ...props }: ComponentProps<"h1"> & {
|
|
34
|
+
ref?: Ref<HTMLHeadingElement>;
|
|
35
|
+
}): import("react").JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* Vertical stack under the page title. Blocks (forms / sections) use `gap-10`;
|
|
38
|
+
* each block lays out its children with `gap-3`.
|
|
39
|
+
*/
|
|
40
|
+
declare function Stack({ className, ...props }: ComponentProps<"div"> & {
|
|
41
|
+
ref?: Ref<HTMLDivElement>;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
43
|
+
/**
|
|
44
|
+
* Title + optional description pair. Keeps that couple tight (`gap-0.5`) so
|
|
45
|
+
* the block's `gap-3` only applies between this header and the card.
|
|
46
|
+
*/
|
|
47
|
+
declare function Header({ className, ...props }: ComponentProps<"div"> & {
|
|
48
|
+
ref?: Ref<HTMLDivElement>;
|
|
49
|
+
}): import("react").JSX.Element;
|
|
50
|
+
/** Section heading — same padding as the page title, smaller type. */
|
|
51
|
+
declare function SectionTitle({ className, children, ...props }: ComponentProps<"h2"> & {
|
|
52
|
+
ref?: Ref<HTMLHeadingElement>;
|
|
53
|
+
}): import("react").JSX.Element;
|
|
54
|
+
/**
|
|
55
|
+
* Supporting copy under a section title. Same display step as the title;
|
|
56
|
+
* color + normal weight keep it secondary. Always wrap with `Header`.
|
|
57
|
+
*/
|
|
58
|
+
declare function Description({ className, ...props }: ComponentProps<"p"> & {
|
|
59
|
+
ref?: Ref<HTMLParagraphElement>;
|
|
60
|
+
}): import("react").JSX.Element;
|
|
61
|
+
/** Bordered card that stacks settings rows with hairline dividers. */
|
|
62
|
+
declare function Card({ className, ...props }: ComponentProps<"div"> & {
|
|
63
|
+
ref?: Ref<HTMLDivElement>;
|
|
64
|
+
}): import("react").JSX.Element;
|
|
65
|
+
/**
|
|
66
|
+
* Standalone callout. Pair with `CalloutTitle` / `CalloutDescription` (and
|
|
67
|
+
* optional actions) for a featured block — e.g. an invite form. Do not wrap
|
|
68
|
+
* in `Card`; this is already the outer surface.
|
|
69
|
+
*/
|
|
70
|
+
declare function Callout({ className, ...props }: ComponentProps<"div"> & {
|
|
71
|
+
ref?: Ref<HTMLDivElement>;
|
|
72
|
+
}): import("react").JSX.Element;
|
|
73
|
+
/** Callout heading — same type as `SectionTitle`. */
|
|
74
|
+
declare function CalloutTitle({ className, children, ...props }: ComponentProps<"h2"> & {
|
|
75
|
+
ref?: Ref<HTMLHeadingElement>;
|
|
76
|
+
}): import("react").JSX.Element;
|
|
77
|
+
/** Supporting copy under a callout title. */
|
|
78
|
+
declare function CalloutDescription({ className, ...props }: ComponentProps<"p"> & {
|
|
79
|
+
ref?: Ref<HTMLParagraphElement>;
|
|
80
|
+
}): import("react").JSX.Element;
|
|
81
|
+
export interface SettingsRowProps extends Omit<ComponentProps<"div">, "title"> {
|
|
82
|
+
/** Left-side field name. */
|
|
83
|
+
label: string;
|
|
84
|
+
/** Optional helper under the label. */
|
|
85
|
+
description?: ReactNode;
|
|
86
|
+
/** Associates the label with a control id. When omitted, the label is not a `<label>`. */
|
|
87
|
+
htmlFor?: string;
|
|
88
|
+
ref?: Ref<HTMLDivElement>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* One settings row: label (+ optional description) left, control right.
|
|
92
|
+
* Pass `htmlFor` when the row wraps a labelled input; omit it for display/
|
|
93
|
+
* action rows (avatar, danger-zone delete).
|
|
94
|
+
*/
|
|
95
|
+
declare function Row({ label, description, htmlFor, className, children, ...props }: SettingsRowProps): import("react").JSX.Element;
|
|
96
|
+
/** Read-only value on the right side of a row (email, etc.). */
|
|
97
|
+
declare function Value({ className, ...props }: ComponentProps<"p"> & {
|
|
98
|
+
ref?: Ref<HTMLParagraphElement>;
|
|
99
|
+
}): import("react").JSX.Element;
|
|
100
|
+
/**
|
|
101
|
+
* Inline error under a row (still inside the card). Matches card horizontal
|
|
102
|
+
* padding so hosts don't re-apply `px-5`.
|
|
103
|
+
*/
|
|
104
|
+
declare function Alert({ className, ...props }: ComponentProps<"p"> & {
|
|
105
|
+
ref?: Ref<HTMLParagraphElement>;
|
|
106
|
+
}): import("react").JSX.Element;
|
|
107
|
+
/**
|
|
108
|
+
* Trailing bar inside a card — typically a save status + submit button that
|
|
109
|
+
* appears after the form is dirty. Same horizontal/vertical padding as a row.
|
|
110
|
+
*/
|
|
111
|
+
declare function Footer({ className, ...props }: ComponentProps<"div"> & {
|
|
112
|
+
ref?: Ref<HTMLDivElement>;
|
|
113
|
+
}): import("react").JSX.Element;
|
|
114
|
+
/**
|
|
115
|
+
* Settings page layout. One stack, one gap rhythm — only title scale differs:
|
|
116
|
+
*
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <Settings.Page>
|
|
119
|
+
* <Settings.PageTitle>Profile</Settings.PageTitle>
|
|
120
|
+
* <Settings.Stack>
|
|
121
|
+
* <form>
|
|
122
|
+
* <Settings.Card>
|
|
123
|
+
* <Settings.Row label="Name" htmlFor="first-name">
|
|
124
|
+
* <Input id="first-name" size="sm" />
|
|
125
|
+
* </Settings.Row>
|
|
126
|
+
* </Settings.Card>
|
|
127
|
+
* </form>
|
|
128
|
+
* <form>
|
|
129
|
+
* <Settings.Header>
|
|
130
|
+
* <Settings.SectionTitle>Agent profile</Settings.SectionTitle>
|
|
131
|
+
* <Settings.Description>…</Settings.Description>
|
|
132
|
+
* </Settings.Header>
|
|
133
|
+
* <Settings.Card>…</Settings.Card>
|
|
134
|
+
* </form>
|
|
135
|
+
* <div>
|
|
136
|
+
* <Settings.SectionTitle>Danger zone</Settings.SectionTitle>
|
|
137
|
+
* <Settings.Card>
|
|
138
|
+
* <Settings.Row label="Delete account" description="…">
|
|
139
|
+
* <Button color="destructive" size="sm">Delete</Button>
|
|
140
|
+
* </Settings.Row>
|
|
141
|
+
* </Settings.Card>
|
|
142
|
+
* </div>
|
|
143
|
+
* <Settings.Callout>
|
|
144
|
+
* <Settings.Header>
|
|
145
|
+
* <Settings.CalloutTitle>Invitations</Settings.CalloutTitle>
|
|
146
|
+
* <Settings.CalloutDescription>…</Settings.CalloutDescription>
|
|
147
|
+
* </Settings.Header>
|
|
148
|
+
* …invite form…
|
|
149
|
+
* </Settings.Callout>
|
|
150
|
+
* </Settings.Stack>
|
|
151
|
+
* </Settings.Page>
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export declare const Settings: {
|
|
155
|
+
Page: typeof Page;
|
|
156
|
+
PageTitle: typeof PageTitle;
|
|
157
|
+
Stack: typeof Stack;
|
|
158
|
+
Header: typeof Header;
|
|
159
|
+
SectionTitle: typeof SectionTitle;
|
|
160
|
+
Description: typeof Description;
|
|
161
|
+
Card: typeof Card;
|
|
162
|
+
Callout: typeof Callout;
|
|
163
|
+
CalloutTitle: typeof CalloutTitle;
|
|
164
|
+
CalloutDescription: typeof CalloutDescription;
|
|
165
|
+
Row: typeof Row;
|
|
166
|
+
Value: typeof Value;
|
|
167
|
+
Alert: typeof Alert;
|
|
168
|
+
Footer: typeof Footer;
|
|
169
|
+
};
|
|
170
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Sidebar, SIDEBAR_ROOT, SIDEBAR_ITEM, SIDEBAR_ITEM_ACTIVE, SIDEBAR_ITEM_INACTIVE, type SidebarItemProps, type SidebarItemLinkProps, type SidebarItemButtonProps, type SidebarAccountRowProps, } from './sidebar';
|
|
1
|
+
export { Sidebar, SIDEBAR_ROOT, SIDEBAR_ROOT_BASE, SIDEBAR_ROOT_INSET, SIDEBAR_HEADER_INSET, SIDEBAR_ITEM, SIDEBAR_ITEM_ACTIVE, SIDEBAR_ITEM_INACTIVE, type SidebarInset, type SidebarItemProps, type SidebarItemLinkProps, type SidebarItemButtonProps, type SidebarAccountRowProps, } from './sidebar';
|
|
@@ -11,26 +11,45 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes, ComponentProps, FC, ReactNo
|
|
|
11
11
|
* `ButtonLink`), state arrives via props (`active`), and interactive footers
|
|
12
12
|
* compose with other Pho parts (e.g. wrap `Sidebar.AccountRow` in a
|
|
13
13
|
* `Menu.Trigger render={...}` to open an account menu).
|
|
14
|
+
*
|
|
15
|
+
* `Root` takes `inset` (`base` | `sm`, default `base`) for the rail's outer
|
|
16
|
+
* padding and header spacing — prefer that over host-app class overrides.
|
|
14
17
|
*/
|
|
15
|
-
/** The rail itself — sticky, full-height, hairline right edge. */
|
|
16
|
-
export declare const SIDEBAR_ROOT = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r
|
|
18
|
+
/** The rail itself — sticky, full-height, hairline right edge (no inset). */
|
|
19
|
+
export declare const SIDEBAR_ROOT = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r";
|
|
20
|
+
/** Outer inset on `Root` — `base` is the docs/default rail; `sm` is tighter. */
|
|
21
|
+
export declare const SIDEBAR_ROOT_INSET: {
|
|
22
|
+
readonly base: "px-4 pt-5 pb-4";
|
|
23
|
+
readonly sm: "px-3 pt-4 pb-3";
|
|
24
|
+
};
|
|
25
|
+
/** `SIDEBAR_ROOT` with the default `base` inset — one string for docs chrome. */
|
|
26
|
+
export declare const SIDEBAR_ROOT_BASE: string;
|
|
27
|
+
/** Header spacing paired with `Root`'s `inset`. */
|
|
28
|
+
export declare const SIDEBAR_HEADER_INSET: {
|
|
29
|
+
readonly base: "mb-6 px-2";
|
|
30
|
+
readonly sm: "mb-3 px-0";
|
|
31
|
+
};
|
|
32
|
+
export type SidebarInset = keyof typeof SIDEBAR_ROOT_INSET;
|
|
17
33
|
/** A nav row at rest and on hover. Active/inactive inks split below. */
|
|
18
|
-
export declare const SIDEBAR_ITEM = "outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
|
|
34
|
+
export declare const SIDEBAR_ITEM = "outline-pho-brand flex min-h-8 w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
|
|
19
35
|
/**
|
|
20
36
|
* Selected row — lifted onto the primary surface with the site hairline.
|
|
21
37
|
* The icon carries the same ink as the label so the pair reads as one unit.
|
|
22
38
|
*/
|
|
23
39
|
export declare const SIDEBAR_ITEM_ACTIVE = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-pho-primary";
|
|
24
40
|
/**
|
|
25
|
-
* Resting row — secondary ink (75%)
|
|
26
|
-
*
|
|
41
|
+
* Resting row — secondary ink (75%). Hover lifts onto the primary surface
|
|
42
|
+
* (same plane as the active chip, without the ring) so the wash stays quiet
|
|
43
|
+
* on `pho-page`. Icon tracks the label's ink at every state.
|
|
27
44
|
*/
|
|
28
45
|
export declare const SIDEBAR_ITEM_INACTIVE: string;
|
|
29
46
|
interface RootProps extends ComponentProps<"aside"> {
|
|
47
|
+
/** Outer padding + header spacing. Defaults to `base`. */
|
|
48
|
+
inset?: SidebarInset;
|
|
30
49
|
ref?: Ref<HTMLElement>;
|
|
31
50
|
}
|
|
32
51
|
/** The rail — a sticky, full-height `<aside>` with the hairline right edge. */
|
|
33
|
-
declare function Root({ className, ...props }: RootProps): import("react").JSX.Element;
|
|
52
|
+
declare function Root({ className, inset, ...props }: RootProps): import("react").JSX.Element;
|
|
34
53
|
/** Brand slot at the top of the rail — logo, wordmark, workspace switcher. */
|
|
35
54
|
declare function Header({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
36
55
|
/** The scrollable middle — a stack of `Sidebar.Group`s. */
|
|
@@ -2,3 +2,4 @@ export { cn } from './cn';
|
|
|
2
2
|
export { isReactComponent } from './is-react-component';
|
|
3
3
|
export { resolveVariant, type VariantDef, type VariantMap, } from './resolve-variant';
|
|
4
4
|
export { LinkProvider, useLinkComponent, type LinkComponent, } from './link-provider';
|
|
5
|
+
export { usePressPulse, CONTROL_PRESS_SCALE, CONTROL_PRESS_SPRING, } from './use-press-pulse';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Transition } from 'motion/react';
|
|
2
|
+
/**
|
|
3
|
+
* Press pulse for selection controls (Select trigger, Checkbox…) — the same
|
|
4
|
+
* spring family as Button's effect press, with one deliberate difference:
|
|
5
|
+
* Button compresses only while held (`whileTap`); these controls play ONE
|
|
6
|
+
* FULL pulse (press in → spring back) from the moment of pointer-down, even
|
|
7
|
+
* if the pointer lifts early — a discrete "it took" tick, kept shallow and
|
|
8
|
+
* quick so it registers without drawing attention.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CONTROL_PRESS_SPRING: {
|
|
11
|
+
readonly type: "spring";
|
|
12
|
+
readonly stiffness: 700;
|
|
13
|
+
readonly damping: 32;
|
|
14
|
+
readonly mass: 0.06;
|
|
15
|
+
};
|
|
16
|
+
/** Default pulse depth — shallow; each control can tune its own. */
|
|
17
|
+
export declare const CONTROL_PRESS_SCALE = 0.96;
|
|
18
|
+
/**
|
|
19
|
+
* One-shot scale pulse driven by Motion controls. Spread `motionProps` onto a
|
|
20
|
+
* `motion.*` element (via Base UI `render`): pointer-down (primary button)
|
|
21
|
+
* plays press → rest in full; re-presses during the pulse are coalesced.
|
|
22
|
+
*/
|
|
23
|
+
export declare function usePressPulse(opts?: {
|
|
24
|
+
scale?: number;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
/** Spring override for both legs of the pulse. @default CONTROL_PRESS_SPRING */
|
|
27
|
+
transition?: Transition;
|
|
28
|
+
}): {
|
|
29
|
+
pulse: () => void;
|
|
30
|
+
motionProps: {
|
|
31
|
+
initial: false;
|
|
32
|
+
animate: import('motion/react').LegacyAnimationControls;
|
|
33
|
+
onPointerDown: (event: {
|
|
34
|
+
button: number;
|
|
35
|
+
}) => void;
|
|
36
|
+
};
|
|
37
|
+
};
|
package/dist/utils.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as
|
|
2
|
+
import { t as s } from "./chunks/cn-DTiB6ek_.js";
|
|
3
|
+
import { n as t, r as e, t as a } from "./chunks/link-provider-9TFNyDqO.js";
|
|
4
|
+
import { t as i } from "./chunks/resolve-variant-CcD8iG5l.js";
|
|
5
|
+
import { n as p, r as P, t as R } from "./chunks/use-press-pulse-CMwPTOR2.js";
|
|
5
6
|
export {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
e as
|
|
7
|
+
R as CONTROL_PRESS_SCALE,
|
|
8
|
+
p as CONTROL_PRESS_SPRING,
|
|
9
|
+
a as LinkProvider,
|
|
10
|
+
s as cn,
|
|
11
|
+
e as isReactComponent,
|
|
12
|
+
i as resolveVariant,
|
|
13
|
+
t as useLinkComponent,
|
|
14
|
+
P as usePressPulse
|
|
11
15
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photon-ai/pho-ui",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Pho Design System — Photon's React component library, built on Base UI",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
"types": "./dist/src/components/input/index.d.ts",
|
|
38
38
|
"import": "./dist/components/input.js"
|
|
39
39
|
},
|
|
40
|
+
"./components/input-group": {
|
|
41
|
+
"types": "./dist/src/components/input-group/index.d.ts",
|
|
42
|
+
"import": "./dist/components/input-group.js"
|
|
43
|
+
},
|
|
44
|
+
"./components/card": {
|
|
45
|
+
"types": "./dist/src/components/card/index.d.ts",
|
|
46
|
+
"import": "./dist/components/card.js"
|
|
47
|
+
},
|
|
40
48
|
"./components/dialog": {
|
|
41
49
|
"types": "./dist/src/components/dialog/index.d.ts",
|
|
42
50
|
"import": "./dist/components/dialog.js"
|
|
@@ -185,6 +193,10 @@
|
|
|
185
193
|
"types": "./dist/src/components/kbd/index.d.ts",
|
|
186
194
|
"import": "./dist/components/kbd.js"
|
|
187
195
|
},
|
|
196
|
+
"./components/skeleton": {
|
|
197
|
+
"types": "./dist/src/components/skeleton/index.d.ts",
|
|
198
|
+
"import": "./dist/components/skeleton.js"
|
|
199
|
+
},
|
|
188
200
|
"./components/spinner": {
|
|
189
201
|
"types": "./dist/src/components/spinner/index.d.ts",
|
|
190
202
|
"import": "./dist/components/spinner.js"
|
|
@@ -197,6 +209,10 @@
|
|
|
197
209
|
"types": "./dist/src/sections/sidebar/index.d.ts",
|
|
198
210
|
"import": "./dist/sections/sidebar.js"
|
|
199
211
|
},
|
|
212
|
+
"./sections/settings": {
|
|
213
|
+
"types": "./dist/src/sections/settings/index.d.ts",
|
|
214
|
+
"import": "./dist/sections/settings.js"
|
|
215
|
+
},
|
|
200
216
|
"./icons": {
|
|
201
217
|
"types": "./dist/src/icons/index.d.ts",
|
|
202
218
|
"import": "./dist/icons.js"
|
package/src/styles/theme.css
CHANGED
|
@@ -280,9 +280,11 @@
|
|
|
280
280
|
oklch(0 0 0 / 0.08)
|
|
281
281
|
);
|
|
282
282
|
--border-color-pho-error: oklch(0.642 0.245 21.5);
|
|
283
|
+
/* Resting invalid edge — readable on the field surface without matching
|
|
284
|
+
the solid focus ring. ~45% light / ~50% dark. */
|
|
283
285
|
--border-color-pho-error-subtle: light-dark(
|
|
284
|
-
oklch(0.642 0.245 21.5 / 0.
|
|
285
|
-
oklch(0.642 0.245 21.5 / 0.
|
|
286
|
+
oklch(0.642 0.245 21.5 / 0.45),
|
|
287
|
+
oklch(0.642 0.245 21.5 / 0.5)
|
|
286
288
|
);
|
|
287
289
|
/* Hairline interactive edge shared by hover/selected surfaces. */
|
|
288
290
|
--border-color-pho-interactive: light-dark(
|
|
@@ -298,9 +300,10 @@
|
|
|
298
300
|
--ring-color-pho-secondary: light-dark(oklch(0.949 0 0), oklch(0.244 0 0));
|
|
299
301
|
--ring-color-pho-brand: light-dark(oklch(0 0 0 / 0.4), oklch(1 0 0 / 0.4));
|
|
300
302
|
--ring-color-pho-error: oklch(0.642 0.245 21.5);
|
|
303
|
+
/* Mirrors border-pho-error-subtle — resting invalid hairline. */
|
|
301
304
|
--ring-color-pho-error-subtle: light-dark(
|
|
302
|
-
oklch(0.642 0.245 21.5 / 0.
|
|
303
|
-
oklch(0.642 0.245 21.5 / 0.
|
|
305
|
+
oklch(0.642 0.245 21.5 / 0.45),
|
|
306
|
+
oklch(0.642 0.245 21.5 / 0.5)
|
|
304
307
|
);
|
|
305
308
|
--ring-color-pho-interactive: light-dark(
|
|
306
309
|
oklch(0 0 0 / 0.1),
|
|
@@ -321,4 +324,15 @@
|
|
|
321
324
|
transform: rotate(360deg);
|
|
322
325
|
}
|
|
323
326
|
}
|
|
327
|
+
|
|
328
|
+
/* ==================== Animation: Skeleton ==================== */
|
|
329
|
+
/* Soft opacity pulse for placeholder bones. Global reduced-motion guard
|
|
330
|
+
in pho.css collapses this to a static wash. */
|
|
331
|
+
--animate-pho-skeleton: pho-skeleton-pulse 1.6s ease-in-out infinite;
|
|
332
|
+
|
|
333
|
+
@keyframes pho-skeleton-pulse {
|
|
334
|
+
50% {
|
|
335
|
+
opacity: 0.45;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
324
338
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"close-button-8f-oRJXt.js","names":[],"sources":["../../src/components/button/button.tsx","../../src/components/button/close-button.tsx"],"sourcesContent":["import {\n isValidElement,\n useMemo,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentType,\n type FC,\n type MouseEvent as ReactMouseEvent,\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\";\nimport { Spinner } from \"../spinner/spinner\";\n\n/**\n * Design notes:\n * - TWO visual axes: `variant` is the surface (filled / outlined / ghost /\n * effect) and `color` is the semantic ink (default / destructive). Dialog\n * confirms use the default `effect`; danger-zone triggers are `ghost` +\n * `destructive`. `effect` is the marketing pill: same-hue 1.5px outer rim\n * plus inset sheen that springs on hover.\n * - `iconReveal` is a separate boolean: on hover, prefix/suffix icons spring\n * in from collapsed — works with any variant, independent of `effect`.\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. Effect’s rim/sheen are\n * box-shadow layers (not layout `border`).\n * - Navigation belongs to `ButtonLink`; an inline text link is an anchor.\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-base 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-base 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-base\",\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 * At `lg`, square buttons add squircle smoothing on top of `rounded-base`\n * (12px) — the same treatment as Input `lg`, so 44px buttons and fields share\n * a corner language. 12/44 ≈ 0.272 — LiftKit's half-step decimal (√φ − 1).\n */\nconst SQUARE_LG_RADIUS = \"squircle\";\n\n/**\n * Optical pad for `effect`: the 1.5px outer rim is a box-shadow (no layout\n * cost), so without this the painted height reads ~3px taller than filled /\n * outlined / ghost. Subtract 1.5px from each side so fill+rim matches siblings.\n */\nconst EFFECT_OPTICAL_PAD: Record<ButtonSize, string> = {\n sm: \"py-[4.5px] data-svg-only:p-[6.5px]\",\n md: \"py-[6.5px] data-svg-only:p-[8.5px]\",\n lg: \"py-[8.5px] data-svg-only:p-[10.5px]\",\n};\n\n/** Surface axis — how the control is drawn. `effect` is the default. */\nexport const BUTTON_VARIANTS = {\n effect: {\n classes: \"\",\n description:\n \"Default — marketing pill with same-hue outer rim and inset sheen\",\n },\n filled: {\n classes: \"\",\n description: \"Solid fill without sheen\",\n },\n outlined: {\n classes: \"\",\n description: \"Hairline ring — supporting action\",\n },\n ghost: {\n classes: \"\",\n description: \"Borderless — quiet / inline action\",\n },\n} satisfies VariantMap;\n\n/** Color axis — semantic ink. `default` is the default. */\nexport const BUTTON_COLORS = {\n default: {\n classes: \"\",\n description: \"Neutral / brand contrast\",\n },\n destructive: {\n classes: \"\",\n description: \"Destructive intent (delete, discard)\",\n },\n} satisfies VariantMap;\n\n/**\n * Surface × color looks. Kept as a matrix so each cell can own its tokens\n * (filled destructive is solid red; ghost destructive is red text only).\n */\nconst BUTTON_LOOKS: Record<\n keyof typeof BUTTON_VARIANTS,\n Record<keyof typeof BUTTON_COLORS, string>\n> = {\n // Icons inherit the label ink (currentColor) — no separate opacity/tint.\n // The rim replicates photon.codes' Framer CTA border: 1.5px in the fill hue\n // at 62% alpha, reading as a soft same-hue edge (the site has NO outer\n // glow — verified against live computed styles). Sheen geometry is CSS\n // vars that Motion springs on hover (see Button). All edges are shadows —\n // never a layout `border` — so height stays paired with other variants.\n effect: {\n default: cn(\n \"squircle overflow-hidden bg-pho-brand-solid text-pho-on-brand transition-none\",\n \"hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-brand-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),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 destructive: cn(\n \"squircle overflow-hidden bg-pho-error-solid text-white outline-pho-error transition-none\",\n \"hover:bg-pho-error-solid data-loading:bg-pho-error-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-error-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.45),rgba(0,0,0,0.18))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_oklch(1_0_0/0.2),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n },\n filled: {\n default:\n \"bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover\",\n destructive:\n \"bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover\",\n },\n outlined: {\n default:\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\",\n destructive:\n \"bg-pho-primary text-pho-error outline-pho-error ring-1 ring-pho-error ring-inset hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n ghost: {\n default:\n \"text-pho-secondary hover:bg-pho-ghost-hover hover:text-pho-secondary-hover data-loading:bg-pho-ghost-hover\",\n destructive:\n \"text-pho-error outline-pho-error hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n};\n\nexport type ButtonSize = keyof typeof BUTTON_SIZES;\nexport type ButtonShape = keyof typeof BUTTON_SHAPES;\nexport type ButtonVariant = keyof typeof BUTTON_VARIANTS;\nexport type ButtonColor = keyof typeof BUTTON_COLORS;\n\n/** Compose the full class string for a button — exported for reuse/testing. */\nexport function buttonVariants(opts?: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n className?: string;\n}): string {\n const {\n variant = \"effect\",\n color = \"default\",\n size = \"md\",\n shape: shapeOpt = \"rounded\",\n className,\n } = opts ?? {};\n // Effect pills are always fully rounded — shape is ignored.\n const shape = variant === \"effect\" ? \"rounded\" : shapeOpt;\n const look =\n BUTTON_LOOKS[variant as keyof typeof BUTTON_LOOKS]?.[\n color as keyof (typeof BUTTON_LOOKS)[\"effect\"]\n ] ?? \"\";\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 // Outer rim is a 1.5px box-shadow (no layout cost). Shrink padding by the\n // same amount so fill+rim paints at the same visual size as filled /\n // outlined / ghost siblings of the same `size`.\n variant === \"effect\" && EFFECT_OPTICAL_PAD[size],\n look,\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 /** Surface variant. @default \"effect\" */\n variant?: ButtonVariant;\n /** Semantic color. @default \"default\" */\n color?: ButtonColor;\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 /**\n * Reveal prefix/suffix icons on hover — collapsed and invisible at rest,\n * spring in on hover. Independent of `variant` (including `effect`).\n * Ignored when `svgOnly`. @default false\n */\n iconReveal?: boolean;\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\n/**\n * DOM event handlers whose React signatures clash with Motion's gesture\n * props of the same name (Motion re-types these on `motion.*` elements).\n * Excluded from the public API — Button renders through Motion when the\n * sheen or icon reveal is active, so these handlers would be intercepted.\n */\ntype MotionHandlerConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface ButtonProps\n extends\n ButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface ButtonLinkProps\n extends\n ButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\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\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 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 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/* ======================== Motion: sheen + icon reveal ======================== */\n\n/** Framer source transition: `spring-physics 200 30 0.1`. */\nconst BUTTON_SPRING = {\n type: \"spring\",\n stiffness: 200,\n damping: 30,\n mass: 0.1,\n} as const;\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/** Per-size sheen hover targets. */\nconst EFFECT_SHEEN_HOVER = {\n sm: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n md: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n lg: {\n \"--pho-sheen-y\": \"-6px\",\n \"--pho-sheen-blur\": \"14px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/**\n * Per-size sheen press targets — the inverse gesture of hover: the light\n * drops back below the rest line and tightens while the pill compresses\n * slightly, so hover → press reads as \"lift, then push in\".\n */\nconst EFFECT_SHEEN_PRESS = {\n sm: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n md: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n lg: {\n \"--pho-sheen-y\": \"3px\",\n \"--pho-sheen-blur\": \"7px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/** Pill compression while pressed — subtle, transform-only. */\nconst EFFECT_PRESS_SCALE = 0.97;\n\n/**\n * Icon reveal sizes — collapsed at rest, full on hover. `lg` matches the\n * Framer 20×21 source; `sm`/`md` scale with the 14px label.\n */\nconst ICON_REVEAL_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\n/**\n * When icons are hidden at rest, undo Button's icon-side padding tuck so the\n * pill doesn't look lopsided. Same `:has()` keys as the tuck (later wins).\n */\nconst ICON_REVEAL_PAD_RESTORE: Record<ButtonSize, 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\nfunction RevealSlot({\n position,\n size,\n children,\n}: {\n position: \"prefix\" | \"suffix\";\n size: ButtonSize;\n children: ReactNode;\n}) {\n return (\n <motion.span\n data-icon={position}\n variants={ICON_REVEAL_VARIANTS[size]}\n transition={BUTTON_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\nfunction wrapRevealIcon(\n icon: Slottable,\n position: \"prefix\" | \"suffix\",\n size: ButtonSize,\n): ReactNode | undefined {\n if (icon == null || icon === false) return undefined;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <RevealSlot position={position} size={size}>\n <Icon />\n </RevealSlot>\n );\n }\n if (isValidElement(icon)) {\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n }\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n}\n\nfunction buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal,\n size,\n}: Pick<\n ButtonBaseProps,\n | \"svgOnly\"\n | \"loading\"\n | \"prefix\"\n | \"suffix\"\n | \"children\"\n | \"iconReveal\"\n | \"size\"\n> & { size: ButtonSize }) {\n if (svgOnly) {\n return loading ? <Spinner /> : children;\n }\n const reveal = Boolean(iconReveal);\n return (\n <>\n {loading ? (\n <Slot position=\"loading\">\n <Spinner />\n </Slot>\n ) : reveal ? (\n wrapRevealIcon(prefix, \"prefix\", size)\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 {reveal\n ? wrapRevealIcon(suffix, \"suffix\", size)\n : renderSlot(suffix, \"suffix\")}\n </>\n );\n}\n\n/** Root Motion props for sheen and/or icon-reveal gap. */\nfunction buttonRootMotionProps(\n size: ButtonSize,\n inert: boolean | undefined,\n opts: { sheen: boolean; iconReveal: boolean },\n) {\n const rest: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_REST, scale: 1 }\n : {};\n const hover: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_HOVER[size] }\n : {};\n // Press composes over hover (whileTap > whileHover): the sheen inverts and\n // the pill compresses; the icon-reveal gap keeps its hover value.\n const press: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_PRESS[size], scale: EFFECT_PRESS_SCALE }\n : {};\n if (opts.iconReveal) {\n // Collapse gap at rest so the hidden icon leaves no hole; lg eases open.\n rest.gap = 0;\n hover.gap = size === \"lg\" ? 4 : 0;\n }\n return {\n initial: false as const,\n animate: \"rest\",\n whileHover: inert ? undefined : \"hover\",\n whileTap: inert || !opts.sheen ? undefined : \"press\",\n variants: { rest, hover, press },\n transition: BUTTON_SPRING,\n };\n}\n\nfunction buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className,\n}: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n loading?: boolean;\n svgOnly?: boolean;\n className?: string;\n}) {\n return cn(\n buttonVariants({ variant, color, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\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 *\n * Default surface is `effect` (same-hue 1.5px outer rim + inset sheen). Use\n * `filled` for a solid fill without those layers. `iconReveal` springs\n * prefix/suffix icons in on hover — independent of variant.\n */\nexport function Button({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: ButtonProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n\n if (sheen || reveal) {\n return (\n <motion.button\n ref={ref}\n type=\"button\"\n {...buttonRootMotionProps(size, props.disabled || loading, {\n sheen,\n iconReveal: reveal,\n })}\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n className={cls}\n >\n {inner}\n </motion.button>\n );\n }\n\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={cls}\n >\n {inner}\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 = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\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 const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n // Hooks must run unconditionally — only used when Motion is needed.\n const MotionLink = useMemo(\n // LinkProvider requires `href`; motion.create needs an open prop record —\n // bridge the two through `unknown` (props are supplied at the call site).\n () =>\n motion.create(Link as unknown as ComponentType<Record<string, unknown>>),\n [Link],\n );\n\n const inert = Boolean(disabled || loading);\n const shared = {\n ...props,\n ...(inert ? {} : { href }),\n \"aria-disabled\": inert || undefined,\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading ? (\"true\" as const) : undefined,\n \"data-svg-only\": svgOnly ? (\"true\" as const) : undefined,\n className: cls,\n tabIndex: inert ? -1 : props.tabIndex,\n onClick: inert\n ? (event: ReactMouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n }\n : props.onClick,\n };\n\n if (inert) {\n return <span {...shared}>{inner}</span>;\n }\n\n if (sheen || reveal) {\n return (\n <MotionLink\n {...buttonRootMotionProps(size, disabled || loading, {\n sheen,\n iconReveal: reveal,\n })}\n {...shared}\n >\n {inner}\n </MotionLink>\n );\n }\n\n return <Link {...shared}>{inner}</Link>;\n}\n\nButtonLink.displayName = \"ButtonLink\";\n\n/* ======================= EffectButton (compat alias) ======================= */\n\nexport const EFFECT_BUTTON_SIZES = BUTTON_SIZES;\nexport type EffectButtonSize = ButtonSize;\n\n/** Compose classes for an effect pill — `Button` with `variant=\"effect\"`. */\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: \"effect\",\n size,\n className: cn(fullWidth && \"w-full\", className),\n });\n}\n\n/**\n * Legacy EffectButton also reserved `style` (sheen variables lived there) and\n * never exposed the HTML `color` attribute — Button's semantic `color` prop\n * now occupies that name.\n */\ntype MotionConflicts = MotionHandlerConflicts | \"style\" | \"color\";\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 * @deprecated Use `iconReveal` on `Button` instead. Mapped to `iconReveal`\n * when set.\n */\n animated?: boolean;\n /** @see ButtonBaseProps.iconReveal */\n iconReveal?: boolean;\n prefix?: Slottable;\n suffix?: Slottable;\n svgOnly?: boolean;\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 href: string;\n disabled?: boolean;\n}\n\n/**\n * @deprecated Prefer `<Button variant=\"effect\">`. Thin alias that forwards to\n * Button with the sheen surface (no icon reveal).\n */\nexport function EffectButton({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonProps) {\n return (\n <Button\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButton.displayName = \"EffectButton\";\n\n/**\n * @deprecated Prefer `<ButtonLink variant=\"effect\">`.\n */\nexport function EffectButtonLink({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonLinkProps) {\n return (\n <ButtonLink\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButtonLink.displayName = \"EffectButtonLink\";\n","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// `rounded-base` (12px) at every size; base classes already apply squircle.\nconst CLOSE_SIZES = {\n sm: { classes: \"size-9 rounded-base *:size-5\" },\n md: { classes: \"size-10 rounded-base *:size-5\" },\n lg: { classes: \"size-11 rounded-base *: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-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-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":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"input-BSJGBUbf.js","names":["__iconNode","__iconNode"],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconEyeOff.mjs","../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconEye.mjs","../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertCircleFilled.mjs","../../src/components/input/input.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M10.585 10.587a2 2 0 0 0 2.829 2.828\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87\", \"key\": \"svg-1\" }], [\"path\", { \"d\": \"M3 3l18 18\", \"key\": \"svg-2\" }]];\nconst IconEyeOff = createReactComponent(\"outline\", \"eye-off\", \"EyeOff\", __iconNode);\n\nexport { __iconNode, IconEyeOff as default };\n//# sourceMappingURL=IconEyeOff.mjs.map\n","/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6\", \"key\": \"svg-1\" }]];\nconst IconEye = createReactComponent(\"outline\", \"eye\", \"Eye\", __iconNode);\n\nexport { __iconNode, IconEye as default };\n//# sourceMappingURL=IconEye.mjs.map\n","/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M12 2c5.523 0 10 4.477 10 10a10 10 0 0 1 -19.995 .324l-.005 -.324l.004 -.28c.148 -5.393 4.566 -9.72 9.996 -9.72zm.01 13l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -8a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z\", \"key\": \"svg-0\" }]];\nconst IconAlertCircleFilled = createReactComponent(\"filled\", \"alert-circle-filled\", \"AlertCircleFilled\", __iconNode);\n\nexport { __iconNode, IconAlertCircleFilled as default };\n//# sourceMappingURL=IconAlertCircleFilled.mjs.map\n","import {\n useState,\n type ComponentType,\n type InputHTMLAttributes,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport {\n IconAlertCircleFilled,\n IconEye,\n IconEyeOff,\n} from \"@tabler/icons-react\";\nimport { Field } from \"@base-ui/react/field\";\nimport { cn } from \"../../utils/cn\";\n\nexport type InputSize = \"sm\" | \"md\" | \"lg\";\n\n// Control padding per size. The wrapper carries a 2px inset (`p-0.5`) so the\n// control's border box — and the browser's autofill fill it paints — sits\n// inside the ring instead of over it; these paddings are the documented\n// effective padding (sm/md 12/8px, lg 14/10px) minus that 2px inset, so the\n// text position and total field height are unchanged.\nconst SIZE_PAD: Record<InputSize, string> = {\n // 16px on small viewports — iOS zooms into any focused field under 16px.\n sm: \"px-2.5 py-1.5 text-md md:text-base\",\n md: \"px-2.5 py-1.5 text-md\",\n lg: \"px-3 py-2 text-md\",\n};\n// Effective icon clearance minus the wrapper's 2px inset.\nconst LEADING_PAD: Record<InputSize, string> = {\n sm: \"pl-8.5\",\n md: \"pl-9.5\",\n lg: \"pl-10\",\n};\n// Icons are absolutely positioned, so they're placed from the wrapper's\n// padding edge — which, with no border, is the visible field edge; the\n// wrapper's `p-0.5` does not offset them.\nconst LEADING_ICON_POS: Record<InputSize, string> = {\n sm: \"left-3 size-4\",\n md: \"left-3 size-5\",\n lg: \"left-3.5 size-5\",\n};\n// Wrapper corner radius — `rounded-base` (12px) at every size; `lg` also gets\n// `squircle` superellipse smoothing (the ring is a box-shadow on this wrapper,\n// so it follows the border-radius automatically).\nconst SIZE_RADIUS: Record<InputSize, string> = {\n sm: \"rounded-base\",\n md: \"rounded-base\",\n lg: \"squircle rounded-base\",\n};\n// Concentric inner-curve cover, `lg` only. Nested corner radii must follow\n// innerRadius = outerRadius − inset, so the hairline's inner edge at a 12px\n// outer radius is 11px at rest (ring-1) and 10px when focused (ring-2).\n// Browsers derive that automatically for plain round corners, but with\n// `corner-shape: superellipse(...)` shipping engines still scale (rather than\n// offset) the inner shadow curve, which renders the ring visibly thicker at\n// the four corners (see w3c/csswg-drafts#12961). This ::before repaints the\n// field surface over everything inside the correct concentric curve — a round\n// radius is the right parallel-offset approximation of the mild superellipse —\n// so the ring reads as a uniform hairline in every engine. `isolate` +\n// `-z-10` slot the cover above the wrapper's background/ring but below the\n// control's text, icons, and autofill fill. `sm`/`md` have no corner-shape,\n// so the native (already concentric) rendering is kept there.\n// The ::before is absolutely positioned, so its inset is measured from the\n// wrapper's border box (border 0): 1px in at rest, 2px in when focused.\nconst SIZE_RING_COVER: Record<InputSize, string> = {\n sm: \"\",\n md: \"\",\n lg: cn(\n \"isolate before:pointer-events-none before:absolute before:-z-10 before:content-['']\",\n \"before:bg-pho-primary before:inset-px before:rounded-[calc(var(--radius-base)-1px)]\",\n \"has-[input:focus-visible]:before:inset-0.5 has-[input:focus-visible]:before:rounded-[calc(var(--radius-base)-2px)]\",\n \"before:transition-[inset,border-radius] before:duration-100 before:ease-linear\",\n ),\n};\n// The control's border box sits 2px inside the wrapper (its `p-0.5` inset),\n// clear of both ring widths, so the browser's autofill fill paints inside the\n// hairline instead of over it. Rounding follows inner = outer − gap, derived\n// from the token with calc. Round (not squircle) — the parallel offset of\n// the wrapper's mild superellipse is effectively circular.\nconst SIZE_AUTOFILL_RADIUS: Record<InputSize, string> = {\n sm: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n md: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n lg: \"autofill:rounded-[calc(var(--radius-base)-2px)]\",\n};\n\nexport interface InputProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"size\"\n> {\n /** Field label (associated for accessibility). */\n label?: ReactNode;\n /** Helper text below the field; rendered in the error color when `invalid`. */\n hint?: ReactNode;\n /** Size variant. @default \"md\" */\n size?: InputSize;\n /** Render the invalid state (ring + icon + error-colored hint). */\n invalid?: boolean;\n /** Leading icon component (e.g. a Tabler icon). */\n icon?: ComponentType<{ className?: string }>;\n /** Node rendered at the right edge of the label row (e.g. \"Forgot?\"). */\n labelTrailing?: ReactNode;\n /** Class applied to the input wrapper. */\n wrapperClassName?: string;\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * Text input built on Base UI `Field` (label/description a11y wiring) with\n * Pho's bordered surface, leading icon, invalid state, and a built-in password\n * reveal toggle.\n */\nexport function Input({\n label,\n hint,\n size = \"md\",\n invalid,\n icon: Icon,\n labelTrailing,\n disabled,\n required,\n type = \"text\",\n className,\n wrapperClassName,\n id,\n ref,\n ...props\n}: InputProps) {\n const [reveal, setReveal] = useState(false);\n const isPassword = type === \"password\";\n const resolvedType = isPassword && reveal ? \"text\" : type;\n const hasLeadingIcon = Boolean(Icon);\n const hasTrailingIcon = isPassword || invalid;\n\n return (\n <Field.Root\n disabled={disabled}\n className=\"group/field flex w-full flex-col gap-1.5\"\n >\n {label && (\n <div className=\"flex items-center justify-between gap-2\">\n <Field.Label className=\"text-pho-secondary flex cursor-default items-center gap-0.5 text-base font-medium\">\n {label}\n {required && <span className=\"text-pho-brand\">*</span>}\n </Field.Label>\n {labelTrailing}\n </div>\n )}\n\n <div\n data-invalid={invalid || undefined}\n className={cn(\n // Flat field surface — hairline ring only; elevation shadows are\n // reserved for floating layers (menus/popovers/dialogs). `p-0.5`\n // insets the control 2px (the focused ring width) so its background\n // and the browser's autofill fill nest inside the ring; the size\n // maps compensate so effective padding/height are unchanged.\n \"bg-pho-primary ring-pho-primary relative flex w-full items-center p-0.5 ring-1 transition-shadow duration-100 ease-linear ring-inset\",\n SIZE_RADIUS[size],\n SIZE_RING_COVER[size],\n \"has-[input:focus-visible]:ring-pho-brand has-[input:focus-visible]:ring-2\",\n \"data-[invalid]:ring-pho-error-subtle data-[invalid]:has-[input:focus-visible]:ring-pho-error\",\n \"group-data-[disabled]/field:cursor-not-allowed group-data-[disabled]/field:opacity-50\",\n wrapperClassName,\n )}\n >\n {Icon && (\n <Icon\n className={cn(\n \"text-pho-secondary pointer-events-none absolute\",\n LEADING_ICON_POS[size],\n )}\n />\n )}\n\n <Field.Control\n ref={ref}\n id={id}\n type={resolvedType}\n required={required}\n {...props}\n className={cn(\n \"text-pho-primary placeholder:text-pho-placeholder m-0 w-full bg-transparent outline-none disabled:cursor-not-allowed\",\n SIZE_PAD[size],\n SIZE_AUTOFILL_RADIUS[size],\n hasLeadingIcon && LEADING_PAD[size],\n // 34px + the wrapper's 2px inset = the previous 36px clearance.\n hasTrailingIcon && \"pr-8.5\",\n className,\n )}\n />\n\n {invalid && !isPassword && (\n <IconAlertCircleFilled className=\"text-pho-error pointer-events-none absolute right-3 size-4\" />\n )}\n\n {isPassword && (\n <button\n type=\"button\"\n // Reachable by click but skipped in tab order so Tab moves field→field.\n tabIndex={-1}\n aria-label=\"Toggle password visibility\"\n onClick={() => setReveal((v) => !v)}\n className=\"text-pho-secondary hover:text-pho-secondary-hover absolute right-3 flex cursor-pointer items-center transition duration-100 ease-linear focus:outline-none\"\n >\n {reveal ? (\n <IconEyeOff className=\"size-4\" />\n ) : (\n <IconEye className=\"size-4\" />\n )}\n </button>\n )}\n </div>\n\n {hint && (\n <Field.Description\n className={cn(\n \"text-pho-description text-base\",\n invalid && \"text-pho-error\",\n )}\n >\n {hint}\n </Field.Description>\n )}\n </Field.Root>\n );\n}\n\nInput.displayName = \"Input\";\n"],"x_google_ignoreList":[0,1,2],"mappings":"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"}
|