@passui/react 0.1.35 → 0.1.37
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/types/src/components/accordion/accordion.d.ts +5 -1
- package/dist/types/src/components/date-picker/date-picker.d.ts +4 -1
- package/dist/types/src/components/dialog/dialog.d.ts +4 -1
- package/dist/types/src/components/disclosure-group/disclosure-group.d.ts +4 -1
- package/dist/types/src/components/morph/index.d.ts +2 -0
- package/dist/types/src/components/morph/morph.d.ts +29 -0
- package/dist/types/src/components/morph/use-morph-disclosure.d.ts +13 -0
- package/dist/types/src/components/radio-group/radio-group.d.ts +4 -1
- package/dist/types/src/components/segmented-control/segmented-control.d.ts +4 -1
- package/dist/types/src/components/sheet/sheet.d.ts +3 -0
- package/dist/types/src/components/slider/slider.d.ts +5 -2
- package/dist/types/src/components/stepper/stepper.d.ts +4 -1
- package/dist/types/src/components/swatch-picker/swatch-picker.d.ts +4 -1
- package/dist/types/src/components/tab-view/tab-view.d.ts +4 -1
- package/dist/types/src/components/toggle/toggle.d.ts +4 -1
- package/dist/types/src/index.d.ts +1 -0
- package/package.json +5 -1
- package/src/components/accordion/accordion.tsx +16 -3
- package/src/components/date-picker/date-picker.tsx +37 -6
- package/src/components/dialog/dialog.tsx +48 -5
- package/src/components/disclosure-group/disclosure-group.tsx +31 -9
- package/src/components/morph/index.ts +2 -0
- package/src/components/morph/morph.tsx +210 -0
- package/src/components/morph/use-morph-disclosure.ts +27 -0
- package/src/components/radio-group/radio-group.tsx +30 -10
- package/src/components/segmented-control/segmented-control.tsx +47 -6
- package/src/components/sheet/sheet.tsx +20 -2
- package/src/components/slider/slider.tsx +58 -9
- package/src/components/stepper/stepper.tsx +37 -11
- package/src/components/swatch-picker/swatch-picker.tsx +40 -5
- package/src/components/tab-view/tab-view.tsx +31 -4
- package/src/components/toggle/toggle.tsx +105 -20
- package/src/index.ts +2 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { type DisclosureGroupSize } from "../disclosure-group/disclosure-group";
|
|
3
|
+
export type AccordionSize = DisclosureGroupSize;
|
|
2
4
|
export interface AccordionItem {
|
|
3
5
|
/** Stable value used to identify the expanded item. */
|
|
4
6
|
value: string;
|
|
@@ -13,7 +15,9 @@ export interface AccordionProps {
|
|
|
13
15
|
value: string | null;
|
|
14
16
|
/** Selecting the open item reports `null`, so every item can collapse. */
|
|
15
17
|
onValueChange: (value: string | null) => void;
|
|
18
|
+
/** Density passed through to each DisclosureGroup. @default "md" */
|
|
19
|
+
size?: AccordionSize;
|
|
16
20
|
className?: string;
|
|
17
21
|
}
|
|
18
22
|
/** A single-open, collapsible accordion with labelled animated content regions. */
|
|
19
|
-
export declare function Accordion({ items, value, onValueChange, className }: AccordionProps): import("react").JSX.Element;
|
|
23
|
+
export declare function Accordion({ items, value, onValueChange, size, className }: AccordionProps): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type TextFieldSize } from "../text-field/text-field";
|
|
1
2
|
type DatePickerProps = {
|
|
2
3
|
value: string;
|
|
3
4
|
onChange: (value: string) => void;
|
|
@@ -10,7 +11,9 @@ type DatePickerProps = {
|
|
|
10
11
|
};
|
|
11
12
|
locale: string;
|
|
12
13
|
max?: string;
|
|
14
|
+
/** Field scale passed to each TextField; matches TextField's xs|sm|md|lg. @default "md" */
|
|
15
|
+
size?: TextFieldSize;
|
|
13
16
|
};
|
|
14
17
|
/** Segmented date entry suited to birthdays, without a native calendar dialog. */
|
|
15
|
-
export declare function DatePicker({ value, onChange, label, labels, locale, max }: DatePickerProps): import("react").JSX.Element;
|
|
18
|
+
export declare function DatePicker({ value, onChange, label, labels, locale, max, size, }: DatePickerProps): import("react").JSX.Element;
|
|
16
19
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type FormEvent, type ReactNode } from "react";
|
|
2
2
|
export type DialogPresentation = "drawer" | "center" | "inline";
|
|
3
3
|
export type DialogTone = "default" | "destructive";
|
|
4
|
+
export type DialogSize = "sm" | "md" | "lg";
|
|
4
5
|
export type DialogProps = {
|
|
5
6
|
open: boolean;
|
|
6
7
|
onOpenChange: (open: boolean) => void;
|
|
@@ -10,6 +11,8 @@ export type DialogProps = {
|
|
|
10
11
|
footer?: ReactNode;
|
|
11
12
|
/** `drawer` (responsive sheet), `center` (floating panel), or `inline`. @default "drawer" */
|
|
12
13
|
presentation?: DialogPresentation;
|
|
14
|
+
/** Title, description, body and footer density. @default "md" */
|
|
15
|
+
size?: DialogSize;
|
|
13
16
|
className?: string;
|
|
14
17
|
onSubmit?: (event: FormEvent<HTMLFormElement>) => void;
|
|
15
18
|
};
|
|
@@ -21,7 +24,7 @@ type ActionsProps = {
|
|
|
21
24
|
tone?: DialogTone;
|
|
22
25
|
onConfirm?: () => void;
|
|
23
26
|
};
|
|
24
|
-
export declare function Dialog({ open, onOpenChange, title, description, children, footer, presentation, className, onSubmit, }: DialogProps): import("react").JSX.Element;
|
|
27
|
+
export declare function Dialog({ open, onOpenChange, title, description, children, footer, presentation, size, className, onSubmit, }: DialogProps): import("react").JSX.Element;
|
|
25
28
|
export declare namespace Dialog {
|
|
26
29
|
export { Actions };
|
|
27
30
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
export type DisclosureGroupSize = "sm" | "md" | "lg";
|
|
2
3
|
export interface DisclosureGroupProps {
|
|
3
4
|
label: string;
|
|
4
5
|
open: boolean;
|
|
@@ -10,6 +11,8 @@ export interface DisclosureGroupProps {
|
|
|
10
11
|
triggerClassName?: string;
|
|
11
12
|
/** Supporting text beneath the title. */
|
|
12
13
|
description?: string;
|
|
14
|
+
/** Trigger, description and content density. @default "md" */
|
|
15
|
+
size?: DisclosureGroupSize;
|
|
13
16
|
}
|
|
14
17
|
/** A controlled, accessible disclosure row with the shared inline reveal. */
|
|
15
|
-
export declare function DisclosureGroup({ label, open, onOpenChange, children, disabled, className, triggerClassName, description, }: DisclosureGroupProps): import("react").JSX.Element;
|
|
18
|
+
export declare function DisclosureGroup({ label, open, onOpenChange, children, disabled, className, triggerClassName, description, size, }: DisclosureGroupProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type HTMLMotionProps } from "motion/react";
|
|
2
|
+
import { type ReactNode, type RefObject } from "react";
|
|
3
|
+
export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
4
|
+
/** Controls whether the overlay is presented. */
|
|
5
|
+
open: boolean;
|
|
6
|
+
/** Called on Escape or outside press. */
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
/** The element whose rect the overlay grows from and collapses back to. */
|
|
9
|
+
sourceRef: RefObject<HTMLElement | null>;
|
|
10
|
+
/** Accessible name (used when there is no labelledBy). */
|
|
11
|
+
label: string;
|
|
12
|
+
/** ID of a visible heading inside the overlay. */
|
|
13
|
+
labelledBy?: string;
|
|
14
|
+
/** Allow tapping the backdrop to close. Escape still calls onClose. @default true */
|
|
15
|
+
dismissOnOutsidePress?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A modal overlay that morphs its glass material from a source element's
|
|
21
|
+
* rect to its own resting rect, then collapses back — Apple's
|
|
22
|
+
* matched-geometry effect. The source is concealed while the overlay is
|
|
23
|
+
* mounted so the material reads as the source itself springing open.
|
|
24
|
+
*
|
|
25
|
+
* Reuses `SheetPortal` (theme inheritance), `useSheetFocus` (focus trap,
|
|
26
|
+
* Escape, modal environment) and the same `menuSurfaceReveal` FLIP
|
|
27
|
+
* primitive that powers anchored `Popover` and `ContextMenu`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function Morph({ open, ...props }: MorphProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disclosure state that keeps the overlay mounted through its exit animation,
|
|
3
|
+
* mirroring `useAnchoredDisclosure`. Pair with a `useRef<HTMLElement>(null)`
|
|
4
|
+
* for the source.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useMorphDisclosure(disabled?: boolean): {
|
|
7
|
+
open: boolean;
|
|
8
|
+
mounted: boolean;
|
|
9
|
+
show: () => void;
|
|
10
|
+
toggle: () => void;
|
|
11
|
+
close: () => void;
|
|
12
|
+
onExited: () => void;
|
|
13
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type RadioGroupSize = "sm" | "md" | "lg";
|
|
1
2
|
export interface RadioGroupOption<T extends string> {
|
|
2
3
|
value: T;
|
|
3
4
|
label: string;
|
|
@@ -12,6 +13,8 @@ export interface RadioGroupProps<T extends string> {
|
|
|
12
13
|
disabled?: boolean;
|
|
13
14
|
name?: string;
|
|
14
15
|
className?: string;
|
|
16
|
+
/** Legend, row, radio glyph and description density. @default "md" */
|
|
17
|
+
size?: RadioGroupSize;
|
|
15
18
|
}
|
|
16
19
|
/** A labelled choice group using the browser's native radio keyboard behavior. */
|
|
17
|
-
export declare function RadioGroup<T extends string>({ value, onChange, label, options, disabled, name, className, }: RadioGroupProps<T>): import("react").JSX.Element;
|
|
20
|
+
export declare function RadioGroup<T extends string>({ value, onChange, label, options, disabled, name, className, size, }: RadioGroupProps<T>): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type SegmentedControlSize = "sm" | "md" | "lg";
|
|
1
2
|
export interface SegmentedControlProps<T extends string> {
|
|
2
3
|
/** Value of the selected segment. */
|
|
3
4
|
value: T;
|
|
@@ -12,6 +13,8 @@ export interface SegmentedControlProps<T extends string> {
|
|
|
12
13
|
label: string;
|
|
13
14
|
/** Custom classes for the control container. */
|
|
14
15
|
className?: string;
|
|
16
|
+
/** Segment padding, label text scale and min-width. @default "md" */
|
|
17
|
+
size?: SegmentedControlSize;
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
20
|
* Mutually exclusive choices with a sliding selection, as on Apple
|
|
@@ -19,4 +22,4 @@ export interface SegmentedControlProps<T extends string> {
|
|
|
19
22
|
* translation, a narrow pane) it becomes a pop-up button instead of
|
|
20
23
|
* squeezing or spilling its text.
|
|
21
24
|
*/
|
|
22
|
-
export declare function SegmentedControl<T extends string>({ value, options, onChange, label, className, }: SegmentedControlProps<T>): import("react").JSX.Element;
|
|
25
|
+
export declare function SegmentedControl<T extends string>({ value, options, onChange, label, className, size, }: SegmentedControlProps<T>): import("react").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
export type SheetPlacement = "adaptive" | "center";
|
|
3
|
+
export type SheetSize = "sm" | "md" | "lg";
|
|
3
4
|
export interface SheetProps {
|
|
4
5
|
/** Controls whether the sheet is presented. */
|
|
5
6
|
open: boolean;
|
|
@@ -13,6 +14,8 @@ export interface SheetProps {
|
|
|
13
14
|
labelledBy?: string;
|
|
14
15
|
/** `adaptive` rises from the bottom on phones; `center` always floats. @default "adaptive" */
|
|
15
16
|
placement?: SheetPlacement;
|
|
17
|
+
/** Outer padding and panel max-width. @default "md" */
|
|
18
|
+
size?: SheetSize;
|
|
16
19
|
className?: string;
|
|
17
20
|
children: ReactNode;
|
|
18
21
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ComponentProps } from "react";
|
|
2
|
-
export
|
|
2
|
+
export type SliderSize = "sm" | "md" | "lg";
|
|
3
|
+
export interface SliderProps extends Omit<ComponentProps<"input">, "type" | "value" | "defaultValue" | "onChange" | "min" | "max" | "step" | "size"> {
|
|
3
4
|
value: number;
|
|
4
5
|
onChange: (value: number) => void;
|
|
5
6
|
label: string;
|
|
@@ -9,6 +10,8 @@ export interface SliderProps extends Omit<ComponentProps<"input">, "type" | "val
|
|
|
9
10
|
description?: string;
|
|
10
11
|
/** Provides a localized, spoken description of the current value. */
|
|
11
12
|
formatValue?: (value: number) => string;
|
|
13
|
+
/** Track height, thumb size and label/description text scale. @default "md" */
|
|
14
|
+
size?: SliderSize;
|
|
12
15
|
}
|
|
13
16
|
/** Native keyboard, touch and pointer behavior with Passui's control treatment. */
|
|
14
|
-
export declare function Slider({ value, onChange, label, min, max, step, description, formatValue, id: suppliedId, className, disabled, ...props }: SliderProps): import("react").JSX.Element;
|
|
17
|
+
export declare function Slider({ value, onChange, label, min, max, step, description, formatValue, size, id: suppliedId, className, disabled, ...props }: SliderProps): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type StepperSize = "sm" | "md" | "lg";
|
|
1
2
|
export interface StepperProps {
|
|
2
3
|
value: number;
|
|
3
4
|
onChange: (value: number) => void;
|
|
@@ -14,6 +15,8 @@ export interface StepperProps {
|
|
|
14
15
|
className?: string;
|
|
15
16
|
/** Formats the displayed value. */
|
|
16
17
|
formatValue?: (value: number) => string;
|
|
18
|
+
/** Label, output and +/- button scale. @default "md" */
|
|
19
|
+
size?: StepperSize;
|
|
17
20
|
}
|
|
18
21
|
/** Discrete increments with disabled boundary actions and a spoken value. */
|
|
19
|
-
export declare function Stepper({ value, onChange, label, labels, min, max, step, disabled, className, formatValue, }: StepperProps): import("react").JSX.Element;
|
|
22
|
+
export declare function Stepper({ value, onChange, label, labels, min, max, step, disabled, className, formatValue, size, }: StepperProps): import("react").JSX.Element;
|
|
@@ -6,6 +6,7 @@ export interface SwatchPickerOption<T extends string> {
|
|
|
6
6
|
/** Color of the selected checkmark. */
|
|
7
7
|
ink: string;
|
|
8
8
|
}
|
|
9
|
+
export type SwatchPickerSize = "sm" | "md" | "lg";
|
|
9
10
|
export interface SwatchPickerProps<T extends string> {
|
|
10
11
|
value: T;
|
|
11
12
|
options: ReadonlyArray<SwatchPickerOption<T>>;
|
|
@@ -14,6 +15,8 @@ export interface SwatchPickerProps<T extends string> {
|
|
|
14
15
|
label: string;
|
|
15
16
|
/** Custom classes for the swatch container. */
|
|
16
17
|
className?: string;
|
|
18
|
+
/** Swatch diameter, check icon and caption scale. @default "md" */
|
|
19
|
+
size?: SwatchPickerSize;
|
|
17
20
|
}
|
|
18
21
|
/** Colour swatches with a check on the chosen one. */
|
|
19
|
-
export declare function SwatchPicker<T extends string>({ value, options, onChange, label, className, }: SwatchPickerProps<T>): import("react").JSX.Element;
|
|
22
|
+
export declare function SwatchPicker<T extends string>({ value, options, onChange, label, className, size, }: SwatchPickerProps<T>): import("react").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
export type TabViewAlign = "start" | "center";
|
|
3
|
+
export type TabViewSize = "sm" | "md" | "lg";
|
|
3
4
|
export interface TabViewProps<Value extends string> {
|
|
4
5
|
/** Available tabs, in keyboard navigation order. */
|
|
5
6
|
options: readonly {
|
|
@@ -18,7 +19,9 @@ export interface TabViewProps<Value extends string> {
|
|
|
18
19
|
label: string;
|
|
19
20
|
/** Renders the selected tab's panel. */
|
|
20
21
|
children: (value: Value) => ReactNode;
|
|
22
|
+
/** Tablist height, tab text scale and underline offset. @default "md" */
|
|
23
|
+
size?: TabViewSize;
|
|
21
24
|
className?: string;
|
|
22
25
|
}
|
|
23
26
|
/** Content tabs with native buttons, roving focus and automatic keyboard activation. */
|
|
24
|
-
export declare function TabView<Value extends string>({ options, defaultValue, value: controlledValue, onValueChange, align, label, children, className, }: TabViewProps<Value>): import("react").JSX.Element | null;
|
|
27
|
+
export declare function TabView<Value extends string>({ options, defaultValue, value: controlledValue, onValueChange, align, label, children, size, className, }: TabViewProps<Value>): import("react").JSX.Element | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type ToggleStyle = "switch" | "checkbox";
|
|
2
|
+
export type ToggleSize = "sm" | "md" | "lg";
|
|
2
3
|
export interface ToggleProps {
|
|
3
4
|
/** Current on or off state. */
|
|
4
5
|
checked: boolean;
|
|
@@ -13,7 +14,9 @@ export interface ToggleProps {
|
|
|
13
14
|
mixed?: boolean;
|
|
14
15
|
/** Keeps the accessible name while hiding the visible label. @default false */
|
|
15
16
|
labelHidden?: boolean;
|
|
17
|
+
/** Switch track size, thumb travel, checkbox box and label text scale. @default "md" */
|
|
18
|
+
size?: ToggleSize;
|
|
16
19
|
/** Custom classes for the toggle container. */
|
|
17
20
|
className?: string;
|
|
18
21
|
}
|
|
19
|
-
export declare function Toggle({ checked, onChange, label, disabled, style, mixed, labelHidden, className, }: ToggleProps): import("react").JSX.Element;
|
|
22
|
+
export declare function Toggle({ checked, onChange, label, disabled, style, mixed, labelHidden, size, className, }: ToggleProps): import("react").JSX.Element;
|
|
@@ -69,3 +69,4 @@ export * from "./components/slosh-slider/slosh-slider";
|
|
|
69
69
|
export * from "./components/wake-slider/wake-slider";
|
|
70
70
|
export * from "./components/one-time-code/one-time-code";
|
|
71
71
|
export * from "./components/aspect-ratio/aspect-ratio";
|
|
72
|
+
export * from "./components/morph";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passui/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"description": "Passui: native React controls, design tokens, presentation and appearance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
"types": "./dist/types/src/components/popover/popover.d.ts",
|
|
63
63
|
"default": "./src/components/popover/popover.tsx"
|
|
64
64
|
},
|
|
65
|
+
"./morph": {
|
|
66
|
+
"types": "./dist/types/src/components/morph/index.d.ts",
|
|
67
|
+
"default": "./src/components/morph/index.ts"
|
|
68
|
+
},
|
|
65
69
|
"./sheet": {
|
|
66
70
|
"types": "./dist/types/src/components/sheet/sheet.d.ts",
|
|
67
71
|
"default": "./src/components/sheet/sheet.tsx"
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { ReactNode } from "react"
|
|
2
|
-
import { DisclosureGroup } from "../disclosure-group/disclosure-group"
|
|
2
|
+
import { DisclosureGroup, type DisclosureGroupSize } from "../disclosure-group/disclosure-group"
|
|
3
3
|
import { cn } from "../../lib/class-names"
|
|
4
4
|
|
|
5
|
+
export type AccordionSize = DisclosureGroupSize
|
|
6
|
+
|
|
7
|
+
const containerRadius: Record<AccordionSize, string> = {
|
|
8
|
+
sm: "rounded-xl",
|
|
9
|
+
md: "rounded-2xl",
|
|
10
|
+
lg: "rounded-3xl",
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export interface AccordionItem {
|
|
6
14
|
/** Stable value used to identify the expanded item. */
|
|
7
15
|
value: string
|
|
@@ -17,15 +25,19 @@ export interface AccordionProps {
|
|
|
17
25
|
value: string | null
|
|
18
26
|
/** Selecting the open item reports `null`, so every item can collapse. */
|
|
19
27
|
onValueChange: (value: string | null) => void
|
|
28
|
+
/** Density passed through to each DisclosureGroup. @default "md" */
|
|
29
|
+
size?: AccordionSize
|
|
20
30
|
className?: string
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/** A single-open, collapsible accordion with labelled animated content regions. */
|
|
24
|
-
export function Accordion({ items, value, onValueChange, className }: AccordionProps) {
|
|
34
|
+
export function Accordion({ items, value, onValueChange, size = "md", className }: AccordionProps) {
|
|
25
35
|
return (
|
|
26
36
|
<div
|
|
27
37
|
className={cn(
|
|
28
|
-
"flex w-full min-w-0 flex-col overflow-hidden
|
|
38
|
+
"flex w-full min-w-0 flex-col overflow-hidden",
|
|
39
|
+
containerRadius[size],
|
|
40
|
+
"border border-separator/60 bg-elevated",
|
|
29
41
|
className
|
|
30
42
|
)}
|
|
31
43
|
>
|
|
@@ -36,6 +48,7 @@ export function Accordion({ items, value, onValueChange, className }: AccordionP
|
|
|
36
48
|
description={item.description}
|
|
37
49
|
open={value === item.value}
|
|
38
50
|
disabled={item.disabled}
|
|
51
|
+
size={size}
|
|
39
52
|
triggerClassName="rounded-none"
|
|
40
53
|
onOpenChange={(open) => onValueChange(open ? item.value : null)}
|
|
41
54
|
className={cn(index > 0 && "border-t border-separator/60")}
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { useState } from "react"
|
|
2
|
-
import { TextField } from "../text-field/text-field"
|
|
2
|
+
import { TextField, type TextFieldSize } from "../text-field/text-field"
|
|
3
|
+
import { cn } from "../../lib/class-names"
|
|
3
4
|
import { dateValue } from "./date-value"
|
|
4
5
|
|
|
6
|
+
const legendText: Record<TextFieldSize, string> = {
|
|
7
|
+
xs: "mb-2 text-caption font-medium",
|
|
8
|
+
sm: "mb-2 text-footnote font-medium",
|
|
9
|
+
md: "mb-3 text-subheadline font-medium",
|
|
10
|
+
lg: "mb-3 text-body font-medium",
|
|
11
|
+
}
|
|
12
|
+
const fieldsetGap: Record<TextFieldSize, string> = {
|
|
13
|
+
xs: "gap-2",
|
|
14
|
+
sm: "gap-2",
|
|
15
|
+
md: "gap-3",
|
|
16
|
+
lg: "gap-4",
|
|
17
|
+
}
|
|
18
|
+
const errorText: Record<TextFieldSize, string> = {
|
|
19
|
+
xs: "text-caption2 text-danger",
|
|
20
|
+
sm: "text-caption text-danger",
|
|
21
|
+
md: "text-footnote text-danger",
|
|
22
|
+
lg: "text-subheadline text-danger",
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
type DatePickerProps = {
|
|
6
26
|
value: string
|
|
7
27
|
onChange: (value: string) => void
|
|
@@ -14,10 +34,20 @@ type DatePickerProps = {
|
|
|
14
34
|
}
|
|
15
35
|
locale: string
|
|
16
36
|
max?: string
|
|
37
|
+
/** Field scale passed to each TextField; matches TextField's xs|sm|md|lg. @default "md" */
|
|
38
|
+
size?: TextFieldSize
|
|
17
39
|
}
|
|
18
40
|
|
|
19
41
|
/** Segmented date entry suited to birthdays, without a native calendar dialog. */
|
|
20
|
-
export function DatePicker({
|
|
42
|
+
export function DatePicker({
|
|
43
|
+
value,
|
|
44
|
+
onChange,
|
|
45
|
+
label,
|
|
46
|
+
labels,
|
|
47
|
+
locale,
|
|
48
|
+
max,
|
|
49
|
+
size = "md",
|
|
50
|
+
}: DatePickerProps) {
|
|
21
51
|
const [year, month, day] = value.split("-")
|
|
22
52
|
const [parts, setParts] = useState({
|
|
23
53
|
year: year ?? "",
|
|
@@ -41,9 +71,9 @@ export function DatePicker({ value, onChange, label, labels, locale, max }: Date
|
|
|
41
71
|
} => part.type === "year" || part.type === "month" || part.type === "day"
|
|
42
72
|
)
|
|
43
73
|
return (
|
|
44
|
-
<fieldset className="flex min-w-0 flex-col
|
|
45
|
-
<legend className=
|
|
46
|
-
<div className="grid grid-cols-3
|
|
74
|
+
<fieldset className={cn("flex min-w-0 flex-col", fieldsetGap[size])}>
|
|
75
|
+
<legend className={legendText[size]}>{label}</legend>
|
|
76
|
+
<div className={cn("grid grid-cols-3", fieldsetGap[size])}>
|
|
47
77
|
{order.map(({ type }) => (
|
|
48
78
|
<TextField
|
|
49
79
|
key={type}
|
|
@@ -52,6 +82,7 @@ export function DatePicker({ value, onChange, label, labels, locale, max }: Date
|
|
|
52
82
|
autoComplete={`bday-${type}`}
|
|
53
83
|
maxLength={type === "year" ? 4 : 2}
|
|
54
84
|
value={parts[type]}
|
|
85
|
+
size={size}
|
|
55
86
|
aria-invalid={invalid || undefined}
|
|
56
87
|
onChange={(event) => {
|
|
57
88
|
const next = {
|
|
@@ -65,7 +96,7 @@ export function DatePicker({ value, onChange, label, labels, locale, max }: Date
|
|
|
65
96
|
))}
|
|
66
97
|
</div>
|
|
67
98
|
{invalid && (
|
|
68
|
-
<p role="status" className=
|
|
99
|
+
<p role="status" className={errorText[size]}>
|
|
69
100
|
{labels.invalid}
|
|
70
101
|
</p>
|
|
71
102
|
)}
|
|
@@ -8,6 +8,28 @@ import { createContext, useContext, useId, type FormEvent, type ReactNode } from
|
|
|
8
8
|
const CloseContext = createContext<() => void>(() => {})
|
|
9
9
|
export type DialogPresentation = "drawer" | "center" | "inline"
|
|
10
10
|
export type DialogTone = "default" | "destructive"
|
|
11
|
+
export type DialogSize = "sm" | "md" | "lg"
|
|
12
|
+
|
|
13
|
+
const titleText: Record<DialogSize, string> = {
|
|
14
|
+
sm: "text-subheadline",
|
|
15
|
+
md: "text-title3",
|
|
16
|
+
lg: "text-title2",
|
|
17
|
+
}
|
|
18
|
+
const descriptionText: Record<DialogSize, string> = {
|
|
19
|
+
sm: "text-caption",
|
|
20
|
+
md: "text-footnote",
|
|
21
|
+
lg: "text-subheadline",
|
|
22
|
+
}
|
|
23
|
+
const bodySpacing: Record<DialogSize, string> = {
|
|
24
|
+
sm: "gap-3 p-4",
|
|
25
|
+
md: "gap-4 p-5",
|
|
26
|
+
lg: "gap-5 p-6",
|
|
27
|
+
}
|
|
28
|
+
const footerSpacing: Record<DialogSize, string> = {
|
|
29
|
+
sm: "pt-2",
|
|
30
|
+
md: "pt-3",
|
|
31
|
+
lg: "pt-4",
|
|
32
|
+
}
|
|
11
33
|
|
|
12
34
|
export type DialogProps = {
|
|
13
35
|
open: boolean
|
|
@@ -18,6 +40,8 @@ export type DialogProps = {
|
|
|
18
40
|
footer?: ReactNode
|
|
19
41
|
/** `drawer` (responsive sheet), `center` (floating panel), or `inline`. @default "drawer" */
|
|
20
42
|
presentation?: DialogPresentation
|
|
43
|
+
/** Title, description, body and footer density. @default "md" */
|
|
44
|
+
size?: DialogSize
|
|
21
45
|
className?: string
|
|
22
46
|
onSubmit?: (event: FormEvent<HTMLFormElement>) => void
|
|
23
47
|
}
|
|
@@ -38,6 +62,7 @@ export function Dialog({
|
|
|
38
62
|
children,
|
|
39
63
|
footer,
|
|
40
64
|
presentation = "drawer",
|
|
65
|
+
size = "md",
|
|
41
66
|
className,
|
|
42
67
|
onSubmit,
|
|
43
68
|
}: DialogProps) {
|
|
@@ -47,13 +72,21 @@ export function Dialog({
|
|
|
47
72
|
const content = (
|
|
48
73
|
<>
|
|
49
74
|
{presentation !== "inline" && (
|
|
50
|
-
<h2 id={titleId} className="
|
|
75
|
+
<h2 id={titleId} className={cn(titleText[size], "font-semibold")}>
|
|
51
76
|
{title}
|
|
52
77
|
</h2>
|
|
53
78
|
)}
|
|
54
|
-
{description &&
|
|
79
|
+
{description && (
|
|
80
|
+
<div className={cn(descriptionText[size], "text-label-secondary")}>
|
|
81
|
+
{description}
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
55
84
|
{children && <div className="flex flex-col gap-4">{children}</div>}
|
|
56
|
-
{footer &&
|
|
85
|
+
{footer && (
|
|
86
|
+
<div className={cn("flex flex-wrap justify-end gap-2", footerSpacing[size])}>
|
|
87
|
+
{footer}
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
57
90
|
</>
|
|
58
91
|
)
|
|
59
92
|
const body = (
|
|
@@ -61,12 +94,22 @@ export function Dialog({
|
|
|
61
94
|
{onSubmit ? (
|
|
62
95
|
<form
|
|
63
96
|
onSubmit={onSubmit}
|
|
64
|
-
className={cn(
|
|
97
|
+
className={cn(
|
|
98
|
+
"flex min-h-0 flex-col overflow-y-auto",
|
|
99
|
+
bodySpacing[size],
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
65
102
|
>
|
|
66
103
|
{content}
|
|
67
104
|
</form>
|
|
68
105
|
) : (
|
|
69
|
-
<div
|
|
106
|
+
<div
|
|
107
|
+
className={cn(
|
|
108
|
+
"flex min-h-0 flex-col overflow-y-auto",
|
|
109
|
+
bodySpacing[size],
|
|
110
|
+
className
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
70
113
|
{content}
|
|
71
114
|
</div>
|
|
72
115
|
)}
|
|
@@ -4,6 +4,29 @@ import { ChevronDownIcon } from "../../icons/icons"
|
|
|
4
4
|
import { cn } from "../../lib/class-names"
|
|
5
5
|
import { InlineDisclosure } from "../inline-disclosure/inline-disclosure"
|
|
6
6
|
|
|
7
|
+
export type DisclosureGroupSize = "sm" | "md" | "lg"
|
|
8
|
+
|
|
9
|
+
const triggerSizes: Record<DisclosureGroupSize, string> = {
|
|
10
|
+
sm: "h-auto min-h-9 w-full rounded-lg px-3 py-2 text-start text-footnote active:scale-100",
|
|
11
|
+
md: "h-auto min-h-11 w-full rounded-xl px-4 py-3 text-start text-subheadline active:scale-100",
|
|
12
|
+
lg: "h-auto min-h-13 w-full rounded-2xl px-5 py-4 text-start text-body active:scale-100",
|
|
13
|
+
}
|
|
14
|
+
const descriptionSizes: Record<DisclosureGroupSize, string> = {
|
|
15
|
+
sm: "text-caption2 leading-caption2 font-normal break-words whitespace-normal text-label-secondary",
|
|
16
|
+
md: "text-footnote leading-footnote font-normal break-words whitespace-normal text-label-secondary",
|
|
17
|
+
lg: "text-subheadline leading-subheadline font-normal break-words whitespace-normal text-label-secondary",
|
|
18
|
+
}
|
|
19
|
+
const contentSizes: Record<DisclosureGroupSize, string> = {
|
|
20
|
+
sm: "min-w-0 px-3 pb-3 text-footnote leading-footnote text-label-secondary [overflow-wrap:anywhere]",
|
|
21
|
+
md: "min-w-0 px-4 pb-4 text-subheadline leading-subheadline text-label-secondary [overflow-wrap:anywhere]",
|
|
22
|
+
lg: "min-w-0 px-5 pb-5 text-body leading-body text-label-secondary [overflow-wrap:anywhere]",
|
|
23
|
+
}
|
|
24
|
+
const chevronSizes: Record<DisclosureGroupSize, number> = {
|
|
25
|
+
sm: 15,
|
|
26
|
+
md: 17,
|
|
27
|
+
lg: 19,
|
|
28
|
+
}
|
|
29
|
+
|
|
7
30
|
export interface DisclosureGroupProps {
|
|
8
31
|
label: string
|
|
9
32
|
open: boolean
|
|
@@ -15,6 +38,8 @@ export interface DisclosureGroupProps {
|
|
|
15
38
|
triggerClassName?: string
|
|
16
39
|
/** Supporting text beneath the title. */
|
|
17
40
|
description?: string
|
|
41
|
+
/** Trigger, description and content density. @default "md" */
|
|
42
|
+
size?: DisclosureGroupSize
|
|
18
43
|
}
|
|
19
44
|
|
|
20
45
|
/** A controlled, accessible disclosure row with the shared inline reveal. */
|
|
@@ -27,6 +52,7 @@ export function DisclosureGroup({
|
|
|
27
52
|
className,
|
|
28
53
|
triggerClassName,
|
|
29
54
|
description,
|
|
55
|
+
size = "md",
|
|
30
56
|
}: DisclosureGroupProps) {
|
|
31
57
|
const id = useId()
|
|
32
58
|
|
|
@@ -43,8 +69,8 @@ export function DisclosureGroup({
|
|
|
43
69
|
disabled={disabled}
|
|
44
70
|
onClick={() => onOpenChange(!open)}
|
|
45
71
|
className={cn(
|
|
46
|
-
|
|
47
|
-
"
|
|
72
|
+
triggerSizes[size],
|
|
73
|
+
"font-normal text-label hover:bg-fill/60 focus-visible:ring-inset focus-visible:ring-offset-0",
|
|
48
74
|
"focus-visible:outline-accent-text disabled:cursor-not-allowed disabled:opacity-40",
|
|
49
75
|
triggerClassName
|
|
50
76
|
)}
|
|
@@ -52,13 +78,11 @@ export function DisclosureGroup({
|
|
|
52
78
|
<span className="flex min-w-0 flex-col gap-0.5">
|
|
53
79
|
<span className="font-medium break-words whitespace-normal">{label}</span>
|
|
54
80
|
{description && (
|
|
55
|
-
<span className=
|
|
56
|
-
{description}
|
|
57
|
-
</span>
|
|
81
|
+
<span className={descriptionSizes[size]}>{description}</span>
|
|
58
82
|
)}
|
|
59
83
|
</span>
|
|
60
84
|
<ChevronDownIcon
|
|
61
|
-
size={
|
|
85
|
+
size={chevronSizes[size]}
|
|
62
86
|
aria-hidden="true"
|
|
63
87
|
className={cn(
|
|
64
88
|
"shrink-0 text-label-tertiary transition-transform duration-normal motion-reduce:transition-none",
|
|
@@ -69,9 +93,7 @@ export function DisclosureGroup({
|
|
|
69
93
|
</h3>
|
|
70
94
|
<section id={id} aria-labelledby={`${id}-trigger`} aria-hidden={!open} inert={!open}>
|
|
71
95
|
<InlineDisclosure open={open}>
|
|
72
|
-
<div className=
|
|
73
|
-
{children}
|
|
74
|
-
</div>
|
|
96
|
+
<div className={contentSizes[size]}>{children}</div>
|
|
75
97
|
</InlineDisclosure>
|
|
76
98
|
</section>
|
|
77
99
|
</div>
|