@noya-app/noya-designsystem 0.1.64 → 0.1.65

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 (115) hide show
  1. package/.turbo/turbo-build.log +13 -10
  2. package/CHANGELOG.md +12 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +501 -228
  5. package/dist/index.d.ts +501 -228
  6. package/dist/index.js +6738 -2571
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +6697 -2499
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +6 -6
  11. package/src/components/ActivityIndicator.tsx +9 -4
  12. package/src/components/ActivityLog.tsx +197 -0
  13. package/src/components/AnimatePresence.tsx +37 -22
  14. package/src/components/Avatar.tsx +42 -25
  15. package/src/components/Banner.tsx +57 -11
  16. package/src/components/BaseToolbar.tsx +29 -7
  17. package/src/components/Breadcrumbs.tsx +2 -2
  18. package/src/components/Button.tsx +23 -24
  19. package/src/components/Checkbox.tsx +6 -35
  20. package/src/components/Chip.tsx +45 -33
  21. package/src/components/Collection.tsx +16 -11
  22. package/src/components/ColorSwatch.tsx +3 -3
  23. package/src/components/ColorSwatchControl.tsx +2 -2
  24. package/src/components/Combobox.tsx +6 -6
  25. package/src/components/ComboboxMenu.tsx +6 -5
  26. package/src/components/CommandPalette.tsx +1 -1
  27. package/src/components/Dialog.tsx +6 -12
  28. package/src/components/Divider.tsx +8 -8
  29. package/src/components/DraggableMenuButton.tsx +9 -5
  30. package/src/components/Drawer.tsx +5 -5
  31. package/src/components/DropdownMenu.tsx +1 -1
  32. package/src/components/EditableText.tsx +2 -2
  33. package/src/components/Fade.tsx +13 -13
  34. package/src/components/FileUploadIndicator.tsx +11 -11
  35. package/src/components/FloatingWindow.tsx +2 -2
  36. package/src/components/Grid.tsx +18 -11
  37. package/src/components/GridView.tsx +32 -25
  38. package/src/components/IVirtualizedList.tsx +5 -0
  39. package/src/components/Icons.tsx +24 -4
  40. package/src/components/InputField.tsx +19 -19
  41. package/src/components/InspectorContainer.tsx +2 -2
  42. package/src/components/InspectorPrimitives.tsx +15 -9
  43. package/src/components/Label.tsx +4 -3
  44. package/src/components/LabeledElementView.tsx +3 -3
  45. package/src/components/LabeledField.tsx +6 -6
  46. package/src/components/List.tsx +103 -45
  47. package/src/components/ListMenu.tsx +91 -0
  48. package/src/components/ListNavigator.tsx +106 -0
  49. package/src/components/ListView.tsx +20 -1158
  50. package/src/components/MediaThumbnail.tsx +56 -15
  51. package/src/components/Message.tsx +5 -5
  52. package/src/components/Navigator.tsx +461 -0
  53. package/src/components/NoyaLogo.tsx +1 -1
  54. package/src/components/Popover.tsx +9 -5
  55. package/src/components/Progress.tsx +19 -9
  56. package/src/components/ResizableContainer.tsx +187 -0
  57. package/src/components/RingProgress.tsx +128 -0
  58. package/src/components/ScrollArea.tsx +4 -4
  59. package/src/components/ScrollableSidebar.tsx +10 -0
  60. package/src/components/SearchCompletionMenu.tsx +3 -3
  61. package/src/components/Section.tsx +107 -57
  62. package/src/components/SegmentedControl.tsx +29 -29
  63. package/src/components/SelectMenu.tsx +13 -20
  64. package/src/components/SelectionToolbar.tsx +4 -3
  65. package/src/components/Slider.tsx +9 -9
  66. package/src/components/Spacer.tsx +2 -2
  67. package/src/components/StackNavigator.tsx +378 -0
  68. package/src/components/Stepper.tsx +88 -0
  69. package/src/components/Switch.tsx +4 -4
  70. package/src/components/Tabs.tsx +3 -3
  71. package/src/components/Text.tsx +23 -13
  72. package/src/components/TextArea.tsx +3 -3
  73. package/src/components/Toast.tsx +4 -4
  74. package/src/components/Toolbar.tsx +50 -9
  75. package/src/components/ToolbarDrawer.tsx +2 -2
  76. package/src/components/Tooltip.tsx +1 -1
  77. package/src/components/TreeView.tsx +11 -4
  78. package/src/components/UserPointer.tsx +4 -4
  79. package/src/components/ai-assistant/AIAssistantLayout.tsx +8 -8
  80. package/src/components/blocks/ImageBlockComponent.tsx +13 -7
  81. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +4 -4
  82. package/src/components/file-explorer/FileExplorerLayout.tsx +20 -8
  83. package/src/components/internal/Checkmark.tsx +1 -1
  84. package/src/components/internal/Menu.tsx +22 -18
  85. package/src/components/internal/MenuViewport.tsx +13 -6
  86. package/src/components/internal/SelectItem.tsx +3 -3
  87. package/src/components/internal/TextInput.tsx +5 -1
  88. package/src/components/listView/ListViewContexts.tsx +68 -0
  89. package/src/components/listView/ListViewEditableRowTitle.tsx +55 -0
  90. package/src/components/listView/ListViewRoot.tsx +521 -0
  91. package/src/components/listView/ListViewRow.tsx +475 -0
  92. package/src/components/listView/ListViewRowTitle.tsx +21 -0
  93. package/src/components/listView/types.ts +11 -0
  94. package/src/components/pipeline/PipelineResultLayout.tsx +6 -6
  95. package/src/components/sorting/DragRegistration.tsx +2 -1
  96. package/src/components/sorting/SharedDragProvider.tsx +91 -9
  97. package/src/components/sorting/Sortable.tsx +71 -17
  98. package/src/components/workspace/DrawerWorkspaceLayout.tsx +5 -5
  99. package/src/components/workspace/PanelWorkspaceLayout.tsx +65 -49
  100. package/src/components/workspace/VerticalTabMenu.tsx +25 -15
  101. package/src/components/workspace/WorkspaceLayout.tsx +30 -21
  102. package/src/contexts/DialogContext.tsx +17 -5
  103. package/src/hooks/useIndent.ts +3 -8
  104. package/src/index.css +24 -10
  105. package/src/index.tsx +10 -5
  106. package/src/utils/classNames.ts +52 -6
  107. package/src/utils/editableBlockStyles.ts +2 -2
  108. package/src/utils/formatByteSize.ts +1 -0
  109. package/src/utils/inputs.ts +8 -6
  110. package/src/utils/sketchColor.ts +0 -34
  111. package/tailwind.config.ts +1 -0
  112. package/src/components/FillInputField.tsx +0 -37
  113. package/src/components/FillPreviewBackground.tsx +0 -134
  114. package/src/components/GradientPicker.tsx +0 -90
  115. package/src/utils/getGradientBackground.tsx +0 -31
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { Size } from "@noya-app/noya-geometry";
3
4
  import {
4
5
  FileIcon,
5
6
  ImageIcon,
@@ -48,6 +49,8 @@ export type MediaThumbnailProps = {
48
49
 
49
50
  fileName?: string;
50
51
  renderThumbnailIcon?: (props: RenderThumbnailIconProps) => React.ReactNode;
52
+ dimensions?: Size;
53
+ aspect?: "contain" | "cover";
51
54
  };
52
55
 
53
56
  export const getThumbnailColors = (
@@ -95,7 +98,9 @@ export const MediaThumbnail = memo(function MediaThumbnail({
95
98
  iconName,
96
99
  size = "medium",
97
100
  fileName,
101
+ dimensions,
98
102
  renderThumbnailIcon,
103
+ aspect = "contain",
99
104
  }: MediaThumbnailProps) {
100
105
  const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
101
106
  colorScheme,
@@ -117,18 +122,41 @@ export const MediaThumbnail = memo(function MediaThumbnail({
117
122
  if (rendered) return rendered;
118
123
  }
119
124
 
125
+ if (url) {
126
+ // const renderUrl = new URL(`http://localhost:8080/render`);
127
+ const renderUrl = new URL(`https://image.noya.io/render`);
128
+
129
+ const imageDimensions = dimensions ?? {
130
+ width: imageSizeMap[size],
131
+ height: imageSizeMap[size],
132
+ };
133
+
134
+ if (imageDimensions.width) {
135
+ renderUrl.searchParams.set("w", imageDimensions.width?.toString());
136
+ }
137
+ if (imageDimensions.height) {
138
+ renderUrl.searchParams.set("h", imageDimensions.height?.toString());
139
+ }
140
+
141
+ renderUrl.searchParams.set("url", url);
142
+ renderUrl.searchParams.set("fit", aspect);
143
+ renderUrl.searchParams.set("theme", "light");
144
+
145
+ return (
146
+ <img
147
+ src={renderUrl.toString()}
148
+ alt=""
149
+ className="n-object-contain n-w-full n-h-full"
150
+ tabIndex={-1}
151
+ width={imageDimensions?.width}
152
+ height={imageDimensions?.height}
153
+ />
154
+ );
155
+ }
156
+
120
157
  switch (contentType) {
121
158
  case "image":
122
- if (!url) return <ImageIcon color={iconColor} style={iconStyles} />;
123
-
124
- return (
125
- <img
126
- src={url}
127
- alt=""
128
- className="object-contain w-full h-full"
129
- tabIndex={-1}
130
- />
131
- );
159
+ return <ImageIcon color={iconColor} style={iconStyles} />;
132
160
  case "video":
133
161
  return <VideoIcon color={iconColor} style={iconStyles} />;
134
162
  case "audio":
@@ -151,7 +179,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
151
179
  <img
152
180
  src={`https://api.iconify.design/catppuccin/${fileIcon}.svg?height=none&box=1`}
153
181
  alt=""
154
- className="w-4 h-4"
182
+ className="n-w-4 n-h-4"
155
183
  />
156
184
  );
157
185
  }
@@ -160,19 +188,22 @@ export const MediaThumbnail = memo(function MediaThumbnail({
160
188
  return <FileIcon color={iconColor} style={iconStyles} />;
161
189
  }
162
190
  }, [
163
- contentType,
191
+ renderThumbnailIcon,
164
192
  url,
193
+ contentType,
194
+ fileName,
195
+ dimensions,
196
+ size,
197
+ aspect,
165
198
  iconColor,
166
199
  iconStyles,
167
200
  iconName,
168
- renderThumbnailIcon,
169
- fileName,
170
201
  ]);
171
202
 
172
203
  return (
173
204
  <div
174
205
  className={cx(
175
- "w-full h-full justify-center flex items-center aspect-square",
206
+ "n-w-full n-h-full n-justify-center n-flex n-items-center n-aspect-square",
176
207
  className
177
208
  )}
178
209
  style={{ backgroundColor }}
@@ -192,6 +223,16 @@ const iconSizeMap: Record<CollectionItemSize, number | string> = {
192
223
  xl: "max(15px, 20%)",
193
224
  };
194
225
 
226
+ const imageSizeMap: Record<CollectionItemSize, number | string> = {
227
+ xxs: 12 * 2,
228
+ xs: 18 * 2,
229
+ small: 24 * 2,
230
+ medium: 36 * 2,
231
+ mediumLarge: 48 * 2,
232
+ large: 64 * 2,
233
+ xl: 96 * 2,
234
+ };
235
+
195
236
  function findFileIcon(fileName: string) {
196
237
  const fileIcon = fileNames[fileName];
197
238
  if (fileIcon) return fileIcon;
@@ -36,8 +36,8 @@ export const Message = memo(
36
36
  return (
37
37
  <div
38
38
  className={cx(
39
- "flex gap-2",
40
- role === "user" ? "flex-row-reverse" : "flex-row"
39
+ "n-flex n-gap-2",
40
+ role === "user" ? "n-flex-row-reverse" : "n-flex-row"
41
41
  )}
42
42
  >
43
43
  {role === "assistant" ? (
@@ -59,9 +59,9 @@ export const Message = memo(
59
59
  <div
60
60
  className={cx(
61
61
  "markdown-message",
62
- "flex flex-row gap-2 rounded-lg py-2 px-3 transition-all duration-200 max-w-[70%] text-sm",
63
- role === "user" ? "text-white" : "text-text",
64
- role === "user" ? "ml-auto" : "bg-input-background"
62
+ "n-flex n-flex-row n-gap-2 n-rounded-lg n-py-2 n-px-3 n-transition-all n-duration-200 n-max-w-[70%] n-text-sm",
63
+ role === "user" ? "n-text-white" : "n-text-text",
64
+ role === "user" ? "n-ml-auto" : "n-bg-input-background"
65
65
  )}
66
66
  style={styles}
67
67
  ref={ref}
@@ -0,0 +1,461 @@
1
+ "use client";
2
+
3
+ import React, {
4
+ Children,
5
+ ReactNode,
6
+ isValidElement,
7
+ useCallback,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ } from "react";
12
+
13
+ import {
14
+ StackItem,
15
+ StackNavigator,
16
+ StackNavigatorProps,
17
+ } from "./StackNavigator";
18
+
19
+ export type NavigatorParams = Record<string, unknown>;
20
+
21
+ export type NavigatorRoute<Params extends NavigatorParams | null> = {
22
+ key: string;
23
+ name: string;
24
+ params: Params;
25
+ };
26
+
27
+ export type NavigatorRoutesConfig<
28
+ RoutesConfig extends Record<string, NavigatorParams | null>,
29
+ > = {
30
+ [K in keyof RoutesConfig]: NavigatorRoute<RoutesConfig[K]>;
31
+ };
32
+
33
+ export type NavigatorPushFunction<
34
+ Routes extends Record<string, NavigatorRoute<any>>,
35
+ > = <K extends keyof Routes>(
36
+ ...args: Routes[K]["params"] extends null
37
+ ? [name: K]
38
+ : [name: K, params: Routes[K]["params"]]
39
+ ) => void;
40
+
41
+ export type NavigatorReplaceFunction<
42
+ Routes extends Record<string, NavigatorRoute<any>>,
43
+ > = <K extends keyof Routes>(
44
+ ...args: Routes[K]["params"] extends null
45
+ ? [name: K]
46
+ : [name: K, params: Routes[K]["params"]]
47
+ ) => void;
48
+
49
+ export type NavigatorNavigation<
50
+ Routes extends Record<string, NavigatorRoute<any>>,
51
+ > = {
52
+ push: NavigatorPushFunction<Routes>;
53
+ replace: NavigatorReplaceFunction<Routes>;
54
+ pop: () => void;
55
+ canGoBack: boolean;
56
+ };
57
+
58
+ export type NavigatorRenderContext<
59
+ Routes extends Record<string, NavigatorRoute<any>>,
60
+ Name extends keyof Routes,
61
+ > = {
62
+ route: NavigatorRoute<Routes[Name]["params"]>;
63
+ navigation: NavigatorNavigation<Routes>;
64
+ stack: NavigatorStackEntry<Routes>[];
65
+ };
66
+
67
+ export type NavigatorHeaderProps<
68
+ Routes extends Record<string, NavigatorRoute<any>>,
69
+ Name extends keyof Routes,
70
+ > = NavigatorRenderContext<Routes, Name> & {
71
+ title: React.ReactNode;
72
+ right: React.ReactNode;
73
+ showBackButton: boolean;
74
+ onBack?: () => void;
75
+ };
76
+
77
+ export type NavigatorRenderHeader<
78
+ Routes extends Record<string, NavigatorRoute<any>>,
79
+ Name extends keyof Routes,
80
+ > =
81
+ | React.ReactNode
82
+ | ((context: NavigatorHeaderProps<Routes, Name>) => React.ReactNode);
83
+
84
+ export type NavigatorScreenProps<
85
+ Routes extends Record<string, NavigatorRoute<any>>,
86
+ Name extends keyof Routes,
87
+ > = {
88
+ name: Name;
89
+ title:
90
+ | React.ReactNode
91
+ | ((context: NavigatorRenderContext<Routes, Name>) => React.ReactNode);
92
+ right?:
93
+ | React.ReactNode
94
+ | ((context: NavigatorRenderContext<Routes, Name>) => React.ReactNode);
95
+ showBackButton?:
96
+ | boolean
97
+ | ((context: NavigatorRenderContext<Routes, Name>) => boolean);
98
+ renderHeader?: NavigatorRenderHeader<Routes, Name>;
99
+ children:
100
+ | React.ReactNode
101
+ | ((context: NavigatorRenderContext<Routes, Name>) => React.ReactNode);
102
+ };
103
+
104
+ const navigatorScreenIdentifier = Symbol("NavigatorScreen");
105
+
106
+ const NavigatorScreen = Object.assign(
107
+ <
108
+ Routes extends Record<string, NavigatorRoute<any>>,
109
+ Name extends keyof Routes,
110
+ >(
111
+ _props: NavigatorScreenProps<Routes, Name>
112
+ ) => null,
113
+ {
114
+ [navigatorScreenIdentifier]: true as const,
115
+ }
116
+ );
117
+
118
+ type NavigatorScreenConfig<
119
+ Routes extends Record<string, NavigatorRoute<any>>,
120
+ Name extends keyof Routes,
121
+ > = NavigatorScreenProps<Routes, Name> & {
122
+ name: Name;
123
+ };
124
+
125
+ export type NavigatorStackEntry<
126
+ Routes extends Record<string, NavigatorRoute<any>>,
127
+ > = {
128
+ key: string;
129
+ name: keyof Routes;
130
+ params: Routes[keyof Routes]["params"];
131
+ };
132
+
133
+ export type NavigatorProps<
134
+ Routes extends Record<string, NavigatorRoute<any>>,
135
+ Name extends keyof Routes,
136
+ > = {
137
+ initialRoute: Routes[Name]["params"] extends null
138
+ ? Name | { name: Name }
139
+ : { name: Name; params: Routes[Name]["params"] };
140
+ children: React.ReactNode;
141
+ showHeader?:
142
+ | boolean
143
+ | ((context: { stack: NavigatorStackEntry<Routes>[] }) => boolean);
144
+ renderHeader?: NavigatorRenderHeader<Routes, any>;
145
+ } & Pick<
146
+ StackNavigatorProps,
147
+ "className" | "style" | "contentClassName" | "cardStyle" | "cardClassName"
148
+ >;
149
+
150
+ type NavigatorScreenContextValue<
151
+ Routes extends Record<string, NavigatorRoute<any>>,
152
+ Name extends keyof Routes,
153
+ > = NavigatorRenderContext<Routes, Name>;
154
+
155
+ const NavigatorScreenContext = React.createContext<NavigatorScreenContextValue<
156
+ any,
157
+ any
158
+ > | null>(null);
159
+
160
+ export function useNavigator<
161
+ Routes extends Record<string, NavigatorRoute<any>>,
162
+ >(): NavigatorNavigation<Routes> {
163
+ const context = React.useContext(NavigatorScreenContext);
164
+
165
+ if (!context) {
166
+ throw new Error("useNavigator must be used within a Navigator screen");
167
+ }
168
+
169
+ return context.navigation;
170
+ }
171
+
172
+ export function useNavigatorRoute<
173
+ Routes extends Record<string, NavigatorRoute<any>>,
174
+ Name extends keyof Routes,
175
+ >(): NavigatorRoute<Routes[Name]["params"]> {
176
+ const context = React.useContext(NavigatorScreenContext);
177
+
178
+ if (!context) {
179
+ throw new Error("useNavigatorRoute must be used within a Navigator screen");
180
+ }
181
+
182
+ return context.route;
183
+ }
184
+
185
+ function isNavigatorScreenElement(
186
+ child: ReactNode
187
+ ): child is React.ReactElement<NavigatorScreenProps<any, any>> {
188
+ return (
189
+ isValidElement(child) &&
190
+ Boolean(
191
+ (child.type as { [navigatorScreenIdentifier]?: boolean })?.[
192
+ navigatorScreenIdentifier
193
+ ]
194
+ )
195
+ );
196
+ }
197
+
198
+ function evaluateOption<T, C>(
199
+ option: T | ((context: C) => T) | undefined,
200
+ context: C
201
+ ): T | undefined {
202
+ if (option instanceof Function) {
203
+ return option(context);
204
+ }
205
+
206
+ return option;
207
+ }
208
+
209
+ function NavigatorImpl<
210
+ Routes extends Record<string, NavigatorRoute<any>>,
211
+ Name extends keyof Routes,
212
+ >({
213
+ initialRoute,
214
+ children,
215
+ className,
216
+ style,
217
+ contentClassName,
218
+ cardStyle,
219
+ cardClassName,
220
+ showHeader,
221
+ renderHeader,
222
+ }: NavigatorProps<Routes, Name>) {
223
+ const screens = useMemo<NavigatorScreenConfig<Routes, any>[]>(() => {
224
+ const configs: NavigatorScreenConfig<Routes, any>[] = [];
225
+
226
+ Children.forEach(children, (child) => {
227
+ if (!isNavigatorScreenElement(child)) return;
228
+
229
+ configs.push(child.props as NavigatorScreenConfig<Routes, any>);
230
+ });
231
+
232
+ return configs;
233
+ }, [children]);
234
+
235
+ const screenMap = useMemo(() => {
236
+ const map = new Map<keyof Routes, NavigatorScreenConfig<Routes, any>>();
237
+
238
+ for (const screen of screens) {
239
+ if (map.has(screen.name)) {
240
+ console.warn(
241
+ `Navigator: duplicate screen name "${String(screen.name)}" detected. The latter definition will be used.`
242
+ );
243
+ }
244
+
245
+ map.set(screen.name, screen);
246
+ }
247
+
248
+ return map;
249
+ }, [screens]);
250
+
251
+ const keyRef = useRef(0);
252
+ const createEntry = useCallback(
253
+ <K extends keyof Routes>(
254
+ name: K,
255
+ params: Routes[K]["params"]
256
+ ): NavigatorStackEntry<Routes> => ({
257
+ key: `${String(name)}-${keyRef.current++}`,
258
+ name,
259
+ params,
260
+ }),
261
+ []
262
+ );
263
+
264
+ const initialScreenName =
265
+ typeof initialRoute === "string"
266
+ ? initialRoute
267
+ : (initialRoute as { name: Name }).name;
268
+
269
+ if (!screenMap.has(initialScreenName)) {
270
+ throw new Error(
271
+ `Navigator: initial route "${String(initialScreenName)}" is not registered as a screen.`
272
+ );
273
+ }
274
+
275
+ const [stack, setStack] = useState<NavigatorStackEntry<Routes>[]>(() => [
276
+ createEntry(
277
+ initialScreenName,
278
+ (initialRoute as { params: Routes[Name]["params"] }).params
279
+ ),
280
+ ]);
281
+
282
+ const push = useCallback(
283
+ <K extends keyof Routes>(
284
+ ...args: Routes[K]["params"] extends null
285
+ ? [name: K]
286
+ : [name: K, params: Routes[K]["params"]]
287
+ ) => {
288
+ const [name, params] = args;
289
+
290
+ const screen = screenMap.get(name);
291
+
292
+ if (!screen) {
293
+ console.warn(
294
+ `Navigator: attempted to push unknown screen "${String(name)}".`
295
+ );
296
+ return;
297
+ }
298
+
299
+ setStack((prev) => [...prev, createEntry(name, params)]);
300
+ },
301
+ [createEntry, screenMap]
302
+ );
303
+
304
+ const pop = useCallback(() => {
305
+ setStack((prev) => {
306
+ if (prev.length <= 1) return prev;
307
+
308
+ const nextStack = prev.slice(0, -1);
309
+
310
+ if (nextStack.length === 0) {
311
+ return nextStack;
312
+ }
313
+
314
+ const updatedStack = [...nextStack];
315
+ const topEntry = updatedStack[updatedStack.length - 1];
316
+
317
+ updatedStack[updatedStack.length - 1] = createEntry(
318
+ topEntry.name as keyof Routes,
319
+ topEntry.params as Routes[keyof Routes]["params"]
320
+ );
321
+
322
+ return updatedStack;
323
+ });
324
+ }, [createEntry]);
325
+
326
+ const replace = useCallback(
327
+ <K extends keyof Routes>(
328
+ targetKey: string,
329
+ name: K,
330
+ params: Routes[K]["params"] | null
331
+ ) => {
332
+ const screen = screenMap.get(name);
333
+
334
+ if (!screen) {
335
+ console.warn(
336
+ `Navigator: attempted to replace with unknown screen "${String(name)}".`
337
+ );
338
+ return;
339
+ }
340
+
341
+ setStack((prev) => {
342
+ const index = prev.findIndex((entry) => entry.key === targetKey);
343
+
344
+ if (index === -1) return prev;
345
+
346
+ const next = [...prev];
347
+ next[index] = createEntry(name, params as Routes[K]["params"]);
348
+
349
+ if (next.length > 0) {
350
+ const top = next[next.length - 1];
351
+ next[next.length - 1] = createEntry(
352
+ top.name as keyof Routes,
353
+ top.params as Routes[keyof Routes]["params"]
354
+ );
355
+ }
356
+
357
+ return next;
358
+ });
359
+ },
360
+ [createEntry, screenMap]
361
+ );
362
+
363
+ const items = useMemo<StackItem[]>(() => {
364
+ return stack.flatMap((entry) => {
365
+ const screen = screenMap.get(entry.name);
366
+
367
+ if (!screen) return [];
368
+
369
+ const canGoBack = stack.length > 1;
370
+
371
+ const route: NavigatorRoute<Routes[keyof Routes]["params"]> = {
372
+ key: entry.key,
373
+ name: String(entry.name),
374
+ params: (entry.params ?? null) as Routes[keyof Routes]["params"],
375
+ };
376
+
377
+ const navigation: NavigatorNavigation<Routes> = {
378
+ push,
379
+ replace: (...args) => replace(entry.key, args[0], args[1]),
380
+ pop,
381
+ canGoBack,
382
+ };
383
+
384
+ const context: NavigatorRenderContext<Routes, any> = {
385
+ route,
386
+ navigation,
387
+ stack,
388
+ };
389
+
390
+ const title = evaluateOption(screen.title, context);
391
+ const right = evaluateOption(screen.right, context);
392
+ const showBackButton = evaluateOption(screen.showBackButton, context);
393
+ const headerOption = screen.renderHeader ?? renderHeader;
394
+
395
+ return {
396
+ key: entry.key,
397
+ title,
398
+ right,
399
+ showBackButton,
400
+ renderHeader:
401
+ headerOption === undefined
402
+ ? undefined
403
+ : (headerProps) =>
404
+ evaluateOption(headerOption, {
405
+ ...context,
406
+ title,
407
+ right,
408
+ showBackButton: headerProps.showBackButton,
409
+ onBack: headerProps.onBack,
410
+ }),
411
+ renderContent: () => {
412
+ return (
413
+ <NavigatorScreenContext.Provider value={context}>
414
+ {evaluateOption(screen.children, context)}
415
+ </NavigatorScreenContext.Provider>
416
+ );
417
+ },
418
+ } satisfies StackItem;
419
+ });
420
+ }, [pop, replace, screenMap, stack, push, renderHeader]);
421
+
422
+ const shouldShowHeader = evaluateOption(showHeader, {
423
+ stack,
424
+ });
425
+
426
+ return (
427
+ <StackNavigator
428
+ items={items}
429
+ onBack={pop}
430
+ className={className}
431
+ style={style}
432
+ contentClassName={contentClassName}
433
+ cardStyle={cardStyle}
434
+ cardClassName={cardClassName}
435
+ showHeader={shouldShowHeader}
436
+ />
437
+ );
438
+ }
439
+
440
+ export const Navigator = Object.assign(NavigatorImpl, {
441
+ Screen: NavigatorScreen,
442
+ });
443
+
444
+ export function createNavigator<
445
+ RoutesConfig extends Record<string, NavigatorParams | null>,
446
+ >() {
447
+ return Object.assign(
448
+ NavigatorImpl as <Name extends keyof RoutesConfig>(
449
+ props: NavigatorProps<NavigatorRoutesConfig<RoutesConfig>, Name>
450
+ ) => ReactNode,
451
+ {
452
+ Screen: NavigatorScreen as <Name extends keyof RoutesConfig>(
453
+ props: NavigatorScreenProps<NavigatorRoutesConfig<RoutesConfig>, Name>
454
+ ) => ReactNode,
455
+ useNavigator: useNavigator<NavigatorRoutesConfig<RoutesConfig>>,
456
+ useNavigatorRoute: useNavigatorRoute as <Name extends keyof RoutesConfig>(
457
+ name: Name
458
+ ) => NavigatorRoute<NavigatorRoutesConfig<RoutesConfig>[Name]["params"]>,
459
+ }
460
+ );
461
+ }
@@ -14,7 +14,7 @@ export const Logo = forwardRef(function Logo(
14
14
  fill="none"
15
15
  xmlns="http://www.w3.org/2000/svg"
16
16
  {...props}
17
- className="w-6 aspect-[24/26]"
17
+ className="n-w-6 n-aspect-[24/26]"
18
18
  >
19
19
  <path
20
20
  d="M6.5 3H2V12H5V6H6.5C7.32843 6 8 6.67157 8 7.5V12H11V7.5C11 5.01472 8.98528 3 6.5 3Z"
@@ -46,13 +46,15 @@ interface Props
46
46
  onClickClose?: () => void;
47
47
  showArrow?: boolean;
48
48
  className?: string;
49
+ style?: React.CSSProperties;
49
50
  portalContainer?: HTMLElement | null;
50
51
  }
51
52
 
52
53
  export const popoverStyle = {
53
- base: "rounded font-[14px] bg-popover-background shadow-popover max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
54
- large: "w-[680px]",
55
- normal: "w-[240px]",
54
+ base:
55
+ "n-rounded n-font-[14px] n-bg-popover-background n-shadow-popover n-max-h-[600px] n-overflow-y-auto n-text-text-muted n-z-[1000]",
56
+ large: "n-w-[680px]",
57
+ normal: "n-w-[240px]",
56
58
  };
57
59
 
58
60
  export function Popover({
@@ -73,6 +75,7 @@ export function Popover({
73
75
  onClickClose,
74
76
  className,
75
77
  portalContainer,
78
+ style,
76
79
  }: Props) {
77
80
  const defaultId = useId();
78
81
  const portalScopeId = usePortalScopeId();
@@ -103,6 +106,7 @@ export function Popover({
103
106
  <PopoverPrimitive.Content
104
107
  {...portalScopeProps(portalScopeId)}
105
108
  ref={contentRef}
109
+ style={style}
106
110
  className={cx(
107
111
  popoverStyle.base,
108
112
  variant === "large" && popoverStyle.large,
@@ -140,11 +144,11 @@ export function Popover({
140
144
  >
141
145
  {typeof children === "function" ? children({ close }) : children}
142
146
  {showArrow && (
143
- <PopoverPrimitive.Arrow className="fill-popover-background" />
147
+ <PopoverPrimitive.Arrow className="n-fill-popover-background" />
144
148
  )}
145
149
  {closable && (
146
150
  <PopoverPrimitive.Close
147
- className="rounded-full h-[25px] w-[25px] inline-flex items-center justify-center absolute top-[5px] right-[5px]"
151
+ className="n-rounded-full n-h-[25px] n-w-[25px] n-inline-flex n-items-center n-justify-center n-absolute n-top-[5px] n-right-[5px]"
148
152
  style={closeStyles}
149
153
  >
150
154
  <IconButton iconName="Cross2Icon" onClick={onClickClose} />