@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,214 @@
1
+ import { type JSX, createSignal, createEffect, onMount, onCleanup, splitProps, For, Show } from 'solid-js'
2
+ import { ChevronLeft, ChevronRight } from 'lucide-solid'
3
+ import { cn } from '../../utilities/classNames'
4
+
5
+ export interface CarouselSlide {
6
+ id: string
7
+ content: JSX.Element
8
+ }
9
+
10
+ export interface CarouselProps extends JSX.HTMLAttributes<HTMLDivElement> {
11
+ slides: CarouselSlide[]
12
+ /** Auto-advance interval in ms (cycle time); 0 to disable */
13
+ autoPlayInterval?: number
14
+ /** Show dot indicators below the carousel. Default true; set false to hide. */
15
+ showDots?: boolean
16
+ /** Show prev/next arrow buttons. Default false; set showArrows or showArrows={true} to enable. */
17
+ showArrows?: boolean
18
+ /** Dot indicators alignment: start, center, or end. Default start. */
19
+ dotsPosition?: 'start' | 'center' | 'end'
20
+ /** Accessible label for the carousel region. */
21
+ 'aria-label'?: string
22
+ }
23
+
24
+ export function Carousel(props: CarouselProps) {
25
+ const [local, others] = splitProps(props, [
26
+ 'slides',
27
+ 'autoPlayInterval',
28
+ 'showDots',
29
+ 'showArrows',
30
+ 'dotsPosition',
31
+ 'aria-label',
32
+ 'class',
33
+ ])
34
+
35
+ const [currentSlide, setCurrentSlide] = createSignal(0)
36
+ const [progressBarReady, setProgressBarReady] = createSignal(false)
37
+ const autoPlayInterval = () => local.autoPlayInterval ?? 5000
38
+
39
+ let intervalId: ReturnType<typeof setInterval> | undefined
40
+
41
+ // Clamp currentSlide when slides array shrinks
42
+ createEffect(() => {
43
+ const max = Math.max(local.slides.length - 1, 0)
44
+ setCurrentSlide(i => Math.min(i, max))
45
+ })
46
+
47
+ // Reactive autoplay: restart timer when interval or slides change
48
+ createEffect(() => {
49
+ const interval = autoPlayInterval()
50
+ const len = local.slides.length
51
+ if (intervalId) clearInterval(intervalId)
52
+ intervalId = undefined
53
+ if (interval > 0 && len > 1) {
54
+ intervalId = setInterval(nextSlide, interval)
55
+ }
56
+ restartProgressBar()
57
+ })
58
+
59
+ function nextSlide() {
60
+ const len = local.slides.length
61
+ if (len === 0) return
62
+ setCurrentSlide((prev) => (prev + 1) % len)
63
+ restartProgressBar()
64
+ }
65
+
66
+ function restartProgressBar() {
67
+ setProgressBarReady(false)
68
+ requestAnimationFrame(() => setProgressBarReady(true))
69
+ }
70
+
71
+ function goToSlide(index: number) {
72
+ const len = local.slides.length
73
+ if (len === 0) return
74
+ const i = ((index % len) + len) % len
75
+ setCurrentSlide(i)
76
+ restartProgressBar()
77
+ if (intervalId) clearInterval(intervalId)
78
+ const interval = autoPlayInterval()
79
+ if (interval > 0 && len > 1) {
80
+ intervalId = setInterval(nextSlide, interval)
81
+ }
82
+ }
83
+
84
+ function goPrev() { goToSlide(currentSlide() - 1) }
85
+ function goNext() { goToSlide(currentSlide() + 1) }
86
+
87
+ onMount(() => {
88
+ // Defer progress bar animation by one frame so it runs on initial load (browser quirk)
89
+ const raf = requestAnimationFrame(() => setProgressBarReady(true))
90
+ onCleanup(() => cancelAnimationFrame(raf))
91
+ })
92
+
93
+ onCleanup(() => {
94
+ if (intervalId) clearInterval(intervalId)
95
+ })
96
+
97
+ return (
98
+ <div
99
+ role="region"
100
+ aria-roledescription="carousel"
101
+ aria-label={local['aria-label'] ?? 'Carousel'}
102
+ {...others}
103
+ class={cn('relative w-full', local.class)}
104
+ >
105
+ {/* Track: inline styles so overflow/position always apply regardless of Tailwind scan */}
106
+ <div
107
+ class="min-h-0 transition-opacity duration-500"
108
+ style={{ position: 'relative', width: '100%', overflow: 'hidden' }}
109
+ >
110
+ <For each={local.slides}>
111
+ {(slide, index) => {
112
+ const isActive = () => index() === currentSlide()
113
+ return (
114
+ <div
115
+ aria-hidden={!isActive()}
116
+ class={cn('transition-opacity duration-500', isActive() ? 'pointer-events-auto' : 'pointer-events-none')}
117
+ style={
118
+ isActive()
119
+ ? {
120
+ position: 'relative' as const,
121
+ opacity: 1,
122
+ visibility: 'visible' as const,
123
+ 'z-index': 2,
124
+ }
125
+ : {
126
+ position: 'absolute' as const,
127
+ inset: 0,
128
+ width: '100%',
129
+ height: '100%',
130
+ opacity: 0,
131
+ visibility: 'hidden' as const,
132
+ 'z-index': 1,
133
+ }
134
+ }
135
+ >
136
+ {slide.content}
137
+ </div>
138
+ )
139
+ }}
140
+ </For>
141
+ </div>
142
+
143
+ <Show when={local.showArrows === true && local.slides.length > 1}>
144
+ <button
145
+ type="button"
146
+ onClick={goPrev}
147
+ aria-label="Previous slide"
148
+ class="absolute left-2 top-1/2 z-10 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70 focus:outline-none focus:ring-2 focus:ring-white/50"
149
+ >
150
+ <ChevronLeft class="h-5 w-5" />
151
+ </button>
152
+ <button
153
+ type="button"
154
+ onClick={goNext}
155
+ aria-label="Next slide"
156
+ class="absolute right-2 top-1/2 z-10 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70 focus:outline-none focus:ring-2 focus:ring-white/50"
157
+ >
158
+ <ChevronRight class="h-5 w-5" />
159
+ </button>
160
+ </Show>
161
+
162
+ <Show when={local.showDots !== false && local.slides.length > 1}>
163
+ <div
164
+ class={cn(
165
+ 'flex gap-2 mt-8',
166
+ (local.dotsPosition ?? 'start') === 'end' && 'justify-end',
167
+ (local.dotsPosition ?? 'start') === 'center' && 'justify-center',
168
+ (local.dotsPosition ?? 'start') === 'start' && 'justify-start'
169
+ )}
170
+ >
171
+ <For each={local.slides}>
172
+ {(_, index) => (
173
+ <button
174
+ type="button"
175
+ onClick={() => goToSlide(index())}
176
+ class={cn(
177
+ 'h-2 rounded-full transition-all duration-300',
178
+ index() === currentSlide()
179
+ ? 'w-8 bg-white/40 relative overflow-hidden'
180
+ : 'w-2 bg-white/40 hover:bg-white/60'
181
+ )}
182
+ aria-label={`Go to slide ${index() + 1}`}
183
+ aria-current={index() === currentSlide() ? 'page' : undefined}
184
+ >
185
+ <Show when={index() === currentSlide() && autoPlayInterval() > 0}>
186
+ <div
187
+ class="absolute top-0 left-0 h-full bg-white rounded-full"
188
+ style={
189
+ progressBarReady()
190
+ ? {
191
+ animation: `carouselProgressBar ${autoPlayInterval()}ms linear forwards`,
192
+ 'animation-fill-mode': 'forwards',
193
+ }
194
+ : { width: '0%' }
195
+ }
196
+ />
197
+ </Show>
198
+ </button>
199
+ )}
200
+ </For>
201
+ </div>
202
+ </Show>
203
+
204
+ <style>
205
+ {`
206
+ @keyframes carouselProgressBar {
207
+ from { width: 0%; }
208
+ to { width: 100%; }
209
+ }
210
+ `}
211
+ </style>
212
+ </div>
213
+ )
214
+ }
@@ -0,0 +1,56 @@
1
+ import { splitProps, createMemo, type JSX } from 'solid-js'
2
+ import { ColorSwatch as KobalteColorSwatch } from '@kobalte/core/color-swatch'
3
+ import { parseColor } from '@kobalte/core/colors'
4
+ import { cn } from '../../utilities/classNames'
5
+ import { normalizeHex, hexToHslString } from '../forms/ColorPicker/color-utils'
6
+
7
+ export type ColorSwatchVariant = 'rounded' | 'circle' | 'square'
8
+
9
+ export interface ColorSwatchProps {
10
+ /** Color as hex string (e.g. #3b82f6). */
11
+ value: string
12
+ /** Shape: rounded (default), circle, or square. */
13
+ variant?: ColorSwatchVariant
14
+ /** Optional accessible name for the color. */
15
+ colorName?: string
16
+ class?: string
17
+ style?: JSX.CSSProperties
18
+ 'aria-label'?: string
19
+ }
20
+
21
+ const variantClass: Record<ColorSwatchVariant, string> = {
22
+ rounded: 'rounded-lg',
23
+ circle: 'rounded-full',
24
+ square: 'rounded-none',
25
+ }
26
+
27
+ /** Renders a color swatch (Kobalte ColorSwatch). Value is a hex string. */
28
+ export function ColorSwatch(props: ColorSwatchProps) {
29
+ const [local, rest] = splitProps(props, ['value', 'variant', 'colorName', 'class', 'style'])
30
+ const normalized = createMemo(() => normalizeHex(local.value ?? ''))
31
+ const color = createMemo(() => {
32
+ const hex = normalized()
33
+ if (!hex) return parseColor('hsl(0, 0%, 50%)')
34
+ try {
35
+ return parseColor(hexToHslString(hex))
36
+ } catch {
37
+ return parseColor('hsl(0, 0%, 50%)')
38
+ }
39
+ })
40
+ const shapeClass = () =>
41
+ variantClass[local.variant ?? 'rounded']
42
+ return (
43
+ <KobalteColorSwatch
44
+ value={color()}
45
+ colorName={local.colorName}
46
+ aria-label={rest['aria-label'] ?? local.colorName ?? normalized() ?? 'Color swatch'}
47
+ class={cn(
48
+ 'block shrink-0 border-2 border-surface-border shadow-sm',
49
+ shapeClass(),
50
+ local.class
51
+ )}
52
+ style={local.style}
53
+ {...rest}
54
+ />
55
+ )
56
+ }