solid-element-ui 0.2.4 → 0.2.5
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/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/accordion/accordion.tsx +80 -0
- package/src/alert/alert.tsx +86 -0
- package/src/alert-dialog/alert-dialog.tsx +98 -0
- package/src/badge/badge.tsx +52 -0
- package/src/breadcrumbs/breadcrumbs.tsx +69 -0
- package/src/button/button.tsx +216 -0
- package/src/checkbox/checkbox.tsx +64 -0
- package/src/collapsible/collapsible.tsx +46 -0
- package/src/color-area/color-area.tsx +46 -0
- package/src/color-channel-field/color-channel-field.tsx +46 -0
- package/src/color-field/color-field.tsx +64 -0
- package/src/color-slider/color-slider.tsx +60 -0
- package/src/color-swatch/color-swatch.tsx +33 -0
- package/src/color-wheel/color-wheel.tsx +50 -0
- package/src/combobox/combobox.tsx +97 -0
- package/src/context-menu/context-menu.tsx +102 -0
- package/src/dialog/dialog.tsx +102 -0
- package/src/dropdown-menu/dropdown-menu.tsx +111 -0
- package/src/file-field/file-field.tsx +114 -0
- package/src/hover-card/hover-card.tsx +62 -0
- package/src/image/image.tsx +59 -0
- package/src/index.tsx +91 -0
- package/src/link/link.tsx +64 -0
- package/src/menubar/menubar.tsx +81 -0
- package/src/meter/meter.tsx +89 -0
- package/src/navigation-menu/navigation-menu.tsx +90 -0
- package/src/number-field/number-field.tsx +80 -0
- package/src/pagination/pagination.tsx +68 -0
- package/src/popover/popover.tsx +59 -0
- package/src/progress/progress.tsx +83 -0
- package/src/radio-group/radio-group.tsx +94 -0
- package/src/rating-group/rating-group.tsx +101 -0
- package/src/search/search.tsx +99 -0
- package/src/segmented-control/segmented-control.tsx +92 -0
- package/src/select/select.tsx +164 -0
- package/src/separator/separator.tsx +62 -0
- package/src/skeleton/skeleton.tsx +73 -0
- package/src/slider/slider.tsx +91 -0
- package/src/style/index.css +150 -0
- package/src/switch/switch.tsx +104 -0
- package/src/tabs/tabs.tsx +73 -0
- package/src/text-field/text-field.tsx +97 -0
- package/src/time-field/time-field.tsx +103 -0
- package/src/toast/toast.tsx +128 -0
- package/src/toggle-button/toggle-button.tsx +68 -0
- package/src/toggle-group/toggle-group.tsx +86 -0
- package/src/tooltip/tooltip.tsx +78 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Meter as KMeter } from "@kobalte/core/meter";
|
|
2
|
+
import { splitProps, type ComponentProps } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
// TODO 1. 格式
|
|
6
|
+
|
|
7
|
+
const meterStyles = tv(
|
|
8
|
+
{
|
|
9
|
+
slots: {
|
|
10
|
+
root: "flex flex-col gap-2 w-full antialiased",
|
|
11
|
+
labelContainer:
|
|
12
|
+
"flex justify-between items-center text-sm font-medium text-slate-700 dark:text-slate-300",
|
|
13
|
+
track: "h-2.5 w-full rounded-full bg-slate-100 dark:bg-slate-800 overflow-hidden",
|
|
14
|
+
fill: "h-full transition-all duration-500 ease-out rounded-full",
|
|
15
|
+
},
|
|
16
|
+
variants: {
|
|
17
|
+
color: {
|
|
18
|
+
primary: { fill: "bg-blue-600" },
|
|
19
|
+
success: { fill: "bg-emerald-500" },
|
|
20
|
+
warning: { fill: "bg-amber-500" },
|
|
21
|
+
danger: { fill: "bg-red-500" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
color: "primary",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
twMerge: true,
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
type MeterVariants = VariantProps<typeof meterStyles>;
|
|
34
|
+
|
|
35
|
+
export const Meter = Object.assign(
|
|
36
|
+
(props: ComponentProps<typeof KMeter> & MeterVariants) => {
|
|
37
|
+
const [local, variantProps, others] = splitProps(
|
|
38
|
+
props,
|
|
39
|
+
["class"],
|
|
40
|
+
["color"]
|
|
41
|
+
);
|
|
42
|
+
const s = () => meterStyles({ color: variantProps.color });
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<KMeter class={s().root({ class: local.class })} {...others}>
|
|
46
|
+
{others.children}
|
|
47
|
+
</KMeter>
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
Label: (props: ComponentProps<typeof KMeter.Label>) => {
|
|
52
|
+
const [local, others] = splitProps(props, ["class"]);
|
|
53
|
+
return (
|
|
54
|
+
<KMeter.Label
|
|
55
|
+
class={meterStyles().labelContainer({ class: local.class })}
|
|
56
|
+
{...others}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
ValueLabel: (props: ComponentProps<typeof KMeter.ValueLabel>) => {
|
|
61
|
+
const [local, others] = splitProps(props, ["class"]);
|
|
62
|
+
return (
|
|
63
|
+
<KMeter.ValueLabel
|
|
64
|
+
class={`text-xs text-slate-500 ${local.class}`}
|
|
65
|
+
{...others}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
Track: (props: ComponentProps<typeof KMeter.Track>) => {
|
|
70
|
+
const [local, others] = splitProps(props, ["class"]);
|
|
71
|
+
return (
|
|
72
|
+
<KMeter.Track
|
|
73
|
+
class={meterStyles().track({ class: local.class })}
|
|
74
|
+
{...others}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
Fill: (props: ComponentProps<typeof KMeter.Fill>) => {
|
|
79
|
+
const [local, others] = splitProps(props, ["class"]);
|
|
80
|
+
// 注意:Fill 不需要手动设置宽度,Kobalte 会通过 style 注入百分比
|
|
81
|
+
return (
|
|
82
|
+
<KMeter.Fill
|
|
83
|
+
class={meterStyles().fill({ class: local.class })}
|
|
84
|
+
{...others}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { NavigationMenu as KNavigationMenu } from "@kobalte/core/navigation-menu";
|
|
2
|
+
import { splitProps, type ComponentProps, type JSX, For, Show } from "solid-js";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
// TODO 不显示问题
|
|
6
|
+
|
|
7
|
+
const navStyles = tv(
|
|
8
|
+
{
|
|
9
|
+
slots: {
|
|
10
|
+
root: "relative z-10 flex w-full justify-center antialiased",
|
|
11
|
+
trigger: [
|
|
12
|
+
"group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-all",
|
|
13
|
+
"hover:bg-slate-100 hover:text-slate-900 data-[state=open]:bg-slate-100/50",
|
|
14
|
+
"dark:hover:bg-slate-800 dark:hover:text-slate-50",
|
|
15
|
+
],
|
|
16
|
+
content:
|
|
17
|
+
"absolute left-0 top-0 w-full p-2 animate-in fade-in zoom-in-95 duration-200",
|
|
18
|
+
viewport:
|
|
19
|
+
"relative mt-1.5 h-(--kb-navigation-menu-viewport-height) w-(--kb-navigation-menu-viewport-width) origin-[top_center] overflow-hidden rounded-md border bg-white shadow-xl dark:bg-slate-950 dark:border-slate-800 transition-[width,height] duration-300",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
twMerge: true,
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const { root, trigger, content, viewport } = navStyles();
|
|
28
|
+
|
|
29
|
+
interface NavItem {
|
|
30
|
+
title: string;
|
|
31
|
+
href?: string;
|
|
32
|
+
content?: JSX.Element;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface NavigationMenuProps
|
|
36
|
+
extends ComponentProps<typeof KNavigationMenu> {
|
|
37
|
+
items: NavItem[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const NavigationMenu = (props: NavigationMenuProps) => {
|
|
41
|
+
const [local, others] = splitProps(props, ["items", "class"]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<KNavigationMenu class={root({ class: local.class })} {...others}>
|
|
45
|
+
<For each={local.items}>
|
|
46
|
+
{(item) => (
|
|
47
|
+
<KNavigationMenu.Menu>
|
|
48
|
+
<Show
|
|
49
|
+
when={item.content}
|
|
50
|
+
fallback={
|
|
51
|
+
<KNavigationMenu.Trigger
|
|
52
|
+
as="a"
|
|
53
|
+
href={item.href}
|
|
54
|
+
class={trigger()}
|
|
55
|
+
>
|
|
56
|
+
{item.title}
|
|
57
|
+
</KNavigationMenu.Trigger>
|
|
58
|
+
}
|
|
59
|
+
>
|
|
60
|
+
<KNavigationMenu.Trigger class={trigger()}>
|
|
61
|
+
{item.title}
|
|
62
|
+
<svg
|
|
63
|
+
class="ml-1 h-3 w-3 transition-transform duration-200 group-data-[state=open]:rotate-180"
|
|
64
|
+
fill="none"
|
|
65
|
+
viewBox="0 0 24 24"
|
|
66
|
+
stroke="currentColor"
|
|
67
|
+
>
|
|
68
|
+
<path
|
|
69
|
+
stroke-linecap="round"
|
|
70
|
+
stroke-linejoin="round"
|
|
71
|
+
stroke-width="2"
|
|
72
|
+
d="M19 9l-7 7-7-7"
|
|
73
|
+
/>
|
|
74
|
+
</svg>
|
|
75
|
+
</KNavigationMenu.Trigger>
|
|
76
|
+
<KNavigationMenu.Portal>
|
|
77
|
+
<KNavigationMenu.Content class={content()}>
|
|
78
|
+
{item.content}
|
|
79
|
+
</KNavigationMenu.Content>
|
|
80
|
+
</KNavigationMenu.Portal>
|
|
81
|
+
</Show>
|
|
82
|
+
</KNavigationMenu.Menu>
|
|
83
|
+
)}
|
|
84
|
+
</For>
|
|
85
|
+
<KNavigationMenu.Viewport class={viewport()}>
|
|
86
|
+
<KNavigationMenu.Arrow />
|
|
87
|
+
</KNavigationMenu.Viewport>
|
|
88
|
+
</KNavigationMenu>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NumberField as KNumberField } from "@kobalte/core/number-field";
|
|
2
|
+
import { splitProps, type ComponentProps, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
import { ChevronUp, ChevronDown } from "lucide-solid";
|
|
5
|
+
|
|
6
|
+
const numberFieldStyles = tv(
|
|
7
|
+
{
|
|
8
|
+
slots: {
|
|
9
|
+
root: "flex flex-col gap-1.5 w-full antialiased",
|
|
10
|
+
label: "text-sm font-medium text-slate-700 dark:text-slate-300 ml-1",
|
|
11
|
+
container: [
|
|
12
|
+
"relative flex items-center rounded-md border border-slate-200 bg-white transition-shadow shadow-sm",
|
|
13
|
+
"focus-within:ring-2 focus-within:ring-blue-500/20 focus-within:border-blue-500",
|
|
14
|
+
"dark:bg-slate-950 dark:border-slate-800",
|
|
15
|
+
],
|
|
16
|
+
input: "flex-1 bg-transparent px-3 py-2 text-sm outline-none placeholder:text-slate-400 disabled:cursor-not-allowed",
|
|
17
|
+
controls:
|
|
18
|
+
"flex flex-col border-l border-slate-200 dark:border-slate-800",
|
|
19
|
+
stepper: [
|
|
20
|
+
"flex h-1/2 w-8 items-center justify-center transition-colors hover:bg-slate-50 active:bg-slate-100",
|
|
21
|
+
"dark:hover:bg-slate-900 dark:active:bg-slate-800 disabled:opacity-30 disabled:pointer-events-none",
|
|
22
|
+
],
|
|
23
|
+
errorMessage: "text-xs text-red-500 font-medium ml-1 mt-1",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
twMerge: true,
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
type NumberFieldVariants = VariantProps<typeof numberFieldStyles>;
|
|
32
|
+
|
|
33
|
+
export interface NumberFieldProps
|
|
34
|
+
extends Omit<ComponentProps<typeof KNumberField>, "class">,
|
|
35
|
+
NumberFieldVariants {
|
|
36
|
+
label?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
class?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const NumberField = (props: NumberFieldProps) => {
|
|
42
|
+
// 严格处理属性,防止 TS 报错“已声明但未使用”
|
|
43
|
+
const [local, others] = splitProps(props, [
|
|
44
|
+
"label",
|
|
45
|
+
"description",
|
|
46
|
+
"class",
|
|
47
|
+
]);
|
|
48
|
+
const s = numberFieldStyles();
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<KNumberField class={s.root({ class: local.class })} {...others}>
|
|
52
|
+
<Show when={local.label}>
|
|
53
|
+
<KNumberField.Label class={s.label()}>
|
|
54
|
+
{local.label}
|
|
55
|
+
</KNumberField.Label>
|
|
56
|
+
</Show>
|
|
57
|
+
|
|
58
|
+
<div class={s.container()}>
|
|
59
|
+
<KNumberField.Input class={s.input()} />
|
|
60
|
+
<div class={s.controls()}>
|
|
61
|
+
<KNumberField.IncrementTrigger class={s.stepper()}>
|
|
62
|
+
<ChevronUp size={14} />
|
|
63
|
+
</KNumberField.IncrementTrigger>
|
|
64
|
+
<KNumberField.DecrementTrigger
|
|
65
|
+
class={s.stepper({
|
|
66
|
+
class: "border-t border-slate-200 dark:border-slate-800",
|
|
67
|
+
})}
|
|
68
|
+
>
|
|
69
|
+
<ChevronDown size={14} />
|
|
70
|
+
</KNumberField.DecrementTrigger>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<Show when={local.description}>
|
|
75
|
+
<KNumberField.Description class="text-xs text-slate-500 ml-1 mt-1" />
|
|
76
|
+
</Show>
|
|
77
|
+
<KNumberField.ErrorMessage class={s.errorMessage()} />
|
|
78
|
+
</KNumberField>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Pagination as KPagination } from "@kobalte/core/pagination";
|
|
2
|
+
import { splitProps, type ComponentProps } from "solid-js";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
import { ChevronLeft, ChevronRight, Ellipsis } from "lucide-solid";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// FIXME 样式修改,
|
|
8
|
+
|
|
9
|
+
const paginationStyles = tv(
|
|
10
|
+
{
|
|
11
|
+
slots: {
|
|
12
|
+
root: "flex w-full justify-center antialiased",
|
|
13
|
+
itemsContainer: "flex items-center gap-1",
|
|
14
|
+
item: [
|
|
15
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-md text-sm font-medium transition-colors",
|
|
16
|
+
"hover:bg-slate-100 hover:text-slate-900",
|
|
17
|
+
"data-[selected]:bg-slate-900 data-[selected]:text-slate-50",
|
|
18
|
+
"dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:data-[selected]:bg-slate-50 dark:data-[selected]:text-slate-900",
|
|
19
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
20
|
+
],
|
|
21
|
+
ellipsis: "flex h-9 w-9 items-center justify-center text-slate-400",
|
|
22
|
+
trigger:
|
|
23
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-transparent hover:bg-slate-100 dark:border-slate-800 dark:hover:bg-slate-800",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
twMerge: true,
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const s = paginationStyles();
|
|
32
|
+
|
|
33
|
+
export interface PaginationProps extends ComponentProps<typeof KPagination> {}
|
|
34
|
+
|
|
35
|
+
export const Pagination = (props: PaginationProps) => {
|
|
36
|
+
// 显式提取 count 以满足类型约束,同时清理 others
|
|
37
|
+
const [local, others] = splitProps(props, ["class", "count"]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<KPagination
|
|
41
|
+
class={s.root({ class: local.class })}
|
|
42
|
+
count={local.count}
|
|
43
|
+
{...others}
|
|
44
|
+
itemComponent={(p) => (
|
|
45
|
+
<KPagination.Item page={p.page} class={s.item()}>
|
|
46
|
+
{p.page}
|
|
47
|
+
</KPagination.Item>
|
|
48
|
+
)}
|
|
49
|
+
ellipsisComponent={() => (
|
|
50
|
+
<KPagination.Ellipsis class={s.ellipsis()}>
|
|
51
|
+
<Ellipsis size={16} />
|
|
52
|
+
</KPagination.Ellipsis>
|
|
53
|
+
)}
|
|
54
|
+
>
|
|
55
|
+
<div class={s.itemsContainer()}>
|
|
56
|
+
<KPagination.Previous class={s.trigger()}>
|
|
57
|
+
<ChevronLeft size={16} />
|
|
58
|
+
</KPagination.Previous>
|
|
59
|
+
|
|
60
|
+
<KPagination.Items />
|
|
61
|
+
|
|
62
|
+
<KPagination.Next class={s.trigger()}>
|
|
63
|
+
<ChevronRight size={16} />
|
|
64
|
+
</KPagination.Next>
|
|
65
|
+
</div>
|
|
66
|
+
</KPagination>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Popover as KPopover } from "@kobalte/core/popover";
|
|
2
|
+
import { CrossIcon } from "lucide-solid";
|
|
3
|
+
import { splitProps, type ComponentProps, type JSX } from "solid-js";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
// FIXME 与其他的气泡样式不统一的问题
|
|
7
|
+
// Description,而不是内敛。
|
|
8
|
+
// trigger用内部,而其他放在标签属性
|
|
9
|
+
|
|
10
|
+
const popoverStyles = tv(
|
|
11
|
+
{
|
|
12
|
+
slots: {
|
|
13
|
+
content: [
|
|
14
|
+
"z-50 w-72 rounded-md border bg-white p-4 shadow-md outline-none antialiased",
|
|
15
|
+
"dark:bg-slate-950 dark:border-slate-800 dark:text-slate-50",
|
|
16
|
+
"data-[expanded]:animate-in data-[closed]:animate-out",
|
|
17
|
+
],
|
|
18
|
+
arrow: "fill-white stroke-slate-200 dark:fill-slate-950 dark:stroke-slate-800",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
twMerge: true,
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const {content, arrow} = popoverStyles();
|
|
27
|
+
|
|
28
|
+
export interface PopoverProps extends ComponentProps<typeof KPopover> {
|
|
29
|
+
trigger: JSX.Element;
|
|
30
|
+
title: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Popover = (props: PopoverProps) => {
|
|
34
|
+
const [local, others] = splitProps(props, ["trigger", "children", "title"]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<KPopover {...others}>
|
|
38
|
+
<KPopover.Trigger class="inline-flex">
|
|
39
|
+
{local.trigger}
|
|
40
|
+
</KPopover.Trigger>
|
|
41
|
+
|
|
42
|
+
<KPopover.Portal>
|
|
43
|
+
<KPopover.Content class={content()}>
|
|
44
|
+
<KPopover.Arrow class={arrow()} />
|
|
45
|
+
|
|
46
|
+
<div class="flex">
|
|
47
|
+
<KPopover.Title>{local.title}</KPopover.Title>
|
|
48
|
+
<KPopover.CloseButton>
|
|
49
|
+
<CrossIcon />
|
|
50
|
+
</KPopover.CloseButton>
|
|
51
|
+
</div>
|
|
52
|
+
<KPopover.Description>
|
|
53
|
+
{local.children}
|
|
54
|
+
</KPopover.Description>
|
|
55
|
+
</KPopover.Content>
|
|
56
|
+
</KPopover.Portal>
|
|
57
|
+
</KPopover>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Progress as KProgress } from "@kobalte/core/progress";
|
|
2
|
+
import { splitProps, type ComponentProps, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
// FIXME 进度条问题,value 直接占满
|
|
6
|
+
|
|
7
|
+
const progressStyles = tv(
|
|
8
|
+
{
|
|
9
|
+
slots: {
|
|
10
|
+
root: "flex flex-col gap-2 w-full antialiased",
|
|
11
|
+
labelContainer:
|
|
12
|
+
"flex justify-between items-center text-sm font-medium text-slate-700 dark:text-slate-300",
|
|
13
|
+
track: "h-2 w-full rounded-full bg-slate-100 dark:bg-slate-800 overflow-hidden",
|
|
14
|
+
fill: "h-full bg-blue-600 transition-all duration-300 ease-in-out data-[indeterminate]:animate-progress-indeterminate",
|
|
15
|
+
},
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: { track: "h-1" },
|
|
19
|
+
md: { track: "h-2" },
|
|
20
|
+
lg: { track: "h-3" },
|
|
21
|
+
},
|
|
22
|
+
radius: {
|
|
23
|
+
none: { track: "rounded-none", fill: "rounded-none" },
|
|
24
|
+
sm: { track: "rounded-sm", fill: "rounded-sm" },
|
|
25
|
+
md: { track: "rounded-md", fill: "rounded-md" },
|
|
26
|
+
lg: { track: "rounded-lg", fill: "rounded-lg" },
|
|
27
|
+
full: { track: "rounded-full", fill: "rounded-full" },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
size: "md",
|
|
32
|
+
radius: "full",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
twMerge: true,
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
type ProgressVariants = VariantProps<typeof progressStyles>;
|
|
41
|
+
|
|
42
|
+
export interface ProgressProps
|
|
43
|
+
extends Omit<ComponentProps<typeof KProgress>, "children">,
|
|
44
|
+
ProgressVariants {
|
|
45
|
+
label?: string;
|
|
46
|
+
showValue?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
export const Progress = (props: ProgressProps) => {
|
|
51
|
+
const [local, variantProps, others] = splitProps(
|
|
52
|
+
props,
|
|
53
|
+
["label", "showValue", "class"],
|
|
54
|
+
["size", "radius"]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const { root, labelContainer, track, fill } = progressStyles({
|
|
58
|
+
size: variantProps.size,
|
|
59
|
+
radius: variantProps.radius,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<KProgress class={root({ class: local.class })} {...others}>
|
|
64
|
+
<Show when={local.label || local.showValue}>
|
|
65
|
+
<div class={labelContainer()}>
|
|
66
|
+
<Show when={local.label}>
|
|
67
|
+
<KProgress.Label>{local.label}</KProgress.Label>
|
|
68
|
+
</Show>
|
|
69
|
+
<Show when={local.showValue}>
|
|
70
|
+
<KProgress.ValueLabel class="text-xs text-slate-500" />
|
|
71
|
+
</Show>
|
|
72
|
+
</div>
|
|
73
|
+
</Show>
|
|
74
|
+
|
|
75
|
+
<KProgress.Track class={track()}>
|
|
76
|
+
<KProgress.Fill
|
|
77
|
+
class={fill()}
|
|
78
|
+
style={{ width: "var(--kb-progress-fill-width)" }}
|
|
79
|
+
/>
|
|
80
|
+
</KProgress.Track>
|
|
81
|
+
</KProgress>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { RadioGroup as KRadioGroup } from "@kobalte/core/radio-group";
|
|
2
|
+
import { splitProps, type ComponentProps, For, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const radioStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
slots: {
|
|
8
|
+
root: "flex flex-col gap-3 antialiased",
|
|
9
|
+
label: "text-sm font-semibold text-slate-900 dark:text-slate-100 mb-1",
|
|
10
|
+
item: "group flex items-center gap-3 cursor-pointer disabled:cursor-not-allowed",
|
|
11
|
+
control: [
|
|
12
|
+
"flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-slate-300 bg-white transition-all shadow-sm",
|
|
13
|
+
"group-hover:border-slate-400 group-data-[checked]:border-blue-600 group-data-[checked]:bg-blue-600",
|
|
14
|
+
"group-focus-visible:ring-2 group-focus-visible:ring-blue-500/20",
|
|
15
|
+
"dark:bg-slate-950 dark:border-slate-700 dark:group-data-[checked]:bg-blue-600 dark:group-data-[checked]:border-blue-600",
|
|
16
|
+
],
|
|
17
|
+
indicator: "h-2 w-2 rounded-full bg-white shadow-sm",
|
|
18
|
+
itemLabel:
|
|
19
|
+
"text-sm font-medium text-slate-700 dark:text-slate-300 group-data-[disabled]:opacity-50",
|
|
20
|
+
},
|
|
21
|
+
variants: {
|
|
22
|
+
orientation: {
|
|
23
|
+
horizontal: { root: "flex-row flex-wrap gap-6" },
|
|
24
|
+
vertical: { root: "flex-col" },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
orientation: "vertical",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
twMerge: true,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
type RadioVariants = VariantProps<typeof radioStyles>;
|
|
37
|
+
|
|
38
|
+
export interface RadioOption {
|
|
39
|
+
label: string;
|
|
40
|
+
value: string;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RadioGroupProps
|
|
45
|
+
extends Omit<ComponentProps<typeof KRadioGroup>, "children" | "class">,
|
|
46
|
+
RadioVariants {
|
|
47
|
+
label?: string;
|
|
48
|
+
options: RadioOption[];
|
|
49
|
+
class?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* RadioGroup 高度封装版
|
|
54
|
+
* 自动处理循环渲染、选中指示器以及水平/垂直布局
|
|
55
|
+
*/
|
|
56
|
+
export const RadioGroup = (props: RadioGroupProps) => {
|
|
57
|
+
const [local, variantProps, others] = splitProps(
|
|
58
|
+
props,
|
|
59
|
+
["label", "options", "class"],
|
|
60
|
+
["orientation"]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const s = () => radioStyles({ orientation: variantProps.orientation });
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<KRadioGroup class={s().root({ class: local.class })} {...others}>
|
|
67
|
+
<Show when={local.label}>
|
|
68
|
+
<KRadioGroup.Label class={s().label()}>
|
|
69
|
+
{local.label}
|
|
70
|
+
</KRadioGroup.Label>
|
|
71
|
+
</Show>
|
|
72
|
+
|
|
73
|
+
<For each={local.options}>
|
|
74
|
+
{(option) => (
|
|
75
|
+
<KRadioGroup.Item
|
|
76
|
+
value={option.value}
|
|
77
|
+
disabled={option.disabled}
|
|
78
|
+
class={s().item()}
|
|
79
|
+
>
|
|
80
|
+
<KRadioGroup.ItemInput />
|
|
81
|
+
<KRadioGroup.ItemControl class={s().control()}>
|
|
82
|
+
<KRadioGroup.ItemIndicator
|
|
83
|
+
class={s().indicator()}
|
|
84
|
+
/>
|
|
85
|
+
</KRadioGroup.ItemControl>
|
|
86
|
+
<KRadioGroup.ItemLabel class={s().itemLabel()}>
|
|
87
|
+
{option.label}
|
|
88
|
+
</KRadioGroup.ItemLabel>
|
|
89
|
+
</KRadioGroup.Item>
|
|
90
|
+
)}
|
|
91
|
+
</For>
|
|
92
|
+
</KRadioGroup>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// import { RatingGroup as KRatingGroup } from "@kobalte/core/rating-group";
|
|
2
|
+
// import { splitProps, type ComponentProps, For, Show } from "solid-js";
|
|
3
|
+
// import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
// import { Star } from "lucide-solid";
|
|
5
|
+
|
|
6
|
+
// const ratingStyles = tv({
|
|
7
|
+
// slots: {
|
|
8
|
+
// root: "flex flex-col gap-1.5 antialiased",
|
|
9
|
+
// label: "text-sm font-medium text-slate-700 dark:text-slate-300",
|
|
10
|
+
// control: "flex items-center gap-0.5",
|
|
11
|
+
// item: "relative cursor-pointer transition-transform active:scale-90 focus-visible:outline-none",
|
|
12
|
+
// itemIndicator: "flex items-center justify-center transition-colors",
|
|
13
|
+
// },
|
|
14
|
+
// variants: {
|
|
15
|
+
// color: {
|
|
16
|
+
// yellow: {
|
|
17
|
+
// itemIndicator:
|
|
18
|
+
// "text-amber-400 fill-transparent data-[highlighted]:fill-amber-400 data-[highlighted]:text-amber-400",
|
|
19
|
+
// },
|
|
20
|
+
// blue: {
|
|
21
|
+
// itemIndicator:
|
|
22
|
+
// "text-blue-500 fill-transparent data-[highlighted]:fill-blue-500 data-[highlighted]:text-blue-500",
|
|
23
|
+
// },
|
|
24
|
+
// red: {
|
|
25
|
+
// itemIndicator:
|
|
26
|
+
// "text-red-500 fill-transparent data-[highlighted]:fill-red-500 data-[highlighted]:text-red-500",
|
|
27
|
+
// },
|
|
28
|
+
// },
|
|
29
|
+
// size: {
|
|
30
|
+
// sm: { itemIndicator: "h-4 w-4" },
|
|
31
|
+
// md: { itemIndicator: "h-6 w-6" },
|
|
32
|
+
// lg: { itemIndicator: "h-8 w-8" },
|
|
33
|
+
// },
|
|
34
|
+
// },
|
|
35
|
+
// defaultVariants: {
|
|
36
|
+
// color: "yellow",
|
|
37
|
+
// size: "md",
|
|
38
|
+
// },
|
|
39
|
+
// });
|
|
40
|
+
|
|
41
|
+
// type RatingVariants = VariantProps<typeof ratingStyles>;
|
|
42
|
+
|
|
43
|
+
// export interface RatingGroupProps
|
|
44
|
+
// extends Omit<ComponentProps<typeof KRatingGroup>, "children" | "class">,
|
|
45
|
+
// RatingVariants {
|
|
46
|
+
// label?: string;
|
|
47
|
+
// count?: number; // 星星总数,默认为 5
|
|
48
|
+
// class?: string;
|
|
49
|
+
// }
|
|
50
|
+
|
|
51
|
+
// /**
|
|
52
|
+
// * RatingGroup 高度封装版
|
|
53
|
+
// * 自动处理星星循环、高亮逻辑及表单集成
|
|
54
|
+
// */
|
|
55
|
+
// export const RatingGroup = (props: RatingGroupProps) => {
|
|
56
|
+
// const [local, variantProps, others] = splitProps(
|
|
57
|
+
// props,
|
|
58
|
+
// ["label", "count", "class"],
|
|
59
|
+
// ["color", "size"]
|
|
60
|
+
// );
|
|
61
|
+
|
|
62
|
+
// const s = () =>
|
|
63
|
+
// ratingStyles({
|
|
64
|
+
// color: variantProps.color,
|
|
65
|
+
// size: variantProps.size,
|
|
66
|
+
// });
|
|
67
|
+
|
|
68
|
+
// const maxCount = () => local.count ?? 5;
|
|
69
|
+
|
|
70
|
+
// return (
|
|
71
|
+
// <KProgress class={s().root({ class: local.class })} {...others}>
|
|
72
|
+
// <Show when={local.label}>
|
|
73
|
+
// <KRatingGroup.Label class={s().label()}>
|
|
74
|
+
// {local.label}
|
|
75
|
+
// </KRatingGroup.Label>
|
|
76
|
+
// </Show>
|
|
77
|
+
|
|
78
|
+
// <KRatingGroup.Control class={s().control()}>
|
|
79
|
+
// <KRatingGroup.HiddenInput />
|
|
80
|
+
// <KRatingGroup.Context>
|
|
81
|
+
// {(state) => (
|
|
82
|
+
// <For each={state.items}>
|
|
83
|
+
// {(index) => (
|
|
84
|
+
// <KRatingGroup.Item
|
|
85
|
+
// index={index}
|
|
86
|
+
// class={s().item()}
|
|
87
|
+
// >
|
|
88
|
+
// <KRatingGroup.ItemIndicator
|
|
89
|
+
// class={s().itemIndicator()}
|
|
90
|
+
// >
|
|
91
|
+
// <Star />
|
|
92
|
+
// </KRatingGroup.ItemIndicator>
|
|
93
|
+
// </KRatingGroup.Item>
|
|
94
|
+
// )}
|
|
95
|
+
// </For>
|
|
96
|
+
// )}
|
|
97
|
+
// </KRatingGroup.Context>
|
|
98
|
+
// </KRatingGroup.Control>
|
|
99
|
+
// </KProgress>
|
|
100
|
+
// );
|
|
101
|
+
// };
|