@nikala-ui/core 0.9.11 → 0.9.12
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 +1 -1
- package/registry/aspect-ratio.json +17 -0
- package/registry/collapsible.json +18 -0
- package/registry/combobox.json +4 -1
- package/registry/command.json +3 -2
- package/registry/context-menu.json +24 -0
- package/registry/dialog.json +4 -1
- package/registry/dropdown-menu.json +4 -1
- package/registry/index.json +111 -1
- package/registry/number-input.json +24 -0
- package/registry/resizable.json +21 -0
- package/registry/scroll-area.json +21 -0
- package/registry/select.json +4 -1
- package/registry/sheet.json +4 -1
- package/registry/theme-manager.json +1 -1
- package/registry/toggle.json +18 -0
- package/src/registry/components/ui/aspect-ratio.tsx +31 -0
- package/src/registry/components/ui/collapsible.tsx +73 -0
- package/src/registry/components/ui/combobox.tsx +23 -84
- package/src/registry/components/ui/command.tsx +154 -66
- package/src/registry/components/ui/context-menu.tsx +192 -0
- package/src/registry/components/ui/dialog.tsx +39 -44
- package/src/registry/components/ui/dropdown-menu.tsx +40 -45
- package/src/registry/components/ui/number-input.tsx +150 -0
- package/src/registry/components/ui/resizable.tsx +193 -0
- package/src/registry/components/ui/scroll-area.tsx +218 -0
- package/src/registry/components/ui/select.tsx +22 -16
- package/src/registry/components/ui/sheet.tsx +62 -63
- package/src/registry/components/ui/theme-toggle.tsx +6 -4
- package/src/registry/components/ui/toggle.tsx +101 -0
- package/src/registry/metadata.ts +45 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSignal,
|
|
3
|
+
createContext,
|
|
4
|
+
useContext,
|
|
5
|
+
splitProps,
|
|
6
|
+
type Component,
|
|
7
|
+
type JSX,
|
|
8
|
+
type Accessor,
|
|
9
|
+
} from "solid-js";
|
|
10
|
+
import { createElementSize } from "@nikala-ui/hooks";
|
|
11
|
+
import { GripVertical, GripHorizontal } from "lucide-solid";
|
|
12
|
+
import { cn } from "@/lib/cn";
|
|
13
|
+
|
|
14
|
+
export interface ResizableContextValue {
|
|
15
|
+
orientation: Accessor<"horizontal" | "vertical">;
|
|
16
|
+
registerPanel: (id: string, initialSizes: number) => void;
|
|
17
|
+
sizes: Accessor<Record<string, number>>;
|
|
18
|
+
startDragging: (handleIndex: number, event: PointerEvent) => void;
|
|
19
|
+
containerRef: () => HTMLDivElement | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ResizableContext = createContext<ResizableContextValue>();
|
|
23
|
+
|
|
24
|
+
export interface ResizableGroupProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
25
|
+
orientation?: "horizontal" | "vertical";
|
|
26
|
+
class?: string;
|
|
27
|
+
children?: JSX.Element;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const ResizableGroup: Component<ResizableGroupProps> = (props) => {
|
|
31
|
+
const [local, rest] = splitProps(props, ["orientation", "class", "children"]);
|
|
32
|
+
const orientation = () => local.orientation || "horizontal";
|
|
33
|
+
let containerEl: HTMLDivElement | undefined;
|
|
34
|
+
|
|
35
|
+
const [panelOrder, setPanelOrder] = createSignal<string[]>([]);
|
|
36
|
+
const [sizes, setSizes] = createSignal<Record<string, number>>({});
|
|
37
|
+
|
|
38
|
+
const registerPanel = (id: string, initialSize: number) => {
|
|
39
|
+
setPanelOrder((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
|
40
|
+
setSizes((prev) => (prev[id] !== undefined ? prev : { ...prev, [id]: initialSize }));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const startDragging = (handleIndex: number, event: PointerEvent) => {
|
|
44
|
+
if (!containerEl) return;
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
|
|
47
|
+
const order = panelOrder();
|
|
48
|
+
if (handleIndex < 0 || handleIndex >= order.length - 1) return;
|
|
49
|
+
|
|
50
|
+
const leftId = order[handleIndex];
|
|
51
|
+
const rightId = order[handleIndex + 1];
|
|
52
|
+
|
|
53
|
+
const isHoriz = orientation() === "horizontal";
|
|
54
|
+
const startPos = isHoriz ? event.clientX : event.clientY;
|
|
55
|
+
const rect = containerEl.getBoundingClientRect();
|
|
56
|
+
const totalPx = isHoriz ? rect.width : rect.height;
|
|
57
|
+
|
|
58
|
+
const startLeftPct = sizes()[leftId] ?? 50;
|
|
59
|
+
const startRightPct = sizes()[rightId] ?? 50;
|
|
60
|
+
|
|
61
|
+
const onPointerMove = (e: PointerEvent) => {
|
|
62
|
+
const currentPos = isHoriz ? e.clientX : e.clientY;
|
|
63
|
+
const deltaPx = currentPos - startPos;
|
|
64
|
+
const deltaPct = (deltaPx / totalPx) * 100;
|
|
65
|
+
|
|
66
|
+
let newLeft = startLeftPct + deltaPct;
|
|
67
|
+
let newRight = startRightPct - deltaPct;
|
|
68
|
+
|
|
69
|
+
if (newLeft < 10) {
|
|
70
|
+
newLeft = 10;
|
|
71
|
+
newRight = startLeftPct + startRightPct - 10;
|
|
72
|
+
} else if (newRight < 10) {
|
|
73
|
+
newRight = 10;
|
|
74
|
+
newLeft = startLeftPct + startRightPct - 10;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setSizes((prev) => ({
|
|
78
|
+
...prev,
|
|
79
|
+
[leftId]: newLeft,
|
|
80
|
+
[rightId]: newRight,
|
|
81
|
+
}));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const onPointerUp = () => {
|
|
85
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
86
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
90
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<ResizableContext.Provider
|
|
95
|
+
value={{
|
|
96
|
+
orientation,
|
|
97
|
+
registerPanel,
|
|
98
|
+
sizes,
|
|
99
|
+
startDragging,
|
|
100
|
+
containerRef: () => containerEl,
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
ref={containerEl}
|
|
105
|
+
class={cn(
|
|
106
|
+
"flex h-full w-full overflow-hidden rounded-lg border border-border bg-background",
|
|
107
|
+
orientation() === "vertical" ? "flex-col" : "flex-row",
|
|
108
|
+
local.class
|
|
109
|
+
)}
|
|
110
|
+
{...rest}
|
|
111
|
+
>
|
|
112
|
+
{local.children}
|
|
113
|
+
</div>
|
|
114
|
+
</ResizableContext.Provider>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export interface ResizablePanelProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
119
|
+
id: string;
|
|
120
|
+
initialSize?: number;
|
|
121
|
+
class?: string;
|
|
122
|
+
children?: JSX.Element;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const ResizablePanel: Component<ResizablePanelProps> = (props) => {
|
|
126
|
+
const [local, rest] = splitProps(props, ["id", "initialSize", "class", "children"]);
|
|
127
|
+
const ctx = useContext(ResizableContext);
|
|
128
|
+
|
|
129
|
+
if (!ctx) {
|
|
130
|
+
throw new Error("ResizablePanel must be used within a ResizableGroup");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
ctx.registerPanel(local.id, local.initialSize ?? 50);
|
|
134
|
+
|
|
135
|
+
const currentPct = () => ctx.sizes()[local.id] ?? local.initialSize ?? 50;
|
|
136
|
+
|
|
137
|
+
const containerSize = createElementSize(() => ctx.containerRef());
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div
|
|
141
|
+
class={cn("overflow-auto transition-[flex-basis] duration-75", local.class)}
|
|
142
|
+
style={{
|
|
143
|
+
"flex-basis": `${currentPct()}%`,
|
|
144
|
+
"flex-grow": 0,
|
|
145
|
+
"flex-shrink": 0,
|
|
146
|
+
}}
|
|
147
|
+
{...rest}
|
|
148
|
+
>
|
|
149
|
+
{local.children}
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export interface ResizableHandleProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
155
|
+
handleIndex: number;
|
|
156
|
+
withHandle?: boolean;
|
|
157
|
+
class?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const ResizableHandle: Component<ResizableHandleProps> = (props) => {
|
|
161
|
+
const [local, rest] = splitProps(props, ["handleIndex", "withHandle", "class"]);
|
|
162
|
+
const ctx = useContext(ResizableContext);
|
|
163
|
+
|
|
164
|
+
if (!ctx) {
|
|
165
|
+
throw new Error("ResizableHandle must be used within a ResizableGroup");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const isHoriz = () => ctx.orientation() === "horizontal";
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
role="separator"
|
|
173
|
+
tabIndex={0}
|
|
174
|
+
class={cn(
|
|
175
|
+
"relative flex select-none items-center justify-center bg-border transition-colors hover:bg-primary/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-col-resize",
|
|
176
|
+
isHoriz() ? "h-full w-1.5 cursor-col-resize" : "h-1.5 w-full cursor-row-resize",
|
|
177
|
+
local.class
|
|
178
|
+
)}
|
|
179
|
+
onPointerDown={(e) => ctx.startDragging(local.handleIndex, e)}
|
|
180
|
+
{...rest}
|
|
181
|
+
>
|
|
182
|
+
{local.withHandle && (
|
|
183
|
+
<div class="z-10 flex h-4 w-3 items-center justify-center rounded-xs border border-border bg-muted shadow-2xs">
|
|
184
|
+
{isHoriz() ? (
|
|
185
|
+
<GripVertical class="h-2.5 w-2.5 text-muted-foreground" />
|
|
186
|
+
) : (
|
|
187
|
+
<GripHorizontal class="h-2.5 w-2.5 text-muted-foreground" />
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSignal,
|
|
3
|
+
createEffect,
|
|
4
|
+
onCleanup,
|
|
5
|
+
splitProps,
|
|
6
|
+
type Component,
|
|
7
|
+
type JSX,
|
|
8
|
+
type Accessor,
|
|
9
|
+
} from "solid-js";
|
|
10
|
+
import { createScrollPosition, createElementSize } from "@nikala-ui/hooks";
|
|
11
|
+
import { cn } from "@/lib/cn";
|
|
12
|
+
|
|
13
|
+
export interface ScrollAreaProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
14
|
+
orientation?: "vertical" | "horizontal" | "both";
|
|
15
|
+
scrollHideDelay?: number;
|
|
16
|
+
class?: string;
|
|
17
|
+
children?: JSX.Element;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ScrollArea: Component<ScrollAreaProps> = (props) => {
|
|
21
|
+
const [local, rest] = splitProps(props, [
|
|
22
|
+
"orientation",
|
|
23
|
+
"scrollHideDelay",
|
|
24
|
+
"class",
|
|
25
|
+
"children",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const orientation = () => local.orientation || "vertical";
|
|
29
|
+
let viewportRef: HTMLDivElement | undefined;
|
|
30
|
+
let verticalTrackRef: HTMLDivElement | undefined;
|
|
31
|
+
let horizontalTrackRef: HTMLDivElement | undefined;
|
|
32
|
+
|
|
33
|
+
const scrollPos = createScrollPosition({
|
|
34
|
+
target: () => viewportRef,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const viewportSize = createElementSize(() => viewportRef);
|
|
38
|
+
|
|
39
|
+
const [thumbHeight, setThumbHeight] = createSignal(0);
|
|
40
|
+
const [thumbTop, setThumbTop] = createSignal(0);
|
|
41
|
+
const [thumbWidth, setThumbWidth] = createSignal(0);
|
|
42
|
+
const [thumbLeft, setThumbLeft] = createSignal(0);
|
|
43
|
+
const [isDragging, setIsDragging] = createSignal(false);
|
|
44
|
+
|
|
45
|
+
const updateThumbMetrics = () => {
|
|
46
|
+
if (!viewportRef) return;
|
|
47
|
+
|
|
48
|
+
const scrollHeight = viewportRef.scrollHeight;
|
|
49
|
+
const clientHeight = viewportRef.clientHeight;
|
|
50
|
+
const scrollWidth = viewportRef.scrollWidth;
|
|
51
|
+
const clientWidth = viewportRef.clientWidth;
|
|
52
|
+
|
|
53
|
+
const trackHeight = verticalTrackRef ? verticalTrackRef.clientHeight - 4 : clientHeight - 4;
|
|
54
|
+
const trackWidth = horizontalTrackRef ? horizontalTrackRef.clientWidth - 4 : clientWidth - 4;
|
|
55
|
+
|
|
56
|
+
if (scrollHeight > clientHeight && clientHeight > 0) {
|
|
57
|
+
const vRatio = clientHeight / scrollHeight;
|
|
58
|
+
const calculatedHeight = Math.max(vRatio * trackHeight, 20);
|
|
59
|
+
const maxTop = trackHeight - calculatedHeight;
|
|
60
|
+
const topPct = scrollPos.y() / (scrollHeight - clientHeight);
|
|
61
|
+
setThumbHeight(calculatedHeight);
|
|
62
|
+
setThumbTop(topPct * maxTop);
|
|
63
|
+
} else {
|
|
64
|
+
setThumbHeight(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (scrollWidth > clientWidth && clientWidth > 0) {
|
|
68
|
+
const hRatio = clientWidth / scrollWidth;
|
|
69
|
+
const calculatedWidth = Math.max(hRatio * trackWidth, 20);
|
|
70
|
+
const maxLeft = trackWidth - calculatedWidth;
|
|
71
|
+
const leftPct = scrollPos.x() / (scrollWidth - clientWidth);
|
|
72
|
+
setThumbWidth(calculatedWidth);
|
|
73
|
+
setThumbLeft(leftPct * maxLeft);
|
|
74
|
+
} else {
|
|
75
|
+
setThumbWidth(0);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
createEffect(() => {
|
|
80
|
+
viewportSize.width();
|
|
81
|
+
viewportSize.height();
|
|
82
|
+
scrollPos.x();
|
|
83
|
+
scrollPos.y();
|
|
84
|
+
updateThumbMetrics();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const handleVerticalThumbPointerDown = (e: PointerEvent) => {
|
|
88
|
+
if (!viewportRef || !verticalTrackRef) return;
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
|
|
92
|
+
setIsDragging(true);
|
|
93
|
+
const startY = e.clientY;
|
|
94
|
+
const startScrollTop = viewportRef.scrollTop;
|
|
95
|
+
const scrollHeight = viewportRef.scrollHeight;
|
|
96
|
+
const clientHeight = viewportRef.clientHeight;
|
|
97
|
+
const trackHeight = verticalTrackRef.clientHeight - 4;
|
|
98
|
+
|
|
99
|
+
const maxScrollTop = scrollHeight - clientHeight;
|
|
100
|
+
const maxThumbTop = trackHeight - thumbHeight();
|
|
101
|
+
const ratio = maxThumbTop > 0 ? maxScrollTop / maxThumbTop : 0;
|
|
102
|
+
|
|
103
|
+
const onPointerMove = (moveEvent: PointerEvent) => {
|
|
104
|
+
const deltaY = moveEvent.clientY - startY;
|
|
105
|
+
viewportRef!.scrollTop = Math.max(0, Math.min(maxScrollTop, startScrollTop + deltaY * ratio));
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const onPointerUp = () => {
|
|
109
|
+
setIsDragging(false);
|
|
110
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
111
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
115
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleHorizontalThumbPointerDown = (e: PointerEvent) => {
|
|
119
|
+
if (!viewportRef || !horizontalTrackRef) return;
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
|
|
123
|
+
setIsDragging(true);
|
|
124
|
+
const startX = e.clientX;
|
|
125
|
+
const startScrollLeft = viewportRef.scrollLeft;
|
|
126
|
+
const scrollWidth = viewportRef.scrollWidth;
|
|
127
|
+
const clientWidth = viewportRef.clientWidth;
|
|
128
|
+
const trackWidth = horizontalTrackRef.clientWidth - 4;
|
|
129
|
+
|
|
130
|
+
const maxScrollLeft = scrollWidth - clientWidth;
|
|
131
|
+
const maxThumbLeft = trackWidth - thumbWidth();
|
|
132
|
+
const ratio = maxThumbLeft > 0 ? maxScrollLeft / maxThumbLeft : 0;
|
|
133
|
+
|
|
134
|
+
const onPointerMove = (moveEvent: PointerEvent) => {
|
|
135
|
+
const deltaX = moveEvent.clientX - startX;
|
|
136
|
+
viewportRef!.scrollLeft = Math.max(0, Math.min(maxScrollLeft, startScrollLeft + deltaX * ratio));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const onPointerUp = () => {
|
|
140
|
+
setIsDragging(false);
|
|
141
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
142
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
146
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const handleWheel = (e: WheelEvent) => {
|
|
150
|
+
if (orientation() === "horizontal" && viewportRef) {
|
|
151
|
+
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
viewportRef.scrollLeft += e.deltaY;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div
|
|
160
|
+
class={cn("relative overflow-hidden group/scroll-area", local.class)}
|
|
161
|
+
onWheel={handleWheel}
|
|
162
|
+
{...rest}
|
|
163
|
+
>
|
|
164
|
+
<div
|
|
165
|
+
ref={viewportRef}
|
|
166
|
+
class="h-full w-full overflow-auto scrollbar-none rounded-[inherit]"
|
|
167
|
+
style={{
|
|
168
|
+
"scrollbar-width": "none",
|
|
169
|
+
"-ms-overflow-style": "none",
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{local.children}
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{(orientation() === "vertical" || orientation() === "both") && thumbHeight() > 0 && (
|
|
176
|
+
<div
|
|
177
|
+
ref={verticalTrackRef}
|
|
178
|
+
class={cn(
|
|
179
|
+
"absolute right-0 top-0 bottom-0 w-2.5 p-0.5 select-none transition-opacity duration-300 pointer-events-none",
|
|
180
|
+
scrollPos.isScrolling() || isDragging()
|
|
181
|
+
? "opacity-100"
|
|
182
|
+
: "opacity-0 group-hover/scroll-area:opacity-100"
|
|
183
|
+
)}
|
|
184
|
+
>
|
|
185
|
+
<div
|
|
186
|
+
class="w-1.5 rounded-full bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
|
|
187
|
+
style={{
|
|
188
|
+
height: `${thumbHeight()}px`,
|
|
189
|
+
transform: `translateY(${thumbTop()}px)`,
|
|
190
|
+
}}
|
|
191
|
+
onPointerDown={handleVerticalThumbPointerDown}
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{(orientation() === "horizontal" || orientation() === "both") && thumbWidth() > 0 && (
|
|
197
|
+
<div
|
|
198
|
+
ref={horizontalTrackRef}
|
|
199
|
+
class={cn(
|
|
200
|
+
"absolute bottom-0 left-0 right-0 h-2.5 p-0.5 select-none transition-opacity duration-300 pointer-events-none",
|
|
201
|
+
scrollPos.isScrolling() || isDragging()
|
|
202
|
+
? "opacity-100"
|
|
203
|
+
: "opacity-0 group-hover/scroll-area:opacity-100"
|
|
204
|
+
)}
|
|
205
|
+
>
|
|
206
|
+
<div
|
|
207
|
+
class="h-1.5 rounded-full bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
|
|
208
|
+
style={{
|
|
209
|
+
width: `${thumbWidth()}px`,
|
|
210
|
+
transform: `translateX(${thumbLeft()}px)`,
|
|
211
|
+
}}
|
|
212
|
+
onPointerDown={handleHorizontalThumbPointerDown}
|
|
213
|
+
/>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
};
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import { splitProps, type JSX, type ValidComponent } from "solid-js";
|
|
1
|
+
import { splitProps, type Component, type JSX, type ValidComponent } from "solid-js";
|
|
2
2
|
import * as SelectPrimitive from "@kobalte/core/select";
|
|
3
3
|
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
|
|
4
4
|
import { createClickOutside } from "@nikala-ui/hooks";
|
|
5
|
+
import { ScrollArea } from "./scroll-area";
|
|
5
6
|
import { cn } from "@/lib/cn";
|
|
6
7
|
|
|
7
8
|
export type SelectRootProps<Option = any, OptGroup = any, T extends ValidComponent = "div"> =
|
|
8
|
-
SelectPrimitive.SelectRootProps<Option, OptGroup, T
|
|
9
|
-
class?: string;
|
|
10
|
-
};
|
|
9
|
+
SelectPrimitive.SelectRootProps<Option, OptGroup, T>;
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* Root Select component built on
|
|
12
|
+
* Root Select component wrapper built on Kobalte primitives.
|
|
14
13
|
*/
|
|
15
14
|
export const Select = <Option = any, OptGroup = any, T extends ValidComponent = "div">(
|
|
16
|
-
props:
|
|
15
|
+
props: SelectRootProps<Option, OptGroup, T>
|
|
17
16
|
) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return <SelectPrimitive.Root class={cn("relative w-full", local.class)} {...(rest as any)} />;
|
|
17
|
+
return <SelectPrimitive.Root {...props} />;
|
|
21
18
|
};
|
|
22
19
|
|
|
23
20
|
export type SelectTriggerProps<T extends ValidComponent = "button"> =
|
|
@@ -109,12 +106,14 @@ export const SelectContent = <T extends ValidComponent = "div">(
|
|
|
109
106
|
if (typeof (props as any).ref === "function") (props as any).ref(el);
|
|
110
107
|
}}
|
|
111
108
|
class={cn(
|
|
112
|
-
"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",
|
|
109
|
+
"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 max-h-60",
|
|
113
110
|
local.class
|
|
114
111
|
)}
|
|
115
112
|
{...rest}
|
|
116
113
|
>
|
|
117
|
-
<
|
|
114
|
+
<ScrollArea class="max-h-60 w-full">
|
|
115
|
+
<SelectPrimitive.Listbox class="p-1 outline-none" />
|
|
116
|
+
</ScrollArea>
|
|
118
117
|
</SelectPrimitive.Content>
|
|
119
118
|
</SelectPrimitive.Portal>
|
|
120
119
|
);
|
|
@@ -137,16 +136,23 @@ export const SelectItem = <T extends ValidComponent = "li">(
|
|
|
137
136
|
return (
|
|
138
137
|
<SelectPrimitive.Item
|
|
139
138
|
class={cn(
|
|
140
|
-
"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",
|
|
139
|
+
"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 data-[disabled]:opacity-50 text-foreground",
|
|
141
140
|
local.class
|
|
142
141
|
)}
|
|
143
142
|
{...rest}
|
|
144
143
|
>
|
|
145
|
-
<
|
|
146
|
-
<
|
|
144
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
145
|
+
<SelectPrimitive.ItemIndicator
|
|
146
|
+
as="svg"
|
|
147
|
+
class="h-4 w-4"
|
|
148
|
+
viewBox="0 0 24 24"
|
|
149
|
+
fill="none"
|
|
150
|
+
stroke="currentColor"
|
|
151
|
+
stroke-width="2"
|
|
152
|
+
>
|
|
147
153
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
148
|
-
</
|
|
149
|
-
</
|
|
154
|
+
</SelectPrimitive.ItemIndicator>
|
|
155
|
+
</span>
|
|
150
156
|
<SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>
|
|
151
157
|
</SelectPrimitive.Item>
|
|
152
158
|
);
|