@aleph-front/ds 0.0.3 → 0.1.0
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 +4 -1
- package/src/components/combobox/combobox.tsx +181 -0
- package/src/components/input/input.tsx +1 -1
- package/src/components/select/select.tsx +1 -1
- package/src/components/slider/slider.tsx +127 -0
- package/src/components/textarea/textarea.tsx +1 -1
- package/src/components/tooltip/tooltip.tsx +1 -1
- package/src/styles/tokens.css +16 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aleph-front/ds",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"./status-dot": "./src/components/status-dot/status-dot.tsx",
|
|
30
30
|
"./table": "./src/components/table/table.tsx",
|
|
31
31
|
"./tooltip": "./src/components/tooltip/tooltip.tsx",
|
|
32
|
+
"./combobox": "./src/components/combobox/combobox.tsx",
|
|
33
|
+
"./slider": "./src/components/slider/slider.tsx",
|
|
32
34
|
"./radio-group": "./src/components/radio-group/radio-group.tsx",
|
|
33
35
|
"./ui/skeleton": "./src/components/ui/skeleton.tsx",
|
|
34
36
|
"./ui/spinner": "./src/components/ui/spinner.tsx",
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"class-variance-authority": "0.7.1",
|
|
50
52
|
"clsx": "2.1.1",
|
|
53
|
+
"cmdk": "1.1.1",
|
|
51
54
|
"radix-ui": "1.4.3",
|
|
52
55
|
"tailwind-merge": "3.5.0"
|
|
53
56
|
},
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { forwardRef, useState } from "react";
|
|
2
|
+
import { Popover } from "radix-ui";
|
|
3
|
+
import { Command } from "cmdk";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cn } from "@ac/lib/cn";
|
|
6
|
+
|
|
7
|
+
const triggerVariants = cva(
|
|
8
|
+
[
|
|
9
|
+
"inline-flex items-center justify-between",
|
|
10
|
+
"w-full font-sans text-foreground bg-surface dark:bg-neutral-800",
|
|
11
|
+
"border-0 shadow-brand rounded-full",
|
|
12
|
+
"focus-visible:outline-none focus-visible:ring-3",
|
|
13
|
+
"focus-visible:ring-primary-500",
|
|
14
|
+
"disabled:opacity-50 disabled:pointer-events-none",
|
|
15
|
+
"ring-0 transition-[color,box-shadow]",
|
|
16
|
+
].join(" "),
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
size: {
|
|
20
|
+
sm: "py-1.5 px-4 text-sm",
|
|
21
|
+
md: "py-2 px-5 text-base",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
size: "md",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
type ComboboxOption = {
|
|
31
|
+
value: string;
|
|
32
|
+
label: string;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type ComboboxProps = VariantProps<typeof triggerVariants> & {
|
|
37
|
+
options: ComboboxOption[];
|
|
38
|
+
value?: string;
|
|
39
|
+
onValueChange?: (value: string) => void;
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
searchPlaceholder?: string;
|
|
42
|
+
emptyMessage?: string;
|
|
43
|
+
size?: "sm" | "md";
|
|
44
|
+
error?: boolean;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
className?: string;
|
|
47
|
+
id?: string;
|
|
48
|
+
"aria-describedby"?: string;
|
|
49
|
+
"aria-invalid"?: boolean;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const Combobox = forwardRef<HTMLButtonElement, ComboboxProps>(
|
|
53
|
+
(
|
|
54
|
+
{
|
|
55
|
+
options,
|
|
56
|
+
value,
|
|
57
|
+
onValueChange,
|
|
58
|
+
placeholder = "Select...",
|
|
59
|
+
searchPlaceholder = "Search...",
|
|
60
|
+
emptyMessage = "No results found.",
|
|
61
|
+
size,
|
|
62
|
+
error = false,
|
|
63
|
+
disabled = false,
|
|
64
|
+
className,
|
|
65
|
+
id,
|
|
66
|
+
"aria-describedby": ariaDescribedBy,
|
|
67
|
+
...rest
|
|
68
|
+
},
|
|
69
|
+
ref,
|
|
70
|
+
) => {
|
|
71
|
+
const [open, setOpen] = useState(false);
|
|
72
|
+
const selectedLabel = options.find((o) => o.value === value)?.label;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Popover.Root open={open} onOpenChange={setOpen}>
|
|
76
|
+
<Popover.Trigger
|
|
77
|
+
ref={ref}
|
|
78
|
+
id={id}
|
|
79
|
+
disabled={disabled}
|
|
80
|
+
aria-describedby={ariaDescribedBy}
|
|
81
|
+
aria-invalid={error || undefined}
|
|
82
|
+
className={cn(
|
|
83
|
+
triggerVariants({ size }),
|
|
84
|
+
error && "border-3 border-error-400 hover:border-error-500",
|
|
85
|
+
!selectedLabel && "text-muted-foreground",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
{...rest}
|
|
89
|
+
>
|
|
90
|
+
<span className="truncate">
|
|
91
|
+
{selectedLabel ?? placeholder}
|
|
92
|
+
</span>
|
|
93
|
+
<svg
|
|
94
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
95
|
+
viewBox="0 0 24 24"
|
|
96
|
+
fill="none"
|
|
97
|
+
stroke="currentColor"
|
|
98
|
+
strokeWidth={2}
|
|
99
|
+
strokeLinecap="round"
|
|
100
|
+
strokeLinejoin="round"
|
|
101
|
+
className={cn(
|
|
102
|
+
"ml-2 size-4 shrink-0 text-muted-foreground",
|
|
103
|
+
"transition-transform motion-reduce:transition-none",
|
|
104
|
+
open && "rotate-180",
|
|
105
|
+
)}
|
|
106
|
+
aria-hidden="true"
|
|
107
|
+
>
|
|
108
|
+
<polyline points="6 9 12 15 18 9" />
|
|
109
|
+
</svg>
|
|
110
|
+
</Popover.Trigger>
|
|
111
|
+
<Popover.Portal>
|
|
112
|
+
<Popover.Content
|
|
113
|
+
className={cn(
|
|
114
|
+
"z-50 w-[var(--radix-popover-trigger-width)]",
|
|
115
|
+
"overflow-hidden rounded-2xl",
|
|
116
|
+
"bg-surface border border-edge shadow-brand",
|
|
117
|
+
)}
|
|
118
|
+
sideOffset={4}
|
|
119
|
+
align="start"
|
|
120
|
+
>
|
|
121
|
+
<Command>
|
|
122
|
+
<Command.Input
|
|
123
|
+
placeholder={searchPlaceholder}
|
|
124
|
+
className={cn(
|
|
125
|
+
"w-full border-b border-edge bg-transparent px-4 py-2.5",
|
|
126
|
+
"text-sm text-foreground placeholder:text-muted-foreground",
|
|
127
|
+
"outline-none",
|
|
128
|
+
)}
|
|
129
|
+
/>
|
|
130
|
+
<Command.List className="max-h-60 overflow-y-auto p-1">
|
|
131
|
+
<Command.Empty className="px-4 py-6 text-center text-sm text-muted-foreground">
|
|
132
|
+
{emptyMessage}
|
|
133
|
+
</Command.Empty>
|
|
134
|
+
{options.map((option) => (
|
|
135
|
+
<Command.Item
|
|
136
|
+
key={option.value}
|
|
137
|
+
value={option.label}
|
|
138
|
+
{...(option.disabled ? { disabled: true } : {})}
|
|
139
|
+
onSelect={() => {
|
|
140
|
+
onValueChange?.(option.value);
|
|
141
|
+
setOpen(false);
|
|
142
|
+
}}
|
|
143
|
+
className={cn(
|
|
144
|
+
"relative flex items-center rounded-xl px-4 py-2",
|
|
145
|
+
"text-sm text-foreground cursor-pointer select-none",
|
|
146
|
+
"outline-none",
|
|
147
|
+
"data-[selected=true]:bg-muted",
|
|
148
|
+
"data-[disabled=true]:opacity-50",
|
|
149
|
+
"data-[disabled=true]:pointer-events-none",
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
152
|
+
<span className="flex-1">{option.label}</span>
|
|
153
|
+
{value === option.value && (
|
|
154
|
+
<svg
|
|
155
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
156
|
+
viewBox="0 0 24 24"
|
|
157
|
+
fill="none"
|
|
158
|
+
stroke="currentColor"
|
|
159
|
+
strokeWidth={2.5}
|
|
160
|
+
strokeLinecap="round"
|
|
161
|
+
strokeLinejoin="round"
|
|
162
|
+
className="ml-auto size-4"
|
|
163
|
+
aria-hidden="true"
|
|
164
|
+
>
|
|
165
|
+
<polyline points="20 6 9 17 4 12" />
|
|
166
|
+
</svg>
|
|
167
|
+
)}
|
|
168
|
+
</Command.Item>
|
|
169
|
+
))}
|
|
170
|
+
</Command.List>
|
|
171
|
+
</Command>
|
|
172
|
+
</Popover.Content>
|
|
173
|
+
</Popover.Portal>
|
|
174
|
+
</Popover.Root>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
Combobox.displayName = "Combobox";
|
|
180
|
+
|
|
181
|
+
export { Combobox, triggerVariants, type ComboboxProps, type ComboboxOption };
|
|
@@ -4,7 +4,7 @@ import { cn } from "@ac/lib/cn";
|
|
|
4
4
|
|
|
5
5
|
const inputVariants = cva(
|
|
6
6
|
[
|
|
7
|
-
"w-full font-sans text-foreground bg-surface dark:bg-
|
|
7
|
+
"w-full font-sans text-foreground bg-surface dark:bg-neutral-800",
|
|
8
8
|
"border-0 shadow-brand rounded-full",
|
|
9
9
|
"placeholder:text-muted-foreground",
|
|
10
10
|
"focus-visible:outline-none focus-visible:ring-3",
|
|
@@ -6,7 +6,7 @@ import { cn } from "@ac/lib/cn";
|
|
|
6
6
|
const triggerVariants = cva(
|
|
7
7
|
[
|
|
8
8
|
"inline-flex items-center justify-between",
|
|
9
|
-
"w-full font-sans text-foreground bg-surface dark:bg-
|
|
9
|
+
"w-full font-sans text-foreground bg-surface dark:bg-neutral-800",
|
|
10
10
|
"border-0 shadow-brand rounded-full",
|
|
11
11
|
"focus-visible:outline-none focus-visible:ring-3",
|
|
12
12
|
"focus-visible:ring-primary-500",
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { forwardRef, useState, type ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import { Slider as SliderPrimitive } from "radix-ui";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@ac/lib/cn";
|
|
5
|
+
|
|
6
|
+
const trackVariants = cva(
|
|
7
|
+
[
|
|
8
|
+
"relative w-full grow overflow-hidden rounded-full",
|
|
9
|
+
"bg-neutral-200 dark:bg-neutral-700",
|
|
10
|
+
].join(" "),
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
size: {
|
|
14
|
+
sm: "h-1.5",
|
|
15
|
+
md: "h-2",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
size: "md",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const thumbVariants = cva(
|
|
25
|
+
[
|
|
26
|
+
"block rounded-full bg-white",
|
|
27
|
+
"border-2 border-primary-500",
|
|
28
|
+
"focus-visible:outline-none focus-visible:ring-3",
|
|
29
|
+
"focus-visible:ring-primary-500",
|
|
30
|
+
"disabled:pointer-events-none",
|
|
31
|
+
].join(" "),
|
|
32
|
+
{
|
|
33
|
+
variants: {
|
|
34
|
+
size: {
|
|
35
|
+
sm: "size-4",
|
|
36
|
+
md: "size-5",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
defaultVariants: {
|
|
40
|
+
size: "md",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
type SliderProps = Omit<
|
|
46
|
+
ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,
|
|
47
|
+
"size"
|
|
48
|
+
> &
|
|
49
|
+
VariantProps<typeof trackVariants> & {
|
|
50
|
+
error?: boolean;
|
|
51
|
+
showTooltip?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const Slider = forwardRef<
|
|
55
|
+
React.ComponentRef<typeof SliderPrimitive.Root>,
|
|
56
|
+
SliderProps
|
|
57
|
+
>(
|
|
58
|
+
(
|
|
59
|
+
{
|
|
60
|
+
size,
|
|
61
|
+
error = false,
|
|
62
|
+
showTooltip = false,
|
|
63
|
+
className,
|
|
64
|
+
disabled,
|
|
65
|
+
onValueChange: onValueChangeProp,
|
|
66
|
+
...rootProps
|
|
67
|
+
},
|
|
68
|
+
ref,
|
|
69
|
+
) => {
|
|
70
|
+
const [hovering, setHovering] = useState(false);
|
|
71
|
+
const [internalValue, setInternalValue] = useState(
|
|
72
|
+
rootProps.defaultValue ?? rootProps.value ?? [0],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const displayValue = rootProps.value ?? internalValue;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<SliderPrimitive.Root
|
|
79
|
+
ref={ref}
|
|
80
|
+
{...(disabled ? { disabled: true } : {})}
|
|
81
|
+
className={cn(
|
|
82
|
+
"relative flex w-full touch-none select-none items-center",
|
|
83
|
+
disabled && "opacity-50 pointer-events-none",
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
86
|
+
onValueChange={(val) => {
|
|
87
|
+
setInternalValue(val);
|
|
88
|
+
onValueChangeProp?.(val);
|
|
89
|
+
}}
|
|
90
|
+
onPointerEnter={() => setHovering(true)}
|
|
91
|
+
onPointerLeave={() => setHovering(false)}
|
|
92
|
+
{...rootProps}
|
|
93
|
+
>
|
|
94
|
+
<SliderPrimitive.Track
|
|
95
|
+
className={cn(
|
|
96
|
+
trackVariants({ size }),
|
|
97
|
+
error && "ring-2 ring-error-400",
|
|
98
|
+
)}
|
|
99
|
+
>
|
|
100
|
+
<SliderPrimitive.Range className="absolute h-full bg-primary-500 rounded-full" />
|
|
101
|
+
</SliderPrimitive.Track>
|
|
102
|
+
{displayValue.map((val, i) => (
|
|
103
|
+
<SliderPrimitive.Thumb
|
|
104
|
+
key={i}
|
|
105
|
+
className={cn(thumbVariants({ size }), "relative")}
|
|
106
|
+
>
|
|
107
|
+
{showTooltip && hovering && (
|
|
108
|
+
<span
|
|
109
|
+
className={cn(
|
|
110
|
+
"absolute bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
111
|
+
"rounded-md bg-neutral-900 dark:bg-neutral-700 px-2 py-1",
|
|
112
|
+
"text-xs text-white whitespace-nowrap pointer-events-none",
|
|
113
|
+
)}
|
|
114
|
+
>
|
|
115
|
+
{val}
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
118
|
+
</SliderPrimitive.Thumb>
|
|
119
|
+
))}
|
|
120
|
+
</SliderPrimitive.Root>
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
Slider.displayName = "Slider";
|
|
126
|
+
|
|
127
|
+
export { Slider, trackVariants, thumbVariants, type SliderProps };
|
|
@@ -4,7 +4,7 @@ import { cn } from "@ac/lib/cn";
|
|
|
4
4
|
|
|
5
5
|
const textareaVariants = cva(
|
|
6
6
|
[
|
|
7
|
-
"w-full font-sans text-foreground bg-surface dark:bg-
|
|
7
|
+
"w-full font-sans text-foreground bg-surface dark:bg-neutral-800",
|
|
8
8
|
"border-0 shadow-brand rounded-2xl",
|
|
9
9
|
"placeholder:text-muted-foreground",
|
|
10
10
|
"focus-visible:outline-none focus-visible:ring-3",
|
|
@@ -16,7 +16,7 @@ const TooltipContent = forwardRef<
|
|
|
16
16
|
sideOffset={sideOffset}
|
|
17
17
|
className={cn(
|
|
18
18
|
[
|
|
19
|
-
"z-50 rounded-lg bg-neutral-900 dark:bg-
|
|
19
|
+
"z-50 rounded-lg bg-neutral-900 dark:bg-neutral-700 px-3 py-1.5",
|
|
20
20
|
"text-sm text-white shadow-brand-sm",
|
|
21
21
|
"animate-in fade-in-0 zoom-in-95",
|
|
22
22
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
|
package/src/styles/tokens.css
CHANGED
|
@@ -80,23 +80,18 @@
|
|
|
80
80
|
--color-destructive-900: var(--color-error-900);
|
|
81
81
|
--color-destructive-950: var(--color-error-950);
|
|
82
82
|
|
|
83
|
-
/* Neutral (
|
|
84
|
-
--color-neutral-50:
|
|
85
|
-
--color-neutral-100: oklch(0.94 0.
|
|
86
|
-
--color-neutral-200: oklch(0.90 0.
|
|
87
|
-
--color-neutral-300: oklch(0.83 0.
|
|
88
|
-
--color-neutral-400: oklch(0.71 0.
|
|
89
|
-
--color-neutral-500: oklch(0.55 0.
|
|
90
|
-
--color-neutral-600: oklch(0.45 0.
|
|
91
|
-
--color-neutral-700: oklch(0.
|
|
92
|
-
--color-neutral-800: oklch(0.
|
|
93
|
-
--color-neutral-900: oklch(0.
|
|
94
|
-
--color-neutral-950: oklch(0.14 0.
|
|
95
|
-
|
|
96
|
-
/* Base (dark indigo, H: 280) — tone-sur-tone dark surface palette */
|
|
97
|
-
--color-base-900: oklch(0.22 0.025 280);
|
|
98
|
-
--color-base-800: oklch(0.28 0.030 280);
|
|
99
|
-
--color-base-700: oklch(0.34 0.034 280);
|
|
83
|
+
/* Neutral (brand indigo tint, H: 280) — unified text + surface palette */
|
|
84
|
+
--color-neutral-50: oklch(0.98 0.006 280);
|
|
85
|
+
--color-neutral-100: oklch(0.94 0.010 280);
|
|
86
|
+
--color-neutral-200: oklch(0.90 0.014 280);
|
|
87
|
+
--color-neutral-300: oklch(0.83 0.018 280);
|
|
88
|
+
--color-neutral-400: oklch(0.71 0.022 280);
|
|
89
|
+
--color-neutral-500: oklch(0.55 0.026 280);
|
|
90
|
+
--color-neutral-600: oklch(0.45 0.030 280);
|
|
91
|
+
--color-neutral-700: oklch(0.34 0.034 280);
|
|
92
|
+
--color-neutral-800: oklch(0.28 0.030 280);
|
|
93
|
+
--color-neutral-900: oklch(0.22 0.025 280);
|
|
94
|
+
--color-neutral-950: oklch(0.14 0.018 280);
|
|
100
95
|
|
|
101
96
|
/* Gradients */
|
|
102
97
|
--gradient-main-base: linear-gradient(90deg, #141421 8.24%, #5100CD 71.81%);
|
|
@@ -151,12 +146,12 @@
|
|
|
151
146
|
--primary-foreground: #ffffff;
|
|
152
147
|
--accent: var(--color-accent-300);
|
|
153
148
|
--accent-foreground: #141421;
|
|
154
|
-
--muted: var(--color-
|
|
149
|
+
--muted: var(--color-neutral-900);
|
|
155
150
|
--muted-foreground: var(--color-neutral-400);
|
|
156
|
-
--surface: var(--color-
|
|
151
|
+
--surface: var(--color-neutral-900);
|
|
157
152
|
--surface-foreground: #F9F4FF;
|
|
158
|
-
--edge: var(--color-
|
|
159
|
-
--edge-hover: var(--color-
|
|
153
|
+
--edge: var(--color-neutral-800);
|
|
154
|
+
--edge-hover: var(--color-neutral-700);
|
|
160
155
|
|
|
161
156
|
--gradient-main: var(--gradient-main-dark);
|
|
162
157
|
}
|