@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,247 @@
|
|
|
1
|
+
import { splitProps, type JSX, type ParentComponent } from "solid-js";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
|
|
5
|
+
/* --- Root Footer --- */
|
|
6
|
+
export const footerVariants = cva("w-full transition-colors", {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "bg-background border-t border-border/60 text-foreground",
|
|
10
|
+
muted: "bg-muted/40 border-t border-border text-foreground",
|
|
11
|
+
bordered: "bg-background border-t-2 border-border text-foreground",
|
|
12
|
+
floating: "my-8 rounded-lg border border-border/80 bg-card text-card-foreground shadow-xs",
|
|
13
|
+
transparent: "bg-transparent border-transparent text-foreground",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: "default",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export interface FooterProps
|
|
22
|
+
extends JSX.HTMLAttributes<HTMLElement>,
|
|
23
|
+
VariantProps<typeof footerVariants> {
|
|
24
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Footer: ParentComponent<FooterProps> = (props) => {
|
|
28
|
+
const [local, rest] = splitProps(props, ["class", "variant", "maxWidth", "children"]);
|
|
29
|
+
|
|
30
|
+
const maxWidthClass = () => {
|
|
31
|
+
switch (local.maxWidth) {
|
|
32
|
+
case "sm":
|
|
33
|
+
return "max-w-screen-sm";
|
|
34
|
+
case "md":
|
|
35
|
+
return "max-w-screen-md";
|
|
36
|
+
case "lg":
|
|
37
|
+
return "max-w-screen-lg";
|
|
38
|
+
case "xl":
|
|
39
|
+
return "max-w-screen-xl";
|
|
40
|
+
case "full":
|
|
41
|
+
return "max-w-full";
|
|
42
|
+
case "2xl":
|
|
43
|
+
default:
|
|
44
|
+
return "max-w-screen-2xl";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<footer
|
|
50
|
+
class={cn(
|
|
51
|
+
footerVariants({ variant: local.variant }),
|
|
52
|
+
local.variant === "floating" && cn("mx-auto", maxWidthClass()),
|
|
53
|
+
local.class
|
|
54
|
+
)}
|
|
55
|
+
{...rest}
|
|
56
|
+
>
|
|
57
|
+
<div class={cn("w-full", local.variant !== "floating" && cn("mx-auto", maxWidthClass()))}>
|
|
58
|
+
{local.children}
|
|
59
|
+
</div>
|
|
60
|
+
</footer>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/* --- Footer Container --- */
|
|
65
|
+
export interface FooterContainerProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
66
|
+
class?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const FooterContainer: ParentComponent<FooterContainerProps> = (props) => {
|
|
70
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
class={cn("px-4 sm:px-6 lg:px-8 py-10 sm:py-12 md:py-16 w-full", local.class)}
|
|
75
|
+
{...rest}
|
|
76
|
+
>
|
|
77
|
+
{local.children}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/* --- Footer Content (Columns Grid / Layout) --- */
|
|
83
|
+
export interface FooterContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
84
|
+
class?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const FooterContent: ParentComponent<FooterContentProps> = (props) => {
|
|
88
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div
|
|
92
|
+
class={cn(
|
|
93
|
+
"grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8 lg:gap-12",
|
|
94
|
+
local.class
|
|
95
|
+
)}
|
|
96
|
+
{...rest}
|
|
97
|
+
>
|
|
98
|
+
{local.children}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/* --- Footer Column --- */
|
|
104
|
+
export interface FooterColumnProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
105
|
+
class?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const FooterColumn: ParentComponent<FooterColumnProps> = (props) => {
|
|
109
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div class={cn("flex flex-col space-y-3", local.class)} {...rest}>
|
|
113
|
+
{local.children}
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/* --- Footer Column Title --- */
|
|
119
|
+
export interface FooterColumnTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {
|
|
120
|
+
class?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const FooterColumnTitle: ParentComponent<FooterColumnTitleProps> = (props) => {
|
|
124
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<h4
|
|
128
|
+
class={cn(
|
|
129
|
+
"text-xs font-semibold uppercase tracking-wider text-foreground",
|
|
130
|
+
local.class
|
|
131
|
+
)}
|
|
132
|
+
{...rest}
|
|
133
|
+
>
|
|
134
|
+
{local.children}
|
|
135
|
+
</h4>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/* --- Footer Column List --- */
|
|
140
|
+
export interface FooterColumnListProps extends JSX.HTMLAttributes<HTMLUListElement> {
|
|
141
|
+
class?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const FooterColumnList: ParentComponent<FooterColumnListProps> = (props) => {
|
|
145
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<ul class={cn("space-y-2 list-none p-0 m-0", local.class)} {...rest}>
|
|
149
|
+
{local.children}
|
|
150
|
+
</ul>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/* --- Footer Link --- */
|
|
155
|
+
export interface FooterLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
156
|
+
class?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const FooterLink: ParentComponent<FooterLinkProps> = (props) => {
|
|
160
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<li>
|
|
164
|
+
<a
|
|
165
|
+
class={cn(
|
|
166
|
+
"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
|
|
167
|
+
local.class
|
|
168
|
+
)}
|
|
169
|
+
{...rest}
|
|
170
|
+
>
|
|
171
|
+
{local.children}
|
|
172
|
+
</a>
|
|
173
|
+
</li>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/* --- Footer Brand / Info Section --- */
|
|
178
|
+
export interface FooterBrandProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
179
|
+
class?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const FooterBrand: ParentComponent<FooterBrandProps> = (props) => {
|
|
183
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<div
|
|
187
|
+
class={cn(
|
|
188
|
+
"col-span-2 sm:col-span-2 md:col-span-4 lg:col-span-2 flex flex-col space-y-3 mb-4 lg:mb-0",
|
|
189
|
+
local.class
|
|
190
|
+
)}
|
|
191
|
+
{...rest}
|
|
192
|
+
>
|
|
193
|
+
{local.children}
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/* --- Footer Bottom (Copyright and Secondary Links Bar) --- */
|
|
199
|
+
export interface FooterBottomProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
200
|
+
class?: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const FooterBottom: ParentComponent<FooterBottomProps> = (props) => {
|
|
204
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div
|
|
208
|
+
class={cn(
|
|
209
|
+
"mt-12 sm:mt-16 pt-8 border-t border-border/50 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-muted-foreground",
|
|
210
|
+
local.class
|
|
211
|
+
)}
|
|
212
|
+
{...rest}
|
|
213
|
+
>
|
|
214
|
+
{local.children}
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/* --- Footer Copyright Text --- */
|
|
220
|
+
export interface FooterCopyrightProps extends JSX.HTMLAttributes<HTMLParagraphElement> {
|
|
221
|
+
class?: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export const FooterCopyright: ParentComponent<FooterCopyrightProps> = (props) => {
|
|
225
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<p class={cn("text-xs text-muted-foreground leading-relaxed", local.class)} {...rest}>
|
|
229
|
+
{local.children}
|
|
230
|
+
</p>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/* --- Footer Social Links Container --- */
|
|
235
|
+
export interface FooterSocialsProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
236
|
+
class?: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export const FooterSocials: ParentComponent<FooterSocialsProps> = (props) => {
|
|
240
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div class={cn("flex items-center gap-3 text-muted-foreground", local.class)} {...rest}>
|
|
244
|
+
{local.children}
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
splitProps,
|
|
3
|
+
type Component,
|
|
4
|
+
type JSX,
|
|
5
|
+
type ParentComponent,
|
|
6
|
+
Show,
|
|
7
|
+
} from "solid-js";
|
|
8
|
+
import { cn } from "@/lib/cn";
|
|
9
|
+
|
|
10
|
+
/* --- 1. Marker Root --- */
|
|
11
|
+
export interface MarkerProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Marker: ParentComponent<MarkerProps> = (props) => {
|
|
16
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
role="status"
|
|
21
|
+
class={cn("flex w-full items-center justify-center my-3 text-center select-none", local.class)}
|
|
22
|
+
{...rest}
|
|
23
|
+
>
|
|
24
|
+
{local.children}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/* --- 2. MarkerContent --- */
|
|
30
|
+
export interface MarkerContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
31
|
+
class?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const MarkerContent: ParentComponent<MarkerContentProps> = (props) => {
|
|
35
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
class={cn(
|
|
40
|
+
"inline-flex items-center gap-1.5 rounded-lg border border-border/60 bg-muted/40 px-3 py-1 text-xs text-muted-foreground shadow-2xs backdrop-blur-xs",
|
|
41
|
+
local.class
|
|
42
|
+
)}
|
|
43
|
+
{...rest}
|
|
44
|
+
>
|
|
45
|
+
{local.children}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/* --- 3. MarkerDate (Date Divider) --- */
|
|
51
|
+
export interface MarkerDateProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
52
|
+
date?: string;
|
|
53
|
+
class?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const MarkerDate: ParentComponent<MarkerDateProps> = (props) => {
|
|
57
|
+
const [local, rest] = splitProps(props, ["date", "class", "children"]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
class={cn("relative flex w-full items-center justify-center my-4", local.class)}
|
|
62
|
+
{...rest}
|
|
63
|
+
>
|
|
64
|
+
<div class="absolute inset-0 flex items-center">
|
|
65
|
+
<div class="w-full border-t border-border/50" />
|
|
66
|
+
</div>
|
|
67
|
+
<div class="relative flex items-center gap-1 bg-background px-3 py-0.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground rounded-md border border-border/40">
|
|
68
|
+
{local.children || local.date}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/* --- 4. MarkerTyping (Live Typing Indicator) --- */
|
|
75
|
+
export interface MarkerTypingProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
76
|
+
name?: string;
|
|
77
|
+
class?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const MarkerTyping: Component<MarkerTypingProps> = (props) => {
|
|
81
|
+
const [local, rest] = splitProps(props, ["name", "class"]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
role="status"
|
|
86
|
+
aria-label={`${local.name || "Someone"} is typing`}
|
|
87
|
+
class={cn("flex w-full items-center gap-2 text-xs text-muted-foreground my-2", local.class)}
|
|
88
|
+
{...rest}
|
|
89
|
+
>
|
|
90
|
+
<Show when={local.name}>
|
|
91
|
+
<span class="font-medium text-foreground">{local.name}</span> is typing
|
|
92
|
+
</Show>
|
|
93
|
+
|
|
94
|
+
{/* 3 Animated Bouncing Dots */}
|
|
95
|
+
<span class="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-muted/60 border border-border/40">
|
|
96
|
+
<span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce [animation-delay:-0.3s]" />
|
|
97
|
+
<span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce [animation-delay:-0.15s]" />
|
|
98
|
+
<span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce" />
|
|
99
|
+
</span>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
splitProps,
|
|
3
|
+
createMemo,
|
|
4
|
+
Index,
|
|
5
|
+
type JSX,
|
|
6
|
+
type ParentComponent,
|
|
7
|
+
} from "solid-js";
|
|
8
|
+
import { cn } from "@/lib/cn";
|
|
9
|
+
|
|
10
|
+
export interface MarqueeProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
class?: string;
|
|
12
|
+
reverse?: boolean;
|
|
13
|
+
pauseOnHover?: boolean;
|
|
14
|
+
vertical?: boolean;
|
|
15
|
+
repeat?: number;
|
|
16
|
+
duration?: number | string;
|
|
17
|
+
gap?: string;
|
|
18
|
+
fadeEdges?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Marquee: ParentComponent<MarqueeProps> = (props) => {
|
|
22
|
+
const [local, rest] = splitProps(props, [
|
|
23
|
+
"class",
|
|
24
|
+
"reverse",
|
|
25
|
+
"pauseOnHover",
|
|
26
|
+
"vertical",
|
|
27
|
+
"repeat",
|
|
28
|
+
"duration",
|
|
29
|
+
"gap",
|
|
30
|
+
"fadeEdges",
|
|
31
|
+
"children",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const repeatCount = () => local.repeat ?? 4;
|
|
35
|
+
const durationValue = () =>
|
|
36
|
+
typeof local.duration === "number"
|
|
37
|
+
? `${local.duration}s`
|
|
38
|
+
: local.duration || "40s";
|
|
39
|
+
const gapValue = () => local.gap || "1rem";
|
|
40
|
+
|
|
41
|
+
const repeatArray = createMemo(() =>
|
|
42
|
+
Array.from({ length: repeatCount() }, (_, i) => i)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<style>{`
|
|
48
|
+
@keyframes nikala-marquee {
|
|
49
|
+
from {
|
|
50
|
+
transform: translateX(0);
|
|
51
|
+
}
|
|
52
|
+
to {
|
|
53
|
+
transform: translateX(calc(-100% - var(--marquee-gap, 1rem)));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
@keyframes nikala-marquee-vertical {
|
|
57
|
+
from {
|
|
58
|
+
transform: translateY(0);
|
|
59
|
+
}
|
|
60
|
+
to {
|
|
61
|
+
transform: translateY(calc(-100% - var(--marquee-gap, 1rem)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
.nikala-marquee-track {
|
|
65
|
+
animation: nikala-marquee var(--marquee-duration, 40s) linear infinite;
|
|
66
|
+
}
|
|
67
|
+
.nikala-marquee-track-vertical {
|
|
68
|
+
animation: nikala-marquee-vertical var(--marquee-duration, 40s) linear infinite;
|
|
69
|
+
}
|
|
70
|
+
[data-reverse="true"] .nikala-marquee-track,
|
|
71
|
+
[data-reverse="true"] .nikala-marquee-track-vertical {
|
|
72
|
+
animation-direction: reverse;
|
|
73
|
+
}
|
|
74
|
+
[data-pause="true"]:hover .nikala-marquee-track,
|
|
75
|
+
[data-pause="true"]:hover .nikala-marquee-track-vertical {
|
|
76
|
+
animation-play-state: paused;
|
|
77
|
+
}
|
|
78
|
+
`}</style>
|
|
79
|
+
<div
|
|
80
|
+
data-reverse={local.reverse ? "true" : "false"}
|
|
81
|
+
data-pause={local.pauseOnHover ? "true" : "false"}
|
|
82
|
+
style={{
|
|
83
|
+
"--marquee-duration": durationValue(),
|
|
84
|
+
"--marquee-gap": gapValue(),
|
|
85
|
+
}}
|
|
86
|
+
class={cn(
|
|
87
|
+
"group flex overflow-hidden p-2 select-none",
|
|
88
|
+
local.vertical ? "flex-col h-full" : "flex-row w-full",
|
|
89
|
+
local.fadeEdges &&
|
|
90
|
+
(local.vertical
|
|
91
|
+
? "[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]"
|
|
92
|
+
: "[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]"),
|
|
93
|
+
local.class
|
|
94
|
+
)}
|
|
95
|
+
{...rest}
|
|
96
|
+
>
|
|
97
|
+
<Index each={repeatArray()}>
|
|
98
|
+
{() => (
|
|
99
|
+
<div
|
|
100
|
+
class={cn(
|
|
101
|
+
"flex shrink-0 justify-around items-center",
|
|
102
|
+
local.vertical
|
|
103
|
+
? "flex-col min-h-full nikala-marquee-track-vertical"
|
|
104
|
+
: "flex-row min-w-full nikala-marquee-track"
|
|
105
|
+
)}
|
|
106
|
+
style={{
|
|
107
|
+
gap: gapValue(),
|
|
108
|
+
[local.vertical ? "margin-bottom" : "margin-right"]: gapValue(),
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
{local.children}
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
</Index>
|
|
115
|
+
</div>
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
splitProps,
|
|
5
|
+
type Component,
|
|
6
|
+
type JSX,
|
|
7
|
+
type ParentComponent,
|
|
8
|
+
type Accessor,
|
|
9
|
+
} from "solid-js";
|
|
10
|
+
import { cn } from "@/lib/cn";
|
|
11
|
+
|
|
12
|
+
/* --- 1. Message Context --- */
|
|
13
|
+
export type MessageAlignment = "start" | "end";
|
|
14
|
+
|
|
15
|
+
interface MessageContextValue {
|
|
16
|
+
align: Accessor<MessageAlignment>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MessageContext = createContext<MessageContextValue>();
|
|
20
|
+
|
|
21
|
+
export function useMessage() {
|
|
22
|
+
const context = useContext(MessageContext);
|
|
23
|
+
if (!context) {
|
|
24
|
+
throw new Error("useMessage must be used within a <Message />");
|
|
25
|
+
}
|
|
26
|
+
return context;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* --- 2. Message Root --- */
|
|
30
|
+
export interface MessageProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
31
|
+
align?: MessageAlignment;
|
|
32
|
+
class?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Message: ParentComponent<MessageProps> = (props) => {
|
|
36
|
+
const [local, rest] = splitProps(props, ["align", "class", "children"]);
|
|
37
|
+
const align = () => local.align ?? "start";
|
|
38
|
+
|
|
39
|
+
const contextValue: MessageContextValue = {
|
|
40
|
+
align,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<MessageContext.Provider value={contextValue}>
|
|
45
|
+
<div
|
|
46
|
+
data-align={align()}
|
|
47
|
+
class={cn(
|
|
48
|
+
"group/message flex w-full gap-3",
|
|
49
|
+
align() === "end" ? "flex-row-reverse items-start" : "flex-row items-start",
|
|
50
|
+
local.class
|
|
51
|
+
)}
|
|
52
|
+
{...rest}
|
|
53
|
+
>
|
|
54
|
+
{local.children}
|
|
55
|
+
</div>
|
|
56
|
+
</MessageContext.Provider>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/* --- 3. MessageAvatar --- */
|
|
61
|
+
export interface MessageAvatarProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
62
|
+
class?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const MessageAvatar: ParentComponent<MessageAvatarProps> = (props) => {
|
|
66
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
class={cn("shrink-0 select-none pt-0.5", local.class)}
|
|
71
|
+
{...rest}
|
|
72
|
+
>
|
|
73
|
+
{local.children}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/* --- 4. MessageHeader --- */
|
|
79
|
+
export interface MessageHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
80
|
+
class?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const MessageHeader: ParentComponent<MessageHeaderProps> = (props) => {
|
|
84
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
85
|
+
const { align } = useMessage();
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
class={cn(
|
|
90
|
+
"flex items-center gap-2 text-xs text-muted-foreground select-none mb-1",
|
|
91
|
+
align() === "end" ? "justify-end" : "justify-start",
|
|
92
|
+
local.class
|
|
93
|
+
)}
|
|
94
|
+
{...rest}
|
|
95
|
+
>
|
|
96
|
+
{local.children}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/* --- 5. MessageContent --- */
|
|
102
|
+
export interface MessageContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
103
|
+
class?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const MessageContent: ParentComponent<MessageContentProps> = (props) => {
|
|
107
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
108
|
+
const { align } = useMessage();
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div
|
|
112
|
+
class={cn(
|
|
113
|
+
"flex flex-col gap-1.5 max-w-[85%] sm:max-w-[75%]",
|
|
114
|
+
align() === "end" ? "items-end text-right" : "items-start text-left",
|
|
115
|
+
local.class
|
|
116
|
+
)}
|
|
117
|
+
{...rest}
|
|
118
|
+
>
|
|
119
|
+
{local.children}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/* --- 6. MessageFooter --- */
|
|
125
|
+
export interface MessageFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
126
|
+
class?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const MessageFooter: ParentComponent<MessageFooterProps> = (props) => {
|
|
130
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
131
|
+
const { align } = useMessage();
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div
|
|
135
|
+
class={cn(
|
|
136
|
+
"flex items-center gap-1.5 text-[11px] text-muted-foreground select-none mt-1",
|
|
137
|
+
align() === "end" ? "justify-end" : "justify-start",
|
|
138
|
+
local.class
|
|
139
|
+
)}
|
|
140
|
+
{...rest}
|
|
141
|
+
>
|
|
142
|
+
{local.children}
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/* --- 7. MessageActions --- */
|
|
148
|
+
export interface MessageActionsProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
149
|
+
class?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const MessageActions: ParentComponent<MessageActionsProps> = (props) => {
|
|
153
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
154
|
+
const { align } = useMessage();
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div
|
|
158
|
+
class={cn(
|
|
159
|
+
"opacity-0 group-hover/message:opacity-100 focus-within:opacity-100 transition-opacity flex items-center gap-1 p-0.5 rounded-md border border-border bg-background shadow-xs",
|
|
160
|
+
align() === "end" ? "flex-row-reverse" : "flex-row",
|
|
161
|
+
local.class
|
|
162
|
+
)}
|
|
163
|
+
{...rest}
|
|
164
|
+
>
|
|
165
|
+
{local.children}
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
};
|