@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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +681 -0
  3. package/dist/components/admin-shell.d.ts +121 -0
  4. package/dist/components/app-shell.d.ts +23 -0
  5. package/dist/components/auth-screen.d.ts +33 -0
  6. package/dist/components/avatar.d.ts +10 -0
  7. package/dist/components/badge.d.ts +14 -0
  8. package/dist/components/banner.d.ts +11 -0
  9. package/dist/components/breadcrumb.d.ts +12 -0
  10. package/dist/components/button.d.ts +21 -0
  11. package/dist/components/card.d.ts +15 -0
  12. package/dist/components/chat-composer.d.ts +52 -0
  13. package/dist/components/chat.d.ts +77 -0
  14. package/dist/components/checkout.d.ts +79 -0
  15. package/dist/components/choice.d.ts +15 -0
  16. package/dist/components/combobox.d.ts +31 -0
  17. package/dist/components/date-field.d.ts +58 -0
  18. package/dist/components/dialog.d.ts +20 -0
  19. package/dist/components/empty-state.d.ts +9 -0
  20. package/dist/components/feedback.d.ts +18 -0
  21. package/dist/components/field.d.ts +47 -0
  22. package/dist/components/icon-button.d.ts +12 -0
  23. package/dist/components/icon.d.ts +72 -0
  24. package/dist/components/layout.d.ts +13 -0
  25. package/dist/components/list.d.ts +16 -0
  26. package/dist/components/menu.d.ts +56 -0
  27. package/dist/components/onboarding.d.ts +50 -0
  28. package/dist/components/otp-input.d.ts +29 -0
  29. package/dist/components/pagination.d.ts +12 -0
  30. package/dist/components/password-field.d.ts +33 -0
  31. package/dist/components/phone-field.d.ts +44 -0
  32. package/dist/components/plan-card.d.ts +45 -0
  33. package/dist/components/plan-picker.d.ts +52 -0
  34. package/dist/components/profile-header.d.ts +47 -0
  35. package/dist/components/profile-screen.d.ts +44 -0
  36. package/dist/components/resource-meter.d.ts +22 -0
  37. package/dist/components/section-header.d.ts +8 -0
  38. package/dist/components/segmented.d.ts +18 -0
  39. package/dist/components/sheet.d.ts +18 -0
  40. package/dist/components/sign-in-flow.d.ts +60 -0
  41. package/dist/components/slider.d.ts +10 -0
  42. package/dist/components/stat.d.ts +10 -0
  43. package/dist/components/stepper.d.ts +17 -0
  44. package/dist/components/tab-bar.d.ts +40 -0
  45. package/dist/components/table.d.ts +33 -0
  46. package/dist/components/tabs.d.ts +23 -0
  47. package/dist/components/task-progress.d.ts +38 -0
  48. package/dist/components/theme.d.ts +49 -0
  49. package/dist/components/toast.d.ts +17 -0
  50. package/dist/components/tooltip.d.ts +18 -0
  51. package/dist/components/typography.d.ts +18 -0
  52. package/dist/components/wizard.d.ts +70 -0
  53. package/dist/index.d.ts +104 -0
  54. package/dist/lib/calendars.d.ts +81 -0
  55. package/dist/lib/cn.d.ts +7 -0
  56. package/dist/lib/countries.d.ts +33 -0
  57. package/dist/lib/use-anchor.d.ts +22 -0
  58. package/dist/nim.css +1 -0
  59. package/dist/nim.js +3266 -0
  60. package/package.json +83 -0
  61. package/src/components/admin-shell.tsx +338 -0
  62. package/src/components/app-shell.tsx +36 -0
  63. package/src/components/auth-screen.tsx +71 -0
  64. package/src/components/avatar.tsx +32 -0
  65. package/src/components/badge.tsx +44 -0
  66. package/src/components/banner.tsx +47 -0
  67. package/src/components/breadcrumb.tsx +45 -0
  68. package/src/components/button.tsx +64 -0
  69. package/src/components/card.tsx +46 -0
  70. package/src/components/chat-composer.tsx +331 -0
  71. package/src/components/chat.tsx +352 -0
  72. package/src/components/checkout.tsx +173 -0
  73. package/src/components/choice.tsx +46 -0
  74. package/src/components/combobox.tsx +157 -0
  75. package/src/components/date-field.tsx +394 -0
  76. package/src/components/dialog.tsx +77 -0
  77. package/src/components/empty-state.tsx +23 -0
  78. package/src/components/feedback.tsx +61 -0
  79. package/src/components/field.tsx +177 -0
  80. package/src/components/icon-button.tsx +35 -0
  81. package/src/components/icon.tsx +167 -0
  82. package/src/components/layout.tsx +38 -0
  83. package/src/components/list.tsx +78 -0
  84. package/src/components/menu.tsx +172 -0
  85. package/src/components/onboarding.tsx +157 -0
  86. package/src/components/otp-input.tsx +144 -0
  87. package/src/components/pagination.tsx +86 -0
  88. package/src/components/password-field.tsx +112 -0
  89. package/src/components/phone-field.tsx +242 -0
  90. package/src/components/plan-card.tsx +124 -0
  91. package/src/components/plan-picker.tsx +144 -0
  92. package/src/components/profile-header.tsx +134 -0
  93. package/src/components/profile-screen.tsx +104 -0
  94. package/src/components/resource-meter.tsx +60 -0
  95. package/src/components/section-header.tsx +31 -0
  96. package/src/components/segmented.tsx +53 -0
  97. package/src/components/sheet.tsx +74 -0
  98. package/src/components/sign-in-flow.tsx +322 -0
  99. package/src/components/slider.tsx +50 -0
  100. package/src/components/stat.tsx +30 -0
  101. package/src/components/stepper.tsx +77 -0
  102. package/src/components/tab-bar.tsx +82 -0
  103. package/src/components/table.tsx +81 -0
  104. package/src/components/tabs.tsx +72 -0
  105. package/src/components/task-progress.tsx +111 -0
  106. package/src/components/theme.tsx +112 -0
  107. package/src/components/toast.tsx +87 -0
  108. package/src/components/tooltip.tsx +30 -0
  109. package/src/components/typography.tsx +65 -0
  110. package/src/components/wizard.tsx +192 -0
  111. package/src/index.ts +216 -0
  112. package/src/lib/calendars.ts +227 -0
  113. package/src/lib/cn.ts +8 -0
  114. package/src/lib/countries.ts +110 -0
  115. package/src/lib/use-anchor.ts +92 -0
  116. package/src/theme/colorways/coral.css +62 -0
  117. package/src/theme/colorways/oxblood.css +23 -0
  118. package/src/theme/colorways/paper.css +68 -0
  119. package/src/theme/colorways/teal.css +56 -0
  120. package/src/theme/colorways/vermilion.css +21 -0
  121. package/src/theme/components.css +4606 -0
  122. package/src/theme/contract.css +204 -0
  123. package/src/theme/fonts.css +67 -0
  124. package/src/theme/index.css +33 -0
  125. package/src/theme/persian.css +47 -0
  126. package/src/theme/reset.css +118 -0
  127. package/src/theme/styles/ledger.css +106 -0
  128. package/src/theme/styles/vlora.css +91 -0
  129. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,47 @@
1
+ import { InputHTMLAttributes, ReactNode, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
2
+ import { IconName } from './icon';
3
+ export interface FieldShellProps {
4
+ children: (ids: {
5
+ control: string;
6
+ describedBy?: string;
7
+ }) => ReactNode;
8
+ className?: string;
9
+ error?: string;
10
+ hint?: string;
11
+ id?: string;
12
+ label?: string;
13
+ required?: boolean;
14
+ }
15
+ /**
16
+ * Every nim form control shares this frame, which is what guarantees that a
17
+ * label, a hint, and an error are wired to the control with the right ids on
18
+ * every screen — the part teams most often get wrong by hand.
19
+ */
20
+ export declare function FieldShell({ children, className, error, hint, id, label, required }: FieldShellProps): import("react").JSX.Element;
21
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
22
+ error?: string;
23
+ hint?: string;
24
+ iconEnd?: IconName;
25
+ iconStart?: IconName;
26
+ label?: string;
27
+ }
28
+ export declare function Input({ className, error, hint, iconEnd, iconStart, id, label, required, ...props }: InputProps): import("react").JSX.Element;
29
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
30
+ error?: string;
31
+ hint?: string;
32
+ label?: string;
33
+ }
34
+ export declare function Textarea({ className, error, hint, id, label, required, rows, ...props }: TextareaProps): import("react").JSX.Element;
35
+ export interface SelectOption {
36
+ disabled?: boolean;
37
+ label: string;
38
+ value: string;
39
+ }
40
+ export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
41
+ error?: string;
42
+ hint?: string;
43
+ label?: string;
44
+ options: SelectOption[];
45
+ placeholder?: string;
46
+ }
47
+ export declare function Select({ className, error, hint, id, label, options, placeholder, required, ...props }: SelectProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { ButtonHTMLAttributes } from 'react';
2
+ import { IconName } from './icon';
3
+ export type IconButtonSize = 'sm' | 'md' | 'lg';
4
+ export type IconButtonVariant = 'ghost' | 'outline' | 'solid';
5
+ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
6
+ /** Required: an icon-only control has no other accessible name. */
7
+ label: string;
8
+ name: IconName;
9
+ size?: IconButtonSize;
10
+ variant?: IconButtonVariant;
11
+ }
12
+ export declare const IconButton: import('react').ForwardRefExoticComponent<IconButtonProps & import('react').RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,72 @@
1
+ import { SVGProps } from 'react';
2
+ /**
3
+ * Icons are addressed by role, not by vendor name. The registry is the whole
4
+ * point: it keeps the icon set finite and reviewable, it stops two screens
5
+ * meaning "delete" with two different glyphs, and it means swapping icon
6
+ * libraries later is one file rather than a codebase sweep.
7
+ */
8
+ declare const REGISTRY: {
9
+ alert: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
10
+ 'arrow-forward': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
11
+ bell: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
12
+ bookmark: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
13
+ calendar: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
14
+ camera: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
15
+ check: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
16
+ 'check-circle': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
17
+ 'chevron-back': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
18
+ 'chevron-down': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
19
+ 'chevron-forward': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
20
+ 'chevron-up': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
21
+ clock: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
22
+ close: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
23
+ copy: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
24
+ danger: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
25
+ document: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
26
+ download: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
27
+ edit: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
28
+ external: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
29
+ eye: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
30
+ filter: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
31
+ heart: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
32
+ home: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
33
+ info: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
34
+ loading: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
35
+ menu: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
36
+ mic: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
37
+ minus: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
38
+ moon: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
39
+ paperclip: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
40
+ pause: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
41
+ play: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
42
+ plus: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
43
+ search: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
44
+ send: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
45
+ settings: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
46
+ share: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
47
+ 'sign-out': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
48
+ stop: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
49
+ sparkle: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
50
+ star: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
51
+ sun: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
52
+ trash: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
53
+ 'trend-down': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
54
+ 'trend-up': import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
55
+ upload: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
56
+ video: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
57
+ user: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
58
+ wallet: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
59
+ };
60
+ export type IconName = keyof typeof REGISTRY;
61
+ export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
62
+ export type IconTone = 'accent' | 'danger' | 'default' | 'muted' | 'success' | 'warning';
63
+ export interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'ref'> {
64
+ /** Give a label only when the icon is the sole carrier of meaning. */
65
+ label?: string;
66
+ name: IconName;
67
+ size?: IconSize;
68
+ tone?: IconTone;
69
+ }
70
+ export declare function Icon({ className, label, name, size, tone, ...props }: IconProps): import("react").JSX.Element;
71
+ export declare const iconNames: IconName[];
72
+ export {};
@@ -0,0 +1,13 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ interface BoxProps extends HTMLAttributes<HTMLDivElement> {
3
+ children: ReactNode;
4
+ }
5
+ /** The mobile app viewport, centred on a desktop screen. */
6
+ export declare function AppFrame({ children, className, ...props }: BoxProps): import("react").JSX.Element;
7
+ /** Vertical rhythm. Spacing between components belongs to the page, not to
8
+ the components, and this is where the page expresses it. */
9
+ export declare function Stack({ children, className, gap, ...props }: BoxProps & {
10
+ gap?: 'loose' | 'md' | 'tight';
11
+ }): import("react").JSX.Element;
12
+ export declare function Inline({ children, className, ...props }: BoxProps): import("react").JSX.Element;
13
+ export {};
@@ -0,0 +1,16 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ export interface ListProps extends HTMLAttributes<HTMLDivElement> {
3
+ children: ReactNode;
4
+ /** Drops the surrounding plate and keeps only the dividers. */
5
+ plain?: boolean;
6
+ }
7
+ export declare function List({ children, className, plain, ...props }: ListProps): import("react").JSX.Element;
8
+ export interface ListRowProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
9
+ /** Renders the row as a link. Mutually exclusive with `onClick`. */
10
+ href?: string;
11
+ leading?: ReactNode;
12
+ subtitle?: ReactNode;
13
+ title: ReactNode;
14
+ trailing?: ReactNode;
15
+ }
16
+ export declare function ListRow({ className, href, leading, onClick, subtitle, title, trailing, ...props }: ListRowProps): import("react").JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { ReactElement, ReactNode, Ref, RefObject } from 'react';
2
+ import { IconName } from './icon';
3
+ export interface MenuAction {
4
+ danger?: boolean;
5
+ disabled?: boolean;
6
+ icon?: IconName;
7
+ kind?: 'action';
8
+ label: string;
9
+ onSelect: () => void;
10
+ /** Rendered in the mono, on the trailing edge — a hint, never a binding. */
11
+ shortcut?: string;
12
+ }
13
+ export interface MenuHeading {
14
+ kind: 'heading';
15
+ label: string;
16
+ }
17
+ export interface MenuSeparator {
18
+ kind: 'separator';
19
+ }
20
+ export type MenuItem = MenuAction | MenuHeading | MenuSeparator;
21
+ export interface MenuProps {
22
+ /** The trigger is the caller's, so the menu has no opinion about what opens
23
+ it — it only needs the ref to anchor against. */
24
+ children: (props: {
25
+ open: boolean;
26
+ ref: Ref<HTMLButtonElement>;
27
+ toggle: () => void;
28
+ }) => ReactElement;
29
+ className?: string;
30
+ items: MenuItem[];
31
+ label: string;
32
+ }
33
+ /**
34
+ * A list of actions, dismissed by choosing one. A menu holds actions and never
35
+ * inputs — something the viewer types into is a Popover.
36
+ *
37
+ * The trigger is supplied by the caller as a render prop so the menu never has
38
+ * an opinion about what opens it; it only needs the ref to anchor against.
39
+ * Arrow keys move a roving active item, Enter and Space choose it, Escape and
40
+ * an outside click dismiss, and focus returns to the trigger either way.
41
+ */
42
+ export declare function Menu({ children, className, items, label }: MenuProps): import("react").JSX.Element;
43
+ export interface PopoverProps {
44
+ children: ReactNode;
45
+ className?: string;
46
+ label: string;
47
+ onClose: () => void;
48
+ open: boolean;
49
+ /** The element the panel is placed against. */
50
+ triggerRef: RefObject<HTMLElement | null>;
51
+ }
52
+ /**
53
+ * A menu's geometry with a form's contents. Unlike a menu it does not close on
54
+ * a click inside, because the thing inside is what the viewer came for.
55
+ */
56
+ export declare function Popover({ children, className, label, onClose, open, triggerRef }: PopoverProps): import('react').ReactPortal | null;
@@ -0,0 +1,50 @@
1
+ import { ReactNode } from 'react';
2
+ export interface OnboardingSlide {
3
+ /** Illustration, video, or anything else. Sized by the caller. */
4
+ art?: ReactNode;
5
+ body?: ReactNode;
6
+ id: string;
7
+ /** The chip above the title — the promise, in three or four words. */
8
+ label?: string;
9
+ /** A caption card under the art: a headline and its supporting points. */
10
+ proof?: {
11
+ icon?: ReactNode;
12
+ points?: string[];
13
+ title: ReactNode;
14
+ };
15
+ title: ReactNode;
16
+ }
17
+ export interface OnboardingProps {
18
+ className?: string;
19
+ /** Label for the button on the last slide. */
20
+ finishLabel: string;
21
+ /** Label for the button on every other slide. */
22
+ nextLabel: string;
23
+ /** Reached from the finish button, or from skip. */
24
+ onDone: () => void;
25
+ onSkip?: () => void;
26
+ /** Notified on every slide change, including from a dot. */
27
+ onStep?: (index: number) => void;
28
+ slides: OnboardingSlide[];
29
+ skipLabel?: string;
30
+ /** Version string, support line — whatever sits under the CTA. */
31
+ footnote?: ReactNode;
32
+ /** Brand mark in the top bar. */
33
+ brand?: ReactNode;
34
+ /** Accessible names for the controls. */
35
+ labels?: {
36
+ back: string;
37
+ dot: (index: number) => string;
38
+ };
39
+ }
40
+ /**
41
+ * The three-screen intro a product opens with: art, a promise, a body, and one
42
+ * CTA that advances.
43
+ *
44
+ * State is the component's, because a first-run carousel is never resumed from
45
+ * a URL — `onStep` reports it for analytics and `onDone` fires when the viewer
46
+ * either finishes or skips, so the caller routes in one place instead of two.
47
+ * Slides are announced through a live region rather than by moving focus,
48
+ * which would yank a screen reader out of the CTA it is already on.
49
+ */
50
+ export declare function Onboarding({ brand, className, finishLabel, footnote, labels, nextLabel, onDone, onSkip, onStep, skipLabel, slides, }: OnboardingProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ export interface OtpInputProps {
2
+ /** Fires as soon as the last box is filled — by typing, pasting, or SMS
3
+ autofill. The caller does not need to watch `value.length` itself. */
4
+ autoFocus?: boolean;
5
+ className?: string;
6
+ error?: string;
7
+ label: string;
8
+ length?: number;
9
+ onChange: (value: string) => void;
10
+ onComplete?: (value: string) => void;
11
+ /** Per-box accessible name, e.g. `(i) => `Digit ${i + 1}``. */
12
+ digitLabel?: (index: number) => string;
13
+ /** The code so far, ASCII digits only, shorter than `length` while typing. */
14
+ value: string;
15
+ }
16
+ /**
17
+ * The boxed one-time code.
18
+ *
19
+ * One `<input>` per digit, but the string is the caller's: the component never
20
+ * holds a per-box array, so a paste, an SMS autofill, and a keystroke all take
21
+ * the same path and cannot disagree. The row is pinned `dir="ltr"` even inside
22
+ * a Persian page — a code is a number, and the first digit belongs on the left
23
+ * in every script. Input is normalised through `toAsciiDigits`, so ۱۲۳۴۵ typed
24
+ * on a Persian keyboard arrives as `12345`.
25
+ *
26
+ * `autocomplete="one-time-code"` on the first box is what lets iOS and Android
27
+ * offer the code from the SMS itself.
28
+ */
29
+ export declare function OtpInput({ autoFocus, className, digitLabel, error, label, length, onChange, onComplete, value, }: OtpInputProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ export interface PaginationProps {
2
+ className?: string;
3
+ label?: string;
4
+ nextLabel?: string;
5
+ onChange: (page: number) => void;
6
+ page: number;
7
+ pageCount: number;
8
+ previousLabel?: string;
9
+ /** Free text on the leading edge — "Showing 1–6 of 84". */
10
+ summary?: string;
11
+ }
12
+ export declare function Pagination({ className, label, nextLabel, onChange, page, pageCount, previousLabel, summary, }: PaginationProps): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { InputHTMLAttributes } from 'react';
2
+ export type PasswordStrength = 'fair' | 'good' | 'strong' | 'weak';
3
+ export interface PasswordFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'type'> {
4
+ error?: string;
5
+ hint?: string;
6
+ label?: string;
7
+ /** Draws the meter. Omit on a sign-in field: scoring a password someone
8
+ already has tells them nothing they can act on. */
9
+ strength?: PasswordStrength;
10
+ /** Accessible names for the reveal toggle and the meter. */
11
+ labels?: {
12
+ hide: string;
13
+ show: string;
14
+ strength: (level: PasswordStrength) => string;
15
+ };
16
+ }
17
+ /**
18
+ * A password input with a reveal toggle and an optional strength meter.
19
+ *
20
+ * Revealing is a real `type` swap rather than a font trick, so a password
21
+ * manager still sees a password field, and the toggle is a button with a
22
+ * label — an icon that changes silently leaves a screen reader unable to tell
23
+ * whether the characters are showing.
24
+ *
25
+ * Scoring is the caller's: strength depends on the policy being enforced, and
26
+ * a meter that disagrees with the server's rules is worse than none.
27
+ */
28
+ export declare function PasswordField({ className, error, hint, id, label, labels, required, strength, ...props }: PasswordFieldProps): import("react").JSX.Element;
29
+ /**
30
+ * A default score for products without a policy of their own: length first,
31
+ * then variety. Deliberately blunt — it is a hint, never a gate.
32
+ */
33
+ export declare function scorePassword(password: string): PasswordStrength;
@@ -0,0 +1,44 @@
1
+ export interface PhoneFieldProps {
2
+ className?: string;
3
+ /** ISO 3166-1 alpha-2 of the selected country, e.g. `IR`. */
4
+ country: string;
5
+ error?: string;
6
+ hint?: string;
7
+ id?: string;
8
+ label?: string;
9
+ /** Locale the country names are shown in. Defaults to the document's. */
10
+ locale?: string;
11
+ onChange: (value: string) => void;
12
+ onCountryChange: (iso2: string) => void;
13
+ onSubmit?: () => void;
14
+ placeholder?: string;
15
+ /** Countries to float above the alphabetical list — the ones most viewers
16
+ of this product will pick. */
17
+ priority?: string[];
18
+ required?: boolean;
19
+ /** Accessible names for the picker. */
20
+ labels?: {
21
+ pickCountry: string;
22
+ search: string;
23
+ noMatch: string;
24
+ };
25
+ /** The national number, ASCII digits only, without the dialling code. */
26
+ value: string;
27
+ }
28
+ /**
29
+ * Phone number entry: a country picker welded to a number input.
30
+ *
31
+ * The two halves are separate props on purpose. A field that owns one E.164
32
+ * string has to re-parse it on every keystroke to know which flag to draw, and
33
+ * gets it wrong the moment someone types a `+` themselves; keeping the country
34
+ * and the national digits apart means `+${dial}${value}` is the whole
35
+ * serialisation and there is nothing to guess.
36
+ *
37
+ * The picker lists every ISO country, named in the viewer's locale, searchable
38
+ * by name, code, or ISO letters. The input is `dir="ltr"` in every direction —
39
+ * a phone number reads left to right in Persian too — and Persian digits are
40
+ * normalised to ASCII on the way in.
41
+ */
42
+ export declare function PhoneField({ className, country, error, hint, id, label, labels, locale, onChange, onCountryChange, onSubmit, placeholder, priority, required, value, }: PhoneFieldProps): import("react").JSX.Element;
43
+ /** `+${dial}${national}` — what an auth API actually wants. */
44
+ export declare function toE164(country: string, national: string): string;
@@ -0,0 +1,45 @@
1
+ import { ReactNode } from 'react';
2
+ import { IconName } from './icon';
3
+ export interface PlanFeature {
4
+ label: ReactNode;
5
+ /** `included` is shipped, `pending` is bought but not released yet, and
6
+ `excluded` is what this plan does not get. All three are worth showing:
7
+ a plan card that lists only wins tells the viewer nothing to choose on. */
8
+ state?: 'excluded' | 'included' | 'pending';
9
+ /** A short status word beside a pending row — "soon", "beta". */
10
+ note?: string;
11
+ }
12
+ export interface PlanCardProps {
13
+ /** The one plan being recommended. At most one card in a set. */
14
+ badge?: string;
15
+ className?: string;
16
+ features?: PlanFeature[];
17
+ icon?: IconName;
18
+ /** The headline figure, already formatted and localised by the caller. */
19
+ price: ReactNode;
20
+ /** What the price buys — "per month", "6 months". */
21
+ priceCaption?: ReactNode;
22
+ /** The comparable unit price, so plans of different lengths can be read
23
+ against each other. */
24
+ secondary?: {
25
+ caption: ReactNode;
26
+ value: ReactNode;
27
+ };
28
+ name: ReactNode;
29
+ onSelect?: () => void;
30
+ selected?: boolean;
31
+ tagline?: ReactNode;
32
+ }
33
+ /**
34
+ * One subscription tier, as a card the viewer chooses.
35
+ *
36
+ * The whole card is the control — a radio hidden behind a tap target the size
37
+ * of a fingernail is the reason plan pickers feel fussy — so it renders as a
38
+ * `<button>` with `aria-pressed` when selectable, and as a plain plate when it
39
+ * is only being displayed.
40
+ *
41
+ * Prices are `ReactNode` rather than numbers: currency, digit shaping and
42
+ * grouping are the product's locale decisions, and a kit that formats them
43
+ * would be wrong in Persian first.
44
+ */
45
+ export declare function PlanCard({ badge, className, features, icon, name, onSelect, price, priceCaption, secondary, selected, tagline, }: PlanCardProps): import("react").JSX.Element;
@@ -0,0 +1,52 @@
1
+ import { ReactNode } from 'react';
2
+ import { PlanCardProps } from './plan-card';
3
+ export interface BillingCycle {
4
+ id: string;
5
+ label: string;
6
+ /** "Save 15%" — the reason to pick the longer commitment. */
7
+ note?: string;
8
+ }
9
+ export interface PlanOffer extends Omit<PlanCardProps, 'onSelect' | 'price' | 'secondary' | 'selected'> {
10
+ id: string;
11
+ /** Price per billing cycle, keyed by cycle id. Already formatted: currency
12
+ and digit shaping are the product's locale decision, not the kit's. */
13
+ prices: Record<string, {
14
+ monthly?: ReactNode;
15
+ price: ReactNode;
16
+ }>;
17
+ }
18
+ export interface PlanPickerProps {
19
+ className?: string;
20
+ /** Billing periods. One cycle, or none at all, hides the switch. */
21
+ cycles?: BillingCycle[];
22
+ cycle?: string;
23
+ defaultCycle?: string;
24
+ defaultPlan?: string;
25
+ labels?: {
26
+ cycle: string;
27
+ monthly: string;
28
+ price: string;
29
+ };
30
+ /** Fine print under the action — renewal terms, store rules. */
31
+ note?: ReactNode;
32
+ onCycleChange?: (cycle: string) => void;
33
+ onPlanChange?: (plan: string) => void;
34
+ /** Fires with the chosen plan and cycle. Payment is the app's business. */
35
+ onSubmit?: (plan: string, cycle: string) => void;
36
+ plans: PlanOffer[];
37
+ plan?: string;
38
+ submitLabel?: string;
39
+ }
40
+ /**
41
+ * The subscription screen: billing period, the tiers, and one action.
42
+ *
43
+ * Ready to mount — it holds the selection, keeps the cycle and the prices in
44
+ * step, and hands `onSubmit` the pair the checkout needs. Pass `plan`/`cycle`
45
+ * to drive it from outside instead, e.g. when a deep link opens the screen on
46
+ * a specific tier.
47
+ *
48
+ * It deliberately does not take a payment handler, a store SDK, or a currency:
49
+ * a plan picker that also knows how to charge is two screens welded together,
50
+ * and only one of them is the same across products.
51
+ */
52
+ export declare function PlanPicker({ className, cycle, cycles, defaultCycle, defaultPlan, labels, note, onCycleChange, onPlanChange, onSubmit, plan, plans, submitLabel, }: PlanPickerProps): import("react").JSX.Element;
@@ -0,0 +1,47 @@
1
+ import { ReactNode } from 'react';
2
+ export interface AvatarRingProps {
3
+ className?: string;
4
+ /** What the ring is measuring, for assistive tech. */
5
+ label: string;
6
+ /** Initials, or a small caption under them. */
7
+ caption?: ReactNode;
8
+ /** Image URL. Falls back to `initials` when absent or broken. */
9
+ src?: string;
10
+ initials: string;
11
+ size?: number;
12
+ /** 0–100. The ring is the progress; the number belongs in the caption. */
13
+ value: number;
14
+ }
15
+ /**
16
+ * An avatar wearing a progress ring: profile completion, a skin score, a
17
+ * streak — one figure the viewer is meant to want to finish.
18
+ *
19
+ * The ring is an SVG arc rather than a conic gradient so it keeps a rounded
20
+ * cap and stays crisp at any size, and it carries `role="img"` with the label,
21
+ * because the arc is the only place the value is stated for a sighted viewer.
22
+ */
23
+ export declare function AvatarRing({ caption, className, initials, label, size, src, value, }: AvatarRingProps): import("react").JSX.Element;
24
+ export interface ProfileHeaderProps {
25
+ /** The row of quick figures under the identity block. */
26
+ stats?: {
27
+ label: ReactNode;
28
+ value: ReactNode;
29
+ }[];
30
+ /** Small pills: plan, skin type, billing cycle. */
31
+ chips?: ReactNode;
32
+ actions?: ReactNode;
33
+ avatar: ReactNode;
34
+ className?: string;
35
+ /** The line above the name — "vlora account", the tenant, the role. */
36
+ eyebrow?: ReactNode;
37
+ name: ReactNode;
38
+ }
39
+ /**
40
+ * The plate at the top of a profile: who this is, what they have, and the two
41
+ * things they should do next.
42
+ *
43
+ * It is a header, not a dashboard. Anything that needs a chart or a list goes
44
+ * in the sections beneath it — this block stays readable at a glance, which is
45
+ * the only reason it earns the space it takes.
46
+ */
47
+ export declare function ProfileHeader({ actions, avatar, chips, className, eyebrow, name, stats, }: ProfileHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { ReactNode } from 'react';
2
+ import { ProfileHeaderProps } from './profile-header';
3
+ import { IconName } from './icon';
4
+ export interface ProfileRow {
5
+ /** A switch instead of a chevron. With `onToggle` the row is the control. */
6
+ checked?: boolean;
7
+ danger?: boolean;
8
+ href?: string;
9
+ icon?: IconName;
10
+ key: string;
11
+ label: ReactNode;
12
+ onSelect?: () => void;
13
+ onToggle?: (next: boolean) => void;
14
+ subtitle?: ReactNode;
15
+ /** A badge, a count, a version string. */
16
+ value?: ReactNode;
17
+ }
18
+ export interface ProfileSection {
19
+ description?: ReactNode;
20
+ key: string;
21
+ rows: ProfileRow[];
22
+ title?: ReactNode;
23
+ }
24
+ export interface ProfileScreenProps extends ProfileHeaderProps {
25
+ className?: string;
26
+ /** Sign out, delete account — whatever ends the session, kept away from the
27
+ rows above so it is never the thing a thumb reaches by accident. */
28
+ footer?: ReactNode;
29
+ sections?: ProfileSection[];
30
+ }
31
+ /**
32
+ * The account screen: the identity plate, then grouped rows of settings.
33
+ *
34
+ * Ready to mount — hand it the header's props and a list of sections and it is
35
+ * the screen. Rows are declared, not composed, so the whole of a settings page
36
+ * is data the app already has: a label, an icon, and either somewhere to go or
37
+ * something to toggle.
38
+ *
39
+ * State stays the caller's. A switch here reports the change and redraws from
40
+ * the prop, because the truth about whether notifications are on lives on a
41
+ * server, and a row that flips optimistically and then disagrees with it is
42
+ * worse than one that waits.
43
+ */
44
+ export declare function ProfileScreen({ className, footer, sections, ...header }: ProfileScreenProps): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ export type ResourceMeterTone = 'accent' | 'danger' | 'success' | 'warning';
3
+ export interface ResourceMeterProps extends HTMLAttributes<HTMLDivElement> {
4
+ /** A concise description of the capacity being measured. */
5
+ label: ReactNode;
6
+ /** A readable measurement such as `18.4 / 32 GiB`; formatting stays in the product. */
7
+ value: ReactNode;
8
+ /** Optional secondary context, for example a node's one-minute load. */
9
+ detail?: ReactNode;
10
+ /** Percentage used, clamped to the meter's valid 0–100 range. Omit for capacity-only data. */
11
+ percent?: number;
12
+ tone?: ResourceMeterTone;
13
+ }
14
+ /**
15
+ * A compact, accessible capacity reading for operator interfaces.
16
+ *
17
+ * `Progress` is intentionally unlabelled composition; a resource figure needs
18
+ * its numerator, denominator, and qualitative state to travel together. The
19
+ * product supplies formatting because bytes, vCPU, and retention windows do
20
+ * not share a unit.
21
+ */
22
+ export declare function ResourceMeter({ className, detail, label, percent, tone, value, ...props }: ResourceMeterProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ export interface SectionHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
3
+ action?: ReactNode;
4
+ description?: ReactNode;
5
+ eyebrow?: ReactNode;
6
+ title: ReactNode;
7
+ }
8
+ export declare function SectionHeader({ action, className, description, eyebrow, title, ...props }: SectionHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { HTMLAttributes } from 'react';
2
+ export interface SegmentedOption<T extends string> {
3
+ disabled?: boolean;
4
+ label: string;
5
+ value: T;
6
+ }
7
+ export interface SegmentedProps<T extends string> extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
8
+ fullWidth?: boolean;
9
+ label: string;
10
+ onChange: (value: T) => void;
11
+ options: SegmentedOption<T>[];
12
+ value: T;
13
+ }
14
+ /**
15
+ * A tablist rather than a row of buttons: arrow-key navigation and the
16
+ * selected state both come from the platform's tab semantics.
17
+ */
18
+ export declare function Segmented<T extends string>({ className, fullWidth, label, onChange, options, value, ...props }: SegmentedProps<T>): import("react").JSX.Element;