@nikala-ui/core 0.10.1 → 0.11.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 +1 -1
- package/registry/bubble.json +18 -0
- package/registry/button-group.json +20 -0
- package/registry/create-chat-scroll.json +13 -0
- package/registry/create-drop-zone.json +13 -0
- package/registry/create-pagination.json +13 -0
- package/registry/dropzone.json +21 -0
- package/registry/footer.json +18 -0
- package/registry/forgot-password-01.json +28 -0
- package/registry/hero-01.json +22 -0
- package/registry/index.json +331 -0
- package/registry/login-01.json +28 -0
- package/registry/marker.json +17 -0
- package/registry/marquee.json +17 -0
- package/registry/message.json +17 -0
- package/registry/navbar.json +19 -0
- package/registry/navigation-menu.json +22 -0
- package/registry/otp-verification-01.json +26 -0
- package/registry/pagination.json +22 -0
- package/registry/rating.json +19 -0
- package/registry/register-01.json +31 -0
- package/registry/review-card.json +23 -0
- package/registry/scroll-area.json +1 -1
- package/registry/sidebar.json +24 -0
- package/registry/spinner.json +1 -1
- package/registry/stat.json +19 -0
- package/registry/table.json +17 -0
- package/registry/timeline.json +18 -0
- package/registry/toggle-group.json +21 -0
- package/src/registry/blocks/forgot-password-01.tsx +141 -0
- package/src/registry/blocks/hero-01.tsx +28 -0
- package/src/registry/blocks/login-01.tsx +231 -0
- package/src/registry/blocks/otp-verification-01.tsx +170 -0
- package/src/registry/blocks/register-01.tsx +318 -0
- package/src/registry/components/ui/bubble.tsx +142 -0
- package/src/registry/components/ui/button-group.tsx +42 -0
- package/src/registry/components/ui/dropzone.tsx +189 -0
- package/src/registry/components/ui/footer.tsx +247 -0
- package/src/registry/components/ui/marker.tsx +102 -0
- package/src/registry/components/ui/marquee.tsx +118 -0
- package/src/registry/components/ui/message.tsx +168 -0
- package/src/registry/components/ui/navbar.tsx +368 -0
- package/src/registry/components/ui/navigation-menu.tsx +358 -0
- package/src/registry/components/ui/pagination.tsx +258 -0
- package/src/registry/components/ui/rating.tsx +185 -0
- package/src/registry/components/ui/review-card.tsx +195 -0
- package/src/registry/components/ui/scroll-area.tsx +2 -2
- package/src/registry/components/ui/sidebar.tsx +692 -0
- package/src/registry/components/ui/spinner.tsx +1 -1
- package/src/registry/components/ui/stat.tsx +245 -0
- package/src/registry/components/ui/table.tsx +160 -0
- package/src/registry/components/ui/timeline.tsx +350 -0
- package/src/registry/components/ui/toggle-group.tsx +203 -0
- package/src/registry/index.ts +3 -3
- package/src/registry/metadata.ts +141 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSignal,
|
|
3
|
+
splitProps,
|
|
4
|
+
For,
|
|
5
|
+
type JSX,
|
|
6
|
+
type Component,
|
|
7
|
+
} from "solid-js";
|
|
8
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
|
+
import { Star } from "lucide-solid";
|
|
10
|
+
import { cn } from "@/lib/cn";
|
|
11
|
+
|
|
12
|
+
export const ratingVariants = cva(
|
|
13
|
+
"inline-flex items-center select-none transition-colors",
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
size: {
|
|
17
|
+
sm: "gap-0.5",
|
|
18
|
+
default: "gap-1",
|
|
19
|
+
lg: "gap-1.5",
|
|
20
|
+
},
|
|
21
|
+
variant: {
|
|
22
|
+
yellow: "text-amber-500 dark:text-amber-400",
|
|
23
|
+
primary: "text-primary",
|
|
24
|
+
destructive: "text-destructive",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
size: "default",
|
|
29
|
+
variant: "yellow",
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const ratingStarVariants = cva(
|
|
35
|
+
"transition-all duration-150 shrink-0",
|
|
36
|
+
{
|
|
37
|
+
variants: {
|
|
38
|
+
size: {
|
|
39
|
+
sm: "size-3.5",
|
|
40
|
+
default: "size-4.5",
|
|
41
|
+
lg: "size-6",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
size: "default",
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
export interface RatingProps
|
|
51
|
+
extends Omit<JSX.HTMLAttributes<HTMLDivElement>, "onChange">,
|
|
52
|
+
VariantProps<typeof ratingVariants> {
|
|
53
|
+
value?: number;
|
|
54
|
+
defaultValue?: number;
|
|
55
|
+
max?: number;
|
|
56
|
+
readOnly?: boolean;
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
class?: string;
|
|
59
|
+
starClass?: string;
|
|
60
|
+
onChange?: (value: number) => void;
|
|
61
|
+
onHover?: (value: number | null) => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const Rating: Component<RatingProps> = (props) => {
|
|
65
|
+
const [local, rest] = splitProps(props, [
|
|
66
|
+
"value",
|
|
67
|
+
"defaultValue",
|
|
68
|
+
"max",
|
|
69
|
+
"readOnly",
|
|
70
|
+
"disabled",
|
|
71
|
+
"size",
|
|
72
|
+
"variant",
|
|
73
|
+
"class",
|
|
74
|
+
"starClass",
|
|
75
|
+
"onChange",
|
|
76
|
+
"onHover",
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const [internalValue, setInternalValue] = createSignal(
|
|
80
|
+
local.defaultValue ?? 0
|
|
81
|
+
);
|
|
82
|
+
const [hoverValue, setHoverValue] = createSignal<number | null>(null);
|
|
83
|
+
|
|
84
|
+
const currentValue = () =>
|
|
85
|
+
local.value !== undefined ? local.value : internalValue();
|
|
86
|
+
|
|
87
|
+
const maxStars = () => local.max ?? 5;
|
|
88
|
+
const isInteractive = () => !local.readOnly && !local.disabled;
|
|
89
|
+
|
|
90
|
+
const stars = () => Array.from({ length: maxStars() }, (_, i) => i + 1);
|
|
91
|
+
|
|
92
|
+
const handleSelect = (starValue: number) => {
|
|
93
|
+
if (!isInteractive()) return;
|
|
94
|
+
if (local.value === undefined) {
|
|
95
|
+
setInternalValue(starValue);
|
|
96
|
+
}
|
|
97
|
+
local.onChange?.(starValue);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleMouseEnter = (starValue: number) => {
|
|
101
|
+
if (!isInteractive()) return;
|
|
102
|
+
setHoverValue(starValue);
|
|
103
|
+
local.onHover?.(starValue);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const handleMouseLeave = () => {
|
|
107
|
+
if (!isInteractive()) return;
|
|
108
|
+
setHoverValue(null);
|
|
109
|
+
local.onHover?.(null);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
113
|
+
if (!isInteractive()) return;
|
|
114
|
+
|
|
115
|
+
const val = currentValue();
|
|
116
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
const next = Math.min(maxStars(), val + 1);
|
|
119
|
+
handleSelect(next);
|
|
120
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
const prev = Math.max(1, val - 1);
|
|
123
|
+
handleSelect(prev);
|
|
124
|
+
} else if (e.key === "Home") {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
handleSelect(1);
|
|
127
|
+
} else if (e.key === "End") {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
handleSelect(maxStars());
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div
|
|
135
|
+
role={isInteractive() ? "radiogroup" : "img"}
|
|
136
|
+
aria-label={`Rating: ${currentValue()} of ${maxStars()} stars`}
|
|
137
|
+
tabIndex={isInteractive() ? 0 : undefined}
|
|
138
|
+
onKeyDown={handleKeyDown}
|
|
139
|
+
onMouseLeave={handleMouseLeave}
|
|
140
|
+
class={cn(
|
|
141
|
+
ratingVariants({ size: local.size, variant: local.variant }),
|
|
142
|
+
local.disabled && "opacity-50 cursor-not-allowed",
|
|
143
|
+
isInteractive() && "cursor-pointer focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-md",
|
|
144
|
+
local.class
|
|
145
|
+
)}
|
|
146
|
+
{...rest}
|
|
147
|
+
>
|
|
148
|
+
<For each={stars()}>
|
|
149
|
+
{(star) => {
|
|
150
|
+
const isFilled = () => {
|
|
151
|
+
const active = hoverValue() !== null ? hoverValue()! : currentValue();
|
|
152
|
+
return star <= active;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<button
|
|
157
|
+
type="button"
|
|
158
|
+
disabled={local.disabled || local.readOnly}
|
|
159
|
+
tabIndex={-1}
|
|
160
|
+
aria-label={`${star} star${star > 1 ? "s" : ""}`}
|
|
161
|
+
onClick={() => handleSelect(star)}
|
|
162
|
+
onMouseEnter={() => handleMouseEnter(star)}
|
|
163
|
+
class={cn(
|
|
164
|
+
"p-0.5 border-0 bg-transparent transition-transform focus:outline-hidden",
|
|
165
|
+
isInteractive() && "hover:scale-115 active:scale-95 cursor-pointer",
|
|
166
|
+
local.readOnly && "cursor-default",
|
|
167
|
+
local.disabled && "cursor-not-allowed"
|
|
168
|
+
)}
|
|
169
|
+
>
|
|
170
|
+
<Star
|
|
171
|
+
class={cn(
|
|
172
|
+
ratingStarVariants({ size: local.size }),
|
|
173
|
+
isFilled()
|
|
174
|
+
? "fill-current"
|
|
175
|
+
: "text-muted-foreground/30 fill-transparent",
|
|
176
|
+
local.starClass
|
|
177
|
+
)}
|
|
178
|
+
/>
|
|
179
|
+
</button>
|
|
180
|
+
);
|
|
181
|
+
}}
|
|
182
|
+
</For>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
splitProps,
|
|
3
|
+
Show,
|
|
4
|
+
type JSX,
|
|
5
|
+
type ParentComponent,
|
|
6
|
+
type Component,
|
|
7
|
+
} from "solid-js";
|
|
8
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
|
+
import { CheckCircle2 } from "lucide-solid";
|
|
10
|
+
import { cn } from "@/lib/cn";
|
|
11
|
+
import { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
12
|
+
import { Rating, type RatingProps } from "./rating";
|
|
13
|
+
|
|
14
|
+
/* --- 1. ReviewCard Root --- */
|
|
15
|
+
export const reviewCardVariants = cva(
|
|
16
|
+
"relative flex flex-col justify-between rounded-lg transition-all duration-200 overflow-hidden",
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
variant: {
|
|
20
|
+
default: "border border-border bg-card text-card-foreground p-4 sm:p-5 shadow-2xs hover:shadow-xs",
|
|
21
|
+
bordered: "border-2 border-border bg-background text-foreground p-4 sm:p-5",
|
|
22
|
+
flat: "bg-muted/40 text-foreground p-4 sm:p-5",
|
|
23
|
+
glass: "backdrop-blur-md bg-card/70 border border-border/80 text-card-foreground p-4 sm:p-5 shadow-2xs",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: "default",
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export interface ReviewCardProps
|
|
33
|
+
extends JSX.HTMLAttributes<HTMLDivElement>,
|
|
34
|
+
VariantProps<typeof reviewCardVariants> {
|
|
35
|
+
class?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const ReviewCard: ParentComponent<ReviewCardProps> = (props) => {
|
|
39
|
+
const [local, rest] = splitProps(props, ["class", "variant", "children"]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
class={cn(reviewCardVariants({ variant: local.variant }), local.class)}
|
|
44
|
+
{...rest}
|
|
45
|
+
>
|
|
46
|
+
{local.children}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/* --- 2. ReviewHeader --- */
|
|
52
|
+
export interface ReviewHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
53
|
+
class?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const ReviewHeader: ParentComponent<ReviewHeaderProps> = (props) => {
|
|
57
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
class={cn("flex items-start justify-between gap-3 mb-3 min-w-0 w-full", local.class)}
|
|
62
|
+
{...rest}
|
|
63
|
+
>
|
|
64
|
+
{local.children}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/* --- 3. ReviewProfile (Avatar + Names container) --- */
|
|
70
|
+
export interface ReviewProfileProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
71
|
+
class?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const ReviewProfile: ParentComponent<ReviewProfileProps> = (props) => {
|
|
75
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div class={cn("flex items-start gap-2.5 min-w-0 flex-1 overflow-hidden", local.class)} {...rest}>
|
|
79
|
+
{local.children}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/* --- 4. ReviewAvatar --- */
|
|
85
|
+
export interface ReviewAvatarProps {
|
|
86
|
+
src?: string;
|
|
87
|
+
alt?: string;
|
|
88
|
+
fallback?: string;
|
|
89
|
+
class?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const ReviewAvatar: Component<ReviewAvatarProps> = (props) => {
|
|
93
|
+
const [local] = splitProps(props, ["src", "alt", "fallback", "class"]);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Avatar class={cn("size-9 shrink-0 mt-0.5", local.class)}>
|
|
97
|
+
<Show when={local.src}>
|
|
98
|
+
<AvatarImage src={local.src!} alt={local.alt || "Reviewer Avatar"} />
|
|
99
|
+
</Show>
|
|
100
|
+
<AvatarFallback>
|
|
101
|
+
{local.fallback || (local.alt ? local.alt.slice(0, 2).toUpperCase() : "U")}
|
|
102
|
+
</AvatarFallback>
|
|
103
|
+
</Avatar>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/* --- 5. ReviewAuthor (Name & Subtitle/Role/Handle) --- */
|
|
108
|
+
export interface ReviewAuthorProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, "role"> {
|
|
109
|
+
name: string;
|
|
110
|
+
username?: string;
|
|
111
|
+
role?: string;
|
|
112
|
+
verified?: boolean;
|
|
113
|
+
class?: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const ReviewAuthor: Component<ReviewAuthorProps> = (props) => {
|
|
117
|
+
const [local, rest] = splitProps(props, [
|
|
118
|
+
"name",
|
|
119
|
+
"username",
|
|
120
|
+
"role",
|
|
121
|
+
"verified",
|
|
122
|
+
"class",
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div class={cn("flex flex-col min-w-0 flex-1 overflow-hidden", local.class)} {...rest}>
|
|
127
|
+
<div class="flex items-center gap-1 font-semibold text-sm leading-tight text-foreground truncate">
|
|
128
|
+
<span class="truncate">{local.name}</span>
|
|
129
|
+
<Show when={local.verified}>
|
|
130
|
+
<CheckCircle2 class="size-3.5 text-primary shrink-0" />
|
|
131
|
+
</Show>
|
|
132
|
+
</div>
|
|
133
|
+
<Show when={local.username || local.role}>
|
|
134
|
+
<p class="text-xs text-muted-foreground truncate mt-0.5">
|
|
135
|
+
{local.username || local.role}
|
|
136
|
+
</p>
|
|
137
|
+
</Show>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/* --- 6. ReviewRating (Composing standalone Rating) --- */
|
|
143
|
+
export interface ReviewRatingProps extends RatingProps {}
|
|
144
|
+
|
|
145
|
+
export const ReviewRating: Component<ReviewRatingProps> = (props) => {
|
|
146
|
+
const [local, rest] = splitProps(props, ["class", "size", "readOnly"]);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Rating
|
|
150
|
+
size={local.size || "sm"}
|
|
151
|
+
readOnly={local.readOnly !== undefined ? local.readOnly : true}
|
|
152
|
+
class={cn("shrink-0 select-none ml-auto pt-0.5", local.class)}
|
|
153
|
+
{...rest}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/* --- 7. ReviewBody (Quote / Content) --- */
|
|
159
|
+
export interface ReviewBodyProps extends JSX.HTMLAttributes<HTMLParagraphElement> {
|
|
160
|
+
class?: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const ReviewBody: ParentComponent<ReviewBodyProps> = (props) => {
|
|
164
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<p
|
|
168
|
+
class={cn("text-xs sm:text-sm text-muted-foreground leading-relaxed", local.class)}
|
|
169
|
+
{...rest}
|
|
170
|
+
>
|
|
171
|
+
{local.children}
|
|
172
|
+
</p>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/* --- 8. ReviewFooter --- */
|
|
177
|
+
export interface ReviewFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
178
|
+
class?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const ReviewFooter: ParentComponent<ReviewFooterProps> = (props) => {
|
|
182
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div
|
|
186
|
+
class={cn(
|
|
187
|
+
"mt-3 pt-3 border-t border-border/40 flex items-center justify-between text-xs text-muted-foreground",
|
|
188
|
+
local.class
|
|
189
|
+
)}
|
|
190
|
+
{...rest}
|
|
191
|
+
>
|
|
192
|
+
{local.children}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
@@ -183,7 +183,7 @@ export const ScrollArea: Component<ScrollAreaProps> = (props) => {
|
|
|
183
183
|
)}
|
|
184
184
|
>
|
|
185
185
|
<div
|
|
186
|
-
class="w-1.5 rounded-
|
|
186
|
+
class="w-1.5 rounded-lg bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
|
|
187
187
|
style={{
|
|
188
188
|
height: `${thumbHeight()}px`,
|
|
189
189
|
transform: `translateY(${thumbTop()}px)`,
|
|
@@ -204,7 +204,7 @@ export const ScrollArea: Component<ScrollAreaProps> = (props) => {
|
|
|
204
204
|
)}
|
|
205
205
|
>
|
|
206
206
|
<div
|
|
207
|
-
class="h-1.5 rounded-
|
|
207
|
+
class="h-1.5 rounded-lg bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
|
|
208
208
|
style={{
|
|
209
209
|
width: `${thumbWidth()}px`,
|
|
210
210
|
transform: `translateX(${thumbLeft()}px)`,
|