@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.
- package/README.md +166 -0
- package/package.json +67 -0
- package/src/components/actions/Button.tsx +612 -0
- package/src/components/actions/ButtonGroup.tsx +728 -0
- package/src/components/actions/Copy.tsx +98 -0
- package/src/components/actions/DarkModeToggle.tsx +80 -0
- package/src/components/actions/Link.tsx +37 -0
- package/src/components/actions/index.ts +19 -0
- package/src/components/actions/useCopyToClipboard.ts +90 -0
- package/src/components/charts/Chart.tsx +331 -0
- package/src/components/charts/Sparkline.tsx +156 -0
- package/src/components/charts/index.ts +13 -0
- package/src/components/data-display/Avatar.tsx +208 -0
- package/src/components/data-display/AvatarGroup.tsx +228 -0
- package/src/components/data-display/Badge.tsx +70 -0
- package/src/components/data-display/Carousel.tsx +214 -0
- package/src/components/data-display/ColorSwatch.tsx +56 -0
- package/src/components/data-display/DataTable.tsx +886 -0
- package/src/components/data-display/EmptyState.tsx +61 -0
- package/src/components/data-display/Image.tsx +277 -0
- package/src/components/data-display/Kbd.tsx +114 -0
- package/src/components/data-display/Persona.tsx +78 -0
- package/src/components/data-display/StatCard.tsx +338 -0
- package/src/components/data-display/Table.tsx +147 -0
- package/src/components/data-display/Tag.tsx +91 -0
- package/src/components/data-display/Timeline.tsx +200 -0
- package/src/components/data-display/TreeView.tsx +172 -0
- package/src/components/data-display/Video.tsx +95 -0
- package/src/components/data-display/avatar-utils.ts +32 -0
- package/src/components/data-display/index.ts +81 -0
- package/src/components/feedback/Loading.tsx +159 -0
- package/src/components/feedback/Progress.tsx +321 -0
- package/src/components/feedback/Skeleton.tsx +62 -0
- package/src/components/feedback/SkeletonBlocks.tsx +222 -0
- package/src/components/feedback/Toast.tsx +648 -0
- package/src/components/feedback/index.ts +44 -0
- package/src/components/feedback/password/PasswordStrengthIndicator.tsx +232 -0
- package/src/components/feedback/password/password-strength.ts +115 -0
- package/src/components/feedback/password/password-validation-data.ts +66 -0
- package/src/components/feedback/password/password-validation.ts +93 -0
- package/src/components/forms/Autocomplete.tsx +268 -0
- package/src/components/forms/Checkbox.tsx +155 -0
- package/src/components/forms/CodeInput.tsx +237 -0
- package/src/components/forms/ColorPicker/ColorPicker.tsx +469 -0
- package/src/components/forms/ColorPicker/color-utils.ts +75 -0
- package/src/components/forms/ColorPicker/index.ts +2 -0
- package/src/components/forms/DatePicker.tsx +516 -0
- package/src/components/forms/DateRangePicker.tsx +464 -0
- package/src/components/forms/FieldPicker.tsx +64 -0
- package/src/components/forms/FileUpload.tsx +614 -0
- package/src/components/forms/FilterBuilder/FilterGroupBlock.ts +6 -0
- package/src/components/forms/FilterBuilder.tsx +16 -0
- package/src/components/forms/FilterRuleRow.tsx +68 -0
- package/src/components/forms/Input.tsx +200 -0
- package/src/components/forms/MultiSelect.tsx +361 -0
- package/src/components/forms/NumberField.tsx +145 -0
- package/src/components/forms/RadioGroup.tsx +135 -0
- package/src/components/forms/RelativeDateDefaultInput.tsx +62 -0
- package/src/components/forms/ReorderableList.tsx +163 -0
- package/src/components/forms/Select.tsx +268 -0
- package/src/components/forms/Slider.tsx +260 -0
- package/src/components/forms/Switch.tsx +135 -0
- package/src/components/forms/TextArea.tsx +202 -0
- package/src/components/forms/ViewCustomizer.tsx +44 -0
- package/src/components/forms/index.ts +43 -0
- package/src/components/layout/Accordion.tsx +110 -0
- package/src/components/layout/Alert.tsx +156 -0
- package/src/components/layout/BlockQuote.tsx +70 -0
- package/src/components/layout/Card.tsx +166 -0
- package/src/components/layout/CodeBlock/CodeBlock.tsx +477 -0
- package/src/components/layout/CodeBlock/code-block-tokens.css +104 -0
- package/src/components/layout/CodeBlock/prism.ts +81 -0
- package/src/components/layout/Collapsible.tsx +84 -0
- package/src/components/layout/Container.tsx +55 -0
- package/src/components/layout/Divider.tsx +64 -0
- package/src/components/layout/Form.tsx +39 -0
- package/src/components/layout/FormActions.tsx +50 -0
- package/src/components/layout/Grid.tsx +53 -0
- package/src/components/layout/PageHeading.tsx +46 -0
- package/src/components/layout/PromptWithAction.tsx +49 -0
- package/src/components/layout/Section.tsx +60 -0
- package/src/components/layout/TablePanel.tsx +24 -0
- package/src/components/layout/TableView/TableView.tsx +1018 -0
- package/src/components/layout/TableView/index.ts +3 -0
- package/src/components/layout/TableView/types.ts +51 -0
- package/src/components/layout/WizardStep.tsx +40 -0
- package/src/components/layout/WizardStepper.tsx +173 -0
- package/src/components/layout/index.ts +96 -0
- package/src/components/navigation/Breadcrumbs.tsx +66 -0
- package/src/components/navigation/DropdownMenu.tsx +86 -0
- package/src/components/navigation/MegaMenu.tsx +480 -0
- package/src/components/navigation/NavigationMenu.tsx +305 -0
- package/src/components/navigation/Pagination.tsx +298 -0
- package/src/components/navigation/Sidebar.tsx +280 -0
- package/src/components/navigation/Tabs.tsx +122 -0
- package/src/components/navigation/ViewSwitcher.tsx +314 -0
- package/src/components/navigation/index.ts +66 -0
- package/src/components/overlays/AlertDialog.tsx +174 -0
- package/src/components/overlays/ContextMenu.tsx +65 -0
- package/src/components/overlays/Dialog.tsx +279 -0
- package/src/components/overlays/Drawer.tsx +370 -0
- package/src/components/overlays/HoverCard.tsx +107 -0
- package/src/components/overlays/Popover.tsx +73 -0
- package/src/components/overlays/Tooltip.tsx +31 -0
- package/src/components/overlays/index.ts +71 -0
- package/src/components/typography/Code.tsx +72 -0
- package/src/components/typography/Icon.tsx +36 -0
- package/src/components/typography/index.ts +10 -0
- package/src/env.d.ts +9 -0
- package/src/index.ts +13 -0
- package/src/styles/theme.css +226 -0
- package/src/types/avatar-types.ts +11 -0
- package/src/types/filter-types.ts +35 -0
- package/src/utilities/classNames.ts +6 -0
- package/src/utilities/componentSize.ts +46 -0
- package/src/utilities/i18n.tsx +60 -0
- package/src/utilities/mergeRefs.ts +12 -0
- 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
|
+
}
|