@lunar-js/lunar 0.0.1
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 +12 -0
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/components/composite/Card/Card.d.ts +64 -0
- package/dist/components/composite/Card/Card.d.ts.map +1 -0
- package/dist/components/composite/Card/Card.js +86 -0
- package/dist/components/composite/Card/Card.js.map +1 -0
- package/dist/components/composite/Card/card.css.js +62 -0
- package/dist/components/composite/Card/card.css.js.map +1 -0
- package/dist/components/composite/Dialog/Dialog.d.ts +45 -0
- package/dist/components/composite/Dialog/Dialog.d.ts.map +1 -0
- package/dist/components/composite/Dialog/Dialog.js +117 -0
- package/dist/components/composite/Dialog/Dialog.js.map +1 -0
- package/dist/components/composite/Dialog/DialogProvider.d.ts +11 -0
- package/dist/components/composite/Dialog/DialogProvider.d.ts.map +1 -0
- package/dist/components/composite/Dialog/DialogProvider.js +19 -0
- package/dist/components/composite/Dialog/DialogProvider.js.map +1 -0
- package/dist/components/composite/Dialog/dialog.css.js +104 -0
- package/dist/components/composite/Dialog/dialog.css.js.map +1 -0
- package/dist/components/primitives/Button/Button.d.ts +27 -0
- package/dist/components/primitives/Button/Button.d.ts.map +1 -0
- package/dist/components/primitives/Button/Button.js +25 -0
- package/dist/components/primitives/Button/Button.js.map +1 -0
- package/dist/components/primitives/Button/button.css.d.ts +16 -0
- package/dist/components/primitives/Button/button.css.d.ts.map +1 -0
- package/dist/components/primitives/Button/button.css.js +202 -0
- package/dist/components/primitives/Button/button.css.js.map +1 -0
- package/dist/components/primitives/Button/button.types.d.ts +8 -0
- package/dist/components/primitives/Button/button.types.d.ts.map +1 -0
- package/dist/components/primitives/Input/Input.d.ts +12 -0
- package/dist/components/primitives/Input/Input.d.ts.map +1 -0
- package/dist/components/primitives/Input/Input.js +21 -0
- package/dist/components/primitives/Input/Input.js.map +1 -0
- package/dist/components/primitives/Input/input.css.js +54 -0
- package/dist/components/primitives/Input/input.css.js.map +1 -0
- package/dist/components/primitives/Label/Label.d.ts +13 -0
- package/dist/components/primitives/Label/Label.d.ts.map +1 -0
- package/dist/components/primitives/Label/Label.js +22 -0
- package/dist/components/primitives/Label/Label.js.map +1 -0
- package/dist/components/primitives/Label/label.css.js +35 -0
- package/dist/components/primitives/Label/label.css.js.map +1 -0
- package/dist/components/primitives/Typography/Text.d.ts +40 -0
- package/dist/components/primitives/Typography/Text.d.ts.map +1 -0
- package/dist/components/primitives/Typography/Text.js +28 -0
- package/dist/components/primitives/Typography/Text.js.map +1 -0
- package/dist/components/primitives/Typography/text.css.d.ts +31 -0
- package/dist/components/primitives/Typography/text.css.d.ts.map +1 -0
- package/dist/components/primitives/Typography/text.css.js +1019 -0
- package/dist/components/primitives/Typography/text.css.js.map +1 -0
- package/dist/components/primitives/Typography/text.types.d.ts +10 -0
- package/dist/components/primitives/Typography/text.types.d.ts.map +1 -0
- package/dist/constants/theming.d.ts +10 -0
- package/dist/constants/theming.d.ts.map +1 -0
- package/dist/constants/theming.js +11 -0
- package/dist/constants/theming.js.map +1 -0
- package/dist/hooks/dialog.d.ts +7 -0
- package/dist/hooks/dialog.d.ts.map +1 -0
- package/dist/hooks/dialog.js +14 -0
- package/dist/hooks/dialog.js.map +1 -0
- package/dist/hooks/refs.js +23 -0
- package/dist/hooks/refs.js.map +1 -0
- package/dist/hooks/theme.d.ts +7 -0
- package/dist/hooks/theme.d.ts.map +1 -0
- package/dist/hooks/theme.js +14 -0
- package/dist/hooks/theme.js.map +1 -0
- package/dist/hooks/utils.js +6 -0
- package/dist/hooks/utils.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/styles.css.d.ts +4 -0
- package/dist/styles.css.js +5 -0
- package/dist/themes/ThemeProvider/ThemeProvider.d.ts +19 -0
- package/dist/themes/ThemeProvider/ThemeProvider.d.ts.map +1 -0
- package/dist/themes/ThemeProvider/ThemeProvider.js +25 -0
- package/dist/themes/ThemeProvider/ThemeProvider.js.map +1 -0
- package/dist/themes/regal.css.d.ts +5 -0
- package/dist/themes/regal.css.d.ts.map +1 -0
- package/dist/themes/regal.css.js +720 -0
- package/dist/themes/regal.css.js.map +1 -0
- package/dist/themes/styles/color-scheme.css.d.ts +6 -0
- package/dist/themes/styles/color-scheme.css.d.ts.map +1 -0
- package/dist/themes/styles/color-scheme.css.js +9 -0
- package/dist/themes/styles/color-scheme.css.js.map +1 -0
- package/dist/themes/styles/utilities.d.ts +125 -0
- package/dist/themes/styles/utilities.d.ts.map +1 -0
- package/dist/themes/styles/utilities.js +129 -0
- package/dist/themes/styles/utilities.js.map +1 -0
- package/dist/themes/tokens/primitives/borders.d.ts +22 -0
- package/dist/themes/tokens/primitives/borders.d.ts.map +1 -0
- package/dist/themes/tokens/primitives/borders.js +23 -0
- package/dist/themes/tokens/primitives/borders.js.map +1 -0
- package/dist/themes/tokens/primitives/colors.d.ts +100 -0
- package/dist/themes/tokens/primitives/colors.d.ts.map +1 -0
- package/dist/themes/tokens/primitives/colors.js +101 -0
- package/dist/themes/tokens/primitives/colors.js.map +1 -0
- package/dist/themes/tokens/primitives/shadows.d.ts +14 -0
- package/dist/themes/tokens/primitives/shadows.d.ts.map +1 -0
- package/dist/themes/tokens/primitives/shadows.js +15 -0
- package/dist/themes/tokens/primitives/shadows.js.map +1 -0
- package/dist/themes/tokens/primitives/spacing.d.ts +41 -0
- package/dist/themes/tokens/primitives/spacing.d.ts.map +1 -0
- package/dist/themes/tokens/primitives/spacing.js +42 -0
- package/dist/themes/tokens/primitives/spacing.js.map +1 -0
- package/dist/themes/tokens/primitives/typography.d.ts +105 -0
- package/dist/themes/tokens/primitives/typography.d.ts.map +1 -0
- package/dist/themes/tokens/primitives/typography.js +106 -0
- package/dist/themes/tokens/primitives/typography.js.map +1 -0
- package/dist/themes/tokens/semantic/borders.js +23 -0
- package/dist/themes/tokens/semantic/borders.js.map +1 -0
- package/dist/themes/tokens/semantic/colors.js +145 -0
- package/dist/themes/tokens/semantic/colors.js.map +1 -0
- package/dist/themes/tokens/semantic/shadows.js +15 -0
- package/dist/themes/tokens/semantic/shadows.js.map +1 -0
- package/dist/themes/tokens/semantic/spacing.js +70 -0
- package/dist/themes/tokens/semantic/spacing.js.map +1 -0
- package/dist/themes/tokens/semantic/typography.js +34 -0
- package/dist/themes/tokens/semantic/typography.js.map +1 -0
- package/dist/themes/tokens/tokens.css.d.ts +714 -0
- package/dist/themes/tokens/tokens.css.d.ts.map +1 -0
- package/dist/themes/tokens/tokens.css.js +36 -0
- package/dist/themes/tokens/tokens.css.js.map +1 -0
- package/dist/types/theming.d.ts +7 -0
- package/dist/types/theming.d.ts.map +1 -0
- package/package.json +80 -0
- package/src/components/composite/Card/Card.tsx +62 -0
- package/src/components/composite/Card/card.css.ts +79 -0
- package/src/components/composite/Dialog/Dialog.tsx +150 -0
- package/src/components/composite/Dialog/DialogProvider.tsx +21 -0
- package/src/components/composite/Dialog/dialog.css.ts +137 -0
- package/src/components/primitives/Button/Button.tsx +35 -0
- package/src/components/primitives/Button/button.css.ts +236 -0
- package/src/components/primitives/Button/button.types.ts +23 -0
- package/src/components/primitives/Input/Input.tsx +13 -0
- package/src/components/primitives/Input/input.css.ts +64 -0
- package/src/components/primitives/Label/Label.tsx +15 -0
- package/src/components/primitives/Label/label.css.ts +39 -0
- package/src/components/primitives/Typography/Text.tsx +55 -0
- package/src/components/primitives/Typography/text.css.ts +1091 -0
- package/src/components/primitives/Typography/text.types.ts +55 -0
- package/src/constants/theming.ts +16 -0
- package/src/hooks/dialog.ts +15 -0
- package/src/hooks/refs.ts +34 -0
- package/src/hooks/theme.ts +15 -0
- package/src/hooks/utils.ts +3 -0
- package/src/index.css.ts +26 -0
- package/src/index.ts +111 -0
- package/src/themes/ThemeProvider/ThemeProvider.tsx +39 -0
- package/src/themes/regal.css.ts +741 -0
- package/src/themes/styles/color-scheme.css.ts +11 -0
- package/src/themes/styles/utilities.ts +140 -0
- package/src/themes/tokens/primitives/borders.ts +21 -0
- package/src/themes/tokens/primitives/colors.ts +114 -0
- package/src/themes/tokens/primitives/shadows.ts +12 -0
- package/src/themes/tokens/primitives/spacing.ts +39 -0
- package/src/themes/tokens/primitives/typography.ts +125 -0
- package/src/themes/tokens/semantic/borders.ts +21 -0
- package/src/themes/tokens/semantic/colors.ts +166 -0
- package/src/themes/tokens/semantic/shadows.ts +12 -0
- package/src/themes/tokens/semantic/spacing.ts +75 -0
- package/src/themes/tokens/semantic/typography.ts +35 -0
- package/src/themes/tokens/tokens.css.ts +42 -0
- package/src/types/theming.ts +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.css.d.ts","names":[],"sources":["../../../src/themes/tokens/tokens.css.ts"],"sourcesContent":[],"mappings":";cAgBM;EAAA,MAAA,EAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ACTION_COLORS, BORDER_COLORS, ICON_COLORS, INPUT_COLORS, SHADOW_COLORS, SURFACE_COLORS, TEXT_COLORS } from "./semantic/colors.js";
|
|
2
|
+
import { SPACING } from "./semantic/spacing.js";
|
|
3
|
+
import { FONT_FAMILY, TYPOGRAPHY_THEME_CONTRACT } from "./semantic/typography.js";
|
|
4
|
+
import { BORDER_RADIUS, BORDER_WIDTH } from "./semantic/borders.js";
|
|
5
|
+
import { BOX_SHADOWS } from "./semantic/shadows.js";
|
|
6
|
+
import { createThemeContract } from "@vanilla-extract/css";
|
|
7
|
+
|
|
8
|
+
//#region src/themes/tokens/tokens.css.ts
|
|
9
|
+
const themeContract = createThemeContract({
|
|
10
|
+
colors: {
|
|
11
|
+
...ACTION_COLORS,
|
|
12
|
+
...TEXT_COLORS,
|
|
13
|
+
...SURFACE_COLORS,
|
|
14
|
+
...BORDER_COLORS,
|
|
15
|
+
...ICON_COLORS,
|
|
16
|
+
...INPUT_COLORS,
|
|
17
|
+
...SHADOW_COLORS
|
|
18
|
+
},
|
|
19
|
+
typography: {
|
|
20
|
+
fontFamily: FONT_FAMILY,
|
|
21
|
+
display: TYPOGRAPHY_THEME_CONTRACT,
|
|
22
|
+
heading: TYPOGRAPHY_THEME_CONTRACT,
|
|
23
|
+
subheading: TYPOGRAPHY_THEME_CONTRACT,
|
|
24
|
+
body: TYPOGRAPHY_THEME_CONTRACT,
|
|
25
|
+
caption: TYPOGRAPHY_THEME_CONTRACT,
|
|
26
|
+
label: TYPOGRAPHY_THEME_CONTRACT
|
|
27
|
+
},
|
|
28
|
+
spacing: SPACING,
|
|
29
|
+
borderRadius: BORDER_RADIUS,
|
|
30
|
+
borderWidth: BORDER_WIDTH,
|
|
31
|
+
boxShadow: BOX_SHADOWS
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { themeContract };
|
|
36
|
+
//# sourceMappingURL=tokens.css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.css.js","names":[],"sources":["../../../src/themes/tokens/tokens.css.ts"],"sourcesContent":["import { createThemeContract } from '@vanilla-extract/css';\n\nimport {\n ACTION_COLORS,\n BORDER_COLORS,\n ICON_COLORS,\n INPUT_COLORS,\n SHADOW_COLORS,\n SURFACE_COLORS,\n TEXT_COLORS,\n} from './semantic/colors.js';\nimport { SPACING } from './semantic/spacing.js';\nimport { FONT_FAMILY, TYPOGRAPHY_THEME_CONTRACT } from './semantic/typography.js';\nimport { BORDER_RADIUS, BORDER_WIDTH } from './semantic/borders.js';\nimport { BOX_SHADOWS } from './semantic/shadows.js';\n\nconst themeContract = createThemeContract({\n colors: {\n ...ACTION_COLORS,\n ...TEXT_COLORS,\n ...SURFACE_COLORS,\n ...BORDER_COLORS,\n ...ICON_COLORS,\n ...INPUT_COLORS,\n ...SHADOW_COLORS,\n },\n typography: {\n fontFamily: FONT_FAMILY,\n display: TYPOGRAPHY_THEME_CONTRACT,\n heading: TYPOGRAPHY_THEME_CONTRACT,\n subheading: TYPOGRAPHY_THEME_CONTRACT,\n body: TYPOGRAPHY_THEME_CONTRACT,\n caption: TYPOGRAPHY_THEME_CONTRACT,\n label: TYPOGRAPHY_THEME_CONTRACT,\n },\n spacing: SPACING,\n borderRadius: BORDER_RADIUS,\n borderWidth: BORDER_WIDTH,\n boxShadow: BOX_SHADOWS,\n});\n\nexport { themeContract };\n"],"mappings":";;;;;;;;AAgBA,MAAM,gBAAgB,oBAAoB;CACxC,QAAQ;EACN,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACJ;CACD,YAAY;EACV,YAAY;EACZ,SAAS;EACT,SAAS;EACT,YAAY;EACZ,MAAM;EACN,SAAS;EACT,OAAO;EACR;CACD,SAAS;CACT,cAAc;CACd,aAAa;CACb,WAAW;CACZ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { COLOR_SCHEME__DARK, COLOR_SCHEME__LIGHT, COLOR_SCHEME__SYSTEM } from "../constants/theming.js";
|
|
2
|
+
|
|
3
|
+
//#region src/types/theming.d.ts
|
|
4
|
+
type ColorScheme = typeof COLOR_SCHEME__SYSTEM | typeof COLOR_SCHEME__DARK | typeof COLOR_SCHEME__LIGHT;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { type ColorScheme };
|
|
7
|
+
//# sourceMappingURL=theming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theming.d.ts","names":[],"sources":["../../src/types/theming.ts"],"sourcesContent":[],"mappings":";;;KASK,WAAA,UAAqB,8BAA8B,4BAA4B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "@lunar-js/lunar",
|
|
4
|
+
"description": "Lunar is a radix/shadcn inspired component/design library",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/lunar-js/lunar.git",
|
|
8
|
+
"directory": "packages/lunar"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/lunar-js/lunar/tree/main/packages/lunar#readme",
|
|
11
|
+
"version": "0.0.1",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./styles.css": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/styles.css.d.ts",
|
|
26
|
+
"default": "./dist/styles.css.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist/",
|
|
34
|
+
"src/",
|
|
35
|
+
"*.md"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@radix-ui/react-label": "^2.1.8",
|
|
39
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
40
|
+
"dialog-closedby-polyfill": "^1.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@vanilla-extract/recipes": "0.5.7",
|
|
44
|
+
"@vanilla-extract/sprinkles": "^1.6.5",
|
|
45
|
+
"clsx": "^2.1.1",
|
|
46
|
+
"react": "^19.1.1",
|
|
47
|
+
"react-dom": "^19.1.1"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@vanilla-extract/css": "^1",
|
|
51
|
+
"@vanilla-extract/recipes": "^0",
|
|
52
|
+
"@vanilla-extract/sprinkles": "^1",
|
|
53
|
+
"clsx": "^2",
|
|
54
|
+
"react": "^19",
|
|
55
|
+
"react-dom": "^19"
|
|
56
|
+
},
|
|
57
|
+
"size-limit": [
|
|
58
|
+
{
|
|
59
|
+
"path": "./dist/index.js",
|
|
60
|
+
"import": "{}",
|
|
61
|
+
"limit": "20 B"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"scripts": {
|
|
66
|
+
"____ RUN COMMANDS ____": "",
|
|
67
|
+
"dev": "tsdown --watch",
|
|
68
|
+
"____ BUILD COMMANDS ____": "",
|
|
69
|
+
"build": "pnpm run clean:build && pnpm run build:dist",
|
|
70
|
+
"build:dist": "tsdown --config ./tsdown.config.ts --format esm",
|
|
71
|
+
"____ VALIDATION COMMANDS ____": "",
|
|
72
|
+
"lint": "eslint --max-warnings=0 .",
|
|
73
|
+
"lint:fix": "pnpm run lint --fix",
|
|
74
|
+
"typecheck": "tsc -p ./tsconfig.json --noEmit",
|
|
75
|
+
"size": "size-limit",
|
|
76
|
+
"____ CLEANUP COMMANDS ____": "",
|
|
77
|
+
"clean": "pnpm run clean:build",
|
|
78
|
+
"clean:build": "rimraf dist"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
|
|
4
|
+
import { card, cardHeader, cardTitle, cardDescription, cardAction, cardContent, cardFooter } from './card.css.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Main card container component that provides a styled container with elevation and borders.
|
|
8
|
+
* Serves as the foundation for card-based layouts and content organization.
|
|
9
|
+
*/
|
|
10
|
+
const Card = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
11
|
+
return <div data-slot="card" className={clsx(card, className)} {...props} />;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Header section of the card, typically containing the card title and description.
|
|
16
|
+
* Provides consistent spacing and layout for the card's top section.
|
|
17
|
+
*/
|
|
18
|
+
const CardHeader = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
19
|
+
return <div data-slot="card-header" className={clsx(cardHeader, className)} {...props} />;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Title component for the card header with appropriate typography styling.
|
|
24
|
+
* Provides semantic structure and consistent text hierarchy.
|
|
25
|
+
*/
|
|
26
|
+
const CardTitle = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
27
|
+
return <div data-slot="card-title" className={clsx(cardTitle, className)} {...props} />;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Description component for the card header with muted text styling.
|
|
32
|
+
* Used to provide additional context or subtitle information.
|
|
33
|
+
*/
|
|
34
|
+
const CardDescription = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
35
|
+
return <div data-slot="card-description" className={clsx(cardDescription, className)} {...props} />;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Action area component for the card, typically containing buttons or interactive elements.
|
|
40
|
+
* Provides proper spacing and alignment for card actions.
|
|
41
|
+
*/
|
|
42
|
+
const CardAction = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
43
|
+
return <div data-slot="card-action" className={clsx(cardAction, className)} {...props} />;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Main content area of the card with appropriate padding and spacing.
|
|
48
|
+
* Houses the primary content between the header and footer sections.
|
|
49
|
+
*/
|
|
50
|
+
const CardContent = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
51
|
+
return <div data-slot="card-content" className={clsx(cardContent, className)} {...props} />;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Footer section of the card, typically used for actions, metadata, or additional information.
|
|
56
|
+
* Provides consistent spacing and styling for the card's bottom section.
|
|
57
|
+
*/
|
|
58
|
+
const CardFooter = ({ className, ...props }: React.ComponentProps<'div'>) => {
|
|
59
|
+
return <div data-slot="card-footer" className={clsx(cardFooter, className)} {...props} />;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { style } from '@vanilla-extract/css';
|
|
2
|
+
|
|
3
|
+
import { themeContract } from '../../../themes/tokens/tokens.css.js';
|
|
4
|
+
|
|
5
|
+
const card = style({
|
|
6
|
+
backgroundColor: themeContract.colors.surface.bg.secondary,
|
|
7
|
+
color: themeContract.colors.text.primary,
|
|
8
|
+
display: 'flex',
|
|
9
|
+
flexDirection: 'column',
|
|
10
|
+
gap: themeContract.spacing[6],
|
|
11
|
+
borderRadius: themeContract.borderRadius['2xl'],
|
|
12
|
+
border: `${themeContract.borderWidth[1]} solid ${themeContract.colors.border.default}`,
|
|
13
|
+
paddingTop: themeContract.spacing[6],
|
|
14
|
+
paddingBottom: themeContract.spacing[6],
|
|
15
|
+
boxShadow: themeContract.boxShadow.lg,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const cardHeader = style({
|
|
19
|
+
containerType: 'inline-size',
|
|
20
|
+
containerName: 'card-header',
|
|
21
|
+
display: 'grid',
|
|
22
|
+
gridAutoRows: 'min-content',
|
|
23
|
+
gridTemplateRows: 'auto auto',
|
|
24
|
+
alignItems: 'flex-start',
|
|
25
|
+
gap: themeContract.spacing[2],
|
|
26
|
+
paddingLeft: themeContract.spacing[6],
|
|
27
|
+
paddingRight: themeContract.spacing[6],
|
|
28
|
+
|
|
29
|
+
// Conditional grid columns when card action is present
|
|
30
|
+
selectors: {
|
|
31
|
+
'&:has([data-slot="card-action"])': {
|
|
32
|
+
gridTemplateColumns: '1fr auto',
|
|
33
|
+
},
|
|
34
|
+
// Conditional padding when border-b class is present
|
|
35
|
+
'&.border-b': {
|
|
36
|
+
paddingBottom: themeContract.spacing[6],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const cardTitle = style({
|
|
42
|
+
lineHeight: '1',
|
|
43
|
+
fontWeight: themeContract.typography.body.md.bold.fontWeight,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const cardDescription = style({
|
|
47
|
+
color: themeContract.colors.text.secondary,
|
|
48
|
+
fontSize: themeContract.typography.body.sm.medium.fontSize,
|
|
49
|
+
lineHeight: themeContract.typography.body.sm.medium.lineHeight,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const cardAction = style({
|
|
53
|
+
gridColumn: '2',
|
|
54
|
+
gridRowStart: '1',
|
|
55
|
+
gridRowEnd: 'span 2',
|
|
56
|
+
alignSelf: 'flex-start',
|
|
57
|
+
justifySelf: 'end',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const cardContent = style({
|
|
61
|
+
paddingLeft: themeContract.spacing[6],
|
|
62
|
+
paddingRight: themeContract.spacing[6],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const cardFooter = style({
|
|
66
|
+
display: 'flex',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
paddingLeft: themeContract.spacing[6],
|
|
69
|
+
paddingRight: themeContract.spacing[6],
|
|
70
|
+
|
|
71
|
+
// Conditional padding when border-t class is present
|
|
72
|
+
selectors: {
|
|
73
|
+
'&.border-t': {
|
|
74
|
+
paddingTop: themeContract.spacing[6],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export { card, cardTitle, cardAction, cardFooter, cardHeader, cardContent, cardDescription };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentProps,
|
|
3
|
+
type FC,
|
|
4
|
+
type MouseEvent,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
useEffect,
|
|
7
|
+
useEffectEvent,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import clsx from 'clsx';
|
|
12
|
+
// TODO: Remove once there's full browser support for Dialog's closedby attribute.
|
|
13
|
+
import 'dialog-closedby-polyfill';
|
|
14
|
+
|
|
15
|
+
import DialogProvider from './DialogProvider.js';
|
|
16
|
+
import { useDialog } from '../../../hooks/dialog.js';
|
|
17
|
+
import { useMergedRef } from '../../../hooks/refs.js';
|
|
18
|
+
import Button, { type ButtonProps } from '../../primitives/Button/Button.js';
|
|
19
|
+
import { dialog, dialogContent, dialogFooter, dialogHeader } from './dialog.css.js';
|
|
20
|
+
|
|
21
|
+
interface DialogProps extends ComponentProps<'dialog'> {
|
|
22
|
+
/**
|
|
23
|
+
* Optional function that renders the trigger element for the dialog.
|
|
24
|
+
* This allows custom trigger components while maintaining dialog functionality.
|
|
25
|
+
*/
|
|
26
|
+
renderTrigger?: () => ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Main Dialog component that renders a modal dialog element.
|
|
31
|
+
* Handles dialog state management and provides context for child components.
|
|
32
|
+
*/
|
|
33
|
+
const Dialog: FC<DialogProps> = ({
|
|
34
|
+
className,
|
|
35
|
+
open = false,
|
|
36
|
+
closedby = 'any',
|
|
37
|
+
renderTrigger,
|
|
38
|
+
ref: forwardedRef,
|
|
39
|
+
...props
|
|
40
|
+
}) => {
|
|
41
|
+
const internalDialogRef = useRef<HTMLDialogElement>(null);
|
|
42
|
+
const mergedRef = useMergedRef(internalDialogRef, forwardedRef);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<DialogProvider isOpen={open} dialogRef={internalDialogRef}>
|
|
46
|
+
{renderTrigger?.()}
|
|
47
|
+
|
|
48
|
+
<dialog
|
|
49
|
+
ref={mergedRef}
|
|
50
|
+
open={open}
|
|
51
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
52
|
+
closedby={closedby}
|
|
53
|
+
data-slot="dialog"
|
|
54
|
+
className={clsx(dialog, className)}
|
|
55
|
+
aria-modal="true"
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
</DialogProvider>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Button component that triggers the dialog to open.
|
|
64
|
+
* Automatically handles aria attributes and dialog state management.
|
|
65
|
+
*/
|
|
66
|
+
const DialogTrigger: FC<ButtonProps> = ({ onClick, ...props }) => {
|
|
67
|
+
const { dialogRef, isOpen } = useDialog();
|
|
68
|
+
|
|
69
|
+
const [isAriaExpanded, setIsAriaExpanded] = useState(isOpen);
|
|
70
|
+
|
|
71
|
+
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
|
|
72
|
+
onClick?.(event);
|
|
73
|
+
if (event.defaultPrevented || event.isPropagationStopped()) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setIsAriaExpanded(true);
|
|
78
|
+
dialogRef.current?.showModal();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const handleClose = useEffectEvent(() => {
|
|
82
|
+
setIsAriaExpanded(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const dialog = dialogRef.current;
|
|
87
|
+
if (!dialog) return;
|
|
88
|
+
|
|
89
|
+
dialog.addEventListener('close', handleClose);
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
dialog.removeEventListener('close', handleClose);
|
|
93
|
+
};
|
|
94
|
+
}, [dialogRef]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Button
|
|
98
|
+
aria-expanded={isAriaExpanded}
|
|
99
|
+
aria-haspopup="dialog"
|
|
100
|
+
data-slot="dialog-trigger"
|
|
101
|
+
onClick={handleClick}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Button component that closes the dialog when clicked.
|
|
109
|
+
* Handles dialog close functionality while preserving custom onClick handlers.
|
|
110
|
+
*/
|
|
111
|
+
const DialogClose: FC<ButtonProps> = ({ onClick, ...props }) => {
|
|
112
|
+
const { dialogRef } = useDialog();
|
|
113
|
+
|
|
114
|
+
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
|
|
115
|
+
onClick?.(event);
|
|
116
|
+
if (event.defaultPrevented || event.isPropagationStopped()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
dialogRef.current?.close();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return <Button onClick={handleClick} {...props} />;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Container component for the main content area of the dialog.
|
|
128
|
+
* Provides semantic structure and consistent styling for dialog content.
|
|
129
|
+
*/
|
|
130
|
+
const DialogContent: FC<ComponentProps<'div'>> = ({ className, ...props }) => {
|
|
131
|
+
return <div data-slot="dialog-content" className={clsx(dialogContent, className)} {...props} />;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Header component for the dialog, typically containing the dialog title.
|
|
136
|
+
* Provides consistent spacing and styling for the dialog header area.
|
|
137
|
+
*/
|
|
138
|
+
const DialogHeader: FC<ComponentProps<'div'>> = ({ className, ...props }) => {
|
|
139
|
+
return <div data-slot="dialog-header" className={clsx(dialogHeader, className)} {...props} />;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Footer component for the dialog, typically containing action buttons.
|
|
144
|
+
* Provides consistent spacing and styling for the dialog footer area.
|
|
145
|
+
*/
|
|
146
|
+
const DialogFooter: FC<ComponentProps<'div'>> = ({ className, ...props }) => {
|
|
147
|
+
return <div data-slot="dialog-footer" className={clsx(dialogFooter, className)} {...props} />;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export { Dialog, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createContext, type PropsWithChildren, type RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
interface DialogContextProps {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
dialogRef: RefObject<HTMLDialogElement | null>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const DialogContext = createContext<DialogContextProps | null>(null);
|
|
9
|
+
|
|
10
|
+
interface DialogProviderProps extends PropsWithChildren {
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
dialogRef: RefObject<HTMLDialogElement | null>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DialogProvider = ({ children, isOpen, dialogRef }: DialogProviderProps) => {
|
|
16
|
+
return <DialogContext.Provider value={{ isOpen, dialogRef }}>{children}</DialogContext.Provider>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type { DialogContextProps };
|
|
20
|
+
export default DialogProvider;
|
|
21
|
+
export { DialogContext };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { style, keyframes } from '@vanilla-extract/css';
|
|
2
|
+
|
|
3
|
+
import { themeContract } from '../../../themes/tokens/tokens.css.js';
|
|
4
|
+
import { withBreakpoint, withSafeTransition } from '../../../themes/styles/utilities.js';
|
|
5
|
+
import { BREAKPOINT__SM, BREAKPOINT__MD } from '../../../constants/theming.js';
|
|
6
|
+
|
|
7
|
+
const fadeIn = keyframes({
|
|
8
|
+
from: {
|
|
9
|
+
opacity: '0',
|
|
10
|
+
transform: 'scale(0.95) translateY(-10px)',
|
|
11
|
+
},
|
|
12
|
+
to: {
|
|
13
|
+
opacity: '1',
|
|
14
|
+
transform: 'scale(1) translateY(0)',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const fadeOut = keyframes({
|
|
19
|
+
from: {
|
|
20
|
+
display: 'block',
|
|
21
|
+
opacity: '1',
|
|
22
|
+
transform: 'scale(1) translateY(0)',
|
|
23
|
+
},
|
|
24
|
+
to: {
|
|
25
|
+
display: 'none',
|
|
26
|
+
opacity: '0',
|
|
27
|
+
transform: 'scale(0.95) translateY(-10px)',
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fadeInBackdrop = keyframes({
|
|
32
|
+
from: {
|
|
33
|
+
opacity: '0',
|
|
34
|
+
},
|
|
35
|
+
to: {
|
|
36
|
+
opacity: '1',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const fadeOutBackdrop = keyframes({
|
|
41
|
+
from: {
|
|
42
|
+
opacity: '1',
|
|
43
|
+
},
|
|
44
|
+
to: {
|
|
45
|
+
opacity: '0',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const dialog = style([
|
|
50
|
+
{
|
|
51
|
+
boxSizing: 'border-box',
|
|
52
|
+
padding: themeContract.spacing[4],
|
|
53
|
+
border: 'none',
|
|
54
|
+
backgroundColor: themeContract.colors.surface.bg.primary,
|
|
55
|
+
color: themeContract.colors.text.primary,
|
|
56
|
+
borderRadius: themeContract.borderRadius.lg,
|
|
57
|
+
boxShadow: themeContract.boxShadow.xl,
|
|
58
|
+
width: '100vw',
|
|
59
|
+
height: '100vh',
|
|
60
|
+
maxWidth: '100vw',
|
|
61
|
+
maxHeight: '100vh',
|
|
62
|
+
|
|
63
|
+
'::backdrop': {
|
|
64
|
+
filter: 'brightness(0%)',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
withSafeTransition({
|
|
68
|
+
transition: 'display 150ms allow-discrete, overlay 150ms allow-discrete',
|
|
69
|
+
animation: `${fadeOut} 150ms cubic-bezier(0.4, 0, 0.2, 1)`,
|
|
70
|
+
|
|
71
|
+
selectors: {
|
|
72
|
+
'&[open]': {
|
|
73
|
+
animation: `${fadeIn} 250ms cubic-bezier(0.34, 1.56, 0.64, 1)`,
|
|
74
|
+
},
|
|
75
|
+
'&[open]::backdrop': {
|
|
76
|
+
animation: `${fadeInBackdrop} 250ms`,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
'::backdrop': {
|
|
81
|
+
transition: 'display 150ms allow-discrete, overlay 150ms allow-discrete',
|
|
82
|
+
animation: `${fadeOutBackdrop} 150ms`,
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
withBreakpoint(BREAKPOINT__MD, {
|
|
86
|
+
width: 'fit-content',
|
|
87
|
+
height: 'fit-content',
|
|
88
|
+
maxWidth: '32rem',
|
|
89
|
+
maxHeight: 'unset',
|
|
90
|
+
}),
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const dialogContent = style([
|
|
94
|
+
{
|
|
95
|
+
display: 'flex',
|
|
96
|
+
flexDirection: 'column',
|
|
97
|
+
gap: themeContract.spacing[4],
|
|
98
|
+
minHeight: '100%',
|
|
99
|
+
padding: 0,
|
|
100
|
+
overflowY: 'auto',
|
|
101
|
+
WebkitOverflowScrolling: 'touch',
|
|
102
|
+
},
|
|
103
|
+
withBreakpoint(BREAKPOINT__SM, {
|
|
104
|
+
minHeight: 'auto',
|
|
105
|
+
maxHeight: 'calc(100vh - 64px)',
|
|
106
|
+
padding: themeContract.spacing[6],
|
|
107
|
+
}),
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
const dialogHeader = style([
|
|
111
|
+
{
|
|
112
|
+
display: 'flex',
|
|
113
|
+
flexDirection: 'column',
|
|
114
|
+
gap: themeContract.spacing[2],
|
|
115
|
+
paddingBottom: themeContract.spacing[2],
|
|
116
|
+
borderBottom: `${themeContract.borderWidth[1]} solid ${themeContract.colors.border.subtle}`,
|
|
117
|
+
},
|
|
118
|
+
withBreakpoint(BREAKPOINT__SM, {
|
|
119
|
+
paddingBottom: themeContract.spacing[4],
|
|
120
|
+
}),
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const dialogFooter = style([
|
|
124
|
+
{
|
|
125
|
+
display: 'flex',
|
|
126
|
+
flexDirection: 'column-reverse',
|
|
127
|
+
gap: themeContract.spacing[2],
|
|
128
|
+
paddingTop: themeContract.spacing[4],
|
|
129
|
+
marginTop: 'auto',
|
|
130
|
+
},
|
|
131
|
+
withBreakpoint(BREAKPOINT__SM, {
|
|
132
|
+
flexDirection: 'row',
|
|
133
|
+
justifyContent: 'flex-end',
|
|
134
|
+
}),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
export { dialog, dialogContent, dialogHeader, dialogFooter };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ComponentProps, type FC } from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
|
|
5
|
+
import { buttonVariants } from './button.css.js';
|
|
6
|
+
import type { ButtonSize, ButtonVariant } from './button.types.js';
|
|
7
|
+
|
|
8
|
+
interface ButtonProps extends ComponentProps<'button'> {
|
|
9
|
+
/**
|
|
10
|
+
* When true, the button will render as a child component using Radix UI's Slot primitive.
|
|
11
|
+
* This allows the button styles to be applied to any element while maintaining accessibility.
|
|
12
|
+
*/
|
|
13
|
+
asChild?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The visual style variant of the button.
|
|
16
|
+
*/
|
|
17
|
+
variant?: ButtonVariant;
|
|
18
|
+
/**
|
|
19
|
+
* The size of the button, affecting padding and font size.
|
|
20
|
+
*/
|
|
21
|
+
size?: ButtonSize;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Versatile button component with multiple variants and sizes.
|
|
26
|
+
* Supports rendering as child components via Radix UI's Slot primitive for flexible composition.
|
|
27
|
+
*/
|
|
28
|
+
const Button: FC<ButtonProps> = ({ className, size, variant, asChild = false, ...props }) => {
|
|
29
|
+
const Comp = asChild ? Slot : 'button';
|
|
30
|
+
|
|
31
|
+
return <Comp data-slot="button" className={clsx(buttonVariants({ variant, size }), className)} {...props} />;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type { ButtonProps };
|
|
35
|
+
export default Button;
|