@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.
- package/dist/types/src/components/compact-date-picker/compact-date-picker.d.ts +4 -1
- package/dist/types/src/components/compact-time-picker/compact-time-picker.d.ts +4 -1
- package/dist/types/src/components/search-field/search-field.d.ts +4 -1
- package/dist/types/src/components/tab-bar/tab-bar.d.ts +4 -1
- package/dist/types/src/components/table/table.d.ts +4 -1
- package/dist/types/src/components/text-editor/text-editor.d.ts +8 -1
- package/dist/types/src/components/text-field/text-field.d.ts +8 -0
- package/package.json +2 -2
- package/src/components/compact-date-picker/compact-date-picker.tsx +5 -2
- package/src/components/compact-time-picker/compact-time-picker.tsx +5 -2
- package/src/components/search-field/search-field.tsx +19 -9
- package/src/components/tab-bar/tab-bar.tsx +37 -4
- package/src/components/table/table.tsx +43 -12
- package/src/components/text-editor/text-editor.tsx +31 -3
- package/src/components/text-field/text-field.tsx +2 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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={
|
|
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
|
|
101
|
-
|
|
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
|
|
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={
|
|
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
|
-
|
|
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
|
-
<
|
|
9
|
-
data-slot="table"
|
|
10
|
-
|
|
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
|
-
|
|
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(
|
|
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 {
|
|
3
|
+
import { fieldSize, type TextFieldSize } from "../text-field/text-field"
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
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 {
|