@neoptocom/neopto-ui 0.5.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/CONSUMER_SETUP.md +41 -58
- package/README.md +2 -23
- package/dist/index.cjs +80 -95
- package/dist/index.d.cts +27 -223
- package/dist/index.d.ts +27 -223
- package/dist/index.js +80 -95
- package/package.json +2 -9
- package/src/components/Avatar.tsx +15 -15
- package/src/components/Button.tsx +40 -32
- package/src/components/IconButton.tsx +37 -36
- package/src/components/Typo.tsx +43 -37
- package/scripts/init.mjs +0 -201
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ import * as React2 from 'react';
|
|
|
2
2
|
import { useState, useMemo, useId, useRef, useCallback, useEffect } from 'react';
|
|
3
3
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
-
import { tv } from 'tailwind-variants';
|
|
6
5
|
|
|
7
6
|
// src/components/Input.tsx
|
|
8
7
|
var Input = React2.forwardRef(
|
|
@@ -94,41 +93,40 @@ function Modal({
|
|
|
94
93
|
const container = document.body;
|
|
95
94
|
return createPortal(overlay, container);
|
|
96
95
|
}
|
|
97
|
-
|
|
98
|
-
base
|
|
99
|
-
variants
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
96
|
+
function getTypoClasses(variant, weight = "normal", muted, className) {
|
|
97
|
+
const base = "text-current";
|
|
98
|
+
const variants = {
|
|
99
|
+
"display-lg": "text-5xl leading-tight",
|
|
100
|
+
"display-md": "text-4xl leading-tight",
|
|
101
|
+
"display-sm": "text-4xl leading-tight",
|
|
102
|
+
"headline-lg": "text-3xl leading-tight",
|
|
103
|
+
"headline-md": "text-3xl leading-tight",
|
|
104
|
+
"headline-sm": "text-3xl leading-tight",
|
|
105
|
+
"title-lg": "text-xl leading-tight",
|
|
106
|
+
"title-md": "text-lg leading-tight",
|
|
107
|
+
"title-sm": "text-base leading-tight",
|
|
108
|
+
"label-lg": "text-sm leading-tight",
|
|
109
|
+
"label-md": "text-xs leading-tight",
|
|
110
|
+
"label-sm": "text-xs leading-tight",
|
|
111
|
+
"body-lg": "text-base leading-relaxed",
|
|
112
|
+
"body-md": "text-sm leading-relaxed",
|
|
113
|
+
"body-sm": "text-xs leading-relaxed",
|
|
114
|
+
"button": "text-base leading-normal"
|
|
115
|
+
};
|
|
116
|
+
const weights = {
|
|
117
|
+
normal: "font-normal",
|
|
118
|
+
medium: "font-medium",
|
|
119
|
+
semibold: "font-semibold",
|
|
120
|
+
bold: "font-bold"
|
|
121
|
+
};
|
|
122
|
+
return [
|
|
123
|
+
base,
|
|
124
|
+
variants[variant],
|
|
125
|
+
weights[weight],
|
|
126
|
+
muted ? "text-[var(--muted-fg)]" : "",
|
|
127
|
+
className
|
|
128
|
+
].filter(Boolean).join(" ");
|
|
129
|
+
}
|
|
132
130
|
function Typo({
|
|
133
131
|
variant,
|
|
134
132
|
bold,
|
|
@@ -148,23 +146,21 @@ function Typo({
|
|
|
148
146
|
return /* @__PURE__ */ jsx(
|
|
149
147
|
Component,
|
|
150
148
|
{
|
|
151
|
-
className:
|
|
149
|
+
className: getTypoClasses(variant, bold, muted, className),
|
|
152
150
|
style: { fontFamily: getFontFamily(variant) },
|
|
153
151
|
...props,
|
|
154
152
|
children
|
|
155
153
|
}
|
|
156
154
|
);
|
|
157
155
|
}
|
|
158
|
-
|
|
159
|
-
base
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
defaultVariants: { size: "sm" }
|
|
167
|
-
});
|
|
156
|
+
function getAvatarClasses(size = "sm", className) {
|
|
157
|
+
const base = "relative box-border flex items-center justify-center overflow-hidden rounded-full border border-[var(--border)] bg-[var(--muted)] text-[var(--fg)] select-none";
|
|
158
|
+
const sizes = {
|
|
159
|
+
sm: "w-[28px] h-[28px]",
|
|
160
|
+
md: "w-[60px] h-[60px]"
|
|
161
|
+
};
|
|
162
|
+
return [base, sizes[size], className].filter(Boolean).join(" ");
|
|
163
|
+
}
|
|
168
164
|
function getInitials(name) {
|
|
169
165
|
if (!name) return "\u2026";
|
|
170
166
|
const words = name.trim().split(/\s+/);
|
|
@@ -193,7 +189,7 @@ function Avatar({
|
|
|
193
189
|
return /* @__PURE__ */ jsx(
|
|
194
190
|
"div",
|
|
195
191
|
{
|
|
196
|
-
className:
|
|
192
|
+
className: getAvatarClasses(size, className),
|
|
197
193
|
"aria-label": alt ?? name,
|
|
198
194
|
role: "img",
|
|
199
195
|
...props,
|
|
@@ -305,29 +301,20 @@ function Icon({
|
|
|
305
301
|
}
|
|
306
302
|
);
|
|
307
303
|
}
|
|
308
|
-
|
|
309
|
-
base:
|
|
310
|
-
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
md: "w-10 h-10",
|
|
323
|
-
lg: "w-12 h-12"
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
defaultVariants: {
|
|
327
|
-
variant: "ghost",
|
|
328
|
-
size: "md"
|
|
329
|
-
}
|
|
330
|
-
});
|
|
304
|
+
function getIconButtonClasses(variant = "ghost", size = "md", className) {
|
|
305
|
+
const base = "flex items-center justify-center rounded-full flex-shrink-0 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50";
|
|
306
|
+
const variants = {
|
|
307
|
+
ghost: "bg-transparent hover:bg-[var(--muted)] active:bg-[var(--muted)]",
|
|
308
|
+
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600",
|
|
309
|
+
secondary: "border border-[var(--border)] bg-[var(--surface)] hover:bg-[var(--muted)] active:bg-[var(--muted)]"
|
|
310
|
+
};
|
|
311
|
+
const sizes = {
|
|
312
|
+
sm: "w-8 h-8",
|
|
313
|
+
md: "w-10 h-10",
|
|
314
|
+
lg: "w-12 h-12"
|
|
315
|
+
};
|
|
316
|
+
return [base, variants[variant], sizes[size], className].filter(Boolean).join(" ");
|
|
317
|
+
}
|
|
331
318
|
var IconButton = React2.forwardRef(
|
|
332
319
|
({
|
|
333
320
|
variant,
|
|
@@ -347,7 +334,7 @@ var IconButton = React2.forwardRef(
|
|
|
347
334
|
"button",
|
|
348
335
|
{
|
|
349
336
|
ref,
|
|
350
|
-
className:
|
|
337
|
+
className: getIconButtonClasses(variant, size, className),
|
|
351
338
|
...props,
|
|
352
339
|
children: /* @__PURE__ */ jsx(
|
|
353
340
|
Icon,
|
|
@@ -761,35 +748,33 @@ function Search({
|
|
|
761
748
|
}
|
|
762
749
|
);
|
|
763
750
|
}
|
|
764
|
-
|
|
765
|
-
base
|
|
766
|
-
variants
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
});
|
|
751
|
+
function getButtonClasses(variant = "primary", size = "md", fullWidth, className) {
|
|
752
|
+
const base = "cursor-pointer inline-flex items-center justify-center gap-2 rounded-[var(--radius-2xl)] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50";
|
|
753
|
+
const variants = {
|
|
754
|
+
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600 disabled:bg-neutral-400",
|
|
755
|
+
secondary: "border border-cyan-500 text-cyan-500 bg-transparent hover:bg-cyan-50 active:bg-cyan-100 disabled:border-neutral-400 disabled:text-neutral-400",
|
|
756
|
+
ghost: "bg-transparent text-cyan-500 hover:bg-cyan-50 active:bg-cyan-100 disabled:text-neutral-400"
|
|
757
|
+
};
|
|
758
|
+
const sizes = {
|
|
759
|
+
sm: "h-9 px-3",
|
|
760
|
+
md: "h-12 px-[18px]",
|
|
761
|
+
lg: "h-14 px-6"
|
|
762
|
+
};
|
|
763
|
+
return [
|
|
764
|
+
base,
|
|
765
|
+
variants[variant],
|
|
766
|
+
sizes[size],
|
|
767
|
+
fullWidth ? "w-full" : "",
|
|
768
|
+
className
|
|
769
|
+
].filter(Boolean).join(" ");
|
|
770
|
+
}
|
|
786
771
|
var Button = React2.forwardRef(
|
|
787
772
|
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
788
773
|
return /* @__PURE__ */ jsx(
|
|
789
774
|
"button",
|
|
790
775
|
{
|
|
791
776
|
ref,
|
|
792
|
-
className:
|
|
777
|
+
className: getButtonClasses(variant, size, fullWidth, className),
|
|
793
778
|
...props,
|
|
794
779
|
children
|
|
795
780
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neoptocom/neopto-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
|
|
6
6
|
"keywords": [
|
|
@@ -40,13 +40,8 @@
|
|
|
40
40
|
"files": [
|
|
41
41
|
"dist",
|
|
42
42
|
"src",
|
|
43
|
-
"scripts/init.mjs",
|
|
44
43
|
"CONSUMER_SETUP.md"
|
|
45
44
|
],
|
|
46
|
-
"bin": {
|
|
47
|
-
"neopto-ui": "./scripts/init.mjs",
|
|
48
|
-
"neopto-ui-init": "./scripts/init.mjs"
|
|
49
|
-
},
|
|
50
45
|
"scripts": {
|
|
51
46
|
"dev": "vite",
|
|
52
47
|
"build": "tsup && npm run build:css",
|
|
@@ -63,9 +58,7 @@
|
|
|
63
58
|
"react": ">=18",
|
|
64
59
|
"react-dom": ">=18"
|
|
65
60
|
},
|
|
66
|
-
"dependencies": {
|
|
67
|
-
"tailwind-variants": "^0.2.0"
|
|
68
|
-
},
|
|
61
|
+
"dependencies": {},
|
|
69
62
|
"devDependencies": {
|
|
70
63
|
"@changesets/cli": "^2.27.8",
|
|
71
64
|
"@storybook/addon-actions": "^8.1.0",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useState, useMemo } from "react";
|
|
3
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
3
|
import Typo from "./Typo";
|
|
5
4
|
|
|
6
5
|
export type AvatarProps = {
|
|
@@ -12,21 +11,22 @@ export type AvatarProps = {
|
|
|
12
11
|
color?: string;
|
|
13
12
|
/** Accessible alt text; defaults to the person's name */
|
|
14
13
|
alt?: string;
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
/** Avatar size */
|
|
15
|
+
size?: "sm" | "md";
|
|
16
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "children">;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
base
|
|
18
|
+
function getAvatarClasses(size: AvatarProps["size"] = "sm", className?: string): string {
|
|
19
|
+
const base =
|
|
20
20
|
"relative box-border flex items-center justify-center overflow-hidden rounded-full " +
|
|
21
|
-
"border border-[var(--border)] bg-[var(--muted)] text-[var(--fg)] select-none"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
21
|
+
"border border-[var(--border)] bg-[var(--muted)] text-[var(--fg)] select-none";
|
|
22
|
+
|
|
23
|
+
const sizes = {
|
|
24
|
+
sm: "w-[28px] h-[28px]",
|
|
25
|
+
md: "w-[60px] h-[60px]"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return [base, sizes[size], className].filter(Boolean).join(" ");
|
|
29
|
+
}
|
|
30
30
|
|
|
31
31
|
function getInitials(name: string) {
|
|
32
32
|
if (!name) return "…";
|
|
@@ -59,7 +59,7 @@ export default function Avatar({
|
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<div
|
|
62
|
-
className={
|
|
62
|
+
className={getAvatarClasses(size, className)}
|
|
63
63
|
aria-label={alt ?? name}
|
|
64
64
|
role="img"
|
|
65
65
|
{...props}
|
|
@@ -1,46 +1,54 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
-
import Typo from "./Typo";
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600 disabled:bg-neutral-400",
|
|
13
|
-
secondary: "border border-cyan-500 text-cyan-500 bg-transparent hover:bg-cyan-50 active:bg-cyan-100 disabled:border-neutral-400 disabled:text-neutral-400",
|
|
14
|
-
ghost: "bg-transparent text-cyan-500 hover:bg-cyan-50 active:bg-cyan-100 disabled:text-neutral-400"
|
|
15
|
-
},
|
|
16
|
-
size: {
|
|
17
|
-
sm: "h-9 px-3",
|
|
18
|
-
md: "h-12 px-[18px]",
|
|
19
|
-
lg: "h-14 px-6"
|
|
20
|
-
},
|
|
21
|
-
fullWidth: {
|
|
22
|
-
true: "w-full"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
defaultVariants: {
|
|
26
|
-
variant: "primary",
|
|
27
|
-
size: "md"
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
type ButtonVariants = VariantProps<typeof buttonStyles>;
|
|
32
|
-
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & ButtonVariants & {
|
|
3
|
+
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
4
|
+
/** Button visual variant */
|
|
5
|
+
variant?: "primary" | "secondary" | "ghost";
|
|
6
|
+
/** Button size */
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
/** Make button full width */
|
|
9
|
+
fullWidth?: boolean;
|
|
33
10
|
/** Icon component to display instead of text */
|
|
34
11
|
icon?: React.ReactNode;
|
|
35
12
|
};
|
|
36
13
|
|
|
14
|
+
function getButtonClasses(
|
|
15
|
+
variant: ButtonProps["variant"] = "primary",
|
|
16
|
+
size: ButtonProps["size"] = "md",
|
|
17
|
+
fullWidth?: boolean,
|
|
18
|
+
className?: string
|
|
19
|
+
): string {
|
|
20
|
+
const base =
|
|
21
|
+
"cursor-pointer inline-flex items-center justify-center gap-2 rounded-[var(--radius-2xl)] transition-colors " +
|
|
22
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 " +
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-50";
|
|
24
|
+
|
|
25
|
+
const variants = {
|
|
26
|
+
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600 disabled:bg-neutral-400",
|
|
27
|
+
secondary: "border border-cyan-500 text-cyan-500 bg-transparent hover:bg-cyan-50 active:bg-cyan-100 disabled:border-neutral-400 disabled:text-neutral-400",
|
|
28
|
+
ghost: "bg-transparent text-cyan-500 hover:bg-cyan-50 active:bg-cyan-100 disabled:text-neutral-400"
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const sizes = {
|
|
32
|
+
sm: "h-9 px-3",
|
|
33
|
+
md: "h-12 px-[18px]",
|
|
34
|
+
lg: "h-14 px-6"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return [
|
|
38
|
+
base,
|
|
39
|
+
variants[variant],
|
|
40
|
+
sizes[size],
|
|
41
|
+
fullWidth ? "w-full" : "",
|
|
42
|
+
className
|
|
43
|
+
].filter(Boolean).join(" ");
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
38
47
|
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
39
|
-
|
|
40
48
|
return (
|
|
41
49
|
<button
|
|
42
50
|
ref={ref}
|
|
43
|
-
className={
|
|
51
|
+
className={getButtonClasses(variant, size, fullWidth, className)}
|
|
44
52
|
{...props}
|
|
45
53
|
>
|
|
46
54
|
{children}
|
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
2
|
import Icon from "./Icon";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
md: "w-10 h-10",
|
|
20
|
-
lg: "w-12 h-12"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
defaultVariants: {
|
|
24
|
-
variant: "ghost",
|
|
25
|
-
size: "md"
|
|
26
|
-
}
|
|
27
|
-
});
|
|
4
|
+
export type IconButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
5
|
+
/** Button visual variant */
|
|
6
|
+
variant?: "ghost" | "primary" | "secondary";
|
|
7
|
+
/** Button size */
|
|
8
|
+
size?: "sm" | "md" | "lg";
|
|
9
|
+
/** Material Symbols icon name */
|
|
10
|
+
icon: string;
|
|
11
|
+
/** Icon size (defaults to button size) */
|
|
12
|
+
iconSize?: "sm" | "md" | "lg";
|
|
13
|
+
/** Icon fill (0 = outlined, 1 = filled) */
|
|
14
|
+
iconFill?: 0 | 1;
|
|
15
|
+
/** Optional custom className for the icon */
|
|
16
|
+
iconClassName?: string;
|
|
17
|
+
};
|
|
28
18
|
|
|
29
|
-
|
|
19
|
+
function getIconButtonClasses(
|
|
20
|
+
variant: IconButtonProps["variant"] = "ghost",
|
|
21
|
+
size: IconButtonProps["size"] = "md",
|
|
22
|
+
className?: string
|
|
23
|
+
): string {
|
|
24
|
+
const base =
|
|
25
|
+
"flex items-center justify-center rounded-full flex-shrink-0 " +
|
|
26
|
+
"transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 " +
|
|
27
|
+
"focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50";
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/** Icon size (defaults to button size) */
|
|
36
|
-
iconSize?: "sm" | "md" | "lg";
|
|
37
|
-
/** Icon fill (0 = outlined, 1 = filled) */
|
|
38
|
-
iconFill?: 0 | 1;
|
|
39
|
-
/** Optional custom className for the icon */
|
|
40
|
-
iconClassName?: string;
|
|
29
|
+
const variants = {
|
|
30
|
+
ghost: "bg-transparent hover:bg-[var(--muted)] active:bg-[var(--muted)]",
|
|
31
|
+
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600",
|
|
32
|
+
secondary: "border border-[var(--border)] bg-[var(--surface)] hover:bg-[var(--muted)] active:bg-[var(--muted)]"
|
|
41
33
|
};
|
|
42
34
|
|
|
35
|
+
const sizes = {
|
|
36
|
+
sm: "w-8 h-8",
|
|
37
|
+
md: "w-10 h-10",
|
|
38
|
+
lg: "w-12 h-12"
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return [base, variants[variant], sizes[size], className].filter(Boolean).join(" ");
|
|
42
|
+
}
|
|
43
|
+
|
|
43
44
|
export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
44
45
|
(
|
|
45
46
|
{
|
|
@@ -71,7 +72,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
|
71
72
|
return (
|
|
72
73
|
<button
|
|
73
74
|
ref={ref}
|
|
74
|
-
className={
|
|
75
|
+
className={getIconButtonClasses(variant, size, className)}
|
|
75
76
|
{...props}
|
|
76
77
|
>
|
|
77
78
|
<Icon
|
package/src/components/Typo.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
2
|
|
|
4
3
|
export type TypoVariant =
|
|
5
4
|
| "display-lg" | "display-md" | "display-sm"
|
|
@@ -11,41 +10,48 @@ export type TypoVariant =
|
|
|
11
10
|
|
|
12
11
|
export type TypoWeight = "normal" | "medium" | "semibold" | "bold";
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
13
|
+
function getTypoClasses(
|
|
14
|
+
variant: TypoVariant,
|
|
15
|
+
weight: TypoWeight = "normal",
|
|
16
|
+
muted?: boolean,
|
|
17
|
+
className?: string
|
|
18
|
+
): string {
|
|
19
|
+
const base = "text-current";
|
|
20
|
+
|
|
21
|
+
const variants: Record<TypoVariant, string> = {
|
|
22
|
+
"display-lg": "text-5xl leading-tight",
|
|
23
|
+
"display-md": "text-4xl leading-tight",
|
|
24
|
+
"display-sm": "text-4xl leading-tight",
|
|
25
|
+
"headline-lg": "text-3xl leading-tight",
|
|
26
|
+
"headline-md": "text-3xl leading-tight",
|
|
27
|
+
"headline-sm": "text-3xl leading-tight",
|
|
28
|
+
"title-lg": "text-xl leading-tight",
|
|
29
|
+
"title-md": "text-lg leading-tight",
|
|
30
|
+
"title-sm": "text-base leading-tight",
|
|
31
|
+
"label-lg": "text-sm leading-tight",
|
|
32
|
+
"label-md": "text-xs leading-tight",
|
|
33
|
+
"label-sm": "text-xs leading-tight",
|
|
34
|
+
"body-lg": "text-base leading-relaxed",
|
|
35
|
+
"body-md": "text-sm leading-relaxed",
|
|
36
|
+
"body-sm": "text-xs leading-relaxed",
|
|
37
|
+
"button": "text-base leading-normal"
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const weights: Record<TypoWeight, string> = {
|
|
41
|
+
normal: "font-normal",
|
|
42
|
+
medium: "font-medium",
|
|
43
|
+
semibold: "font-semibold",
|
|
44
|
+
bold: "font-bold"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
base,
|
|
49
|
+
variants[variant],
|
|
50
|
+
weights[weight],
|
|
51
|
+
muted ? "text-[var(--muted-fg)]" : "",
|
|
52
|
+
className
|
|
53
|
+
].filter(Boolean).join(" ");
|
|
54
|
+
}
|
|
49
55
|
|
|
50
56
|
export type TypoProps<T extends React.ElementType = "span"> = {
|
|
51
57
|
/**
|
|
@@ -83,7 +89,7 @@ export default function Typo<T extends React.ElementType = "span">({
|
|
|
83
89
|
|
|
84
90
|
return (
|
|
85
91
|
<Component
|
|
86
|
-
className={
|
|
92
|
+
className={getTypoClasses(variant, bold, muted, className)}
|
|
87
93
|
style={{ fontFamily: getFontFamily(variant) }}
|
|
88
94
|
{...props}
|
|
89
95
|
>
|