@noya-app/noya-designsystem 0.1.50 → 0.1.52

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 (56) hide show
  1. package/.turbo/turbo-build.log +10 -13
  2. package/CHANGELOG.md +19 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +314 -203
  5. package/dist/index.d.ts +314 -203
  6. package/dist/index.js +6473 -12656
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +8399 -14605
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +15 -12
  11. package/postcss.config.js +7 -0
  12. package/src/components/ActionMenu.tsx +12 -5
  13. package/src/components/Banner.tsx +2 -2
  14. package/src/components/BaseToolbar.tsx +6 -2
  15. package/src/components/Breadcrumbs.tsx +32 -2
  16. package/src/components/Button.tsx +30 -19
  17. package/src/components/Chip.tsx +3 -0
  18. package/src/components/Collection.tsx +12 -3
  19. package/src/components/ColorSwatch.tsx +42 -0
  20. package/src/components/ColorSwatchControl.tsx +92 -0
  21. package/src/components/ComboboxMenu.tsx +10 -1
  22. package/src/components/Dialog.tsx +1 -1
  23. package/src/components/FillInputField.tsx +1 -1
  24. package/src/components/Grid.tsx +22 -12
  25. package/src/components/GridView.tsx +1 -1
  26. package/src/components/LabeledField.tsx +4 -1
  27. package/src/components/List.tsx +37 -26
  28. package/src/components/ListView.tsx +7 -4
  29. package/src/components/MediaThumbnail.tsx +63 -15
  30. package/src/components/Popover.tsx +1 -1
  31. package/src/components/SearchCompletionMenu.tsx +12 -12
  32. package/src/components/SegmentedControl.tsx +2 -2
  33. package/src/components/SelectionToolbar.tsx +70 -0
  34. package/src/components/Switch.tsx +1 -1
  35. package/src/components/Text.tsx +2 -2
  36. package/src/components/Toast.tsx +13 -7
  37. package/src/components/Toolbar.tsx +33 -11
  38. package/src/components/Tooltip.tsx +4 -2
  39. package/src/components/catppuccin/fileIcons.ts +2430 -0
  40. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +3 -1
  41. package/src/components/file-explorer/FileExplorerLayout.tsx +12 -2
  42. package/src/components/internal/Menu.tsx +5 -4
  43. package/src/components/internal/MenuViewport.tsx +2 -0
  44. package/src/components/internal/TextInput.tsx +7 -2
  45. package/src/components/pipeline/PipelineResultLayout.tsx +18 -0
  46. package/src/contexts/DialogContext.tsx +20 -5
  47. package/src/index.css +103 -34
  48. package/src/index.tsx +3 -0
  49. package/src/theme/index.ts +7 -2
  50. package/src/theme/themeUtils.ts +10 -3
  51. package/src/utils/classNames.ts +4 -3
  52. package/src/utils/combobox.ts +18 -13
  53. package/src/utils/moveTreeItem.ts +4 -4
  54. package/tailwind.config.ts +2 -239
  55. package/tailwind.d.ts +1 -1
  56. package/tsup.config.ts +1 -4
@@ -38,7 +38,9 @@ export const ConnectedUsersMenuLayout = ({
38
38
  }: ConnectedUsersMenuLayoutProps) => {
39
39
  return (
40
40
  <div className="flex gap-1.5">
41
- <AvatarStack size={INPUT_HEIGHT}>{renderUsers()}</AvatarStack>
41
+ <AvatarStack size={INPUT_HEIGHT} className="mr-1">
42
+ {renderUsers()}
43
+ </AvatarStack>
42
44
 
43
45
  {/* AI Assistant Avatar */}
44
46
  {launchAIAssistant && (
@@ -8,7 +8,12 @@ import {
8
8
  import { forwardRefGeneric } from "@noya-app/react-utils";
9
9
  import React from "react";
10
10
  import { cx } from "../../utils/classNames";
11
- import { Collection, CollectionProps, CollectionRef } from "../Collection";
11
+ import {
12
+ Collection,
13
+ CollectionItemSize,
14
+ CollectionProps,
15
+ CollectionRef,
16
+ } from "../Collection";
12
17
 
13
18
  export const FileExplorerLayout = ({
14
19
  children,
@@ -53,13 +58,18 @@ export const FileExplorerCollection = forwardRefGeneric(
53
58
 
54
59
  export const FileExplorerDetail = ({
55
60
  selected,
61
+ size,
56
62
  children,
57
63
  }: {
58
64
  selected: boolean;
59
65
  children: React.ReactNode;
66
+ size?: CollectionItemSize;
60
67
  }) => (
61
68
  <span
62
- className={cx("text-sm", selected ? "text-primary" : "text-text-muted")}
69
+ className={cx(
70
+ size === "small" ? "text-list-small" : "text-sm",
71
+ selected ? "text-primary" : "text-text-muted"
72
+ )}
63
73
  >
64
74
  {children}
65
75
  </span>
@@ -92,6 +92,7 @@ export type SelectableMenuItem<T extends string> = BaseMenuItem & {
92
92
  shortcut?: string;
93
93
  value: T;
94
94
  testSelected?: boolean;
95
+ tooltip?: ReactNode;
95
96
  };
96
97
 
97
98
  type SubMenuItem<T extends string> = BaseMenuItem & {
@@ -164,12 +165,12 @@ export const CHECKBOX_INDENT_WIDTH = 6;
164
165
  export const styles = {
165
166
  separatorStyle: "h-px bg-divider mx-3 my-1",
166
167
  selectedItemStyle:
167
- "focus:outline-none focus:text-white focus:bg-primary focus:kbd:text-white",
168
- testSelectedItemStyle: "outline-none text-white bg-primary kbd:text-white",
168
+ "focus:outline-none focus:text-selected-list-item-text focus:bg-selected-list-item-background focus:kbd:text-selected-list-item-text",
169
+ testSelectedItemStyle:
170
+ "outline-none text-selected-list-item-text bg-selected-list-item-background kbd:text-selected-list-item-text",
169
171
  itemStyle: ({ disabled }: { disabled?: boolean }) => `
170
172
  flex-none select-none cursor-pointer rounded
171
173
  py-1.5 px-2
172
- active:bg-primary-light
173
174
  transition-colors
174
175
  flex items-center
175
176
  font-sans text-button font-medium
@@ -182,7 +183,7 @@ export const styles = {
182
183
  },
183
184
  },
184
185
  contentStyle:
185
- "rounded bg-popover-background text-text shadow-[0_2px_4px_rgba(0,0,0,0.2),_0_0_12px_rgba(0,0,0,0.1)] z-menu py-1",
186
+ "rounded bg-popover-background text-text shadow-popover z-menu py-1",
186
187
  };
187
188
 
188
189
  function getKeyboardShortcuts<T extends string>(
@@ -113,6 +113,7 @@ export const MenuViewport = <T extends string>({
113
113
  onSelect={onSelect}
114
114
  Components={Components}
115
115
  indented={hasCheckedItem}
116
+ disabled={item.disabled}
116
117
  >
117
118
  <div className="flex items-center flex-1">
118
119
  {item.title}
@@ -147,6 +148,7 @@ export const MenuViewport = <T extends string>({
147
148
  Components={Components}
148
149
  indented={hasCheckedItem}
149
150
  testSelected={item.testSelected}
151
+ disabled={item.disabled}
150
152
  >
151
153
  {item.title ?? item.value}
152
154
  </SelectItem>
@@ -126,6 +126,8 @@ type SubmittableProps = Props & {
126
126
  onSubmit: (value: string) => void;
127
127
  allowSubmittingWithSameValue?: boolean;
128
128
  submitAutomaticallyAfterDelay?: number;
129
+ /** @default true */
130
+ submitOnBlur?: boolean;
129
131
  };
130
132
 
131
133
  const SubmittableTextInput = forwardRef(function SubmittableTextInput(
@@ -138,6 +140,7 @@ const SubmittableTextInput = forwardRef(function SubmittableTextInput(
138
140
  onFocusCapture,
139
141
  allowSubmittingWithSameValue = false,
140
142
  submitAutomaticallyAfterDelay,
143
+ submitOnBlur = true,
141
144
  ...rest
142
145
  }: SubmittableProps,
143
146
  forwardedRef: ForwardedRef<HTMLInputElement>
@@ -218,9 +221,11 @@ const SubmittableTextInput = forwardRef(function SubmittableTextInput(
218
221
 
219
222
  onFocusChange?.(false);
220
223
 
221
- handleSubmit();
224
+ if (submitOnBlur) {
225
+ handleSubmit();
226
+ }
222
227
  },
223
- [onBlur, onFocusChange, handleSubmit]
228
+ [onBlur, onFocusChange, handleSubmit, submitOnBlur]
224
229
  );
225
230
 
226
231
  const handleFocusCapture = useCallback(
@@ -30,3 +30,21 @@ export const PipelineResultLayout = ({
30
30
  }) => {
31
31
  return <div className="flex flex-col gap-2">{children}</div>;
32
32
  };
33
+
34
+ export function getPipelineResultLink(
35
+ value: unknown
36
+ ): { url: string } | undefined {
37
+ if (typeof value !== "object" || value === null) return undefined;
38
+
39
+ // Check for htmlUrl first
40
+ if ("html_url" in value && typeof value.html_url === "string") {
41
+ return { url: value.html_url };
42
+ }
43
+
44
+ // Fall back to regular url
45
+ if ("url" in value && typeof value.url === "string") {
46
+ return { url: value.url };
47
+ }
48
+
49
+ return undefined;
50
+ }
@@ -43,7 +43,12 @@ export type DialogContextValue = {
43
43
  ): Promise<string | undefined>;
44
44
 
45
45
  openConfirmationDialog(
46
- options: string | BaseDialogContents
46
+ options:
47
+ | string
48
+ | (BaseDialogContents & {
49
+ confirmButtonLabel?: string;
50
+ cancelButtonLabel?: string;
51
+ })
47
52
  ): Promise<boolean>;
48
53
 
49
54
  containsElement(element: HTMLElement): boolean;
@@ -67,6 +72,8 @@ type InputDialogContents = BaseDialogContents & {
67
72
 
68
73
  type ConfirmationDialogContents = BaseDialogContents & {
69
74
  type: "confirmation";
75
+ confirmButtonLabel?: string;
76
+ cancelButtonLabel?: string;
70
77
  resolve: (value: boolean) => void;
71
78
  };
72
79
 
@@ -139,13 +146,21 @@ export const DialogProvider = function DialogProvider({
139
146
  useCallback((options) => {
140
147
  const { promise, resolve } = createDeferredPromise<boolean>();
141
148
 
142
- const { title, description, style, className } =
143
- typeof options === "string" ? { title: options } : options;
149
+ const {
150
+ title,
151
+ description,
152
+ confirmButtonLabel,
153
+ cancelButtonLabel,
154
+ style,
155
+ className,
156
+ } = typeof options === "string" ? { title: options } : options;
144
157
 
145
158
  setContents({
146
159
  type: "confirmation",
147
160
  title,
148
161
  description,
162
+ confirmButtonLabel,
163
+ cancelButtonLabel,
149
164
  style,
150
165
  className,
151
166
  resolve: (value: boolean) => {
@@ -262,7 +277,7 @@ export const DialogProvider = function DialogProvider({
262
277
  setContents(undefined);
263
278
  }}
264
279
  >
265
- Cancel
280
+ {contents.cancelButtonLabel ?? "Cancel"}
266
281
  </Button>
267
282
  <Spacer.Horizontal size={16} />
268
283
  <Button
@@ -271,7 +286,7 @@ export const DialogProvider = function DialogProvider({
271
286
  setContents(undefined);
272
287
  }}
273
288
  >
274
- OK
289
+ {contents.confirmButtonLabel ?? "OK"}
275
290
  </Button>
276
291
  </div>
277
292
  ) : contents?.type === "custom" ? (
package/src/index.css CHANGED
@@ -1,42 +1,61 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
1
  :root {
6
- --n-logo-fill: rgb(150, 152, 172);
7
- --n-logo-highlight: rgb(150, 152, 172);
8
- --n-background: rgb(255, 255, 255);
9
- --n-text: rgb(38, 48, 83);
10
- --n-text-muted: rgb(107, 113, 136);
11
- --n-text-subtle: rgb(117, 121, 129);
12
- --n-text-disabled: rgb(150, 152, 172);
13
- --n-text-decorative-light: rgb(168, 185, 212);
14
- --n-divider-subtle: rgba(30, 50, 100, 0.04);
15
- --n-divider: rgba(30, 50, 100, 0.07);
16
- --n-divider-strong: rgba(30, 50, 100, 0.09);
17
- --n-primary: rgb(103, 70, 255);
18
- --n-primary-light: rgb(147, 86, 255);
19
- --n-primary-pastel: rgba(234, 230, 255);
2
+ --n-primary-900: rgb(45, 25, 95); /* dark: --n-primary-pastel */
3
+ --n-primary-600: rgb(103, 70, 255); /* --n-primary */
4
+ --n-primary-500: rgb(119, 66, 255); /* dark: --n-primary */
5
+ --n-primary-400: rgb(147, 86, 255); /* --n-primary-light */
6
+ --n-primary-300: rgb(134, 86, 255); /* dark: --n-primary-light */
7
+ --n-primary-250: rgb(171, 137, 251);
8
+ --n-primary-200: rgb(188, 160, 255);
9
+ --n-primary-150: rgb(225, 219, 255);
10
+ --n-primary-100: rgb(234, 230, 255); /* --n-primary-pastel */
11
+
12
+ --n-indigo-950: rgb(18, 26, 37);
13
+ --n-indigo-900: rgb(30, 40, 60);
14
+ --n-indigo-800: rgb(38, 48, 83);
15
+ --n-indigo-700: rgb(55, 65, 99);
16
+ --n-indigo-600: rgb(76, 86, 122);
17
+ --n-indigo-500: rgb(107, 113, 136);
18
+ --n-indigo-400: rgb(117, 121, 129);
19
+ --n-indigo-300: rgb(129, 131, 165);
20
+ --n-indigo-200: rgb(150, 152, 172);
21
+ --n-indigo-150: rgb(226, 230, 238);
22
+ --n-indigo-100: rgb(240, 242, 246);
23
+ --n-indigo-50: rgb(243, 245, 249);
24
+ --n-indigo-25: rgb(251, 252, 254);
25
+
26
+ --n-logo-fill: var(--n-indigo-200);
27
+ --n-logo-highlight: var(--n-indigo-200);
28
+ --n-background: white;
29
+ --n-text: var(--n-indigo-800);
30
+ --n-text-muted: var(--n-indigo-500);
31
+ --n-text-subtle: var(--n-indigo-400);
32
+ --n-text-disabled: var(--n-indigo-200);
33
+ --n-text-decorative-light: var(--n-indigo-100);
34
+ --n-divider-subtle: color-mix(in srgb, var(--n-indigo-900) 4%, transparent);
35
+ --n-divider: color-mix(in srgb, var(--n-indigo-900) 7%, transparent);
36
+ --n-divider-strong: color-mix(in srgb, var(--n-indigo-900) 9%, transparent);
37
+ --n-primary: var(--n-primary-600);
38
+ --n-primary-light: var(--n-primary-400);
39
+ --n-primary-pastel: var(--n-primary-100);
20
40
  --n-secondary: rgb(0, 151, 117);
21
41
  --n-secondary-light: rgb(0, 160, 129);
22
42
  --n-secondary-pastel: rgb(205, 238, 231);
23
43
  --n-secondary-bright: #0ab557;
24
44
  --n-input-background-light: rgb(243, 245, 249);
25
45
  --n-code-background: rgb(250, 250, 250);
26
- --n-code-background-dark: #435080;
46
+ --n-code-background-dark: var(--n-indigo-950);
27
47
  --n-selected-background: rgb(242, 245, 250);
28
- --n-breadcrumb-text: rgb(107, 113, 136);
29
- --n-breadcrumb-text-hover: rgb(117, 121, 129);
30
- --n-breadcrumb-icon: rgb(129, 131, 165);
31
- --n-canvas-background: rgb(249, 249, 249);
48
+ --n-breadcrumb-text: var(--n-indigo-500);
49
+ --n-breadcrumb-text-hover: var(--n-indigo-400);
50
+ --n-breadcrumb-icon: var(--n-indigo-300);
51
+ --n-canvas-background: var(--n-indigo-25);
32
52
  --n-canvas-grid: rgba(0, 0, 0, 0.05);
33
53
  --n-sidebar-background: rgb(255, 255, 255);
34
- --n-sidebar-background-transparent: rgba(255, 255, 255, 0.85);
35
54
  --n-popover-background: rgb(252, 252, 252);
36
55
  --n-popover-divider: transparent;
37
56
  --n-listview-raised-background: rgba(0, 0, 0, 0.03);
38
57
  --n-listview-editing-background: #fff;
39
- --n-input-background: rgb(240, 242, 246);
58
+ --n-input-background: var(--n-indigo-100);
40
59
  --n-list-view-hover-background: rgb(244, 246, 250);
41
60
  --n-list-view-thumbnail-background: var(--n-input-background);
42
61
  --n-slider-thumb-background: white;
@@ -45,14 +64,14 @@
45
64
  --n-transparent-checker: rgba(255, 255, 255, 0.8);
46
65
  --n-scrollbar: rgba(199, 199, 199, 0.8);
47
66
  --n-placeholder-dots: rgba(0, 0, 0, 0.3);
48
- --n-drag-outline: rgb(103, 70, 255);
49
- --n-active-background: rgba(0, 0, 0, 0.1);
67
+ --n-drag-outline: var(--n-primary-600);
68
+ --n-active-background: var(--n-indigo-150);
50
69
  --n-thumbnail-background: #f0efff;
51
70
  --n-thumbnail-shadow: #d3ceed66;
52
- --n-inline-code-text: rgb(103, 70, 255);
71
+ --n-inline-code-text: var(--n-primary-600);
53
72
  --n-inline-code-background: rgb(240, 242, 246);
54
- --n-text-link: rgb(103, 70, 255);
55
- --n-text-link-focused: rgb(147, 86, 255);
73
+ --n-text-link: var(--n-primary-600);
74
+ --n-text-link-focused: var(--n-primary-400);
56
75
  --n-inset-top: 46px;
57
76
  --n-sidebar-width: 260px;
58
77
  --n-toolbar-height: 46px;
@@ -61,7 +80,7 @@
61
80
  --n-inspector-v-separator: 10px;
62
81
  --n-dialog-padding: 16px;
63
82
  --n-input-height: 27px;
64
- --n-icon: rgb(129, 131, 165);
83
+ --n-icon: var(--n-indigo-300);
65
84
  --n-icon-selected: rgb(220, 220, 220);
66
85
  --n-warning: rgb(251, 211, 0);
67
86
  --n-segmented-control-item: rgb(139, 139, 139);
@@ -82,6 +101,26 @@
82
101
  --n-floating-button: rgb(248, 248, 250);
83
102
  --n-block-border: rgb(184, 201, 218);
84
103
  --n-block-highlight: rgb(255, 165, 0);
104
+ --n-markdown-editor-line-placeholder-content: "Write or press / for commands";
105
+ --n-selected-list-item-background: var(--n-primary-100);
106
+ --n-selected-list-item-text: var(--n-primary);
107
+ --n-selected-list-item-icon-background: var(--n-primary-150);
108
+ /* based on vs code light theme: https://github.com/fsegurai/codemirror-themes/blob/main/packages/vscode-light/src/index.ts */
109
+ --n-cm-keyword: var(--n-primary-600);
110
+ --n-cm-function: #1b4db8;
111
+ --n-cm-control-keyword: var(--n-primary-600);
112
+ --n-cm-class-type: #1b4db8;
113
+ --n-cm-regexp: #1097af;
114
+ --n-cm-link: #1097af;
115
+ --n-cm-string: #1097af;
116
+ --n-cm-number: #1097af;
117
+ --n-cm-comment: var(--n-indigo-300);
118
+ --n-cm-operator: var(--n-indigo-800);
119
+ --n-cm-variable: var(--n-indigo-800);
120
+ --n-cm-type: #f59e0b;
121
+ --n-cm-error: #e45649;
122
+ --n-cm-warning: #fb9d00;
123
+ --n-cm-info: #3390ff;
85
124
  }
86
125
 
87
126
  [data-theme="dark"] {
@@ -95,8 +134,11 @@
95
134
  --n-divider-subtle: rgba(255, 255, 255, 0.04);
96
135
  --n-divider: rgba(255, 255, 255, 0.08);
97
136
  --n-divider-strong: rgba(0, 0, 0, 1);
98
- --n-primary: rgb(119, 66, 255);
99
- --n-primary-light: rgb(134, 86, 255);
137
+ --n-primary: var(--n-primary-500);
138
+ --n-primary-light: var(--n-primary-300);
139
+ --n-selected-list-item-background: var(--n-primary-900);
140
+ --n-selected-list-item-text: var(--n-primary-200);
141
+ --n-selected-list-item-icon-background: var(--n-primary-900);
100
142
  --n-secondary-bright: #36fe91;
101
143
  --n-input-background: rgba(181, 178, 255, 0.08);
102
144
  --n-input-background-light: rgba(181, 178, 255, 0.1);
@@ -104,7 +146,6 @@
104
146
  --n-canvas-background: rgb(20, 19, 23);
105
147
  --n-canvas-grid: rgba(0, 0, 0, 0.1);
106
148
  --n-sidebar-background: rgb(34, 33, 39);
107
- --n-sidebar-background-transparent: rgba(34, 33, 39, 0.95);
108
149
  --n-popover-background: rgb(34, 33, 39);
109
150
  --n-popover-divider: rgba(255, 255, 255, 0.08);
110
151
  --n-listview-raised-background: rgba(181, 178, 255, 0.1);
@@ -124,4 +165,32 @@
124
165
  --n-dot: rgba(255, 255, 255, 0.15);
125
166
  --n-table-row-background: var(--n-sidebar-background);
126
167
  --n-floating-button: #333333;
168
+ /* based on vs code dark theme: https://github.com/fsegurai/codemirror-themes/blob/main/packages/vscode-dark/src/index.ts */
169
+ --n-cm-keyword: #569cd6;
170
+ --n-cm-function: #dcdcaa;
171
+ --n-cm-control-keyword: #c586c0;
172
+ --n-cm-class-type: #4ec9b0;
173
+ --n-cm-regexp: #d16969;
174
+ --n-cm-link: #4078f2;
175
+ --n-cm-string: #ce9178;
176
+ --n-cm-number: #b5cea8;
177
+ --n-cm-comment: #6a9955;
178
+ --n-cm-operator: #d4d4d4;
179
+ --n-cm-variable: #9cdcfe;
180
+ --n-cm-type: #f59e0b;
181
+ --n-cm-error: #ff0000;
182
+ --n-cm-warning: #fb9d00;
183
+ --n-cm-info: #3390ff;
184
+ }
185
+
186
+ .noya-styles {
187
+ @tailwind base;
188
+ @tailwind components;
189
+ @tailwind utilities;
190
+
191
+ .markdown-editor-line-placeholder::after {
192
+ content: var(--n-markdown-editor-line-placeholder-content);
193
+ color: var(--n-text-disabled);
194
+ pointer-events: none;
195
+ }
127
196
  }
package/src/index.tsx CHANGED
@@ -10,6 +10,8 @@ export * from "./components/Button";
10
10
  export * from "./components/Checkbox";
11
11
  export * from "./components/Chip";
12
12
  export * from "./components/Collection";
13
+ export * from "./components/ColorSwatch";
14
+ export * from "./components/ColorSwatchControl";
13
15
  export * from "./components/Combobox";
14
16
  export * from "./components/ComboboxMenu";
15
17
  export * from "./components/CommandPalette";
@@ -55,6 +57,7 @@ export * from "./components/ScrollArea";
55
57
  export * from "./components/SearchCompletionMenu";
56
58
  export * from "./components/Section";
57
59
  export * from "./components/SegmentedControl";
60
+ export * from "./components/SelectionToolbar";
58
61
  export * from "./components/SelectMenu";
59
62
  export * from "./components/Slider";
60
63
  export * from "./components/Sortable";
@@ -1,5 +1,5 @@
1
- import tailwindConfig from "../../tailwind.config";
2
- import { convertKebabToCamelCase } from "./themeUtils";
1
+ import tailwindConfig from "@noya-app/noya-tailwind-config";
2
+ import { convertKebabToCamelCase, parseCssVarName } from "./themeUtils";
3
3
 
4
4
  export type Theme = (typeof tailwindConfig.theme)["extend"];
5
5
 
@@ -7,4 +7,9 @@ export type ThemeColor = keyof Theme["colors"];
7
7
 
8
8
  export const cssVars = convertKebabToCamelCase(tailwindConfig.theme.extend);
9
9
 
10
+ export const cssVarNames = convertKebabToCamelCase(
11
+ tailwindConfig.theme.extend,
12
+ parseCssVarName
13
+ );
14
+
10
15
  export const INPUT_HEIGHT = 27;
@@ -48,7 +48,8 @@ export function kebabToCamelCase<S extends string>(
48
48
 
49
49
  // Runtime function to convert all keys in an object from kebab-case to camelCase
50
50
  export function convertKebabToCamelCase<T extends object>(
51
- obj: T
51
+ obj: T,
52
+ mapValue?: (value: string) => string
52
53
  ): KebabToCamelCaseKeys<T> {
53
54
  if (Array.isArray(obj) || obj instanceof Set || obj instanceof Map) {
54
55
  return obj as KebabToCamelCaseKeys<T>;
@@ -64,9 +65,15 @@ export function convertKebabToCamelCase<T extends object>(
64
65
  !Array.isArray(value) &&
65
66
  !(value instanceof Set) &&
66
67
  !(value instanceof Map)
67
- ? convertKebabToCamelCase(value)
68
- : value;
68
+ ? convertKebabToCamelCase(value, mapValue)
69
+ : mapValue && typeof value === "string"
70
+ ? mapValue(value)
71
+ : value;
69
72
  }
70
73
 
71
74
  return result as KebabToCamelCaseKeys<T>;
72
75
  }
76
+
77
+ export function parseCssVarName(key: string) {
78
+ return key.match(/var\((--.*?)\)/)?.[1] ?? "";
79
+ }
@@ -1,7 +1,7 @@
1
- import tailwindConfig from "../../tailwind.config";
1
+ import tailwindConfig from "@noya-app/noya-tailwind-config";
2
2
 
3
3
  type ClassNameItem = string | number | BigInt | boolean | null | undefined;
4
- type Category = "text" | "color" | "font" | "flex";
4
+ type Category = "text" | "color" | "font" | "flex" | "position";
5
5
  type Options = {
6
6
  categories?: Category[];
7
7
  };
@@ -32,12 +32,13 @@ const flexKeysSet = new Set([
32
32
  "flex-[0_0_auto]",
33
33
  "flex-1",
34
34
  ]);
35
-
35
+ const positionKeysSet = new Set(["absolute", "relative", "fixed", "sticky"]);
36
36
  const keyMap: Record<Category, Set<string>> = {
37
37
  text: textKeysSet,
38
38
  flex: flexKeysSet,
39
39
  color: colorKeysSet,
40
40
  font: fontKeysSet,
41
+ position: positionKeysSet,
41
42
  };
42
43
 
43
44
  export function cx(...args: ClassNameItem[]): string {
@@ -168,12 +168,12 @@ export class ComboboxState<T extends string> {
168
168
 
169
169
  moveSelection(direction: "up" | "down") {
170
170
  const filteredItems = this.getFilteredItems();
171
- this.selectedIndex = getNextIndex(
172
- filteredItems,
173
- this.selectedIndex,
174
- direction === "down" ? "next" : "previous",
175
- (item) => isNonSelectableMenuItem(item)
176
- );
171
+ this.selectedIndex = getNextIndex({
172
+ items: filteredItems,
173
+ currentIndex: this.selectedIndex,
174
+ direction: direction === "down" ? "next" : "previous",
175
+ isDisabled: (item) => isNonSelectableMenuItem(item),
176
+ });
177
177
  this.notifyChange();
178
178
  }
179
179
 
@@ -257,7 +257,7 @@ export class ComboboxState<T extends string> {
257
257
  }
258
258
  }
259
259
 
260
- function filterWithGroupedSections<T extends string>(
260
+ export function filterWithGroupedSections<T extends string>(
261
261
  items: MenuItem<T>[],
262
262
  query: string
263
263
  ): ScoredMenuItem<T>[] {
@@ -341,12 +341,17 @@ function filterWithGroupedSections<T extends string>(
341
341
  return result;
342
342
  }
343
343
 
344
- export function getNextIndex<T>(
345
- items: T[],
346
- currentIndex: number,
347
- direction: "next" | "previous",
348
- isDisabled: (item: T) => boolean
349
- ): number {
344
+ export function getNextIndex<T>({
345
+ items,
346
+ currentIndex,
347
+ direction,
348
+ isDisabled,
349
+ }: {
350
+ items: T[];
351
+ currentIndex: number;
352
+ direction: "next" | "previous";
353
+ isDisabled: (item: T) => boolean;
354
+ }): number {
350
355
  // Make sure the current index is within bounds
351
356
  currentIndex =
352
357
  currentIndex < 0
@@ -5,7 +5,7 @@ import {
5
5
  } from "../components/Sortable";
6
6
 
7
7
  type MoveOptions = {
8
- indexPaths: number[][];
8
+ paths: number[][];
9
9
  to: number[];
10
10
  };
11
11
 
@@ -27,19 +27,19 @@ export function moveTreeItem<T>({
27
27
  switch (position) {
28
28
  case "above": {
29
29
  return move(root, {
30
- indexPaths: [sourceIndexPath],
30
+ paths: [sourceIndexPath],
31
31
  to: targetIndexPath,
32
32
  });
33
33
  }
34
34
  case "below": {
35
35
  return move(root, {
36
- indexPaths: [sourceIndexPath],
36
+ paths: [sourceIndexPath],
37
37
  to: [...targetIndexPath.slice(0, -1), targetIndexPath.at(-1)! + 1],
38
38
  });
39
39
  }
40
40
  case "inside": {
41
41
  return move(root, {
42
- indexPaths: [sourceIndexPath],
42
+ paths: [sourceIndexPath],
43
43
  to: [...targetIndexPath, 1000],
44
44
  });
45
45
  }