@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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/registry/bubble.json +18 -0
  3. package/registry/button-group.json +20 -0
  4. package/registry/create-chat-scroll.json +13 -0
  5. package/registry/create-drop-zone.json +13 -0
  6. package/registry/create-pagination.json +13 -0
  7. package/registry/dropzone.json +21 -0
  8. package/registry/footer.json +18 -0
  9. package/registry/forgot-password-01.json +28 -0
  10. package/registry/hero-01.json +22 -0
  11. package/registry/index.json +331 -0
  12. package/registry/login-01.json +28 -0
  13. package/registry/marker.json +17 -0
  14. package/registry/marquee.json +17 -0
  15. package/registry/message.json +17 -0
  16. package/registry/navbar.json +19 -0
  17. package/registry/navigation-menu.json +22 -0
  18. package/registry/otp-verification-01.json +26 -0
  19. package/registry/pagination.json +22 -0
  20. package/registry/rating.json +19 -0
  21. package/registry/register-01.json +31 -0
  22. package/registry/review-card.json +23 -0
  23. package/registry/scroll-area.json +1 -1
  24. package/registry/sidebar.json +24 -0
  25. package/registry/spinner.json +1 -1
  26. package/registry/stat.json +19 -0
  27. package/registry/table.json +17 -0
  28. package/registry/timeline.json +18 -0
  29. package/registry/toggle-group.json +21 -0
  30. package/src/registry/blocks/forgot-password-01.tsx +141 -0
  31. package/src/registry/blocks/hero-01.tsx +28 -0
  32. package/src/registry/blocks/login-01.tsx +231 -0
  33. package/src/registry/blocks/otp-verification-01.tsx +170 -0
  34. package/src/registry/blocks/register-01.tsx +318 -0
  35. package/src/registry/components/ui/bubble.tsx +142 -0
  36. package/src/registry/components/ui/button-group.tsx +42 -0
  37. package/src/registry/components/ui/dropzone.tsx +189 -0
  38. package/src/registry/components/ui/footer.tsx +247 -0
  39. package/src/registry/components/ui/marker.tsx +102 -0
  40. package/src/registry/components/ui/marquee.tsx +118 -0
  41. package/src/registry/components/ui/message.tsx +168 -0
  42. package/src/registry/components/ui/navbar.tsx +368 -0
  43. package/src/registry/components/ui/navigation-menu.tsx +358 -0
  44. package/src/registry/components/ui/pagination.tsx +258 -0
  45. package/src/registry/components/ui/rating.tsx +185 -0
  46. package/src/registry/components/ui/review-card.tsx +195 -0
  47. package/src/registry/components/ui/scroll-area.tsx +2 -2
  48. package/src/registry/components/ui/sidebar.tsx +692 -0
  49. package/src/registry/components/ui/spinner.tsx +1 -1
  50. package/src/registry/components/ui/stat.tsx +245 -0
  51. package/src/registry/components/ui/table.tsx +160 -0
  52. package/src/registry/components/ui/timeline.tsx +350 -0
  53. package/src/registry/components/ui/toggle-group.tsx +203 -0
  54. package/src/registry/index.ts +3 -3
  55. 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-full bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
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-full bg-border hover:bg-muted-foreground/50 transition-colors cursor-pointer pointer-events-auto"
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)`,