@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aleph-front/ds",
3
- "version": "0.0.3",
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-base-800",
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-base-800",
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-base-800",
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-base-700 px-3 py-1.5",
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",
@@ -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 (gray with slight purple tint, H: 265) */
84
- --color-neutral-50: oklch(0.98 0.003 265);
85
- --color-neutral-100: oklch(0.94 0.006 265);
86
- --color-neutral-200: oklch(0.90 0.008 265);
87
- --color-neutral-300: oklch(0.83 0.012 265);
88
- --color-neutral-400: oklch(0.71 0.015 265);
89
- --color-neutral-500: oklch(0.55 0.015 265);
90
- --color-neutral-600: oklch(0.45 0.014 265);
91
- --color-neutral-700: oklch(0.37 0.012 265);
92
- --color-neutral-800: oklch(0.29 0.010 265);
93
- --color-neutral-900: oklch(0.21 0.008 265);
94
- --color-neutral-950: oklch(0.14 0.005 265);
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-base-900);
149
+ --muted: var(--color-neutral-900);
155
150
  --muted-foreground: var(--color-neutral-400);
156
- --surface: var(--color-base-900);
151
+ --surface: var(--color-neutral-900);
157
152
  --surface-foreground: #F9F4FF;
158
- --edge: var(--color-base-800);
159
- --edge-hover: var(--color-base-700);
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
  }