@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,18 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+ import styles from "./SidebarBody.module.css";
5
+ import { cn } from "@studiocubics/utils";
6
+ import { useSidebar } from "../Sidebar";
7
+ export function SidebarBody(props: ComponentProps<"div">) {
8
+ const { children, className, ...rest } = props;
9
+ const { sidebarOpen } = useSidebar();
10
+ return (
11
+ <div
12
+ className={cn(styles.root, className, sidebarOpen ? styles.open : "")}
13
+ {...rest}
14
+ >
15
+ {children}
16
+ </div>
17
+ );
18
+ }
@@ -0,0 +1,20 @@
1
+ .root {
2
+ background: var(--color-surface);
3
+ transition: width var(--transition-time) var(--transition-tf);
4
+ width: 100%;
5
+ display: flex;
6
+ flex-direction: row;
7
+ z-index: -1;
8
+ }
9
+
10
+ @media (min-width: 600px) {
11
+ .root {
12
+ background: var(--sidebar-color-surface);
13
+ flex-direction: column;
14
+ width: var(--sidebar-closed-width);
15
+ z-index: 0;
16
+ }
17
+ .open {
18
+ width: var(--sidebar-open-width);
19
+ }
20
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+ import styles from "./SidebarDrawer.module.css";
5
+ import { cn } from "@studiocubics/utils";
6
+ import { useSidebar } from "../Sidebar";
7
+
8
+ export function SidebarDrawer(props: ComponentProps<"div">) {
9
+ const { children, className, ...rest } = props;
10
+ const { sidebarOpen } = useSidebar();
11
+ return (
12
+ <aside
13
+ className={cn(styles.root, className, sidebarOpen ? styles.open : "")}
14
+ {...rest}
15
+ >
16
+ {children}
17
+ </aside>
18
+ );
19
+ }
@@ -0,0 +1,35 @@
1
+ .root {
2
+ /* display: none; */
3
+
4
+ position: fixed;
5
+ left: -100%;
6
+ top: calc(var(--sidebar-header-height) + var(--sidebar-body-height));
7
+ width: var(--sidebar-open-width);
8
+ padding-bottom: var(--spacing-gap-2);
9
+ padding-top: var(--spacing-gap-1);
10
+
11
+ background-color: var(--color-surface-alpha);
12
+ backdrop-filter: var(--backdrop-blur);
13
+ height: calc(
14
+ 100dvh - var(--sidebar-body-height) - var(--sidebar-header-height)
15
+ );
16
+ overflow: auto;
17
+ transition: left var(--transition-time) var(--transition-tf);
18
+ }
19
+ .open {
20
+ left: 0;
21
+ }
22
+ @media (min-width: 600px) {
23
+ .root {
24
+ position: relative;
25
+ left: 0;
26
+ height: unset;
27
+ width: unset;
28
+ top: 0;
29
+ background-color: transparent;
30
+ backdrop-filter: none;
31
+ }
32
+ .open {
33
+ left: 0;
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ import { cn } from "@studiocubics/utils";
4
+ import styles from "./SidebarFooter.module.css";
5
+ import type { ComponentProps } from "react";
6
+ import { useSidebar } from "../Sidebar";
7
+
8
+ export function SidebarFooter(props: ComponentProps<"div">) {
9
+ const { children, className, ...rest } = props;
10
+ const { sidebarOpen } = useSidebar();
11
+ return (
12
+ <div
13
+ className={cn(styles.root, className, sidebarOpen ? styles.open : "")}
14
+ {...rest}
15
+ >
16
+ {children}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+ import { useSidebar } from "../Sidebar";
5
+
6
+ export function SidebarHeader(props: ComponentProps<"div">) {
7
+ const { children, ...rest } = props;
8
+ const { sidebarOpen } = useSidebar();
9
+ return (
10
+ <div data-open={sidebarOpen} {...rest}>
11
+ {children}
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,12 @@
1
+ .root {
2
+ /* background: var(--sidebar-color-surface); */
3
+ flex: 1;
4
+ z-index: -3;
5
+ overflow: auto;
6
+ container-type: inline-size;
7
+ }
8
+ @media (min-width: 600px) {
9
+ .root {
10
+ z-index: 0;
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps } from "react";
2
+ import styles from "./SidebarViewport.module.css";
3
+ import { cn } from "@studiocubics/utils";
4
+ export function SidebarViewport(props: ComponentProps<"div">) {
5
+ const { children, className, ...rest } = props;
6
+ return (
7
+ <div className={cn(styles.root, className)} {...rest}>
8
+ {children}
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./Sidebar";
2
+ export * from "./SidebarBody/SidebarBody";
3
+ export * from "./SidebarDrawer/SidebarDrawer";
4
+ export * from "./SidebarFooter/SidebarFooter";
5
+ export * from "./SidebarHeader/SidebarHeader";
6
+ export * from "./SidebarViewport/SidebarViewport";
@@ -0,0 +1,46 @@
1
+ .root {
2
+ overflow: auto;
3
+ max-width: 100%;
4
+ max-height: 100%;
5
+ }
6
+ .table {
7
+ display: grid;
8
+ width: 100%;
9
+ /* border-collapse: collapse; */
10
+ font-family:
11
+ system-ui,
12
+ -apple-system,
13
+ BlinkMacSystemFont,
14
+ sans-serif;
15
+ font-size: var(--fs-body2);
16
+ line-height: 1.4;
17
+ border-radius: var(--shape-br-md);
18
+ overflow: hidden;
19
+ }
20
+ .tableCell {
21
+ padding: 8px 12px;
22
+ border-right: 1px solid var(--color-outline);
23
+ border-bottom: 1px solid var(--color-outline);
24
+ background: var(--color-background);
25
+ overflow: hidden;
26
+ text-overflow: ellipsis;
27
+ white-space: nowrap;
28
+ user-select: none;
29
+ &:hover {
30
+ background: var(--color-background-faint);
31
+ }
32
+ &:focus-visible {
33
+ outline: 2px solid var(--color-primary);
34
+ outline-offset: -2px;
35
+ }
36
+ /* Last column */
37
+ &:nth-child(n) {
38
+ border-right: none;
39
+ }
40
+ &[data-selected="true"] {
41
+ background: var(--color-surface);
42
+ outline: 2px solid var(--color-primary);
43
+ outline-offset: -2px;
44
+ z-index: 1;
45
+ }
46
+ }
@@ -0,0 +1,222 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ isValidElement,
6
+ useContext,
7
+ useRef,
8
+ useState,
9
+ type ElementType,
10
+ type MouseEvent,
11
+ } from "react";
12
+ import { getColumnCount, isAOA, isAOO, isOCR, isOOA } from "./tableUtils";
13
+ import styles from "./Table.module.css";
14
+ import type { CellData, TableContextProps, TableProps } from "./types";
15
+ import type {
16
+ PolymorphicComponentProps,
17
+ PolymorphicComponentType,
18
+ } from "@studiocubics/types";
19
+ import { cn, mergeRefs } from "@studiocubics/utils";
20
+
21
+ const TableContext = createContext<TableContextProps | null>(null);
22
+
23
+ export function useTable() {
24
+ const c = useContext(TableContext);
25
+ if (!c) throw new Error("Components must be wrapped in <Table/>");
26
+ return c;
27
+ }
28
+
29
+ /**
30
+ * Works for data of four types
31
+ *
32
+ * **Array of arrays:-**
33
+ *
34
+ * ```ts
35
+ * const aoaData: TableData = [
36
+ ["name", "email", "age"], // column headers are always 0th element
37
+ ["pumpernickel", "pump@nick.com", 420],
38
+ ["rumpelstiltskin", "rump@stilt.com", 96],
39
+ ];
40
+ * ```
41
+ * **Array of objects:-**
42
+ *
43
+ * ```ts
44
+ * const aooData: TableData = [
45
+ { name: "pumpernickel", email: "pump@nick.com", age: 420 }, // keys are column headers
46
+ { name: "rumpelstiltskin", email: "rump@stilt.com", age: 96 },
47
+ ];
48
+ * ```
49
+ * **Object of arrays:-**
50
+ *
51
+ * ```ts
52
+ * const ooaData: TableData = {
53
+ name: ["pumpernickel", "rumpelstiltskin"], // keys are column headers
54
+ email: ["pump@nick.com", "rump@stilt.com"],
55
+ age: [420, 96],
56
+ };
57
+ * ```
58
+ * **Explicit schema + rows:-**
59
+ *
60
+ * ```ts
61
+ * const ocrData: TableData = {
62
+ columns: ["name", "email", "age"], // column headers are defined in a seperate columns array
63
+ rows: [
64
+ ["pumpernickel", "pump@nick.com", 420],
65
+ ["rumpelstiltskin", "rump@stilt.com", 96],
66
+ ],
67
+ };
68
+ * ```
69
+ */
70
+ export function Table({ data }: TableProps) {
71
+ const [selectedCell, setSelectedCell] = useState<HTMLElement | null>(null);
72
+
73
+ const columnCount = getColumnCount(data);
74
+ function renderRows() {
75
+ // AOA (array of arrays)
76
+ if (isAOA(data)) {
77
+ return data.map((row, r) =>
78
+ Array.isArray(row)
79
+ ? row.map((cell, c) => (
80
+ <TableCell
81
+ id={`${r},${c}`}
82
+ key={`${r},${c}`}
83
+ // selected={selectedCell[0] === r && selectedCell[1] === c}
84
+ >
85
+ {renderCell(cell)}
86
+ </TableCell>
87
+ ))
88
+ : null,
89
+ );
90
+ }
91
+
92
+ // AOO (array of objects)
93
+ if (isAOO(data)) {
94
+ const columns = Object.keys(data[0]);
95
+
96
+ return data.map((row, r) =>
97
+ columns.map((col, c) => (
98
+ <TableCell
99
+ id={`${r},${c}`}
100
+ key={`${r},${c}`}
101
+ // selected={selectedCell[0] === r && selectedCell[1] === c}
102
+ >
103
+ {renderCell(row[col])}
104
+ </TableCell>
105
+ )),
106
+ );
107
+ }
108
+
109
+ // OOA (object of arrays)
110
+ if (isOOA(data)) {
111
+ const columns = Object.keys(data);
112
+ const rowCount = data[columns[0]]?.length ?? 0;
113
+
114
+ return Array.from({ length: rowCount }, (_, r) =>
115
+ columns.map((col, c) => (
116
+ <TableCell
117
+ id={`${r},${c}`}
118
+ key={`${r},${c}`}
119
+ // selected={selectedCell[0] === r && selectedCell[1] === c}
120
+ >
121
+ {renderCell(data[col][r])}
122
+ </TableCell>
123
+ )),
124
+ );
125
+ }
126
+
127
+ // OCR (columns + rows)
128
+ if (isOCR(data)) {
129
+ return data.rows.map((row, r) =>
130
+ row.map((cell, c) => (
131
+ <TableCell
132
+ id={`${r},${c}`}
133
+ key={`${r},${c}`}
134
+ // selected={selectedCell[0] === r && selectedCell[1] === c}
135
+ >
136
+ {renderCell(cell)}
137
+ </TableCell>
138
+ )),
139
+ );
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ return (
146
+ <TableContext.Provider
147
+ value={{ selectedCell, setSelectedCell, columnCount }}
148
+ >
149
+ <div className={styles.root}>
150
+ <div
151
+ className={styles.table}
152
+ style={{
153
+ gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,
154
+ }}
155
+ >
156
+ {renderRows()}
157
+ </div>
158
+ </div>
159
+ </TableContext.Provider>
160
+ );
161
+ }
162
+ export interface TableCellBaseProps {}
163
+
164
+ const defaultElement = "div";
165
+ type DefaultElement = typeof defaultElement;
166
+
167
+ export type TableCellProps<C extends ElementType = DefaultElement> =
168
+ PolymorphicComponentProps<C, TableCellBaseProps>;
169
+
170
+ function TableCellBase<C extends ElementType = DefaultElement>(
171
+ props: TableCellProps<C>,
172
+ ) {
173
+ const { as, children, ref, onClick, ...rest } =
174
+ props as TableCellProps<DefaultElement>;
175
+ const cellRef = useRef<HTMLDivElement>(null);
176
+ const { setSelectedCell, selectedCell } = useTable();
177
+
178
+ const Component = as || defaultElement;
179
+ const componentProps = {
180
+ className: cn(
181
+ styles.tableCell,
182
+ selectedCell?.id == cellRef.current?.id ? styles.selected : "",
183
+ ),
184
+ ref: mergeRefs(cellRef, ref),
185
+ onClick: handleSelectCell,
186
+ ...rest,
187
+ };
188
+
189
+ function handleSelectCell(e: MouseEvent<HTMLDivElement>) {
190
+ setSelectedCell(cellRef.current);
191
+ onClick?.(e);
192
+ }
193
+
194
+ return <Component {...componentProps}>{children}</Component>;
195
+ }
196
+
197
+ TableCellBase.displayName = "TableCell";
198
+
199
+ export const TableCell = TableCellBase as PolymorphicComponentType<
200
+ TableCellBaseProps,
201
+ DefaultElement
202
+ >;
203
+
204
+ export function renderCell(cell: CellData) {
205
+ if (typeof cell === "boolean") return cell ? "✔" : "❌";
206
+ if (!cell) return;
207
+ if (typeof cell === "string" || isValidElement(cell)) return cell;
208
+ if (typeof cell === "object" && "type" in cell) {
209
+ switch (cell.type) {
210
+ case "boolean":
211
+ return cell.value ? "✔" : "X";
212
+ case "timestamp":
213
+ return cell.value.toLocaleString();
214
+ case "array":
215
+ return JSON.stringify(cell.value);
216
+ case "image":
217
+ return <img src={cell.value} alt={cell.alt} />;
218
+ default:
219
+ return cell.value;
220
+ }
221
+ }
222
+ }
@@ -0,0 +1,4 @@
1
+ // TODO complete table footer
2
+ export function TableFooter() {
3
+ return <div>TableFooter</div>;
4
+ }
@@ -0,0 +1,4 @@
1
+ // TODO complete table header
2
+ export function TableHeader() {
3
+ return <div>TableHeader</div>;
4
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./Table";
2
+ export * from "./TableFooter";
3
+ export * from "./TableHeader";
4
+ export * from "./tableUtils";
5
+ export * from "./types";
@@ -0,0 +1,142 @@
1
+ import type { TableData, RowData, CellData } from "./types";
2
+
3
+ /**
4
+ * Checks if array of arrays:-
5
+ *
6
+ * ```ts
7
+ * const aoaData: TableData = [
8
+ * ["name", "email", "age"],
9
+ * ["pumpernickel", "pump@nick.com", 420],
10
+ * ["rumpelstiltskin", "rump@stilt.com", 96],
11
+ * ];
12
+ * ```
13
+ */
14
+ export function isAOA(data: TableData): data is RowData[] {
15
+ if (!Array.isArray(data) || data.length === 0) return false;
16
+
17
+ if (!Array.isArray(data[0])) return false;
18
+
19
+ for (let i = 1; i < data.length; i++) {
20
+ const row = data[i];
21
+ if (row === null || (typeof row !== "object" && !Array.isArray(row))) {
22
+ return false;
23
+ }
24
+ }
25
+ return true;
26
+ }
27
+
28
+ /*
29
+ * Checks if array of objects:-
30
+ *
31
+ * ```ts
32
+ * const aooData: TableData = [
33
+ * { name: "pumpernickel", email: "pump@nick.com", age: 420 },
34
+ * { name: "rumpelstiltskin", email: "rump@stilt.com", age: 96 },
35
+ * ];
36
+ * ```
37
+ */
38
+ export function isAOO(
39
+ data: TableData,
40
+ ): data is { [column: string]: CellData }[] {
41
+ if (!Array.isArray(data) || data.length === 0) return false;
42
+
43
+ for (let i = 0; i < data.length; i++) {
44
+ const row = data[i];
45
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
46
+ return false;
47
+ }
48
+ }
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * Checks if object of arrays:-
54
+ *
55
+ * ```ts
56
+ * const ooaData: TableData = {
57
+ * name: ["pumpernickel", "rumpelstiltskin"],
58
+ * email: ["pump@nick.com", "rump@stilt.com"],
59
+ * age: [420, 96],
60
+ * };
61
+ * ```
62
+ */
63
+ export function isOOA(
64
+ data: TableData,
65
+ ): data is { [column: string]: CellData[] } {
66
+ if (
67
+ data === null ||
68
+ typeof data !== "object" ||
69
+ Array.isArray(data) ||
70
+ "columns" in data
71
+ ) {
72
+ return false;
73
+ }
74
+
75
+ for (const value of Object.values(data)) {
76
+ if (!Array.isArray(value)) return false;
77
+ }
78
+ return true;
79
+ }
80
+
81
+ /**
82
+ * Checks if explicit schema + rows:-
83
+ *
84
+ * ```ts
85
+ * const ocrData: TableData = {
86
+ * columns: ["name", "email", "age"],
87
+ * rows: [
88
+ * ["pumpernickel", "pump@nick.com", 420],
89
+ * ["rumpelstiltskin", "rump@stilt.com", 96],
90
+ * ],
91
+ * };
92
+ * ```
93
+ */
94
+ export function isOCR(
95
+ data: TableData,
96
+ ): data is { columns: CellData[]; rows: CellData[][] } {
97
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
98
+ return false;
99
+ }
100
+
101
+ const d = data as any;
102
+
103
+ if (!Array.isArray(d.columns) || !Array.isArray(d.rows)) {
104
+ return false;
105
+ }
106
+
107
+ for (let i = 0; i < d.rows.length; i++) {
108
+ if (!Array.isArray(d.rows[i])) return false;
109
+ }
110
+
111
+ return true;
112
+ }
113
+
114
+ /**
115
+ * Returns the column count for any supported TableData shape.
116
+ * Assumes the data has already been validated with the appropriate is* guard.
117
+ */
118
+ export function getColumnCount(data: TableData): number {
119
+ // Array of arrays (AOA)
120
+ if (Array.isArray(data)) {
121
+ if (data.length === 0) return 0;
122
+
123
+ const firstRow = data[0];
124
+
125
+ // Header row (AOA)
126
+ if (Array.isArray(firstRow)) {
127
+ return firstRow.length;
128
+ }
129
+
130
+ // Array of objects (AOO)
131
+ return Object.keys(firstRow).length;
132
+ }
133
+
134
+ // Object of arrays (OOA)
135
+ if ("columns" in data && "rows" in data) {
136
+ // Explicit columns + rows (OCR)
137
+ return data.columns.length;
138
+ }
139
+
140
+ // Object of arrays (OOA)
141
+ return Object.keys(data).length;
142
+ }
@@ -0,0 +1,48 @@
1
+ import type { SetState } from "@studiocubics/types";
2
+ import type { ReactNode } from "react";
3
+
4
+ export type TableData =
5
+ | RowData[]
6
+ | { columns: CellData[]; rows: CellData[][] }
7
+ | { [column: string]: CellData[] };
8
+
9
+ export type RowData = { [column: string]: CellData } | CellData[];
10
+ export type ITableCell = {
11
+ defaultSelected?: boolean;
12
+ } & (
13
+ | {
14
+ value: Date;
15
+ type: "timestamp";
16
+ }
17
+ | {
18
+ value: (string | number)[];
19
+ type: "array";
20
+ }
21
+ | {
22
+ value: string;
23
+ type: "string";
24
+ }
25
+ | {
26
+ value: number;
27
+ type: "number";
28
+ }
29
+ | {
30
+ alt: string;
31
+ value: string;
32
+ type: "image";
33
+ }
34
+ | {
35
+ value: boolean;
36
+ type: "boolean";
37
+ }
38
+ );
39
+ export type CellData = ITableCell | ReactNode;
40
+ export interface TableContextProps {
41
+ selectedCell: HTMLElement | null;
42
+ setSelectedCell: SetState<HTMLElement | null>;
43
+ columnCount: number;
44
+ }
45
+ export interface TableProps {
46
+ data: TableData;
47
+ showMarker?: boolean;
48
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./Dialog/Dialog";
2
+ export * from "./PageLayout/PageLayout";
3
+ export * from "./PageLayoutPagination/PageLayoutPagination";
4
+ export * from "./PageLayoutTabs/PageLayoutTabs";
5
+ export * from "./Popover/Popover";
6
+ export * from "./SectionWrapper/SectionWrapper";
7
+ export * from "./Sidebar/_index";
8
+ export * from "./Table/_index";
@@ -0,0 +1,31 @@
1
+ .root {
2
+ position: fixed;
3
+ width: 100%;
4
+ height: 100vh;
5
+ top: 0;
6
+ left: 0;
7
+ z-index: -1;
8
+ isolation: isolate;
9
+ pointer-events: none;
10
+ }
11
+ .main {
12
+ /* --cursor-mouse-down-size: 11em; */
13
+ background: radial-gradient(
14
+ 41.94% 82.95% at 50% 50%,
15
+ color-mix(in srgb, var(--color-primary) 50%, transparent) 0%,
16
+ color-mix(in srgb, var(--color-secondary) 50%, transparent) 100%
17
+ );
18
+ filter: blur(90px);
19
+
20
+ position: absolute;
21
+ transition-property: width, height, filter;
22
+ transition-duration: var(--transition-time);
23
+ transition-timing-function: var(--transition-tf);
24
+ translate: -50% -50%;
25
+ border-radius: 50%;
26
+ }
27
+ /* .mouseDowned {
28
+ filter: blur(50px);
29
+ width: calc(1.618 * var(--cursor-mouse-down-size));
30
+ height: var(--cursor-mouse-down-size);
31
+ } */