@blibliki/ui 0.9.2

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.
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+
5
+ const ROOT = process.cwd();
6
+ const SRC_DIR = join(ROOT, "src");
7
+
8
+ const CSS_EXTENSION = ".css";
9
+ const IGNORED_FILES = new Set(["tokens.css", "src/tokens.css"]);
10
+
11
+ const RAW_COLOR_PATTERN =
12
+ /#(?:[\da-fA-F]{3,4}|[\da-fA-F]{6}|[\da-fA-F]{8})\b|(?:rgb|rgba|hsl|hsla|oklch)\(/;
13
+
14
+ function collectCssFiles(directory) {
15
+ const entries = readdirSync(directory, { withFileTypes: true });
16
+ const files = [];
17
+
18
+ for (const entry of entries) {
19
+ const fullPath = join(directory, entry.name);
20
+ if (entry.isDirectory()) {
21
+ files.push(...collectCssFiles(fullPath));
22
+ continue;
23
+ }
24
+
25
+ if (entry.isFile() && entry.name.endsWith(CSS_EXTENSION)) {
26
+ files.push(fullPath);
27
+ }
28
+ }
29
+
30
+ return files;
31
+ }
32
+
33
+ function checkCssFile(absolutePath) {
34
+ const packageRelativePath = relative(ROOT, absolutePath);
35
+ if (IGNORED_FILES.has(packageRelativePath)) {
36
+ return [];
37
+ }
38
+
39
+ const source = readFileSync(absolutePath, "utf8");
40
+ const lines = source.split("\n");
41
+ const violations = [];
42
+
43
+ for (let index = 0; index < lines.length; index += 1) {
44
+ const line = lines[index];
45
+ if (line.includes("palette-ignore-line")) {
46
+ continue;
47
+ }
48
+
49
+ if (!RAW_COLOR_PATTERN.test(line)) {
50
+ continue;
51
+ }
52
+
53
+ violations.push({
54
+ file: packageRelativePath,
55
+ line: index + 1,
56
+ content: line.trim(),
57
+ });
58
+ }
59
+
60
+ return violations;
61
+ }
62
+
63
+ function main() {
64
+ if (!existsSync(SRC_DIR) || !statSync(SRC_DIR).isDirectory()) {
65
+ console.log("No src directory found, palette check skipped.");
66
+ return;
67
+ }
68
+
69
+ const cssFiles = collectCssFiles(SRC_DIR);
70
+ const violations = cssFiles.flatMap((file) => checkCssFile(file));
71
+
72
+ if (violations.length === 0) {
73
+ console.log("CSS palette check passed: no raw color literals found.");
74
+ return;
75
+ }
76
+
77
+ console.error("CSS palette check failed.");
78
+ console.error("Use variables from tokens.css instead of raw color values.");
79
+ console.error(
80
+ "Add 'palette-ignore-line' on a line only when you intentionally need an exception.",
81
+ );
82
+ console.error("");
83
+
84
+ for (const violation of violations) {
85
+ console.error(`${violation.file}:${violation.line}`);
86
+ console.error(` ${violation.content}`);
87
+ }
88
+
89
+ process.exitCode = 1;
90
+ }
91
+
92
+ main();
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/cn";
3
+ import {
4
+ createTheme,
5
+ type UIMode,
6
+ type UITheme,
7
+ themeToCssVariables,
8
+ } from "@/theme";
9
+
10
+ export interface UIProviderProps {
11
+ children: React.ReactNode;
12
+ mode?: UIMode;
13
+ theme?: UITheme;
14
+ className?: string;
15
+ }
16
+
17
+ export function UIProvider({
18
+ children,
19
+ mode = "light",
20
+ theme,
21
+ className,
22
+ }: UIProviderProps): React.JSX.Element {
23
+ const resolvedTheme = React.useMemo(() => createTheme(theme), [theme]);
24
+ const variables = React.useMemo(
25
+ () => themeToCssVariables(resolvedTheme, mode),
26
+ [resolvedTheme, mode],
27
+ );
28
+
29
+ return (
30
+ <div
31
+ className={cn(mode === "dark" && "dark", className)}
32
+ data-theme={mode}
33
+ style={variables}
34
+ >
35
+ {children}
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,57 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { forwardRef, type HTMLAttributes } from "react";
4
+ import { cn } from "@/lib/cn";
5
+
6
+ const badgeVariants = cva("ui-badge", {
7
+ variants: {
8
+ tone: {
9
+ neutral: "ui-badge--tone-neutral",
10
+ primary: "ui-badge--tone-primary",
11
+ secondary: "ui-badge--tone-secondary",
12
+ success: "ui-badge--tone-success",
13
+ warning: "ui-badge--tone-warning",
14
+ error: "ui-badge--tone-error",
15
+ info: "ui-badge--tone-info",
16
+ },
17
+ variant: {
18
+ soft: "ui-badge--variant-soft",
19
+ solid: "ui-badge--variant-solid",
20
+ outline: "ui-badge--variant-outline",
21
+ },
22
+ size: {
23
+ sm: "ui-badge--size-sm",
24
+ md: "ui-badge--size-md",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ tone: "neutral",
29
+ variant: "soft",
30
+ size: "md",
31
+ },
32
+ });
33
+
34
+ type BadgeElement = HTMLSpanElement;
35
+
36
+ export interface BadgeProps
37
+ extends HTMLAttributes<BadgeElement>, VariantProps<typeof badgeVariants> {
38
+ asChild?: boolean;
39
+ }
40
+
41
+ const Badge = forwardRef<BadgeElement, BadgeProps>(
42
+ ({ className, tone, variant, size, asChild = false, ...props }, ref) => {
43
+ const Comp = asChild ? Slot : "span";
44
+
45
+ return (
46
+ <Comp
47
+ ref={ref}
48
+ className={cn(badgeVariants({ tone, variant, size }), className)}
49
+ {...props}
50
+ />
51
+ );
52
+ },
53
+ );
54
+
55
+ Badge.displayName = "Badge";
56
+
57
+ export { Badge, badgeVariants };
@@ -0,0 +1,65 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+ import { cn } from "@/lib/cn";
5
+
6
+ const buttonVariants = cva("ui-button", {
7
+ variants: {
8
+ variant: {
9
+ contained: "ui-button--variant-contained",
10
+ outlined: "ui-button--variant-outlined",
11
+ text: "ui-button--variant-text",
12
+ },
13
+ color: {
14
+ primary: "ui-button--color-primary",
15
+ neutral: "ui-button--color-neutral",
16
+ secondary: "ui-button--color-secondary",
17
+ error: "ui-button--color-error",
18
+ warning: "ui-button--color-warning",
19
+ info: "ui-button--color-info",
20
+ success: "ui-button--color-success",
21
+ },
22
+ size: {
23
+ md: "ui-button--size-md",
24
+ sm: "ui-button--size-sm",
25
+ lg: "ui-button--size-lg",
26
+ icon: "ui-button--size-icon",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "contained",
31
+ color: "primary",
32
+ size: "md",
33
+ },
34
+ });
35
+
36
+ type ButtonElement = HTMLButtonElement;
37
+
38
+ export interface ButtonProps
39
+ extends
40
+ Omit<React.ButtonHTMLAttributes<ButtonElement>, "color">,
41
+ VariantProps<typeof buttonVariants> {
42
+ asChild?: boolean;
43
+ }
44
+
45
+ const Button = React.forwardRef<ButtonElement, ButtonProps>(
46
+ (
47
+ { className, variant, color, size, asChild = false, type, ...props },
48
+ ref,
49
+ ) => {
50
+ const Comp = asChild ? Slot : "button";
51
+
52
+ return (
53
+ <Comp
54
+ ref={ref}
55
+ className={cn(buttonVariants({ variant, color, size }), className)}
56
+ type={asChild ? undefined : (type ?? "button")}
57
+ {...props}
58
+ />
59
+ );
60
+ },
61
+ );
62
+
63
+ Button.displayName = "Button";
64
+
65
+ export { Button, buttonVariants };
@@ -0,0 +1,44 @@
1
+ import { type ComponentProps, forwardRef } from "react";
2
+ import { cn } from "@/lib/cn";
3
+
4
+ const Card = forwardRef<HTMLDivElement, ComponentProps<"div">>(
5
+ ({ className, ...props }, ref) => {
6
+ return <div ref={ref} className={cn("ui-card", className)} {...props} />;
7
+ },
8
+ );
9
+
10
+ Card.displayName = "Card";
11
+
12
+ function CardHeader({ className, ...props }: ComponentProps<"div">) {
13
+ return <div className={cn("ui-card-header", className)} {...props} />;
14
+ }
15
+
16
+ function CardTitle({ className, ...props }: ComponentProps<"div">) {
17
+ return <div className={cn("ui-card-title", className)} {...props} />;
18
+ }
19
+
20
+ function CardDescription({ className, ...props }: ComponentProps<"div">) {
21
+ return <div className={cn("ui-card-description", className)} {...props} />;
22
+ }
23
+
24
+ function CardAction({ className, ...props }: ComponentProps<"div">) {
25
+ return <div className={cn("ui-card-action", className)} {...props} />;
26
+ }
27
+
28
+ function CardContent({ className, ...props }: ComponentProps<"div">) {
29
+ return <div className={cn("ui-card-content", className)} {...props} />;
30
+ }
31
+
32
+ function CardFooter({ className, ...props }: ComponentProps<"div">) {
33
+ return <div className={cn("ui-card-footer", className)} {...props} />;
34
+ }
35
+
36
+ export {
37
+ Card,
38
+ CardAction,
39
+ CardContent,
40
+ CardDescription,
41
+ CardFooter,
42
+ CardHeader,
43
+ CardTitle,
44
+ };
@@ -0,0 +1,239 @@
1
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
2
+ import type { ComponentProps } from "react";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ function MenuCheckIcon(props: ComponentProps<"svg">) {
6
+ return (
7
+ <svg viewBox="0 0 16 16" fill="none" aria-hidden {...props}>
8
+ <path
9
+ d="M3.5 8.5 6.5 11.5 12.5 4.5"
10
+ stroke="currentColor"
11
+ strokeWidth="1.75"
12
+ strokeLinecap="round"
13
+ strokeLinejoin="round"
14
+ />
15
+ </svg>
16
+ );
17
+ }
18
+
19
+ function MenuDotIcon(props: ComponentProps<"svg">) {
20
+ return (
21
+ <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden {...props}>
22
+ <circle cx="8" cy="8" r="3" />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ function MenuChevronRightIcon(props: ComponentProps<"svg">) {
28
+ return (
29
+ <svg viewBox="0 0 16 16" fill="none" aria-hidden {...props}>
30
+ <path
31
+ d="M6 3.5 10.5 8 6 12.5"
32
+ stroke="currentColor"
33
+ strokeWidth="1.5"
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ function ContextMenu(props: ComponentProps<typeof ContextMenuPrimitive.Root>) {
42
+ return <ContextMenuPrimitive.Root {...props} />;
43
+ }
44
+
45
+ function ContextMenuTrigger(
46
+ props: ComponentProps<typeof ContextMenuPrimitive.Trigger>,
47
+ ) {
48
+ return <ContextMenuPrimitive.Trigger {...props} />;
49
+ }
50
+
51
+ function ContextMenuGroup(
52
+ props: ComponentProps<typeof ContextMenuPrimitive.Group>,
53
+ ) {
54
+ return <ContextMenuPrimitive.Group {...props} />;
55
+ }
56
+
57
+ function ContextMenuPortal(
58
+ props: ComponentProps<typeof ContextMenuPrimitive.Portal>,
59
+ ) {
60
+ return <ContextMenuPrimitive.Portal {...props} />;
61
+ }
62
+
63
+ function ContextMenuSub(
64
+ props: ComponentProps<typeof ContextMenuPrimitive.Sub>,
65
+ ) {
66
+ return <ContextMenuPrimitive.Sub {...props} />;
67
+ }
68
+
69
+ function ContextMenuRadioGroup(
70
+ props: ComponentProps<typeof ContextMenuPrimitive.RadioGroup>,
71
+ ) {
72
+ return <ContextMenuPrimitive.RadioGroup {...props} />;
73
+ }
74
+
75
+ function ContextMenuSubTrigger({
76
+ className,
77
+ inset = false,
78
+ children,
79
+ ...props
80
+ }: ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
81
+ inset?: boolean;
82
+ }) {
83
+ return (
84
+ <ContextMenuPrimitive.SubTrigger
85
+ data-inset={inset ? "true" : undefined}
86
+ className={cn("ui-dropdown-item ui-dropdown-sub-trigger", className)}
87
+ {...props}
88
+ >
89
+ {children}
90
+ <MenuChevronRightIcon className="ui-dropdown-chevron" />
91
+ </ContextMenuPrimitive.SubTrigger>
92
+ );
93
+ }
94
+
95
+ function ContextMenuSubContent({
96
+ className,
97
+ ...props
98
+ }: ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
99
+ return (
100
+ <ContextMenuPrimitive.SubContent
101
+ className={cn(
102
+ "ui-dropdown-content ui-dropdown-sub-content ui-context-content ui-context-sub-content",
103
+ className,
104
+ )}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+
110
+ function ContextMenuContent({
111
+ className,
112
+ ...props
113
+ }: ComponentProps<typeof ContextMenuPrimitive.Content>) {
114
+ return (
115
+ <ContextMenuPrimitive.Portal>
116
+ <ContextMenuPrimitive.Content
117
+ className={cn("ui-dropdown-content ui-context-content", className)}
118
+ {...props}
119
+ />
120
+ </ContextMenuPrimitive.Portal>
121
+ );
122
+ }
123
+
124
+ function ContextMenuItem({
125
+ className,
126
+ inset = false,
127
+ variant = "default",
128
+ ...props
129
+ }: ComponentProps<typeof ContextMenuPrimitive.Item> & {
130
+ inset?: boolean;
131
+ variant?: "default" | "destructive";
132
+ }) {
133
+ return (
134
+ <ContextMenuPrimitive.Item
135
+ data-inset={inset ? "true" : undefined}
136
+ data-variant={variant}
137
+ className={cn("ui-dropdown-item", className)}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function ContextMenuCheckboxItem({
144
+ className,
145
+ children,
146
+ checked,
147
+ ...props
148
+ }: ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
149
+ return (
150
+ <ContextMenuPrimitive.CheckboxItem
151
+ checked={checked}
152
+ className={cn(
153
+ "ui-dropdown-item ui-dropdown-item--with-indicator",
154
+ className,
155
+ )}
156
+ {...props}
157
+ >
158
+ <span className="ui-dropdown-indicator" aria-hidden>
159
+ <ContextMenuPrimitive.ItemIndicator>
160
+ <MenuCheckIcon className="ui-dropdown-indicator-icon" />
161
+ </ContextMenuPrimitive.ItemIndicator>
162
+ </span>
163
+ {children}
164
+ </ContextMenuPrimitive.CheckboxItem>
165
+ );
166
+ }
167
+
168
+ function ContextMenuRadioItem({
169
+ className,
170
+ children,
171
+ ...props
172
+ }: ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
173
+ return (
174
+ <ContextMenuPrimitive.RadioItem
175
+ className={cn(
176
+ "ui-dropdown-item ui-dropdown-item--with-indicator",
177
+ className,
178
+ )}
179
+ {...props}
180
+ >
181
+ <span className="ui-dropdown-indicator" aria-hidden>
182
+ <ContextMenuPrimitive.ItemIndicator>
183
+ <MenuDotIcon className="ui-dropdown-indicator-icon" />
184
+ </ContextMenuPrimitive.ItemIndicator>
185
+ </span>
186
+ {children}
187
+ </ContextMenuPrimitive.RadioItem>
188
+ );
189
+ }
190
+
191
+ function ContextMenuLabel({
192
+ className,
193
+ inset = false,
194
+ ...props
195
+ }: ComponentProps<typeof ContextMenuPrimitive.Label> & {
196
+ inset?: boolean;
197
+ }) {
198
+ return (
199
+ <ContextMenuPrimitive.Label
200
+ data-inset={inset ? "true" : undefined}
201
+ className={cn("ui-dropdown-label", className)}
202
+ {...props}
203
+ />
204
+ );
205
+ }
206
+
207
+ function ContextMenuSeparator({
208
+ className,
209
+ ...props
210
+ }: ComponentProps<typeof ContextMenuPrimitive.Separator>) {
211
+ return (
212
+ <ContextMenuPrimitive.Separator
213
+ className={cn("ui-dropdown-separator", className)}
214
+ {...props}
215
+ />
216
+ );
217
+ }
218
+
219
+ function ContextMenuShortcut({ className, ...props }: ComponentProps<"span">) {
220
+ return <span className={cn("ui-dropdown-shortcut", className)} {...props} />;
221
+ }
222
+
223
+ export {
224
+ ContextMenu,
225
+ ContextMenuTrigger,
226
+ ContextMenuContent,
227
+ ContextMenuItem,
228
+ ContextMenuCheckboxItem,
229
+ ContextMenuRadioItem,
230
+ ContextMenuLabel,
231
+ ContextMenuSeparator,
232
+ ContextMenuShortcut,
233
+ ContextMenuGroup,
234
+ ContextMenuPortal,
235
+ ContextMenuSub,
236
+ ContextMenuSubContent,
237
+ ContextMenuSubTrigger,
238
+ ContextMenuRadioGroup,
239
+ };
@@ -0,0 +1,127 @@
1
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
2
+ import * as React from "react";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ function Dialog(props: React.ComponentProps<typeof DialogPrimitive.Root>) {
6
+ return <DialogPrimitive.Root {...props} />;
7
+ }
8
+
9
+ function DialogTrigger(
10
+ props: React.ComponentProps<typeof DialogPrimitive.Trigger>,
11
+ ) {
12
+ return <DialogPrimitive.Trigger {...props} />;
13
+ }
14
+
15
+ function DialogPortal(
16
+ props: React.ComponentProps<typeof DialogPrimitive.Portal>,
17
+ ) {
18
+ return <DialogPrimitive.Portal {...props} />;
19
+ }
20
+
21
+ function DialogClose(
22
+ props: React.ComponentProps<typeof DialogPrimitive.Close>,
23
+ ) {
24
+ return <DialogPrimitive.Close {...props} />;
25
+ }
26
+
27
+ function DialogOverlay({
28
+ className,
29
+ ...props
30
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
31
+ return (
32
+ <DialogPrimitive.Overlay
33
+ className={cn("ui-dialog-overlay", className)}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function DialogCloseIcon(props: React.ComponentProps<"svg">) {
40
+ return (
41
+ <svg viewBox="0 0 16 16" fill="none" aria-hidden {...props}>
42
+ <path
43
+ d="M3.5 3.5 12.5 12.5M12.5 3.5 3.5 12.5"
44
+ stroke="currentColor"
45
+ strokeWidth="1.5"
46
+ strokeLinecap="round"
47
+ />
48
+ </svg>
49
+ );
50
+ }
51
+
52
+ interface DialogContentProps extends React.ComponentProps<
53
+ typeof DialogPrimitive.Content
54
+ > {
55
+ hideCloseButton?: boolean;
56
+ }
57
+
58
+ function DialogContent({
59
+ className,
60
+ children,
61
+ hideCloseButton = false,
62
+ ...props
63
+ }: DialogContentProps) {
64
+ return (
65
+ <DialogPortal>
66
+ <DialogOverlay />
67
+ <DialogPrimitive.Content
68
+ className={cn("ui-dialog-content", className)}
69
+ {...props}
70
+ >
71
+ {children}
72
+ {!hideCloseButton && (
73
+ <DialogPrimitive.Close className="ui-dialog-close">
74
+ <DialogCloseIcon className="ui-dialog-close-icon" />
75
+ <span className="ui-visually-hidden">Close</span>
76
+ </DialogPrimitive.Close>
77
+ )}
78
+ </DialogPrimitive.Content>
79
+ </DialogPortal>
80
+ );
81
+ }
82
+
83
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
84
+ return <div className={cn("ui-dialog-header", className)} {...props} />;
85
+ }
86
+
87
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
88
+ return <div className={cn("ui-dialog-footer", className)} {...props} />;
89
+ }
90
+
91
+ function DialogTitle({
92
+ className,
93
+ ...props
94
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
95
+ return (
96
+ <DialogPrimitive.Title
97
+ className={cn("ui-dialog-title", className)}
98
+ {...props}
99
+ />
100
+ );
101
+ }
102
+
103
+ function DialogDescription({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
107
+ return (
108
+ <DialogPrimitive.Description
109
+ className={cn("ui-dialog-description", className)}
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
115
+ export {
116
+ Dialog,
117
+ DialogPortal,
118
+ DialogOverlay,
119
+ DialogClose,
120
+ DialogTrigger,
121
+ DialogContent,
122
+ DialogHeader,
123
+ DialogFooter,
124
+ DialogTitle,
125
+ DialogDescription,
126
+ type DialogContentProps,
127
+ };
@@ -0,0 +1,45 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ const dividerVariants = cva("ui-divider", {
6
+ variants: {
7
+ orientation: {
8
+ horizontal: "ui-divider--horizontal",
9
+ vertical: "ui-divider--vertical",
10
+ },
11
+ tone: {
12
+ subtle: "ui-divider--tone-subtle",
13
+ strong: "ui-divider--tone-strong",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ orientation: "horizontal",
18
+ tone: "subtle",
19
+ },
20
+ });
21
+
22
+ type DividerElement = HTMLDivElement;
23
+
24
+ export interface DividerProps
25
+ extends
26
+ React.HTMLAttributes<DividerElement>,
27
+ VariantProps<typeof dividerVariants> {}
28
+
29
+ const Divider = React.forwardRef<DividerElement, DividerProps>(
30
+ ({ className, orientation, tone, ...props }, ref) => {
31
+ return (
32
+ <div
33
+ ref={ref}
34
+ role="separator"
35
+ aria-orientation={orientation ?? "horizontal"}
36
+ className={cn(dividerVariants({ orientation, tone }), className)}
37
+ {...props}
38
+ />
39
+ );
40
+ },
41
+ );
42
+
43
+ Divider.displayName = "Divider";
44
+
45
+ export { Divider, dividerVariants };