@gunjo/ui 0.0.1-alpha.1 → 0.0.1-alpha.2
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/LICENSE +21 -0
- package/README.ja.md +90 -0
- package/README.md +52 -91
- package/package.json +47 -6
- package/src/components/display/Accordion.tsx +185 -0
- package/src/components/display/AccordionGroup.tsx +155 -0
- package/src/components/display/ActionDataTable.tsx +413 -0
- package/src/components/display/ActivityTimelineCard.tsx +483 -0
- package/src/components/display/AnalyticsCard.tsx +167 -0
- package/src/components/display/AssetCard.tsx +242 -0
- package/src/components/display/AssetGrid.tsx +164 -0
- package/src/components/display/Avatar.tsx +127 -0
- package/src/components/display/AvatarGroup.tsx +131 -0
- package/src/components/{atoms → display}/Badge.tsx +3 -3
- package/src/components/display/BarChart.tsx +247 -0
- package/src/components/{molecules → display}/Card.tsx +1 -1
- package/src/components/display/Carousel.tsx +593 -0
- package/src/components/display/ChartLegend.tsx +124 -0
- package/src/components/display/ChatMessage.tsx +382 -0
- package/src/components/display/ChoroplethMap.tsx +613 -0
- package/src/components/display/Code.tsx +42 -0
- package/src/components/display/CodeBlock.tsx +338 -0
- package/src/components/display/ColorSwatch.tsx +71 -0
- package/src/components/display/ConcentricProgressCard.tsx +545 -0
- package/src/components/display/DataTable.tsx +522 -0
- package/src/components/display/DistributionBar.tsx +102 -0
- package/src/components/display/DocNote.tsx +36 -0
- package/src/components/display/DonutChart.tsx +257 -0
- package/src/components/display/EmptyState.tsx +44 -0
- package/src/components/display/FileTree.tsx +180 -0
- package/src/components/display/GaugeChart.tsx +219 -0
- package/src/components/display/HeatmapChart.tsx +266 -0
- package/src/components/display/Icon.tsx +66 -0
- package/src/components/display/ImagePreview.tsx +140 -0
- package/src/components/{atoms → display}/Img.tsx +46 -12
- package/src/components/display/LabeledDonutCard.tsx +475 -0
- package/src/components/display/LineChart.tsx +464 -0
- package/src/components/{molecules → display}/List.tsx +20 -13
- package/src/components/display/MarkdownRenderer.tsx +157 -0
- package/src/components/display/MetadataList.tsx +81 -0
- package/src/components/display/MiniDistributionBarCard.tsx +314 -0
- package/src/components/display/PieChart.tsx +234 -0
- package/src/components/display/QuadrantMatrix.tsx +330 -0
- package/src/components/display/RadarChart.tsx +335 -0
- package/src/components/display/RadialBarChart.tsx +264 -0
- package/src/components/display/RetentionCohortCard.tsx +350 -0
- package/src/components/display/RibbonChart.tsx +618 -0
- package/src/components/display/SearchableAccordion.tsx +270 -0
- package/src/components/display/SegmentTimelineCard.tsx +452 -0
- package/src/components/display/SegmentedGaugeCard.tsx +607 -0
- package/src/components/display/Spacer.tsx +51 -0
- package/src/components/display/SparklineChart.tsx +394 -0
- package/src/components/display/StackedBarChart.tsx +393 -0
- package/src/components/display/Statistic.tsx +70 -0
- package/src/components/{molecules → display}/Table.tsx +22 -7
- package/src/components/display/Tag.tsx +80 -0
- package/src/components/display/TagEditor.tsx +141 -0
- package/src/components/display/Timeline.tsx +121 -0
- package/src/components/{atoms → display}/ToolPill.tsx +42 -18
- package/src/components/display/TreeView.tsx +226 -0
- package/src/components/display/chart-tooltip.tsx +423 -0
- package/src/components/display/chart-utils.ts +71 -0
- package/src/components/display/circular-chart-utils.ts +147 -0
- package/src/components/display/generated/default-variant-keys.ts +90 -0
- package/src/components/display/generated/variant-keys.ts +169 -0
- package/src/components/{atoms → feedback}/Alert.tsx +12 -5
- package/src/components/feedback/Banner.tsx +90 -0
- package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
- package/src/components/feedback/ProgressWidget.tsx +44 -0
- package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
- package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
- package/src/components/feedback/StatusScreen.tsx +148 -0
- package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
- package/src/components/feedback/Toast.tsx +108 -0
- package/src/components/feedback/ToastProvider.tsx +78 -0
- package/src/components/feedback/generated/default-variant-keys.ts +16 -0
- package/src/components/feedback/generated/variant-keys.ts +21 -0
- package/src/components/generated/component-manifest.ts +1568 -454
- package/src/components/generated/component-style-hints.ts +1958 -718
- package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
- package/src/components/inputs/Calendar.tsx +212 -0
- package/src/components/inputs/ChatComposer.tsx +75 -0
- package/src/components/inputs/ChatInput.tsx +528 -0
- package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
- package/src/components/inputs/Combobox.tsx +175 -0
- package/src/components/inputs/CopyButton.tsx +187 -0
- package/src/components/inputs/DatePicker.tsx +519 -0
- package/src/components/inputs/DateRangePicker.tsx +878 -0
- package/src/components/inputs/EditableField.tsx +182 -0
- package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
- package/src/components/inputs/FilterButton.tsx +163 -0
- package/src/components/{molecules → inputs}/Form.tsx +20 -3
- package/src/components/{atoms → inputs}/Input.tsx +2 -0
- package/src/components/inputs/InputOTP.tsx +75 -0
- package/src/components/inputs/Mention.tsx +279 -0
- package/src/components/inputs/NumberInput.tsx +109 -0
- package/src/components/inputs/PasswordGroup.tsx +138 -0
- package/src/components/inputs/PasswordInput.tsx +74 -0
- package/src/components/inputs/PasswordRequirementList.tsx +96 -0
- package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
- package/src/components/inputs/PhoneInput.tsx +99 -0
- package/src/components/inputs/PostalCodeInput.tsx +98 -0
- package/src/components/inputs/RangeSlider.tsx +129 -0
- package/src/components/inputs/SearchInput.tsx +76 -0
- package/src/components/inputs/Select.tsx +39 -0
- package/src/components/{atoms → inputs}/Slider.tsx +18 -5
- package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
- package/src/components/{atoms → inputs}/Switch.tsx +15 -4
- package/src/components/inputs/TagInput.tsx +114 -0
- package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
- package/src/components/inputs/TimePicker.tsx +150 -0
- package/src/components/inputs/Toggle.tsx +48 -0
- package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
- package/src/components/inputs/TooltipButton.tsx +148 -0
- package/src/components/inputs/VoiceInputButton.tsx +317 -0
- package/src/components/inputs/calendar-holidays.ts +56 -0
- package/src/components/inputs/generated/default-variant-keys.ts +32 -0
- package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
- package/src/components/layout/AspectRatio.tsx +12 -0
- package/src/components/layout/AssetInspectorPanel.tsx +416 -0
- package/src/components/layout/Cluster.tsx +56 -0
- package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
- package/src/components/layout/Container.tsx +43 -0
- package/src/components/layout/DeviceFrame.tsx +227 -0
- package/src/components/layout/Grid.tsx +65 -0
- package/src/components/layout/HStack.tsx +73 -0
- package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
- package/src/components/layout/MarqueeFrame.tsx +158 -0
- package/src/components/layout/Resizable.tsx +94 -0
- package/src/components/layout/ScrollArea.tsx +71 -0
- package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
- package/src/components/layout/VStack.tsx +69 -0
- package/src/components/layout/generated/default-variant-keys.ts +16 -0
- package/src/components/layout/generated/variant-keys.ts +21 -0
- package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
- package/src/components/navigation/Command.tsx +266 -0
- package/src/components/navigation/CommandPalette.tsx +83 -0
- package/src/components/navigation/DocumentPager.tsx +171 -0
- package/src/components/navigation/Footer.tsx +88 -0
- package/src/components/navigation/Header.tsx +80 -0
- package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
- package/src/components/navigation/NavigationMenu.tsx +128 -0
- package/src/components/navigation/PageAside.tsx +84 -0
- package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
- package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
- package/src/components/navigation/Sidebar.tsx +223 -0
- package/src/components/navigation/SidebarItem.tsx +160 -0
- package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
- package/src/components/navigation/TextLink.tsx +71 -0
- package/src/components/navigation/generated/default-variant-keys.ts +12 -0
- package/src/components/navigation/generated/variant-keys.ts +13 -0
- package/src/components/overlay/AIChatInput.tsx +5 -0
- package/src/components/overlay/AIChatMessage.tsx +6 -0
- package/src/components/overlay/AlertDialog.tsx +145 -0
- package/src/components/overlay/ChatPanel.tsx +180 -0
- package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
- package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
- package/src/components/overlay/Drawer.tsx +131 -0
- package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
- package/src/components/overlay/FloatingPanel.tsx +90 -0
- package/src/components/overlay/HoverCard.tsx +36 -0
- package/src/components/overlay/MediaLightbox.tsx +403 -0
- package/src/components/overlay/MediaPickerDialog.tsx +198 -0
- package/src/components/overlay/Modal.tsx +103 -0
- package/src/components/overlay/OnboardingFlow.tsx +172 -0
- package/src/components/overlay/Popover.tsx +36 -0
- package/src/components/overlay/ShareModal.tsx +324 -0
- package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
- package/src/components/overlay/Tooltip.tsx +130 -0
- package/src/components/overlay/generated/default-variant-keys.ts +14 -0
- package/src/components/overlay/generated/variant-keys.ts +17 -0
- package/src/components/patterns/BlogTemplate.tsx +46 -0
- package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
- package/src/components/patterns/DocsTemplate.tsx +41 -0
- package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
- package/src/components/patterns/OnboardingTemplate.tsx +32 -0
- package/src/components/patterns/PricingTemplate.tsx +106 -0
- package/src/globals.css +173 -22
- package/src/index.ts +177 -76
- package/tailwind-theme-extend.cjs +48 -3
- package/design/atoms-metadata.json +0 -82
- package/design/molecules-metadata.json +0 -130
- package/design/organisms-metadata.json +0 -38
- package/design/templates-metadata.json +0 -38
- package/src/components/atoms/Avatar.tsx +0 -57
- package/src/components/atoms/Select.tsx +0 -28
- package/src/components/atoms/generated/default-variant-keys.ts +0 -36
- package/src/components/molecules/AIChatInput.tsx +0 -140
- package/src/components/molecules/AIChatMessage.tsx +0 -109
- package/src/components/molecules/Accordion.tsx +0 -99
- package/src/components/molecules/Calendar.tsx +0 -60
- package/src/components/molecules/Carousel.tsx +0 -261
- package/src/components/molecules/Command.tsx +0 -152
- package/src/components/molecules/FilterButton.tsx +0 -133
- package/src/components/molecules/HoverCard.tsx +0 -29
- package/src/components/molecules/Modal.tsx +0 -66
- package/src/components/molecules/Popover.tsx +0 -31
- package/src/components/molecules/ProgressWidget.tsx +0 -40
- package/src/components/molecules/Resizable.tsx +0 -47
- package/src/components/molecules/ScrollArea.tsx +0 -48
- package/src/components/molecules/SidebarItem.tsx +0 -134
- package/src/components/molecules/Toast.tsx +0 -57
- package/src/components/molecules/Tooltip.tsx +0 -30
- package/src/components/molecules/generated/default-variant-keys.ts +0 -22
- package/src/components/molecules/generated/variant-keys.ts +0 -33
- package/src/components/organisms/CommandPalette.tsx +0 -58
- package/src/components/organisms/FloatingPanel.tsx +0 -46
- package/src/components/organisms/ShareModal.tsx +0 -182
- package/src/components/organisms/ToastProvider.tsx +0 -49
- /package/src/components/{atoms → display}/Kbd.tsx +0 -0
- /package/src/components/{atoms → display}/Separator.tsx +0 -0
- /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
- /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
- /package/src/components/{atoms → inputs}/Button.tsx +0 -0
- /package/src/components/{atoms → inputs}/Label.tsx +0 -0
- /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
- /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
- /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import {
|
|
5
|
+
IconCheck as Check,
|
|
6
|
+
IconCircle as Circle,
|
|
7
|
+
IconX as X,
|
|
8
|
+
} from "@tabler/icons-react"
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils"
|
|
11
|
+
|
|
12
|
+
export interface PasswordRequirement {
|
|
13
|
+
id: string
|
|
14
|
+
label: React.ReactNode
|
|
15
|
+
met?: boolean
|
|
16
|
+
description?: React.ReactNode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PasswordRequirementListProps
|
|
20
|
+
extends React.HTMLAttributes<HTMLUListElement> {
|
|
21
|
+
requirements: PasswordRequirement[]
|
|
22
|
+
metLabel?: string
|
|
23
|
+
unmetLabel?: string
|
|
24
|
+
pendingLabel?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PasswordRequirementList = React.forwardRef<
|
|
28
|
+
HTMLUListElement,
|
|
29
|
+
PasswordRequirementListProps
|
|
30
|
+
>(
|
|
31
|
+
(
|
|
32
|
+
{
|
|
33
|
+
className,
|
|
34
|
+
requirements,
|
|
35
|
+
metLabel = "Requirement met",
|
|
36
|
+
unmetLabel = "Requirement not met",
|
|
37
|
+
pendingLabel = "Requirement pending",
|
|
38
|
+
...props
|
|
39
|
+
},
|
|
40
|
+
ref
|
|
41
|
+
) => {
|
|
42
|
+
return (
|
|
43
|
+
<ul
|
|
44
|
+
ref={ref}
|
|
45
|
+
className={cn("grid gap-1.5 text-sm", className)}
|
|
46
|
+
data-slot="password-requirement-list"
|
|
47
|
+
{...props}
|
|
48
|
+
>
|
|
49
|
+
{requirements.map((requirement) => {
|
|
50
|
+
const state =
|
|
51
|
+
requirement.met === undefined
|
|
52
|
+
? "pending"
|
|
53
|
+
: requirement.met
|
|
54
|
+
? "met"
|
|
55
|
+
: "unmet"
|
|
56
|
+
const label =
|
|
57
|
+
state === "met"
|
|
58
|
+
? metLabel
|
|
59
|
+
: state === "unmet"
|
|
60
|
+
? unmetLabel
|
|
61
|
+
: pendingLabel
|
|
62
|
+
const Icon =
|
|
63
|
+
state === "met" ? Check : state === "unmet" ? X : Circle
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<li
|
|
67
|
+
key={requirement.id}
|
|
68
|
+
className={cn(
|
|
69
|
+
"grid grid-cols-[auto_minmax(0,1fr)] items-start gap-2",
|
|
70
|
+
state === "met" && "text-success-strong",
|
|
71
|
+
state === "unmet" && "text-destructive-strong",
|
|
72
|
+
state === "pending" && "text-muted-foreground"
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<Icon
|
|
76
|
+
className="mt-0.5 h-4 w-4 shrink-0"
|
|
77
|
+
aria-label={label}
|
|
78
|
+
/>
|
|
79
|
+
<span className="min-w-0">
|
|
80
|
+
<span>{requirement.label}</span>
|
|
81
|
+
{requirement.description ? (
|
|
82
|
+
<span className="block text-xs text-muted-foreground">
|
|
83
|
+
{requirement.description}
|
|
84
|
+
</span>
|
|
85
|
+
) : null}
|
|
86
|
+
</span>
|
|
87
|
+
</li>
|
|
88
|
+
)
|
|
89
|
+
})}
|
|
90
|
+
</ul>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
PasswordRequirementList.displayName = "PasswordRequirementList"
|
|
95
|
+
|
|
96
|
+
export { PasswordRequirementList }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
export interface PasswordStrengthMeterProps
|
|
8
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
score: number
|
|
10
|
+
maxScore?: number
|
|
11
|
+
label?: React.ReactNode
|
|
12
|
+
description?: React.ReactNode
|
|
13
|
+
valueLabel?: React.ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getStrengthClass(score: number, maxScore: number) {
|
|
17
|
+
const ratio = maxScore <= 0 ? 0 : score / maxScore
|
|
18
|
+
if (ratio >= 0.75) return "bg-success"
|
|
19
|
+
if (ratio >= 0.5) return "bg-warning"
|
|
20
|
+
if (ratio > 0) return "bg-destructive"
|
|
21
|
+
return "bg-muted"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PasswordStrengthMeter = React.forwardRef<
|
|
25
|
+
HTMLDivElement,
|
|
26
|
+
PasswordStrengthMeterProps
|
|
27
|
+
>(
|
|
28
|
+
(
|
|
29
|
+
{
|
|
30
|
+
className,
|
|
31
|
+
score,
|
|
32
|
+
maxScore = 4,
|
|
33
|
+
label = "Password strength",
|
|
34
|
+
description,
|
|
35
|
+
valueLabel,
|
|
36
|
+
...props
|
|
37
|
+
},
|
|
38
|
+
ref
|
|
39
|
+
) => {
|
|
40
|
+
const normalizedMax = Math.max(1, maxScore)
|
|
41
|
+
const normalizedScore = Math.max(0, Math.min(score, normalizedMax))
|
|
42
|
+
const segments = Array.from({ length: normalizedMax }, (_, index) => index)
|
|
43
|
+
const meterId = React.useId()
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn("grid gap-2", className)}
|
|
49
|
+
data-slot="password-strength-meter"
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<div className="flex min-w-0 items-center justify-between gap-3">
|
|
53
|
+
<span id={meterId} className="text-sm font-medium">
|
|
54
|
+
{label}
|
|
55
|
+
</span>
|
|
56
|
+
{valueLabel ? (
|
|
57
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
58
|
+
{valueLabel}
|
|
59
|
+
</span>
|
|
60
|
+
) : null}
|
|
61
|
+
</div>
|
|
62
|
+
<div
|
|
63
|
+
className="grid gap-1"
|
|
64
|
+
style={{ gridTemplateColumns: `repeat(${normalizedMax}, minmax(0, 1fr))` }}
|
|
65
|
+
role="meter"
|
|
66
|
+
aria-labelledby={meterId}
|
|
67
|
+
aria-valuemin={0}
|
|
68
|
+
aria-valuemax={normalizedMax}
|
|
69
|
+
aria-valuenow={normalizedScore}
|
|
70
|
+
>
|
|
71
|
+
{segments.map((segment) => (
|
|
72
|
+
<span
|
|
73
|
+
key={segment}
|
|
74
|
+
className={cn(
|
|
75
|
+
"h-1.5 rounded-full",
|
|
76
|
+
segment < normalizedScore
|
|
77
|
+
? getStrengthClass(normalizedScore, normalizedMax)
|
|
78
|
+
: "bg-muted"
|
|
79
|
+
)}
|
|
80
|
+
aria-hidden="true"
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
{description ? (
|
|
85
|
+
<p className="text-xs text-muted-foreground">{description}</p>
|
|
86
|
+
) : null}
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
PasswordStrengthMeter.displayName = "PasswordStrengthMeter"
|
|
92
|
+
|
|
93
|
+
export { PasswordStrengthMeter }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip"
|
|
7
|
+
import { Input, type InputProps } from "./Input"
|
|
8
|
+
|
|
9
|
+
export interface PhoneInputProps
|
|
10
|
+
extends Omit<InputProps, "type" | "value" | "defaultValue" | "onChange"> {
|
|
11
|
+
value?: string
|
|
12
|
+
defaultValue?: string
|
|
13
|
+
onValueChange?: (value: string) => void
|
|
14
|
+
countryCallingCode?: string
|
|
15
|
+
countryLabel?: React.ReactNode
|
|
16
|
+
formatValue?: (value: string) => string
|
|
17
|
+
disabledReason?: React.ReactNode
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatJapanesePhone(value: string) {
|
|
21
|
+
const digits = value.replace(/\D/g, "").slice(0, 11)
|
|
22
|
+
if (digits.length <= 3) return digits
|
|
23
|
+
if (digits.length <= 7) return `${digits.slice(0, 3)}-${digits.slice(3)}`
|
|
24
|
+
return `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7)}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
|
|
28
|
+
(
|
|
29
|
+
{
|
|
30
|
+
className,
|
|
31
|
+
value,
|
|
32
|
+
defaultValue,
|
|
33
|
+
onValueChange,
|
|
34
|
+
countryCallingCode = "+81",
|
|
35
|
+
countryLabel = "Japan",
|
|
36
|
+
formatValue = formatJapanesePhone,
|
|
37
|
+
disabled,
|
|
38
|
+
disabledReason,
|
|
39
|
+
...props
|
|
40
|
+
},
|
|
41
|
+
ref
|
|
42
|
+
) => {
|
|
43
|
+
const isControlled = value !== undefined
|
|
44
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue ?? "")
|
|
45
|
+
const resolvedValue = isControlled ? value : internalValue
|
|
46
|
+
|
|
47
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
48
|
+
const next = formatValue(event.target.value)
|
|
49
|
+
if (!isControlled) {
|
|
50
|
+
setInternalValue(next)
|
|
51
|
+
}
|
|
52
|
+
onValueChange?.(next)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const control = (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
"inline-flex w-[280px] max-w-full items-stretch rounded-lg border border-input bg-transparent shadow-sm focus-within:ring-1 focus-within:ring-ring",
|
|
59
|
+
disabled && "opacity-50",
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
data-slot="phone-input"
|
|
63
|
+
>
|
|
64
|
+
<span className="inline-flex min-w-16 items-center justify-center border-r border-input px-3 text-sm text-muted-foreground">
|
|
65
|
+
<span className="sr-only">{countryLabel}</span>
|
|
66
|
+
{countryCallingCode}
|
|
67
|
+
</span>
|
|
68
|
+
<Input
|
|
69
|
+
ref={ref}
|
|
70
|
+
type="tel"
|
|
71
|
+
inputMode="tel"
|
|
72
|
+
value={resolvedValue}
|
|
73
|
+
onChange={handleChange}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
className="h-9 min-w-0 flex-1 border-0 shadow-none focus-visible:ring-0"
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if (disabled && disabledReason) {
|
|
82
|
+
return (
|
|
83
|
+
<Tooltip>
|
|
84
|
+
<TooltipTrigger asChild>
|
|
85
|
+
<span className="inline-flex max-w-full" tabIndex={0}>
|
|
86
|
+
{control}
|
|
87
|
+
</span>
|
|
88
|
+
</TooltipTrigger>
|
|
89
|
+
<TooltipContent>{disabledReason}</TooltipContent>
|
|
90
|
+
</Tooltip>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return control
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
PhoneInput.displayName = "PhoneInput"
|
|
98
|
+
|
|
99
|
+
export { PhoneInput, formatJapanesePhone }
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip"
|
|
7
|
+
import { Input, type InputProps } from "./Input"
|
|
8
|
+
|
|
9
|
+
export interface PostalCodeInputProps
|
|
10
|
+
extends Omit<InputProps, "type" | "value" | "defaultValue" | "onChange" | "prefix"> {
|
|
11
|
+
value?: string
|
|
12
|
+
defaultValue?: string
|
|
13
|
+
onValueChange?: (value: string) => void
|
|
14
|
+
prefix?: React.ReactNode
|
|
15
|
+
formatValue?: (value: string) => string
|
|
16
|
+
disabledReason?: React.ReactNode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatJapanesePostalCode(value: string) {
|
|
20
|
+
const digits = value.replace(/\D/g, "").slice(0, 7)
|
|
21
|
+
if (digits.length <= 3) return digits
|
|
22
|
+
return `${digits.slice(0, 3)}-${digits.slice(3)}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const PostalCodeInput = React.forwardRef<
|
|
26
|
+
HTMLInputElement,
|
|
27
|
+
PostalCodeInputProps
|
|
28
|
+
>(
|
|
29
|
+
(
|
|
30
|
+
{
|
|
31
|
+
className,
|
|
32
|
+
value,
|
|
33
|
+
defaultValue,
|
|
34
|
+
onValueChange,
|
|
35
|
+
prefix = "〒",
|
|
36
|
+
formatValue = formatJapanesePostalCode,
|
|
37
|
+
disabled,
|
|
38
|
+
disabledReason,
|
|
39
|
+
...props
|
|
40
|
+
},
|
|
41
|
+
ref
|
|
42
|
+
) => {
|
|
43
|
+
const isControlled = value !== undefined
|
|
44
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue ?? "")
|
|
45
|
+
const resolvedValue = isControlled ? value : internalValue
|
|
46
|
+
|
|
47
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
48
|
+
const next = formatValue(event.target.value)
|
|
49
|
+
if (!isControlled) {
|
|
50
|
+
setInternalValue(next)
|
|
51
|
+
}
|
|
52
|
+
onValueChange?.(next)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const control = (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
"inline-flex w-[220px] max-w-full items-stretch rounded-lg border border-input bg-transparent shadow-sm focus-within:ring-1 focus-within:ring-ring",
|
|
59
|
+
disabled && "opacity-50",
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
data-slot="postal-code-input"
|
|
63
|
+
>
|
|
64
|
+
<span className="inline-flex items-center justify-center border-r border-input px-3 text-sm text-muted-foreground">
|
|
65
|
+
{prefix}
|
|
66
|
+
</span>
|
|
67
|
+
<Input
|
|
68
|
+
ref={ref}
|
|
69
|
+
type="text"
|
|
70
|
+
inputMode="numeric"
|
|
71
|
+
value={resolvedValue}
|
|
72
|
+
onChange={handleChange}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
className="h-9 min-w-0 flex-1 border-0 shadow-none focus-visible:ring-0"
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (disabled && disabledReason) {
|
|
81
|
+
return (
|
|
82
|
+
<Tooltip>
|
|
83
|
+
<TooltipTrigger asChild>
|
|
84
|
+
<span className="inline-flex max-w-full" tabIndex={0}>
|
|
85
|
+
{control}
|
|
86
|
+
</span>
|
|
87
|
+
</TooltipTrigger>
|
|
88
|
+
<TooltipContent>{disabledReason}</TooltipContent>
|
|
89
|
+
</Tooltip>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return control
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
PostalCodeInput.displayName = "PostalCodeInput"
|
|
97
|
+
|
|
98
|
+
export { PostalCodeInput, formatJapanesePostalCode }
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
export interface RangeSliderProps
|
|
8
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> {
|
|
9
|
+
value?: [number, number]
|
|
10
|
+
defaultValue?: [number, number]
|
|
11
|
+
onValueChange?: (value: [number, number]) => void
|
|
12
|
+
min?: number
|
|
13
|
+
max?: number
|
|
14
|
+
step?: number
|
|
15
|
+
minLabel?: string
|
|
16
|
+
maxLabel?: string
|
|
17
|
+
disabled?: boolean
|
|
18
|
+
trackClassName?: string
|
|
19
|
+
rangeClassName?: string
|
|
20
|
+
thumbClassName?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function clamp(value: number, min: number, max: number) {
|
|
24
|
+
return Math.min(max, Math.max(min, value))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function roundToStep(value: number, step: number) {
|
|
28
|
+
const precision = String(step).split(".")[1]?.length ?? 0
|
|
29
|
+
return Number((Math.round(value / step) * step).toFixed(precision))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const RangeSlider = React.forwardRef<HTMLDivElement, RangeSliderProps>(
|
|
33
|
+
(
|
|
34
|
+
{
|
|
35
|
+
className,
|
|
36
|
+
value,
|
|
37
|
+
defaultValue,
|
|
38
|
+
onValueChange,
|
|
39
|
+
min = 0,
|
|
40
|
+
max = 100,
|
|
41
|
+
step = 1,
|
|
42
|
+
minLabel = "Minimum value",
|
|
43
|
+
maxLabel = "Maximum value",
|
|
44
|
+
disabled,
|
|
45
|
+
trackClassName,
|
|
46
|
+
rangeClassName,
|
|
47
|
+
thumbClassName,
|
|
48
|
+
...props
|
|
49
|
+
},
|
|
50
|
+
ref
|
|
51
|
+
) => {
|
|
52
|
+
const isControlled = value !== undefined
|
|
53
|
+
const [internalValue, setInternalValue] = React.useState<[number, number]>(
|
|
54
|
+
defaultValue ?? [min, max]
|
|
55
|
+
)
|
|
56
|
+
const currentValue = value ?? internalValue
|
|
57
|
+
const minValue = clamp(Math.min(currentValue[0], currentValue[1]), min, max)
|
|
58
|
+
const maxValue = clamp(Math.max(currentValue[0], currentValue[1]), min, max)
|
|
59
|
+
const minPercent = ((minValue - min) / (max - min)) * 100
|
|
60
|
+
const maxPercent = ((maxValue - min) / (max - min)) * 100
|
|
61
|
+
|
|
62
|
+
const commit = (nextValue: [number, number]) => {
|
|
63
|
+
const next: [number, number] = [
|
|
64
|
+
clamp(roundToStep(Math.min(nextValue[0], nextValue[1]), step), min, max),
|
|
65
|
+
clamp(roundToStep(Math.max(nextValue[0], nextValue[1]), step), min, max),
|
|
66
|
+
]
|
|
67
|
+
if (!isControlled) setInternalValue(next)
|
|
68
|
+
onValueChange?.(next)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
ref={ref}
|
|
74
|
+
className={cn(
|
|
75
|
+
"relative flex h-7 w-[240px] min-w-0 touch-none select-none items-center",
|
|
76
|
+
disabled && "pointer-events-none opacity-50",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
data-slot="range-slider"
|
|
80
|
+
{...props}
|
|
81
|
+
>
|
|
82
|
+
<div
|
|
83
|
+
className={cn("absolute left-0 right-0 h-2 rounded-full bg-input", trackClassName)}
|
|
84
|
+
aria-hidden="true"
|
|
85
|
+
/>
|
|
86
|
+
<div
|
|
87
|
+
className={cn("absolute h-2 rounded-full bg-primary", rangeClassName)}
|
|
88
|
+
style={{ left: `${minPercent}%`, right: `${100 - maxPercent}%` }}
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
/>
|
|
91
|
+
<input
|
|
92
|
+
type="range"
|
|
93
|
+
min={min}
|
|
94
|
+
max={max}
|
|
95
|
+
step={step}
|
|
96
|
+
value={minValue}
|
|
97
|
+
disabled={disabled}
|
|
98
|
+
onChange={(event) => commit([Number(event.currentTarget.value), maxValue])}
|
|
99
|
+
className={cn(
|
|
100
|
+
"pointer-events-none absolute inset-0 z-20 h-7 w-full appearance-none bg-transparent",
|
|
101
|
+
"[&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-foreground [&::-moz-range-thumb]:bg-background",
|
|
102
|
+
"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background",
|
|
103
|
+
thumbClassName
|
|
104
|
+
)}
|
|
105
|
+
aria-label={minLabel}
|
|
106
|
+
/>
|
|
107
|
+
<input
|
|
108
|
+
type="range"
|
|
109
|
+
min={min}
|
|
110
|
+
max={max}
|
|
111
|
+
step={step}
|
|
112
|
+
value={maxValue}
|
|
113
|
+
disabled={disabled}
|
|
114
|
+
onChange={(event) => commit([minValue, Number(event.currentTarget.value)])}
|
|
115
|
+
className={cn(
|
|
116
|
+
"pointer-events-none absolute inset-0 z-30 h-7 w-full appearance-none bg-transparent",
|
|
117
|
+
"[&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-foreground [&::-moz-range-thumb]:bg-background",
|
|
118
|
+
"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background",
|
|
119
|
+
thumbClassName
|
|
120
|
+
)}
|
|
121
|
+
aria-label={maxLabel}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
RangeSlider.displayName = "RangeSlider"
|
|
128
|
+
|
|
129
|
+
export { RangeSlider }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { IconSearch as Search, IconX as X } from "@tabler/icons-react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip"
|
|
8
|
+
|
|
9
|
+
export interface SearchInputProps
|
|
10
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "onChange"> {
|
|
11
|
+
value?: string
|
|
12
|
+
onValueChange?: (value: string) => void
|
|
13
|
+
/** Show a clear (×) button when value is non-empty. Default true. */
|
|
14
|
+
clearable?: boolean
|
|
15
|
+
clearLabel?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
|
|
19
|
+
(
|
|
20
|
+
{
|
|
21
|
+
className,
|
|
22
|
+
value,
|
|
23
|
+
onValueChange,
|
|
24
|
+
clearable = true,
|
|
25
|
+
clearLabel = "Clear search",
|
|
26
|
+
disabled,
|
|
27
|
+
placeholder = "Search...",
|
|
28
|
+
...props
|
|
29
|
+
},
|
|
30
|
+
ref
|
|
31
|
+
) => {
|
|
32
|
+
const showClear = clearable && !!value && !disabled
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
className={cn(
|
|
37
|
+
"relative inline-flex items-center w-full",
|
|
38
|
+
disabled && "opacity-50 pointer-events-none",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
data-slot="search-input"
|
|
42
|
+
>
|
|
43
|
+
<Search className="pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
44
|
+
<input
|
|
45
|
+
ref={ref}
|
|
46
|
+
type="search"
|
|
47
|
+
value={value ?? ""}
|
|
48
|
+
onChange={(e) => onValueChange?.(e.target.value)}
|
|
49
|
+
disabled={disabled}
|
|
50
|
+
placeholder={placeholder}
|
|
51
|
+
className="flex h-9 w-full rounded-md border border-input bg-transparent pl-9 pr-9 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring [&::-webkit-search-cancel-button]:hidden"
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
{showClear ? (
|
|
55
|
+
<Tooltip>
|
|
56
|
+
<TooltipTrigger asChild>
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
tabIndex={-1}
|
|
60
|
+
onClick={() => onValueChange?.("")}
|
|
61
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground"
|
|
62
|
+
aria-label={clearLabel}
|
|
63
|
+
>
|
|
64
|
+
<X className="h-3.5 w-3.5" />
|
|
65
|
+
</button>
|
|
66
|
+
</TooltipTrigger>
|
|
67
|
+
<TooltipContent>{clearLabel}</TooltipContent>
|
|
68
|
+
</Tooltip>
|
|
69
|
+
) : null}
|
|
70
|
+
</div>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
SearchInput.displayName = "SearchInput"
|
|
75
|
+
|
|
76
|
+
export { SearchInput }
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../lib/utils"
|
|
3
|
+
import { IconChevronDown as ChevronDown } from "@tabler/icons-react";
|
|
4
|
+
|
|
5
|
+
export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { }
|
|
6
|
+
|
|
7
|
+
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
8
|
+
({ className, children, ...props }, ref) => {
|
|
9
|
+
const isFullWidth =
|
|
10
|
+
typeof className === "string" &&
|
|
11
|
+
className.split(/\s+/).includes("w-full")
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
"relative inline-block max-w-full align-middle",
|
|
17
|
+
isFullWidth && "block w-full"
|
|
18
|
+
)}
|
|
19
|
+
data-slot="select"
|
|
20
|
+
>
|
|
21
|
+
<select
|
|
22
|
+
className={cn(
|
|
23
|
+
"inline-flex h-9 w-[200px] max-w-full appearance-none items-center justify-between rounded-lg border border-input bg-transparent px-3 py-2 pr-9 text-sm font-normal text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground disabled:opacity-50",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
ref={ref}
|
|
27
|
+
{...props}
|
|
28
|
+
data-slot="select-control"
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</select>
|
|
32
|
+
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
Select.displayName = "Select"
|
|
38
|
+
|
|
39
|
+
export { Select }
|
|
@@ -3,18 +3,31 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { cn } from "../../lib/utils"
|
|
5
5
|
|
|
6
|
-
export interface SliderProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
export interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue"> {
|
|
7
|
+
value?: number | string
|
|
8
|
+
defaultValue?: number | string
|
|
9
|
+
onValueChange?: (value: number) => void
|
|
10
|
+
}
|
|
7
11
|
|
|
8
12
|
const Slider = React.forwardRef<HTMLInputElement, SliderProps>(
|
|
9
|
-
({ className, ...props }, ref) => (
|
|
10
|
-
<div
|
|
13
|
+
({ className, onChange, onValueChange, ...props }, ref) => (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
"relative flex w-[200px] touch-none select-none items-center",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
data-slot="slider"
|
|
20
|
+
>
|
|
11
21
|
<input
|
|
12
22
|
type="range"
|
|
13
23
|
ref={ref}
|
|
24
|
+
onChange={(event) => {
|
|
25
|
+
onChange?.(event)
|
|
26
|
+
onValueChange?.(Number(event.currentTarget.value))
|
|
27
|
+
}}
|
|
14
28
|
className={cn(
|
|
15
29
|
"h-5 w-full cursor-pointer appearance-none rounded-[10px] bg-input disabled:cursor-not-allowed disabled:opacity-50",
|
|
16
|
-
"[&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:transition-colors [&::-webkit-slider-thumb]:focus-visible:outline-none [&::-webkit-slider-thumb]:focus-visible:ring-1 [&::-webkit-slider-thumb]:focus-visible:ring-ring [&::-webkit-slider-thumb]:focus-visible:ring-offset-1 [&::-webkit-slider-thumb]:focus-visible:ring-offset-background [&::-webkit-slider-thumb]:disabled:pointer-events-none [&::-webkit-slider-thumb]:disabled:opacity-50"
|
|
17
|
-
className
|
|
30
|
+
"[&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:transition-colors [&::-webkit-slider-thumb]:focus-visible:outline-none [&::-webkit-slider-thumb]:focus-visible:ring-1 [&::-webkit-slider-thumb]:focus-visible:ring-ring [&::-webkit-slider-thumb]:focus-visible:ring-offset-1 [&::-webkit-slider-thumb]:focus-visible:ring-offset-background [&::-webkit-slider-thumb]:disabled:pointer-events-none [&::-webkit-slider-thumb]:disabled:opacity-50"
|
|
18
31
|
)}
|
|
19
32
|
{...props}
|
|
20
33
|
/>
|