@financedistrict/apps-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +119 -0
- package/dist/Radio-BYWz1n7p.d.cts +32 -0
- package/dist/Radio-BYWz1n7p.d.ts +32 -0
- package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
- package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
- package/dist/accordion.cjs +322 -0
- package/dist/accordion.d.cts +156 -0
- package/dist/accordion.d.ts +156 -0
- package/dist/accordion.js +2 -0
- package/dist/apps-ui.umd.js +1 -0
- package/dist/button.cjs +366 -0
- package/dist/button.d.cts +52 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +3 -0
- package/dist/checkbox.cjs +74 -0
- package/dist/checkbox.d.cts +63 -0
- package/dist/checkbox.d.ts +63 -0
- package/dist/checkbox.js +2 -0
- package/dist/chunk-3RU7JMFE.js +27 -0
- package/dist/chunk-5TWRK4NW.js +783 -0
- package/dist/chunk-7EOSDFS3.js +9 -0
- package/dist/chunk-AIQ4UOGK.js +142 -0
- package/dist/chunk-B3MMGTPV.js +200 -0
- package/dist/chunk-DAHQYLUI.js +146 -0
- package/dist/chunk-DGBXADSS.js +1 -0
- package/dist/chunk-E6KPNTWW.js +295 -0
- package/dist/chunk-EOAB6G2D.js +149 -0
- package/dist/chunk-FAOVKDVM.js +32 -0
- package/dist/chunk-FV76Z4Z2.js +78 -0
- package/dist/chunk-GLTLBUZ7.js +285 -0
- package/dist/chunk-GTZFV43T.js +154 -0
- package/dist/chunk-HRGZNL4A.js +88 -0
- package/dist/chunk-IEVN3J2F.js +98 -0
- package/dist/chunk-IRS3T6LF.js +352 -0
- package/dist/chunk-IYYHCQXG.js +146 -0
- package/dist/chunk-JIXW3455.js +89 -0
- package/dist/chunk-N4VAP2GB.js +77 -0
- package/dist/chunk-SJ2ZRYVV.js +167 -0
- package/dist/chunk-SNU4YXOR.js +66 -0
- package/dist/chunk-TT6JQATV.js +99 -0
- package/dist/chunk-UPQ3ZPCO.js +66 -0
- package/dist/chunk-UVUSVFOH.js +264 -0
- package/dist/chunk-WJFPGGJB.js +255 -0
- package/dist/chunk-WXEBQA7A.js +371 -0
- package/dist/chunk-Y2O4YYZQ.js +184 -0
- package/dist/cut-corner.cjs +111 -0
- package/dist/cut-corner.d.cts +49 -0
- package/dist/cut-corner.d.ts +49 -0
- package/dist/cut-corner.js +1 -0
- package/dist/dialog.cjs +441 -0
- package/dist/dialog.d.cts +240 -0
- package/dist/dialog.d.ts +240 -0
- package/dist/dialog.js +3 -0
- package/dist/feature-card.cjs +182 -0
- package/dist/feature-card.d.cts +54 -0
- package/dist/feature-card.d.ts +54 -0
- package/dist/feature-card.js +2 -0
- package/dist/filter.cjs +873 -0
- package/dist/filter.d.cts +129 -0
- package/dist/filter.d.ts +129 -0
- package/dist/filter.js +6 -0
- package/dist/index.cjs +4583 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +27 -0
- package/dist/inline-select.cjs +856 -0
- package/dist/inline-select.d.cts +92 -0
- package/dist/inline-select.d.ts +92 -0
- package/dist/inline-select.js +6 -0
- package/dist/input.cjs +263 -0
- package/dist/input.d.cts +157 -0
- package/dist/input.d.ts +157 -0
- package/dist/input.js +4 -0
- package/dist/panel.cjs +185 -0
- package/dist/panel.d.cts +103 -0
- package/dist/panel.d.ts +103 -0
- package/dist/panel.js +2 -0
- package/dist/property-grid.cjs +110 -0
- package/dist/property-grid.d.cts +56 -0
- package/dist/property-grid.d.ts +56 -0
- package/dist/property-grid.js +2 -0
- package/dist/radio.cjs +74 -0
- package/dist/radio.d.cts +39 -0
- package/dist/radio.d.ts +39 -0
- package/dist/radio.js +2 -0
- package/dist/scrollbar.cjs +34 -0
- package/dist/scrollbar.d.cts +35 -0
- package/dist/scrollbar.d.ts +35 -0
- package/dist/scrollbar.js +1 -0
- package/dist/search-bar.cjs +408 -0
- package/dist/search-bar.d.cts +42 -0
- package/dist/search-bar.d.ts +42 -0
- package/dist/search-bar.js +4 -0
- package/dist/select.cjs +998 -0
- package/dist/select.d.cts +357 -0
- package/dist/select.d.ts +357 -0
- package/dist/select.js +5 -0
- package/dist/selectable-feature-card.cjs +159 -0
- package/dist/selectable-feature-card.d.cts +74 -0
- package/dist/selectable-feature-card.d.ts +74 -0
- package/dist/selectable-feature-card.js +3 -0
- package/dist/sidebar-item.cjs +175 -0
- package/dist/sidebar-item.d.cts +75 -0
- package/dist/sidebar-item.d.ts +75 -0
- package/dist/sidebar-item.js +2 -0
- package/dist/switch.cjs +86 -0
- package/dist/switch.d.cts +72 -0
- package/dist/switch.d.ts +72 -0
- package/dist/switch.js +2 -0
- package/dist/table-cell.cjs +196 -0
- package/dist/table-cell.d.cts +76 -0
- package/dist/table-cell.d.ts +76 -0
- package/dist/table-cell.js +2 -0
- package/dist/table-title.cjs +34 -0
- package/dist/table-title.d.cts +16 -0
- package/dist/table-title.d.ts +16 -0
- package/dist/table-title.js +2 -0
- package/dist/tabs.cjs +299 -0
- package/dist/tabs.d.cts +116 -0
- package/dist/tabs.d.ts +116 -0
- package/dist/tabs.js +2 -0
- package/dist/toast.cjs +87 -0
- package/dist/toast.d.cts +48 -0
- package/dist/toast.d.ts +48 -0
- package/dist/toast.js +2 -0
- package/dist/token-input.cjs +415 -0
- package/dist/token-input.d.cts +90 -0
- package/dist/token-input.d.ts +90 -0
- package/dist/token-input.js +3 -0
- package/dist/tooltip.cjs +384 -0
- package/dist/tooltip.d.cts +142 -0
- package/dist/tooltip.d.ts +142 -0
- package/dist/tooltip.js +2 -0
- package/package.json +200 -0
- package/src/fonts.css +33 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { S as SelectOption } from './SelectTrigger-By0iPBI3.cjs';
|
|
3
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
4
|
+
import { VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* InlineSelect trigger styling — the inline-text select (Figma node 4926:5462).
|
|
8
|
+
* Unlike `Select`, there is NO field box: the trigger is a run of inline text
|
|
9
|
+
* (the value label) plus a small filled caret. Binds ONLY to the semantic
|
|
10
|
+
* `--color-*` token layer (never primitives, never arbitrary values); theme
|
|
11
|
+
* switching is automatic via the `.dark` class.
|
|
12
|
+
*
|
|
13
|
+
* Two `variation`s map to the two card-foreground families:
|
|
14
|
+
* - `primary` → neutral card foreground (`card-primary-foreground`), muted
|
|
15
|
+
* caret/placeholder (`card-primary-foreground-muted`).
|
|
16
|
+
* - `secondary` → brand card foreground (`card-brand-secondary-foreground`),
|
|
17
|
+
* muted caret/placeholder (`card-brand-secondary-foreground-muted`).
|
|
18
|
+
*
|
|
19
|
+
* FOCUS is the ADR-0010 system ring — a momentary control (not a text field, so
|
|
20
|
+
* NOT the border-recolor treatment `Select`/`Input` use): 2px `--focus` stroke,
|
|
21
|
+
* 2px offset, on `:focus-visible` only, with a 2px (`radius/xs`) corner to match
|
|
22
|
+
* the Figma focus ring. Suppressed when disabled. The placeholder / value / caret
|
|
23
|
+
* colours are applied by `InlineSelectTrigger` (they depend on runtime state:
|
|
24
|
+
* whether a value is selected, and whether the caret follows the muted or full
|
|
25
|
+
* foreground), so this file owns the shared layout + focus + disabled shell.
|
|
26
|
+
*/
|
|
27
|
+
declare const inlineSelectVariants: (props?: ({
|
|
28
|
+
variation?: "primary" | "secondary" | null | undefined;
|
|
29
|
+
disabled?: boolean | null | undefined;
|
|
30
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
31
|
+
type InlineSelectVariantsProps = VariantProps<typeof inlineSelectVariants>;
|
|
32
|
+
|
|
33
|
+
interface InlineSelectProps extends Omit<React.ComponentProps<"button">, "value" | "defaultValue" | "onChange"> {
|
|
34
|
+
/** The options to choose from (array-of-data API). */
|
|
35
|
+
options: SelectOption[];
|
|
36
|
+
/** Controlled selection (single). */
|
|
37
|
+
value?: string;
|
|
38
|
+
/** Uncontrolled initial selection (single). */
|
|
39
|
+
defaultValue?: string;
|
|
40
|
+
/** Fires with the next selected value. */
|
|
41
|
+
onValueChange?: (value: string) => void;
|
|
42
|
+
/** `primary` (neutral foreground, default) | `secondary` (brand foreground). */
|
|
43
|
+
variation?: NonNullable<InlineSelectVariantsProps["variation"]>;
|
|
44
|
+
/** Shown when nothing is selected. */
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/** Leading adornment before the label (decorative, 16px). */
|
|
47
|
+
leadingSlot?: React.ReactNode;
|
|
48
|
+
/** Disabled — the trigger won't open. */
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
/** Form field name (mirrors the value into a hidden input for form submission). */
|
|
51
|
+
name?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* InlineSelect — a single-select styled as inline text (NO field box), for use
|
|
55
|
+
* inline in a sentence, a card header, or a toolbar (Figma node 4926:5462). The
|
|
56
|
+
* trigger is the value label + a small filled caret; choosing opens a portaled
|
|
57
|
+
* dropdown reusing `Select`'s `SelectListbox`.
|
|
58
|
+
*
|
|
59
|
+
* Single-select only (no multi): `value` / `defaultValue` are `string`s and
|
|
60
|
+
* `onValueChange` emits a `string`. Uncontrolled by default (`defaultValue`)
|
|
61
|
+
* with a controlled escape hatch (`value` + `onValueChange`). Behaviour (open,
|
|
62
|
+
* keyboard model, type-ahead, active-descendant, reposition, outside-click) is
|
|
63
|
+
* shared with `Select` via the `useSelectListbox` hook. `ref` is a normal React
|
|
64
|
+
* 19 prop, forwarded to the trigger button.
|
|
65
|
+
*/
|
|
66
|
+
declare function InlineSelect({ options, value: valueProp, defaultValue, onValueChange, variation, placeholder, leadingSlot, disabled, name, id: idProp, className, ref, ...props }: InlineSelectProps): React.JSX.Element;
|
|
67
|
+
|
|
68
|
+
interface InlineSelectTriggerProps extends Omit<React.ComponentProps<"button">, "value">, Omit<InlineSelectVariantsProps, "disabled"> {
|
|
69
|
+
/** Resolved display text (from the hook's `selectTriggerLabel`). */
|
|
70
|
+
displayText: string;
|
|
71
|
+
/** Whether `displayText` is the placeholder (muted) vs a real value. */
|
|
72
|
+
isPlaceholder: boolean;
|
|
73
|
+
/** Whether the listbox is open (flips the caret). */
|
|
74
|
+
open?: boolean;
|
|
75
|
+
/** Leading adornment (decorative — kept out of the a11y tree via `aria-hidden`). */
|
|
76
|
+
leadingSlot?: React.ReactNode;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The InlineSelect trigger — a `<button type="button">` styled as inline text
|
|
80
|
+
* (NO field box): an optional 16px leading slot, the value label (`body-03`),
|
|
81
|
+
* and a 12px filled caret that flips (`rotate-180`) when `open`. The value uses
|
|
82
|
+
* the variation foreground colour; the placeholder + caret use the muted
|
|
83
|
+
* variation colour (Figma node 4926:5462).
|
|
84
|
+
*
|
|
85
|
+
* Presentational leaf: all open/selection/keyboard orchestration and the ARIA
|
|
86
|
+
* popup wiring (`aria-haspopup`/`aria-expanded`/`aria-controls`/
|
|
87
|
+
* `aria-activedescendant`) live in `InlineSelect`, which spreads them through
|
|
88
|
+
* `...props`. `ref` is a normal React 19 prop, forwarded to the button.
|
|
89
|
+
*/
|
|
90
|
+
declare function InlineSelectTrigger({ variation, disabled, displayText, isPlaceholder, open, leadingSlot, className, ref, ...props }: InlineSelectTriggerProps): React.JSX.Element;
|
|
91
|
+
|
|
92
|
+
export { InlineSelect, type InlineSelectProps, InlineSelectTrigger, type InlineSelectTriggerProps, type InlineSelectVariantsProps, inlineSelectVariants };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { S as SelectOption } from './SelectTrigger-By0iPBI3.js';
|
|
3
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
4
|
+
import { VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* InlineSelect trigger styling — the inline-text select (Figma node 4926:5462).
|
|
8
|
+
* Unlike `Select`, there is NO field box: the trigger is a run of inline text
|
|
9
|
+
* (the value label) plus a small filled caret. Binds ONLY to the semantic
|
|
10
|
+
* `--color-*` token layer (never primitives, never arbitrary values); theme
|
|
11
|
+
* switching is automatic via the `.dark` class.
|
|
12
|
+
*
|
|
13
|
+
* Two `variation`s map to the two card-foreground families:
|
|
14
|
+
* - `primary` → neutral card foreground (`card-primary-foreground`), muted
|
|
15
|
+
* caret/placeholder (`card-primary-foreground-muted`).
|
|
16
|
+
* - `secondary` → brand card foreground (`card-brand-secondary-foreground`),
|
|
17
|
+
* muted caret/placeholder (`card-brand-secondary-foreground-muted`).
|
|
18
|
+
*
|
|
19
|
+
* FOCUS is the ADR-0010 system ring — a momentary control (not a text field, so
|
|
20
|
+
* NOT the border-recolor treatment `Select`/`Input` use): 2px `--focus` stroke,
|
|
21
|
+
* 2px offset, on `:focus-visible` only, with a 2px (`radius/xs`) corner to match
|
|
22
|
+
* the Figma focus ring. Suppressed when disabled. The placeholder / value / caret
|
|
23
|
+
* colours are applied by `InlineSelectTrigger` (they depend on runtime state:
|
|
24
|
+
* whether a value is selected, and whether the caret follows the muted or full
|
|
25
|
+
* foreground), so this file owns the shared layout + focus + disabled shell.
|
|
26
|
+
*/
|
|
27
|
+
declare const inlineSelectVariants: (props?: ({
|
|
28
|
+
variation?: "primary" | "secondary" | null | undefined;
|
|
29
|
+
disabled?: boolean | null | undefined;
|
|
30
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
31
|
+
type InlineSelectVariantsProps = VariantProps<typeof inlineSelectVariants>;
|
|
32
|
+
|
|
33
|
+
interface InlineSelectProps extends Omit<React.ComponentProps<"button">, "value" | "defaultValue" | "onChange"> {
|
|
34
|
+
/** The options to choose from (array-of-data API). */
|
|
35
|
+
options: SelectOption[];
|
|
36
|
+
/** Controlled selection (single). */
|
|
37
|
+
value?: string;
|
|
38
|
+
/** Uncontrolled initial selection (single). */
|
|
39
|
+
defaultValue?: string;
|
|
40
|
+
/** Fires with the next selected value. */
|
|
41
|
+
onValueChange?: (value: string) => void;
|
|
42
|
+
/** `primary` (neutral foreground, default) | `secondary` (brand foreground). */
|
|
43
|
+
variation?: NonNullable<InlineSelectVariantsProps["variation"]>;
|
|
44
|
+
/** Shown when nothing is selected. */
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/** Leading adornment before the label (decorative, 16px). */
|
|
47
|
+
leadingSlot?: React.ReactNode;
|
|
48
|
+
/** Disabled — the trigger won't open. */
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
/** Form field name (mirrors the value into a hidden input for form submission). */
|
|
51
|
+
name?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* InlineSelect — a single-select styled as inline text (NO field box), for use
|
|
55
|
+
* inline in a sentence, a card header, or a toolbar (Figma node 4926:5462). The
|
|
56
|
+
* trigger is the value label + a small filled caret; choosing opens a portaled
|
|
57
|
+
* dropdown reusing `Select`'s `SelectListbox`.
|
|
58
|
+
*
|
|
59
|
+
* Single-select only (no multi): `value` / `defaultValue` are `string`s and
|
|
60
|
+
* `onValueChange` emits a `string`. Uncontrolled by default (`defaultValue`)
|
|
61
|
+
* with a controlled escape hatch (`value` + `onValueChange`). Behaviour (open,
|
|
62
|
+
* keyboard model, type-ahead, active-descendant, reposition, outside-click) is
|
|
63
|
+
* shared with `Select` via the `useSelectListbox` hook. `ref` is a normal React
|
|
64
|
+
* 19 prop, forwarded to the trigger button.
|
|
65
|
+
*/
|
|
66
|
+
declare function InlineSelect({ options, value: valueProp, defaultValue, onValueChange, variation, placeholder, leadingSlot, disabled, name, id: idProp, className, ref, ...props }: InlineSelectProps): React.JSX.Element;
|
|
67
|
+
|
|
68
|
+
interface InlineSelectTriggerProps extends Omit<React.ComponentProps<"button">, "value">, Omit<InlineSelectVariantsProps, "disabled"> {
|
|
69
|
+
/** Resolved display text (from the hook's `selectTriggerLabel`). */
|
|
70
|
+
displayText: string;
|
|
71
|
+
/** Whether `displayText` is the placeholder (muted) vs a real value. */
|
|
72
|
+
isPlaceholder: boolean;
|
|
73
|
+
/** Whether the listbox is open (flips the caret). */
|
|
74
|
+
open?: boolean;
|
|
75
|
+
/** Leading adornment (decorative — kept out of the a11y tree via `aria-hidden`). */
|
|
76
|
+
leadingSlot?: React.ReactNode;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The InlineSelect trigger — a `<button type="button">` styled as inline text
|
|
80
|
+
* (NO field box): an optional 16px leading slot, the value label (`body-03`),
|
|
81
|
+
* and a 12px filled caret that flips (`rotate-180`) when `open`. The value uses
|
|
82
|
+
* the variation foreground colour; the placeholder + caret use the muted
|
|
83
|
+
* variation colour (Figma node 4926:5462).
|
|
84
|
+
*
|
|
85
|
+
* Presentational leaf: all open/selection/keyboard orchestration and the ARIA
|
|
86
|
+
* popup wiring (`aria-haspopup`/`aria-expanded`/`aria-controls`/
|
|
87
|
+
* `aria-activedescendant`) live in `InlineSelect`, which spreads them through
|
|
88
|
+
* `...props`. `ref` is a normal React 19 prop, forwarded to the button.
|
|
89
|
+
*/
|
|
90
|
+
declare function InlineSelectTrigger({ variation, disabled, displayText, isPlaceholder, open, leadingSlot, className, ref, ...props }: InlineSelectTriggerProps): React.JSX.Element;
|
|
91
|
+
|
|
92
|
+
export { InlineSelect, type InlineSelectProps, InlineSelectTrigger, type InlineSelectTriggerProps, type InlineSelectVariantsProps, inlineSelectVariants };
|
package/dist/input.cjs
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var react = require('@phosphor-icons/react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
29
|
+
|
|
30
|
+
// src/input/Input.tsx
|
|
31
|
+
function cn(...inputs) {
|
|
32
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
33
|
+
}
|
|
34
|
+
var inputVariants = classVarianceAuthority.cva(
|
|
35
|
+
[
|
|
36
|
+
// Field row layout — icons + input on one line, vertically centred.
|
|
37
|
+
"flex w-full items-center",
|
|
38
|
+
// Colour transitions for the focus border change.
|
|
39
|
+
"transition-colors",
|
|
40
|
+
// I-beam text cursor over the WHOLE field row (not just the inner input),
|
|
41
|
+
// so clicking anywhere in the padding/icon area reads as "type here". This
|
|
42
|
+
// is the only hover affordance — hover intentionally does NOT recolour the
|
|
43
|
+
// border (user-authorized deviation from the Figma). `cursor-text` sits in
|
|
44
|
+
// the base; `has-[:disabled]:cursor-not-allowed` below is declared AFTER it
|
|
45
|
+
// so it wins on a disabled field (same-specificity utilities, later class
|
|
46
|
+
// takes precedence — confirmed by tailwind-merge ordering).
|
|
47
|
+
"cursor-text",
|
|
48
|
+
// Disabled: not-allowed cursor + block pointer interaction on the whole
|
|
49
|
+
// field (the native `disabled` on the input already stops typing; icons are
|
|
50
|
+
// dimmed separately by the `disabled`-derived `iconStateClass` in <Input>).
|
|
51
|
+
// Declared after `cursor-text` so it overrides the I-beam when disabled.
|
|
52
|
+
"has-[:disabled]:cursor-not-allowed"
|
|
53
|
+
],
|
|
54
|
+
{
|
|
55
|
+
variants: {
|
|
56
|
+
variation: {
|
|
57
|
+
field: [
|
|
58
|
+
"bg-input-background border border-input-border rounded-sm",
|
|
59
|
+
// Focus (mouse or keyboard) strengthens the whole border to the
|
|
60
|
+
// accent. Hover does NOT change colour (cursor-only affordance).
|
|
61
|
+
"focus-within:border-input-border-accent"
|
|
62
|
+
],
|
|
63
|
+
line: [
|
|
64
|
+
"border-b border-input-border",
|
|
65
|
+
// Focus (mouse or keyboard) strengthens the bottom line to the
|
|
66
|
+
// accent. Hover does NOT change colour (cursor-only affordance).
|
|
67
|
+
"focus-within:border-input-border-accent"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
size: {
|
|
71
|
+
// sm/lg heights reuse the Button scale; gap follows Figma per size.
|
|
72
|
+
sm: "h-8 gap-1",
|
|
73
|
+
lg: "h-10 gap-2"
|
|
74
|
+
},
|
|
75
|
+
error: {
|
|
76
|
+
// Error out-ranks focus: declare the destructive border for the resting
|
|
77
|
+
// state AND its `focus-within:` form so an errored field never recolours
|
|
78
|
+
// to the accent when focused (a bare `border-…` alone would lose to the
|
|
79
|
+
// variation's `focus-within:` accent utility above). No `hover:` form is
|
|
80
|
+
// needed — hover no longer changes colour on any variation. The `error`
|
|
81
|
+
// variant's classes are emitted after the variation's, so at equal
|
|
82
|
+
// specificity the destructive `focus-within:` wins by order.
|
|
83
|
+
true: "border-input-destructive-foreground-accent focus-within:border-input-destructive-foreground-accent",
|
|
84
|
+
false: ""
|
|
85
|
+
},
|
|
86
|
+
// Bottom-line visibility toggle. LINE ONLY — the hide effect is applied
|
|
87
|
+
// via a compound variant below (scoped to `variation:line`), so `field`
|
|
88
|
+
// is unaffected. Boolean → `true`/`false`; `true` (default) keeps today's
|
|
89
|
+
// behaviour. No classes here: `true` is the resting `border-input-border`
|
|
90
|
+
// from the variation, and `false`'s hide is the compound variant.
|
|
91
|
+
showBottomLine: {
|
|
92
|
+
true: "",
|
|
93
|
+
false: ""
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
compoundVariants: [
|
|
97
|
+
// field padding — Figma field-lg = px-3 (12px) / py-2 (8px).
|
|
98
|
+
{ variation: "field", size: "lg", className: "px-3 py-2" },
|
|
99
|
+
// field-sm: DERIVED. px-2 (8px = spacing/2) steps down from field-lg's
|
|
100
|
+
// px-3 (12px) and equals py-2 for a uniform spacing/2 inset all around.
|
|
101
|
+
{ variation: "field", size: "sm", className: "px-2 py-2" },
|
|
102
|
+
// line — no horizontal padding (Figma px-0); vertical padding per size.
|
|
103
|
+
{ variation: "line", size: "lg", className: "py-2" },
|
|
104
|
+
{ variation: "line", size: "sm", className: "py-2.5" },
|
|
105
|
+
// Hide the resting bottom line (LINE ONLY): recolour to transparent while
|
|
106
|
+
// keeping `border-b` for a stable 1px width (no layout shift). Gated on
|
|
107
|
+
// `error:false` so an errored field keeps its destructive rest border
|
|
108
|
+
// (transparent would otherwise last-win over it). Focus still wins:
|
|
109
|
+
// `focus-within:border-input-border-accent` out-ranks `border-transparent`
|
|
110
|
+
// (later in class order), so the line reappears on focus. `field` is
|
|
111
|
+
// untouched — this compound only matches `variation:line`.
|
|
112
|
+
{
|
|
113
|
+
variation: "line",
|
|
114
|
+
showBottomLine: false,
|
|
115
|
+
error: false,
|
|
116
|
+
className: "border-transparent"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
defaultVariants: {
|
|
120
|
+
variation: "field",
|
|
121
|
+
size: "lg",
|
|
122
|
+
error: false,
|
|
123
|
+
showBottomLine: true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
function Input({
|
|
128
|
+
className,
|
|
129
|
+
fieldClassName,
|
|
130
|
+
wrapperClassName,
|
|
131
|
+
variation = "field",
|
|
132
|
+
size = "lg",
|
|
133
|
+
error = false,
|
|
134
|
+
// LINE-only bottom-line toggle. Destructured OUT of `props` so it never
|
|
135
|
+
// spreads onto the native <input> (invalid DOM attribute → React warning).
|
|
136
|
+
showBottomLine = true,
|
|
137
|
+
label,
|
|
138
|
+
optional = false,
|
|
139
|
+
hint,
|
|
140
|
+
leftSlot,
|
|
141
|
+
rightSlot,
|
|
142
|
+
disabled,
|
|
143
|
+
id: idProp,
|
|
144
|
+
ref,
|
|
145
|
+
...props
|
|
146
|
+
}) {
|
|
147
|
+
const generatedId = React__namespace.useId();
|
|
148
|
+
const id = idProp ?? generatedId;
|
|
149
|
+
const hintId = hint ? `${id}-hint` : void 0;
|
|
150
|
+
const iconStateClass = cn(
|
|
151
|
+
error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
|
|
152
|
+
disabled && "text-input-foreground-muted"
|
|
153
|
+
);
|
|
154
|
+
const HintIcon = error ? react.WarningIcon : react.InfoIcon;
|
|
155
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", wrapperClassName), children: [
|
|
156
|
+
label != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
158
|
+
"label",
|
|
159
|
+
{
|
|
160
|
+
htmlFor: id,
|
|
161
|
+
"data-slot": "input-label",
|
|
162
|
+
className: cn(
|
|
163
|
+
"caption-03-medium uppercase",
|
|
164
|
+
error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
|
|
165
|
+
disabled && "text-input-foreground-muted"
|
|
166
|
+
),
|
|
167
|
+
children: label
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
optional && /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
"span",
|
|
172
|
+
{
|
|
173
|
+
"data-slot": "input-optional",
|
|
174
|
+
className: "body-04 text-input-foreground-muted",
|
|
175
|
+
children: "Optional"
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
180
|
+
"div",
|
|
181
|
+
{
|
|
182
|
+
"data-slot": "input-field",
|
|
183
|
+
"data-variation": variation,
|
|
184
|
+
"data-error": error || void 0,
|
|
185
|
+
className: cn(
|
|
186
|
+
inputVariants({ variation, size, error, showBottomLine }),
|
|
187
|
+
// Icon slots inherit the shared state colour; 16px default size.
|
|
188
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
189
|
+
iconStateClass,
|
|
190
|
+
fieldClassName
|
|
191
|
+
),
|
|
192
|
+
children: [
|
|
193
|
+
leftSlot != null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
194
|
+
"span",
|
|
195
|
+
{
|
|
196
|
+
"data-slot": "input-leading",
|
|
197
|
+
"aria-hidden": "true",
|
|
198
|
+
className: "flex items-center",
|
|
199
|
+
children: leftSlot
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
203
|
+
"input",
|
|
204
|
+
{
|
|
205
|
+
id,
|
|
206
|
+
"data-slot": "input",
|
|
207
|
+
disabled,
|
|
208
|
+
"aria-invalid": error || void 0,
|
|
209
|
+
"aria-describedby": hintId,
|
|
210
|
+
className: cn(
|
|
211
|
+
// Typography (Figma `body-03`) + fill the row. `min-w-0` lets the
|
|
212
|
+
// input shrink inside the flex row instead of overflowing.
|
|
213
|
+
"body-03 w-full min-w-0 bg-transparent outline-none",
|
|
214
|
+
// Typed text + caret colour (Figma `input-foreground-accent`).
|
|
215
|
+
"text-input-foreground-accent caret-input-foreground-accent",
|
|
216
|
+
// Placeholder colour (Figma `input-foreground-muted`).
|
|
217
|
+
"placeholder:text-input-foreground-muted",
|
|
218
|
+
// Disabled text goes muted; keep the not-allowed cursor.
|
|
219
|
+
"disabled:cursor-not-allowed disabled:text-input-foreground-muted",
|
|
220
|
+
className
|
|
221
|
+
),
|
|
222
|
+
ref,
|
|
223
|
+
...props
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
rightSlot != null && // NOT aria-hidden: the trailing slot may host an interactive control
|
|
227
|
+
// (e.g. SearchBar's clear button) that must stay reachable by AT +
|
|
228
|
+
// keyboard, and a focusable element inside aria-hidden is an ARIA
|
|
229
|
+
// violation. Decorative bare-<svg> icons are silent to AT on their own
|
|
230
|
+
// (no role / no accessible name), so dropping aria-hidden here does not
|
|
231
|
+
// make them announced. The leading slot stays decorative (aria-hidden).
|
|
232
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-slot": "input-trailing", className: "flex items-center", children: rightSlot })
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
),
|
|
236
|
+
hint != null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
237
|
+
"p",
|
|
238
|
+
{
|
|
239
|
+
id: hintId,
|
|
240
|
+
"data-slot": "input-hint",
|
|
241
|
+
className: cn(
|
|
242
|
+
"body-04 flex items-center gap-1",
|
|
243
|
+
error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
|
|
244
|
+
disabled && "text-input-foreground-muted"
|
|
245
|
+
),
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
248
|
+
HintIcon,
|
|
249
|
+
{
|
|
250
|
+
"aria-hidden": "true",
|
|
251
|
+
weight: "regular",
|
|
252
|
+
className: "size-4 shrink-0"
|
|
253
|
+
}
|
|
254
|
+
),
|
|
255
|
+
hint
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
exports.Input = Input;
|
|
263
|
+
exports.inputVariants = inputVariants;
|
package/dist/input.d.cts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Input styling — mirrors the Figma `BaseInput` component (node 4973:6816) and
|
|
7
|
+
* binds ONLY to the semantic `--input-*` token layer (never primitives, never
|
|
8
|
+
* arbitrary values). Theme switching is automatic via the `.dark` class, so no
|
|
9
|
+
* `dark:` variants are needed.
|
|
10
|
+
*
|
|
11
|
+
* This cva styles the FIELD wrapper (the row that holds the leading icon, the
|
|
12
|
+
* real `<input>`, and the trailing icon) — NOT the bare `<input>`, whose own
|
|
13
|
+
* classes live in <Input> so the token that colours typed text
|
|
14
|
+
* (`text-input-foreground-accent`) and the placeholder
|
|
15
|
+
* (`text-input-foreground-muted`) can sit on the element itself.
|
|
16
|
+
*
|
|
17
|
+
* The 7 Figma "states" (default / hover / focused&active / active&filled /
|
|
18
|
+
* filled …) are runtime DOM states of a real `<input>`, NOT a prop:
|
|
19
|
+
* - hover → a text (I-beam) cursor affordance ONLY (`cursor-text` in the
|
|
20
|
+
* base). Hover does NOT recolour the border/underline. This is a
|
|
21
|
+
* deliberate, user-authorized deviation from the Figma (which draws
|
|
22
|
+
* the accent border on hover): the team decided hover is a pointer
|
|
23
|
+
* affordance, not a colour change.
|
|
24
|
+
* - focus → `focus-within:` on the field wrapper recolours the border (field)
|
|
25
|
+
* / bottom line (line) to `input-border-accent`. The input is the
|
|
26
|
+
* focusable descendant; the visible treatment lives on its wrapper.
|
|
27
|
+
* There is NO system outline ring on this control (see below).
|
|
28
|
+
* - filled → whether the input holds a value (no styling delta in this token
|
|
29
|
+
* set beyond the typed-text colour, applied on the input directly).
|
|
30
|
+
* Only `disabled` and `error` are real props (handled below + in <Input>).
|
|
31
|
+
*
|
|
32
|
+
* Focus model — user-authorized exception to ADR-0010 #4 / ADR-0011:
|
|
33
|
+
* text inputs do NOT use the system `:focus-visible` outline ring. A keyboard
|
|
34
|
+
* user never triggers `:hover`, and (since hover no longer changes colour) the
|
|
35
|
+
* gray→accent border recolour on ANY focus is itself a clear, unambiguous
|
|
36
|
+
* WCAG 2.1 AA (2.4.7) focus indicator. Text inputs also hold focus while the
|
|
37
|
+
* user types, so `focus-within` (any focus, not just `:focus-visible`) is the
|
|
38
|
+
* correct trigger. The ring is intentionally omitted here; do NOT re-add it.
|
|
39
|
+
*
|
|
40
|
+
* Variant × size axes (both independent, 4 combinations):
|
|
41
|
+
* - variation `field`: bordered rounded box (`bg-input-background`, `border`,
|
|
42
|
+
* `rounded-sm`, horizontal padding). Focus strengthens the border to
|
|
43
|
+
* `input-border-accent` (hover does not change colour).
|
|
44
|
+
* - variation `line`: no box/background — a single bottom border only
|
|
45
|
+
* (`border-b`), no horizontal padding. Focus strengthens the bottom line
|
|
46
|
+
* (hover does not change colour).
|
|
47
|
+
*
|
|
48
|
+
* Sizes reuse the sibling sm/lg scale (Button = `h-8` / `h-10`):
|
|
49
|
+
* - lg: `h-10`, `gap-2`, `py-2`; field adds `px-3` (Figma field-lg = 12px).
|
|
50
|
+
* - sm: `h-8`, `gap-1`; line uses `py-2.5` (Figma line-sm = 10px), field uses
|
|
51
|
+
* `py-2` (matches lg vertical rhythm inside the shorter box).
|
|
52
|
+
*
|
|
53
|
+
* field-sm horizontal padding is DERIVED (Figma only draws field-lg): we use
|
|
54
|
+
* `px-2` (8px = `spacing/2`), stepping down from field-lg's `px-3` (12px) and
|
|
55
|
+
* matching field-sm's own `py-2` (8px) for a uniform `spacing/2` inset on all
|
|
56
|
+
* sides. (This is a deliberate token-aligned choice; it does NOT match
|
|
57
|
+
* Button's sm `px-2.5`.)
|
|
58
|
+
*
|
|
59
|
+
* Error is a prop, not an interaction state: it recolours the border/bottom
|
|
60
|
+
* line to the destructive accent and out-ranks focus (an errored field stays
|
|
61
|
+
* red when focused — it never flips to the purple accent), so its border
|
|
62
|
+
* utilities are declared with matching-but-later specificity via the `error`
|
|
63
|
+
* variant below (a plain destructive border for the resting state plus a
|
|
64
|
+
* `focus-within:` destructive form that out-ranks the variation's
|
|
65
|
+
* `focus-within:` accent by class order).
|
|
66
|
+
*
|
|
67
|
+
* `showBottomLine` (LINE ONLY) toggles the resting visibility of the bottom
|
|
68
|
+
* line without any layout shift. The `border-b` (1px width) is ALWAYS present;
|
|
69
|
+
* only the border COLOUR changes. When `false`, a compound variant recolours
|
|
70
|
+
* the resting bottom line to `border-transparent` (invisible), while focus
|
|
71
|
+
* (`focus-within:border-input-border-accent`) and error still win — so the
|
|
72
|
+
* line reappears on focus and on error. The compound is gated on `error:false`
|
|
73
|
+
* so an errored field's destructive rest border is NOT overridden by
|
|
74
|
+
* transparent (tailwind-merge last-wins would otherwise drop it). On the
|
|
75
|
+
* `field` variation `showBottomLine` has NO effect: the compound is scoped to
|
|
76
|
+
* `variation:line`, so `field`'s box border is untouched.
|
|
77
|
+
*/
|
|
78
|
+
declare const inputVariants: (props?: ({
|
|
79
|
+
variation?: "line" | "field" | null | undefined;
|
|
80
|
+
size?: "sm" | "lg" | null | undefined;
|
|
81
|
+
error?: boolean | null | undefined;
|
|
82
|
+
showBottomLine?: boolean | null | undefined;
|
|
83
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
84
|
+
type InputVariantsProps = VariantProps<typeof inputVariants>;
|
|
85
|
+
|
|
86
|
+
interface InputProps extends Omit<React.ComponentProps<"input">, "size">, Omit<InputVariantsProps, "error"> {
|
|
87
|
+
/**
|
|
88
|
+
* Uppercase label shown above the field. When omitted the label row is not
|
|
89
|
+
* rendered. Always paired with the `<input>` via generated `id`/`htmlFor`
|
|
90
|
+
* (or your own `id`), so the control is properly named for assistive tech.
|
|
91
|
+
*/
|
|
92
|
+
label?: React.ReactNode;
|
|
93
|
+
/**
|
|
94
|
+
* Marks the field as not required — renders an "Optional" tag on the right of
|
|
95
|
+
* the label row (Figma). Has no effect without a `label`.
|
|
96
|
+
*/
|
|
97
|
+
optional?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Helper text shown below the field, with a leading 16px icon (info by
|
|
100
|
+
* default, warning when `error`). Linked to the input via `aria-describedby`.
|
|
101
|
+
*/
|
|
102
|
+
hint?: React.ReactNode;
|
|
103
|
+
/**
|
|
104
|
+
* Error state (a prop, not an interaction state): sets `aria-invalid`, turns
|
|
105
|
+
* the label, border/bottom-line, icons and hint to the destructive accent,
|
|
106
|
+
* and swaps the hint icon to a warning.
|
|
107
|
+
*/
|
|
108
|
+
error?: boolean;
|
|
109
|
+
/** Leading adornment (usually a 16px Phosphor icon). Decorative — kept out of the a11y tree via `aria-hidden`. */
|
|
110
|
+
leftSlot?: React.ReactNode;
|
|
111
|
+
/**
|
|
112
|
+
* Trailing adornment. Usually a 16px Phosphor icon (decorative), but MAY be an
|
|
113
|
+
* interactive control (e.g. SearchBar's clear `<button>`). The wrapper is NOT
|
|
114
|
+
* `aria-hidden` (that would hide a focusable child from assistive tech — an
|
|
115
|
+
* ARIA violation); decorative bare `<svg>` icons are silent to AT on their own
|
|
116
|
+
* (no role, no accessible name), so they stay unannounced without it. An
|
|
117
|
+
* interactive control placed here must carry its own accessible name.
|
|
118
|
+
*/
|
|
119
|
+
rightSlot?: React.ReactNode;
|
|
120
|
+
/** Class names for the outer wrapper (the vertical label/field/hint stack). */
|
|
121
|
+
wrapperClassName?: string;
|
|
122
|
+
/** Class names for the field row (border/background box), not the `<input>`. */
|
|
123
|
+
fieldClassName?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Toggles the resting visibility of the bottom line on the **`line`**
|
|
126
|
+
* variation only. Default `true` (line visible at rest). When `false`, the
|
|
127
|
+
* bottom line is hidden at rest (recoloured to transparent) but still appears
|
|
128
|
+
* on focus (accent) and on error (destructive); the 1px `border-b` is kept in
|
|
129
|
+
* all cases so there is no layout shift — only the border colour changes.
|
|
130
|
+
*
|
|
131
|
+
* Has NO effect on the `field` variation (its box border is out of scope).
|
|
132
|
+
*/
|
|
133
|
+
showBottomLine?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Text input — the Figma `BaseInput` component (node 4973:6816), rendered as a
|
|
137
|
+
* real `<input>` wrapped in a field row with optional `leftSlot`/`rightSlot`
|
|
138
|
+
* icon slots, an optional uppercase label row, and an optional hint message.
|
|
139
|
+
*
|
|
140
|
+
* Follows the ADR-0010 form-control conventions: the native `<input>` is the
|
|
141
|
+
* single source of truth (uncontrolled `defaultValue` or controlled
|
|
142
|
+
* `value` + `onChange`); the component holds no React state. `ref` is a normal
|
|
143
|
+
* prop (React 19). Not `asChild` — the root must stay a void, platform-behaviour
|
|
144
|
+
* -bearing `<input>` (ADR-0010).
|
|
145
|
+
*
|
|
146
|
+
* Two independent axes (4 combinations): `variation` (`field` bordered box /
|
|
147
|
+
* `line` bottom-border-only) × `size` (`sm` / `lg`, reusing the sibling scale).
|
|
148
|
+
* The Figma "states" (hover/focus/filled) are runtime DOM states: hover is an
|
|
149
|
+
* I-beam cursor affordance only (no colour change), focus recolours the border
|
|
150
|
+
* via `focus-within:` (no system outline ring — a user-authorized exception to
|
|
151
|
+
* ADR-0010 #4 / ADR-0011, see `inputVariants.ts`), and filled shows the typed-
|
|
152
|
+
* text colour. Only `disabled` (native attribute) and `error` (prop) are real
|
|
153
|
+
* props.
|
|
154
|
+
*/
|
|
155
|
+
declare function Input({ className, fieldClassName, wrapperClassName, variation, size, error, showBottomLine, label, optional, hint, leftSlot, rightSlot, disabled, id: idProp, ref, ...props }: InputProps): React.JSX.Element;
|
|
156
|
+
|
|
157
|
+
export { Input, type InputProps, type InputVariantsProps, inputVariants };
|