solid-element-ui 0.2.4 → 0.2.6
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/{src/alert-dialog → alert-dialog}/alert-dialog.d.ts +1 -1
- package/dist/{src/badge → badge}/badge.d.ts +2 -2
- package/dist/index.css +3 -1
- package/dist/index.js +16097 -17258
- package/dist/{src/separator → separator}/separator.d.ts +11 -5
- package/dist/{src/skeleton → skeleton}/skeleton.d.ts +2 -2
- package/dist/{src/slider → slider}/slider.d.ts +12 -0
- package/dist/{src/toast → toast}/toast.d.ts +3 -1
- package/dist/{src/toggle-button → toggle-button}/toggle-button.d.ts +0 -3
- package/package.json +11 -7
- package/src/accordion/accordion.tsx +80 -0
- package/src/alert/alert.tsx +86 -0
- package/src/alert-dialog/alert-dialog.tsx +129 -0
- package/src/badge/badge.tsx +49 -0
- package/src/breadcrumbs/breadcrumbs.tsx +69 -0
- package/src/button/button.tsx +216 -0
- package/src/checkbox/checkbox.tsx +63 -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 +101 -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 +61 -0
- package/src/image/image.tsx +59 -0
- package/src/index.tsx +94 -0
- package/src/link/link.tsx +64 -0
- package/src/menubar/menubar.tsx +85 -0
- package/src/meter/meter.tsx +89 -0
- package/src/navigation-menu/navigation-menu.tsx +90 -0
- package/src/number-field/number-field.tsx +79 -0
- package/src/pagination/pagination.tsx +67 -0
- package/src/popover/popover.tsx +58 -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 +163 -0
- package/src/separator/separator.tsx +64 -0
- package/src/skeleton/skeleton.tsx +73 -0
- package/src/slider/slider.tsx +94 -0
- package/src/style/global.css +156 -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 +132 -0
- package/src/toggle-button/toggle-button.tsx +69 -0
- package/src/toggle-group/toggle-group.tsx +85 -0
- package/src/tooltip/tooltip.tsx +73 -0
- /package/dist/{src/accordion → accordion}/accordion.d.ts +0 -0
- /package/dist/{src/alert → alert}/alert.d.ts +0 -0
- /package/dist/{src/breadcrumbs → breadcrumbs}/breadcrumbs.d.ts +0 -0
- /package/dist/{src/button → button}/button.d.ts +0 -0
- /package/dist/{src/checkbox → checkbox}/checkbox.d.ts +0 -0
- /package/dist/{src/collapsible → collapsible}/collapsible.d.ts +0 -0
- /package/dist/{src/color-area → color-area}/color-area.d.ts +0 -0
- /package/dist/{src/color-channel-field → color-channel-field}/color-channel-field.d.ts +0 -0
- /package/dist/{src/color-field → color-field}/color-field.d.ts +0 -0
- /package/dist/{src/color-slider → color-slider}/color-slider.d.ts +0 -0
- /package/dist/{src/color-swatch → color-swatch}/color-swatch.d.ts +0 -0
- /package/dist/{src/color-wheel → color-wheel}/color-wheel.d.ts +0 -0
- /package/dist/{src/combobox → combobox}/combobox.d.ts +0 -0
- /package/dist/{src/context-menu → context-menu}/context-menu.d.ts +0 -0
- /package/dist/{src/dialog → dialog}/dialog.d.ts +0 -0
- /package/dist/{src/dropdown-menu → dropdown-menu}/dropdown-menu.d.ts +0 -0
- /package/dist/{src/file-field → file-field}/file-field.d.ts +0 -0
- /package/dist/{src/hover-card → hover-card}/hover-card.d.ts +0 -0
- /package/dist/{src/image → image}/image.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{src/link → link}/link.d.ts +0 -0
- /package/dist/{src/menubar → menubar}/menubar.d.ts +0 -0
- /package/dist/{src/meter → meter}/meter.d.ts +0 -0
- /package/dist/{src/navigation-menu → navigation-menu}/navigation-menu.d.ts +0 -0
- /package/dist/{src/number-field → number-field}/number-field.d.ts +0 -0
- /package/dist/{src/pagination → pagination}/pagination.d.ts +0 -0
- /package/dist/{src/popover → popover}/popover.d.ts +0 -0
- /package/dist/{src/progress → progress}/progress.d.ts +0 -0
- /package/dist/{src/radio-group → radio-group}/radio-group.d.ts +0 -0
- /package/dist/{src/rating-group → rating-group}/rating-group.d.ts +0 -0
- /package/dist/{src/search → search}/search.d.ts +0 -0
- /package/dist/{src/segmented-control → segmented-control}/segmented-control.d.ts +0 -0
- /package/dist/{src/select → select}/select.d.ts +0 -0
- /package/dist/{src/switch → switch}/switch.d.ts +0 -0
- /package/dist/{src/tabs → tabs}/tabs.d.ts +0 -0
- /package/dist/{src/text-field → text-field}/text-field.d.ts +0 -0
- /package/dist/{src/time-field → time-field}/time-field.d.ts +0 -0
- /package/dist/{src/toggle-group → toggle-group}/toggle-group.d.ts +0 -0
- /package/dist/{src/tooltip → tooltip}/tooltip.d.ts +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Separator as KSeparator } from "@kobalte/core/separator";
|
|
2
|
+
import { splitProps, type ComponentProps } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const separatorStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
base: "bg-foreground shrink-0 transition-colors",
|
|
8
|
+
variants: {
|
|
9
|
+
orientation: {
|
|
10
|
+
horizontal: "h-[1px] w-full my-4",
|
|
11
|
+
vertical: "h-full w-[1px] mx-4",
|
|
12
|
+
},
|
|
13
|
+
thickness: {
|
|
14
|
+
thin: "", // 默认 1px
|
|
15
|
+
medium: "data-[orientation=horizontal]:h-[2px] data-[orientation=vertical]:w-[2px]",
|
|
16
|
+
thick: "data-[orientation=horizontal]:h-[4px] data-[orientation=vertical]:w-[4px] rounded-full",
|
|
17
|
+
},
|
|
18
|
+
variant: {
|
|
19
|
+
default: "bg-foreground",
|
|
20
|
+
primary: "bg-primary",
|
|
21
|
+
success: "bg-success",
|
|
22
|
+
warning: "bg-warning",
|
|
23
|
+
danger: "bg-danger",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
orientation: "horizontal",
|
|
28
|
+
thickness: "thin",
|
|
29
|
+
variant: "default",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
twMerge: true,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
type SeparatorVariants = VariantProps<typeof separatorStyles>;
|
|
38
|
+
|
|
39
|
+
export interface SeparatorProps
|
|
40
|
+
extends ComponentProps<typeof KSeparator>,
|
|
41
|
+
SeparatorVariants {
|
|
42
|
+
class?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const Separator = (props: SeparatorProps) => {
|
|
46
|
+
const [local, variantProps, others] = splitProps(
|
|
47
|
+
props,
|
|
48
|
+
["class"],
|
|
49
|
+
["orientation", "thickness", "variant"]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<KSeparator
|
|
54
|
+
class={separatorStyles({
|
|
55
|
+
orientation: variantProps.orientation,
|
|
56
|
+
thickness: variantProps.thickness,
|
|
57
|
+
variant: variantProps.variant,
|
|
58
|
+
class: local.class,
|
|
59
|
+
})}
|
|
60
|
+
orientation={variantProps.orientation}
|
|
61
|
+
{...others}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Skeleton as KSkeleton } from "@kobalte/core/skeleton";
|
|
2
|
+
import { splitProps, type ComponentProps } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const skeletonStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
base: "bg-foreground",
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
rect: "rounded-md",
|
|
11
|
+
circle: "rounded-full",
|
|
12
|
+
text: "rounded h-3 w-full",
|
|
13
|
+
},
|
|
14
|
+
animation: {
|
|
15
|
+
pulse: "animate-pulse",
|
|
16
|
+
wave: "relative overflow-hidden before:absolute before:inset-0 before:-translate-x-full before:animate-[wave_2s_linear_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/20 before:to-transparent",
|
|
17
|
+
none: "",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: "rect",
|
|
22
|
+
animation: "pulse",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
twMerge: true,
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
type SkeletonVariants = VariantProps<typeof skeletonStyles>;
|
|
31
|
+
|
|
32
|
+
// 核心修正:使用 Omit 排除掉冲突的 width 和 height
|
|
33
|
+
export interface SkeletonProps
|
|
34
|
+
extends Omit<
|
|
35
|
+
ComponentProps<typeof KSkeleton>,
|
|
36
|
+
"class" | "width" | "height"
|
|
37
|
+
>,
|
|
38
|
+
SkeletonVariants {
|
|
39
|
+
width?: string | number;
|
|
40
|
+
height?: string | number;
|
|
41
|
+
class?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Skeleton = (props: SkeletonProps) => {
|
|
45
|
+
// 显式提取这些属性,避免传给 KSkeleton 引起类型或运行时错误
|
|
46
|
+
const [local, variantProps, others] = splitProps(
|
|
47
|
+
props,
|
|
48
|
+
["class", "width", "height", "style"],
|
|
49
|
+
["variant", "animation"]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const mergedStyle = () => ({
|
|
53
|
+
width:
|
|
54
|
+
typeof local.width === "number" ? `${local.width}px` : local.width,
|
|
55
|
+
height:
|
|
56
|
+
typeof local.height === "number"
|
|
57
|
+
? `${local.height}px`
|
|
58
|
+
: local.height,
|
|
59
|
+
...(typeof local.style === "object" ? local.style : {}),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<KSkeleton
|
|
64
|
+
class={skeletonStyles({
|
|
65
|
+
variant: variantProps.variant,
|
|
66
|
+
animation: variantProps.animation,
|
|
67
|
+
class: local.class,
|
|
68
|
+
})}
|
|
69
|
+
style={mergedStyle()}
|
|
70
|
+
{...others}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Slider as KSlider } from "@kobalte/core/slider";
|
|
2
|
+
import { splitProps, type ComponentProps, For, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
// FIXME 点击轨道时报错
|
|
6
|
+
|
|
7
|
+
const sliderStyles = tv(
|
|
8
|
+
{
|
|
9
|
+
slots: {
|
|
10
|
+
root: "relative flex flex-col items-center select-none touch-none w-full gap-2",
|
|
11
|
+
labelWrapper: "flex w-full justify-between items-center",
|
|
12
|
+
label: "text-sm font-medium text-muted",
|
|
13
|
+
value: "text-sm text-muted font-mono",
|
|
14
|
+
track: "relative h-2 w-full grow rounded-full bg-foreground",
|
|
15
|
+
fill: "absolute h-full rounded-full ",
|
|
16
|
+
thumb: [
|
|
17
|
+
"block h-5 w-5 rounded-full border-2 bg-app ring-offset-white transition-colors",
|
|
18
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-2",
|
|
19
|
+
"disabled:pointer-events-none disabled:opacity-50 hover:scale-110 active:scale-95 transition-transform",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
default: { fill: "bg-primary", thumb: "border-primary" },
|
|
25
|
+
danger: { fill: "bg-danger", thumb: "border-danger" },
|
|
26
|
+
warning: { fill: "bg-warning", thumb: "border-warning" },
|
|
27
|
+
success: {
|
|
28
|
+
fill: "bg-success",
|
|
29
|
+
thumb: "border-success",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
size: {
|
|
33
|
+
sm: { track: "h-1", thumb: "h-4 w-4" },
|
|
34
|
+
md: { track: "h-2", thumb: "h-5 w-5" },
|
|
35
|
+
lg: { track: "h-3", thumb: "h-6 w-6" },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default",
|
|
40
|
+
size: "md",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
twMerge: true,
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
type SliderVariants = VariantProps<typeof sliderStyles>;
|
|
49
|
+
|
|
50
|
+
export interface SliderProps
|
|
51
|
+
extends Omit<ComponentProps<typeof KSlider>, "class">,
|
|
52
|
+
SliderVariants {
|
|
53
|
+
label?: string;
|
|
54
|
+
showValue?: boolean;
|
|
55
|
+
class?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const Slider = (props: SliderProps) => {
|
|
59
|
+
const [local, variantProps, others] = splitProps(
|
|
60
|
+
props,
|
|
61
|
+
["label", "showValue", "class"],
|
|
62
|
+
["variant", "size"]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const styles = sliderStyles(variantProps);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<KSlider class={styles.root({ class: local.class })} {...others}>
|
|
69
|
+
<Show when={local.label || local.showValue}>
|
|
70
|
+
<div class={styles.labelWrapper()}>
|
|
71
|
+
<Show when={local.label}>
|
|
72
|
+
<KSlider.Label class={styles.label()}>
|
|
73
|
+
{local.label}
|
|
74
|
+
</KSlider.Label>
|
|
75
|
+
</Show>
|
|
76
|
+
<Show when={local.showValue}>
|
|
77
|
+
<KSlider.ValueLabel class={styles.value()} />
|
|
78
|
+
</Show>
|
|
79
|
+
</div>
|
|
80
|
+
</Show>
|
|
81
|
+
|
|
82
|
+
<KSlider.Track class={styles.track()}>
|
|
83
|
+
<KSlider.Fill class={styles.fill()} />
|
|
84
|
+
<For each={others.value ?? [others.defaultValue]}>
|
|
85
|
+
{() => (
|
|
86
|
+
<KSlider.Thumb class={styles.thumb()}>
|
|
87
|
+
<KSlider.Input />
|
|
88
|
+
</KSlider.Thumb>
|
|
89
|
+
)}
|
|
90
|
+
</For>
|
|
91
|
+
</KSlider.Track>
|
|
92
|
+
</KSlider>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@layer theme {
|
|
4
|
+
:root {
|
|
5
|
+
--primary: var(--color-blue-600);
|
|
6
|
+
--success: var(--color-green-600);
|
|
7
|
+
--warning: var(--color-orange-600);
|
|
8
|
+
--danger: var(--color-red-600);
|
|
9
|
+
|
|
10
|
+
--app-bg: var(--color-white); /* 应用背景色 */
|
|
11
|
+
--app-bg-reversal: var(--color-zinc-900); /* 应用反转背景色 */
|
|
12
|
+
--app-foreground: var(--color-zinc-100); /* 应用前景色 */
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
--app-text-main: var(--color-zinc-950); /* 应用主文字色 */
|
|
16
|
+
--app-text-muted: var(--color-zinc-600); /* 应用次级文字色*/
|
|
17
|
+
--app-border: var(--color-zinc-500); /* 应用边框色*/
|
|
18
|
+
--app-border-light: var(--color-zinc-300); /* 应用浅边框色 */
|
|
19
|
+
--app-ring: var(--color-zinc-300);
|
|
20
|
+
--app-text-reversal: var(--color-zinc-100); /* 应用反转文字色 */
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[data-theme="dark"] {
|
|
24
|
+
--app-text-reversal: var(--color-zinc-950);
|
|
25
|
+
--app-bg-reversal: var(--color-zinc-50);
|
|
26
|
+
--app-bg: var(--color-zinc-950);
|
|
27
|
+
--app-text-main: var(--color-zinc-50);
|
|
28
|
+
--app-text-muted: var(--color-zinc-400);
|
|
29
|
+
--app-ring: var(--color-zinc-600);
|
|
30
|
+
--app-foreground: var(--color-zinc-900);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
[data-theme="coffee"] {
|
|
34
|
+
--app-bg: #2c2420;
|
|
35
|
+
--app-text-main: #f3e5d8;
|
|
36
|
+
--app-ring: #8b5e3c;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@keyframes accordion-down {
|
|
40
|
+
from {
|
|
41
|
+
height: 0;
|
|
42
|
+
}
|
|
43
|
+
to {
|
|
44
|
+
height: var(--kb-accordion-content-height);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@keyframes accordion-up {
|
|
49
|
+
from {
|
|
50
|
+
height: var(--kb-accordion-content-height);
|
|
51
|
+
}
|
|
52
|
+
to {
|
|
53
|
+
height: 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@keyframes fade-in {
|
|
58
|
+
from {
|
|
59
|
+
opacity: 0;
|
|
60
|
+
}
|
|
61
|
+
to {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@keyframes fade-out {
|
|
67
|
+
from {
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
to {
|
|
71
|
+
opacity: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@keyframes collapsible-down {
|
|
76
|
+
from {
|
|
77
|
+
height: 0;
|
|
78
|
+
}
|
|
79
|
+
to {
|
|
80
|
+
height: var(--kb-collapsible-content-height);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@keyframes collapsible-up {
|
|
85
|
+
from {
|
|
86
|
+
height: var(--kb-collapsible-content-height);
|
|
87
|
+
}
|
|
88
|
+
to {
|
|
89
|
+
height: 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@keyframes slide-in {
|
|
94
|
+
from {
|
|
95
|
+
transform: translateX(calc(100% + var(--viewport-padding)));
|
|
96
|
+
}
|
|
97
|
+
to {
|
|
98
|
+
transform: translateX(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@keyframes hide {
|
|
103
|
+
from {
|
|
104
|
+
opacity: 1;
|
|
105
|
+
}
|
|
106
|
+
to {
|
|
107
|
+
opacity: 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@keyframes swipe-out {
|
|
112
|
+
from {
|
|
113
|
+
transform: translateX(var(--kb-toast-swipe-end-x));
|
|
114
|
+
}
|
|
115
|
+
to {
|
|
116
|
+
transform: translateX(calc(100% + var(--viewport-padding)));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* --- 2. 注册到 Tailwind 主题系统 --- */
|
|
122
|
+
@theme {
|
|
123
|
+
--color-primary: var(--primary);
|
|
124
|
+
--color-success: var(--success);
|
|
125
|
+
--color-warning: var(--warning);
|
|
126
|
+
--color-danger: var(--danger);
|
|
127
|
+
|
|
128
|
+
--color-app: var(--app-bg);
|
|
129
|
+
--color-foreground: var(--app-foreground);
|
|
130
|
+
--color-reversal-bg: var(--app-bg-reversal);
|
|
131
|
+
--color-main: var(--app-text-main);
|
|
132
|
+
--color-muted: var(--app-text-muted);
|
|
133
|
+
--color-reversal: var(--app-text-reversal);
|
|
134
|
+
--color-base: var(--app-border);
|
|
135
|
+
--color-light: var(--app-border-light);
|
|
136
|
+
--radius-app: 0.75rem;
|
|
137
|
+
|
|
138
|
+
/* 注册动画到主题 */
|
|
139
|
+
--animate-accordion-down: accordion-down 0.2s ease-out;
|
|
140
|
+
--animate-accordion-up: accordion-up 0.2s ease-out;
|
|
141
|
+
|
|
142
|
+
--animate-in: fade-in 0.2s ease-in;
|
|
143
|
+
--animate-out: fade-out 0.2s ease-out forwards;
|
|
144
|
+
|
|
145
|
+
--animate-collapsible-down: collapsible-down 0.2s ease-out;
|
|
146
|
+
--animate-collapsible-up: collapsible-up 0.2s ease-out;
|
|
147
|
+
|
|
148
|
+
--animate-slide-in: slide-in 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
149
|
+
--animate-hide: hide 100ms ease-in;
|
|
150
|
+
--animate-swipe-out: swipe-out 100ms ease-out;
|
|
151
|
+
|
|
152
|
+
--viewport-padding: 16px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* --- 3. 暗色主题变体 --- */
|
|
156
|
+
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Switch as KSwitch } from "@kobalte/core/switch";
|
|
2
|
+
import { splitProps, type ComponentProps, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const switchStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
slots: {
|
|
8
|
+
root: "inline-flex items-center gap-2 group",
|
|
9
|
+
control: [
|
|
10
|
+
"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
|
|
11
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-primary focus-visible:ring-offset-2",
|
|
12
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
13
|
+
"bg-foreground data-[checked]:bg-primary",
|
|
14
|
+
],
|
|
15
|
+
thumb: [
|
|
16
|
+
"pointer-events-none block h-5 w-5 rounded-full bg-app shadow-lg ring-0 transition-transform",
|
|
17
|
+
"data-[checked]:translate-x-5 translate-x-0",
|
|
18
|
+
],
|
|
19
|
+
label: "text-sm font-medium leading-none group-data-[disabled]:opacity-70",
|
|
20
|
+
description: "text-xs text-muted",
|
|
21
|
+
},
|
|
22
|
+
variants: {
|
|
23
|
+
size: {
|
|
24
|
+
sm: {
|
|
25
|
+
control: "h-5 w-9",
|
|
26
|
+
thumb: "h-4 w-4 data-[checked]:translate-x-4",
|
|
27
|
+
},
|
|
28
|
+
md: {
|
|
29
|
+
control: "h-6 w-11",
|
|
30
|
+
thumb: "h-5 w-5 data-[checked]:translate-x-5",
|
|
31
|
+
},
|
|
32
|
+
lg: {
|
|
33
|
+
control: "h-7 w-13",
|
|
34
|
+
thumb: "h-6 w-6 data-[checked]:translate-x-6",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
variant: {
|
|
38
|
+
primary: {
|
|
39
|
+
control:
|
|
40
|
+
"data-[checked]:bg-primary",
|
|
41
|
+
},
|
|
42
|
+
success: {
|
|
43
|
+
control:
|
|
44
|
+
"data-[checked]:bg-success",
|
|
45
|
+
},
|
|
46
|
+
danger: {
|
|
47
|
+
control:
|
|
48
|
+
"data-[checked]:bg-danger",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
size: "md",
|
|
54
|
+
variant: "primary",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
twMerge: true,
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
type SwitchVariants = VariantProps<typeof switchStyles>;
|
|
63
|
+
|
|
64
|
+
export interface SwitchProps
|
|
65
|
+
extends Omit<ComponentProps<typeof KSwitch>, "class">,
|
|
66
|
+
SwitchVariants {
|
|
67
|
+
label?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
class?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const Switch = (props: SwitchProps) => {
|
|
73
|
+
const [local, variantProps, others] = splitProps(
|
|
74
|
+
props,
|
|
75
|
+
["label", "description", "class"],
|
|
76
|
+
["size", "variant"]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const styles = switchStyles(variantProps);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<KSwitch class={styles.root({ class: local.class })} {...others}>
|
|
83
|
+
<KSwitch.Input />
|
|
84
|
+
<KSwitch.Control class={styles.control()}>
|
|
85
|
+
<KSwitch.Thumb class={styles.thumb()} />
|
|
86
|
+
</KSwitch.Control>
|
|
87
|
+
|
|
88
|
+
<Show when={local.label || local.description}>
|
|
89
|
+
<div class="flex flex-col gap-0.5">
|
|
90
|
+
<Show when={local.label}>
|
|
91
|
+
<KSwitch.Label class={styles.label()}>
|
|
92
|
+
{local.label}
|
|
93
|
+
</KSwitch.Label>
|
|
94
|
+
</Show>
|
|
95
|
+
<Show when={local.description}>
|
|
96
|
+
<KSwitch.Description class={styles.description()}>
|
|
97
|
+
{local.description}
|
|
98
|
+
</KSwitch.Description>
|
|
99
|
+
</Show>
|
|
100
|
+
</div>
|
|
101
|
+
</Show>
|
|
102
|
+
</KSwitch>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Tabs as KTabs } from "@kobalte/core/tabs";
|
|
2
|
+
import { splitProps, type JSX, For } from "solid-js";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const tabsStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
slots: {
|
|
8
|
+
root: "flex flex-col w-full",
|
|
9
|
+
list: "relative flex items-center border-b border-base",
|
|
10
|
+
trigger: [
|
|
11
|
+
"relative flex h-9 items-center justify-center px-4 text-sm font-medium transition-colors outline-none select-none cursor-pointer",
|
|
12
|
+
"text-muted hover:text-muted/80 ",
|
|
13
|
+
"data-[selected]:text-main",
|
|
14
|
+
],
|
|
15
|
+
indicator:
|
|
16
|
+
"absolute bottom-[-1px] h-0.5 bg-reversal-bg transition-all duration-200",
|
|
17
|
+
content:
|
|
18
|
+
"mt-4 text-sm text-main focus-visible:outline-none",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
twMerge: true,
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const { root, list, trigger, indicator, content } = tabsStyles();
|
|
27
|
+
|
|
28
|
+
export type TabItem = {
|
|
29
|
+
value: string;
|
|
30
|
+
label: string | JSX.Element;
|
|
31
|
+
content: JSX.Element;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
interface TabsProps {
|
|
36
|
+
items: TabItem[];
|
|
37
|
+
defaultValue?: string;
|
|
38
|
+
value?: string;
|
|
39
|
+
onValueChange?: (value: string) => void;
|
|
40
|
+
orientation?: "horizontal" | "vertical";
|
|
41
|
+
class?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Tabs = (props: TabsProps) => {
|
|
45
|
+
const [local, others] = splitProps(props, ["items", "class"]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<KTabs class={root({ class: local.class })} {...others}>
|
|
49
|
+
<KTabs.List class={list()}>
|
|
50
|
+
<For each={local.items}>
|
|
51
|
+
{(item) => (
|
|
52
|
+
<KTabs.Trigger
|
|
53
|
+
class={trigger()}
|
|
54
|
+
value={item.value}
|
|
55
|
+
disabled={item.disabled}
|
|
56
|
+
>
|
|
57
|
+
{item.label}
|
|
58
|
+
</KTabs.Trigger>
|
|
59
|
+
)}
|
|
60
|
+
</For>
|
|
61
|
+
<KTabs.Indicator class={indicator()} />
|
|
62
|
+
</KTabs.List>
|
|
63
|
+
|
|
64
|
+
<For each={local.items}>
|
|
65
|
+
{(item) => (
|
|
66
|
+
<KTabs.Content class={content()} value={item.value}>
|
|
67
|
+
{item.content}
|
|
68
|
+
</KTabs.Content>
|
|
69
|
+
)}
|
|
70
|
+
</For>
|
|
71
|
+
</KTabs>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { TextField as KTextField } from "@kobalte/core/text-field";
|
|
2
|
+
import { splitProps, type ComponentProps, Show } from "solid-js";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
const textFieldStyles = tv(
|
|
6
|
+
{
|
|
7
|
+
slots: {
|
|
8
|
+
root: "flex flex-col gap-1.5 w-full",
|
|
9
|
+
label: "text-sm font-medium text-muted peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
10
|
+
input: [
|
|
11
|
+
"flex h-10 w-full rounded-md border border-light bg-app px-3 py-2 text-sm transition-shadow text-main",
|
|
12
|
+
"ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
|
13
|
+
"placeholder:text-muted/40 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary",
|
|
14
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
15
|
+
"data-[invalid]:border-danger data-[invalid]:focus-visible:ring-danger",
|
|
16
|
+
],
|
|
17
|
+
description: "text-xs text-muted",
|
|
18
|
+
errorMessage:
|
|
19
|
+
"text-xs text-danger animate-in fade-in-50 slide-in-from-top-1",
|
|
20
|
+
},
|
|
21
|
+
variants: {
|
|
22
|
+
size: {
|
|
23
|
+
sm: { input: "h-8 px-2 text-xs" },
|
|
24
|
+
md: { input: "h-10 px-3 text-sm" },
|
|
25
|
+
lg: { input: "h-12 px-4 text-base" },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
size: "md",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
twMerge: true,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
type TextFieldVariants = VariantProps<typeof textFieldStyles>;
|
|
38
|
+
|
|
39
|
+
export interface TextFieldProps
|
|
40
|
+
extends Omit<ComponentProps<typeof KTextField>, "class">,
|
|
41
|
+
TextFieldVariants {
|
|
42
|
+
label?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
errorMessage?: string;
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
type?: string;
|
|
47
|
+
class?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const TextField = (props: TextFieldProps) => {
|
|
51
|
+
const [local, variantProps, others] = splitProps(
|
|
52
|
+
props,
|
|
53
|
+
[
|
|
54
|
+
"label",
|
|
55
|
+
"description",
|
|
56
|
+
"errorMessage",
|
|
57
|
+
"placeholder",
|
|
58
|
+
"type",
|
|
59
|
+
"class",
|
|
60
|
+
],
|
|
61
|
+
["size"]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const styles = textFieldStyles(variantProps);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<KTextField
|
|
68
|
+
class={styles.root({ class: local.class })}
|
|
69
|
+
validationState={local.errorMessage ? "invalid" : "valid"}
|
|
70
|
+
{...others}
|
|
71
|
+
>
|
|
72
|
+
<Show when={local.label}>
|
|
73
|
+
<KTextField.Label class={styles.label()}>
|
|
74
|
+
{local.label}
|
|
75
|
+
</KTextField.Label>
|
|
76
|
+
</Show>
|
|
77
|
+
|
|
78
|
+
<KTextField.Input
|
|
79
|
+
class={styles.input()}
|
|
80
|
+
type={local.type}
|
|
81
|
+
placeholder={local.placeholder}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<Show when={local.description}>
|
|
85
|
+
<KTextField.Description class={styles.description()}>
|
|
86
|
+
{local.description}
|
|
87
|
+
</KTextField.Description>
|
|
88
|
+
</Show>
|
|
89
|
+
|
|
90
|
+
<Show when={local.errorMessage}>
|
|
91
|
+
<KTextField.ErrorMessage class={styles.errorMessage()}>
|
|
92
|
+
{local.errorMessage}
|
|
93
|
+
</KTextField.ErrorMessage>
|
|
94
|
+
</Show>
|
|
95
|
+
</KTextField>
|
|
96
|
+
);
|
|
97
|
+
};
|