@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,119 @@
1
+ "use client";
2
+
3
+ import type {
4
+ PolymorphicComponentProps,
5
+ PolymorphicComponentType,
6
+ } from "@studiocubics/types";
7
+ import { type ElementType, type ComponentProps, type ReactNode } from "react";
8
+ import { cn } from "@studiocubics/utils";
9
+ import styles from "./IdentityDisplay.module.css";
10
+ import {
11
+ eventWithRipple,
12
+ useRipple,
13
+ type UseRippleProps,
14
+ } from "../../Misc/_index";
15
+
16
+ interface IdentityDisplayBaseProps {
17
+ profileName: ReactNode;
18
+ role?: ReactNode;
19
+ desc?: ReactNode;
20
+ alt?: string;
21
+ profileImage?: string | null;
22
+ variant?: "compact" | "full" | "square" | "image-only";
23
+ href?: ComponentProps<"a">["href"];
24
+ slotProps?: {
25
+ image?: ComponentProps<"div">;
26
+ ripple?: UseRippleProps;
27
+ };
28
+ }
29
+
30
+ const defaultElement = "div";
31
+ type DefaultELement = typeof defaultElement;
32
+
33
+ export type IdentityDisplayProps<C extends ElementType = DefaultELement> =
34
+ PolymorphicComponentProps<C, IdentityDisplayBaseProps>;
35
+
36
+ function IdentityDisplayBase<C extends ElementType = DefaultELement>(
37
+ props: IdentityDisplayProps<C>
38
+ ) {
39
+ const {
40
+ as,
41
+ className,
42
+ profileName,
43
+ role,
44
+ profileImage,
45
+ desc,
46
+ variant = "full",
47
+ onClick,
48
+ onTouchStart,
49
+ alt = "Alt text missing",
50
+ slotProps: _slotProps,
51
+ ref,
52
+ ...restProps
53
+ } = props;
54
+ const slotProps = {
55
+ image: _slotProps?.image ?? {},
56
+ ripple: _slotProps?.ripple ?? {},
57
+ };
58
+ const Component = (as ?? defaultElement) as ElementType;
59
+ const clickable = !!props.onClick || !!props.href;
60
+ const { rippleElements, createRipple } = useRipple(slotProps.ripple);
61
+
62
+ const componentProps = {
63
+ className: cn(
64
+ className,
65
+ styles.root,
66
+ styles[variant],
67
+ clickable ? styles.clickable : ""
68
+ ),
69
+ onTouchStart: eventWithRipple(createRipple, onTouchStart),
70
+ onClick: eventWithRipple(createRipple, onClick),
71
+
72
+ ref,
73
+ ...(Component === "a" ? { href: props.href } : {}),
74
+ ...restProps,
75
+ };
76
+
77
+ return (
78
+ <Component {...componentProps}>
79
+ {clickable && rippleElements}
80
+ <div className={styles.main}>
81
+ {profileImage && (
82
+ <div
83
+ {...slotProps.image}
84
+ className={cn(styles.image, slotProps.image?.className)}
85
+ >
86
+ <img
87
+ src={profileImage}
88
+ alt={typeof profileName == "string" ? profileName : alt}
89
+ width={48}
90
+ height={48}
91
+ />
92
+ </div>
93
+ )}
94
+ <div className={styles.details}>
95
+ {profileName &&
96
+ (typeof profileName == "string" ? (
97
+ <h4>{profileName}</h4>
98
+ ) : (
99
+ profileName
100
+ ))}
101
+ {role && (typeof role == "string" ? <p>{role}</p> : role)}
102
+ </div>
103
+ </div>
104
+ {desc &&
105
+ (typeof desc == "string" ? (
106
+ <p className={styles.desc}>{desc}</p>
107
+ ) : (
108
+ desc
109
+ ))}
110
+ </Component>
111
+ );
112
+ }
113
+ IdentityDisplayBase.displayName = "IdentityDisplay";
114
+
115
+ // Type assertion to make it polymorphic
116
+ export const IdentityDisplay = IdentityDisplayBase as PolymorphicComponentType<
117
+ IdentityDisplayBaseProps,
118
+ DefaultELement
119
+ >;
@@ -0,0 +1,6 @@
1
+ .root {
2
+ display: flex;
3
+ flex-direction: column;
4
+ color: var(--color-error);
5
+ font-size: 0.9em;
6
+ }
@@ -0,0 +1,52 @@
1
+ import { type ElementType } from "react";
2
+ import styles from "./InputErrors.module.css";
3
+ import { cn } from "@studiocubics/utils";
4
+ import type {
5
+ PolymorphicComponentProps,
6
+ PolymorphicComponentType,
7
+ } from "@studiocubics/types";
8
+
9
+ export interface InputErrorsBaseProps {
10
+ error?: string | string[];
11
+ children?: null;
12
+ }
13
+
14
+ const defaultElement = "p";
15
+ type DefaultElement = typeof defaultElement;
16
+
17
+ export type InputErrorsProps<C extends ElementType = DefaultElement> =
18
+ PolymorphicComponentProps<C, InputErrorsBaseProps>;
19
+
20
+ function InputErrorsBase<C extends ElementType = DefaultElement>(
21
+ props: InputErrorsProps<C>,
22
+ ) {
23
+ const {
24
+ as,
25
+ error: _error,
26
+ className,
27
+ ...rest
28
+ } = props as InputErrorsProps<DefaultElement>;
29
+ const error = _error?.length == 1 ? _error[0] : _error;
30
+ if (!error || !error.length) return null;
31
+ const isStringError = typeof error === "string";
32
+ const Component = (
33
+ as || isStringError ? defaultElement : "ul"
34
+ ) as ElementType;
35
+ const componentProps = { className: cn(className, styles.root), ...rest };
36
+ return (
37
+ <Component {...componentProps}>
38
+ {isStringError
39
+ ? error
40
+ : error.map((c, i) => {
41
+ return <li key={i}>{c}</li>;
42
+ })}
43
+ </Component>
44
+ );
45
+ }
46
+
47
+ InputErrorsBase.displayName = "InputErrors";
48
+
49
+ export const InputErrors = InputErrorsBase as PolymorphicComponentType<
50
+ InputErrorsBaseProps,
51
+ DefaultElement
52
+ >;
@@ -0,0 +1,29 @@
1
+ .root {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: var(--spacing-gap);
5
+ }
6
+ .kbd {
7
+ --kbd-size-x: 1;
8
+ --kbd-size-y: 1;
9
+ --kbd-base: 30px;
10
+ width: calc(var(--kbd-size-x) * var(--kbd-base));
11
+ height: calc(var(--kbd-size-y) * var(--kbd-base));
12
+ padding: 0.2em;
13
+ display: flex;
14
+ flex-direction: column;
15
+ justify-content: center;
16
+ gap: 0.1em;
17
+ text-align: center;
18
+ font-size: 60%;
19
+ /* font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif; */
20
+ font-family:
21
+ ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
22
+ /* font-weight: bold; */
23
+
24
+ &.surface {
25
+ color: #ebd6dc;
26
+ background: #313133;
27
+ border-radius: 0.4em;
28
+ }
29
+ }
@@ -0,0 +1,39 @@
1
+ import { cn } from "@studiocubics/utils";
2
+ import { type CSSProperties, Fragment, type ComponentProps } from "react";
3
+ import styles from "./Kbd.module.css";
4
+ import { type ButtonList, buttonList, type ButtonSpec } from "./buttonList";
5
+
6
+ export type KeyboardKey = keyof ButtonList;
7
+ export type KeyCombo = KeyboardKey[];
8
+ export interface KbdProps extends Omit<ComponentProps<"div">, "children"> {
9
+ buttons: KeyCombo;
10
+ }
11
+
12
+ export function Kbd(props: KbdProps) {
13
+ const { className, buttons, ...rest } = props;
14
+
15
+ return (
16
+ <div className={cn(styles.root, className)} {...rest}>
17
+ {buttons.map((b, i) => {
18
+ const button: ButtonSpec = buttonList[b];
19
+ return (
20
+ <Fragment key={i}>
21
+ <kbd
22
+ className={cn(styles.kbd, styles.surface)}
23
+ style={
24
+ {
25
+ "--kbd-size-x": button.sizeX ?? 1,
26
+ "--kbd-size-y": button.sizeY ?? 1,
27
+ } as CSSProperties
28
+ }
29
+ >
30
+ {button.content}
31
+ </kbd>
32
+
33
+ {i >= 0 && i < buttons.length - 1 && "+"}
34
+ </Fragment>
35
+ );
36
+ })}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Kbd";
2
+ export * from "./buttonList";
@@ -0,0 +1,246 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type ButtonSpec = {
4
+ content: ReactNode;
5
+ sizeX?: number;
6
+ sizeY?: number;
7
+ };
8
+
9
+ export const buttonList = {
10
+ A: { content: <>A</> },
11
+ B: { content: <>B</> },
12
+ C: { content: <>C</> },
13
+ D: { content: <>D</> },
14
+ E: { content: <>E</> },
15
+ F: { content: <>F</> },
16
+ G: { content: <>G</> },
17
+ H: { content: <>H</> },
18
+ I: { content: <>I</> },
19
+ J: { content: <>J</> },
20
+ K: { content: <>K</> },
21
+ L: { content: <>L</> },
22
+ M: { content: <>M</> },
23
+ N: { content: <>N</> },
24
+ O: { content: <>O</> },
25
+ P: { content: <>P</> },
26
+ Q: { content: <>Q</> },
27
+ R: { content: <>R</> },
28
+ S: { content: <>S</> },
29
+ T: { content: <>T</> },
30
+ U: { content: <>U</> },
31
+ V: { content: <>V</> },
32
+ W: { content: <>W</> },
33
+ X: { content: <>X</> },
34
+ Y: { content: <>Y</> },
35
+ Z: { content: <>Z</> },
36
+
37
+ "0": { content: <>0</> },
38
+ "1": { content: <>1</> },
39
+ "2": { content: <>2</> },
40
+ "3": { content: <>3</> },
41
+ "4": { content: <>4</> },
42
+ "5": { content: <>5</> },
43
+ "6": { content: <>6</> },
44
+ "7": { content: <>7</> },
45
+ "8": { content: <>8</> },
46
+ "9": { content: <>9</> },
47
+
48
+ "`": { content: <>`</> },
49
+ "-": { content: <>-</> },
50
+ "=": { content: <>=</> },
51
+ "[": { content: <>{"["}</> },
52
+ "]": { content: <>{"]"}</> },
53
+ Backslash: {
54
+ content: <>\</>,
55
+ },
56
+ ";": { content: <>;</> },
57
+ "'": {
58
+ content: <>&apos;</>,
59
+ },
60
+ ",": { content: <>,</> },
61
+ ".": { content: <>.</> },
62
+ Forwardslash: {
63
+ content: <>/</>,
64
+ },
65
+
66
+ "~": { content: <>~</> },
67
+ "!": { content: <>!</> },
68
+ "@": { content: <>@</> },
69
+ "#": { content: <>#</> },
70
+ $: { content: <>$</> },
71
+ "%": { content: <>%</> },
72
+ "^": { content: <>^</> },
73
+ "&": { content: <>&</> },
74
+ "*": { content: <>*</> },
75
+ "(": { content: <>{"("}</> },
76
+ ")": { content: <>{")"}</> },
77
+ _: { content: <>_</> },
78
+ "+": { content: <>+</> },
79
+
80
+ "{": {
81
+ content: <>{"{"}</>,
82
+ },
83
+ "}": {
84
+ content: <>{"}"}</>,
85
+ },
86
+ "|": { content: <>|</> },
87
+ ":": { content: <>:</> },
88
+ '"': {
89
+ content: <>&quot;</>,
90
+ },
91
+ "<": {
92
+ content: <>{"<"}</>,
93
+ },
94
+ ">": {
95
+ content: <>{">"}</>,
96
+ },
97
+ "?": { content: <>?</> },
98
+
99
+ Space: {
100
+ content: <>Space</>,
101
+ },
102
+ Enter: {
103
+ content: <>Enter</>,
104
+ },
105
+ Backspace: {
106
+ content: <>Backspace</>,
107
+ },
108
+ Esc: {
109
+ content: <>Esc</>,
110
+ },
111
+ Shift: {
112
+ content: <>Shift</>,
113
+ },
114
+ Ctrl: {
115
+ content: <>Ctrl</>,
116
+ },
117
+ Alt: {
118
+ content: <>Alt</>,
119
+ },
120
+
121
+ Tab: {
122
+ content: (
123
+ <>
124
+ Tab
125
+ <br />↹
126
+ </>
127
+ ),
128
+ },
129
+
130
+ CapsLock: {
131
+ content: (
132
+ <>
133
+ Caps
134
+ <br />
135
+ Lock
136
+ </>
137
+ ),
138
+ },
139
+ Fn: { content: <>Fn</> },
140
+ UpArrow: {
141
+ content: <>↑</>,
142
+ },
143
+ DownArrow: {
144
+ content: <>↓</>,
145
+ },
146
+ LeftArrow: {
147
+ content: <>←</>,
148
+ },
149
+ RightArrow: {
150
+ content: <>→</>,
151
+ },
152
+
153
+ Insert: {
154
+ content: <>Insert</>,
155
+ },
156
+ Delete: {
157
+ content: <>Delete</>,
158
+ },
159
+ Home: {
160
+ content: <>Home</>,
161
+ },
162
+ End: {
163
+ content: <>End</>,
164
+ },
165
+ PageUp: {
166
+ content: <>Pg Up</>,
167
+ },
168
+ PageDown: {
169
+ content: <>Pg Dn</>,
170
+ },
171
+
172
+ PrintScreen: {
173
+ content: (
174
+ <>
175
+ Print
176
+ <br />
177
+ Scr
178
+ </>
179
+ ),
180
+ },
181
+
182
+ ScrollLock: {
183
+ content: (
184
+ <>
185
+ Scrl
186
+ <br />
187
+ Lock
188
+ </>
189
+ ),
190
+ },
191
+
192
+ Pause: {
193
+ content: <>Pause</>,
194
+ },
195
+
196
+ NumLock: {
197
+ content: (
198
+ <>
199
+ Num
200
+ <br />
201
+ Lock
202
+ </>
203
+ ),
204
+ },
205
+
206
+ "⌘": { content: <>⌘</> },
207
+
208
+ "⏮": {
209
+ content: <>⏮</>,
210
+ },
211
+ "▶": {
212
+ content: <>▶</>,
213
+ },
214
+ "⏸": {
215
+ content: <>⏸</>,
216
+ },
217
+ "⏯": {
218
+ content: <>⏯</>,
219
+ },
220
+ "⏹": {
221
+ content: <>⏹</>,
222
+ },
223
+ "⏭": {
224
+ content: <>⏭</>,
225
+ },
226
+ "⏪": {
227
+ content: <>⏪</>,
228
+ },
229
+ "⏩": {
230
+ content: <>⏩</>,
231
+ },
232
+ "🔈": {
233
+ content: <>🔈</>,
234
+ },
235
+ "🔉": {
236
+ content: <>🔉</>,
237
+ },
238
+ "🔊": {
239
+ content: <>🔊</>,
240
+ },
241
+ "🔇": {
242
+ content: <>🔇</>,
243
+ },
244
+ } satisfies Record<string, ButtonSpec>;
245
+
246
+ export type ButtonList = typeof buttonList;
@@ -0,0 +1,32 @@
1
+ .root {
2
+ display: flex;
3
+ flex-direction: row;
4
+ padding-block: var(--spacing-gap);
5
+ align-items: center;
6
+ /* padding-inline: var(--spacing-gap-4); */
7
+ /* & > label, */
8
+ & > .content {
9
+ flex: 1;
10
+ }
11
+ & > label {
12
+ padding-right: var(--spacing-gap-2);
13
+ font-size: 0.9em;
14
+ color: var(--color-on-background-faint);
15
+ }
16
+ & > .content {
17
+ padding-left: var(--spacing-gap-2);
18
+ border-left: 1px solid var(--color-outline);
19
+ }
20
+ }
21
+ @container (max-width:400px) {
22
+ .root {
23
+ flex-direction: column;
24
+ & > label {
25
+ padding-right: 0;
26
+ }
27
+ & > .content {
28
+ padding-left: 0;
29
+ border-left: none;
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,20 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ import styles from "./LabeledValue.module.css";
3
+ import { cn } from "@studiocubics/utils";
4
+ export interface LabeledValueProps extends ComponentProps<"div"> {
5
+ label: ReactNode;
6
+ slotProps?: {
7
+ label?: ComponentProps<"label">;
8
+ content?: ComponentProps<"span">;
9
+ };
10
+ }
11
+
12
+ export function LabeledValue(props: LabeledValueProps) {
13
+ const { label, children, className, ...rest } = props;
14
+ return (
15
+ <div className={cn(styles.root, className)} {...rest}>
16
+ <label>{label}</label>
17
+ <span className={styles.content}>{children}</span>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,143 @@
1
+ .root {
2
+ list-style-type: none;
3
+ padding-inline: var(--spacing-gap-3);
4
+ display: flex;
5
+ flex-direction: column;
6
+ position: relative;
7
+ }
8
+ .root:has(.shortened) {
9
+ padding: 0;
10
+ }
11
+ .secondary {
12
+ border-left: 1px solid var(--color-outline);
13
+ margin-left: var(--spacing-gap-3);
14
+ padding-inline: var(--spacing-gap);
15
+ margin-block: var(--spacing-gap-2);
16
+ }
17
+ .listItem {
18
+ --list-color: var(--color-on-background);
19
+ --list-background: transparent;
20
+ --list-hover-color: var(--color-on-background);
21
+ --list-hover-background: var(--color-background-faint);
22
+ --list-selected-color: var(--color-primary);
23
+ --list-selected-background: none;
24
+ --list-selected-hover-color: var(--color-on-background);
25
+ --list-selected-hover-background: none;
26
+ padding: calc(var(--spacing-gap-3) / 1.75)
27
+ calc(1.618 * var(--spacing-gap-3) / 1.75);
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ gap: var(--spacing-gap);
32
+ overflow: hidden;
33
+ position: relative;
34
+ border-radius: var(--shape-br-sm);
35
+ color: var(--list-color);
36
+ transition: all var(--transition-time) var(--transition-tf);
37
+ background: var(--list-background);
38
+ }
39
+
40
+ .listItem.clickable {
41
+ cursor: pointer;
42
+ }
43
+ .listItem.clickable:not(.selected):not(:disabled):hover {
44
+ background: color-mix(in srgb, var(--list-hover-background) 50%, transparent);
45
+ color: var(--list-hover-color);
46
+ }
47
+ .listItem:focus-visible {
48
+ outline: var(--focus-ring);
49
+ }
50
+ .selected {
51
+ background: var(--list-selected-background);
52
+ color: var(--list-selected-color);
53
+ &:hover {
54
+ background: var(--list-selected-hover-background);
55
+ color: var(--list-selected-hover-color);
56
+ }
57
+ }
58
+ .disabled {
59
+ cursor: not-allowed;
60
+ background: var(--color-background-faint);
61
+ color: var(--color-on-background-faint);
62
+ }
63
+ .shortened {
64
+ gap: 0;
65
+ & > *:not(.primaryIcon) {
66
+ max-width: 0;
67
+ }
68
+ }
69
+ .content {
70
+ flex: 1;
71
+ display: flex;
72
+ align-items: center;
73
+ max-width: 50ch;
74
+ color: inherit;
75
+ font-weight: bold;
76
+ transition: max-width var(--transition-time) var(--transition-tf);
77
+ overflow: hidden;
78
+ }
79
+
80
+ .iconContainer {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ color: inherit;
85
+ max-width: 50ch;
86
+ transition: max-width var(--transition-time) var(--transition-tf);
87
+ }
88
+
89
+ .dropDownIcon {
90
+ position: relative;
91
+ display: block;
92
+ }
93
+ .dropDownIcon.openSublist {
94
+ rotate: 180deg;
95
+ }
96
+
97
+ .marker {
98
+ display: none;
99
+ position: absolute;
100
+ left: 0;
101
+ width: 0;
102
+ z-index: -1;
103
+ transition:
104
+ width 0s linear,
105
+ top var(--transition-time) var(--transition-tf),
106
+ left var(--transition-time) var(--transition-tf);
107
+ --glass-border-radius: var(--shape-br-sm);
108
+ background-color: color-mix(
109
+ in srgb,
110
+ var(--color-background) 50%,
111
+ transparent
112
+ );
113
+ }
114
+ .listItem[data-color="primary"] {
115
+ --list-color: var(--color-primary);
116
+ --list-background: transparent;
117
+ --list-hover-color: var(--color-on-primary-container);
118
+ --list-hover-background: var(--color-primary-container);
119
+ --list-selected-color: none;
120
+ --list-selected-background: none;
121
+ --list-selected-hover-color: var(--color-on-background);
122
+ --list-selected-hover-background: none;
123
+ }
124
+ .listItem[data-color="secondary"] {
125
+ --list-color: var(--color-secondary);
126
+ --list-background: transparent;
127
+ --list-hover-color: var(--color-on-secondary-container);
128
+ --list-hover-background: var(--color-secondary-container);
129
+ --list-selected-color: none;
130
+ --list-selected-background: none;
131
+ --list-selected-hover-color: var(--color-on-background);
132
+ --list-selected-hover-background: none;
133
+ }
134
+ .listItem[data-color="error"] {
135
+ --list-color: var(--color-error);
136
+ --list-background: transparent;
137
+ --list-hover-color: var(--color-on-error-container);
138
+ --list-hover-background: var(--color-error-container);
139
+ --list-selected-color: none;
140
+ --list-selected-background: none;
141
+ --list-selected-hover-color: var(--color-on-secondary);
142
+ --list-selected-hover-background: none;
143
+ }