@studiocubics/components 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 +11 -0
- package/README.md +71 -0
- package/eslint.config.js +21 -0
- package/package.json +66 -0
- package/rollup.config.js +34 -0
- package/src/Cards/Card/Card.module.css +27 -0
- package/src/Cards/Card/Card.tsx +105 -0
- package/src/Cards/CollectionItemCard/CollectionItemCard.module.css +84 -0
- package/src/Cards/CollectionItemCard/CollectionItemCard.tsx +170 -0
- package/src/Cards/CollectionItemCard/CollectionItemCardActions.tsx +85 -0
- package/src/Cards/CollectionItemCard/_index.ts +2 -0
- package/src/Cards/GlassCard/GlassCard.module.css +71 -0
- package/src/Cards/GlassCard/GlassCard.tsx +80 -0
- package/src/Cards/_index.ts +3 -0
- package/src/Display/Accordion/Accordion.module.css +69 -0
- package/src/Display/Accordion/Accordion.tsx +61 -0
- package/src/Display/Accordion/AccordionItem.tsx +135 -0
- package/src/Display/Accordion/_index.ts +2 -0
- package/src/Display/Chip/Chip.module.css +64 -0
- package/src/Display/Chip/Chip.tsx +105 -0
- package/src/Display/IdentityDisplay/IdentityDisplay.module.css +95 -0
- package/src/Display/IdentityDisplay/IdentityDisplay.tsx +119 -0
- package/src/Display/InputErrors/InputErrors.module.css +6 -0
- package/src/Display/InputErrors/InputErrors.tsx +52 -0
- package/src/Display/Kbd/Kbd.module.css +29 -0
- package/src/Display/Kbd/Kbd.tsx +39 -0
- package/src/Display/Kbd/_index.ts +2 -0
- package/src/Display/Kbd/buttonList.tsx +246 -0
- package/src/Display/LabeledValue/LabeledValue.module.css +32 -0
- package/src/Display/LabeledValue/LabeledValue.tsx +20 -0
- package/src/Display/List/List.module.css +143 -0
- package/src/Display/List/List.tsx +298 -0
- package/src/Display/PasswordStrength/PasswordStrength.module.css +45 -0
- package/src/Display/PasswordStrength/PasswordStrength.tsx +41 -0
- package/src/Display/PasswordStrength/usePasswordStrength.tsx +77 -0
- package/src/Display/Skeleton/Skeleton.module.css +54 -0
- package/src/Display/Skeleton/Skeleton.tsx +28 -0
- package/src/Display/Toast/Toaster.tsx +58 -0
- package/src/Display/Toast/_index.ts +2 -0
- package/src/Display/Toast/toast.ts +44 -0
- package/src/Display/Tooltip/Tooltip.module.css +128 -0
- package/src/Display/Tooltip/Tooltip.tsx +93 -0
- package/src/Display/Tooltip/getArrowDirection.ts +55 -0
- package/src/Display/Tooltip/useTooltip.tsx +63 -0
- package/src/Display/_index.ts +12 -0
- package/src/Forms/ConfirmationForm/ConfirmationForm.module.css +23 -0
- package/src/Forms/ConfirmationForm/ConfirmationForm.tsx +60 -0
- package/src/Forms/_index.ts +1 -0
- package/src/Inputs/Button/Button.module.css +131 -0
- package/src/Inputs/Button/Button.tsx +178 -0
- package/src/Inputs/Checkbox/Checkbox.module.css +77 -0
- package/src/Inputs/Checkbox/Checkbox.tsx +191 -0
- package/src/Inputs/Checkbox/CheckboxGroup/CheckboxGroup.module.css +10 -0
- package/src/Inputs/Checkbox/CheckboxGroup/CheckboxGroup.tsx +83 -0
- package/src/Inputs/Checkbox/CheckboxSelectAll.tsx +34 -0
- package/src/Inputs/Checkbox/_index.ts +3 -0
- package/src/Inputs/PasswordInput/PasswordInput.module.css +111 -0
- package/src/Inputs/PasswordInput/PasswordInput.tsx +229 -0
- package/src/Inputs/Select/Select.module.css +138 -0
- package/src/Inputs/Select/Select.tsx +136 -0
- package/src/Inputs/Switch/Switch.module.css +119 -0
- package/src/Inputs/Switch/Switch.tsx +195 -0
- package/src/Inputs/TextAreaInput/TextAreaInput.module.css +65 -0
- package/src/Inputs/TextAreaInput/TextAreaInput.tsx +97 -0
- package/src/Inputs/TextInput/TextInput.module.css +112 -0
- package/src/Inputs/TextInput/TextInput.tsx +142 -0
- package/src/Inputs/ThemeToggle/ThemeToggleListItem.tsx +80 -0
- package/src/Inputs/ThemeToggle/_index.ts +1 -0
- package/src/Inputs/_index.ts +8 -0
- package/src/Layout/Dialog/Dialog.module.css +15 -0
- package/src/Layout/Dialog/Dialog.tsx +115 -0
- package/src/Layout/PageLayout/PageLayout.module.css +20 -0
- package/src/Layout/PageLayout/PageLayout.tsx +79 -0
- package/src/Layout/PageLayoutPagination/PageLayoutPagination.module.css +5 -0
- package/src/Layout/PageLayoutPagination/PageLayoutPagination.tsx +40 -0
- package/src/Layout/PageLayoutTabs/PageLayoutTabs.module.css +3 -0
- package/src/Layout/PageLayoutTabs/PageLayoutTabs.tsx +62 -0
- package/src/Layout/Popover/Popover.module.css +9 -0
- package/src/Layout/Popover/Popover.tsx +145 -0
- package/src/Layout/SectionWrapper/SectionWrapper.module.css +31 -0
- package/src/Layout/SectionWrapper/SectionWrapper.tsx +62 -0
- package/src/Layout/Sidebar/Sidebar.module.css +17 -0
- package/src/Layout/Sidebar/Sidebar.tsx +39 -0
- package/src/Layout/Sidebar/SidebarBody/SidebarBody.module.css +31 -0
- package/src/Layout/Sidebar/SidebarBody/SidebarBody.tsx +18 -0
- package/src/Layout/Sidebar/SidebarDrawer/SidebarDrawer.module.css +20 -0
- package/src/Layout/Sidebar/SidebarDrawer/SidebarDrawer.tsx +19 -0
- package/src/Layout/Sidebar/SidebarFooter/SidebarFooter.module.css +35 -0
- package/src/Layout/Sidebar/SidebarFooter/SidebarFooter.tsx +19 -0
- package/src/Layout/Sidebar/SidebarHeader/SidebarHeader.tsx +14 -0
- package/src/Layout/Sidebar/SidebarViewport/SidebarViewport.module.css +12 -0
- package/src/Layout/Sidebar/SidebarViewport/SidebarViewport.tsx +11 -0
- package/src/Layout/Sidebar/_index.ts +6 -0
- package/src/Layout/Table/Table.module.css +46 -0
- package/src/Layout/Table/Table.tsx +222 -0
- package/src/Layout/Table/TableFooter.tsx +4 -0
- package/src/Layout/Table/TableHeader.tsx +4 -0
- package/src/Layout/Table/_index.ts +5 -0
- package/src/Layout/Table/tableUtils.ts +142 -0
- package/src/Layout/Table/types.ts +48 -0
- package/src/Layout/_index.ts +8 -0
- package/src/Misc/Cursor/Cursor.module.css +31 -0
- package/src/Misc/Cursor/Cursor.tsx +77 -0
- package/src/Misc/Logos.tsx +230 -0
- package/src/Misc/PoweredByBanner/PoweredByBanner.module.css +20 -0
- package/src/Misc/PoweredByBanner/PoweredByBanner.tsx +17 -0
- package/src/Misc/Ripple/Ripple.module.css +25 -0
- package/src/Misc/Ripple/Ripple.tsx +126 -0
- package/src/Misc/Spinner/Spinner.module.css +38 -0
- package/src/Misc/Spinner/Spinner.tsx +36 -0
- package/src/Misc/TransitionAnimation/TransitionAnimation.module.css +131 -0
- package/src/Misc/TransitionAnimation/TransitionAnimation.tsx +166 -0
- package/src/Misc/_index.ts +6 -0
- package/src/Navigation/Breadcrumbs/Breadcrumbs.module.css +22 -0
- package/src/Navigation/Breadcrumbs/Breadcrumbs.tsx +127 -0
- package/src/Navigation/Breadcrumbs/BreadcrumbsItem.tsx +31 -0
- package/src/Navigation/Breadcrumbs/_index.ts +3 -0
- package/src/Navigation/Breadcrumbs/useBreadcrumbs.tsx +74 -0
- package/src/Navigation/Pagination/Pagination.module.css +41 -0
- package/src/Navigation/Pagination/Pagination.tsx +187 -0
- package/src/Navigation/Pagination/PaginationItem.tsx +28 -0
- package/src/Navigation/Pagination/_index.ts +3 -0
- package/src/Navigation/Pagination/usePagination.tsx +65 -0
- package/src/Navigation/Tabs/Tab/Tab.module.css +43 -0
- package/src/Navigation/Tabs/Tab/Tab.tsx +155 -0
- package/src/Navigation/Tabs/Tabs.tsx +37 -0
- package/src/Navigation/Tabs/TabsBar/TabsBar.module.css +47 -0
- package/src/Navigation/Tabs/TabsBar/TabsBar.tsx +92 -0
- package/src/Navigation/Tabs/_index.ts +3 -0
- package/src/Navigation/_index.ts +3 -0
- package/src/Typography/ClampedText/ClampedText.module.css +5 -0
- package/src/Typography/ClampedText/ClampedText.tsx +77 -0
- package/src/Typography/CopyableText/CopyableText.module.css +21 -0
- package/src/Typography/CopyableText/CopyableText.tsx +120 -0
- package/src/Typography/PageTitle/PageTitle.module.css +47 -0
- package/src/Typography/PageTitle/PageTitle.tsx +35 -0
- package/src/Typography/_index.ts +3 -0
- package/src/declaration.d.ts +4 -0
- package/src/index.ts +8 -0
- package/tsconfig.json +32 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
## Cards
|
|
4
|
+
|
|
5
|
+
[x] Card
|
|
6
|
+
[x] GlassCard
|
|
7
|
+
[x] CollectionItemCard
|
|
8
|
+
|
|
9
|
+
## Display
|
|
10
|
+
|
|
11
|
+
[x] Accordion
|
|
12
|
+
[] AspectRatio
|
|
13
|
+
[] Carousel
|
|
14
|
+
[x] Chip
|
|
15
|
+
[] Code
|
|
16
|
+
[x] IdentityDisplay
|
|
17
|
+
[x] Kbd
|
|
18
|
+
[x] LabeledValue
|
|
19
|
+
[x] List
|
|
20
|
+
[x] Skeleton
|
|
21
|
+
[x] Tooltip
|
|
22
|
+
|
|
23
|
+
## Forms
|
|
24
|
+
|
|
25
|
+
[x] ConfirmationForm
|
|
26
|
+
|
|
27
|
+
## Inputs
|
|
28
|
+
|
|
29
|
+
[x] Button
|
|
30
|
+
[] ButtonGroup
|
|
31
|
+
[x] Checkbox
|
|
32
|
+
[] Combobox
|
|
33
|
+
[] DateTime
|
|
34
|
+
[] OTP
|
|
35
|
+
[x] Select
|
|
36
|
+
[] Slider
|
|
37
|
+
[x] Switch
|
|
38
|
+
[x] TextAreaInput
|
|
39
|
+
[x] TextInput
|
|
40
|
+
[] ToggleGroup
|
|
41
|
+
|
|
42
|
+
## Layout
|
|
43
|
+
|
|
44
|
+
[x] Dialog
|
|
45
|
+
[x] PageLayout
|
|
46
|
+
[x] PageLayoutPagination
|
|
47
|
+
[x] PageLayoutTabs
|
|
48
|
+
[x] Popover
|
|
49
|
+
[x] SectionWrapper
|
|
50
|
+
[x] Sidebar
|
|
51
|
+
[x] Table
|
|
52
|
+
|
|
53
|
+
## Misc
|
|
54
|
+
|
|
55
|
+
[x] Cursor
|
|
56
|
+
[x] Logos
|
|
57
|
+
[x] Ripple
|
|
58
|
+
[x] Spinner
|
|
59
|
+
[x] TransitionAnimation
|
|
60
|
+
|
|
61
|
+
## Navigation
|
|
62
|
+
|
|
63
|
+
[x] Breadcrumbs
|
|
64
|
+
[x] Pagination
|
|
65
|
+
[x] Tabs
|
|
66
|
+
|
|
67
|
+
## Typography
|
|
68
|
+
|
|
69
|
+
[x] ClampedText
|
|
70
|
+
[x] CopyableText
|
|
71
|
+
[x] PageTitle
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
6
|
+
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
globalIgnores(["dist"]),
|
|
9
|
+
{
|
|
10
|
+
files: ["**/*.{ts,tsx}"],
|
|
11
|
+
extends: [
|
|
12
|
+
js.configs.recommended,
|
|
13
|
+
tseslint.configs.recommended,
|
|
14
|
+
reactHooks.configs.flat.recommended,
|
|
15
|
+
],
|
|
16
|
+
languageOptions: {
|
|
17
|
+
ecmaVersion: 2020,
|
|
18
|
+
globals: globals.browser,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
]);
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@studiocubics/components",
|
|
3
|
+
"description": "Package containing UI components by Studio Cubics",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"private": false,
|
|
8
|
+
"version": "0.0.1",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"@studiocubics",
|
|
11
|
+
"cubics",
|
|
12
|
+
"studio",
|
|
13
|
+
"components",
|
|
14
|
+
"ui",
|
|
15
|
+
"design"
|
|
16
|
+
],
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Studio Cubics",
|
|
19
|
+
"email": "studiocubics7@gmail.com",
|
|
20
|
+
"url": "https://studio-cubics.vercel.app"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./src/index.d.ts",
|
|
27
|
+
"development": "./src/index.ts",
|
|
28
|
+
"production": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./styles.css": "./dist/index.css"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">= 19",
|
|
35
|
+
"react-dom": ">= 19"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@studiocubics/hooks": "^0.0.1",
|
|
39
|
+
"@studiocubics/utils": "^0.0.1",
|
|
40
|
+
"@studiocubics/types": "^0.0.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@eslint/js": "^9.39.1",
|
|
44
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
45
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
46
|
+
"@types/node": "^24.10.1",
|
|
47
|
+
"@types/react": "^19",
|
|
48
|
+
"@types/react-dom": "^19",
|
|
49
|
+
"eslint": "^9.39.1",
|
|
50
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
51
|
+
"globals": "^16.5.0",
|
|
52
|
+
"postcss": "^8.5.6",
|
|
53
|
+
"postcss-modules": "^6.0.1",
|
|
54
|
+
"rollup": "^4.53.3",
|
|
55
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
56
|
+
"rollup-preserve-directives": "^1.1.3",
|
|
57
|
+
"typescript": "~5.9.3",
|
|
58
|
+
"typescript-eslint": "^8.46.4"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "rollup -c",
|
|
62
|
+
"clean:build": "rimraf dist node_modules && rollup -c",
|
|
63
|
+
"lint": "eslint .",
|
|
64
|
+
"clean": "rimraf dist node_modules"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineConfig } from "rollup";
|
|
2
|
+
import typescript from "@rollup/plugin-typescript";
|
|
3
|
+
import postcss from "rollup-plugin-postcss";
|
|
4
|
+
import terser from "@rollup/plugin-terser";
|
|
5
|
+
import preserveDirectives from "rollup-preserve-directives";
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
input: "src/index.ts",
|
|
9
|
+
output: {
|
|
10
|
+
preserveModules: true,
|
|
11
|
+
preserveModulesRoot: "src",
|
|
12
|
+
dir: "dist",
|
|
13
|
+
format: "esm",
|
|
14
|
+
sourcemap: true,
|
|
15
|
+
},
|
|
16
|
+
plugins: [
|
|
17
|
+
preserveDirectives(),
|
|
18
|
+
postcss({
|
|
19
|
+
include: "**/*.module.css",
|
|
20
|
+
modules: true,
|
|
21
|
+
extract: true,
|
|
22
|
+
// minimize: true,
|
|
23
|
+
}),
|
|
24
|
+
typescript({ tsconfig: "./tsconfig.json" }),
|
|
25
|
+
// terser({ compress: { directives: false } }),
|
|
26
|
+
],
|
|
27
|
+
external: [
|
|
28
|
+
"react/jsx-runtime",
|
|
29
|
+
"react",
|
|
30
|
+
"react-dom",
|
|
31
|
+
"@studiocubics/utils",
|
|
32
|
+
"@studiocubics/hooks",
|
|
33
|
+
],
|
|
34
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.square {
|
|
2
|
+
aspect-ratio: 1;
|
|
3
|
+
}
|
|
4
|
+
.fullWidth {
|
|
5
|
+
width: 100%;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.size_sm {
|
|
9
|
+
padding: var(--spacing-gap-2);
|
|
10
|
+
border-radius: var(--shape-br-sm);
|
|
11
|
+
}
|
|
12
|
+
.size_md {
|
|
13
|
+
padding: var(--spacing-gap-4);
|
|
14
|
+
border-radius: var(--shape-br-md);
|
|
15
|
+
}
|
|
16
|
+
.size_lg {
|
|
17
|
+
padding: var(--spacing-gap-6);
|
|
18
|
+
border-radius: var(--shape-br-lg);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.outlined {
|
|
22
|
+
border: 1px solid var(--color-outline);
|
|
23
|
+
background: none;
|
|
24
|
+
}
|
|
25
|
+
.contained {
|
|
26
|
+
background: var(--color-surface-alpha);
|
|
27
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
PolymorphicComponentProps,
|
|
5
|
+
PolymorphicComponentType,
|
|
6
|
+
} from "@studiocubics/types";
|
|
7
|
+
import { type ElementType } from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "@studiocubics/utils";
|
|
10
|
+
import styles from "./Card.module.css";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props specific to the Card component.
|
|
14
|
+
*
|
|
15
|
+
* These extend the intrinsic element props of whatever element is passed via `as`.
|
|
16
|
+
* @group Card
|
|
17
|
+
* @category inputs
|
|
18
|
+
*/
|
|
19
|
+
export interface CardBaseProps {
|
|
20
|
+
/** Visual style variant.
|
|
21
|
+
* @default "contained"
|
|
22
|
+
*/
|
|
23
|
+
variant?: "contained" | "outlined";
|
|
24
|
+
|
|
25
|
+
/** Expands width to 100%. */
|
|
26
|
+
fullWidth?: boolean;
|
|
27
|
+
|
|
28
|
+
/** Force a 1:1 aspect ratio. */
|
|
29
|
+
square?: boolean;
|
|
30
|
+
|
|
31
|
+
/** Card size.
|
|
32
|
+
* @default "md"
|
|
33
|
+
*/
|
|
34
|
+
size?: "sm" | "md" | "lg";
|
|
35
|
+
}
|
|
36
|
+
const defaultElement = "div";
|
|
37
|
+
type DefaultElement = typeof defaultElement;
|
|
38
|
+
/**
|
|
39
|
+
* Polymorphic props for the Card component.
|
|
40
|
+
*
|
|
41
|
+
* `C` defines the element type rendered by the component (e.g. `"Card"`, `"a"`, `"div"`).
|
|
42
|
+
* All intrinsic props for `C` are supported unless overridden by `CardBaseProps`.
|
|
43
|
+
*
|
|
44
|
+
* @group Card
|
|
45
|
+
* @category inputs
|
|
46
|
+
*/
|
|
47
|
+
export type CardProps<C extends ElementType = DefaultElement> =
|
|
48
|
+
PolymorphicComponentProps<C, CardBaseProps>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Base implementation for the Card component.
|
|
52
|
+
*
|
|
53
|
+
* This is a polymorphic component that defaults to rendering a `<Card>`.
|
|
54
|
+
* Use the `as` prop to change the underlying element.
|
|
55
|
+
*
|
|
56
|
+
* @typeParam C - The intrinsic or custom element type to render.
|
|
57
|
+
*
|
|
58
|
+
* @group Card
|
|
59
|
+
* @category inputs
|
|
60
|
+
*/
|
|
61
|
+
function CardBase<C extends ElementType = DefaultElement>(props: CardProps<C>) {
|
|
62
|
+
const {
|
|
63
|
+
as,
|
|
64
|
+
className,
|
|
65
|
+
variant = "contained",
|
|
66
|
+
size = "md",
|
|
67
|
+
fullWidth = false,
|
|
68
|
+
square,
|
|
69
|
+
ref,
|
|
70
|
+
...restProps
|
|
71
|
+
} = props;
|
|
72
|
+
const Component = (as || defaultElement) as ElementType;
|
|
73
|
+
|
|
74
|
+
const componentProps = {
|
|
75
|
+
className: cn(
|
|
76
|
+
className,
|
|
77
|
+
square ? styles.square : "",
|
|
78
|
+
fullWidth ? styles.fullWidth : "",
|
|
79
|
+
styles[`size_${size}`],
|
|
80
|
+
styles[variant]
|
|
81
|
+
),
|
|
82
|
+
ref,
|
|
83
|
+
...restProps,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return <Component {...componentProps} />;
|
|
87
|
+
}
|
|
88
|
+
CardBase.displayName = "Card";
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A polymorphic Card component.
|
|
92
|
+
*
|
|
93
|
+
* By default it renders a `<Card>`, but any element can be used via the `as` prop:
|
|
94
|
+
*
|
|
95
|
+
* ```tsx
|
|
96
|
+
* <Card as="a" href="/docs">Read docs</Card>
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @group Card
|
|
100
|
+
* @category inputs
|
|
101
|
+
*/
|
|
102
|
+
export const Card = CardBase as PolymorphicComponentType<
|
|
103
|
+
CardBaseProps,
|
|
104
|
+
DefaultElement
|
|
105
|
+
>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: stretch;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
border-radius: var(--shape-br-md);
|
|
7
|
+
background-color: var(--color-background);
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
}
|
|
10
|
+
.image {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 20svh;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
& > img {
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
object-fit: cover;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
.main {
|
|
24
|
+
flex: 1;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: 0.1618em;
|
|
28
|
+
padding: var(--spacing-gap-2) calc(1.618 * var(--spacing-gap-2));
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
transition: all var(--transition-time) var(--transition-tf);
|
|
31
|
+
color: var(--color-on-surface);
|
|
32
|
+
pointer-events: all;
|
|
33
|
+
border-radius: calc(var(--shape-br-md) - var(--spacing-gap));
|
|
34
|
+
margin: var(--spacing-gap);
|
|
35
|
+
& > p {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
.description {
|
|
41
|
+
color: var(--color-on-background-faint);
|
|
42
|
+
font-size: 0.8em;
|
|
43
|
+
}
|
|
44
|
+
.subtitle {
|
|
45
|
+
color: var(--color-on-background-faint);
|
|
46
|
+
font-size: 1em;
|
|
47
|
+
}
|
|
48
|
+
.clickable {
|
|
49
|
+
transition: all var(--transition-time) var(--transition-tf);
|
|
50
|
+
& > .main {
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
background: var(--color-background);
|
|
53
|
+
}
|
|
54
|
+
&:has(.main:hover) {
|
|
55
|
+
background: var(--color-background-faint);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
.title {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: var(--spacing-gap-2);
|
|
62
|
+
& > h4 {
|
|
63
|
+
flex: 1;
|
|
64
|
+
font-size: 1.2em;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
.footer {
|
|
68
|
+
display: flex;
|
|
69
|
+
justify-content: flex-end;
|
|
70
|
+
pointer-events: all;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@container (min-width:600px) {
|
|
74
|
+
.root {
|
|
75
|
+
flex-direction: row;
|
|
76
|
+
& > .image {
|
|
77
|
+
width: 10vw;
|
|
78
|
+
height: auto;
|
|
79
|
+
aspect-ratio: 1;
|
|
80
|
+
min-width: 3rem;
|
|
81
|
+
max-width: 6rem;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps, ElementType, ReactNode } from "react";
|
|
4
|
+
import styles from "./CollectionItemCard.module.css";
|
|
5
|
+
import type {
|
|
6
|
+
PolymorphicComponentProps,
|
|
7
|
+
PolymorphicComponentType,
|
|
8
|
+
} from "@studiocubics/types";
|
|
9
|
+
import { cn } from "@studiocubics/utils";
|
|
10
|
+
import {
|
|
11
|
+
ClampedText,
|
|
12
|
+
type ClampedTextProps,
|
|
13
|
+
} from "../../Typography/ClampedText/ClampedText";
|
|
14
|
+
import { Chip, type ChipProps } from "../../Display/Chip/Chip";
|
|
15
|
+
/**
|
|
16
|
+
* Props specific to the CollectionItemCard component.
|
|
17
|
+
*
|
|
18
|
+
* These extend the intrinsic element props of whatever element is passed via `as`.
|
|
19
|
+
* @group CollectionItemCard
|
|
20
|
+
* @category inputs
|
|
21
|
+
*/
|
|
22
|
+
export interface CollectionItemCardBaseProps {
|
|
23
|
+
thumbnail?: ReactNode;
|
|
24
|
+
title: ReactNode;
|
|
25
|
+
chip?: ReactNode;
|
|
26
|
+
subtitle?: ReactNode;
|
|
27
|
+
description?: ReactNode;
|
|
28
|
+
footer?: ReactNode;
|
|
29
|
+
slotProps?: {
|
|
30
|
+
root?: ComponentProps<"div">;
|
|
31
|
+
chip?: ChipProps;
|
|
32
|
+
thumbnail?: ComponentProps<"div">;
|
|
33
|
+
titleContainer?: ComponentProps<"div">;
|
|
34
|
+
title?: ClampedTextProps;
|
|
35
|
+
subtitle?: ClampedTextProps;
|
|
36
|
+
description?: ClampedTextProps;
|
|
37
|
+
footerContainer?: ComponentProps<"div">;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const defaultElement = "a";
|
|
41
|
+
type DefaultElement = typeof defaultElement;
|
|
42
|
+
/**
|
|
43
|
+
* Polymorphic props for the CollectionItemCard component.
|
|
44
|
+
*
|
|
45
|
+
* `C` defines the element type rendered by the component (e.g. `"CollectionItemCard"`, `"a"`, `"div"`).
|
|
46
|
+
* All intrinsic props for `C` are supported unless overridden by `CollectionItemCardBaseProps`.
|
|
47
|
+
*
|
|
48
|
+
* @group CollectionItemCard
|
|
49
|
+
* @category inputs
|
|
50
|
+
*/
|
|
51
|
+
export type CollectionItemCardProps<C extends ElementType = DefaultElement> =
|
|
52
|
+
PolymorphicComponentProps<C, CollectionItemCardBaseProps>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Base implementation for the CollectionItemCard component.
|
|
56
|
+
*
|
|
57
|
+
* This is a polymorphic component that defaults to rendering a `<CollectionItemCard>`.
|
|
58
|
+
* Use the `as` prop to change the underlying element.
|
|
59
|
+
*
|
|
60
|
+
* @typeParam C - The intrinsic or custom element type to render.
|
|
61
|
+
*
|
|
62
|
+
* @group CollectionItemCard
|
|
63
|
+
* @category inputs
|
|
64
|
+
*/
|
|
65
|
+
function CollectionItemCardBase<C extends ElementType = DefaultElement>(
|
|
66
|
+
props: CollectionItemCardProps<C>,
|
|
67
|
+
) {
|
|
68
|
+
const {
|
|
69
|
+
as,
|
|
70
|
+
className,
|
|
71
|
+
ref,
|
|
72
|
+
thumbnail,
|
|
73
|
+
title,
|
|
74
|
+
chip,
|
|
75
|
+
subtitle,
|
|
76
|
+
description,
|
|
77
|
+
footer,
|
|
78
|
+
slotProps = {},
|
|
79
|
+
...restProps
|
|
80
|
+
} = props;
|
|
81
|
+
const Component = as || defaultElement;
|
|
82
|
+
|
|
83
|
+
const clickable = !!props.href || !!props.onClick;
|
|
84
|
+
const componentProps = {
|
|
85
|
+
className: cn(className, styles.main),
|
|
86
|
+
ref,
|
|
87
|
+
...restProps,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div
|
|
92
|
+
{...slotProps.root}
|
|
93
|
+
className={cn(
|
|
94
|
+
styles.root,
|
|
95
|
+
clickable ? styles.clickable : "",
|
|
96
|
+
slotProps.root?.className,
|
|
97
|
+
)}
|
|
98
|
+
>
|
|
99
|
+
{thumbnail && (
|
|
100
|
+
<div
|
|
101
|
+
{...slotProps.thumbnail}
|
|
102
|
+
className={cn(styles.image, slotProps.thumbnail?.className)}
|
|
103
|
+
>
|
|
104
|
+
{thumbnail}
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
<Component {...componentProps}>
|
|
108
|
+
<div
|
|
109
|
+
{...slotProps.titleContainer}
|
|
110
|
+
className={cn(styles.title, slotProps.titleContainer?.className)}
|
|
111
|
+
>
|
|
112
|
+
{chip &&
|
|
113
|
+
(typeof chip == "string" ? (
|
|
114
|
+
<Chip size="sm" {...slotProps.chip}>
|
|
115
|
+
{chip}
|
|
116
|
+
</Chip>
|
|
117
|
+
) : (
|
|
118
|
+
chip
|
|
119
|
+
))}
|
|
120
|
+
<ClampedText as="h4" {...slotProps.title}>
|
|
121
|
+
{title}
|
|
122
|
+
</ClampedText>
|
|
123
|
+
</div>
|
|
124
|
+
{subtitle && (
|
|
125
|
+
<ClampedText
|
|
126
|
+
{...slotProps.subtitle}
|
|
127
|
+
className={cn(styles.subtitle, slotProps.subtitle?.className)}
|
|
128
|
+
>
|
|
129
|
+
{subtitle}
|
|
130
|
+
</ClampedText>
|
|
131
|
+
)}
|
|
132
|
+
{description && (
|
|
133
|
+
<ClampedText
|
|
134
|
+
{...slotProps.description}
|
|
135
|
+
className={cn(styles.description, slotProps.description?.className)}
|
|
136
|
+
>
|
|
137
|
+
{description}
|
|
138
|
+
</ClampedText>
|
|
139
|
+
)}
|
|
140
|
+
</Component>
|
|
141
|
+
{footer && (
|
|
142
|
+
<div
|
|
143
|
+
{...slotProps.footerContainer}
|
|
144
|
+
className={cn(styles.footer, slotProps.footerContainer?.className)}
|
|
145
|
+
>
|
|
146
|
+
{footer}
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
CollectionItemCardBase.displayName = "CollectionItemCard";
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A polymorphic CollectionItemCard component.
|
|
156
|
+
*
|
|
157
|
+
* By default it renders a `<CollectionItemCard>`, but any element can be used via the `as` prop:
|
|
158
|
+
*
|
|
159
|
+
* ```tsx
|
|
160
|
+
* <CollectionItemCard as="a" href="/docs">Read docs</CollectionItemCard>
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* @group CollectionItemCard
|
|
164
|
+
* @category inputs
|
|
165
|
+
*/
|
|
166
|
+
export const CollectionItemCard =
|
|
167
|
+
CollectionItemCardBase as PolymorphicComponentType<
|
|
168
|
+
CollectionItemCardBaseProps,
|
|
169
|
+
DefaultElement
|
|
170
|
+
>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useAnchorElement } from "@studiocubics/hooks";
|
|
2
|
+
import {
|
|
3
|
+
type ElementType,
|
|
4
|
+
type ReactNode,
|
|
5
|
+
type MouseEvent,
|
|
6
|
+
Fragment,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { Button, type ButtonProps } from "../../Inputs/Button/Button";
|
|
9
|
+
import { Tooltip } from "../../Display/Tooltip/Tooltip";
|
|
10
|
+
import { Popover } from "../../Layout/Popover/Popover";
|
|
11
|
+
|
|
12
|
+
export type DocumentAction<C extends ElementType = "a"> = {
|
|
13
|
+
label?: string;
|
|
14
|
+
} & (
|
|
15
|
+
| ({
|
|
16
|
+
actionType: "popover";
|
|
17
|
+
popoverChildren: ReactNode;
|
|
18
|
+
icon: ReactNode;
|
|
19
|
+
} & ButtonProps<C>)
|
|
20
|
+
| { actionType: "node"; children: ReactNode }
|
|
21
|
+
| ({
|
|
22
|
+
actionType: "link";
|
|
23
|
+
href: string;
|
|
24
|
+
icon: ReactNode;
|
|
25
|
+
} & ButtonProps) //TODO Avoid 'as' conflict
|
|
26
|
+
);
|
|
27
|
+
export function CollectionItemCardActions({
|
|
28
|
+
actions,
|
|
29
|
+
}: {
|
|
30
|
+
actions: DocumentAction[];
|
|
31
|
+
}) {
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
{actions.map((action, i) => {
|
|
35
|
+
switch (action.actionType) {
|
|
36
|
+
case "popover":
|
|
37
|
+
return <PopoverTypeAction key={i} action={action} />;
|
|
38
|
+
case "link":
|
|
39
|
+
return <LinkTypeAction key={i} action={action} />;
|
|
40
|
+
default:
|
|
41
|
+
return <Fragment key={i}>{action.children}</Fragment>;
|
|
42
|
+
}
|
|
43
|
+
})}
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function PopoverTypeAction({
|
|
49
|
+
action,
|
|
50
|
+
}: {
|
|
51
|
+
action: DocumentAction & { actionType: "popover" };
|
|
52
|
+
}) {
|
|
53
|
+
const { actionType: _, icon, popoverChildren, onClick, ...rest } = action;
|
|
54
|
+
const { anchorEl, open, handleClick, handleClose } = useAnchorElement();
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
<Tooltip renderArrow title={action.label}>
|
|
58
|
+
<Button
|
|
59
|
+
{...rest}
|
|
60
|
+
onClick={(e: MouseEvent<HTMLAnchorElement>) => {
|
|
61
|
+
handleClick(e);
|
|
62
|
+
onClick?.(e);
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{icon}
|
|
66
|
+
</Button>
|
|
67
|
+
</Tooltip>
|
|
68
|
+
<Popover open={open} anchorEl={anchorEl} onClose={handleClose}>
|
|
69
|
+
{popoverChildren}
|
|
70
|
+
</Popover>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function LinkTypeAction({
|
|
75
|
+
action,
|
|
76
|
+
}: {
|
|
77
|
+
action: DocumentAction & { actionType: "link" };
|
|
78
|
+
}) {
|
|
79
|
+
const { actionType: _, icon, ...rest } = action;
|
|
80
|
+
return (
|
|
81
|
+
<Tooltip renderArrow title={action.label}>
|
|
82
|
+
<Button {...rest}>{icon}</Button>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
);
|
|
85
|
+
}
|