@torch-ui/solid 0.1.3

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 (118) hide show
  1. package/README.md +166 -0
  2. package/package.json +67 -0
  3. package/src/components/actions/Button.tsx +612 -0
  4. package/src/components/actions/ButtonGroup.tsx +728 -0
  5. package/src/components/actions/Copy.tsx +98 -0
  6. package/src/components/actions/DarkModeToggle.tsx +80 -0
  7. package/src/components/actions/Link.tsx +37 -0
  8. package/src/components/actions/index.ts +19 -0
  9. package/src/components/actions/useCopyToClipboard.ts +90 -0
  10. package/src/components/charts/Chart.tsx +331 -0
  11. package/src/components/charts/Sparkline.tsx +156 -0
  12. package/src/components/charts/index.ts +13 -0
  13. package/src/components/data-display/Avatar.tsx +208 -0
  14. package/src/components/data-display/AvatarGroup.tsx +228 -0
  15. package/src/components/data-display/Badge.tsx +70 -0
  16. package/src/components/data-display/Carousel.tsx +214 -0
  17. package/src/components/data-display/ColorSwatch.tsx +56 -0
  18. package/src/components/data-display/DataTable.tsx +886 -0
  19. package/src/components/data-display/EmptyState.tsx +61 -0
  20. package/src/components/data-display/Image.tsx +277 -0
  21. package/src/components/data-display/Kbd.tsx +114 -0
  22. package/src/components/data-display/Persona.tsx +78 -0
  23. package/src/components/data-display/StatCard.tsx +338 -0
  24. package/src/components/data-display/Table.tsx +147 -0
  25. package/src/components/data-display/Tag.tsx +91 -0
  26. package/src/components/data-display/Timeline.tsx +200 -0
  27. package/src/components/data-display/TreeView.tsx +172 -0
  28. package/src/components/data-display/Video.tsx +95 -0
  29. package/src/components/data-display/avatar-utils.ts +32 -0
  30. package/src/components/data-display/index.ts +81 -0
  31. package/src/components/feedback/Loading.tsx +159 -0
  32. package/src/components/feedback/Progress.tsx +321 -0
  33. package/src/components/feedback/Skeleton.tsx +62 -0
  34. package/src/components/feedback/SkeletonBlocks.tsx +222 -0
  35. package/src/components/feedback/Toast.tsx +648 -0
  36. package/src/components/feedback/index.ts +44 -0
  37. package/src/components/feedback/password/PasswordStrengthIndicator.tsx +232 -0
  38. package/src/components/feedback/password/password-strength.ts +115 -0
  39. package/src/components/feedback/password/password-validation-data.ts +66 -0
  40. package/src/components/feedback/password/password-validation.ts +93 -0
  41. package/src/components/forms/Autocomplete.tsx +268 -0
  42. package/src/components/forms/Checkbox.tsx +155 -0
  43. package/src/components/forms/CodeInput.tsx +237 -0
  44. package/src/components/forms/ColorPicker/ColorPicker.tsx +469 -0
  45. package/src/components/forms/ColorPicker/color-utils.ts +75 -0
  46. package/src/components/forms/ColorPicker/index.ts +2 -0
  47. package/src/components/forms/DatePicker.tsx +516 -0
  48. package/src/components/forms/DateRangePicker.tsx +464 -0
  49. package/src/components/forms/FieldPicker.tsx +64 -0
  50. package/src/components/forms/FileUpload.tsx +614 -0
  51. package/src/components/forms/FilterBuilder/FilterGroupBlock.ts +6 -0
  52. package/src/components/forms/FilterBuilder.tsx +16 -0
  53. package/src/components/forms/FilterRuleRow.tsx +68 -0
  54. package/src/components/forms/Input.tsx +200 -0
  55. package/src/components/forms/MultiSelect.tsx +361 -0
  56. package/src/components/forms/NumberField.tsx +145 -0
  57. package/src/components/forms/RadioGroup.tsx +135 -0
  58. package/src/components/forms/RelativeDateDefaultInput.tsx +62 -0
  59. package/src/components/forms/ReorderableList.tsx +163 -0
  60. package/src/components/forms/Select.tsx +268 -0
  61. package/src/components/forms/Slider.tsx +260 -0
  62. package/src/components/forms/Switch.tsx +135 -0
  63. package/src/components/forms/TextArea.tsx +202 -0
  64. package/src/components/forms/ViewCustomizer.tsx +44 -0
  65. package/src/components/forms/index.ts +43 -0
  66. package/src/components/layout/Accordion.tsx +110 -0
  67. package/src/components/layout/Alert.tsx +156 -0
  68. package/src/components/layout/BlockQuote.tsx +70 -0
  69. package/src/components/layout/Card.tsx +166 -0
  70. package/src/components/layout/CodeBlock/CodeBlock.tsx +477 -0
  71. package/src/components/layout/CodeBlock/code-block-tokens.css +104 -0
  72. package/src/components/layout/CodeBlock/prism.ts +81 -0
  73. package/src/components/layout/Collapsible.tsx +84 -0
  74. package/src/components/layout/Container.tsx +55 -0
  75. package/src/components/layout/Divider.tsx +64 -0
  76. package/src/components/layout/Form.tsx +39 -0
  77. package/src/components/layout/FormActions.tsx +50 -0
  78. package/src/components/layout/Grid.tsx +53 -0
  79. package/src/components/layout/PageHeading.tsx +46 -0
  80. package/src/components/layout/PromptWithAction.tsx +49 -0
  81. package/src/components/layout/Section.tsx +60 -0
  82. package/src/components/layout/TablePanel.tsx +24 -0
  83. package/src/components/layout/TableView/TableView.tsx +1018 -0
  84. package/src/components/layout/TableView/index.ts +3 -0
  85. package/src/components/layout/TableView/types.ts +51 -0
  86. package/src/components/layout/WizardStep.tsx +40 -0
  87. package/src/components/layout/WizardStepper.tsx +173 -0
  88. package/src/components/layout/index.ts +96 -0
  89. package/src/components/navigation/Breadcrumbs.tsx +66 -0
  90. package/src/components/navigation/DropdownMenu.tsx +86 -0
  91. package/src/components/navigation/MegaMenu.tsx +480 -0
  92. package/src/components/navigation/NavigationMenu.tsx +305 -0
  93. package/src/components/navigation/Pagination.tsx +298 -0
  94. package/src/components/navigation/Sidebar.tsx +280 -0
  95. package/src/components/navigation/Tabs.tsx +122 -0
  96. package/src/components/navigation/ViewSwitcher.tsx +314 -0
  97. package/src/components/navigation/index.ts +66 -0
  98. package/src/components/overlays/AlertDialog.tsx +174 -0
  99. package/src/components/overlays/ContextMenu.tsx +65 -0
  100. package/src/components/overlays/Dialog.tsx +279 -0
  101. package/src/components/overlays/Drawer.tsx +370 -0
  102. package/src/components/overlays/HoverCard.tsx +107 -0
  103. package/src/components/overlays/Popover.tsx +73 -0
  104. package/src/components/overlays/Tooltip.tsx +31 -0
  105. package/src/components/overlays/index.ts +71 -0
  106. package/src/components/typography/Code.tsx +72 -0
  107. package/src/components/typography/Icon.tsx +36 -0
  108. package/src/components/typography/index.ts +10 -0
  109. package/src/env.d.ts +9 -0
  110. package/src/index.ts +13 -0
  111. package/src/styles/theme.css +226 -0
  112. package/src/types/avatar-types.ts +11 -0
  113. package/src/types/filter-types.ts +35 -0
  114. package/src/utilities/classNames.ts +6 -0
  115. package/src/utilities/componentSize.ts +46 -0
  116. package/src/utilities/i18n.tsx +60 -0
  117. package/src/utilities/mergeRefs.ts +12 -0
  118. package/src/utilities/relativeDateDefault.ts +14 -0
@@ -0,0 +1,280 @@
1
+ import { type JSX, splitProps, Show, For } from 'solid-js'
2
+ import { cn } from '../../utilities/classNames'
3
+
4
+ export interface SidebarItem {
5
+ /** Unique key for the item */
6
+ key: string
7
+ /** Display label for the item */
8
+ label: string
9
+ /** Icon to display (optional) */
10
+ icon?: JSX.Element
11
+ /** Whether the item is currently active */
12
+ active?: boolean
13
+ /** Whether the item is disabled */
14
+ disabled?: boolean
15
+ /** Badge to show (optional) */
16
+ badge?: string | number
17
+ /** Click handler */
18
+ onClick?: () => void
19
+ /** Link href (renders as anchor if provided) */
20
+ href?: string
21
+ /** Sub-items for nested navigation */
22
+ items?: SidebarItem[]
23
+ }
24
+
25
+ export interface SidebarFooter {
26
+ /** Footer content */
27
+ content: JSX.Element
28
+ /** Whether footer is sticky to bottom (default: false) */
29
+ sticky?: boolean
30
+ }
31
+
32
+ export interface SidebarProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children'> {
33
+ /** Array of navigation items */
34
+ items: SidebarItem[]
35
+ /** Sidebar title */
36
+ title?: string
37
+ /** Whether to show the sidebar title */
38
+ showTitle?: boolean
39
+ /** Whether the sidebar is collapsible */
40
+ collapsible?: boolean
41
+ /** Whether the sidebar is collapsed */
42
+ collapsed?: boolean
43
+ /** Callback when collapse state changes */
44
+ onCollapseChange?: (collapsed: boolean) => void
45
+ /** Whether to show icons */
46
+ showIcons?: boolean | undefined
47
+ /** Whether to show badges */
48
+ showBadges?: boolean | undefined
49
+ /** Custom footer content */
50
+ footer?: JSX.Element | SidebarFooter
51
+ /** Variant styling */
52
+ variant?: 'default' | 'minimal' | 'padded'
53
+ }
54
+
55
+ /**
56
+ * Sidebar component with nested navigation support.
57
+ * Built with accessibility in mind and supports various styling options.
58
+ */
59
+ export function Sidebar(props: SidebarProps) {
60
+ const [local, others] = splitProps(props, [
61
+ 'items',
62
+ 'title',
63
+ 'showTitle',
64
+ 'collapsible',
65
+ 'collapsed',
66
+ 'onCollapseChange',
67
+ 'showIcons',
68
+ 'showBadges',
69
+ 'footer',
70
+ 'variant',
71
+ 'class',
72
+ ])
73
+
74
+ const variantClasses = () => {
75
+ switch (local.variant) {
76
+ case 'minimal':
77
+ return 'border-r border-ink-200 bg-surface-base'
78
+ case 'padded':
79
+ return 'border-r border-ink-200 bg-surface-raised px-3 py-2'
80
+ default:
81
+ return 'border-r border-ink-200 bg-surface-raised'
82
+ }
83
+ }
84
+
85
+ const sidebarClass = () =>
86
+ cn(
87
+ local.footer && typeof local.footer === 'object' && 'sticky' in local.footer && local.footer.sticky === true
88
+ ? 'flex flex-col h-full min-w-0'
89
+ : 'h-full overflow-x-hidden overflow-y-auto min-w-0',
90
+ variantClasses(),
91
+ local.collapsed ? 'w-16' : local.variant === 'padded' ? 'w-72' : 'w-64',
92
+ 'transition-all duration-300 ease-in-out',
93
+ local.class
94
+ )
95
+
96
+ const titleClass = () =>
97
+ cn(
98
+ 'px-4 py-3 text-lg font-semibold text-ink-900 border-b border-ink-200',
99
+ local.collapsed ? 'hidden' : 'block'
100
+ )
101
+
102
+ const navigationClass = () =>
103
+ cn(
104
+ local.footer && typeof local.footer === 'object' && 'sticky' in local.footer && local.footer.sticky === true
105
+ ? 'flex-1 overflow-x-hidden overflow-y-auto'
106
+ : '',
107
+ local.variant === 'padded' ? 'py-1' : 'py-4'
108
+ )
109
+
110
+ const footerClass = () =>
111
+ cn(
112
+ 'border-t border-ink-200 p-4',
113
+ local.collapsed ? 'hidden' : 'block'
114
+ )
115
+
116
+ const renderSidebarItem = (item: SidebarItem, level: number = 0) => {
117
+ const itemClass = () =>
118
+ cn(
119
+ 'flex items-center w-full min-w-0 px-3 py-2 text-sm rounded-md transition-colors',
120
+ 'hover:bg-surface-hover focus:bg-surface-hover focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
121
+ item.active
122
+ ? 'bg-primary-600 text-white font-medium'
123
+ : 'text-ink-700 hover:text-ink-900 dark:hover:text-ink-900',
124
+ item.disabled && 'opacity-50 cursor-not-allowed',
125
+ level > 0 && 'ml-4'
126
+ )
127
+
128
+ const iconClass = () =>
129
+ cn(
130
+ 'w-5 h-5 flex-shrink-0',
131
+ (local.showIcons ?? true) && !local.collapsed && 'mr-3',
132
+ (local.showIcons ?? true) && local.collapsed && 'mx-auto',
133
+ item.active ? 'text-white' : 'text-ink-500'
134
+ )
135
+
136
+ const badgeClass = () =>
137
+ cn(
138
+ 'px-2 py-1 text-xs rounded-full font-medium flex-shrink-0',
139
+ (local.showBadges ?? true) && !local.collapsed && 'ml-auto',
140
+ item.active
141
+ ? 'bg-white text-primary-600'
142
+ : 'bg-surface-dim text-ink-600'
143
+ )
144
+
145
+ const ItemContent = () => (
146
+ <>
147
+ {/* Icon */}
148
+ <Show when={item.icon && (local.showIcons ?? true)}>
149
+ <div class={iconClass()}>
150
+ {item.icon}
151
+ </div>
152
+ </Show>
153
+
154
+ {/* Label */}
155
+ <span class="truncate">{item.label}</span>
156
+
157
+ {/* Badge */}
158
+ <Show when={item.badge && (local.showBadges ?? true) && !local.collapsed}>
159
+ <span class={badgeClass()}>
160
+ {item.badge}
161
+ </span>
162
+ </Show>
163
+ </>
164
+ )
165
+
166
+ // Handle nested items
167
+ if (item.items && item.items.length > 0) {
168
+ return (
169
+ <li role="listitem">
170
+ <div class="space-y-1">
171
+ {/* Parent item */}
172
+ {item.href ? (
173
+ <a
174
+ href={item.href}
175
+ class={itemClass()}
176
+ aria-current={item.active ? 'page' : undefined}
177
+ >
178
+ <ItemContent />
179
+ </a>
180
+ ) : (
181
+ <button
182
+ type="button"
183
+ class={itemClass()}
184
+ onClick={item.onClick}
185
+ disabled={item.disabled}
186
+ aria-current={item.active ? 'page' : undefined}
187
+ >
188
+ <ItemContent />
189
+ </button>
190
+ )}
191
+
192
+ {/* Nested items */}
193
+ <Show when={!local.collapsed}>
194
+ <div class="ml-4 space-y-1">
195
+ {renderNavigationItems(item.items, level + 1)}
196
+ </div>
197
+ </Show>
198
+ </div>
199
+ </li>
200
+ )
201
+ }
202
+
203
+ // Handle regular items
204
+ return (
205
+ <li role="listitem">
206
+ {item.href ? (
207
+ <a
208
+ href={item.href}
209
+ class={itemClass()}
210
+ onClick={item.onClick}
211
+ aria-current={item.active ? 'page' : undefined}
212
+ >
213
+ <ItemContent />
214
+ </a>
215
+ ) : (
216
+ <button
217
+ type="button"
218
+ class={itemClass()}
219
+ onClick={item.onClick}
220
+ disabled={item.disabled}
221
+ aria-current={item.active ? 'page' : undefined}
222
+ >
223
+ <ItemContent />
224
+ </button>
225
+ )}
226
+ </li>
227
+ )
228
+ }
229
+
230
+ const renderNavigationItems = (items: SidebarItem[], level: number = 0) => {
231
+ return (
232
+ <div class="space-y-1 min-w-0">
233
+ <For each={items}>
234
+ {(item) => {
235
+ if (item.items && item.items.length > 0) {
236
+ return (
237
+ <div class="space-y-1 min-w-0">
238
+ {renderSidebarItem(item, level)}
239
+ </div>
240
+ )
241
+ }
242
+ return renderSidebarItem(item, level)
243
+ }}
244
+ </For>
245
+ </div>
246
+ )
247
+ }
248
+
249
+ return (
250
+ <div class={sidebarClass()} {...others}>
251
+ {/* Header */}
252
+ <Show when={local.title && local.showTitle !== false}>
253
+ <div class={titleClass()}>
254
+ {!local.collapsed && local.title}
255
+ </div>
256
+ </Show>
257
+
258
+ {/* Navigation */}
259
+ <nav
260
+ class={navigationClass()}
261
+ aria-label={local.title || 'Sidebar navigation'}
262
+ >
263
+ <ul class="space-y-1" role="list">
264
+ {renderNavigationItems(local.items)}
265
+ </ul>
266
+ </nav>
267
+
268
+ {/* Footer */}
269
+ <Show when={local.footer}>
270
+ <div class={footerClass()}>
271
+ {!local.collapsed && local.footer && (
272
+ typeof local.footer === 'object' && 'content' in local.footer
273
+ ? local.footer.content
274
+ : local.footer
275
+ )}
276
+ </div>
277
+ </Show>
278
+ </div>
279
+ )
280
+ }
@@ -0,0 +1,122 @@
1
+ import { For, splitProps, type Accessor } from 'solid-js'
2
+ import { Tabs as KobalteTabs, type TabsRootProps as KobalteTabsRootProps, type TabsListProps as KobalteTabsListProps, type TabsTriggerProps as KobalteTabsTriggerProps, type TabsContentProps as KobalteTabsContentProps } from '@kobalte/core/tabs'
3
+ import { cn } from '../../utilities/classNames'
4
+
5
+ /** Kobalte tabs root. Use for custom tab layouts; prefer Tabs for the standard tab bar. */
6
+ export { KobalteTabs }
7
+
8
+ export interface TabItem {
9
+ id: string
10
+ label: string
11
+ }
12
+
13
+ export interface TabsProps
14
+ extends Omit<KobalteTabsRootProps, 'value' | 'defaultValue' | 'onChange'> {
15
+ tabs: TabItem[]
16
+ /** Current tab id (controlled), or omit and use defaultValue for uncontrolled. */
17
+ value?: string | Accessor<string>
18
+ /** Initial tab id when uncontrolled. */
19
+ defaultValue?: string
20
+ /** Called when selected tab changes. Optional for uncontrolled usage. */
21
+ onValueChange?: (tabId: string) => void
22
+ /** Accessible label for the tab list. Default: "Tabs". */
23
+ ariaLabel?: string
24
+ class?: string
25
+ }
26
+
27
+ /** Generic tab bar. Content is rendered by the parent based on value. */
28
+ export function Tabs(props: TabsProps) {
29
+ const [local, others] = splitProps(props, [
30
+ 'tabs', 'value', 'defaultValue', 'onValueChange', 'ariaLabel', 'class',
31
+ ])
32
+
33
+ const resolvedValue = () => {
34
+ const v = local.value
35
+ if (v === undefined) return undefined
36
+ return typeof v === 'function' ? v() : v
37
+ }
38
+ const isControlled = () => local.value !== undefined
39
+ // Tab ids are never empty — coerce empty string to undefined so Kobalte
40
+ // doesn't try to select a non-existent tab.
41
+ const normalizedValue = () => {
42
+ const v = resolvedValue()
43
+ return v && v.length > 0 ? v : undefined
44
+ }
45
+
46
+ return (
47
+ <div class={cn('mb-6 border-b border-surface-border', local.class)}>
48
+ <KobalteTabs
49
+ value={isControlled() ? normalizedValue() : undefined}
50
+ defaultValue={!isControlled() ? (local.defaultValue ?? local.tabs[0]?.id) : undefined}
51
+ onChange={local.onValueChange}
52
+ {...others}
53
+ >
54
+ <KobalteTabs.List
55
+ class="flex w-full flex-nowrap gap-1 overflow-x-auto rounded-none border-0 bg-transparent p-0"
56
+ aria-label={local.ariaLabel ?? 'Tabs'}
57
+ >
58
+ <For each={local.tabs}>
59
+ {(tab) => (
60
+ <KobalteTabs.Trigger
61
+ value={tab.id}
62
+ class="shrink-0 whitespace-nowrap rounded-none border-b-2 border-transparent bg-transparent px-5 py-3 text-sm font-medium text-ink-500 transition-colors hover:border-primary-300 hover:text-primary-700 data-[selected]:border-primary-500 data-[selected]:bg-transparent data-[selected]:text-primary-600 data-[selected]:shadow-none dark:hover:text-primary-400 dark:data-[selected]:border-primary-400 dark:data-[selected]:text-primary-400"
63
+ >
64
+ {tab.label}
65
+ </KobalteTabs.Trigger>
66
+ )}
67
+ </For>
68
+ </KobalteTabs.List>
69
+ </KobalteTabs>
70
+ </div>
71
+ )
72
+ }
73
+
74
+ export interface TabsListProps extends KobalteTabsListProps {
75
+ class?: string
76
+ }
77
+
78
+ export function TabsList(props: TabsListProps) {
79
+ const [local, others] = splitProps(props, ['class'])
80
+ return (
81
+ <KobalteTabs.List
82
+ class={cn(
83
+ 'inline-flex items-center gap-1 rounded-lg border border-surface-border bg-surface-raised p-1',
84
+ local.class,
85
+ )}
86
+ {...others}
87
+ />
88
+ )
89
+ }
90
+
91
+ export interface TabsTriggerProps extends KobalteTabsTriggerProps {
92
+ class?: string
93
+ }
94
+
95
+ export function TabsTrigger(props: TabsTriggerProps) {
96
+ const [local, others] = splitProps(props, ['class'])
97
+ return (
98
+ <KobalteTabs.Trigger
99
+ class={cn(
100
+ 'rounded-md px-3 py-1.5 text-xs font-medium text-ink-500 transition-colors',
101
+ 'data-[selected]:bg-surface-overlay data-[selected]:text-ink-900',
102
+ 'data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed',
103
+ local.class,
104
+ )}
105
+ {...others}
106
+ />
107
+ )
108
+ }
109
+
110
+ export interface TabsContentProps extends KobalteTabsContentProps {
111
+ class?: string
112
+ }
113
+
114
+ export function TabsContent(props: TabsContentProps) {
115
+ const [local, others] = splitProps(props, ['class'])
116
+ return (
117
+ <KobalteTabs.Content
118
+ class={cn('mt-4', local.class)}
119
+ {...others}
120
+ />
121
+ )
122
+ }