retail-design-system 1.0.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/.github/workflows/release.yml +46 -0
- package/.oxfmtrc.json +17 -0
- package/.oxlintrc.json +132 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +13 -0
- package/README.md +56 -0
- package/apps/storybook/.storybook/main.ts +8 -0
- package/apps/storybook/.storybook/preview.css +9 -0
- package/apps/storybook/.storybook/preview.ts +6 -0
- package/apps/storybook/package.json +24 -0
- package/apps/storybook/stories/button.stories.ts +118 -0
- package/apps/storybook/stories/input.stories.ts +127 -0
- package/apps/storybook/stories/label.stories.ts +98 -0
- package/apps/storybook/tsconfig.app.json +24 -0
- package/apps/storybook/tsconfig.json +4 -0
- package/apps/storybook/tsconfig.node.json +22 -0
- package/apps/storybook/vite.config.ts +15 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/get-child-block.ts +17 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/get-component-page-match.ts +56 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/get-direct-child-block.ts +22 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/layout.tsx +25 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/page.tsx +32 -0
- package/apps/web/app/(sidebar)/components/[...slugs]/pascal-to-kebab-case.ts +9 -0
- package/apps/web/app/(sidebar)/components/button2/page.tsx +154 -0
- package/apps/web/app/(sidebar)/components/input/page.tsx +98 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/mayhem-mode-card-badge.tsx +9 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/mayhem-mode-coin-active-badge.tsx +14 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/mayhem-mode-coin-inactive-badge.tsx +12 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/mayhem-mode-create-coin.tsx +44 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/mayhem-mode-dialog-icon.tsx +47 -0
- package/apps/web/app/(sidebar)/experiments/2025-10-22/page.tsx +167 -0
- package/apps/web/app/(sidebar)/experiments/2025-11-04/filters.tsx +90 -0
- package/apps/web/app/(sidebar)/experiments/2025-11-04/page.tsx +18 -0
- package/apps/web/app/(sidebar)/layout.tsx +17 -0
- package/apps/web/app/(sidebar)/primitives/colors/page.tsx +49 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +39 -0
- package/apps/web/app/page.tsx +14 -0
- package/apps/web/app/providers.tsx +15 -0
- package/apps/web/components/dialog.tsx +21 -0
- package/apps/web/components/logo.tsx +11 -0
- package/apps/web/components/logomark.tsx +21 -0
- package/apps/web/components/logotype.tsx +25 -0
- package/apps/web/components/notion/notion-block-content.tsx +401 -0
- package/apps/web/components/notion/notion-docs-blocks.tsx +18 -0
- package/apps/web/components/notion/notion-docs-code-page.tsx +20 -0
- package/apps/web/components/notion/notion-docs-layout.tsx +52 -0
- package/apps/web/components/notion/notion-revalidate-button-client.tsx +14 -0
- package/apps/web/components/notion/notion-revalidate-button.tsx +20 -0
- package/apps/web/components/notion/notion-rich-text-segments.tsx +55 -0
- package/apps/web/components/notion/notion-tabs.tsx +38 -0
- package/apps/web/components/notion/notion.ts +223 -0
- package/apps/web/components/sidebar-client.tsx +60 -0
- package/apps/web/components/sidebar-server.tsx +185 -0
- package/apps/web/components/tooltip.tsx +53 -0
- package/apps/web/components/topbar.tsx +14 -0
- package/apps/web/next.config.ts +10 -0
- package/apps/web/package.json +42 -0
- package/apps/web/postcss.config.mjs +5 -0
- package/apps/web/public/2025-10-22-dialog-banner.png +0 -0
- package/apps/web/public/pump-logomark.svg +7 -0
- package/apps/web/styles/custom.css +31 -0
- package/apps/web/styles/font.css +8 -0
- package/apps/web/styles/global.css +5 -0
- package/apps/web/styles/tailwind-reset.css +102 -0
- package/apps/web/styles/tailwind.css +140 -0
- package/apps/web/tsconfig.json +34 -0
- package/bun.lock +1249 -0
- package/bunfig.toml +2 -0
- package/package.json +41 -0
- package/packages/ui/global.d.ts +4 -0
- package/packages/ui/package.json +49 -0
- package/packages/ui/src/components/button/button-spinner.module.css +95 -0
- package/packages/ui/src/components/button/button-spinner.tsx +18 -0
- package/packages/ui/src/components/button/button.module.css +144 -0
- package/packages/ui/src/components/button/button.tsx +102 -0
- package/packages/ui/src/components/button-link/button-link.tsx +46 -0
- package/packages/ui/src/components/column/column.module.css +4 -0
- package/packages/ui/src/components/column/column.tsx +65 -0
- package/packages/ui/src/components/row/row.module.css +4 -0
- package/packages/ui/src/components/row/row.tsx +65 -0
- package/packages/ui/src/components/spacer/spacer.module.css +3 -0
- package/packages/ui/src/components/spacer/spacer.tsx +30 -0
- package/packages/ui/src/components/switch/switch.module.css +62 -0
- package/packages/ui/src/components/switch/switch.tsx +58 -0
- package/packages/ui/src/components/tabs/tabs-panel.module.css +4 -0
- package/packages/ui/src/components/tabs/tabs-panel.tsx +21 -0
- package/packages/ui/src/components/tabs/tabs.module.css +5 -0
- package/packages/ui/src/components/tabs/tabs.tsx +21 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-indicator.module.css +10 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-indicator.tsx +33 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-list.module.css +8 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-list.tsx +27 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-tab.module.css +24 -0
- package/packages/ui/src/components/tabs-underline/tabs-underline-tab.tsx +30 -0
- package/packages/ui/src/foundations/colors/colors.ts +475 -0
- package/packages/ui/src/foundations/colors/generate-css.ts +34 -0
- package/packages/ui/src/foundations/colors/retail-design-system.css +116 -0
- package/packages/ui/src/foundations/colors/tailwind-v3.ts +18 -0
- package/packages/ui/src/foundations/colors/tailwind-v4.css +116 -0
- package/packages/ui/src/index.ts +34 -0
- package/packages/ui/src/input.module.css +57 -0
- package/packages/ui/src/input.tsx +49 -0
- package/packages/ui/src/label.module.css +8 -0
- package/packages/ui/src/label.tsx +23 -0
- package/packages/ui/tsconfig.json +14 -0
- package/packages/ui/tsup.config.ts +31 -0
- package/scripts/clean.sh +69 -0
- package/scripts/sort-package-json.sh +30 -0
- package/turbo.json +15 -0
package/bunfig.toml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "retail-design-system",
|
|
3
|
+
"workspaces": [
|
|
4
|
+
"apps/*",
|
|
5
|
+
"packages/*"
|
|
6
|
+
],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "turbo build",
|
|
9
|
+
"check": "bun run check:format && bun run check:lint",
|
|
10
|
+
"check:fix": "bun run check:format:fix && bun run check:lint:fix",
|
|
11
|
+
"check:format": "oxfmt --check",
|
|
12
|
+
"check:format:fix": "oxfmt",
|
|
13
|
+
"check:lint": "oxlint",
|
|
14
|
+
"check:lint:fix": "oxlint --fix --fix-suggestions --fix-dangerously",
|
|
15
|
+
"check:type": "turbo check:type",
|
|
16
|
+
"clean": "sh scripts/clean.sh",
|
|
17
|
+
"dev": "turbo dev",
|
|
18
|
+
"update": "bun update --interactive --recursive"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"eslint-plugin-perfectionist": "5.6.0",
|
|
22
|
+
"eslint-plugin-unused-imports": "4.4.1",
|
|
23
|
+
"oxfmt": "0.40.0",
|
|
24
|
+
"oxlint": "1.55.0",
|
|
25
|
+
"turbo": "2.8.17"
|
|
26
|
+
},
|
|
27
|
+
"packageManager": "bun@1.3.0",
|
|
28
|
+
"catalog": {
|
|
29
|
+
"@base-ui/react": "1.3.0",
|
|
30
|
+
"@nattui/tailwind-colors": "0.0.8",
|
|
31
|
+
"@pump-fun/icons-filled": "npm:@central-icons-react/round-filled-radius-1-stroke-2@1.1.158",
|
|
32
|
+
"@pump-fun/icons-line": "npm:@central-icons-react/round-outlined-radius-1-stroke-2@1.1.158",
|
|
33
|
+
"@types/node": "25.5.0",
|
|
34
|
+
"@types/react": "19.2.14",
|
|
35
|
+
"@types/react-dom": "19.2.3",
|
|
36
|
+
"react": "19.2.4",
|
|
37
|
+
"react-dom": "19.2.4",
|
|
38
|
+
"typescript": "5.9.3"
|
|
39
|
+
},
|
|
40
|
+
"version": "1.0.0"
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pump-fun/retail-design-system",
|
|
3
|
+
"version": "0.0.16",
|
|
4
|
+
"description": "A collection of reusable React components built with TypeScript and CSS Modules",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./tailwind-v3/colors": {
|
|
19
|
+
"types": "./dist/foundations/colors/tailwind-v3.d.ts",
|
|
20
|
+
"import": "./dist/foundations/colors/tailwind-v3.js",
|
|
21
|
+
"require": "./dist/foundations/colors/tailwind-v3.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./tailwind-v4/colors.css": "./dist/foundations/colors/tailwind-v4.css",
|
|
24
|
+
"./retail-design-system.css": "./dist/foundations/colors/retail-design-system.css"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "restricted"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "bun run src/foundations/colors/generate-css.ts && tsup",
|
|
31
|
+
"check:types": "tsc",
|
|
32
|
+
"dev": "bun run src/foundations/colors/generate-css.ts && tsup --watch",
|
|
33
|
+
"prepublishOnly": "pnpm run build",
|
|
34
|
+
"update": "pnpx npm-check-updates --upgrade"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@base-ui/react": "catalog:"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "catalog:",
|
|
41
|
+
"@types/react": "catalog:",
|
|
42
|
+
"tsup": "8.5.1",
|
|
43
|
+
"typescript": "catalog:"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
47
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.button_spinner {
|
|
2
|
+
position: relative;
|
|
3
|
+
width: var(--size);
|
|
4
|
+
height: var(--size);
|
|
5
|
+
scale: -1 1;
|
|
6
|
+
animation: spinner 1000ms steps(12, end) infinite;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@keyframes spinner {
|
|
10
|
+
to {
|
|
11
|
+
rotate: 1turn;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.button_spinner > div {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 50%;
|
|
18
|
+
right: 0;
|
|
19
|
+
width: calc(var(--size) / 2);
|
|
20
|
+
height: calc(var(--size) / 12);
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
transform-origin: center left;
|
|
23
|
+
translate: 0 -50%;
|
|
24
|
+
transition: all 150ms;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.button_spinner > div::after {
|
|
28
|
+
position: absolute;
|
|
29
|
+
right: 0;
|
|
30
|
+
width: 50%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
content: "";
|
|
33
|
+
background-color: currentColor;
|
|
34
|
+
border-radius: 9999px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.button_spinner > div:nth-child(1) {
|
|
38
|
+
opacity: 1;
|
|
39
|
+
rotate: 0deg;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.button_spinner > div:nth-child(2) {
|
|
43
|
+
opacity: 0.925;
|
|
44
|
+
rotate: 30deg;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.button_spinner > div:nth-child(3) {
|
|
48
|
+
opacity: 0.85;
|
|
49
|
+
rotate: 60deg;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.button_spinner > div:nth-child(4) {
|
|
53
|
+
opacity: 0.775;
|
|
54
|
+
rotate: 90deg;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.button_spinner > div:nth-child(5) {
|
|
58
|
+
opacity: 0.7;
|
|
59
|
+
rotate: 120deg;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.button_spinner > div:nth-child(6) {
|
|
63
|
+
opacity: 0.625;
|
|
64
|
+
rotate: 150deg;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.button_spinner > div:nth-child(7) {
|
|
68
|
+
opacity: 0.55;
|
|
69
|
+
rotate: 180deg;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.button_spinner > div:nth-child(8) {
|
|
73
|
+
opacity: 0.475;
|
|
74
|
+
rotate: 210deg;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.button_spinner > div:nth-child(9) {
|
|
78
|
+
opacity: 0.4;
|
|
79
|
+
rotate: 240deg;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.button_spinner > div:nth-child(10) {
|
|
83
|
+
opacity: 0.325;
|
|
84
|
+
rotate: 270deg;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.button_spinner > div:nth-child(11) {
|
|
88
|
+
opacity: 0.25;
|
|
89
|
+
rotate: 300deg;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.button_spinner > div:nth-child(12) {
|
|
93
|
+
opacity: 0.15;
|
|
94
|
+
rotate: 330deg;
|
|
95
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CSSProperties, JSX } from "react"
|
|
2
|
+
import styles from "@/components/button/button-spinner.module.css"
|
|
3
|
+
|
|
4
|
+
export interface ButtonSpinnerProps {
|
|
5
|
+
size?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function ButtonSpinner(props: ButtonSpinnerProps): JSX.Element {
|
|
9
|
+
const { size = 16 } = props
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.button_spinner} style={{ "--size": `${size}px` } as CSSProperties}>
|
|
13
|
+
{Array.from({ length: 12 }).map((_, index) => (
|
|
14
|
+
<div key={index} />
|
|
15
|
+
))}
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* ===================================================== */
|
|
2
|
+
/* Base */
|
|
3
|
+
/* ===================================================== */
|
|
4
|
+
.button {
|
|
5
|
+
align-items: safe center;
|
|
6
|
+
border-style: none;
|
|
7
|
+
column-gap: 8px;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-shrink: 0;
|
|
11
|
+
font-family: var(--font-sans, sans-serif);
|
|
12
|
+
font-size: 14px;
|
|
13
|
+
font-weight: 500;
|
|
14
|
+
height: var(--size);
|
|
15
|
+
justify-content: safe center;
|
|
16
|
+
line-height: 1.5;
|
|
17
|
+
outline-color: var(--color-bg-accent);
|
|
18
|
+
outline-offset: 2px;
|
|
19
|
+
outline-style: none;
|
|
20
|
+
outline-width: 2px;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
transition-duration: 150ms;
|
|
23
|
+
transition-property: background-color, box-shadow, opacity, translate;
|
|
24
|
+
user-select: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* When pressed, move the button down */
|
|
28
|
+
.button:active:not(:disabled),
|
|
29
|
+
.button[aria-pressed="true"]:not(:disabled) {
|
|
30
|
+
translate: 0 1px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* When disabled, disable cursor and reduce opacity */
|
|
34
|
+
.button:disabled {
|
|
35
|
+
cursor: not-allowed;
|
|
36
|
+
opacity: 0.5;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* When keyboard focused, show outline */
|
|
40
|
+
.button:focus-visible {
|
|
41
|
+
outline-style: solid;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ===================================================== */
|
|
45
|
+
/* Icon only */
|
|
46
|
+
/* ===================================================== */
|
|
47
|
+
.button__icon_only {
|
|
48
|
+
width: var(--size) !important;
|
|
49
|
+
padding: 0 !important;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ===================================================== */
|
|
53
|
+
/* Rounded */
|
|
54
|
+
/* ===================================================== */
|
|
55
|
+
.button__rounded_base {
|
|
56
|
+
border-radius: 12px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.button__rounded_full {
|
|
60
|
+
border-radius: 9999px !important;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ===================================================== */
|
|
64
|
+
/* Size */
|
|
65
|
+
/* ===================================================== */
|
|
66
|
+
.button__size_32 {
|
|
67
|
+
--size: 32px;
|
|
68
|
+
padding: 0 10px;
|
|
69
|
+
border-radius: 8px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.button__size_36 {
|
|
73
|
+
--size: 36px;
|
|
74
|
+
padding: 0 12px;
|
|
75
|
+
border-radius: 10px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.button__size_40 {
|
|
79
|
+
--size: 40px;
|
|
80
|
+
padding: 0 14px;
|
|
81
|
+
border-radius: 12px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.button__size_44 {
|
|
85
|
+
--size: 44px;
|
|
86
|
+
padding: 0 16px;
|
|
87
|
+
border-radius: 14px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.button__size_48 {
|
|
91
|
+
--size: 48px;
|
|
92
|
+
padding: 0 18px;
|
|
93
|
+
border-radius: 16px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ===================================================== */
|
|
97
|
+
/* Variant */
|
|
98
|
+
/* ===================================================== */
|
|
99
|
+
/* Ghost */
|
|
100
|
+
.button__variant_ghost {
|
|
101
|
+
color: var(--color-text-primary);
|
|
102
|
+
background-color: transparent;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.button__variant_ghost:active:not(:disabled),
|
|
106
|
+
.button__variant_ghost:hover:not(:disabled),
|
|
107
|
+
.button__variant_ghost[aria-pressed="true"]:not(:disabled) {
|
|
108
|
+
background-color: color-mix(in oklab, var(--color-bg-tertiary) 75%, transparent 0%);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Primary */
|
|
112
|
+
.button__variant_primary {
|
|
113
|
+
color: var(--color-text-on-accent);
|
|
114
|
+
background-color: var(--color-bg-accent);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.button__variant_primary:active:not(:disabled),
|
|
118
|
+
.button__variant_primary:hover:not(:disabled),
|
|
119
|
+
.button__variant_primary[aria-pressed="true"]:not(:disabled) {
|
|
120
|
+
background-color: color-mix(in oklab, var(--color-bg-accent) 75%, transparent 0%);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Secondary */
|
|
124
|
+
.button__variant_secondary {
|
|
125
|
+
color: var(--color-text-primary);
|
|
126
|
+
background-color: var(--color-bg-tertiary);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.button__variant_secondary:active:not(:disabled),
|
|
130
|
+
.button__variant_secondary:hover:not(:disabled),
|
|
131
|
+
.button__variant_secondary[aria-pressed="true"]:not(:disabled) {
|
|
132
|
+
background-color: color-mix(in oklab, var(--color-bg-tertiary) 75%, transparent 0%);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ===================================================== */
|
|
136
|
+
/* Width */
|
|
137
|
+
/* ===================================================== */
|
|
138
|
+
.button__width_base {
|
|
139
|
+
width: max-content; /* Fit the button content */
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.button__width_full {
|
|
143
|
+
width: 100%;
|
|
144
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ComponentProps, JSX, ReactNode } from "react"
|
|
2
|
+
import { ButtonSpinner } from "@/components/button/button-spinner"
|
|
3
|
+
import styles from "@/components/button/button.module.css"
|
|
4
|
+
|
|
5
|
+
export interface ButtonIconProps extends ButtonInternalProps {
|
|
6
|
+
children?: ReactNode
|
|
7
|
+
iconEnd?: never
|
|
8
|
+
iconStart?: never
|
|
9
|
+
isIconOnly: true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ButtonProps extends ButtonInternalProps {
|
|
13
|
+
children?: string | string[]
|
|
14
|
+
isIconOnly?: false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ButtonInternalProps extends Omit<ComponentProps<"button">, "aria-pressed" | "disabled"> {
|
|
18
|
+
iconEnd?: ReactNode
|
|
19
|
+
iconStart?: ReactNode
|
|
20
|
+
isActive?: boolean
|
|
21
|
+
isDisabled?: boolean
|
|
22
|
+
isFullWidth?: boolean
|
|
23
|
+
isIconOnly?: boolean
|
|
24
|
+
isLoading?: boolean
|
|
25
|
+
isRounded?: boolean
|
|
26
|
+
size?: 32 | 36 | 40 | 44 | 48
|
|
27
|
+
variant?: "ghost" | "primary" | "secondary"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ButtonUnionProps = ButtonIconProps | ButtonProps
|
|
31
|
+
|
|
32
|
+
export function Button(props: ButtonUnionProps): JSX.Element {
|
|
33
|
+
const {
|
|
34
|
+
children = "",
|
|
35
|
+
className: customClassName = "",
|
|
36
|
+
iconEnd = "",
|
|
37
|
+
iconStart = "",
|
|
38
|
+
isActive = false,
|
|
39
|
+
isDisabled = false,
|
|
40
|
+
isFullWidth = false,
|
|
41
|
+
isIconOnly = false,
|
|
42
|
+
isLoading = false,
|
|
43
|
+
isRounded = false,
|
|
44
|
+
size = 40,
|
|
45
|
+
type = "button",
|
|
46
|
+
variant = "primary",
|
|
47
|
+
...rest
|
|
48
|
+
} = props
|
|
49
|
+
|
|
50
|
+
const combinedClassName = `
|
|
51
|
+
${BUTTON_CLASS_NAME.BASE}
|
|
52
|
+
${BUTTON_CLASS_NAME.SIZE[size]}
|
|
53
|
+
${BUTTON_CLASS_NAME.VARIANT[variant.toUpperCase() as keyof typeof BUTTON_CLASS_NAME.VARIANT]}
|
|
54
|
+
${isFullWidth ? BUTTON_CLASS_NAME.WIDTH.FULL : BUTTON_CLASS_NAME.WIDTH.BASE}
|
|
55
|
+
${isIconOnly ? BUTTON_CLASS_NAME.ICON_ONLY : ""}
|
|
56
|
+
${isRounded ? BUTTON_CLASS_NAME.ROUNDED.FULL : BUTTON_CLASS_NAME.ROUNDED.BASE}
|
|
57
|
+
${customClassName}
|
|
58
|
+
`
|
|
59
|
+
.replaceAll(/\s+/g, " ")
|
|
60
|
+
.trim()
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<button
|
|
64
|
+
aria-pressed={isActive}
|
|
65
|
+
className={combinedClassName}
|
|
66
|
+
disabled={isDisabled || isLoading}
|
|
67
|
+
type={type}
|
|
68
|
+
{...rest}
|
|
69
|
+
>
|
|
70
|
+
{isLoading && <ButtonSpinner />}
|
|
71
|
+
{!isLoading && iconStart}
|
|
72
|
+
{isIconOnly ? isLoading ? undefined : children : <span>{children}</span>}
|
|
73
|
+
{!isLoading && iconEnd}
|
|
74
|
+
</button>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const BUTTON_CLASS_NAME = {
|
|
79
|
+
BASE: styles.button,
|
|
80
|
+
ICON_ONLY: styles.button__icon_only,
|
|
81
|
+
ROUNDED: {
|
|
82
|
+
BASE: styles.button__rounded_base,
|
|
83
|
+
FULL: styles.button__rounded_full,
|
|
84
|
+
},
|
|
85
|
+
SIZE: {
|
|
86
|
+
32: styles.button__size_32,
|
|
87
|
+
36: styles.button__size_36,
|
|
88
|
+
40: styles.button__size_40,
|
|
89
|
+
44: styles.button__size_44,
|
|
90
|
+
48: styles.button__size_48,
|
|
91
|
+
},
|
|
92
|
+
VARIANT: {
|
|
93
|
+
ACCENT: styles.button__variant_accent,
|
|
94
|
+
GHOST: styles.button__variant_ghost,
|
|
95
|
+
PRIMARY: styles.button__variant_primary,
|
|
96
|
+
SECONDARY: styles.button__variant_secondary,
|
|
97
|
+
},
|
|
98
|
+
WIDTH: {
|
|
99
|
+
BASE: styles.button__width_base,
|
|
100
|
+
FULL: styles.button__width_full,
|
|
101
|
+
},
|
|
102
|
+
} as const
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createElement, type ComponentProps, type ElementType, type JSX } from "react"
|
|
2
|
+
import { BUTTON_CLASS_NAME, type ButtonProps } from "@/components/button/button"
|
|
3
|
+
|
|
4
|
+
export type ButtonLinkProps<ComponentType extends ElementType = "a"> = ButtonLinkInternalProps &
|
|
5
|
+
ComponentProps<ComponentType>
|
|
6
|
+
|
|
7
|
+
interface ButtonLinkInternalProps extends Pick<
|
|
8
|
+
ButtonProps,
|
|
9
|
+
"isFullWidth" | "isIconOnly" | "isRounded" | "size" | "variant"
|
|
10
|
+
> {
|
|
11
|
+
as?: ElementType
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function ButtonLink<ComponentType extends ElementType = "a">(
|
|
15
|
+
props: ButtonLinkProps<ComponentType>,
|
|
16
|
+
): JSX.Element {
|
|
17
|
+
const {
|
|
18
|
+
as = "a",
|
|
19
|
+
className: customClassName = "",
|
|
20
|
+
isFullWidth = false,
|
|
21
|
+
isIconOnly = false,
|
|
22
|
+
isRounded = false,
|
|
23
|
+
size = 40,
|
|
24
|
+
variant = "primary",
|
|
25
|
+
...rest
|
|
26
|
+
} = props
|
|
27
|
+
|
|
28
|
+
const Component = as as ElementType
|
|
29
|
+
|
|
30
|
+
const combinedClassName = `
|
|
31
|
+
${BUTTON_CLASS_NAME.BASE}
|
|
32
|
+
${BUTTON_CLASS_NAME.SIZE[size]}
|
|
33
|
+
${BUTTON_CLASS_NAME.VARIANT[variant.toUpperCase() as keyof typeof BUTTON_CLASS_NAME.VARIANT]}
|
|
34
|
+
${isFullWidth ? BUTTON_CLASS_NAME.WIDTH.FULL : BUTTON_CLASS_NAME.WIDTH.BASE}
|
|
35
|
+
${isIconOnly ? BUTTON_CLASS_NAME.ICON_ONLY : ""}
|
|
36
|
+
${isRounded ? BUTTON_CLASS_NAME.ROUNDED.FULL : BUTTON_CLASS_NAME.ROUNDED.BASE}
|
|
37
|
+
${customClassName}
|
|
38
|
+
`
|
|
39
|
+
.replaceAll(/\s+/g, " ")
|
|
40
|
+
.trim()
|
|
41
|
+
|
|
42
|
+
return createElement(Component, {
|
|
43
|
+
className: combinedClassName,
|
|
44
|
+
...rest,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
type ComponentProps,
|
|
4
|
+
type CSSProperties,
|
|
5
|
+
type ElementType,
|
|
6
|
+
type JSX,
|
|
7
|
+
} from "react"
|
|
8
|
+
import styles from "@/components/column/column.module.css"
|
|
9
|
+
|
|
10
|
+
export type ColumnProps<ComponentType extends ElementType = "div"> = ColumnInternalProps &
|
|
11
|
+
ComponentProps<ComponentType>
|
|
12
|
+
|
|
13
|
+
interface ColumnInternalProps {
|
|
14
|
+
alignItems?: CSSProperties["alignItems"]
|
|
15
|
+
as?: keyof JSX.IntrinsicElements
|
|
16
|
+
flexWrap?: CSSProperties["flexWrap"]
|
|
17
|
+
gap?: CSSProperties["gap"]
|
|
18
|
+
gapX?: CSSProperties["columnGap"]
|
|
19
|
+
gapY?: CSSProperties["rowGap"]
|
|
20
|
+
justifyContent?: CSSProperties["justifyContent"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function Column(props: ColumnProps): JSX.Element {
|
|
24
|
+
const {
|
|
25
|
+
alignItems = undefined,
|
|
26
|
+
as = "div",
|
|
27
|
+
className: customClassName = "",
|
|
28
|
+
flexWrap = undefined,
|
|
29
|
+
gap = undefined,
|
|
30
|
+
gapX = undefined,
|
|
31
|
+
gapY = undefined,
|
|
32
|
+
justifyContent = undefined,
|
|
33
|
+
style: customStyle,
|
|
34
|
+
...rest
|
|
35
|
+
} = props
|
|
36
|
+
|
|
37
|
+
const Component = as
|
|
38
|
+
|
|
39
|
+
const combinedClassName = `
|
|
40
|
+
${COLUMN_CLASS_NAME.BASE}
|
|
41
|
+
${customClassName}
|
|
42
|
+
`
|
|
43
|
+
.replaceAll(/\s+/g, " ")
|
|
44
|
+
.trim()
|
|
45
|
+
|
|
46
|
+
const combinedStyle = {
|
|
47
|
+
...customStyle,
|
|
48
|
+
...(gap === undefined ? {} : { gap }),
|
|
49
|
+
...(gapX === undefined ? {} : { columnGap: gapX }),
|
|
50
|
+
...(gapY === undefined ? {} : { rowGap: gapY }),
|
|
51
|
+
...(flexWrap === undefined ? {} : { flexWrap }),
|
|
52
|
+
...(alignItems === undefined ? {} : { alignItems }),
|
|
53
|
+
...(justifyContent === undefined ? {} : { justifyContent }),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return createElement(Component, {
|
|
57
|
+
className: combinedClassName,
|
|
58
|
+
style: combinedStyle,
|
|
59
|
+
...rest,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const COLUMN_CLASS_NAME = {
|
|
64
|
+
BASE: styles.column,
|
|
65
|
+
} as const
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
type ComponentProps,
|
|
4
|
+
type CSSProperties,
|
|
5
|
+
type ElementType,
|
|
6
|
+
type JSX,
|
|
7
|
+
} from "react"
|
|
8
|
+
import styles from "@/components/row/row.module.css"
|
|
9
|
+
|
|
10
|
+
export type RowProps<ComponentType extends ElementType = "div"> = ComponentProps<ComponentType> &
|
|
11
|
+
RowInternalProps
|
|
12
|
+
|
|
13
|
+
interface RowInternalProps {
|
|
14
|
+
alignItems?: CSSProperties["alignItems"]
|
|
15
|
+
as?: keyof JSX.IntrinsicElements
|
|
16
|
+
flexWrap?: CSSProperties["flexWrap"]
|
|
17
|
+
gap?: CSSProperties["gap"]
|
|
18
|
+
gapX?: CSSProperties["columnGap"]
|
|
19
|
+
gapY?: CSSProperties["rowGap"]
|
|
20
|
+
justifyContent?: CSSProperties["justifyContent"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function Row(props: RowProps): JSX.Element {
|
|
24
|
+
const {
|
|
25
|
+
alignItems = undefined,
|
|
26
|
+
as = "div",
|
|
27
|
+
className: customClassName = "",
|
|
28
|
+
flexWrap = undefined,
|
|
29
|
+
gap = undefined,
|
|
30
|
+
gapX = undefined,
|
|
31
|
+
gapY = undefined,
|
|
32
|
+
justifyContent = undefined,
|
|
33
|
+
style: customStyle,
|
|
34
|
+
...rest
|
|
35
|
+
} = props
|
|
36
|
+
|
|
37
|
+
const Component = as
|
|
38
|
+
|
|
39
|
+
const combinedClassName = `
|
|
40
|
+
${ROW_CLASS_NAME.BASE}
|
|
41
|
+
${customClassName}
|
|
42
|
+
`
|
|
43
|
+
.replaceAll(/\s+/g, " ")
|
|
44
|
+
.trim()
|
|
45
|
+
|
|
46
|
+
const combinedStyle = {
|
|
47
|
+
...customStyle,
|
|
48
|
+
...(gap === undefined ? {} : { gap }),
|
|
49
|
+
...(gapX === undefined ? {} : { columnGap: gapX }),
|
|
50
|
+
...(gapY === undefined ? {} : { rowGap: gapY }),
|
|
51
|
+
...(flexWrap === undefined ? {} : { flexWrap }),
|
|
52
|
+
...(alignItems === undefined ? {} : { alignItems }),
|
|
53
|
+
...(justifyContent === undefined ? {} : { justifyContent }),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return createElement(Component, {
|
|
57
|
+
className: combinedClassName,
|
|
58
|
+
style: combinedStyle,
|
|
59
|
+
...rest,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const ROW_CLASS_NAME = {
|
|
64
|
+
BASE: styles.row,
|
|
65
|
+
} as const
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ComponentProps, CSSProperties } from "react"
|
|
2
|
+
import styles from "@/components/spacer/spacer.module.css"
|
|
3
|
+
|
|
4
|
+
export interface SpacerProps extends Omit<ComponentProps<"div">, "children"> {
|
|
5
|
+
height?: CSSProperties["height"]
|
|
6
|
+
width?: CSSProperties["width"]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Spacer(props: SpacerProps) {
|
|
10
|
+
const { className: customClassName = "", height, style: customStyle, width, ...rest } = props
|
|
11
|
+
|
|
12
|
+
const combinedClassName = `
|
|
13
|
+
${SPACER_CLASS_NAME.BASE}
|
|
14
|
+
${customClassName}
|
|
15
|
+
`
|
|
16
|
+
.replaceAll(/\s+/g, " ")
|
|
17
|
+
.trim()
|
|
18
|
+
|
|
19
|
+
const combinedStyle = {
|
|
20
|
+
...customStyle,
|
|
21
|
+
...(width === undefined ? {} : { width }),
|
|
22
|
+
...(height === undefined ? {} : { height }),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return <div className={combinedClassName} style={combinedStyle} {...rest} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const SPACER_CLASS_NAME = {
|
|
29
|
+
BASE: styles.spacer,
|
|
30
|
+
} as const
|