@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.
Files changed (140) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +71 -0
  3. package/eslint.config.js +21 -0
  4. package/package.json +66 -0
  5. package/rollup.config.js +34 -0
  6. package/src/Cards/Card/Card.module.css +27 -0
  7. package/src/Cards/Card/Card.tsx +105 -0
  8. package/src/Cards/CollectionItemCard/CollectionItemCard.module.css +84 -0
  9. package/src/Cards/CollectionItemCard/CollectionItemCard.tsx +170 -0
  10. package/src/Cards/CollectionItemCard/CollectionItemCardActions.tsx +85 -0
  11. package/src/Cards/CollectionItemCard/_index.ts +2 -0
  12. package/src/Cards/GlassCard/GlassCard.module.css +71 -0
  13. package/src/Cards/GlassCard/GlassCard.tsx +80 -0
  14. package/src/Cards/_index.ts +3 -0
  15. package/src/Display/Accordion/Accordion.module.css +69 -0
  16. package/src/Display/Accordion/Accordion.tsx +61 -0
  17. package/src/Display/Accordion/AccordionItem.tsx +135 -0
  18. package/src/Display/Accordion/_index.ts +2 -0
  19. package/src/Display/Chip/Chip.module.css +64 -0
  20. package/src/Display/Chip/Chip.tsx +105 -0
  21. package/src/Display/IdentityDisplay/IdentityDisplay.module.css +95 -0
  22. package/src/Display/IdentityDisplay/IdentityDisplay.tsx +119 -0
  23. package/src/Display/InputErrors/InputErrors.module.css +6 -0
  24. package/src/Display/InputErrors/InputErrors.tsx +52 -0
  25. package/src/Display/Kbd/Kbd.module.css +29 -0
  26. package/src/Display/Kbd/Kbd.tsx +39 -0
  27. package/src/Display/Kbd/_index.ts +2 -0
  28. package/src/Display/Kbd/buttonList.tsx +246 -0
  29. package/src/Display/LabeledValue/LabeledValue.module.css +32 -0
  30. package/src/Display/LabeledValue/LabeledValue.tsx +20 -0
  31. package/src/Display/List/List.module.css +143 -0
  32. package/src/Display/List/List.tsx +298 -0
  33. package/src/Display/PasswordStrength/PasswordStrength.module.css +45 -0
  34. package/src/Display/PasswordStrength/PasswordStrength.tsx +41 -0
  35. package/src/Display/PasswordStrength/usePasswordStrength.tsx +77 -0
  36. package/src/Display/Skeleton/Skeleton.module.css +54 -0
  37. package/src/Display/Skeleton/Skeleton.tsx +28 -0
  38. package/src/Display/Toast/Toaster.tsx +58 -0
  39. package/src/Display/Toast/_index.ts +2 -0
  40. package/src/Display/Toast/toast.ts +44 -0
  41. package/src/Display/Tooltip/Tooltip.module.css +128 -0
  42. package/src/Display/Tooltip/Tooltip.tsx +93 -0
  43. package/src/Display/Tooltip/getArrowDirection.ts +55 -0
  44. package/src/Display/Tooltip/useTooltip.tsx +63 -0
  45. package/src/Display/_index.ts +12 -0
  46. package/src/Forms/ConfirmationForm/ConfirmationForm.module.css +23 -0
  47. package/src/Forms/ConfirmationForm/ConfirmationForm.tsx +60 -0
  48. package/src/Forms/_index.ts +1 -0
  49. package/src/Inputs/Button/Button.module.css +131 -0
  50. package/src/Inputs/Button/Button.tsx +178 -0
  51. package/src/Inputs/Checkbox/Checkbox.module.css +77 -0
  52. package/src/Inputs/Checkbox/Checkbox.tsx +191 -0
  53. package/src/Inputs/Checkbox/CheckboxGroup/CheckboxGroup.module.css +10 -0
  54. package/src/Inputs/Checkbox/CheckboxGroup/CheckboxGroup.tsx +83 -0
  55. package/src/Inputs/Checkbox/CheckboxSelectAll.tsx +34 -0
  56. package/src/Inputs/Checkbox/_index.ts +3 -0
  57. package/src/Inputs/PasswordInput/PasswordInput.module.css +111 -0
  58. package/src/Inputs/PasswordInput/PasswordInput.tsx +229 -0
  59. package/src/Inputs/Select/Select.module.css +138 -0
  60. package/src/Inputs/Select/Select.tsx +136 -0
  61. package/src/Inputs/Switch/Switch.module.css +119 -0
  62. package/src/Inputs/Switch/Switch.tsx +195 -0
  63. package/src/Inputs/TextAreaInput/TextAreaInput.module.css +65 -0
  64. package/src/Inputs/TextAreaInput/TextAreaInput.tsx +97 -0
  65. package/src/Inputs/TextInput/TextInput.module.css +112 -0
  66. package/src/Inputs/TextInput/TextInput.tsx +142 -0
  67. package/src/Inputs/ThemeToggle/ThemeToggleListItem.tsx +80 -0
  68. package/src/Inputs/ThemeToggle/_index.ts +1 -0
  69. package/src/Inputs/_index.ts +8 -0
  70. package/src/Layout/Dialog/Dialog.module.css +15 -0
  71. package/src/Layout/Dialog/Dialog.tsx +115 -0
  72. package/src/Layout/PageLayout/PageLayout.module.css +20 -0
  73. package/src/Layout/PageLayout/PageLayout.tsx +79 -0
  74. package/src/Layout/PageLayoutPagination/PageLayoutPagination.module.css +5 -0
  75. package/src/Layout/PageLayoutPagination/PageLayoutPagination.tsx +40 -0
  76. package/src/Layout/PageLayoutTabs/PageLayoutTabs.module.css +3 -0
  77. package/src/Layout/PageLayoutTabs/PageLayoutTabs.tsx +62 -0
  78. package/src/Layout/Popover/Popover.module.css +9 -0
  79. package/src/Layout/Popover/Popover.tsx +145 -0
  80. package/src/Layout/SectionWrapper/SectionWrapper.module.css +31 -0
  81. package/src/Layout/SectionWrapper/SectionWrapper.tsx +62 -0
  82. package/src/Layout/Sidebar/Sidebar.module.css +17 -0
  83. package/src/Layout/Sidebar/Sidebar.tsx +39 -0
  84. package/src/Layout/Sidebar/SidebarBody/SidebarBody.module.css +31 -0
  85. package/src/Layout/Sidebar/SidebarBody/SidebarBody.tsx +18 -0
  86. package/src/Layout/Sidebar/SidebarDrawer/SidebarDrawer.module.css +20 -0
  87. package/src/Layout/Sidebar/SidebarDrawer/SidebarDrawer.tsx +19 -0
  88. package/src/Layout/Sidebar/SidebarFooter/SidebarFooter.module.css +35 -0
  89. package/src/Layout/Sidebar/SidebarFooter/SidebarFooter.tsx +19 -0
  90. package/src/Layout/Sidebar/SidebarHeader/SidebarHeader.tsx +14 -0
  91. package/src/Layout/Sidebar/SidebarViewport/SidebarViewport.module.css +12 -0
  92. package/src/Layout/Sidebar/SidebarViewport/SidebarViewport.tsx +11 -0
  93. package/src/Layout/Sidebar/_index.ts +6 -0
  94. package/src/Layout/Table/Table.module.css +46 -0
  95. package/src/Layout/Table/Table.tsx +222 -0
  96. package/src/Layout/Table/TableFooter.tsx +4 -0
  97. package/src/Layout/Table/TableHeader.tsx +4 -0
  98. package/src/Layout/Table/_index.ts +5 -0
  99. package/src/Layout/Table/tableUtils.ts +142 -0
  100. package/src/Layout/Table/types.ts +48 -0
  101. package/src/Layout/_index.ts +8 -0
  102. package/src/Misc/Cursor/Cursor.module.css +31 -0
  103. package/src/Misc/Cursor/Cursor.tsx +77 -0
  104. package/src/Misc/Logos.tsx +230 -0
  105. package/src/Misc/PoweredByBanner/PoweredByBanner.module.css +20 -0
  106. package/src/Misc/PoweredByBanner/PoweredByBanner.tsx +17 -0
  107. package/src/Misc/Ripple/Ripple.module.css +25 -0
  108. package/src/Misc/Ripple/Ripple.tsx +126 -0
  109. package/src/Misc/Spinner/Spinner.module.css +38 -0
  110. package/src/Misc/Spinner/Spinner.tsx +36 -0
  111. package/src/Misc/TransitionAnimation/TransitionAnimation.module.css +131 -0
  112. package/src/Misc/TransitionAnimation/TransitionAnimation.tsx +166 -0
  113. package/src/Misc/_index.ts +6 -0
  114. package/src/Navigation/Breadcrumbs/Breadcrumbs.module.css +22 -0
  115. package/src/Navigation/Breadcrumbs/Breadcrumbs.tsx +127 -0
  116. package/src/Navigation/Breadcrumbs/BreadcrumbsItem.tsx +31 -0
  117. package/src/Navigation/Breadcrumbs/_index.ts +3 -0
  118. package/src/Navigation/Breadcrumbs/useBreadcrumbs.tsx +74 -0
  119. package/src/Navigation/Pagination/Pagination.module.css +41 -0
  120. package/src/Navigation/Pagination/Pagination.tsx +187 -0
  121. package/src/Navigation/Pagination/PaginationItem.tsx +28 -0
  122. package/src/Navigation/Pagination/_index.ts +3 -0
  123. package/src/Navigation/Pagination/usePagination.tsx +65 -0
  124. package/src/Navigation/Tabs/Tab/Tab.module.css +43 -0
  125. package/src/Navigation/Tabs/Tab/Tab.tsx +155 -0
  126. package/src/Navigation/Tabs/Tabs.tsx +37 -0
  127. package/src/Navigation/Tabs/TabsBar/TabsBar.module.css +47 -0
  128. package/src/Navigation/Tabs/TabsBar/TabsBar.tsx +92 -0
  129. package/src/Navigation/Tabs/_index.ts +3 -0
  130. package/src/Navigation/_index.ts +3 -0
  131. package/src/Typography/ClampedText/ClampedText.module.css +5 -0
  132. package/src/Typography/ClampedText/ClampedText.tsx +77 -0
  133. package/src/Typography/CopyableText/CopyableText.module.css +21 -0
  134. package/src/Typography/CopyableText/CopyableText.tsx +120 -0
  135. package/src/Typography/PageTitle/PageTitle.module.css +47 -0
  136. package/src/Typography/PageTitle/PageTitle.tsx +35 -0
  137. package/src/Typography/_index.ts +3 -0
  138. package/src/declaration.d.ts +4 -0
  139. package/src/index.ts +8 -0
  140. package/tsconfig.json +32 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # @studiocubics/components
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - First publish inshallah!
8
+ - Updated dependencies
9
+ - @studiocubics/hooks@0.0.1
10
+ - @studiocubics/types@0.0.1
11
+ - @studiocubics/utils@0.0.1
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
@@ -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
+ }
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./CollectionItemCard";
2
+ export * from "./CollectionItemCardActions";