@passui/react 0.1.37 → 0.1.39

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.
@@ -1,3 +1,4 @@
1
+ import { type PickerSize } from "../picker/picker-trigger";
1
2
  export interface CompactDatePickerProps {
2
3
  /** `YYYY-MM-DD`, or "" when no date is chosen. */
3
4
  value: string;
@@ -18,10 +19,12 @@ export interface CompactDatePickerProps {
18
19
  className?: string;
19
20
  /** Prevents opening the date panel. @default false */
20
21
  disabled?: boolean;
22
+ /** Trigger button density; matches Picker's sm | md | lg. @default "md" */
23
+ size?: PickerSize;
21
24
  }
22
25
  /**
23
26
  * A date as a compact button that opens a month calendar, like SwiftUI's
24
27
  * compact DatePicker: for dates on a calendar, such as an event or an
25
28
  * invoice. For dates people know by heart, like a birthday, use DatePicker.
26
29
  */
27
- export declare function CompactDatePicker({ value, onChange, label, locale, min, max, placeholder, labels, className, disabled, }: CompactDatePickerProps): import("react").JSX.Element;
30
+ export declare function CompactDatePicker({ value, onChange, label, locale, min, max, placeholder, labels, className, disabled, size, }: CompactDatePickerProps): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { type PickerSize } from "../picker/picker-trigger";
1
2
  export interface CompactTimePickerProps {
2
3
  /** `HH:MM` on a 24 hour clock, or "" when no time is chosen. */
3
4
  value: string;
@@ -17,10 +18,12 @@ export interface CompactTimePickerProps {
17
18
  className?: string;
18
19
  /** Prevents opening the time panel. @default false */
19
20
  disabled?: boolean;
21
+ /** Trigger button density; matches Picker's sm | md | lg. @default "md" */
22
+ size?: PickerSize;
20
23
  }
21
24
  /**
22
25
  * A time as a compact button that opens columns of hours and minutes,
23
26
  * like SwiftUI's compact DatePicker for times, on the clock the locale
24
27
  * reads (12 hours with AM and PM, or 24).
25
28
  */
26
- export declare function CompactTimePicker({ value, onChange, label, locale, minuteStep, placeholder, labels, className, disabled, }: CompactTimePickerProps): import("react").JSX.Element;
29
+ export declare function CompactTimePicker({ value, onChange, label, locale, minuteStep, placeholder, labels, className, disabled, size, }: CompactTimePickerProps): import("react").JSX.Element;
@@ -1,6 +1,9 @@
1
1
  import { type InputHTMLAttributes } from "react";
2
- export interface SearchFieldProps extends InputHTMLAttributes<HTMLInputElement> {
2
+ export type SearchFieldSize = "sm" | "md" | "lg";
3
+ export interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
3
4
  /** Expand a single pill from its search icon on focus. @default false */
4
5
  collapsible?: boolean;
6
+ /** Pill height, icon size and text scale (non-collapsible variant). @default "md" */
7
+ size?: SearchFieldSize;
5
8
  }
6
9
  export declare const SearchField: import("react").ForwardRefExoticComponent<SearchFieldProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -12,6 +12,7 @@ export interface TabBarLinkProps {
12
12
  children: ReactNode;
13
13
  }
14
14
  export type TabBarMinimizeBehavior = "platform" | "scroll" | "never";
15
+ export type TabBarSize = "sm" | "md" | "lg";
15
16
  export interface TabBarProps<Tab extends TabBarTab> {
16
17
  /** `platform` follows the platform, `scroll` always minimizes, `never` stays expanded. @default "platform" */
17
18
  minimizeBehavior?: TabBarMinimizeBehavior;
@@ -26,6 +27,8 @@ export interface TabBarProps<Tab extends TabBarTab> {
26
27
  };
27
28
  /** Accessible name for the navigation. */
28
29
  label: string;
30
+ /** Tab height, label text, minimized width and search button diameter. @default "md" */
31
+ size?: TabBarSize;
29
32
  className?: string;
30
33
  }
31
34
  /**
@@ -37,4 +40,4 @@ export interface TabBarProps<Tab extends TabBarTab> {
37
40
  *
38
41
  * Links stay the app's own (`renderLink`), so the bar works with any router.
39
42
  */
40
- export declare function TabBar<Tab extends TabBarTab>({ tabs, active, renderLink, search, label, className, minimizeBehavior, }: TabBarProps<Tab>): import("react").JSX.Element;
43
+ export declare function TabBar<Tab extends TabBarTab>({ tabs, active, renderLink, search, label, className, size, minimizeBehavior, }: TabBarProps<Tab>): import("react").JSX.Element;
@@ -1,5 +1,8 @@
1
1
  import type * as React from "react";
2
- declare function Table({ className, ...props }: React.ComponentProps<"table">): React.JSX.Element;
2
+ export type TableSize = "sm" | "md" | "lg";
3
+ declare function Table({ className, size, ...props }: React.ComponentProps<"table"> & {
4
+ /** Row height, cell padding and text scale. @default "md" */ size?: TableSize;
5
+ }): React.JSX.Element;
3
6
  declare function TableHeader({ className, ...props }: React.ComponentProps<"thead">): React.JSX.Element;
4
7
  declare function TableBody({ className, ...props }: React.ComponentProps<"tbody">): React.JSX.Element;
5
8
  declare function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">): React.JSX.Element;
@@ -1,2 +1,9 @@
1
1
  import type { ComponentProps } from "react";
2
- export declare function TextEditor({ className, ...props }: ComponentProps<"textarea">): import("react").JSX.Element;
2
+ import { type TextFieldSize } from "../text-field/text-field";
3
+ export type TextEditorSize = TextFieldSize;
4
+ export interface TextEditorProps extends Omit<ComponentProps<"textarea">, "size"> {
5
+ /** Field surface scale; matches TextField's xs | sm | md | lg. @default "md" */
6
+ size?: TextEditorSize;
7
+ }
8
+ /** A multi-line text field that reuses TextField's shared surface treatment. */
9
+ export declare function TextEditor({ className, size, ...props }: TextEditorProps): import("react").JSX.Element;
@@ -3,6 +3,14 @@ import { type ComponentProps, type ReactNode } from "react";
3
3
  export declare const textControlClasses: string;
4
4
  /** Size scale shared across TextField, SecureField, and form fields. */
5
5
  export type TextFieldSize = "xs" | "sm" | "md" | "lg";
6
+ export interface FieldSize {
7
+ height: string;
8
+ floatingHeight: string;
9
+ padding: string;
10
+ text: string;
11
+ radius: string;
12
+ }
13
+ export declare function fieldSize(size: TextFieldSize): FieldSize;
6
14
  export type TextFieldProps = Omit<ComponentProps<"input">, "size"> & {
7
15
  label: string;
8
16
  description?: ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passui/react",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "Passui: native React controls, design tokens, presentation and appearance.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -300,7 +300,7 @@
300
300
  "clsx": "^2.1.1",
301
301
  "tailwind-merge": "^3.7.0",
302
302
  "tw-animate-css": "^1.4.0",
303
- "@passui/tokens": "^0.1.1"
303
+ "@passui/tokens": "^0.1.3"
304
304
  },
305
305
  "types": "./dist/types/src/index.d.ts",
306
306
  "sideEffects": [
@@ -6,7 +6,7 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../../icons/ico
6
6
  import { cn } from "../../lib/class-names"
7
7
  import { haptic } from "../../lib/haptics"
8
8
  import { IconButton } from "../button/icon-button"
9
- import { pickerTriggerStyle } from "../picker/picker-trigger"
9
+ import { pickerTriggerStyle, type PickerSize } from "../picker/picker-trigger"
10
10
  import {
11
11
  addDays,
12
12
  addMonths,
@@ -38,6 +38,8 @@ export interface CompactDatePickerProps {
38
38
  className?: string
39
39
  /** Prevents opening the date panel. @default false */
40
40
  disabled?: boolean
41
+ /** Trigger button density; matches Picker's sm | md | lg. @default "md" */
42
+ size?: PickerSize
41
43
  }
42
44
 
43
45
  const WIDTH = 308
@@ -69,6 +71,7 @@ export function CompactDatePicker({
69
71
  labels,
70
72
  className,
71
73
  disabled = false,
74
+ size = "md",
72
75
  }: CompactDatePickerProps) {
73
76
  const panel = useAnchoredDisclosure(disabled)
74
77
  const { open } = panel
@@ -98,7 +101,7 @@ export function CompactDatePicker({
98
101
  aria-controls={open && !disabled ? calendarId : undefined}
99
102
  disabled={disabled}
100
103
  onClick={() => panel.toggle()}
101
- className={pickerTriggerStyle(className)}
104
+ className={pickerTriggerStyle(className, size)}
102
105
  >
103
106
  <span
104
107
  className={cn(
@@ -5,7 +5,7 @@ import { createPortal } from "react-dom"
5
5
  import { ClockIcon } from "../../icons/icons"
6
6
  import { cn } from "../../lib/class-names"
7
7
  import { haptic } from "../../lib/haptics"
8
- import { pickerTriggerStyle } from "../picker/picker-trigger"
8
+ import { pickerTriggerStyle, type PickerSize } from "../picker/picker-trigger"
9
9
  import {
10
10
  formatTime,
11
11
  fromTwelveHours,
@@ -31,6 +31,8 @@ export interface CompactTimePickerProps {
31
31
  className?: string
32
32
  /** Prevents opening the time panel. @default false */
33
33
  disabled?: boolean
34
+ /** Trigger button density; matches Picker's sm | md | lg. @default "md" */
35
+ size?: PickerSize
34
36
  }
35
37
 
36
38
  /**
@@ -48,6 +50,7 @@ export function CompactTimePicker({
48
50
  labels,
49
51
  className,
50
52
  disabled = false,
53
+ size = "md",
51
54
  }: CompactTimePickerProps) {
52
55
  const panel = useAnchoredDisclosure(disabled)
53
56
  const { open } = panel
@@ -73,7 +76,7 @@ export function CompactTimePicker({
73
76
  aria-controls={open && !disabled ? panelId : undefined}
74
77
  disabled={disabled}
75
78
  onClick={() => panel.toggle()}
76
- className={pickerTriggerStyle(className)}
79
+ className={pickerTriggerStyle(className, size)}
77
80
  >
78
81
  <span
79
82
  className={cn(
@@ -3,13 +3,28 @@ import { SearchIcon } from "../../icons/icons"
3
3
  import { cn } from "../../lib/class-names"
4
4
  import { ExpandingSearchField } from "./expanding-search-field"
5
5
 
6
- export interface SearchFieldProps extends InputHTMLAttributes<HTMLInputElement> {
6
+ export type SearchFieldSize = "sm" | "md" | "lg"
7
+
8
+ const pillSizes: Record<SearchFieldSize, string> = {
9
+ sm: "control-field flex items-center gap-1.5 rounded-full px-2.5 py-1 text-caption text-label-tertiary",
10
+ md: "control-field flex items-center gap-2 rounded-full px-3 py-2 text-subheadline text-label-tertiary",
11
+ lg: "control-field flex items-center gap-2.5 rounded-full px-4 py-2.5 text-body text-label-tertiary",
12
+ }
13
+ const iconSizes: Record<SearchFieldSize, number> = {
14
+ sm: 13,
15
+ md: 15,
16
+ lg: 17,
17
+ }
18
+
19
+ export interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
7
20
  /** Expand a single pill from its search icon on focus. @default false */
8
21
  collapsible?: boolean
22
+ /** Pill height, icon size and text scale (non-collapsible variant). @default "md" */
23
+ size?: SearchFieldSize
9
24
  }
10
25
 
11
26
  export const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(function SearchField(
12
- { className, placeholder = "Search", collapsible = false, ...props },
27
+ { className, placeholder = "Search", collapsible = false, size = "md", ...props },
13
28
  ref
14
29
  ) {
15
30
  if (collapsible)
@@ -25,14 +40,9 @@ export const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(functi
25
40
  ? undefined
26
41
  : (props["aria-label"] ?? placeholder)
27
42
  return (
28
- <label
29
- className={cn(
30
- "control-field flex items-center gap-2 rounded-full px-3 py-2 text-subheadline text-label-tertiary",
31
- className
32
- )}
33
- >
43
+ <label className={cn(pillSizes[size], className)}>
34
44
  <span aria-hidden="true" className="shrink-0">
35
- <SearchIcon size={15} strokeWidth={2} />
45
+ <SearchIcon size={iconSizes[size]} strokeWidth={2} />
36
46
  </span>
37
47
  <input
38
48
  ref={ref}
@@ -20,6 +20,33 @@ export interface TabBarLinkProps {
20
20
  }
21
21
 
22
22
  export type TabBarMinimizeBehavior = "platform" | "scroll" | "never"
23
+ export type TabBarSize = "sm" | "md" | "lg"
24
+
25
+ const tabHeights: Record<TabBarSize, string> = {
26
+ sm: "h-9",
27
+ md: "h-11",
28
+ lg: "h-13",
29
+ }
30
+ const tabMinimizedWidths: Record<TabBarSize, string> = {
31
+ sm: "w-9",
32
+ md: "w-11",
33
+ lg: "w-13",
34
+ }
35
+ const tabText: Record<TabBarSize, string> = {
36
+ sm: "text-caption",
37
+ md: "text-caption2",
38
+ lg: "text-subheadline",
39
+ }
40
+ const searchButtonSizes: Record<TabBarSize, string> = {
41
+ sm: "size-10",
42
+ md: "size-12",
43
+ lg: "size-14",
44
+ }
45
+ const searchIconSizes: Record<TabBarSize, number> = {
46
+ sm: 17,
47
+ md: 19,
48
+ lg: 21,
49
+ }
23
50
 
24
51
  export interface TabBarProps<Tab extends TabBarTab> {
25
52
  /** `platform` follows the platform, `scroll` always minimizes, `never` stays expanded. @default "platform" */
@@ -32,6 +59,8 @@ export interface TabBarProps<Tab extends TabBarTab> {
32
59
  search?: { label: string; onPress: () => void }
33
60
  /** Accessible name for the navigation. */
34
61
  label: string
62
+ /** Tab height, label text, minimized width and search button diameter. @default "md" */
63
+ size?: TabBarSize
35
64
  className?: string
36
65
  }
37
66
 
@@ -51,6 +80,7 @@ export function TabBar<Tab extends TabBarTab>({
51
80
  search,
52
81
  label,
53
82
  className,
83
+ size = "md",
54
84
  minimizeBehavior = "platform",
55
85
  }: TabBarProps<Tab>) {
56
86
  // Scopes the sliding selection to this bar, so two bars on one
@@ -97,8 +127,10 @@ export function TabBar<Tab extends TabBarTab>({
97
127
  // Switching tabs is silent, as on Apple and Android: no haptic.
98
128
  onClick: () => {},
99
129
  className: cn(
100
- "relative isolate flex h-11 min-w-0 flex-col items-center justify-center gap-0.5 rounded-full font-semibold text-caption2 tracking-[0.01em] no-underline transition-[color,transform] duration-fast active:scale-[0.94]",
101
- minimized ? "w-11" : "flex-1 px-1",
130
+ "relative isolate flex min-w-0 flex-col items-center justify-center gap-0.5 rounded-full font-semibold tracking-[0.01em] no-underline transition-[color,transform] duration-fast active:scale-[0.94]",
131
+ tabHeights[size],
132
+ tabText[size],
133
+ minimized ? tabMinimizedWidths[size] : "flex-1 px-1",
102
134
  current ? "text-accent-text" : "text-label-secondary"
103
135
  ),
104
136
  children: (
@@ -133,10 +165,11 @@ export function TabBar<Tab extends TabBarTab>({
133
165
  aria-label={search.label}
134
166
  onClick={search.onPress}
135
167
  className={cn(
136
- "glass ms-auto flex size-12 shrink-0 items-center justify-center rounded-full text-label transition-transform duration-fast active:scale-[0.92]"
168
+ "glass ms-auto flex shrink-0 items-center justify-center rounded-full text-label transition-transform duration-fast active:scale-[0.92]",
169
+ searchButtonSizes[size]
137
170
  )}
138
171
  >
139
- <SearchIcon size={19} strokeWidth={2} />
172
+ <SearchIcon size={searchIconSizes[size]} strokeWidth={2} />
140
173
  </button>
141
174
  )}
142
175
  </nav>
@@ -1,15 +1,46 @@
1
+ import { createContext, useContext } from "react"
1
2
  import type * as React from "react"
2
3
 
3
4
  import { cn } from "../../lib/class-names"
4
5
 
5
- function Table({ className, ...props }: React.ComponentProps<"table">) {
6
+ export type TableSize = "sm" | "md" | "lg"
7
+
8
+ const tableText: Record<TableSize, string> = {
9
+ sm: "text-footnote text-label",
10
+ md: "text-subheadline text-label",
11
+ lg: "text-body text-label",
12
+ }
13
+ const headCell: Record<TableSize, string> = {
14
+ sm: "h-8 px-2 text-start align-middle text-caption2 font-semibold whitespace-nowrap",
15
+ md: "h-10 px-3 text-start align-middle text-footnote font-semibold whitespace-nowrap",
16
+ lg: "h-12 px-4 text-start align-middle text-subheadline font-semibold whitespace-nowrap",
17
+ }
18
+ const bodyCell: Record<TableSize, string> = {
19
+ sm: "px-2 py-1.5 align-middle whitespace-nowrap",
20
+ md: "px-3 py-2.5 align-middle whitespace-nowrap",
21
+ lg: "px-4 py-3.5 align-middle whitespace-nowrap",
22
+ }
23
+ const captionText: Record<TableSize, string> = {
24
+ sm: "mt-3 text-caption2 text-label-secondary",
25
+ md: "mt-4 text-footnote text-label-secondary",
26
+ lg: "mt-5 text-subheadline text-label-secondary",
27
+ }
28
+
29
+ const TableSizeContext = createContext<TableSize>("md")
30
+ const useTableSize = () => useContext(TableSizeContext)
31
+
32
+ function Table({
33
+ className,
34
+ size = "md",
35
+ ...props
36
+ }: React.ComponentProps<"table"> & {
37
+ /** Row height, cell padding and text scale. @default "md" */ size?: TableSize
38
+ }) {
6
39
  return (
7
40
  <div data-slot="table-container" className="relative w-full overflow-x-auto">
8
- <table
9
- data-slot="table"
10
- className={cn("w-full caption-bottom text-subheadline text-label", className)}
11
- {...props}
12
- />
41
+ <TableSizeContext value={size}>
42
+ <table data-slot="table" className={cn(tableText[size], className)} {...props} />
43
+ </TableSizeContext>
13
44
  </div>
14
45
  )
15
46
  }
@@ -62,11 +93,12 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
62
93
  }
63
94
 
64
95
  function TableHead({ className, ...props }: React.ComponentProps<"th">) {
96
+ const size = useTableSize()
65
97
  return (
66
98
  <th
67
99
  data-slot="table-head"
68
100
  className={cn(
69
- "h-10 px-3 text-start align-middle text-footnote font-semibold whitespace-nowrap",
101
+ headCell[size],
70
102
  "text-label-secondary has-[[role=checkbox]]:pe-0",
71
103
  className
72
104
  )}
@@ -76,23 +108,22 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
76
108
  }
77
109
 
78
110
  function TableCell({ className, ...props }: React.ComponentProps<"td">) {
111
+ const size = useTableSize()
79
112
  return (
80
113
  <td
81
114
  data-slot="table-cell"
82
- className={cn(
83
- "px-3 py-2.5 align-middle whitespace-nowrap has-[[role=checkbox]]:pe-0",
84
- className
85
- )}
115
+ className={cn(bodyCell[size], "has-[[role=checkbox]]:pe-0", className)}
86
116
  {...props}
87
117
  />
88
118
  )
89
119
  }
90
120
 
91
121
  function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {
122
+ const size = useTableSize()
92
123
  return (
93
124
  <caption
94
125
  data-slot="table-caption"
95
- className={cn("mt-4 text-footnote text-label-secondary", className)}
126
+ className={cn(captionText[size], className)}
96
127
  {...props}
97
128
  />
98
129
  )
@@ -1,9 +1,37 @@
1
1
  import type { ComponentProps } from "react"
2
2
  import { cn } from "../../lib/class-names"
3
- import { textControlClasses } from "../text-field/text-field"
3
+ import { fieldSize, type TextFieldSize } from "../text-field/text-field"
4
4
 
5
- export function TextEditor({ className, ...props }: ComponentProps<"textarea">) {
5
+ const minHeights: Record<TextFieldSize, string> = {
6
+ xs: "min-h-24",
7
+ sm: "min-h-24",
8
+ md: "min-h-28",
9
+ lg: "min-h-32",
10
+ }
11
+
12
+ export type TextEditorSize = TextFieldSize
13
+
14
+ export interface TextEditorProps extends Omit<ComponentProps<"textarea">, "size"> {
15
+ /** Field surface scale; matches TextField's xs | sm | md | lg. @default "md" */
16
+ size?: TextEditorSize
17
+ }
18
+
19
+ /** A multi-line text field that reuses TextField's shared surface treatment. */
20
+ export function TextEditor({ className, size = "md", ...props }: TextEditorProps) {
21
+ const surface = fieldSize(size)
6
22
  return (
7
- <textarea {...props} className={cn(textControlClasses, "min-h-28 resize-y", className)} />
23
+ <textarea
24
+ {...props}
25
+ className={cn(
26
+ "w-full min-w-0 resize-y border border-separator bg-elevated text-label placeholder:text-label-tertiary",
27
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-text",
28
+ "disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger",
29
+ surface.radius,
30
+ surface.padding,
31
+ surface.text,
32
+ minHeights[size],
33
+ className
34
+ )}
35
+ />
8
36
  )
9
37
  }
@@ -15,7 +15,7 @@ const fieldIconClasses = "flex shrink-0 items-center text-label-secondary"
15
15
  /** Size scale shared across TextField, SecureField, and form fields. */
16
16
  export type TextFieldSize = "xs" | "sm" | "md" | "lg"
17
17
 
18
- interface FieldSize {
18
+ export interface FieldSize {
19
19
  height: string
20
20
  floatingHeight: string
21
21
  padding: string
@@ -23,7 +23,7 @@ interface FieldSize {
23
23
  radius: string
24
24
  }
25
25
 
26
- function fieldSize(size: TextFieldSize): FieldSize {
26
+ export function fieldSize(size: TextFieldSize): FieldSize {
27
27
  switch (size) {
28
28
  case "xs":
29
29
  return {