@noya-app/noya-designsystem 0.1.37 → 0.1.38

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.
@@ -24,6 +24,7 @@ import { WindowScroller, WindowScrollerChildProps } from "react-virtualized";
24
24
  import { ListChildComponentProps, VariableSizeList } from "react-window";
25
25
  import { mergeEventHandlers } from "../hooks/mergeEventHandlers";
26
26
  import { useHover } from "../hooks/useHover";
27
+ import { cssVars } from "../theme";
27
28
  import { ContextMenu } from "./ContextMenu";
28
29
  import { InputField, InputFieldInputProps } from "./InputField";
29
30
  import { MenuItem } from "./internal/Menu";
@@ -180,22 +181,6 @@ function getPositionMargin(marginType: ListRowMarginType) {
180
181
  * Row
181
182
  * ------------------------------------------------------------------------- */
182
183
 
183
- const theme = {
184
- colors: {
185
- textMuted: "var(--text-muted)",
186
- listView: {
187
- raisedBackground: "var(--listview-raised-background)",
188
- },
189
- secondary: "var(--secondary)",
190
- primary: "var(--primary)",
191
- textDisabled: "var(--text-disabled)",
192
- primaryLight: "var(--primary-light)",
193
- secondaryLight: "var(--secondary-light)",
194
- activeBackground: "var(--active-background)",
195
- dividerSubtle: "var(--divider-subtle)",
196
- },
197
- };
198
-
199
184
  const RowContainer = forwardRef<
200
185
  HTMLDivElement,
201
186
  React.HTMLAttributes<HTMLDivElement> & {
@@ -231,6 +216,7 @@ const RowContainer = forwardRef<
231
216
  $gap,
232
217
  $backgroundColor,
233
218
  style,
219
+ "aria-describedby": _, // Causes hydration warning
234
220
  ...props
235
221
  },
236
222
  ref
@@ -264,19 +250,19 @@ const RowContainer = forwardRef<
264
250
  marginBottom: `${margin.bottom}px`,
265
251
  }),
266
252
  }),
267
- color: theme.colors.textMuted,
253
+ color: cssVars.colors.textMuted,
268
254
  ...($isSectionHeader && {
269
- backgroundColor: theme.colors.listView.raisedBackground,
255
+ backgroundColor: cssVars.colors.listviewRaisedBackground,
270
256
  ...($sectionHeaderVariant === "label" && {
271
- color: theme.colors.textDisabled,
257
+ color: cssVars.colors.textDisabled,
272
258
  }),
273
259
  }),
274
260
  ...($disabled && {
275
- color: theme.colors.textDisabled,
261
+ color: cssVars.colors.textDisabled,
276
262
  }),
277
263
  ...($selected && {
278
264
  color: "white",
279
- backgroundColor: theme.colors[$colorScheme],
265
+ backgroundColor: cssVars.colors[$colorScheme],
280
266
  }),
281
267
  display: "flex",
282
268
  alignItems: "center",
@@ -296,19 +282,19 @@ const RowContainer = forwardRef<
296
282
  }),
297
283
  position: "relative",
298
284
  ...($hovered && {
299
- boxShadow: `0 0 0 1px ${theme.colors[$colorScheme]} inset`,
285
+ boxShadow: `0 0 0 1px ${cssVars.colors[$colorScheme]} inset`,
300
286
  }),
301
287
  ...($showsActiveState && {
302
288
  "&:active": {
303
289
  backgroundColor: $selected
304
290
  ? $colorScheme === "secondary"
305
- ? theme.colors.secondaryLight
306
- : theme.colors.primaryLight
307
- : theme.colors.activeBackground,
291
+ ? cssVars.colors.secondaryLight
292
+ : cssVars.colors.primaryLight
293
+ : cssVars.colors.activeBackground,
308
294
  },
309
295
  }),
310
296
  ...($divider && {
311
- borderBottom: `1px solid ${theme.colors.dividerSubtle}`,
297
+ borderBottom: `1px solid ${cssVars.colors.dividerSubtle}`,
312
298
  }),
313
299
  ...($backgroundColor && {
314
300
  backgroundColor: $backgroundColor,
@@ -360,10 +346,10 @@ const ListViewDragIndicatorElement = forwardRef<
360
346
  ...($relativeDropPosition === "inside"
361
347
  ? {
362
348
  inset: 2,
363
- boxShadow: `0 0 0 1px var(--sidebar-background), 0 0 0 3px ${
349
+ boxShadow: `0 0 0 1px ${cssVars.colors.sidebarBackground}, 0 0 0 3px ${
364
350
  $colorScheme === "secondary"
365
- ? "var(--secondary)"
366
- : "var(--drag-outline)"
351
+ ? cssVars.colors.secondary
352
+ : cssVars.colors.dragOutline
367
353
  }`,
368
354
  }
369
355
  : {
@@ -380,15 +366,16 @@ const ListViewDragIndicatorElement = forwardRef<
380
366
  height: 6,
381
367
  backgroundColor:
382
368
  $colorScheme === "secondary"
383
- ? "var(--secondary)"
384
- : "var(--primary)",
369
+ ? cssVars.colors.secondary
370
+ : cssVars.colors.primary,
385
371
  border: `2px solid white`,
386
372
  boxShadow: "0 0 2px rgba(0,0,0,0.5)",
387
373
  }),
388
374
  ...style,
389
375
  }}
390
376
  />
391
- );}
377
+ );
378
+ }
392
379
  );
393
380
 
394
381
  interface ListViewClickInfo {
@@ -29,7 +29,7 @@ const ToggleGroupItem = forwardRef(function ToggleGroupItem(
29
29
  ref={forwardedRef}
30
30
  value={value}
31
31
  disabled={disabled}
32
- className={`font-sans text-heading5 font-normal relative flex-1 appearance-none border-none bg-none text-radio-group-item p-0 m-0 rounded inline-flex items-center justify-center align-middle focus:outline-none focus:shadow-[0_0_0_1px_var(--sidebar-background),0_0_0_3px_${colorScheme === "secondary" ? "var(--secondary)" : "var(--primary)"}] aria-checked:bg-${colorScheme ? colorScheme : "radio-group-background"} aria-checked:text-${colorScheme ? "radio-group-background" : "text"} aria-checked:shadow-${colorScheme ? "none" : "0_1px_1px_rgba(0,0,0,0.1)"}`}
32
+ className={`font-sans text-heading5 font-normal relative flex-1 appearance-none border-none bg-none text-radio-group-item p-0 m-0 rounded inline-flex items-center justify-center align-middle focus:outline-none focus:shadow-[0_0_0_1px_var(--sidebar-background),0_0_0_3px_${colorScheme === "secondary" ? "var(--secondary)" : "var(--primary)"}] aria-checked:bg-${colorScheme ? colorScheme : "radio-group-background"} aria-checked:text-${colorScheme ? "radio-group-background" : "text"} aria-checked:shadow-${colorScheme ? "none" : "0_1px_1px_rgba(0,0,0,0.1)"} focus:z-interactable`}
33
33
  >
34
34
  {children}
35
35
  </ToggleGroupPrimitive.Item>
@@ -29,7 +29,7 @@ const readOnlyStyle: CSSProperties = {
29
29
  textAlign: "left",
30
30
  };
31
31
 
32
- const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] pointer-events-none flex items-center mr-1.5`;
32
+ const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] pointer-events-none flex items-center mr-1.5 z-label`;
33
33
 
34
34
  const scrollButtonStyles = `
35
35
  flex
@@ -66,7 +66,7 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
66
66
  id={id}
67
67
  style={style}
68
68
  contentStyle={readOnlyStyle}
69
- className={`${className ?? ""} flex-1`}
69
+ className={`${className ?? ""} flex-1 focus:z-interactable`}
70
70
  disabled={disabled}
71
71
  >
72
72
  {icon && (
@@ -87,7 +87,7 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
87
87
  <Button
88
88
  id={id}
89
89
  style={style}
90
- className={`${className ?? "w-full"} flex-1`}
90
+ className={`${className ?? "w-full"} flex-1 focus:z-interactable`}
91
91
  disabled={disabled}
92
92
  >
93
93
  {icon && (
@@ -27,7 +27,7 @@ export const Switch = function Switch({
27
27
  onCheckedChange={(newValue) => {
28
28
  onChange(newValue);
29
29
  }}
30
- className={`all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary ${colorScheme === "secondary" && "data-[state=checked]:bg-secondary"}`}
30
+ className={`all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary ${colorScheme === "secondary" && "data-[state=checked]:bg-secondary"} focus:z-interactable`}
31
31
  >
32
32
  <SwitchPrimitive.Thumb className="block w-[15px] h-[15px] bg-white rounded-full transition-transform translate-x-[2px] data-[state=checked]:translate-x-[15px]" />
33
33
  </SwitchPrimitive.Root>
@@ -1,7 +1,20 @@
1
1
  import React, { ForwardedRef, forwardRef, ReactHTML, ReactNode } from "react";
2
- import { camelCaseToKebabCase, ThemeColor } from "../utils/tailwind";
3
-
4
- type Variant = "title" | "subtitle" | "heading1" | "heading2" | "heading3" | "heading4" | "heading5" | "body" | "small" | "button" | "code" | "label";
2
+ import { ThemeColor } from "../theme";
3
+ import { camelToKebabCase, KebabToCamelCase } from "../theme/themeUtils";
4
+
5
+ type Variant =
6
+ | "title"
7
+ | "subtitle"
8
+ | "heading1"
9
+ | "heading2"
10
+ | "heading3"
11
+ | "heading4"
12
+ | "heading5"
13
+ | "body"
14
+ | "small"
15
+ | "button"
16
+ | "code"
17
+ | "label";
5
18
 
6
19
  const elements: Record<Variant, keyof ReactHTML> = {
7
20
  title: "h1",
@@ -33,19 +46,19 @@ export const textStyles: Record<Variant, string> = {
33
46
  label: "font-sans text-label font-normal uppercase",
34
47
  } as const;
35
48
 
36
- export type TextProps = {
49
+ export type TextProps = {
37
50
  as?: keyof ReactHTML;
38
51
  href?: string;
39
52
  className?: string;
40
53
  style?: React.CSSProperties;
41
54
  variant: Variant;
42
- color?: ThemeColor;
55
+ color?: KebabToCamelCase<ThemeColor>;
43
56
  children: ReactNode;
44
57
  onClick?: () => void;
45
58
  onDoubleClick?: () => void;
46
59
  onKeyDown?: (event: React.KeyboardEvent<HTMLSpanElement>) => void;
47
60
  tabIndex?: number;
48
- }
61
+ };
49
62
 
50
63
  export const Text = forwardRef(function Text(
51
64
  {
@@ -69,7 +82,7 @@ export const Text = forwardRef(function Text(
69
82
  <Component
70
83
  // @ts-expect-error expected to be many semantic html elements
71
84
  ref={forwardedRef}
72
- className={`${textStyles[variant]} text-${camelCaseToKebabCase(String(color)) ?? "text"} ${className ?? ""}`}
85
+ className={`${textStyles[variant]} text-${camelToKebabCase(String(color)) ?? "text"} ${className ?? ""}`}
73
86
  style={style}
74
87
  tabIndex={tabIndex}
75
88
  onClick={onClick}
@@ -45,7 +45,7 @@ export const AutoResizingTextArea = memo(
45
45
 
46
46
  return (
47
47
  <textarea
48
- className={`font-sans text-heading5 font-normal text-text bg-input-background w-0 flex-1 py-1 px-1.5 border-none outline-none h-[100px] rounded-4 resize-none placeholder:text-text-disabled focus:ring-2 focus:ring-primary focus:z-10 read-only:text-text-disabled ${className ?? ""}`}
48
+ className={`font-sans text-heading5 font-normal text-text bg-input-background w-0 flex-1 py-1 px-1.5 border-none outline-none h-[100px] rounded-4 resize-none placeholder:text-text-disabled focus:ring-2 focus:ring-primary read-only:text-text-disabled focus:z-interactable ${className ?? ""}`}
49
49
  ref={handleRef}
50
50
  {...rest}
51
51
  onChange={handleChange}
@@ -22,7 +22,7 @@ export const Tooltip = memo(function Tooltip({ children, content }: Props) {
22
22
  align="center"
23
23
  sideOffset={2}
24
24
  collisionPadding={8}
25
- className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-[0_2px_4px_var(--shadow-color)_0_0_12px_var(--shadow-color)] border border-solid border-divider-strong`}
25
+ className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2)_0_0_12px_rgba(0,0,0,0.1)] border border-solid border-divider-strong`}
26
26
  >
27
27
  {content}
28
28
  {/* <Arrow /> */}
@@ -22,6 +22,7 @@ import {
22
22
  usePreservePanelSize,
23
23
  } from "../hooks/usePreservePanelSize";
24
24
  import { useWindowSize } from "../hooks/useWindowSize";
25
+ import { cssVars } from "../theme";
25
26
 
26
27
  interface Props {
27
28
  autoSavePrefix?: string;
@@ -184,7 +185,7 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
184
185
  id={id}
185
186
  className={className}
186
187
  style={{
187
- backgroundColor: "var(--canvas-background)",
188
+ backgroundColor: cssVars.colors.canvasBackground,
188
189
  display: "flex",
189
190
  flexDirection: "row",
190
191
  ...style,
@@ -226,7 +227,7 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
226
227
  cursor: "col-resize",
227
228
  width: "1px",
228
229
  height: "100%",
229
- backgroundColor: "var(--divider)",
230
+ backgroundColor: cssVars.colors.divider,
230
231
  }}
231
232
  />
232
233
  </>
@@ -251,7 +252,7 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
251
252
  cursor: "col-resize",
252
253
  width: "1px",
253
254
  height: "100%",
254
- backgroundColor: "var(--divider)",
255
+ backgroundColor: cssVars.colors.divider,
255
256
  }}
256
257
  />
257
258
  <Panel
@@ -53,6 +53,7 @@ const ReadOnlyTextInput = forwardRef(function ReadOnlyTextInput(
53
53
  value={value}
54
54
  onKeyDown={onKeyDown}
55
55
  readOnly
56
+ className="focus:z-interactable"
56
57
  />
57
58
  );
58
59
  });
package/src/index.css CHANGED
@@ -65,7 +65,17 @@
65
65
  --radio-group-item: rgb(139, 139, 139);
66
66
  --dot: rgba(0,0,0,0.25);
67
67
  --row-highlight: #3390FF10;
68
- --table-row-background: var(--background)
68
+ --table-row-background: var(--background);
69
+ --interactable-z-index: 2;
70
+ --label-z-index: 3;
71
+ --chip-primary-bg: rgba(238,229,255,0.2);
72
+ --chip-secondary-bg: rgba(205,238,231,0.2);
73
+ --chip-error-bg: rgba(255,219,219,0.2);
74
+ --chip-default-bg: rgba(0,0,0,0.1);
75
+ --chip-primary-shadow: rgb(238,229,255);
76
+ --chip-secondary-shadow: rgb(205,238,231);
77
+ --chip-error-shadow: rgb(255,219,219);
78
+ --chip-default-shadow: rgb(0,0,0);
69
79
  }
70
80
 
71
81
  [data-theme="dark"] {
@@ -105,4 +115,4 @@
105
115
  --thumbnail-shadow: #1f1d3366;
106
116
  --dot: rgba(255,255,255,0.15);
107
117
  --table-row-background: var(--sidebar-background);
108
- }
118
+ }
package/src/index.tsx CHANGED
@@ -71,7 +71,6 @@ export * from "./hooks/usePlatform";
71
71
  export * from "./mediaQuery";
72
72
  export * from "./theme";
73
73
  export { cn } from './utils/tailwind';
74
- export type { ThemeColor } from './utils/tailwind';
75
74
  export * from "./utils/createSectionedMenu";
76
75
  export * from "./utils/getGradientBackground";
77
76
  export * from "./hooks/usePreservePanelSize";
@@ -1,5 +1,8 @@
1
1
  import tailwindConfig from "../../tailwind.config";
2
+ import { convertKebabToCamelCase } from "./themeUtils";
2
3
 
3
- // Extract the theme configuration
4
- // @ts-expect-error TODO: figure this out
5
- export type Theme = typeof tailwindConfig.theme.extend;
4
+ export type Theme = (typeof tailwindConfig.theme)["extend"];
5
+
6
+ export type ThemeColor = keyof Theme["colors"];
7
+
8
+ export const cssVars = convertKebabToCamelCase(tailwindConfig.theme.extend);
@@ -0,0 +1,72 @@
1
+ export type KebabToCamelCase<S extends string> =
2
+ S extends `${infer First}-${infer Rest}`
3
+ ? `${First}${Capitalize<KebabToCamelCase<Rest>>}`
4
+ : S;
5
+
6
+ type IsPlainObject<T> = T extends Array<any> | Set<any> | Map<any, any>
7
+ ? false
8
+ : T extends object
9
+ ? true
10
+ : false;
11
+
12
+ type KebabToCamelCaseKeys<T> = {
13
+ [K in keyof T as K extends string
14
+ ? KebabToCamelCase<K>
15
+ : K]: T[K] extends object
16
+ ? IsPlainObject<T[K]> extends true
17
+ ? KebabToCamelCaseKeys<T[K]>
18
+ : T[K]
19
+ : T[K];
20
+ };
21
+
22
+ type _CamelToKebabCase<S extends string> = S extends `${infer C}${infer Rest}`
23
+ ? C extends Uppercase<C>
24
+ ? `-${Lowercase<C>}${_CamelToKebabCase<Rest>}`
25
+ : `${C}${_CamelToKebabCase<Rest>}`
26
+ : S;
27
+
28
+ // Remove leading hyphen if exists
29
+ type TrimHyphen<S extends string> = S extends `-${infer Rest}` ? Rest : S;
30
+
31
+ export type CamelToKebabCase<S extends string> = TrimHyphen<
32
+ _CamelToKebabCase<S>
33
+ >;
34
+
35
+ export function camelToKebabCase<S extends string>(
36
+ str: S
37
+ ): CamelToKebabCase<S> {
38
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase() as CamelToKebabCase<S>;
39
+ }
40
+
41
+ export function kebabToCamelCase<S extends string>(
42
+ str: S
43
+ ): KebabToCamelCase<S> {
44
+ return str.replace(/-([a-z])/g, (_, letter) =>
45
+ letter.toUpperCase()
46
+ ) as KebabToCamelCase<S>;
47
+ }
48
+
49
+ // Runtime function to convert all keys in an object from kebab-case to camelCase
50
+ export function convertKebabToCamelCase<T extends object>(
51
+ obj: T
52
+ ): KebabToCamelCaseKeys<T> {
53
+ if (Array.isArray(obj) || obj instanceof Set || obj instanceof Map) {
54
+ return obj as KebabToCamelCaseKeys<T>;
55
+ }
56
+
57
+ const result: any = {};
58
+
59
+ for (const [key, value] of Object.entries(obj)) {
60
+ const camelKey = kebabToCamelCase(key);
61
+ result[camelKey] =
62
+ value &&
63
+ typeof value === "object" &&
64
+ !Array.isArray(value) &&
65
+ !(value instanceof Set) &&
66
+ !(value instanceof Map)
67
+ ? convertKebabToCamelCase(value)
68
+ : value;
69
+ }
70
+
71
+ return result as KebabToCamelCaseKeys<T>;
72
+ }
@@ -1,17 +1,9 @@
1
- import { Theme } from '../theme';
2
1
  import { twMerge } from "tailwind-merge";
3
2
 
4
- /**
3
+ /**
5
4
  * Utility for merging classes conditionally. Right side will override left side if there are any conflicts.
6
5
  * Usage: cn('bg-red-500 text-white', 'bg-blue-500') // Returns: 'text-white bg-blue-500'
7
6
  * @danger do not use this function to concatenate class names. Use it to merge classes conditionally.
8
- */
9
- export const cn = (...classes: (string | boolean | undefined | null)[]) => twMerge(classes.filter(Boolean) as string[])
10
-
11
- // @ts-expect-error TODO: figure this out
12
- type ThemeExtend = NonNullable<Theme['extend']>
13
- export type ThemeColor = keyof ThemeExtend['colors'];
14
-
15
- export function camelCaseToKebabCase(string: string) {
16
- return string.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
17
- }
7
+ */
8
+ export const cn = (...classes: (string | boolean | undefined | null)[]) =>
9
+ twMerge(classes.filter(Boolean) as string[]);
@@ -1,6 +1,6 @@
1
- import { Config } from "tailwindcss";
1
+ import type { Config } from "tailwindcss";
2
2
 
3
- const config: Config = {
3
+ const config = {
4
4
  content: ["./src/**/*.{ts,tsx}"],
5
5
  corePlugins: {
6
6
  // disables @tailwind base global styles
@@ -66,6 +66,14 @@ const config: Config = {
66
66
  dot: "var(--dot)",
67
67
  "row-highlight": "var(--row-highlight)",
68
68
  "table-row-background": "var(--table-row-background)",
69
+ "chip-primary-bg": "var(--chip-primary-bg)",
70
+ "chip-secondary-bg": "var(--chip-secondary-bg)",
71
+ "chip-error-bg": "var(--chip-error-bg)",
72
+ "chip-default-bg": "var(--chip-default-bg)",
73
+ "chip-primary-shadow": "var(--chip-primary-shadow)",
74
+ "chip-secondary-shadow": "var(--chip-secondary-shadow)",
75
+ "chip-error-shadow": "var(--chip-error-shadow)",
76
+ "chip-default-shadow": "var(--chip-default-shadow)",
69
77
  },
70
78
  fontFamily: {
71
79
  sans: [
@@ -200,6 +208,10 @@ const config: Config = {
200
208
  spin: "spin 1s linear infinite",
201
209
  shimmer: "shimmer 6s infinite linear",
202
210
  },
211
+ zIndex: {
212
+ interactable: "var(--interactable-z-index)",
213
+ label: "var(--label-z-index)",
214
+ }
203
215
  },
204
216
  },
205
217
  safelist: [
@@ -218,7 +230,8 @@ const config: Config = {
218
230
  "-mr-2",
219
231
  "border-gray-200",
220
232
  "gap-1.5",
233
+ "gap-toolbar-separator",
221
234
  ],
222
- };
235
+ } satisfies Config;
223
236
 
224
237
  export default config;