@nim.zone/ui 0.6.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 +21 -0
- package/README.md +681 -0
- package/dist/components/admin-shell.d.ts +121 -0
- package/dist/components/app-shell.d.ts +23 -0
- package/dist/components/auth-screen.d.ts +33 -0
- package/dist/components/avatar.d.ts +10 -0
- package/dist/components/badge.d.ts +14 -0
- package/dist/components/banner.d.ts +11 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/button.d.ts +21 -0
- package/dist/components/card.d.ts +15 -0
- package/dist/components/chat-composer.d.ts +52 -0
- package/dist/components/chat.d.ts +77 -0
- package/dist/components/checkout.d.ts +79 -0
- package/dist/components/choice.d.ts +15 -0
- package/dist/components/combobox.d.ts +31 -0
- package/dist/components/date-field.d.ts +58 -0
- package/dist/components/dialog.d.ts +20 -0
- package/dist/components/empty-state.d.ts +9 -0
- package/dist/components/feedback.d.ts +18 -0
- package/dist/components/field.d.ts +47 -0
- package/dist/components/icon-button.d.ts +12 -0
- package/dist/components/icon.d.ts +72 -0
- package/dist/components/layout.d.ts +13 -0
- package/dist/components/list.d.ts +16 -0
- package/dist/components/menu.d.ts +56 -0
- package/dist/components/onboarding.d.ts +50 -0
- package/dist/components/otp-input.d.ts +29 -0
- package/dist/components/pagination.d.ts +12 -0
- package/dist/components/password-field.d.ts +33 -0
- package/dist/components/phone-field.d.ts +44 -0
- package/dist/components/plan-card.d.ts +45 -0
- package/dist/components/plan-picker.d.ts +52 -0
- package/dist/components/profile-header.d.ts +47 -0
- package/dist/components/profile-screen.d.ts +44 -0
- package/dist/components/resource-meter.d.ts +22 -0
- package/dist/components/section-header.d.ts +8 -0
- package/dist/components/segmented.d.ts +18 -0
- package/dist/components/sheet.d.ts +18 -0
- package/dist/components/sign-in-flow.d.ts +60 -0
- package/dist/components/slider.d.ts +10 -0
- package/dist/components/stat.d.ts +10 -0
- package/dist/components/stepper.d.ts +17 -0
- package/dist/components/tab-bar.d.ts +40 -0
- package/dist/components/table.d.ts +33 -0
- package/dist/components/tabs.d.ts +23 -0
- package/dist/components/task-progress.d.ts +38 -0
- package/dist/components/theme.d.ts +49 -0
- package/dist/components/toast.d.ts +17 -0
- package/dist/components/tooltip.d.ts +18 -0
- package/dist/components/typography.d.ts +18 -0
- package/dist/components/wizard.d.ts +70 -0
- package/dist/index.d.ts +104 -0
- package/dist/lib/calendars.d.ts +81 -0
- package/dist/lib/cn.d.ts +7 -0
- package/dist/lib/countries.d.ts +33 -0
- package/dist/lib/use-anchor.d.ts +22 -0
- package/dist/nim.css +1 -0
- package/dist/nim.js +3266 -0
- package/package.json +83 -0
- package/src/components/admin-shell.tsx +338 -0
- package/src/components/app-shell.tsx +36 -0
- package/src/components/auth-screen.tsx +71 -0
- package/src/components/avatar.tsx +32 -0
- package/src/components/badge.tsx +44 -0
- package/src/components/banner.tsx +47 -0
- package/src/components/breadcrumb.tsx +45 -0
- package/src/components/button.tsx +64 -0
- package/src/components/card.tsx +46 -0
- package/src/components/chat-composer.tsx +331 -0
- package/src/components/chat.tsx +352 -0
- package/src/components/checkout.tsx +173 -0
- package/src/components/choice.tsx +46 -0
- package/src/components/combobox.tsx +157 -0
- package/src/components/date-field.tsx +394 -0
- package/src/components/dialog.tsx +77 -0
- package/src/components/empty-state.tsx +23 -0
- package/src/components/feedback.tsx +61 -0
- package/src/components/field.tsx +177 -0
- package/src/components/icon-button.tsx +35 -0
- package/src/components/icon.tsx +167 -0
- package/src/components/layout.tsx +38 -0
- package/src/components/list.tsx +78 -0
- package/src/components/menu.tsx +172 -0
- package/src/components/onboarding.tsx +157 -0
- package/src/components/otp-input.tsx +144 -0
- package/src/components/pagination.tsx +86 -0
- package/src/components/password-field.tsx +112 -0
- package/src/components/phone-field.tsx +242 -0
- package/src/components/plan-card.tsx +124 -0
- package/src/components/plan-picker.tsx +144 -0
- package/src/components/profile-header.tsx +134 -0
- package/src/components/profile-screen.tsx +104 -0
- package/src/components/resource-meter.tsx +60 -0
- package/src/components/section-header.tsx +31 -0
- package/src/components/segmented.tsx +53 -0
- package/src/components/sheet.tsx +74 -0
- package/src/components/sign-in-flow.tsx +322 -0
- package/src/components/slider.tsx +50 -0
- package/src/components/stat.tsx +30 -0
- package/src/components/stepper.tsx +77 -0
- package/src/components/tab-bar.tsx +82 -0
- package/src/components/table.tsx +81 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/task-progress.tsx +111 -0
- package/src/components/theme.tsx +112 -0
- package/src/components/toast.tsx +87 -0
- package/src/components/tooltip.tsx +30 -0
- package/src/components/typography.tsx +65 -0
- package/src/components/wizard.tsx +192 -0
- package/src/index.ts +216 -0
- package/src/lib/calendars.ts +227 -0
- package/src/lib/cn.ts +8 -0
- package/src/lib/countries.ts +110 -0
- package/src/lib/use-anchor.ts +92 -0
- package/src/theme/colorways/coral.css +62 -0
- package/src/theme/colorways/oxblood.css +23 -0
- package/src/theme/colorways/paper.css +68 -0
- package/src/theme/colorways/teal.css +56 -0
- package/src/theme/colorways/vermilion.css +21 -0
- package/src/theme/components.css +4606 -0
- package/src/theme/contract.css +204 -0
- package/src/theme/fonts.css +67 -0
- package/src/theme/index.css +33 -0
- package/src/theme/persian.css +47 -0
- package/src/theme/reset.css +118 -0
- package/src/theme/styles/ledger.css +106 -0
- package/src/theme/styles/vlora.css +91 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { IconName } from './icon';
|
|
3
|
+
export interface AdminNavItem {
|
|
4
|
+
href?: string;
|
|
5
|
+
icon?: IconName;
|
|
6
|
+
key: string;
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
onSelect?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface AdminNavGroup {
|
|
11
|
+
icon?: IconName;
|
|
12
|
+
items: AdminNavItem[];
|
|
13
|
+
key: string;
|
|
14
|
+
label: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export interface AdminShellProps {
|
|
17
|
+
/** Brand mark at the top of the sidebar. */
|
|
18
|
+
brand?: ReactNode;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
/** Under the nav: session state, build version — the things an operator
|
|
22
|
+
checks before believing a screen. */
|
|
23
|
+
sidebarFooter?: ReactNode;
|
|
24
|
+
groups: AdminNavGroup[];
|
|
25
|
+
labels?: {
|
|
26
|
+
menu: string;
|
|
27
|
+
nav: string;
|
|
28
|
+
close: string;
|
|
29
|
+
};
|
|
30
|
+
/** `key` of the active item. */
|
|
31
|
+
value: string;
|
|
32
|
+
/** Search field, status pills, the signed-in operator. */
|
|
33
|
+
toolbar?: ReactNode;
|
|
34
|
+
title?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The console layout: a grouped sidebar, a topbar, and one scrolling workspace.
|
|
38
|
+
*
|
|
39
|
+
* The counterpart to `AppShell`, and deliberately a different component rather
|
|
40
|
+
* than a `variant`: an operator console is a desktop-first, two-column, deep
|
|
41
|
+
* hierarchy, and a phone app is a single column with five destinations. Sharing
|
|
42
|
+
* one component would mean every screen carrying the other's assumptions.
|
|
43
|
+
*
|
|
44
|
+
* Below the layout's breakpoint the same sidebar becomes a drawer — the same
|
|
45
|
+
* markup, not a second nav to keep in sync, which is how the two drift apart.
|
|
46
|
+
*/
|
|
47
|
+
export declare function AdminShell({ brand, children, className, groups, labels, sidebarFooter, title, toolbar, value, }: AdminShellProps): import("react").JSX.Element;
|
|
48
|
+
export interface DetailHeaderProps {
|
|
49
|
+
/** Buttons for this record: approve, retry, export. */
|
|
50
|
+
actions?: ReactNode;
|
|
51
|
+
back?: {
|
|
52
|
+
href?: string;
|
|
53
|
+
label: string;
|
|
54
|
+
onClick?: () => void;
|
|
55
|
+
};
|
|
56
|
+
className?: string;
|
|
57
|
+
/** Status badge, owner, id — the facts that qualify the title. */
|
|
58
|
+
meta?: ReactNode;
|
|
59
|
+
status?: ReactNode;
|
|
60
|
+
subtitle?: ReactNode;
|
|
61
|
+
title: ReactNode;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The top of a record: where it sits, what it is, and what can be done to it.
|
|
65
|
+
*
|
|
66
|
+
* The actions live here rather than at the bottom of the page because an
|
|
67
|
+
* operator working a queue acts on the record without reading all of it, and a
|
|
68
|
+
* button below a thousand-row table is a button nobody finds.
|
|
69
|
+
*/
|
|
70
|
+
export declare function DetailHeader({ actions, back, className, meta, status, subtitle, title, }: DetailHeaderProps): import("react").JSX.Element;
|
|
71
|
+
export interface FilterChip {
|
|
72
|
+
key: string;
|
|
73
|
+
label: ReactNode;
|
|
74
|
+
onRemove: () => void;
|
|
75
|
+
/** The value the filter is set to, shown after the label. */
|
|
76
|
+
value?: ReactNode;
|
|
77
|
+
}
|
|
78
|
+
export interface FilterChipsProps {
|
|
79
|
+
chips: FilterChip[];
|
|
80
|
+
className?: string;
|
|
81
|
+
clearLabel?: string;
|
|
82
|
+
labels?: {
|
|
83
|
+
remove: (label: string) => string;
|
|
84
|
+
toolbar: string;
|
|
85
|
+
};
|
|
86
|
+
onClearAll?: () => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The filters currently narrowing a table, each removable.
|
|
90
|
+
*
|
|
91
|
+
* It renders nothing when there are none — an empty toolbar reserving space
|
|
92
|
+
* for filters nobody set is the reason these strips feel like chrome. Every
|
|
93
|
+
* chip states what it removes in its accessible name, because "remove" ten
|
|
94
|
+
* times over is no name at all.
|
|
95
|
+
*/
|
|
96
|
+
export declare function FilterChips({ chips, className, clearLabel, labels, onClearAll, }: FilterChipsProps): import("react").JSX.Element | null;
|
|
97
|
+
export interface ActivityEvent {
|
|
98
|
+
/** What happened, in the product's own words. */
|
|
99
|
+
action: ReactNode;
|
|
100
|
+
actor?: ReactNode;
|
|
101
|
+
/** ISO timestamp. Formatted here, in the viewer's locale. */
|
|
102
|
+
at?: string;
|
|
103
|
+
icon?: IconName;
|
|
104
|
+
id: string;
|
|
105
|
+
target?: ReactNode;
|
|
106
|
+
tone?: 'accent' | 'danger' | 'default' | 'success' | 'warning';
|
|
107
|
+
}
|
|
108
|
+
export interface ActivityFeedProps {
|
|
109
|
+
className?: string;
|
|
110
|
+
empty?: ReactNode;
|
|
111
|
+
events: ActivityEvent[];
|
|
112
|
+
locale?: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Who did what, most recent first.
|
|
116
|
+
*
|
|
117
|
+
* Timestamps are absolute rather than "3 hours ago": an audit trail is read to
|
|
118
|
+
* reconstruct a sequence, and a relative time that keeps moving is exactly
|
|
119
|
+
* what you cannot compare two of.
|
|
120
|
+
*/
|
|
121
|
+
export declare function ActivityFeed({ className, empty, events, locale }: ActivityFeedProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { TabBarProps } from './tab-bar';
|
|
3
|
+
export interface AppShellProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
/** Sticky top row: a title, a back control, an action. Optional — many
|
|
7
|
+
screens carry their own header inside the content instead. */
|
|
8
|
+
header?: ReactNode;
|
|
9
|
+
/** The bottom navigation. Omit it on a screen deeper in the stack: a tab bar
|
|
10
|
+
that stays visible under a detail page invites the viewer to leave. */
|
|
11
|
+
tabs?: TabBarProps;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The frame an app lives in: a sticky header, one scrolling region, and the
|
|
15
|
+
* tab bar.
|
|
16
|
+
*
|
|
17
|
+
* The scroll container is here and nowhere else, which is what makes scroll
|
|
18
|
+
* restoration, pull-to-refresh and "scroll to top on tab change" one thing to
|
|
19
|
+
* implement rather than one per screen. The content region reserves room for
|
|
20
|
+
* the bar it is drawn under, so the last row of a list is never hidden behind
|
|
21
|
+
* it — the failure this component exists to prevent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function AppShell({ children, className, header, tabs }: AppShellProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface AuthScreenProps {
|
|
3
|
+
/** The form itself: a PhoneField, an OtpInput, a PasswordField. */
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
/** Brand mark above the heading. */
|
|
7
|
+
brand?: ReactNode;
|
|
8
|
+
/** Small link back to the previous step — the OTP step's way home. */
|
|
9
|
+
back?: {
|
|
10
|
+
label: string;
|
|
11
|
+
onClick: () => void;
|
|
12
|
+
};
|
|
13
|
+
/** Terms line, support link, resend timer. Sits under the CTA. */
|
|
14
|
+
footer?: ReactNode;
|
|
15
|
+
subtitle?: ReactNode;
|
|
16
|
+
title: ReactNode;
|
|
17
|
+
/** The single primary action. A sign-in screen has exactly one. */
|
|
18
|
+
action?: {
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
label: string;
|
|
21
|
+
loading?: boolean;
|
|
22
|
+
onClick: () => void;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The frame every step of a sign-in flow shares: brand, one heading, one
|
|
27
|
+
* explanation, the fields, and a single action pinned to the bottom.
|
|
28
|
+
*
|
|
29
|
+
* Keeping it one component is what makes the phone step, the code step and the
|
|
30
|
+
* password step read as one screen changing rather than three screens — and it
|
|
31
|
+
* puts the CTA in the same place each time, which is the part a thumb learns.
|
|
32
|
+
*/
|
|
33
|
+
export declare function AuthScreen({ action, back, brand, children, className, footer, subtitle, title, }: AuthScreenProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
export type AvatarSize = 'lg' | 'md' | 'sm';
|
|
3
|
+
export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
|
|
4
|
+
/** Falls back to initials derived from `name` when no image is supplied. */
|
|
5
|
+
name: string;
|
|
6
|
+
shape?: 'round' | 'square';
|
|
7
|
+
size?: AvatarSize;
|
|
8
|
+
src?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function Avatar({ className, name, shape, size, src, ...props }: AvatarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type BadgeSize = 'md' | 'sm';
|
|
3
|
+
export type BadgeTone = 'outline' | 'soft' | 'solid';
|
|
4
|
+
export type BadgeVariant = 'accent' | 'danger' | 'info' | 'neutral' | 'success' | 'warning';
|
|
5
|
+
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** Shows a status dot before the label — for live/among-states meanings. */
|
|
8
|
+
dot?: boolean;
|
|
9
|
+
pill?: boolean;
|
|
10
|
+
size?: BadgeSize;
|
|
11
|
+
tone?: BadgeTone;
|
|
12
|
+
variant?: BadgeVariant;
|
|
13
|
+
}
|
|
14
|
+
export declare function Badge({ children, className, dot, pill, size, tone, variant, ...props }: BadgeProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { IconName } from './icon';
|
|
3
|
+
export type BannerTone = 'accent' | 'danger' | 'info' | 'neutral' | 'success' | 'warning';
|
|
4
|
+
export interface BannerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
5
|
+
action?: ReactNode;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
icon?: IconName;
|
|
8
|
+
title?: ReactNode;
|
|
9
|
+
tone?: BannerTone;
|
|
10
|
+
}
|
|
11
|
+
export declare function Banner({ action, children, className, icon, title, tone, ...props }: BannerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Crumb {
|
|
2
|
+
href?: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface BreadcrumbProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
/** The trail, root first. The last entry is the current page and is never a
|
|
8
|
+
link — a link to where the reader already is has nothing to offer. */
|
|
9
|
+
items: Crumb[];
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function Breadcrumb({ className, items, label }: BreadcrumbProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { IconName } from './icon';
|
|
3
|
+
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
4
|
+
export type ButtonVariant = 'accent' | 'danger' | 'ghost' | 'primary' | 'secondary';
|
|
5
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
fullWidth?: boolean;
|
|
8
|
+
/** Icon on the trailing edge — travels with the reading direction. */
|
|
9
|
+
iconEnd?: IconName;
|
|
10
|
+
iconStart?: IconName;
|
|
11
|
+
/** Blocks interaction and announces busy; the label stays in place so the
|
|
12
|
+
button does not change width mid-request. */
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
size?: ButtonSize;
|
|
15
|
+
variant?: ButtonVariant;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The ref is forwarded because overlays anchor to their trigger: Menu and
|
|
19
|
+
* Popover need the element, not a wrapper around it.
|
|
20
|
+
*/
|
|
21
|
+
export declare const Button: import('react').ForwardRefExoticComponent<ButtonProps & import('react').RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type CardPadding = 'lg' | 'md' | 'none' | 'sm';
|
|
3
|
+
export type CardVariant = 'accent' | 'default' | 'muted' | 'outline' | 'raised';
|
|
4
|
+
export interface CardProps extends HTMLAttributes<HTMLElement> {
|
|
5
|
+
as?: 'article' | 'div' | 'section';
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
footer?: ReactNode;
|
|
8
|
+
header?: ReactNode;
|
|
9
|
+
/** Visual affordance only. An interactive card must still be wrapped in, or
|
|
10
|
+
rendered as, a real button or link — this never adds a handler. */
|
|
11
|
+
interactive?: boolean;
|
|
12
|
+
padding?: CardPadding;
|
|
13
|
+
variant?: CardVariant;
|
|
14
|
+
}
|
|
15
|
+
export declare function Card({ as: Component, children, className, footer, header, interactive, padding, variant, ...props }: CardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ChatAttachment } from './chat';
|
|
2
|
+
export interface ChatDraft {
|
|
3
|
+
attachments: ChatAttachment[];
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ChatComposerProps {
|
|
7
|
+
/** Accept list for the attach button. Narrow it to what the product takes. */
|
|
8
|
+
accept?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
labels?: Partial<typeof DEFAULT_LABELS>;
|
|
12
|
+
/** Called with the draft. Uploading is the app's: the attachments carry
|
|
13
|
+
object URLs and their `File`s are handed over in `onFiles`. */
|
|
14
|
+
onSend: (draft: ChatDraft) => void;
|
|
15
|
+
/** The raw `File`s behind the attachments in the same order, so the caller
|
|
16
|
+
can upload them without re-reading the object URLs. */
|
|
17
|
+
onFiles?: (files: File[]) => void;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
/** Turn off what the product does not support. Voice also switches itself
|
|
20
|
+
off where the browser has no recorder. */
|
|
21
|
+
allow?: {
|
|
22
|
+
file?: boolean;
|
|
23
|
+
video?: boolean;
|
|
24
|
+
voice?: boolean;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
declare const DEFAULT_LABELS: {
|
|
28
|
+
attach: string;
|
|
29
|
+
cancel: string;
|
|
30
|
+
discard: string;
|
|
31
|
+
record: string;
|
|
32
|
+
recording: string;
|
|
33
|
+
send: string;
|
|
34
|
+
stop: string;
|
|
35
|
+
video: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The composer: text, a file, a video, or a voice message recorded in place.
|
|
39
|
+
*
|
|
40
|
+
* It holds the draft and nothing else — no transport, no upload, no socket.
|
|
41
|
+
* `onSend` receives the text and the attachments, and `onFiles` hands over the
|
|
42
|
+
* original `File`s, because an object URL is for showing and a `File` is for
|
|
43
|
+
* uploading and the caller needs both.
|
|
44
|
+
*
|
|
45
|
+
* Voice recording is `MediaRecorder` over `getUserMedia`. Where either is
|
|
46
|
+
* missing — an old browser, an insecure origin, a denied microphone — the
|
|
47
|
+
* button is simply not rendered, rather than offered and then failing. The
|
|
48
|
+
* stream's tracks are stopped on every exit path, including unmount: a
|
|
49
|
+
* recorder left running is a microphone indicator that never goes away.
|
|
50
|
+
*/
|
|
51
|
+
export declare function ChatComposer({ accept, allow, className, disabled, labels, onFiles, onSend, placeholder, }: ChatComposerProps): import("react").JSX.Element;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** What a message carries. Text is a string; the rest is an attachment. */
|
|
3
|
+
export type ChatMediaKind = 'file' | 'image' | 'text' | 'video' | 'voice';
|
|
4
|
+
export interface ChatAttachment {
|
|
5
|
+
/** Seconds. Voice and video only — a duration the viewer sees before playing. */
|
|
6
|
+
duration?: number;
|
|
7
|
+
kind: Exclude<ChatMediaKind, 'text'>;
|
|
8
|
+
name?: string;
|
|
9
|
+
/** Still frame for a video. Without one the browser draws its own first frame. */
|
|
10
|
+
poster?: string;
|
|
11
|
+
/** Bytes. Shown on files so nobody taps a 40MB download on mobile data. */
|
|
12
|
+
size?: number;
|
|
13
|
+
/** Object URL or remote URL. The kit never uploads anything itself. */
|
|
14
|
+
url: string;
|
|
15
|
+
/** Normalised 0–1 samples for a voice message. Absent draws a flat track. */
|
|
16
|
+
waveform?: number[];
|
|
17
|
+
}
|
|
18
|
+
export interface ChatMessage {
|
|
19
|
+
attachments?: ChatAttachment[];
|
|
20
|
+
/** Who wrote it. `own` messages are the viewer's and sit on the trailing edge. */
|
|
21
|
+
author?: {
|
|
22
|
+
avatar?: string;
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
id: string;
|
|
26
|
+
own?: boolean;
|
|
27
|
+
/** Delivery state, shown on own messages only — the other side's receipts
|
|
28
|
+
are not the viewer's business. */
|
|
29
|
+
status?: 'failed' | 'read' | 'sending' | 'sent';
|
|
30
|
+
text?: string;
|
|
31
|
+
/** ISO timestamp. Formatted in the viewer's locale, never by the caller. */
|
|
32
|
+
at?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ChatProps {
|
|
35
|
+
className?: string;
|
|
36
|
+
/** Someone is typing. A name renders "Sara is typing", bare `true` renders
|
|
37
|
+
the dots alone. */
|
|
38
|
+
typing?: boolean | string;
|
|
39
|
+
/** Rendered under the last message — a date divider, a system notice. */
|
|
40
|
+
footer?: ReactNode;
|
|
41
|
+
/** Accessible names and the few words the transcript itself says. */
|
|
42
|
+
labels?: Partial<typeof DEFAULT_LABELS>;
|
|
43
|
+
/** The composer. Passing none makes the transcript read-only, which is what
|
|
44
|
+
an archive or a shared thread wants. */
|
|
45
|
+
composer?: ReactNode;
|
|
46
|
+
locale?: string;
|
|
47
|
+
messages: ChatMessage[];
|
|
48
|
+
/** Header row: who this conversation is with, and its actions. */
|
|
49
|
+
header?: ReactNode;
|
|
50
|
+
}
|
|
51
|
+
declare const DEFAULT_LABELS: {
|
|
52
|
+
download: string;
|
|
53
|
+
failed: string;
|
|
54
|
+
pause: string;
|
|
55
|
+
play: string;
|
|
56
|
+
read: string;
|
|
57
|
+
sending: string;
|
|
58
|
+
sent: string;
|
|
59
|
+
today: string;
|
|
60
|
+
typing: string;
|
|
61
|
+
yesterday: string;
|
|
62
|
+
voiceMessage: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* One conversation: a scrolling transcript with a composer under it.
|
|
66
|
+
*
|
|
67
|
+
* The transcript is a `<ol>` in a live region, so a message arriving is
|
|
68
|
+
* announced without stealing focus from whatever the viewer is typing. It
|
|
69
|
+
* follows the newest message only when the viewer is already at the bottom —
|
|
70
|
+
* yanking someone back down while they are reading history is the single most
|
|
71
|
+
* common chat bug, and it is a scroll check, not a scroll call.
|
|
72
|
+
*
|
|
73
|
+
* Media is played by the platform's own elements. The kit renders transports
|
|
74
|
+
* and bubbles; it never uploads, transcodes, or holds a socket.
|
|
75
|
+
*/
|
|
76
|
+
export declare function Chat({ className, composer, footer, header, labels, locale, messages, typing, }: ChatProps): import("react").JSX.Element;
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { IconName } from './icon';
|
|
3
|
+
export interface OptionCardProps {
|
|
4
|
+
/** A short word on the trailing edge: "manual review", "default". */
|
|
5
|
+
badge?: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
icon?: IconName;
|
|
10
|
+
/** Rendered under the description when chosen — an account number, a
|
|
11
|
+
delivery window. Hidden otherwise, because it is only true of the
|
|
12
|
+
selected option. */
|
|
13
|
+
detail?: ReactNode;
|
|
14
|
+
name?: string;
|
|
15
|
+
onSelect: () => void;
|
|
16
|
+
selected: boolean;
|
|
17
|
+
title: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One choice in a list of choices: a payment method, a saved address, a
|
|
21
|
+
* delivery window.
|
|
22
|
+
*
|
|
23
|
+
* A real `<input type="radio">` inside the card, visually hidden, is what makes
|
|
24
|
+
* a set of these behave like a radio group — arrow keys move between them, the
|
|
25
|
+
* name groups them, and a form submits the chosen one. The card is the label,
|
|
26
|
+
* so the whole plate is the target rather than a 20px dot beside it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function OptionCard({ badge, className, description, detail, disabled, icon, name, onSelect, selected, title, }: OptionCardProps): import("react").JSX.Element;
|
|
29
|
+
export interface SummaryLine {
|
|
30
|
+
/** Set on the line the eye should land on — the total. At most one. */
|
|
31
|
+
emphasis?: boolean;
|
|
32
|
+
key: string;
|
|
33
|
+
label: ReactNode;
|
|
34
|
+
/** A qualifier under the label: "6 months · renews 1405/12/01". */
|
|
35
|
+
meta?: ReactNode;
|
|
36
|
+
/** Already formatted. Currency and digits are the product's locale call. */
|
|
37
|
+
value: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
export interface OrderSummaryProps {
|
|
40
|
+
className?: string;
|
|
41
|
+
/** Deductions. Rendered in the success tone with the sign the caller gives
|
|
42
|
+
them — the kit does not do arithmetic on money it cannot see. */
|
|
43
|
+
items: SummaryLine[];
|
|
44
|
+
/** Subtotal, tax, fees, total: the arithmetic, under a rule. */
|
|
45
|
+
totals?: SummaryLine[];
|
|
46
|
+
title?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What is being bought, and what it comes to.
|
|
50
|
+
*
|
|
51
|
+
* Every figure is a `ReactNode` the caller has already formatted. That is
|
|
52
|
+
* deliberate: money is the last thing a UI kit should be computing or
|
|
53
|
+
* rounding, and a component that took numbers would have to guess a currency,
|
|
54
|
+
* a tax rule and a digit shape — three decisions that belong to the product
|
|
55
|
+
* and are wrong in Persian first.
|
|
56
|
+
*/
|
|
57
|
+
export declare function OrderSummary({ className, items, title, totals }: OrderSummaryProps): import("react").JSX.Element;
|
|
58
|
+
export interface ActionBarProps {
|
|
59
|
+
/** The primary control. One only: a bar with two equal buttons has none. */
|
|
60
|
+
action: ReactNode;
|
|
61
|
+
className?: string;
|
|
62
|
+
/** Fine print under the row — renewal terms, delivery estimate. */
|
|
63
|
+
note?: ReactNode;
|
|
64
|
+
/** The figure the action commits to, beside it rather than above it, so it
|
|
65
|
+
is read in the same glance as the button. */
|
|
66
|
+
total?: {
|
|
67
|
+
label: ReactNode;
|
|
68
|
+
value: ReactNode;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The bar a purchase screen ends with: what it costs, and the button.
|
|
73
|
+
*
|
|
74
|
+
* It sticks to the bottom of the scroll container rather than the viewport, so
|
|
75
|
+
* it belongs to the screen it is part of and cannot end up floating over an
|
|
76
|
+
* unrelated one. It sits above the safe-area inset, and the content above owes
|
|
77
|
+
* it room the way it does the tab bar.
|
|
78
|
+
*/
|
|
79
|
+
export declare function ActionBar({ action, className, note, total }: ActionBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
interface ChoiceBaseProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
description?: string;
|
|
5
|
+
}
|
|
6
|
+
export type CheckboxProps = ChoiceBaseProps;
|
|
7
|
+
export type SwitchProps = ChoiceBaseProps;
|
|
8
|
+
/**
|
|
9
|
+
* Both controls keep a real `<input>` in the tree — visually hidden but
|
|
10
|
+
* focusable — so keyboard behaviour, form participation, and assistive tech
|
|
11
|
+
* come from the platform rather than from re-implemented ARIA.
|
|
12
|
+
*/
|
|
13
|
+
export declare function Checkbox({ children, className, description, ...props }: CheckboxProps): import("react").JSX.Element;
|
|
14
|
+
export declare function Switch({ children, className, description, ...props }: SwitchProps): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ComboboxOption<T extends string> {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
label: string;
|
|
4
|
+
/** A trailing hint set in the mono — a count, a code, a date. */
|
|
5
|
+
meta?: string;
|
|
6
|
+
value: T;
|
|
7
|
+
}
|
|
8
|
+
export interface ComboboxProps<T extends string> {
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Shown when nothing matches. Give the viewer the way forward rather than
|
|
11
|
+
a dead end: "No client matches — create it". */
|
|
12
|
+
emptyState?: (query: string) => React.ReactNode;
|
|
13
|
+
error?: string;
|
|
14
|
+
hint?: string;
|
|
15
|
+
id?: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
onChange: (value: T | null) => void;
|
|
18
|
+
options: ComboboxOption<T>[];
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
required?: boolean;
|
|
21
|
+
value: T | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A text input that suggests, and can still be typed into.
|
|
25
|
+
*
|
|
26
|
+
* A real `<input>` with `role="combobox"` over a `role="listbox"`, so the
|
|
27
|
+
* platform's text editing, autofill and form participation are untouched.
|
|
28
|
+
* Arrow keys move the active option, Enter commits it, Escape reverts to the
|
|
29
|
+
* last committed value rather than clearing the field.
|
|
30
|
+
*/
|
|
31
|
+
export declare function Combobox<T extends string>({ className, emptyState, error, hint, id, label, onChange, options, placeholder, required, value, }: ComboboxProps<T>): import("react").JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CalendarSystem, IsoDate } from '../lib/calendars';
|
|
2
|
+
export type { CalendarSystem, IsoDate } from '../lib/calendars';
|
|
3
|
+
export interface CalendarProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** Days to flag — a due date, a booking, anything the month should mark. */
|
|
6
|
+
marked?: IsoDate[];
|
|
7
|
+
max?: IsoDate;
|
|
8
|
+
min?: IsoDate;
|
|
9
|
+
month: IsoDate;
|
|
10
|
+
onMonthChange: (month: IsoDate) => void;
|
|
11
|
+
onSelect: (date: IsoDate) => void;
|
|
12
|
+
/** The calendar the grid is drawn in. Defaults to Jalali for an `fa` locale
|
|
13
|
+
and Gregorian everywhere else. The value stays an ISO Gregorian date in
|
|
14
|
+
both — the calendar is what the viewer reads, not what the API gets. */
|
|
15
|
+
system?: CalendarSystem;
|
|
16
|
+
value?: IsoDate | null;
|
|
17
|
+
/** 1 = Monday. Defaults to Saturday on the Jalali calendar, Monday on the
|
|
18
|
+
Gregorian one — the week does not start on the same day everywhere. */
|
|
19
|
+
weekStart?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function Calendar({ className, marked, max, min, month, onMonthChange, onSelect, system, value, weekStart, }: CalendarProps): import("react").JSX.Element;
|
|
22
|
+
interface DateEntryProps extends Omit<CalendarProps, 'month' | 'onMonthChange' | 'onSelect' | 'value'> {
|
|
23
|
+
error?: string;
|
|
24
|
+
hint?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
onChange: (value: IsoDate) => void;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
value: IsoDate;
|
|
30
|
+
}
|
|
31
|
+
export type DateFieldProps = DateEntryProps;
|
|
32
|
+
/**
|
|
33
|
+
* A text field with a calendar under it, in that order of importance: typing a
|
|
34
|
+
* date always works, which matters more to more people than the grid does.
|
|
35
|
+
*
|
|
36
|
+
* Both halves answer the same calendar system, so a Persian interface types
|
|
37
|
+
* ۱۴۰۴/۰۶/۰۱ into a Jalali grid — and still hands the caller `2025-08-23`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function DateField({ error, hint, id, label, onChange, required, value, ...calendarProps }: DateFieldProps): import("react").JSX.Element;
|
|
40
|
+
export interface DatePickerProps extends DateEntryProps {
|
|
41
|
+
/** Accessible names for the two controls the picker adds. */
|
|
42
|
+
labels?: {
|
|
43
|
+
clear: string;
|
|
44
|
+
open: string;
|
|
45
|
+
};
|
|
46
|
+
/** The same date on the other calendar, under the field. On by default for
|
|
47
|
+
Jalali, where a viewer often has to reconcile a Gregorian document. */
|
|
48
|
+
showEquivalent?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The compact form of date entry: one field, with the grid behind a button.
|
|
52
|
+
*
|
|
53
|
+
* Use it in a form, where a permanently open month steals the space three
|
|
54
|
+
* other fields need; `DateField` is for the screen whose subject is the date.
|
|
55
|
+
* On the Jalali calendar it prints the Gregorian equivalent under the field —
|
|
56
|
+
* the reconciliation an Iranian office does by hand all day.
|
|
57
|
+
*/
|
|
58
|
+
export declare function DatePicker({ error, hint, id, label, labels: names, onChange, required, showEquivalent, value, ...calendarProps }: DatePickerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface DialogProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
closeLabel?: string;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
footer?: ReactNode;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
open: boolean;
|
|
10
|
+
title: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The centred modal, for a decision that has to be made now. The Sheet remains
|
|
14
|
+
* nim's mobile-first surface; this is what a desktop confirmation wants.
|
|
15
|
+
*
|
|
16
|
+
* It renders a real `<dialog>` opened with `showModal()`, so the top layer,
|
|
17
|
+
* the focus trap, the inert background and Escape all come from the platform
|
|
18
|
+
* rather than from a scrim div and a keydown listener.
|
|
19
|
+
*/
|
|
20
|
+
export declare function Dialog({ children, className, closeLabel, description, footer, onClose, open, title, }: DialogProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { IconName } from './icon';
|
|
3
|
+
export interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
4
|
+
actions?: ReactNode;
|
|
5
|
+
description?: ReactNode;
|
|
6
|
+
icon?: IconName;
|
|
7
|
+
title: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare function EmptyState({ actions, className, description, icon, title, ...props }: EmptyStateProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
export interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
|
|
3
|
+
label?: string;
|
|
4
|
+
size?: 'lg' | 'md' | 'sm';
|
|
5
|
+
}
|
|
6
|
+
export declare function Spinner({ className, label, size, ...props }: SpinnerProps): import("react").JSX.Element;
|
|
7
|
+
export interface ProgressProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Omit for an indeterminate bar. */
|
|
10
|
+
value?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function Progress({ className, label, value, ...props }: ProgressProps): import("react").JSX.Element;
|
|
13
|
+
export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
|
|
14
|
+
height?: number | string;
|
|
15
|
+
radius?: string;
|
|
16
|
+
width?: number | string;
|
|
17
|
+
}
|
|
18
|
+
export declare function Skeleton({ className, height, radius, width, ...props }: SkeletonProps): import("react").JSX.Element;
|