@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
@@ -0,0 +1,128 @@
1
+ .root {
2
+ position: fixed;
3
+ background: var(--color-surface);
4
+ color: var(--color-on-surface);
5
+ padding: var(--spacing-gap) var(--spacing-gap-2);
6
+ border-radius: var(--shape-br-sm);
7
+ font-size: var(--fs-body2);
8
+ font-weight: bold;
9
+ pointer-events: none;
10
+ z-index: 9999;
11
+ text-align: center;
12
+ --spacing-arrow-offset: 6px;
13
+ }
14
+ .root.arrow-bottom {
15
+ margin-bottom: var(--spacing-arrow-offset);
16
+ }
17
+ .root.arrow-top {
18
+ margin-top: var(--spacing-arrow-offset);
19
+ }
20
+ .root.arrow-right {
21
+ margin-left: var(--spacing-arrow-offset);
22
+ }
23
+ .root.arrow-left {
24
+ margin-right: var(--spacing-arrow-offset);
25
+ }
26
+ .root.arrow-topLeft {
27
+ border-top-left-radius: 0;
28
+ }
29
+ .root.arrow-topRight {
30
+ border-top-right-radius: 0;
31
+ }
32
+ .root.arrow-bottomLeft {
33
+ border-bottom-left-radius: 0;
34
+ }
35
+ .root.arrow-bottomRight {
36
+ border-bottom-right-radius: 0;
37
+ }
38
+ /* common arrow style */
39
+ .root::after {
40
+ content: "";
41
+ position: absolute;
42
+ width: 0;
43
+ height: 0;
44
+ }
45
+
46
+ /* arrow pointing up (tooltip below anchor) */
47
+ .arrow-bottom::after {
48
+ bottom: calc(-1 * var(--spacing-arrow-offset));
49
+ left: 50%;
50
+ transform: translateX(-50%);
51
+ border-width: var(--spacing-arrow-offset) var(--spacing-arrow-offset) 0
52
+ var(--spacing-arrow-offset);
53
+ border-style: solid;
54
+ border-color: var(--color-surface) transparent transparent transparent;
55
+ }
56
+
57
+ /* arrow pointing down (tooltip above anchor) */
58
+ .arrow-top::after {
59
+ top: calc(-1 * var(--spacing-arrow-offset));
60
+ left: 50%;
61
+ transform: translateX(-50%);
62
+ border-width: 0 var(--spacing-arrow-offset) var(--spacing-arrow-offset)
63
+ var(--spacing-arrow-offset);
64
+ border-style: solid;
65
+ border-color: transparent transparent var(--color-surface) transparent;
66
+ }
67
+
68
+ /* arrow pointing left (tooltip right of anchor) */
69
+ .arrow-right::after {
70
+ right: calc(-1 * var(--spacing-arrow-offset));
71
+ top: 50%;
72
+ transform: translateY(-50%);
73
+ border-width: var(--spacing-arrow-offset) 0 var(--spacing-arrow-offset)
74
+ var(--spacing-arrow-offset);
75
+ border-style: solid;
76
+ border-color: transparent transparent transparent var(--color-surface);
77
+ }
78
+
79
+ /* arrow pointing right (tooltip left of anchor) */
80
+ .arrow-left::after {
81
+ left: calc(-1 * var(--spacing-arrow-offset));
82
+ top: 50%;
83
+ transform: translateY(-50%);
84
+ border-width: var(--spacing-arrow-offset) var(--spacing-arrow-offset)
85
+ var(--spacing-arrow-offset) 0;
86
+ border-style: solid;
87
+ border-color: transparent var(--color-surface) transparent transparent;
88
+ }
89
+
90
+ /* arrow pointing down-left (tooltip above-right of anchor) */
91
+ .arrow-topLeft::after {
92
+ top: calc(-1 * var(--spacing-arrow-offset));
93
+ left: 0;
94
+ border-width: 0 var(--spacing-arrow-offset) var(--spacing-arrow-offset)
95
+ var(--spacing-arrow-offset);
96
+ border-style: solid;
97
+ border-color: transparent transparent var(--color-surface) transparent;
98
+ }
99
+
100
+ /* arrow pointing down-right (tooltip above-left of anchor) */
101
+ .arrow-topRight::after {
102
+ top: calc(-1 * var(--spacing-arrow-offset));
103
+ right: 0;
104
+ border-width: 0 var(--spacing-arrow-offset) var(--spacing-arrow-offset)
105
+ var(--spacing-arrow-offset);
106
+ border-style: solid;
107
+ border-color: transparent transparent var(--color-surface) transparent;
108
+ }
109
+
110
+ /* arrow pointing up-left (tooltip below-right of anchor) */
111
+ .arrow-bottomLeft::after {
112
+ bottom: calc(-1 * var(--spacing-arrow-offset));
113
+ left: 0;
114
+ border-width: var(--spacing-arrow-offset) var(--spacing-arrow-offset) 0
115
+ var(--spacing-arrow-offset);
116
+ border-style: solid;
117
+ border-color: var(--color-surface) transparent transparent transparent;
118
+ }
119
+
120
+ /* arrow pointing up-right (tooltip below-left of anchor) */
121
+ .arrow-bottomRight::after {
122
+ bottom: calc(-1 * var(--spacing-arrow-offset));
123
+ right: 0;
124
+ border-width: var(--spacing-arrow-offset) var(--spacing-arrow-offset) 0
125
+ var(--spacing-arrow-offset);
126
+ border-style: solid;
127
+ border-color: var(--color-surface) transparent transparent transparent;
128
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import {
4
+ Children,
5
+ cloneElement,
6
+ isValidElement,
7
+ useEffect,
8
+ useMemo,
9
+ type ReactElement,
10
+ type ReactNode,
11
+ type RefObject,
12
+ } from "react";
13
+ import { createPortal } from "react-dom";
14
+ import { useTooltip } from "./useTooltip";
15
+ import styles from "./Tooltip.module.css";
16
+ import getArrowDirection from "./getArrowDirection";
17
+ import { cn, type SafePositionOptions } from "@studiocubics/utils";
18
+
19
+ export interface TooltipProps extends SafePositionOptions {
20
+ title?: ReactNode;
21
+ renderArrow?: boolean;
22
+ children: ReactElement;
23
+ }
24
+
25
+ export function Tooltip({
26
+ title,
27
+ children,
28
+ renderArrow = false,
29
+ anchorOrigin,
30
+ transformOrigin,
31
+ margin,
32
+ }: TooltipProps) {
33
+ const options = useMemo(() => {
34
+ return {
35
+ anchorOrigin: anchorOrigin ?? "bottom center",
36
+ transformOrigin: transformOrigin ?? "top center",
37
+ margin: margin ?? 8,
38
+ };
39
+ }, [anchorOrigin, transformOrigin, margin]);
40
+ const { anchorRef, showTooltip, tooltipRef, position, updatePosition } =
41
+ useTooltip(options);
42
+
43
+ const direction = renderArrow
44
+ ? getArrowDirection(
45
+ anchorOrigin ?? "bottom center",
46
+ transformOrigin ?? "top center"
47
+ )
48
+ : null;
49
+
50
+ // Ensure only one child is passed
51
+ const child = Children.only(children);
52
+
53
+ const childWithRef = () => {
54
+ if (isValidElement(child))
55
+ return cloneElement(child, { ref: anchorRef } as {
56
+ ref: RefObject<HTMLElement | null>;
57
+ });
58
+ else {
59
+ throw new Error(
60
+ "Children passed to Tooltip should be a valid react element"
61
+ );
62
+ }
63
+ };
64
+
65
+ useEffect(() => {
66
+ if (showTooltip) updatePosition();
67
+ }, [title, showTooltip, updatePosition]);
68
+
69
+ if (!title) return child;
70
+
71
+ return (
72
+ <>
73
+ {showTooltip &&
74
+ createPortal(
75
+ <div
76
+ className={cn(
77
+ styles.root,
78
+ renderArrow && direction ? styles[`arrow-${direction}`] : ""
79
+ )}
80
+ ref={tooltipRef}
81
+ style={{
82
+ left: `${position.x}px`,
83
+ top: `${position.y}px`,
84
+ }}
85
+ >
86
+ {title}
87
+ </div>,
88
+ document.body
89
+ )}
90
+ {childWithRef()}
91
+ </>
92
+ );
93
+ }
@@ -0,0 +1,55 @@
1
+ export default function getArrowDirection(
2
+ anchorOrigin: string,
3
+ transformOrigin: string
4
+ ):
5
+ | "top"
6
+ | "bottom"
7
+ | "left"
8
+ | "right"
9
+ | "topLeft"
10
+ | "topRight"
11
+ | "bottomLeft"
12
+ | "bottomRight"
13
+ | null {
14
+ const parse = (origin: string) => origin.toLowerCase().trim().split(/\s+/);
15
+
16
+ const [aV = "top", aH = "left"] = parse(anchorOrigin);
17
+ const [tV = "top", tH = "left"] = parse(transformOrigin);
18
+
19
+ // Vertical priority
20
+ if (aV !== tV) {
21
+ // anchor top, tooltip attached bottom -> tooltip is above anchor -> arrow on bottom side
22
+ if (aV === "top" && tV === "bottom") {
23
+ if (aH === "left" && tH === "right") return "bottomRight";
24
+ if (aH === "right" && tH === "left") return "bottomLeft";
25
+ return "bottom";
26
+ }
27
+
28
+ // anchor bottom, tooltip attached top -> tooltip is below anchor -> arrow on top side
29
+ if (aV === "bottom" && tV === "top") {
30
+ if (aH === "left" && tH === "right") return "topRight";
31
+ if (aH === "right" && tH === "left") return "topLeft";
32
+ return "top";
33
+ }
34
+ }
35
+
36
+ // Horizontal fallback
37
+ if (aH !== tH) {
38
+ // anchor left, tooltip attached right -> tooltip sits left of anchor -> arrow on right side
39
+ if (aH === "left" && tH === "right") {
40
+ if (aV === "top" && tV === "top") return "topRight";
41
+ if (aV === "bottom" && tV === "bottom") return "bottomRight";
42
+ return "right";
43
+ }
44
+
45
+ // anchor right, tooltip attached left -> tooltip sits right of anchor -> arrow on left side
46
+ if (aH === "right" && tH === "left") {
47
+ if (aV === "top" && tV === "top") return "topLeft";
48
+ if (aV === "bottom" && tV === "bottom") return "bottomLeft";
49
+ return "left";
50
+ }
51
+ }
52
+
53
+ // No clear difference (both centers or identical origins)
54
+ return null;
55
+ }
@@ -0,0 +1,63 @@
1
+ "use client"
2
+ import {
3
+ calculateSafePosition,
4
+ type Position,
5
+ type SafePositionOptions,
6
+ } from "@studiocubics/utils";
7
+ import { useCallback, useEffect, useRef, useState } from "react";
8
+
9
+ export function useTooltip(options: SafePositionOptions = {}) {
10
+ const [showTooltip, setShowTooltip] = useState(false);
11
+ const [position, setPosition] = useState<Position>({ x: -9999, y: -9999 });
12
+ const anchorRef = useRef<HTMLElement>(null);
13
+ const tooltipRef = useRef<HTMLDivElement>(null);
14
+
15
+ useEffect(() => {
16
+ const element = anchorRef.current;
17
+ if (!element) return;
18
+
19
+ const handleMouseEnter = () => {
20
+ setShowTooltip(true);
21
+ };
22
+
23
+ const handleMouseLeave = () => {
24
+ setShowTooltip(false);
25
+ };
26
+
27
+ element.addEventListener("mouseenter", handleMouseEnter);
28
+ element.addEventListener("mouseleave", handleMouseLeave);
29
+
30
+ return () => {
31
+ element.removeEventListener("mouseenter", handleMouseEnter);
32
+ element.removeEventListener("mouseleave", handleMouseLeave);
33
+ };
34
+ }, []);
35
+
36
+ const updatePosition = useCallback(() => {
37
+ const safePos = calculateSafePosition(
38
+ tooltipRef.current,
39
+ anchorRef.current,
40
+ options
41
+ );
42
+
43
+ setPosition(safePos);
44
+ }, [options]);
45
+
46
+ useEffect(() => {
47
+ if (!showTooltip) return;
48
+
49
+ // Initial position calculation
50
+ updatePosition();
51
+
52
+ // Update position on scroll and resize
53
+ window.addEventListener("scroll", updatePosition);
54
+ window.addEventListener("resize", updatePosition);
55
+
56
+ return () => {
57
+ window.removeEventListener("scroll", updatePosition);
58
+ window.removeEventListener("resize", updatePosition);
59
+ };
60
+ }, [showTooltip]);
61
+
62
+ return { showTooltip, position, anchorRef, tooltipRef, updatePosition };
63
+ }
@@ -0,0 +1,12 @@
1
+ export * from "./Accordion/_index";
2
+ export * from "./Chip/Chip";
3
+ export * from "./IdentityDisplay/IdentityDisplay";
4
+ export * from "./InputErrors/InputErrors";
5
+ export * from "./Kbd/_index";
6
+ export * from "./LabeledValue/LabeledValue";
7
+ export * from "./List/List";
8
+ export * from "./Skeleton/Skeleton";
9
+ export * from "./Toast/_index";
10
+ export * from "./Tooltip/Tooltip";
11
+ export * from "./Tooltip/useTooltip";
12
+ export * from "./Tooltip/getArrowDirection";
@@ -0,0 +1,23 @@
1
+ .root {
2
+ display: flex;
3
+ flex-direction: column;
4
+ font-size: var(--fs-body2);
5
+ gap: var(--spacing-gap-3);
6
+ color: var(--color-on-background-faint);
7
+ & > h4 {
8
+ font-size: 1.2em;
9
+ color: var(--color-on-background-faint);
10
+ padding-bottom: var(--spacing-gap-3);
11
+ border-bottom: 1px solid var(--color-outline);
12
+ }
13
+ }
14
+ .danger {
15
+ & > h4 {
16
+ color: var(--color-error);
17
+ }
18
+ }
19
+ .actions {
20
+ display: flex;
21
+ justify-content: flex-end;
22
+ gap: var(--spacing-gap-2);
23
+ }
@@ -0,0 +1,60 @@
1
+ import type { ComponentProps } from "react";
2
+ import styles from "./ConfirmationForm.module.css";
3
+ import { cn } from "@studiocubics/utils";
4
+ import { Button, type ButtonProps } from "../../Inputs/Button/Button";
5
+ export interface ConfirmationFormProps extends ComponentProps<"form"> {
6
+ formTitle?: string;
7
+ onCancel?: () => void;
8
+ variant?: "danger" | "info";
9
+ confirmText?: string;
10
+ cancelText?: string;
11
+ disabled?: boolean;
12
+ slotProps?: {
13
+ confirmButton?: ButtonProps;
14
+ cancelButton?: ButtonProps;
15
+ };
16
+ }
17
+ export function ConfirmationForm(props: ConfirmationFormProps) {
18
+ const {
19
+ onCancel,
20
+ formTitle,
21
+ variant = "info",
22
+ confirmText = "Confirm",
23
+ cancelText = "Cancel",
24
+ disabled,
25
+ slotProps = {},
26
+ className,
27
+ children,
28
+ ...rest
29
+ } = props;
30
+ return (
31
+ <form
32
+ aria-disabled={disabled}
33
+ className={cn(styles.root, className, styles[variant])}
34
+ {...rest}
35
+ >
36
+ <h4>{formTitle}</h4>
37
+ {children}
38
+ <div className={styles.actions}>
39
+ <Button
40
+ variant={variant == "danger" ? "contained" : "text"}
41
+ disabled={disabled}
42
+ type="button"
43
+ onClick={() => onCancel?.()}
44
+ {...slotProps.cancelButton}
45
+ >
46
+ {cancelText}
47
+ </Button>
48
+ <Button
49
+ disabled={disabled}
50
+ variant={variant == "danger" ? "text" : "contained"}
51
+ color={variant == "danger" ? "error" : undefined}
52
+ type="submit"
53
+ {...slotProps.confirmButton}
54
+ >
55
+ {confirmText}
56
+ </Button>
57
+ </div>
58
+ </form>
59
+ );
60
+ }
@@ -0,0 +1 @@
1
+ export * from "./ConfirmationForm/ConfirmationForm";
@@ -0,0 +1,131 @@
1
+ .root {
2
+ --button-color: var(--color-on-background);
3
+ --button-background: none;
4
+ --button-hover-color: var(--color-on-background);
5
+ --button-hover-background: var(--color-background-faint);
6
+ --button-outline-color: var(--color-on-background);
7
+ position: relative;
8
+ display: flex;
9
+ justify-content: center;
10
+ align-items: center;
11
+ gap: var(--spacing-gap);
12
+ padding: var(--spacing-gap) calc(1.618 * var(--spacing-gap-3));
13
+ cursor: pointer;
14
+ border: none;
15
+ background: var(--button-background);
16
+ color: var(--button-color);
17
+ font-family: var(--font-p);
18
+ font-size: var(--fs-body2);
19
+ font-weight: bold;
20
+ transition: all var(--transition-time) var(--transition-tf);
21
+ &:not(:disabled):hover {
22
+ color: var(--button-hover-color);
23
+ background: color-mix(
24
+ in srgb,
25
+ var(--button-hover-background) 50%,
26
+ transparent
27
+ );
28
+ }
29
+ &:disabled {
30
+ opacity: 0.5;
31
+ }
32
+ }
33
+
34
+ .iconContainer {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ height: max-content;
39
+ }
40
+ .square {
41
+ padding: var(--spacing-gap-2);
42
+ }
43
+ .root:disabled {
44
+ cursor: not-allowed;
45
+ }
46
+ .root:focus-visible {
47
+ outline: var(--focus-ring);
48
+ }
49
+ .size_sm {
50
+ padding: var(--spacing-gap) calc(1.618 * var(--spacing-gap));
51
+ border-radius: var(--shape-br-sm);
52
+ font-size: 0.9em;
53
+ &.square {
54
+ padding: var(--spacing-gap);
55
+ }
56
+ }
57
+ .size_md {
58
+ padding: var(--spacing-gap-2) calc(1.618 * var(--spacing-gap-2));
59
+ border-radius: var(--shape-br-md);
60
+ &.square {
61
+ padding: var(--spacing-gap-2);
62
+ }
63
+ }
64
+ .size_lg {
65
+ font-size: var(--fs-body);
66
+ padding: var(--spacing-gap-3) calc(1.618 * var(--spacing-gap-3));
67
+ border-radius: var(--shape-br-lg);
68
+ &.square {
69
+ padding: var(--spacing-gap-3);
70
+ }
71
+ }
72
+ .contained {
73
+ --button-color: var(--color-background);
74
+ --button-background: var(--color-on-background);
75
+
76
+ &:not(:disabled):hover {
77
+ background: var(--button-hover-background);
78
+ }
79
+ }
80
+ .outlined {
81
+ border: 1px solid var(--button-outline-color);
82
+ }
83
+
84
+ .position_absolute {
85
+ position: absolute;
86
+ }
87
+ .position_relative {
88
+ position: relative;
89
+ }
90
+ .position_fixed {
91
+ position: fixed;
92
+ }
93
+ .position_sticky {
94
+ position: sticky;
95
+ }
96
+ .fullWidth {
97
+ width: 100%;
98
+ }
99
+ .root[data-color="primary"] {
100
+ --button-color: var(--color-primary);
101
+ --button-background: none;
102
+ --button-hover-color: var(--color-on-primary-container);
103
+ --button-hover-background: var(--color-primary-container);
104
+ --button-outline-color: var(--color-primary);
105
+ }
106
+ .root[data-color="secondary"] {
107
+ --button-color: var(--color-secondary);
108
+ --button-background: none;
109
+ --button-hover-color: var(--color-on-secondary-container);
110
+ --button-hover-background: var(--color-secondary-container);
111
+ --button-outline-color: var(--color-secondary);
112
+ }
113
+ .root[data-color="error"] {
114
+ --button-color: var(--color-error);
115
+ --button-background: none;
116
+ --button-hover-color: var(--color-on-error-container);
117
+ --button-hover-background: var(--color-error-container);
118
+ --button-outline-color: var(--color-error);
119
+ }
120
+ .contained[data-color="primary"] {
121
+ --button-color: var(--color-on-primary);
122
+ --button-background: var(--color-primary);
123
+ }
124
+ .contained[data-color="secondary"] {
125
+ --button-color: var(--color-on-secondary);
126
+ --button-background: var(--color-secondary);
127
+ }
128
+ .contained[data-color="error"] {
129
+ --button-color: var(--color-on-error);
130
+ --button-background: var(--color-error);
131
+ }