@tangle-network/ui 11.17.0 → 11.20.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/CHANGELOG.md +23 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -1
- package/dist/primitives/alert-dialog.d.ts +34 -0
- package/dist/primitives/alert-dialog.js +68 -0
- package/dist/primitives/checkbox.d.ts +12 -0
- package/dist/primitives/checkbox.js +34 -0
- package/dist/primitives/collapsible.d.ts +12 -0
- package/dist/primitives/collapsible.js +13 -0
- package/dist/primitives/context-menu.d.ts +36 -0
- package/dist/primitives/context-menu.js +86 -0
- package/dist/primitives/icon-tile.d.ts +6 -1
- package/dist/primitives/icon-tile.js +3 -1
- package/dist/primitives/popover.d.ts +15 -0
- package/dist/primitives/popover.js +25 -0
- package/dist/primitives/separator.d.ts +11 -0
- package/dist/primitives/separator.js +21 -0
- package/dist/primitives/sheet.d.ts +28 -0
- package/dist/primitives/sheet.js +70 -0
- package/dist/primitives/slider.d.ts +14 -0
- package/dist/primitives/slider.js +37 -0
- package/dist/primitives/theme-toggle.js +8 -0
- package/dist/primitives/tooltip.d.ts +16 -0
- package/dist/primitives/tooltip.js +32 -0
- package/dist/primitives.d.ts +10 -1
- package/dist/primitives.js +10 -1
- package/package.json +9 -2
- package/src/primitives/alert-dialog.tsx +139 -0
- package/src/primitives/checkbox-separator-sheet-tooltip.stories.tsx +92 -0
- package/src/primitives/checkbox-separator-sheet-tooltip.test.tsx +107 -0
- package/src/primitives/checkbox.tsx +42 -0
- package/src/primitives/collapsible.tsx +16 -0
- package/src/primitives/context-menu.tsx +168 -0
- package/src/primitives/icon-tile.stories.tsx +2 -2
- package/src/primitives/icon-tile.test.tsx +39 -0
- package/src/primitives/icon-tile.tsx +8 -1
- package/src/primitives/index.ts +57 -0
- package/src/primitives/popover-collapsible-alert-dialog-context-menu-slider.stories.tsx +124 -0
- package/src/primitives/popover-collapsible-alert-dialog-context-menu-slider.test.tsx +165 -0
- package/src/primitives/popover.tsx +45 -0
- package/src/primitives/separator.tsx +30 -0
- package/src/primitives/sheet.tsx +134 -0
- package/src/primitives/slider.tsx +71 -0
- package/src/primitives/theme-toggle.test.tsx +2 -1
- package/src/primitives/theme-toggle.tsx +2 -0
- package/src/primitives/tooltip.tsx +54 -0
- package/src/stories/theme-showcase.stories.tsx +2 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Popover = PopoverPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
12
|
+
|
|
13
|
+
const PopoverClose = PopoverPrimitive.Close;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A floating card anchored to its trigger, on the same surface, border and
|
|
17
|
+
* shadow as `DropdownMenuContent`. Width defaults to 18rem; pass a width class
|
|
18
|
+
* for wider content and keep long text wrapping inside it.
|
|
19
|
+
*/
|
|
20
|
+
const PopoverContent = React.forwardRef<
|
|
21
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
22
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
23
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
24
|
+
<PopoverPrimitive.Portal>
|
|
25
|
+
<PopoverPrimitive.Content
|
|
26
|
+
ref={ref}
|
|
27
|
+
align={align}
|
|
28
|
+
sideOffset={sideOffset}
|
|
29
|
+
className={cn(
|
|
30
|
+
"z-50 w-72 max-w-[calc(100vw-2rem)] origin-(--radix-popover-content-transform-origin) rounded-xl border border-border bg-card p-4 text-foreground shadow-[var(--shadow-card)] outline-hidden",
|
|
31
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
32
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
33
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
34
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
35
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
36
|
+
"motion-reduce:animate-none",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
</PopoverPrimitive.Portal>
|
|
42
|
+
));
|
|
43
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
44
|
+
|
|
45
|
+
export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A hairline rule on the border token. Decorative by default, so assistive
|
|
9
|
+
* technology skips it; pass `decorative={false}` when it separates groups a
|
|
10
|
+
* screen reader should announce.
|
|
11
|
+
*/
|
|
12
|
+
const Separator = React.forwardRef<
|
|
13
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
15
|
+
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
|
16
|
+
<SeparatorPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
decorative={decorative}
|
|
19
|
+
orientation={orientation}
|
|
20
|
+
className={cn(
|
|
21
|
+
"shrink-0 bg-border",
|
|
22
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
));
|
|
28
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
29
|
+
|
|
30
|
+
export { Separator };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { X } from "lucide-react";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { focusRing } from "../lib/focus";
|
|
8
|
+
import { cn } from "../lib/utils";
|
|
9
|
+
|
|
10
|
+
/** A dialog that slides in from an edge: a mobile nav drawer or a side panel. */
|
|
11
|
+
const Sheet = SheetPrimitive.Root;
|
|
12
|
+
|
|
13
|
+
const SheetTrigger = SheetPrimitive.Trigger;
|
|
14
|
+
|
|
15
|
+
const SheetClose = SheetPrimitive.Close;
|
|
16
|
+
|
|
17
|
+
const SheetPortal = SheetPrimitive.Portal;
|
|
18
|
+
|
|
19
|
+
const SheetOverlay = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<SheetPrimitive.Overlay
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn(
|
|
26
|
+
"fixed inset-0 z-50 bg-black/40",
|
|
27
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
28
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
));
|
|
34
|
+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
35
|
+
|
|
36
|
+
const sheetVariants = cva(
|
|
37
|
+
[
|
|
38
|
+
"fixed z-50 flex flex-col gap-4 border-border bg-card shadow-lg",
|
|
39
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
40
|
+
"data-[state=closed]:duration-200 data-[state=open]:duration-300 ease-out motion-reduce:animate-none",
|
|
41
|
+
],
|
|
42
|
+
{
|
|
43
|
+
variants: {
|
|
44
|
+
side: {
|
|
45
|
+
right:
|
|
46
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
47
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
48
|
+
top: "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
49
|
+
bottom:
|
|
50
|
+
"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: { side: "right" },
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const SheetContent = React.forwardRef<
|
|
58
|
+
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
59
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> &
|
|
60
|
+
VariantProps<typeof sheetVariants> & {
|
|
61
|
+
/** Hide only the built-in close button; Escape and outside clicks still dismiss. */
|
|
62
|
+
hideCloseButton?: boolean;
|
|
63
|
+
}
|
|
64
|
+
>(({ className, children, side = "right", hideCloseButton = false, ...props }, ref) => (
|
|
65
|
+
<SheetPortal>
|
|
66
|
+
<SheetOverlay />
|
|
67
|
+
<SheetPrimitive.Content
|
|
68
|
+
ref={ref}
|
|
69
|
+
className={cn(sheetVariants({ side }), className)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
{!hideCloseButton && (
|
|
74
|
+
<SheetPrimitive.Close
|
|
75
|
+
className={cn(
|
|
76
|
+
"absolute top-4 right-4 rounded-sm text-muted-foreground opacity-70 transition-[opacity,box-shadow] hover:opacity-100 focus-visible:opacity-100 disabled:pointer-events-none",
|
|
77
|
+
focusRing,
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<X className="size-4" />
|
|
81
|
+
<span className="sr-only">Close</span>
|
|
82
|
+
</SheetPrimitive.Close>
|
|
83
|
+
)}
|
|
84
|
+
</SheetPrimitive.Content>
|
|
85
|
+
</SheetPortal>
|
|
86
|
+
));
|
|
87
|
+
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
88
|
+
|
|
89
|
+
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
90
|
+
<div className={cn("flex flex-col gap-1.5 p-4 pr-10", className)} {...props} />
|
|
91
|
+
);
|
|
92
|
+
SheetHeader.displayName = "SheetHeader";
|
|
93
|
+
|
|
94
|
+
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
95
|
+
<div className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />
|
|
96
|
+
);
|
|
97
|
+
SheetFooter.displayName = "SheetFooter";
|
|
98
|
+
|
|
99
|
+
const SheetTitle = React.forwardRef<
|
|
100
|
+
React.ElementRef<typeof SheetPrimitive.Title>,
|
|
101
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
102
|
+
>(({ className, ...props }, ref) => (
|
|
103
|
+
<SheetPrimitive.Title
|
|
104
|
+
ref={ref}
|
|
105
|
+
className={cn("font-semibold text-base text-foreground leading-tight", className)}
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
));
|
|
109
|
+
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
110
|
+
|
|
111
|
+
const SheetDescription = React.forwardRef<
|
|
112
|
+
React.ElementRef<typeof SheetPrimitive.Description>,
|
|
113
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
|
114
|
+
>(({ className, ...props }, ref) => (
|
|
115
|
+
<SheetPrimitive.Description
|
|
116
|
+
ref={ref}
|
|
117
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
));
|
|
121
|
+
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
122
|
+
|
|
123
|
+
export {
|
|
124
|
+
Sheet,
|
|
125
|
+
SheetClose,
|
|
126
|
+
SheetContent,
|
|
127
|
+
SheetDescription,
|
|
128
|
+
SheetFooter,
|
|
129
|
+
SheetHeader,
|
|
130
|
+
SheetOverlay,
|
|
131
|
+
SheetPortal,
|
|
132
|
+
SheetTitle,
|
|
133
|
+
SheetTrigger,
|
|
134
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { focusRing } from "../lib/focus";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Range input with one thumb per value. A single value renders one thumb; an
|
|
10
|
+
* array of two renders a range. The thumbs are what assistive technology
|
|
11
|
+
* focuses, so the names go on them: `aria-label` / `aria-labelledby` name a
|
|
12
|
+
* single thumb, and `thumbLabels` names each thumb of a range.
|
|
13
|
+
*/
|
|
14
|
+
const Slider = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & {
|
|
17
|
+
thumbLabels?: string[];
|
|
18
|
+
}
|
|
19
|
+
>(
|
|
20
|
+
(
|
|
21
|
+
{
|
|
22
|
+
className,
|
|
23
|
+
defaultValue,
|
|
24
|
+
value,
|
|
25
|
+
min = 0,
|
|
26
|
+
max = 100,
|
|
27
|
+
thumbLabels,
|
|
28
|
+
"aria-label": ariaLabel,
|
|
29
|
+
"aria-labelledby": ariaLabelledBy,
|
|
30
|
+
...props
|
|
31
|
+
},
|
|
32
|
+
ref,
|
|
33
|
+
) => {
|
|
34
|
+
const values = Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min];
|
|
35
|
+
const single = values.length === 1;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<SliderPrimitive.Root
|
|
39
|
+
ref={ref}
|
|
40
|
+
defaultValue={defaultValue}
|
|
41
|
+
value={value}
|
|
42
|
+
min={min}
|
|
43
|
+
max={max}
|
|
44
|
+
className={cn(
|
|
45
|
+
"relative flex w-full touch-none select-none items-center data-[disabled]:opacity-50",
|
|
46
|
+
"data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
47
|
+
className,
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
<SliderPrimitive.Track className="relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5">
|
|
52
|
+
<SliderPrimitive.Range className="absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full" />
|
|
53
|
+
</SliderPrimitive.Track>
|
|
54
|
+
{values.map((_, index) => (
|
|
55
|
+
<SliderPrimitive.Thumb
|
|
56
|
+
key={index}
|
|
57
|
+
aria-label={thumbLabels?.[index] ?? (single ? ariaLabel : undefined)}
|
|
58
|
+
aria-labelledby={single && !thumbLabels?.[index] ? ariaLabelledBy : undefined}
|
|
59
|
+
className={cn(
|
|
60
|
+
"block size-4 shrink-0 rounded-full border border-primary bg-background shadow-sm transition-[box-shadow] disabled:pointer-events-none",
|
|
61
|
+
focusRing,
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</SliderPrimitive.Root>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
70
|
+
|
|
71
|
+
export { Slider };
|
|
@@ -101,7 +101,8 @@ describe("root mode preference", () => {
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it.each([
|
|
104
|
-
["aubergine", "aubergine-light"], ["arena", "arena-light"], ["tangle-dark", "tangle-light"],
|
|
104
|
+
["aubergine", "aubergine-light"], ["arena", "arena-light"], ["super", "super-light"], ["tangle-dark", "tangle-light"],
|
|
105
|
+
["agents", "agents-light"],
|
|
105
106
|
])("keeps the %s named family in both directions", (name, lightName) => {
|
|
106
107
|
document.documentElement.dataset.theme = name;
|
|
107
108
|
render(<ThemeToggle />);
|
|
@@ -10,7 +10,9 @@ const systemQuery = "(prefers-color-scheme: dark)";
|
|
|
10
10
|
const namedModes = [
|
|
11
11
|
{ dark: "aubergine", light: "aubergine-light" },
|
|
12
12
|
{ dark: "arena", light: "arena-light" },
|
|
13
|
+
{ dark: "super", light: "super-light" },
|
|
13
14
|
{ dark: "tangle-dark", light: "tangle-light" },
|
|
15
|
+
{ dark: "agents", light: "agents-light" },
|
|
14
16
|
] as const;
|
|
15
17
|
|
|
16
18
|
// Client-only preference cache also keeps the control usable when storage is
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wrap an app (or a toolbar) once. The open delay applies to the first tooltip;
|
|
9
|
+
* moving between triggers inside `skipDelayDuration` opens the next one at
|
|
10
|
+
* once, which keeps an icon rail fast to scan.
|
|
11
|
+
*/
|
|
12
|
+
const TooltipProvider = ({
|
|
13
|
+
delayDuration = 400,
|
|
14
|
+
skipDelayDuration = 300,
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) => (
|
|
17
|
+
<TooltipPrimitive.Provider
|
|
18
|
+
delayDuration={delayDuration}
|
|
19
|
+
skipDelayDuration={skipDelayDuration}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
TooltipProvider.displayName = "TooltipProvider";
|
|
24
|
+
|
|
25
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
26
|
+
|
|
27
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Inverse-surface label for a control whose name is otherwise only an icon.
|
|
31
|
+
* It is supplementary: the trigger still needs its own accessible name.
|
|
32
|
+
*/
|
|
33
|
+
const TooltipContent = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
35
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
36
|
+
>(({ className, sideOffset = 6, ...props }, ref) => (
|
|
37
|
+
<TooltipPrimitive.Portal>
|
|
38
|
+
<TooltipPrimitive.Content
|
|
39
|
+
ref={ref}
|
|
40
|
+
sideOffset={sideOffset}
|
|
41
|
+
className={cn(
|
|
42
|
+
"z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground px-2.5 py-1.5 text-background text-xs leading-snug shadow-md",
|
|
43
|
+
"data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95",
|
|
44
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
45
|
+
"duration-150 ease-out motion-reduce:animate-none",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
</TooltipPrimitive.Portal>
|
|
51
|
+
));
|
|
52
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
53
|
+
|
|
54
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
@@ -17,6 +17,8 @@ const THEMES: { name: string; theme?: string }[] = [
|
|
|
17
17
|
{ name: "Aubergine · light", theme: "aubergine-light" },
|
|
18
18
|
{ name: "Experimental green", theme: "arena" },
|
|
19
19
|
{ name: "Experimental green · light", theme: "arena-light" },
|
|
20
|
+
{ name: "Super (forest)", theme: "super" },
|
|
21
|
+
{ name: "Super · light", theme: "super-light" },
|
|
20
22
|
];
|
|
21
23
|
|
|
22
24
|
const NOW = Date.now();
|