@stasho/ds 0.4.1 → 0.6.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stasho/ds",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "stasho design system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"exports": {
|
|
25
25
|
"./alert": "./src/components/alert/alert.tsx",
|
|
26
26
|
"./button": "./src/components/button/button.tsx",
|
|
27
|
+
"./icon-button": "./src/components/button/icon-button.tsx",
|
|
27
28
|
"./checkbox": "./src/components/checkbox/checkbox.tsx",
|
|
28
29
|
"./input": "./src/components/input/input.tsx",
|
|
29
30
|
"./textarea": "./src/components/textarea/textarea.tsx",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"./combobox": "./src/components/combobox/combobox.tsx",
|
|
41
42
|
"./slider": "./src/components/slider/slider.tsx",
|
|
42
43
|
"./radio-group": "./src/components/radio-group/radio-group.tsx",
|
|
44
|
+
"./selectable-card": "./src/components/selectable-card/selectable-card.tsx",
|
|
43
45
|
"./ui/skeleton": "./src/components/ui/skeleton.tsx",
|
|
44
46
|
"./multi-select": "./src/components/multi-select/multi-select.tsx",
|
|
45
47
|
"./pagination": "./src/components/pagination/pagination.tsx",
|
|
@@ -101,6 +101,14 @@ const buttonVariants = cva(
|
|
|
101
101
|
"dark:hover:bg-white/[0.04] dark:hover:text-white",
|
|
102
102
|
"dark:disabled:text-white/30 dark:disabled:bg-transparent",
|
|
103
103
|
].join(" "),
|
|
104
|
+
link: [
|
|
105
|
+
// chrome-less inline text action: text-primary, underline on hover, no LED, no padding (zeroed per size below)
|
|
106
|
+
"bg-transparent font-normal underline-offset-2 text-primary",
|
|
107
|
+
"hover:underline active:translate-y-0",
|
|
108
|
+
"dark:text-accent",
|
|
109
|
+
"disabled:text-foreground/30 disabled:no-underline disabled:hover:no-underline",
|
|
110
|
+
"dark:disabled:text-white/30",
|
|
111
|
+
].join(" "),
|
|
104
112
|
},
|
|
105
113
|
size: {
|
|
106
114
|
xs: "py-[6px] px-3 text-[11px] gap-1.5",
|
|
@@ -115,6 +123,10 @@ const buttonVariants = cva(
|
|
|
115
123
|
{ variant: "outline", size: "sm", class: "py-[6px] px-[13px]" },
|
|
116
124
|
{ variant: "outline", size: "md", class: "py-[8px] px-[17px]" },
|
|
117
125
|
{ variant: "outline", size: "lg", class: "py-[12px] px-[23px]" },
|
|
126
|
+
{ variant: "link", size: "xs", class: "p-0" },
|
|
127
|
+
{ variant: "link", size: "sm", class: "p-0" },
|
|
128
|
+
{ variant: "link", size: "md", class: "p-0" },
|
|
129
|
+
{ variant: "link", size: "lg", class: "p-0" },
|
|
118
130
|
],
|
|
119
131
|
defaultVariants: {
|
|
120
132
|
variant: "primary",
|
|
@@ -152,6 +164,8 @@ const ledColorClass: Record<Variant, string> = {
|
|
|
152
164
|
outline: "bg-primary/35 text-primary dark:bg-accent/50 dark:text-accent",
|
|
153
165
|
// ghost: LED is never rendered for ghost, so this entry is a sentinel.
|
|
154
166
|
ghost: "",
|
|
167
|
+
// link: LED is never rendered for link, so this entry is a sentinel.
|
|
168
|
+
link: "",
|
|
155
169
|
};
|
|
156
170
|
|
|
157
171
|
// iconLeft glow treatment per variant — applied to the wrapper span.
|
|
@@ -163,6 +177,7 @@ const iconGlowClass: Record<Variant, string> = {
|
|
|
163
177
|
success: "text-success-foreground",
|
|
164
178
|
outline: "text-primary dark:text-accent [filter:drop-shadow(0_0_4px_currentColor)]",
|
|
165
179
|
ghost: "text-foreground/60 dark:text-white/60",
|
|
180
|
+
link: "text-primary dark:text-accent",
|
|
166
181
|
};
|
|
167
182
|
|
|
168
183
|
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
|
@@ -199,8 +214,8 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
199
214
|
);
|
|
200
215
|
|
|
201
216
|
const leadingSlot = (() => {
|
|
202
|
-
// Loading replaces everything in the leading slot (except on ghost).
|
|
203
|
-
if (loading && v !== "ghost") {
|
|
217
|
+
// Loading replaces everything in the leading slot (except on ghost/link).
|
|
218
|
+
if (loading && v !== "ghost" && v !== "link") {
|
|
204
219
|
return (
|
|
205
220
|
<span
|
|
206
221
|
data-led-chase
|
|
@@ -241,8 +256,8 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
241
256
|
</span>
|
|
242
257
|
);
|
|
243
258
|
}
|
|
244
|
-
// Resting state with static LED (non-ghost variants).
|
|
245
|
-
if (v !== "ghost") {
|
|
259
|
+
// Resting state with static LED (non-ghost/link variants).
|
|
260
|
+
if (v !== "ghost" && v !== "link") {
|
|
246
261
|
return (
|
|
247
262
|
<span
|
|
248
263
|
data-led
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@ac/lib/cn";
|
|
4
|
+
import { buttonVariants } from "./button";
|
|
5
|
+
|
|
6
|
+
// Square inset per size. The glyph dimensions are consumer-controlled (e.g. <Bell size={18} />);
|
|
7
|
+
// the button stays square because the padding is symmetric.
|
|
8
|
+
const squareSizeClass = {
|
|
9
|
+
xs: "p-1",
|
|
10
|
+
sm: "p-1.5",
|
|
11
|
+
md: "p-2",
|
|
12
|
+
lg: "p-2.5",
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
type Variant = NonNullable<VariantProps<typeof buttonVariants>["variant"]>;
|
|
16
|
+
type Size = keyof typeof squareSizeClass;
|
|
17
|
+
|
|
18
|
+
// aria-label is required: an icon-only button must carry an accessible name.
|
|
19
|
+
type IconButtonProps = Omit<
|
|
20
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
21
|
+
"aria-label"
|
|
22
|
+
> & {
|
|
23
|
+
variant?: Variant;
|
|
24
|
+
size?: Size;
|
|
25
|
+
"aria-label": string;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
30
|
+
({ variant = "ghost", size = "md", className, children, ...rest }, ref) => (
|
|
31
|
+
<button
|
|
32
|
+
ref={ref}
|
|
33
|
+
type="button"
|
|
34
|
+
className={cn(
|
|
35
|
+
// reuse Button's color/chassis; tailwind-merge collapses the default
|
|
36
|
+
// rectangular size padding into the square p-* appended after it.
|
|
37
|
+
buttonVariants({ variant }),
|
|
38
|
+
"justify-center shrink-0",
|
|
39
|
+
squareSizeClass[size],
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
{...rest}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</button>
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
IconButton.displayName = "IconButton";
|
|
50
|
+
|
|
51
|
+
export { IconButton, type IconButtonProps };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
type ButtonHTMLAttributes,
|
|
4
|
+
type ComponentPropsWithoutRef,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
|
|
8
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
|
+
import { Check } from "@phosphor-icons/react";
|
|
10
|
+
import { cn } from "@ac/lib/cn";
|
|
11
|
+
|
|
12
|
+
const selectableCardVariants = cva(
|
|
13
|
+
[
|
|
14
|
+
"relative rounded-2xl border border-edge bg-surface text-left",
|
|
15
|
+
"transition-[colors,box-shadow]",
|
|
16
|
+
"hover:border-edge-hover",
|
|
17
|
+
"focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2",
|
|
18
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-edge",
|
|
19
|
+
// selected (data-[state=on]) — only SelectableCard receives this state
|
|
20
|
+
"data-[state=on]:border-accent data-[state=on]:bg-accent/5",
|
|
21
|
+
].join(" "),
|
|
22
|
+
{
|
|
23
|
+
variants: {
|
|
24
|
+
padding: {
|
|
25
|
+
sm: "p-4",
|
|
26
|
+
md: "p-6",
|
|
27
|
+
lg: "p-8",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
padding: "md",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
type CardPadding = VariantProps<typeof selectableCardVariants>;
|
|
37
|
+
|
|
38
|
+
type ActionCardProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
|
39
|
+
CardPadding & {
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const ActionCard = forwardRef<HTMLButtonElement, ActionCardProps>(
|
|
44
|
+
({ padding, className, children, type, ...rest }, ref) => (
|
|
45
|
+
<button
|
|
46
|
+
ref={ref}
|
|
47
|
+
type={type ?? "button"}
|
|
48
|
+
className={cn(selectableCardVariants({ padding }), className)}
|
|
49
|
+
{...rest}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</button>
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
ActionCard.displayName = "ActionCard";
|
|
56
|
+
|
|
57
|
+
type SelectableCardGroupProps = ComponentPropsWithoutRef<
|
|
58
|
+
typeof ToggleGroupPrimitive.Root
|
|
59
|
+
>;
|
|
60
|
+
|
|
61
|
+
const SelectableCardGroup = forwardRef<
|
|
62
|
+
HTMLDivElement,
|
|
63
|
+
SelectableCardGroupProps
|
|
64
|
+
>(({ className, ...rest }, ref) => (
|
|
65
|
+
<ToggleGroupPrimitive.Root ref={ref} className={className} {...rest} />
|
|
66
|
+
));
|
|
67
|
+
SelectableCardGroup.displayName = "SelectableCardGroup";
|
|
68
|
+
|
|
69
|
+
type SelectableCardProps = ComponentPropsWithoutRef<
|
|
70
|
+
typeof ToggleGroupPrimitive.Item
|
|
71
|
+
> &
|
|
72
|
+
CardPadding;
|
|
73
|
+
|
|
74
|
+
const SelectableCard = forwardRef<HTMLButtonElement, SelectableCardProps>(
|
|
75
|
+
({ padding, className, children, ...rest }, ref) => (
|
|
76
|
+
<ToggleGroupPrimitive.Item
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn(selectableCardVariants({ padding }), className)}
|
|
79
|
+
{...rest}
|
|
80
|
+
>
|
|
81
|
+
<span
|
|
82
|
+
data-testid="selectable-card-check"
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
className={cn(
|
|
85
|
+
"absolute right-3 top-3 text-accent",
|
|
86
|
+
"opacity-0 transition-opacity",
|
|
87
|
+
"[[data-state=on]_&]:opacity-100",
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
<Check weight="bold" />
|
|
91
|
+
</span>
|
|
92
|
+
{children}
|
|
93
|
+
</ToggleGroupPrimitive.Item>
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
SelectableCard.displayName = "SelectableCard";
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
ActionCard,
|
|
100
|
+
SelectableCard,
|
|
101
|
+
SelectableCardGroup,
|
|
102
|
+
selectableCardVariants,
|
|
103
|
+
type ActionCardProps,
|
|
104
|
+
type SelectableCardProps,
|
|
105
|
+
type SelectableCardGroupProps,
|
|
106
|
+
};
|