@momentumworld/ui-react 0.17.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 (138) hide show
  1. package/dist/chunk-2HQPCV2L.js +895 -0
  2. package/dist/chunk-2HQPCV2L.js.map +1 -0
  3. package/dist/form.d.ts +175 -0
  4. package/dist/form.js +356 -0
  5. package/dist/form.js.map +1 -0
  6. package/dist/index.d.ts +2162 -0
  7. package/dist/index.js +11640 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/input-JCZs61MO.d.ts +24 -0
  10. package/package.json +90 -0
  11. package/src/components/form/checkbox-group-field.tsx +101 -0
  12. package/src/components/form/date-field.tsx +79 -0
  13. package/src/components/form/date-range-field.tsx +106 -0
  14. package/src/components/form/form-context.ts +10 -0
  15. package/src/components/form/form.tsx +54 -0
  16. package/src/components/form/number-field.tsx +69 -0
  17. package/src/components/form/select-field.tsx +76 -0
  18. package/src/components/form/submit-button.tsx +28 -0
  19. package/src/components/form/text-field.tsx +107 -0
  20. package/src/components/layout/dashboard-header.tsx +54 -0
  21. package/src/components/layout/dashboard-panel.tsx +34 -0
  22. package/src/components/layout/dashboard-topbar.tsx +221 -0
  23. package/src/components/theme-provider.tsx +403 -0
  24. package/src/components/ui/accordion.tsx +69 -0
  25. package/src/components/ui/alert-dialog.tsx +169 -0
  26. package/src/components/ui/alert.tsx +80 -0
  27. package/src/components/ui/animated-counter.tsx +109 -0
  28. package/src/components/ui/animated-theme-toggler.tsx +265 -0
  29. package/src/components/ui/app-store-buttons.tsx +182 -0
  30. package/src/components/ui/aspect-ratio.tsx +23 -0
  31. package/src/components/ui/aurora-orb.tsx +163 -0
  32. package/src/components/ui/aurora-text.tsx +76 -0
  33. package/src/components/ui/autocomplete.tsx +296 -0
  34. package/src/components/ui/avatar-group.tsx +150 -0
  35. package/src/components/ui/avatar.tsx +285 -0
  36. package/src/components/ui/badge-group.tsx +160 -0
  37. package/src/components/ui/badge.tsx +172 -0
  38. package/src/components/ui/breadcrumb.tsx +112 -0
  39. package/src/components/ui/button.tsx +77 -0
  40. package/src/components/ui/calendar.tsx +137 -0
  41. package/src/components/ui/card.tsx +244 -0
  42. package/src/components/ui/carousel.tsx +258 -0
  43. package/src/components/ui/chart.tsx +379 -0
  44. package/src/components/ui/chat-divider.tsx +67 -0
  45. package/src/components/ui/chat.tsx +494 -0
  46. package/src/components/ui/checkbox-group.tsx +16 -0
  47. package/src/components/ui/checkbox.tsx +82 -0
  48. package/src/components/ui/collapsible.tsx +45 -0
  49. package/src/components/ui/color-picker.tsx +389 -0
  50. package/src/components/ui/combobox.tsx +411 -0
  51. package/src/components/ui/command.tsx +264 -0
  52. package/src/components/ui/confetti.tsx +196 -0
  53. package/src/components/ui/context-menu.tsx +271 -0
  54. package/src/components/ui/context-meter.tsx +134 -0
  55. package/src/components/ui/credit-card.tsx +214 -0
  56. package/src/components/ui/data-table-paged.tsx +163 -0
  57. package/src/components/ui/date-picker.tsx +154 -0
  58. package/src/components/ui/dialog.tsx +196 -0
  59. package/src/components/ui/drawer.tsx +135 -0
  60. package/src/components/ui/empty.tsx +127 -0
  61. package/src/components/ui/fab.tsx +45 -0
  62. package/src/components/ui/featured-icon.tsx +149 -0
  63. package/src/components/ui/field.tsx +88 -0
  64. package/src/components/ui/fieldset.tsx +29 -0
  65. package/src/components/ui/form.tsx +17 -0
  66. package/src/components/ui/frame.tsx +82 -0
  67. package/src/components/ui/generic-empty.tsx +142 -0
  68. package/src/components/ui/group.tsx +97 -0
  69. package/src/components/ui/help-tip.tsx +94 -0
  70. package/src/components/ui/horizontal-scroll-fader.tsx +228 -0
  71. package/src/components/ui/input-group.tsx +102 -0
  72. package/src/components/ui/input-otp.tsx +96 -0
  73. package/src/components/ui/input.tsx +66 -0
  74. package/src/components/ui/item.tsx +198 -0
  75. package/src/components/ui/kbd.tsx +30 -0
  76. package/src/components/ui/label.tsx +28 -0
  77. package/src/components/ui/markdown.tsx +188 -0
  78. package/src/components/ui/menu.tsx +312 -0
  79. package/src/components/ui/menubar.tsx +93 -0
  80. package/src/components/ui/meter.tsx +67 -0
  81. package/src/components/ui/multi-select.tsx +308 -0
  82. package/src/components/ui/navigation-menu.tsx +143 -0
  83. package/src/components/ui/number-field.tsx +160 -0
  84. package/src/components/ui/pagination-controls.tsx +74 -0
  85. package/src/components/ui/pagination.tsx +149 -0
  86. package/src/components/ui/pipeline.tsx +102 -0
  87. package/src/components/ui/popover.tsx +119 -0
  88. package/src/components/ui/preview-card.tsx +55 -0
  89. package/src/components/ui/progress.tsx +289 -0
  90. package/src/components/ui/qr-code.tsx +150 -0
  91. package/src/components/ui/radio-group.tsx +103 -0
  92. package/src/components/ui/resizable.tsx +56 -0
  93. package/src/components/ui/scroll-area.tsx +90 -0
  94. package/src/components/ui/scroller.tsx +38 -0
  95. package/src/components/ui/section-header.tsx +118 -0
  96. package/src/components/ui/segmented-control.tsx +83 -0
  97. package/src/components/ui/select.tsx +181 -0
  98. package/src/components/ui/separator.tsx +23 -0
  99. package/src/components/ui/sheet.tsx +224 -0
  100. package/src/components/ui/sidebar.tsx +774 -0
  101. package/src/components/ui/skeleton.tsx +16 -0
  102. package/src/components/ui/slider.tsx +108 -0
  103. package/src/components/ui/smooth-scroll.tsx +206 -0
  104. package/src/components/ui/social-button.tsx +247 -0
  105. package/src/components/ui/spinner-on-demand.tsx +32 -0
  106. package/src/components/ui/spinner.tsx +18 -0
  107. package/src/components/ui/stat.tsx +187 -0
  108. package/src/components/ui/stepper.tsx +167 -0
  109. package/src/components/ui/switch.tsx +56 -0
  110. package/src/components/ui/table.tsx +126 -0
  111. package/src/components/ui/tabs.tsx +90 -0
  112. package/src/components/ui/tag.tsx +242 -0
  113. package/src/components/ui/target-countdown.tsx +46 -0
  114. package/src/components/ui/text-editor.tsx +313 -0
  115. package/src/components/ui/textarea.tsx +51 -0
  116. package/src/components/ui/thinking-orb.tsx +6 -0
  117. package/src/components/ui/time-picker.tsx +308 -0
  118. package/src/components/ui/timeline.tsx +116 -0
  119. package/src/components/ui/toast.tsx +268 -0
  120. package/src/components/ui/toggle-group.tsx +101 -0
  121. package/src/components/ui/toggle.tsx +45 -0
  122. package/src/components/ui/toolbar.tsx +89 -0
  123. package/src/components/ui/tooltip.tsx +102 -0
  124. package/src/components/ui/vertical-scroll-fader.tsx +250 -0
  125. package/src/components/ui/video-player.tsx +275 -0
  126. package/src/components/upload/avatar-upload-base.tsx +131 -0
  127. package/src/components/upload/image-upload-base.tsx +112 -0
  128. package/src/form.ts +17 -0
  129. package/src/index.ts +143 -0
  130. package/src/lib/hooks/use-callback-ref.ts +15 -0
  131. package/src/lib/hooks/use-first-render.ts +11 -0
  132. package/src/lib/hooks/use-hover.ts +53 -0
  133. package/src/lib/hooks/use-is-tab-active.ts +17 -0
  134. package/src/lib/hooks/use-media-query.ts +164 -0
  135. package/src/lib/utils/css.ts +6 -0
  136. package/src/styles.css +329 -0
  137. package/src/types/helpers.ts +24 -0
  138. package/src/types/react.d.ts +7 -0
@@ -0,0 +1,107 @@
1
+ "use client";
2
+
3
+ import type { StandardSchemaV1Issue } from "@tanstack/react-form";
4
+ import {
5
+ Field,
6
+ FieldControl,
7
+ FieldError,
8
+ FieldLabel,
9
+ } from "../ui/field";
10
+ import { Input } from "../ui/input";
11
+ import {
12
+ InputGroup,
13
+ InputGroupAddon,
14
+ InputGroupInput,
15
+ } from "../ui/input-group";
16
+
17
+ export function TextField({
18
+ field,
19
+ label,
20
+ placeholder,
21
+ type = "text",
22
+ inlineLabel,
23
+ leadingIcon,
24
+ trailingIcon,
25
+ }: {
26
+ field: {
27
+ name: string;
28
+ state: {
29
+ value: string;
30
+ meta: {
31
+ readonly errors: ReadonlyArray<
32
+ string | StandardSchemaV1Issue | undefined
33
+ >;
34
+ };
35
+ };
36
+ handleChange: (nextValue: string) => void;
37
+ handleBlur: () => void;
38
+ };
39
+ label: string;
40
+ placeholder?: string;
41
+ type?: React.ComponentProps<typeof Input>["type"];
42
+ /** Render label inside the input border as a block-start addon */
43
+ inlineLabel?: boolean;
44
+ /** Content rendered as an inline-start addon (e.g. an icon) */
45
+ leadingIcon?: React.ReactNode;
46
+ /** Content rendered as an inline-end addon (e.g. an icon) */
47
+ trailingIcon?: React.ReactNode;
48
+ }) {
49
+ const errors = field.state.meta.errors
50
+ .map((err) => (typeof err === "string" ? err : err?.message))
51
+ .filter(Boolean);
52
+
53
+ const useInputGroup = inlineLabel || leadingIcon || trailingIcon;
54
+
55
+ if (useInputGroup) {
56
+ return (
57
+ <Field name={field.name}>
58
+ {!inlineLabel && <FieldLabel>{label}</FieldLabel>}
59
+ <InputGroup>
60
+ {inlineLabel && (
61
+ <InputGroupAddon align="block-start">
62
+ <FieldLabel>{label}</FieldLabel>
63
+ </InputGroupAddon>
64
+ )}
65
+ {leadingIcon && (
66
+ <InputGroupAddon align="inline-start">
67
+ {leadingIcon}
68
+ </InputGroupAddon>
69
+ )}
70
+ <FieldControl
71
+ render={
72
+ <InputGroupInput
73
+ value={field.state.value}
74
+ onChange={(e) => field.handleChange(e.target.value)}
75
+ onBlur={field.handleBlur}
76
+ placeholder={placeholder}
77
+ type={type}
78
+ />
79
+ }
80
+ />
81
+ {trailingIcon && (
82
+ <InputGroupAddon align="inline-end">{trailingIcon}</InputGroupAddon>
83
+ )}
84
+ </InputGroup>
85
+ <FieldError match={!!errors.length}>{errors.join(", ")}</FieldError>
86
+ </Field>
87
+ );
88
+ }
89
+
90
+ return (
91
+ <Field name={field.name}>
92
+ <FieldLabel>{label}</FieldLabel>
93
+ <FieldControl
94
+ render={
95
+ <Input
96
+ value={field.state.value}
97
+ onChange={(e) => field.handleChange(e.target.value)}
98
+ onBlur={field.handleBlur}
99
+ placeholder={placeholder}
100
+ type={type}
101
+ />
102
+ }
103
+ />
104
+ <FieldError match={!!errors.length}>{errors.join(", ")}</FieldError>
105
+ </Field>
106
+ );
107
+ }
@@ -0,0 +1,54 @@
1
+ import { Slot, Slottable } from "@radix-ui/react-slot";
2
+ import clsx from "clsx";
3
+ import { isValidElement } from "react";
4
+ import type { AsChildProps } from "../../types/helpers";
5
+
6
+ interface DashboardHeaderProps extends Omit<AsChildProps<"div">, "title"> {
7
+ title?: React.ReactNode;
8
+ subtitle?: React.ReactNode;
9
+ rightActions?: React.ReactNode;
10
+ breadcrumb?: React.ReactNode;
11
+ }
12
+
13
+ export function DashboardHeader({
14
+ title,
15
+ subtitle,
16
+ className,
17
+ children,
18
+ asChild,
19
+ rightActions,
20
+ breadcrumb,
21
+ ...props
22
+ }: DashboardHeaderProps) {
23
+ const TitleComp = isValidElement(title) ? Slot : "div";
24
+ const Comp = asChild ? Slot : "div";
25
+
26
+ return (
27
+ <Comp
28
+ className={clsx("flex flex-col border-b rounded-t-lg", className)}
29
+ {...props}
30
+ >
31
+ <div className={clsx("flex flex-col gap-4 p-4")}>
32
+ {breadcrumb}
33
+ {(title || subtitle || rightActions) && (
34
+ <div className="flex flex-col gap-2">
35
+ <div className="flex items-center gap-3 min-h-8">
36
+ {title && (
37
+ <TitleComp className="grow min-w-0 text-lg font-medium leading-7 tracking-[-0.18px] text-foreground">
38
+ {title}
39
+ </TitleComp>
40
+ )}
41
+ {rightActions}
42
+ </div>
43
+ {subtitle && (
44
+ <div className="hidden md:block text-sm font-normal leading-5 text-muted-foreground">
45
+ {subtitle}
46
+ </div>
47
+ )}
48
+ </div>
49
+ )}
50
+ <Slottable>{children}</Slottable>
51
+ </div>
52
+ </Comp>
53
+ );
54
+ }
@@ -0,0 +1,34 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import clsx from "clsx";
3
+ import type { AsChildProps } from "../../types/helpers";
4
+ import { ScrollArea } from "../ui/scroll-area";
5
+
6
+ interface DashboardPanelProps extends AsChildProps<"div"> {
7
+ header?: React.ReactNode;
8
+ }
9
+
10
+ export function DashboardPanel({
11
+ className,
12
+ asChild,
13
+ children,
14
+ header,
15
+ ...props
16
+ }: DashboardPanelProps) {
17
+ const Comp = asChild ? Slot : "div";
18
+ return (
19
+ <Comp
20
+ className={clsx(
21
+ "flex flex-col grow min-h-0 min-w-0 bg-background rounded border",
22
+ className,
23
+ )}
24
+ {...props}
25
+ >
26
+ {header}
27
+ <ScrollArea className="flex grow flex-col min-h-0 *:data-[slot=scroll-area-viewport]:p-4">
28
+ {children}
29
+ </ScrollArea>
30
+ </Comp>
31
+ );
32
+ }
33
+
34
+ export default DashboardPanel;
@@ -0,0 +1,221 @@
1
+ "use client";
2
+
3
+ import { LifeBuoy, LogOut, Search, Settings } from "lucide-react";
4
+ import type * as React from "react";
5
+
6
+ import { cn } from "../../lib/utils/css";
7
+ import { AnimatedThemeToggler } from "../ui/animated-theme-toggler";
8
+ import { Avatar } from "../ui/avatar";
9
+ import { Button } from "../ui/button";
10
+ import { Kbd, KbdGroup } from "../ui/kbd";
11
+ import {
12
+ Menu,
13
+ MenuItem,
14
+ MenuPopup,
15
+ MenuSeparator,
16
+ MenuTrigger,
17
+ } from "../ui/menu";
18
+ import { SidebarTrigger } from "../ui/sidebar";
19
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
20
+
21
+ export interface DashboardTopbarUser {
22
+ /** Display name, shown in the account menu and used as the avatar alt text. */
23
+ name: string;
24
+ /** Optional email, shown under the name in the account menu. */
25
+ email?: string;
26
+ /** Optional avatar image URL. Falls back to initials when omitted. */
27
+ avatarSrc?: string;
28
+ /** Initials shown when there is no avatar image. */
29
+ initials?: string;
30
+ }
31
+
32
+ export interface DashboardTopbarProps {
33
+ /** The signed-in user rendered in the account menu + avatar trigger. */
34
+ user: DashboardTopbarUser;
35
+ /** Opens the consumer's command palette. The search field is a button. */
36
+ onSearch?: () => void;
37
+ /** Placeholder text inside the search affordance. Defaults to "Search". */
38
+ searchPlaceholder?: string;
39
+ /**
40
+ * Trailing shortcut hint inside the search affordance. Defaults to
41
+ * `<KbdGroup><Kbd>⌘</Kbd><Kbd>K</Kbd></KbdGroup>`. Pass `null` to hide it.
42
+ */
43
+ searchShortcut?: React.ReactNode;
44
+ /** Account-menu "Settings" item handler. The item is omitted when undefined. */
45
+ onSettings?: () => void;
46
+ /** Account-menu "Log out" item handler. The item is omitted when undefined. */
47
+ onLogout?: () => void;
48
+ /** mailto/url for the support icon button. The button renders only when set. */
49
+ supportHref?: string;
50
+ /** Whether to render the animated light/dark theme toggle. Defaults to `true`. */
51
+ showThemeToggle?: boolean;
52
+ /** Optional extra controls rendered left of the theme toggle. */
53
+ actions?: React.ReactNode;
54
+ className?: string;
55
+ }
56
+
57
+ const defaultSearchShortcut = (
58
+ <KbdGroup className="absolute right-3 top-1/2 hidden -translate-y-1/2 sm:inline-flex">
59
+ <Kbd>⌘</Kbd>
60
+ <Kbd>K</Kbd>
61
+ </KbdGroup>
62
+ );
63
+
64
+ /**
65
+ * Dashboard top bar shared by the host and sponsor dashboards.
66
+ *
67
+ * Zones: mobile/desktop sidebar trigger · a global search field styled as an
68
+ * input that calls `onSearch` (the consumer owns the command palette + ⌘K key
69
+ * handler) · optional extra `actions` · the animated theme toggle · an optional
70
+ * support link · the account menu (Settings / Log out). The inner row shares a
71
+ * `max-w-[1280px]` wrapper so the search field and avatar line up with the page.
72
+ */
73
+ export function DashboardTopbar({
74
+ user,
75
+ onSearch,
76
+ searchPlaceholder = "Search",
77
+ searchShortcut = defaultSearchShortcut,
78
+ onSettings,
79
+ onLogout,
80
+ supportHref,
81
+ showThemeToggle = true,
82
+ actions,
83
+ className,
84
+ }: DashboardTopbarProps) {
85
+ return (
86
+ <header
87
+ data-slot="dashboard-topbar"
88
+ className={cn("h-16 flex-shrink-0 bg-page-bg", className)}
89
+ >
90
+ <div className="mx-auto flex h-full max-w-[1280px] items-center gap-2 px-4 sm:gap-3 sm:px-6 md:gap-4 md:px-8">
91
+ {/* Sidebar toggle — collapse/open the navigation. */}
92
+ <SidebarTrigger
93
+ aria-label="Toggle navigation"
94
+ className="size-10 border border-input bg-background sm:size-9"
95
+ />
96
+
97
+ {/* Search — looks like an input, opens the consumer's command palette. */}
98
+ <button
99
+ type="button"
100
+ onClick={onSearch}
101
+ aria-label={searchPlaceholder}
102
+ className="group min-w-0 max-w-xl flex-1 outline-none"
103
+ >
104
+ <span className="relative flex h-10 w-full items-center rounded-lg border border-input bg-background pl-10 pr-3 text-sm text-muted-foreground transition-colors group-hover:bg-accent/50 group-focus-visible:ring-2 group-focus-visible:ring-ring sm:pr-16">
105
+ <Search
106
+ className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
107
+ strokeWidth={2}
108
+ />
109
+ {searchPlaceholder}
110
+ {searchShortcut}
111
+ </span>
112
+ </button>
113
+
114
+ {/* Right cluster */}
115
+ <div className="ml-auto flex flex-shrink-0 items-center gap-2 sm:gap-3">
116
+ {actions}
117
+
118
+ {/* Theme toggle. The toggler is anchored inside a wrapper span rather
119
+ than being the TooltipTrigger's render target directly:
120
+ AnimatedThemeToggler spreads incoming props (including the ref Base
121
+ UI injects) AFTER its own `ref={buttonRef}`, so making it the
122
+ trigger clobbers that ref — and its click handler bails out on
123
+ `if (!buttonRef.current) return`, so the theme never flips.
124
+ Wrapping keeps the toggler's own ref + onClick intact while the
125
+ span carries the tooltip. */}
126
+ {showThemeToggle ? (
127
+ <Tooltip>
128
+ <TooltipTrigger render={<span className="inline-flex" />}>
129
+ <AnimatedThemeToggler
130
+ aria-label="Toggle light / dark theme"
131
+ className="size-10 border border-input bg-background sm:size-9"
132
+ variant="circle"
133
+ />
134
+ </TooltipTrigger>
135
+ <TooltipContent>Toggle light / dark theme</TooltipContent>
136
+ </Tooltip>
137
+ ) : null}
138
+
139
+ {/* Support */}
140
+ {supportHref ? (
141
+ <Tooltip>
142
+ <TooltipTrigger
143
+ render={
144
+ <Button
145
+ aria-label="Contact support"
146
+ className="hidden sm:inline-flex"
147
+ render={<a href={supportHref} />}
148
+ size="icon-lg"
149
+ variant="outline"
150
+ >
151
+ <LifeBuoy strokeWidth={1.8} />
152
+ </Button>
153
+ }
154
+ />
155
+ <TooltipContent>Contact support</TooltipContent>
156
+ </Tooltip>
157
+ ) : null}
158
+
159
+ {/* Account menu */}
160
+ <Menu>
161
+ <MenuTrigger
162
+ render={
163
+ <button
164
+ type="button"
165
+ aria-label={`Account menu — signed in as ${user.name}`}
166
+ className="rounded-full outline-none transition hover:ring-2 hover:ring-ring focus-visible:ring-2 focus-visible:ring-ring"
167
+ />
168
+ }
169
+ >
170
+ <Avatar
171
+ alt={user.name}
172
+ initials={user.initials}
173
+ size="md"
174
+ src={user.avatarSrc}
175
+ />
176
+ </MenuTrigger>
177
+ <MenuPopup
178
+ align="end"
179
+ className="w-[min(280px,calc(100vw-32px))]"
180
+ sideOffset={8}
181
+ >
182
+ <div className="flex items-center gap-3 px-2 py-2">
183
+ <Avatar
184
+ alt=""
185
+ initials={user.initials}
186
+ size="md"
187
+ src={user.avatarSrc}
188
+ />
189
+ <div className="min-w-0">
190
+ <p className="truncate text-sm font-semibold text-foreground">
191
+ {user.name}
192
+ </p>
193
+ {user.email ? (
194
+ <p className="truncate text-xs text-muted-foreground">
195
+ {user.email}
196
+ </p>
197
+ ) : null}
198
+ </div>
199
+ </div>
200
+ {(onSettings || onLogout) ? <MenuSeparator /> : null}
201
+ {onSettings ? (
202
+ <MenuItem onClick={onSettings}>
203
+ <Settings strokeWidth={1.8} />
204
+ Settings
205
+ </MenuItem>
206
+ ) : null}
207
+ {onLogout ? (
208
+ <MenuItem onClick={onLogout} variant="destructive">
209
+ <LogOut strokeWidth={1.8} />
210
+ Log out
211
+ </MenuItem>
212
+ ) : null}
213
+ </MenuPopup>
214
+ </Menu>
215
+ </div>
216
+ </div>
217
+ </header>
218
+ );
219
+ }
220
+
221
+ export default DashboardTopbar;