@nikala-ui/core 0.9.9 → 0.9.10-nightly.f842585
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/package.json +2 -2
- package/registry/accordion.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/button.json +1 -1
- package/registry/checkbox.json +1 -1
- package/registry/combobox.json +18 -0
- package/registry/command.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/hover-card.json +18 -0
- package/registry/index.json +53 -0
- package/registry/input-group.json +1 -1
- package/registry/input.json +1 -1
- package/registry/list.json +1 -1
- package/registry/logo.json +17 -0
- package/registry/pin-input.json +17 -0
- package/registry/popover.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/select.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/slider.json +18 -0
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/textarea.json +1 -1
- package/registry/theme-manager.json +1 -1
- package/registry/toast.json +1 -1
- package/src/registry/components/ui/accordion.tsx +0 -4
- package/src/registry/components/ui/avatar.tsx +1 -1
- package/src/registry/components/ui/badge.tsx +6 -0
- package/src/registry/components/ui/button.tsx +6 -2
- package/src/registry/components/ui/checkbox.tsx +1 -1
- package/src/registry/components/ui/combobox.tsx +295 -0
- package/src/registry/components/ui/command.tsx +15 -15
- package/src/registry/components/ui/dialog.tsx +3 -3
- package/src/registry/components/ui/dropdown-menu.tsx +5 -5
- package/src/registry/components/ui/hover-card.tsx +105 -0
- package/src/registry/components/ui/input-group.tsx +2 -2
- package/src/registry/components/ui/input.tsx +1 -1
- package/src/registry/components/ui/list.tsx +5 -5
- package/src/registry/components/ui/logo.tsx +68 -0
- package/src/registry/components/ui/pin-input.tsx +198 -0
- package/src/registry/components/ui/popover.tsx +1 -1
- package/src/registry/components/ui/progress.tsx +2 -2
- package/src/registry/components/ui/select.tsx +2 -2
- package/src/registry/components/ui/skeleton.tsx +1 -1
- package/src/registry/components/ui/slider.tsx +153 -0
- package/src/registry/components/ui/switch.tsx +2 -2
- package/src/registry/components/ui/tabs.tsx +1 -1
- package/src/registry/components/ui/textarea.tsx +1 -1
- package/src/registry/components/ui/theme-toggle.tsx +1 -1
- package/src/registry/components/ui/toast.tsx +1 -1
- package/src/registry/metadata.ts +20 -0
package/registry/popover.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "ui/popover.tsx",
|
|
15
|
-
"content": "import { splitProps, type Component, type ComponentProps } from \"solid-js\";\nimport { Popover as KobaltePopover } from \"@kobalte/core/popover\";\nimport { createClickOutside } from \"@nikala-ui/hooks\";\nimport { X } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\nexport const Popover = KobaltePopover;\n\nexport const PopoverTrigger = KobaltePopover.Trigger;\n\nexport const PopoverArrow: Component<ComponentProps<typeof KobaltePopover.Arrow>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobaltePopover.Arrow\n class={cn(\"fill-popover stroke-border\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const PopoverContent: Component<ComponentProps<typeof KobaltePopover.Content>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\", \"onInteractOutside\"]);\n let contentRef: HTMLElement | undefined;\n\n createClickOutside({\n target: () => contentRef,\n onInteractOutside: (e) => {\n if (typeof local.onInteractOutside === \"function\") {\n local.onInteractOutside(e as any);\n }\n },\n });\n\n return (\n <KobaltePopover.Portal>\n <KobaltePopover.Content\n ref={(el) => {\n contentRef = el;\n if (typeof (props as any).ref === \"function\") (props as any).ref(el);\n }}\n class={cn(\n \"z-50 w-72 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-
|
|
15
|
+
"content": "import { splitProps, type Component, type ComponentProps } from \"solid-js\";\nimport { Popover as KobaltePopover } from \"@kobalte/core/popover\";\nimport { createClickOutside } from \"@nikala-ui/hooks\";\nimport { X } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\nexport const Popover = KobaltePopover;\n\nexport const PopoverTrigger = KobaltePopover.Trigger;\n\nexport const PopoverArrow: Component<ComponentProps<typeof KobaltePopover.Arrow>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobaltePopover.Arrow\n class={cn(\"fill-popover stroke-border\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const PopoverContent: Component<ComponentProps<typeof KobaltePopover.Content>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\", \"onInteractOutside\"]);\n let contentRef: HTMLElement | undefined;\n\n createClickOutside({\n target: () => contentRef,\n onInteractOutside: (e) => {\n if (typeof local.onInteractOutside === \"function\") {\n local.onInteractOutside(e as any);\n }\n },\n });\n\n return (\n <KobaltePopover.Portal>\n <KobaltePopover.Content\n ref={(el) => {\n contentRef = el;\n if (typeof (props as any).ref === \"function\") (props as any).ref(el);\n }}\n class={cn(\n \"z-50 w-72 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-closed:zoom-out-95 data-expanded:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </KobaltePopover.Content>\n </KobaltePopover.Portal>\n );\n};\n\nexport const PopoverTitle: Component<ComponentProps<typeof KobaltePopover.Title>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobaltePopover.Title\n class={cn(\"text-sm font-semibold text-foreground\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const PopoverDescription: Component<ComponentProps<typeof KobaltePopover.Description>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobaltePopover.Description\n class={cn(\"text-sm text-muted-foreground\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const PopoverCloseButton: Component<ComponentProps<typeof KobaltePopover.CloseButton>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <KobaltePopover.CloseButton\n class={cn(\n \"absolute right-2 top-2 rounded-md p-1 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\",\n local.class\n )}\n {...rest}\n >\n {local.children || <X class=\"h-4 w-4 text-muted-foreground\" />}\n </KobaltePopover.CloseButton>\n );\n};",
|
|
16
16
|
"type": "registry:ui"
|
|
17
17
|
}
|
|
18
18
|
]
|
package/registry/progress.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "ui/progress.tsx",
|
|
14
|
-
"content": "import { splitProps, Show, type Component, type JSX } from \"solid-js\";\nimport { Progress as KobalteProgress } from \"@kobalte/core/progress\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface ProgressProps\n extends Omit<\n JSX.HTMLAttributes<HTMLDivElement>,\n \"value\" | \"aria-valuenow\" | \"aria-valuemin\" | \"aria-valuemax\"\n > {\n /** The current numeric progress value. */\n value?: number;\n /** Minimum progress value. Defaults to 0. */\n minValue?: number;\n /** Maximum progress value. Defaults to 100. */\n maxValue?: number;\n /** Label text for accessibility and screen readers. */\n getValueLabel?: (params: { value: number; max: number }) => string;\n /** Custom label element to display above or beside progress bar. */\n label?: JSX.Element;\n /** Optional custom class for root container. */\n class?: string;\n /** Optional custom class for indicator fill bar. */\n indicatorClass?: string;\n}\n\n/**\n * Nikala UI Progress component for showing task completion status or media timeline positions.\n */\nexport const Progress: Component<ProgressProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"minValue\",\n \"maxValue\",\n \"getValueLabel\",\n \"label\",\n \"class\",\n \"indicatorClass\",\n ]);\n\n return (\n <KobalteProgress\n value={local.value ?? 0}\n minValue={local.minValue ?? 0}\n maxValue={local.maxValue ?? 100}\n getValueLabel={local.getValueLabel}\n class={cn(\"flex w-full flex-col gap-1.5\", local.class)}\n {...rest}\n >\n <Show when={local.label}>\n <div class=\"flex justify-between text-xs font-medium text-muted-foreground\">\n <KobalteProgress.Label>{local.label}</KobalteProgress.Label>\n <KobalteProgress.ValueLabel class=\"font-mono text-foreground\" />\n </div>\n </Show>\n\n <KobalteProgress.Track class=\"relative h-2 w-full overflow-hidden rounded-
|
|
14
|
+
"content": "import { splitProps, Show, type Component, type JSX } from \"solid-js\";\nimport { Progress as KobalteProgress } from \"@kobalte/core/progress\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface ProgressProps\n extends Omit<\n JSX.HTMLAttributes<HTMLDivElement>,\n \"value\" | \"aria-valuenow\" | \"aria-valuemin\" | \"aria-valuemax\"\n > {\n /** The current numeric progress value. */\n value?: number;\n /** Minimum progress value. Defaults to 0. */\n minValue?: number;\n /** Maximum progress value. Defaults to 100. */\n maxValue?: number;\n /** Label text for accessibility and screen readers. */\n getValueLabel?: (params: { value: number; max: number }) => string;\n /** Custom label element to display above or beside progress bar. */\n label?: JSX.Element;\n /** Optional custom class for root container. */\n class?: string;\n /** Optional custom class for indicator fill bar. */\n indicatorClass?: string;\n}\n\n/**\n * Nikala UI Progress component for showing task completion status or media timeline positions.\n */\nexport const Progress: Component<ProgressProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"minValue\",\n \"maxValue\",\n \"getValueLabel\",\n \"label\",\n \"class\",\n \"indicatorClass\",\n ]);\n\n return (\n <KobalteProgress\n value={local.value ?? 0}\n minValue={local.minValue ?? 0}\n maxValue={local.maxValue ?? 100}\n getValueLabel={local.getValueLabel}\n class={cn(\"flex w-full flex-col gap-1.5\", local.class)}\n {...rest}\n >\n <Show when={local.label}>\n <div class=\"flex justify-between text-xs font-medium text-muted-foreground\">\n <KobalteProgress.Label>{local.label}</KobalteProgress.Label>\n <KobalteProgress.ValueLabel class=\"font-mono text-foreground\" />\n </div>\n </Show>\n\n <KobalteProgress.Track class=\"relative h-2 w-full overflow-hidden rounded-lg bg-primary/20\">\n <KobalteProgress.Fill\n class={cn(\n \"h-full w-(--kb-progress-fill-width) bg-primary transition-all duration-300 ease-in-out\",\n local.indicatorClass\n )}\n />\n </KobalteProgress.Track>\n </KobalteProgress>\n );\n};\n",
|
|
15
15
|
"type": "registry:ui"
|
|
16
16
|
}
|
|
17
17
|
]
|
package/registry/select.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "ui/select.tsx",
|
|
14
|
-
"content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as SelectPrimitive from \"@kobalte/core/select\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { createClickOutside } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\nexport type SelectRootProps<Option = any, OptGroup = any, T extends ValidComponent = \"div\"> =\n SelectPrimitive.SelectRootProps<Option, OptGroup, T> & {\n class?: string;\n };\n\n/**\n * Root Select component built on top of Kobalte headless primitives.\n */\nexport const Select = <Option = any, OptGroup = any, T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SelectRootProps<Option, OptGroup, T>>\n) => {\n const [local, rest] = splitProps(props as SelectRootProps, [\"class\"]);\n\n return <SelectPrimitive.Root class={cn(\"relative w-full\", local.class)} {...(rest as any)} />;\n};\n\nexport type SelectTriggerProps<T extends ValidComponent = \"button\"> =\n SelectPrimitive.SelectTriggerProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Trigger button opening the Select options list.\n */\nexport const SelectTrigger = <T extends ValidComponent = \"button\">(\n props: PolymorphicProps<T, SelectTriggerProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectTriggerProps, [\"class\", \"children\"]);\n\n return (\n <SelectPrimitive.Trigger\n class={cn(\n \"flex h-9 w-full items-center justify-between rounded-md border border-input bg-
|
|
14
|
+
"content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as SelectPrimitive from \"@kobalte/core/select\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { createClickOutside } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\nexport type SelectRootProps<Option = any, OptGroup = any, T extends ValidComponent = \"div\"> =\n SelectPrimitive.SelectRootProps<Option, OptGroup, T> & {\n class?: string;\n };\n\n/**\n * Root Select component built on top of Kobalte headless primitives.\n */\nexport const Select = <Option = any, OptGroup = any, T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SelectRootProps<Option, OptGroup, T>>\n) => {\n const [local, rest] = splitProps(props as SelectRootProps, [\"class\"]);\n\n return <SelectPrimitive.Root class={cn(\"relative w-full\", local.class)} {...(rest as any)} />;\n};\n\nexport type SelectTriggerProps<T extends ValidComponent = \"button\"> =\n SelectPrimitive.SelectTriggerProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Trigger button opening the Select options list.\n */\nexport const SelectTrigger = <T extends ValidComponent = \"button\">(\n props: PolymorphicProps<T, SelectTriggerProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectTriggerProps, [\"class\", \"children\"]);\n\n return (\n <SelectPrimitive.Trigger\n class={cn(\n \"flex h-9 w-full items-center justify-between rounded-md border border-input bg-muted px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer text-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <SelectPrimitive.Icon\n as=\"svg\"\n class=\"h-4 w-4 opacity-50 transition-transform\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n};\n\nexport type SelectValueProps<Option, T extends ValidComponent = \"span\"> =\n SelectPrimitive.SelectValueProps<Option, T> & {\n class?: string;\n };\n\n/**\n * Renders the currently selected option text or placeholder.\n */\nexport const SelectValue = <Option = any, T extends ValidComponent = \"span\">(\n props: PolymorphicProps<T, SelectValueProps<Option, T>>\n) => {\n const [local, rest] = splitProps(props as SelectValueProps<Option>, [\"class\"]);\n\n return (\n <SelectPrimitive.Value\n class={cn(\"block truncate\", local.class)}\n {...(rest as any)}\n />\n );\n};\n\nexport type SelectContentProps<T extends ValidComponent = \"div\"> =\n SelectPrimitive.SelectContentProps<T> & {\n class?: string;\n };\n\n/**\n * Portaled overlay container rendering the listbox options.\n */\nexport const SelectContent = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SelectContentProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectContentProps, [\"class\"]);\n let contentRef: HTMLElement | undefined;\n\n createClickOutside({\n target: () => contentRef,\n onInteractOutside: (e) => {\n if (typeof (props as any).onInteractOutside === \"function\") {\n (props as any).onInteractOutside(e);\n }\n },\n });\n\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={(el) => {\n contentRef = el;\n if (typeof (props as any).ref === \"function\") (props as any).ref(el);\n }}\n class={cn(\n \"relative z-50 min-w-8rem overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md animate-in fade-in-80\",\n local.class\n )}\n {...rest}\n >\n <SelectPrimitive.Listbox class=\"p-1 outline-none\" />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n};\n\nexport type SelectItemProps<T extends ValidComponent = \"li\"> =\n SelectPrimitive.SelectItemProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Individual option item choice inside SelectContent.\n */\nexport const SelectItem = <T extends ValidComponent = \"li\">(\n props: PolymorphicProps<T, SelectItemProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectItemProps, [\"class\", \"children\"]);\n\n return (\n <SelectPrimitive.Item\n class={cn(\n \"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n local.class\n )}\n {...rest}\n >\n <SelectPrimitive.ItemIndicator class=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <svg class=\"h-4 w-4 fill-none stroke-current stroke-2\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n </SelectPrimitive.ItemIndicator>\n <SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>\n </SelectPrimitive.Item>\n );\n};",
|
|
15
15
|
"type": "registry:ui"
|
|
16
16
|
}
|
|
17
17
|
]
|
package/registry/skeleton.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "ui/skeleton.tsx",
|
|
13
|
-
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SkeletonProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Nikala UI Skeleton component for rendering animated pulse loading placeholders.\n */\nexport const Skeleton: Component<SkeletonProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"animate-pulse rounded-md bg-
|
|
13
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SkeletonProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Nikala UI Skeleton component for rendering animated pulse loading placeholders.\n */\nexport const Skeleton: Component<SkeletonProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"animate-pulse rounded-md bg-accent\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
14
14
|
"type": "registry:ui"
|
|
15
15
|
}
|
|
16
16
|
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slider",
|
|
3
|
+
"title": "Slider",
|
|
4
|
+
"description": "Numeric range selection slider supporting single/dual thumbs, custom steps, vertical orientation, and formatted value labels, built on Kobalte primitives.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"@kobalte/core"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "ui/slider.tsx",
|
|
14
|
+
"content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as SliderPrimitive from \"@kobalte/core/slider\";\nimport { cn } from \"@/lib/cn\";\n\nexport type SliderRootProps<T extends ValidComponent = \"div\"> =\n SliderPrimitive.SliderRootProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Root Slider component built on Kobalte primitives.\n */\nexport const Slider = <T extends ValidComponent = \"div\">(\n props: SliderRootProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderRootProps, [\"class\", \"children\"]);\n\n return (\n <SliderPrimitive.Root\n class={cn(\n \"relative flex w-full touch-none select-none flex-col gap-2 data-[orientation=vertical]:h-full data-[orientation=vertical]:w-auto data-[orientation=vertical]:items-center\",\n local.class\n )}\n {...(rest as any)}\n >\n {local.children}\n </SliderPrimitive.Root>\n );\n};\n\nexport type SliderTrackProps<T extends ValidComponent = \"div\"> =\n SliderPrimitive.SliderTrackProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Slider track containing the active range fill and thumbs.\n */\nexport const SliderTrack = <T extends ValidComponent = \"div\">(\n props: SliderTrackProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderTrackProps, [\"class\", \"children\"]);\n\n return (\n <SliderPrimitive.Track\n class={cn(\n \"relative h-2 w-full flex items-center rounded-lg bg-secondary cursor-pointer data-[orientation=vertical]:w-2 data-[orientation=vertical]:h-full data-[orientation=vertical]:flex-col data-[orientation=vertical]:justify-center\",\n local.class\n )}\n {...(rest as any)}\n >\n <SliderFill />\n {local.children}\n </SliderPrimitive.Track>\n );\n};\n\nexport type SliderFillProps<T extends ValidComponent = \"div\"> =\n SliderPrimitive.SliderFillProps<T> & {\n class?: string;\n };\n\n/**\n * Active range indicator fill inside SliderTrack.\n */\nexport const SliderFill = <T extends ValidComponent = \"div\">(\n props: SliderFillProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderFillProps, [\"class\"]);\n\n return (\n <SliderPrimitive.Fill\n class={cn(\n \"absolute rounded-lg bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\",\n local.class\n )}\n {...(rest as any)}\n />\n );\n};\n\nexport type SliderThumbProps<T extends ValidComponent = \"span\"> =\n SliderPrimitive.SliderThumbProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Draggable thumb handle for selecting slider values.\n * Positioned centered on Kobalte offset coordinates using -translate-x-1/2 -translate-y-1/2.\n */\nexport const SliderThumb = <T extends ValidComponent = \"span\">(\n props: SliderThumbProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderThumbProps, [\"class\", \"children\"]);\n\n return (\n <SliderPrimitive.Thumb\n class={cn(\n \"block h-5 w-5 rounded-lg border-2 border-primary bg-background shadow-md transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 cursor-grab active:cursor-grabbing z-10 data-[orientation=horizontal]:translate-x-0 data-[orientation=horizontal]:translate-y-0 data-[orientation=vertical]:translate-x-0 data-[orientation=vertical]:translate-y-0\",\n local.class\n )}\n {...(rest as any)}\n >\n <SliderPrimitive.Input />\n {local.children}\n </SliderPrimitive.Thumb>\n );\n};\n\nexport type SliderLabelProps<T extends ValidComponent = \"label\"> =\n SliderPrimitive.SliderLabelProps<T> & {\n class?: string;\n };\n\n/**\n * Accessible label for the Slider component.\n */\nexport const SliderLabel = <T extends ValidComponent = \"label\">(\n props: SliderLabelProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderLabelProps, [\"class\"]);\n\n return (\n <SliderPrimitive.Label\n class={cn(\"text-sm font-medium leading-none text-foreground\", local.class)}\n {...(rest as any)}\n />\n );\n};\n\nexport type SliderValueLabelProps<T extends ValidComponent = \"output\"> =\n SliderPrimitive.SliderValueLabelProps<T> & {\n class?: string;\n };\n\n/**\n * Displays current formatted value label for single or dual thumbs.\n */\nexport const SliderValueLabel = <T extends ValidComponent = \"output\">(\n props: SliderValueLabelProps<T>\n) => {\n const [local, rest] = splitProps(props as SliderValueLabelProps, [\"class\"]);\n\n return (\n <SliderPrimitive.ValueLabel\n class={cn(\"text-sm font-medium text-muted-foreground\", local.class)}\n {...(rest as any)}\n />\n );\n};\n",
|
|
15
|
+
"type": "registry:ui"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/registry/switch.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "ui/switch.tsx",
|
|
13
|
-
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { createControllableSignal } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SwitchProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Controlled checked state */\n checked?: boolean;\n /** Uncontrolled default checked state */\n defaultChecked?: boolean;\n /** Event handler triggered when toggle state changes */\n onChange?: (checked: boolean) => void;\n class?: string;\n}\n\n/**\n * Nikala UI Switch component for boolean toggle inputs built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Switch: Component<SwitchProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"checked\",\n \"defaultChecked\",\n \"onChange\",\n \"disabled\",\n \"class\",\n \"onClick\",\n ]);\n\n const [isChecked, setIsChecked] = createControllableSignal({\n value: () => local.checked,\n defaultValue: local.defaultChecked ?? false,\n onChange: (val) => local.onChange?.(val),\n });\n\n const toggle = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n setIsChecked(!isChecked());\n\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={
|
|
13
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { createControllableSignal } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SwitchProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Controlled checked state */\n checked?: boolean;\n /** Uncontrolled default checked state */\n defaultChecked?: boolean;\n /** Event handler triggered when toggle state changes */\n onChange?: (checked: boolean) => void;\n class?: string;\n}\n\n/**\n * Nikala UI Switch component for boolean toggle inputs built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Switch: Component<SwitchProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"checked\",\n \"defaultChecked\",\n \"onChange\",\n \"disabled\",\n \"class\",\n \"onClick\",\n ]);\n\n const [isChecked, setIsChecked] = createControllableSignal({\n value: () => local.checked,\n defaultValue: local.defaultChecked ?? false,\n onChange: (val) => local.onChange?.(val),\n });\n\n const toggle = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n setIsChecked(!isChecked());\n\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isChecked()}\n data-state={isChecked() ? \"checked\" : \"unchecked\"}\n disabled={local.disabled}\n onClick={toggle}\n class={cn(\n \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-lg border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted\",\n local.class\n )}\n {...rest}\n >\n <span\n data-state={isChecked() ? \"checked\" : \"unchecked\"}\n class={cn(\n \"pointer-events-none block h-4 w-4 rounded-sm bg-primary-foreground shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\"\n )}\n />\n </button>\n );\n};",
|
|
14
14
|
"type": "registry:ui"
|
|
15
15
|
}
|
|
16
16
|
]
|
package/registry/tabs.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "ui/tabs.tsx",
|
|
13
|
-
"content": "import {\n createContext,\n useContext,\n splitProps,\n Show,\n type Component,\n type JSX,\n type Accessor,\n} from \"solid-js\";\nimport { createControllableSignal } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\ninterface TabsContextValue {\n value: Accessor<string | undefined>;\n setValue: (value: string) => void;\n orientation: Accessor<\"horizontal\" | \"vertical\">;\n}\n\nconst TabsContext = createContext<TabsContextValue>();\n\nexport interface TabsProps\n extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Controlled active tab value */\n value?: string;\n /** Uncontrolled default active tab value */\n defaultValue?: string;\n /** Callback fired when active tab changes */\n onChange?: (value: string) => void;\n /** Layout orientation of tab triggers and content */\n orientation?: \"horizontal\" | \"vertical\";\n class?: string;\n}\n\n/**\n * Root Tabs container component managing active tab context state and orientation.\n */\nexport const Tabs: Component<TabsProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"defaultValue\",\n \"onChange\",\n \"orientation\",\n \"class\",\n \"children\",\n ]);\n\n const [currentValue, setCurrentValue] = createControllableSignal<string>({\n value: () => local.value,\n defaultValue: local.defaultValue,\n onChange: (val) => local.onChange?.(val),\n });\n\n const orientation = () => local.orientation || \"horizontal\";\n\n const contextValue: TabsContextValue = {\n value: currentValue,\n setValue: (val: string) => setCurrentValue(val),\n orientation,\n };\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n data-orientation={orientation()}\n class={cn(\n \"w-full\",\n orientation() === \"vertical\" ? \"flex flex-row gap-4\" : \"flex flex-col gap-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </TabsContext.Provider>\n );\n};\n\nexport interface TabsListProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Container wrapper for Tab triggers.\n */\nexport const TabsList: Component<TabsListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const context = useContext(TabsContext);\n\n const isVertical = () => context?.orientation() === \"vertical\";\n\n return (\n <div\n role=\"tablist\"\n aria-orientation={context?.orientation() || \"horizontal\"}\n class={cn(\n \"inline-flex rounded-lg bg-muted p-1 text-muted-foreground\",\n isVertical()\n ? \"flex-col h-auto w-auto items-stretch justify-start\"\n : \"h-9 items-center justify-center\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\nexport interface TabsTriggerProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Unique value identifier for this tab */\n value: string;\n class?: string;\n}\n\n/**\n * Tab button trigger to activate a specific tab panel.\n */\nexport const TabsTrigger: Component<TabsTriggerProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"disabled\",\n \"class\",\n \"children\",\n \"onClick\",\n ]);\n const context = useContext(TabsContext);\n\n if (!context) {\n throw new Error(\"TabsTrigger must be used within a Tabs component\");\n }\n\n const isSelected = () => context.value() === local.value;\n const isVertical = () => context.orientation() === \"vertical\";\n\n const handleClick = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n context.setValue(local.value);\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected()}\n data-state={isSelected() ? \"active\" : \"inactive\"}\n data-orientation={context.orientation()}\n disabled={local.disabled}\n onClick={handleClick}\n class={cn(\n \"inline-flex items-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-
|
|
13
|
+
"content": "import {\n createContext,\n useContext,\n splitProps,\n Show,\n type Component,\n type JSX,\n type Accessor,\n} from \"solid-js\";\nimport { createControllableSignal } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\n\ninterface TabsContextValue {\n value: Accessor<string | undefined>;\n setValue: (value: string) => void;\n orientation: Accessor<\"horizontal\" | \"vertical\">;\n}\n\nconst TabsContext = createContext<TabsContextValue>();\n\nexport interface TabsProps\n extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Controlled active tab value */\n value?: string;\n /** Uncontrolled default active tab value */\n defaultValue?: string;\n /** Callback fired when active tab changes */\n onChange?: (value: string) => void;\n /** Layout orientation of tab triggers and content */\n orientation?: \"horizontal\" | \"vertical\";\n class?: string;\n}\n\n/**\n * Root Tabs container component managing active tab context state and orientation.\n */\nexport const Tabs: Component<TabsProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"defaultValue\",\n \"onChange\",\n \"orientation\",\n \"class\",\n \"children\",\n ]);\n\n const [currentValue, setCurrentValue] = createControllableSignal<string>({\n value: () => local.value,\n defaultValue: local.defaultValue,\n onChange: (val) => local.onChange?.(val),\n });\n\n const orientation = () => local.orientation || \"horizontal\";\n\n const contextValue: TabsContextValue = {\n value: currentValue,\n setValue: (val: string) => setCurrentValue(val),\n orientation,\n };\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n data-orientation={orientation()}\n class={cn(\n \"w-full\",\n orientation() === \"vertical\" ? \"flex flex-row gap-4\" : \"flex flex-col gap-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </TabsContext.Provider>\n );\n};\n\nexport interface TabsListProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Container wrapper for Tab triggers.\n */\nexport const TabsList: Component<TabsListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const context = useContext(TabsContext);\n\n const isVertical = () => context?.orientation() === \"vertical\";\n\n return (\n <div\n role=\"tablist\"\n aria-orientation={context?.orientation() || \"horizontal\"}\n class={cn(\n \"inline-flex rounded-lg bg-muted p-1 text-muted-foreground\",\n isVertical()\n ? \"flex-col h-auto w-auto items-stretch justify-start\"\n : \"h-9 items-center justify-center\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\nexport interface TabsTriggerProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Unique value identifier for this tab */\n value: string;\n class?: string;\n}\n\n/**\n * Tab button trigger to activate a specific tab panel.\n */\nexport const TabsTrigger: Component<TabsTriggerProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"disabled\",\n \"class\",\n \"children\",\n \"onClick\",\n ]);\n const context = useContext(TabsContext);\n\n if (!context) {\n throw new Error(\"TabsTrigger must be used within a Tabs component\");\n }\n\n const isSelected = () => context.value() === local.value;\n const isVertical = () => context.orientation() === \"vertical\";\n\n const handleClick = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n context.setValue(local.value);\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected()}\n data-state={isSelected() ? \"active\" : \"inactive\"}\n data-orientation={context.orientation()}\n disabled={local.disabled}\n onClick={handleClick}\n class={cn(\n \"inline-flex items-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-accent data-[state=active]:border data-[state=active]:border-border data-[state=active]:text-foreground data-[state=active]:shadow-sm cursor-pointer\",\n isVertical() ? \"justify-start py-1.5\" : \"justify-center\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </button>\n );\n};\n\nexport interface TabsContentProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** Value matching the corresponding tab trigger */\n value: string;\n class?: string;\n}\n\n/**\n * Content panel revealed when the associated tab is active.\n */\nexport const TabsContent: Component<TabsContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"value\", \"class\", \"children\"]);\n const context = useContext(TabsContext);\n\n if (!context) {\n throw new Error(\"TabsContent must be used within a Tabs component\");\n }\n\n const isSelected = () => context.value() === local.value;\n const isVertical = () => context.orientation() === \"vertical\";\n\n return (\n <Show when={isSelected()}>\n <div\n role=\"tabpanel\"\n data-state={isSelected() ? \"active\" : \"inactive\"}\n data-orientation={context.orientation()}\n class={cn(\n \"ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isVertical() ? \"flex-1 mt-0\" : \"mt-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};",
|
|
14
14
|
"type": "registry:ui"
|
|
15
15
|
}
|
|
16
16
|
]
|
package/registry/textarea.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "ui/textarea.tsx",
|
|
13
|
-
"content": "// src/components/ui/textarea.tsx\nimport {\n createSignal,\n splitProps,\n Show,\n type Component,\n type JSX,\n} from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface TextareaProps\n extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Uncontrolled initial default value */\n defaultValue?: string | number;\n /** Maximum allowed character limit */\n maxLength?: number;\n /** Whether to show live character count badge */\n showCount?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI Textarea component with optional live character counter and limit indicators.\n */\nexport const Textarea: Component<TextareaProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"maxLength\",\n \"showCount\",\n \"value\",\n \"defaultValue\",\n \"onInput\",\n \"class\",\n ]);\n\n /* Internal reactive signal for character count tracking */\n const [currentValue, setCurrentValue] = createSignal<string>(\n String(local.value || local.defaultValue || \"\")\n );\n\n const handleInput: JSX.EventHandlerUnion<HTMLTextAreaElement, InputEvent> = (\n e\n ) => {\n setCurrentValue(e.currentTarget.value);\n if (typeof local.onInput === \"function\") {\n (local.onInput as (e: InputEvent) => void)(e);\n }\n };\n\n const count = () => currentValue().length;\n const isAtLimit = () =>\n local.maxLength !== undefined && count() >= local.maxLength;\n\n return (\n <div class=\"relative flex flex-col w-full\">\n <textarea\n value={local.value !== undefined ? String(local.value) : currentValue()}\n maxLength={local.maxLength}\n onInput={handleInput}\n class={cn(\n \"flex min-h-
|
|
13
|
+
"content": "// src/components/ui/textarea.tsx\nimport {\n createSignal,\n splitProps,\n Show,\n type Component,\n type JSX,\n} from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface TextareaProps\n extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Uncontrolled initial default value */\n defaultValue?: string | number;\n /** Maximum allowed character limit */\n maxLength?: number;\n /** Whether to show live character count badge */\n showCount?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI Textarea component with optional live character counter and limit indicators.\n */\nexport const Textarea: Component<TextareaProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"maxLength\",\n \"showCount\",\n \"value\",\n \"defaultValue\",\n \"onInput\",\n \"class\",\n ]);\n\n /* Internal reactive signal for character count tracking */\n const [currentValue, setCurrentValue] = createSignal<string>(\n String(local.value || local.defaultValue || \"\")\n );\n\n const handleInput: JSX.EventHandlerUnion<HTMLTextAreaElement, InputEvent> = (\n e\n ) => {\n setCurrentValue(e.currentTarget.value);\n if (typeof local.onInput === \"function\") {\n (local.onInput as (e: InputEvent) => void)(e);\n }\n };\n\n const count = () => currentValue().length;\n const isAtLimit = () =>\n local.maxLength !== undefined && count() >= local.maxLength;\n\n return (\n <div class=\"relative flex flex-col w-full\">\n <textarea\n value={local.value !== undefined ? String(local.value) : currentValue()}\n maxLength={local.maxLength}\n onInput={handleInput}\n class={cn(\n \"flex min-h-20 w-full rounded-md border border-input bg-muted px-3 py-2 text-base shadow-2xs transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n local.class\n )}\n {...rest}\n />\n\n {/* Dynamic Character Counter */}\n <Show when={local.showCount ?? Boolean(local.maxLength)}>\n <div\n class={cn(\n \"mt-1 text-right text-[11px] font-mono transition-colors select-none\",\n isAtLimit() ? \"text-rose-500 font-bold\" : \"text-muted-foreground\"\n )}\n >\n <span>{count()}</span>\n <Show when={local.maxLength}>\n <span> / {local.maxLength}</span>\n </Show>\n </div>\n </Show>\n </div>\n );\n};",
|
|
14
14
|
"type": "registry:ui"
|
|
15
15
|
}
|
|
16
16
|
]
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"path": "ui/theme-toggle.tsx",
|
|
34
|
-
"content": "import { splitProps, type Component, For, Show } from \"solid-js\";\nimport { Sun, Moon, Monitor } from \"lucide-solid\";\nimport {\n useTheme,\n type AccentColor,\n type Radius,\n type Theme,\n} from \"../../providers/theme-provider\";\nimport {\n runThemeTransition,\n type ThemeEffect,\n} from \"../../providers/theme-transitions\";\nimport { Button } from \"./button\";\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"./dropdown-menu\";\nimport { cn } from \"@/lib/cn\";\n\nimport { createColorMode } from \"@nikala-ui/hooks\";\n\nexport interface ThemeToggleProps {\n /** Display mode: \"mini\" for compact dropdown, \"max\" for full customizer panel (default: \"mini\") */\n mode?: \"mini\" | \"max\";\n /** Transition animation effect when changing themes (\"none\", \"circular\", \"fade\") */\n effect?: ThemeEffect;\n class?: string;\n}\n\nconst ACCENT_OPTIONS: { name: AccentColor; label: string; color: string }[] = [\n { name: \"wine\", label: \"Wine\", color: \"bg-[#722f37]\" },\n { name: \"violet\", label: \"Violet\", color: \"bg-[#7c3aed]\" },\n { name: \"sky\", label: \"Sky\", color: \"bg-[#0284c7]\" },\n { name: \"emerald\", label: \"Emerald\", color: \"bg-[#059669]\" },\n { name: \"rose\", label: \"Rose\", color: \"bg-[#e11d48]\" },\n { name: \"amber\", label: \"Amber\", color: \"bg-[#d97706]\" },\n { name: \"zinc\", label: \"Zinc\", color: \"bg-[#18181b]\" },\n];\n\nconst RADIUS_OPTIONS: { value: Radius; label: string }[] = [\n { value: \"0\", label: \"0\" },\n { value: \"0.3\", label: \"0.3\" },\n { value: \"0.5\", label: \"0.5\" },\n { value: \"0.75\", label: \"0.75\" },\n { value: \"1.0\", label: \"1.0\" },\n];\n\n/**\n * Interactive UI theme switcher supporting mini/max modes and View Transition animations.\n */\nexport const ThemeToggle: Component<ThemeToggleProps> = (props) => {\n const [local] = splitProps(props, [\"mode\", \"effect\", \"class\"]);\n const { theme, setTheme, accent, setAccent, radius, setRadius } = useTheme();\n\n const colorMode = createColorMode({\n initialValue: theme(),\n storageKey: \"nikala-theme-mode\",\n });\n\n const mode = () => local.mode || \"mini\";\n const effect = () => local.effect || \"none\";\n\n /* Reactive accessor determining whether dark mode is active via createColorMode hook */\n const isDarkMode = () => {\n const currentTheme = theme();\n if (currentTheme === \"dark\") return true;\n if (currentTheme === \"light\") return false;\n return colorMode.isDark();\n };\n\n const changeThemeWithEffect = (newTheme: Theme, e: MouseEvent) => {\n runThemeTransition(effect(), e, () => {\n setTheme(newTheme);\n colorMode.setMode(newTheme);\n });\n };\n\n return (\n <DropdownMenu placement=\"bottom-end\">\n <DropdownMenuTrigger\n as={Button}\n variant=\"
|
|
34
|
+
"content": "import { splitProps, type Component, For, Show } from \"solid-js\";\nimport { Sun, Moon, Monitor } from \"lucide-solid\";\nimport {\n useTheme,\n type AccentColor,\n type Radius,\n type Theme,\n} from \"../../providers/theme-provider\";\nimport {\n runThemeTransition,\n type ThemeEffect,\n} from \"../../providers/theme-transitions\";\nimport { Button } from \"./button\";\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"./dropdown-menu\";\nimport { cn } from \"@/lib/cn\";\n\nimport { createColorMode } from \"@nikala-ui/hooks\";\n\nexport interface ThemeToggleProps {\n /** Display mode: \"mini\" for compact dropdown, \"max\" for full customizer panel (default: \"mini\") */\n mode?: \"mini\" | \"max\";\n /** Transition animation effect when changing themes (\"none\", \"circular\", \"fade\") */\n effect?: ThemeEffect;\n class?: string;\n}\n\nconst ACCENT_OPTIONS: { name: AccentColor; label: string; color: string }[] = [\n { name: \"wine\", label: \"Wine\", color: \"bg-[#722f37]\" },\n { name: \"violet\", label: \"Violet\", color: \"bg-[#7c3aed]\" },\n { name: \"sky\", label: \"Sky\", color: \"bg-[#0284c7]\" },\n { name: \"emerald\", label: \"Emerald\", color: \"bg-[#059669]\" },\n { name: \"rose\", label: \"Rose\", color: \"bg-[#e11d48]\" },\n { name: \"amber\", label: \"Amber\", color: \"bg-[#d97706]\" },\n { name: \"zinc\", label: \"Zinc\", color: \"bg-[#18181b]\" },\n];\n\nconst RADIUS_OPTIONS: { value: Radius; label: string }[] = [\n { value: \"0\", label: \"0\" },\n { value: \"0.3\", label: \"0.3\" },\n { value: \"0.5\", label: \"0.5\" },\n { value: \"0.75\", label: \"0.75\" },\n { value: \"1.0\", label: \"1.0\" },\n];\n\n/**\n * Interactive UI theme switcher supporting mini/max modes and View Transition animations.\n */\nexport const ThemeToggle: Component<ThemeToggleProps> = (props) => {\n const [local] = splitProps(props, [\"mode\", \"effect\", \"class\"]);\n const { theme, setTheme, accent, setAccent, radius, setRadius } = useTheme();\n\n const colorMode = createColorMode({\n initialValue: theme(),\n storageKey: \"nikala-theme-mode\",\n });\n\n const mode = () => local.mode || \"mini\";\n const effect = () => local.effect || \"none\";\n\n /* Reactive accessor determining whether dark mode is active via createColorMode hook */\n const isDarkMode = () => {\n const currentTheme = theme();\n if (currentTheme === \"dark\") return true;\n if (currentTheme === \"light\") return false;\n return colorMode.isDark();\n };\n\n const changeThemeWithEffect = (newTheme: Theme, e: MouseEvent) => {\n runThemeTransition(effect(), e, () => {\n setTheme(newTheme);\n colorMode.setMode(newTheme);\n });\n };\n\n return (\n <DropdownMenu placement=\"bottom-end\">\n <DropdownMenuTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n class={cn(\"relative h-9 w-9 cursor-pointer\", local.class)}\n >\n {/* Reactive Sun / Moon Icon Toggle */}\n <Show\n when={isDarkMode()}\n fallback={<Sun class=\"h-4 w-4 text-foreground transition-transform\" />}\n >\n <Moon class=\"h-4 w-4 text-foreground transition-transform\" />\n </Show>\n\n <span class=\"sr-only\">Toggle theme</span>\n </DropdownMenuTrigger>\n\n <Show\n when={mode() === \"max\"}\n fallback={\n /* Mini Mode: Compact Dropdown */\n <DropdownMenuContent>\n <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect(\"light\", e)}>\n <Sun class=\"mr-2 h-4 w-4 text-muted-foreground\" />\n Light\n </DropdownMenuItem>\n\n <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect(\"dark\", e)}>\n <Moon class=\"mr-2 h-4 w-4 text-muted-foreground\" />\n Dark\n </DropdownMenuItem>\n\n <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect(\"system\", e)}>\n <Monitor class=\"mr-2 h-4 w-4 text-muted-foreground\" />\n System\n </DropdownMenuItem>\n </DropdownMenuContent>\n }\n >\n {/* Max Mode: Full Theme Customizer Panel */}\n <DropdownMenuContent class=\"w-64 p-3\">\n <DropdownMenuLabel class=\"px-0 pt-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Theme Mode\n </DropdownMenuLabel>\n <div class=\"grid grid-cols-3 gap-1 my-1.5\">\n <Button\n variant={theme() === \"light\" ? \"default\" : \"outline\"}\n size=\"sm\"\n onClick={(e: MouseEvent) => changeThemeWithEffect(\"light\", e)}\n class=\"h-8 text-xs cursor-pointer\"\n >\n Light\n </Button>\n <Button\n variant={theme() === \"dark\" ? \"default\" : \"outline\"}\n size=\"sm\"\n onClick={(e: MouseEvent) => changeThemeWithEffect(\"dark\", e)}\n class=\"h-8 text-xs cursor-pointer\"\n >\n Dark\n </Button>\n <Button\n variant={theme() === \"system\" ? \"default\" : \"outline\"}\n size=\"sm\"\n onClick={(e: MouseEvent) => changeThemeWithEffect(\"system\", e)}\n class=\"h-8 text-xs cursor-pointer\"\n >\n System\n </Button>\n </div>\n\n <DropdownMenuSeparator class=\"my-2\" />\n\n <DropdownMenuLabel class=\"px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Brand Accent Color\n </DropdownMenuLabel>\n <div class=\"flex flex-wrap gap-1.5 my-1.5\">\n <For each={ACCENT_OPTIONS}>\n {(opt) => (\n <button\n type=\"button\"\n title={opt.label}\n onClick={() => setAccent(opt.name)}\n class={cn(\n \"h-6 w-6 rounded-md transition-all cursor-pointer border border-border flex items-center justify-center\",\n opt.color,\n accent() === opt.name ? \"ring-2 ring-primary ring-offset-2 ring-offset-background scale-110\" : \"hover:scale-105\"\n )}\n />\n )}\n </For>\n </div>\n\n <DropdownMenuSeparator class=\"my-2\" />\n\n <DropdownMenuLabel class=\"px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Border Radius\n </DropdownMenuLabel>\n <div class=\"grid grid-cols-5 gap-1 my-1.5\">\n <For each={RADIUS_OPTIONS}>\n {(r) => (\n <Button\n variant={radius() === r.value ? \"default\" : \"outline\"}\n size=\"sm\"\n onClick={() => setRadius(r.value)}\n class=\"h-7 text-xs px-1 cursor-pointer\"\n >\n {r.label}\n </Button>\n )}\n </For>\n </div>\n </DropdownMenuContent>\n </Show>\n </DropdownMenu>\n );\n};",
|
|
35
35
|
"type": "registry:ui"
|
|
36
36
|
}
|
|
37
37
|
]
|
package/registry/toast.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
{
|
|
15
15
|
"path": "ui/toast.tsx",
|
|
16
|
-
"content": "import { splitProps, type Component, type JSX, type ComponentProps } from \"solid-js\";\nimport { Toast as KobalteToast, toaster } from \"@kobalte/core/toast\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X, CircleCheck, Info, CircleAlert, TriangleAlert } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\nexport const toastVariants = cva(\n \"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-lg border p-4 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--kb-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--kb-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[opened]:animate-in data-[closed]:animate-out data-[swipe=end]:animate-out data-[closed]:fade-out-80 data-[closed]:slide-out-to-right-full data-[opened]:slide-in-from-top-full data-[opened]:sm:slide-in-from-bottom-full\",\n {\n variants: {\n variant: {\n default: \"border-border bg-background text-foreground\",\n success: \"border-emerald-500/20 bg-emerald-500/10 text-emerald-900 dark:text-emerald-200 border-emerald-500/30\",\n destructive: \"border-destructive/30 bg-destructive/10 text-destructive dark:text-red-300\",\n warning: \"border-amber-500/20 bg-amber-500/10 text-amber-900 dark:text-amber-200 border-amber-500/30\",\n info: \"border-sky-500/20 bg-sky-500/10 text-sky-900 dark:text-sky-200 border-sky-500/30\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface ToastProps\n extends ComponentProps<typeof KobalteToast>,\n VariantProps<typeof toastVariants> {\n class?: string;\n}\n\nexport const Toast: Component<ToastProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"variant\"]);\n\n return (\n <KobalteToast\n class={cn(toastVariants({ variant: local.variant }), local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastTitle: Component<ComponentProps<typeof KobalteToast.Title>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Title\n class={cn(\"text-sm font-semibold [&+div]:text-xs\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastDescription: Component<ComponentProps<typeof KobalteToast.Description>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Description\n class={cn(\"text-sm opacity-90\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastCloseButton: Component<ComponentProps<typeof KobalteToast.CloseButton>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <KobalteToast.CloseButton\n class={cn(\n \"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100\",\n local.class\n )}\n {...rest}\n >\n {local.children || <X class=\"h-4 w-4\" />}\n </KobalteToast.CloseButton>\n );\n};\n\nexport const ToastRegion: Component<ComponentProps<typeof KobalteToast.Region>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Region\n class={cn(\n \"fixed top-0 z-
|
|
16
|
+
"content": "import { splitProps, type Component, type JSX, type ComponentProps } from \"solid-js\";\nimport { Toast as KobalteToast, toaster } from \"@kobalte/core/toast\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X, CircleCheck, Info, CircleAlert, TriangleAlert } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\nexport const toastVariants = cva(\n \"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-lg border p-4 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--kb-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--kb-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[opened]:animate-in data-[closed]:animate-out data-[swipe=end]:animate-out data-[closed]:fade-out-80 data-[closed]:slide-out-to-right-full data-[opened]:slide-in-from-top-full data-[opened]:sm:slide-in-from-bottom-full\",\n {\n variants: {\n variant: {\n default: \"border-border bg-background text-foreground\",\n success: \"border-emerald-500/20 bg-emerald-500/10 text-emerald-900 dark:text-emerald-200 border-emerald-500/30\",\n destructive: \"border-destructive/30 bg-destructive/10 text-destructive dark:text-red-300\",\n warning: \"border-amber-500/20 bg-amber-500/10 text-amber-900 dark:text-amber-200 border-amber-500/30\",\n info: \"border-sky-500/20 bg-sky-500/10 text-sky-900 dark:text-sky-200 border-sky-500/30\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface ToastProps\n extends ComponentProps<typeof KobalteToast>,\n VariantProps<typeof toastVariants> {\n class?: string;\n}\n\nexport const Toast: Component<ToastProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"variant\"]);\n\n return (\n <KobalteToast\n class={cn(toastVariants({ variant: local.variant }), local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastTitle: Component<ComponentProps<typeof KobalteToast.Title>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Title\n class={cn(\"text-sm font-semibold [&+div]:text-xs\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastDescription: Component<ComponentProps<typeof KobalteToast.Description>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Description\n class={cn(\"text-sm opacity-90\", local.class)}\n {...rest}\n />\n );\n};\n\nexport const ToastCloseButton: Component<ComponentProps<typeof KobalteToast.CloseButton>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <KobalteToast.CloseButton\n class={cn(\n \"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100\",\n local.class\n )}\n {...rest}\n >\n {local.children || <X class=\"h-4 w-4\" />}\n </KobalteToast.CloseButton>\n );\n};\n\nexport const ToastRegion: Component<ComponentProps<typeof KobalteToast.Region>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <KobalteToast.Region\n class={cn(\n \"fixed top-0 z-100 flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-105\",\n local.class\n )}\n {...rest}\n />\n );\n};\n\nexport const ToastList: Component<ComponentProps<typeof KobalteToast.List>> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return <KobalteToast.List class={cn(\"flex flex-col gap-2\", local.class)} {...rest} />;\n};\n\n/* --- Helper Function to Trigger Toast Notifications --- */\nexport interface ShowToastOptions {\n title: string;\n description?: string;\n variant?: \"default\" | \"success\" | \"destructive\" | \"warning\" | \"info\";\n duration?: number;\n}\n\nexport const showToast = (options: ShowToastOptions) => {\n return toaster.show((props) => (\n <Toast toastId={props.toastId} variant={options.variant || \"default\"}>\n <div class=\"flex items-start gap-3\">\n {options.variant === \"success\" && <CircleCheck class=\"h-5 w-5 text-emerald-500 shrink-0 mt-0.5\" />}\n {options.variant === \"destructive\" && <CircleAlert class=\"h-5 w-5 text-red-500 shrink-0 mt-0.5\" />}\n {options.variant === \"warning\" && <TriangleAlert class=\"h-5 w-5 text-amber-500 shrink-0 mt-0.5\" />}\n {options.variant === \"info\" && <Info class=\"h-5 w-5 text-sky-500 shrink-0 mt-0.5\" />}\n <div class=\"grid gap-1\">\n <ToastTitle>{options.title}</ToastTitle>\n {options.description && <ToastDescription>{options.description}</ToastDescription>}\n </div>\n </div>\n <ToastCloseButton />\n </Toast>\n ));\n};",
|
|
17
17
|
"type": "registry:ui"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
@@ -26,8 +26,6 @@ export const Accordion = <T extends ValidComponent = "div">(
|
|
|
26
26
|
"class",
|
|
27
27
|
"type",
|
|
28
28
|
"multiple",
|
|
29
|
-
"value",
|
|
30
|
-
"defaultValue",
|
|
31
29
|
]);
|
|
32
30
|
|
|
33
31
|
// Support both `type="multiple"` and `multiple={true}`
|
|
@@ -36,8 +34,6 @@ export const Accordion = <T extends ValidComponent = "div">(
|
|
|
36
34
|
return (
|
|
37
35
|
<AccordionPrimitive.Root
|
|
38
36
|
multiple={isMultiple()}
|
|
39
|
-
value={local.value}
|
|
40
|
-
defaultValue={local.defaultValue}
|
|
41
37
|
class={cn("w-full divide-y divide-border", local.class)}
|
|
42
38
|
{...(rest as any)}
|
|
43
39
|
/>
|
|
@@ -21,7 +21,7 @@ export const Avatar: Component<AvatarProps> = (props) => {
|
|
|
21
21
|
return (
|
|
22
22
|
<div
|
|
23
23
|
class={cn(
|
|
24
|
-
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-
|
|
24
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-lg border border-border bg-muted",
|
|
25
25
|
local.class
|
|
26
26
|
)}
|
|
27
27
|
{...rest}
|
|
@@ -18,6 +18,12 @@ export const badgeVariants = cva(
|
|
|
18
18
|
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
19
19
|
outline:
|
|
20
20
|
"text-foreground border-border",
|
|
21
|
+
success:
|
|
22
|
+
"border-transparent bg-green-600 text-zinc-50 shadow-sm hover:bg-green-600/90 dark:bg-green-900 dark:text-zinc-50 dark:hover:bg-green-900/90",
|
|
23
|
+
warning:
|
|
24
|
+
"border-transparent bg-yellow-600 text-zinc-50 shadow-sm hover:bg-yellow-600/90 dark:bg-yellow-900 dark:text-zinc-50 dark:hover:bg-yellow-900/90",
|
|
25
|
+
info:
|
|
26
|
+
"border-transparent bg-blue-600 text-zinc-50 shadow-sm hover:bg-blue-600/90 dark:bg-blue-900 dark:text-zinc-50 dark:hover:bg-blue-900/90",
|
|
21
27
|
},
|
|
22
28
|
},
|
|
23
29
|
defaultVariants: {
|
|
@@ -11,18 +11,22 @@ export const buttonVariants = cva(
|
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
13
|
default:
|
|
14
|
-
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
14
|
+
"bg-primary text-primary-foreground shadow hover:bg-primary/90 border border-primary/50",
|
|
15
15
|
destructive:
|
|
16
16
|
"bg-red-600 text-zinc-50 shadow-sm hover:bg-red-600/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90",
|
|
17
17
|
outline:
|
|
18
18
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
19
19
|
secondary:
|
|
20
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 border border-border",
|
|
21
21
|
ghost:
|
|
22
22
|
"hover:bg-accent hover:text-accent-foreground",
|
|
23
23
|
link: "text-primary underline-offset-4 hover:underline",
|
|
24
|
+
success: "*:bg-green-600 text-zinc-50 shadow-sm hover:bg-green-600/90 dark:bg-green-900 dark:text-zinc-50 dark:hover:bg-green-900/90",
|
|
25
|
+
warning: "*:bg-yellow-600 text-zinc-50 shadow-sm hover:bg-yellow-600/90 dark:bg-yellow-900 dark:text-zinc-50 dark:hover:bg-yellow-900/90",
|
|
26
|
+
info: "*:bg-blue-600 text-zinc-50 shadow-sm hover:bg-blue-600/90 dark:bg-blue-900 dark:text-zinc-50 dark:hover:bg-blue-900/90",
|
|
24
27
|
},
|
|
25
28
|
size: {
|
|
29
|
+
xs: "h-6 rounded-md px-2 text-xs",
|
|
26
30
|
default: "h-9 px-4 py-2",
|
|
27
31
|
sm: "h-8 rounded-md px-3 text-xs",
|
|
28
32
|
lg: "h-10 rounded-md px-8",
|
|
@@ -47,7 +47,7 @@ export const Checkbox: Component<CheckboxProps> = (props) => {
|
|
|
47
47
|
<button
|
|
48
48
|
type="button"
|
|
49
49
|
role="checkbox"
|
|
50
|
-
aria-checked={
|
|
50
|
+
aria-checked={isChecked()}
|
|
51
51
|
data-state={isChecked() ? "checked" : "unchecked"}
|
|
52
52
|
disabled={local.disabled}
|
|
53
53
|
onClick={toggle}
|