@noya-app/noya-designsystem 0.1.63 → 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 (143) hide show
  1. package/.turbo/turbo-build.log +13 -10
  2. package/CHANGELOG.md +25 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +648 -333
  5. package/dist/index.d.ts +648 -333
  6. package/dist/index.js +13813 -9157
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +13949 -9269
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +10 -22
  11. package/src/__tests__/validateDropIndicator.test.ts +26 -41
  12. package/src/__tests__/workspaceLayout.test.ts +281 -0
  13. package/src/components/ActionMenu.tsx +2 -0
  14. package/src/components/ActivityIndicator.tsx +13 -6
  15. package/src/components/ActivityLog.tsx +197 -0
  16. package/src/components/AnimatePresence.tsx +43 -26
  17. package/src/components/Avatar.tsx +45 -26
  18. package/src/components/Banner.tsx +57 -11
  19. package/src/components/BaseToolbar.tsx +67 -34
  20. package/src/components/Breadcrumbs.tsx +4 -2
  21. package/src/components/Button.tsx +28 -23
  22. package/src/components/Checkbox.tsx +6 -35
  23. package/src/components/Chip.tsx +47 -33
  24. package/src/components/Collection.tsx +20 -12
  25. package/src/components/ColorSwatch.tsx +42 -22
  26. package/src/components/ColorSwatchControl.tsx +7 -5
  27. package/src/components/Combobox.tsx +8 -6
  28. package/src/components/ComboboxMenu.tsx +6 -5
  29. package/src/components/CommandPalette.tsx +3 -1
  30. package/src/components/ContextMenu.tsx +3 -1
  31. package/src/components/Dialog.tsx +9 -13
  32. package/src/components/DimensionInput.tsx +2 -0
  33. package/src/components/Divider.tsx +18 -14
  34. package/src/components/DraggableMenuButton.tsx +11 -5
  35. package/src/components/Drawer.tsx +14 -6
  36. package/src/components/DropdownMenu.tsx +4 -2
  37. package/src/components/EditableText.tsx +4 -2
  38. package/src/components/Fade.tsx +13 -13
  39. package/src/components/FileUploadIndicator.tsx +108 -0
  40. package/src/components/FloatingWindow.tsx +4 -2
  41. package/src/components/Grid.tsx +58 -42
  42. package/src/components/GridView.tsx +65 -36
  43. package/src/components/IVirtualizedList.tsx +5 -0
  44. package/src/components/IconButton.tsx +2 -0
  45. package/src/components/Icons.tsx +24 -4
  46. package/src/components/InputField.tsx +21 -19
  47. package/src/components/InspectorContainer.tsx +2 -2
  48. package/src/components/InspectorPrimitives.tsx +15 -9
  49. package/src/components/Label.tsx +4 -3
  50. package/src/components/LabeledElementView.tsx +9 -5
  51. package/src/components/LabeledField.tsx +8 -6
  52. package/src/components/List.tsx +112 -46
  53. package/src/components/ListMenu.tsx +91 -0
  54. package/src/components/ListNavigator.tsx +106 -0
  55. package/src/components/ListView.tsx +22 -1147
  56. package/src/components/MediaThumbnail.tsx +84 -14
  57. package/src/components/Message.tsx +11 -12
  58. package/src/components/Navigator.tsx +461 -0
  59. package/src/components/NoyaLogo.tsx +1 -1
  60. package/src/components/Popover.tsx +12 -6
  61. package/src/components/Progress.tsx +31 -8
  62. package/src/components/ResizableContainer.tsx +187 -0
  63. package/src/components/RingProgress.tsx +128 -0
  64. package/src/components/ScrollArea.tsx +7 -5
  65. package/src/components/ScrollableSidebar.tsx +10 -0
  66. package/src/components/SearchCompletionMenu.tsx +5 -3
  67. package/src/components/Section.tsx +109 -57
  68. package/src/components/SegmentedControl.tsx +51 -25
  69. package/src/components/SelectMenu.tsx +16 -21
  70. package/src/components/SelectionToolbar.tsx +7 -4
  71. package/src/components/Slider.tsx +12 -10
  72. package/src/components/Spacer.tsx +2 -2
  73. package/src/components/StackNavigator.tsx +378 -0
  74. package/src/components/Stepper.tsx +88 -0
  75. package/src/components/Switch.tsx +7 -5
  76. package/src/components/Tabs.tsx +6 -4
  77. package/src/components/Text.tsx +29 -18
  78. package/src/components/TextArea.tsx +5 -3
  79. package/src/components/Toast.tsx +5 -5
  80. package/src/components/Toolbar.tsx +57 -10
  81. package/src/components/ToolbarDrawer.tsx +29 -0
  82. package/src/components/Tooltip.tsx +2 -2
  83. package/src/components/TreeView.tsx +13 -4
  84. package/src/components/UserPointer.tsx +6 -4
  85. package/src/components/ai-assistant/AIAssistantLayout.tsx +10 -8
  86. package/src/components/blocks/ImageBlockComponent.tsx +162 -0
  87. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +6 -4
  88. package/src/components/file-explorer/FileExplorerLayout.tsx +22 -8
  89. package/src/components/internal/Checkmark.tsx +24 -0
  90. package/src/components/internal/Menu.tsx +24 -19
  91. package/src/components/internal/MenuViewport.tsx +15 -6
  92. package/src/components/internal/SelectItem.tsx +7 -26
  93. package/src/components/internal/TextInput.tsx +7 -1
  94. package/src/components/listView/ListViewContexts.tsx +68 -0
  95. package/src/components/listView/ListViewEditableRowTitle.tsx +55 -0
  96. package/src/components/listView/ListViewRoot.tsx +521 -0
  97. package/src/components/listView/ListViewRow.tsx +475 -0
  98. package/src/components/listView/ListViewRowTitle.tsx +21 -0
  99. package/src/components/listView/types.ts +11 -0
  100. package/src/components/pipeline/PipelineResultLayout.tsx +6 -6
  101. package/src/components/sorting/DragRegistration.tsx +10 -5
  102. package/src/components/sorting/SharedDragProvider.tsx +134 -27
  103. package/src/components/sorting/Sortable.tsx +90 -27
  104. package/src/components/sorting/createSharedDrag.tsx +7 -6
  105. package/src/components/sorting/sorting.ts +6 -9
  106. package/src/components/workspace/DrawerWorkspaceLayout.tsx +110 -29
  107. package/src/components/workspace/PanelWorkspaceLayout.tsx +148 -99
  108. package/src/components/workspace/VerticalTabMenu.tsx +46 -0
  109. package/src/components/workspace/WorkspaceLayout.tsx +76 -63
  110. package/src/components/workspace/constants.ts +4 -0
  111. package/src/components/workspace/panelStorage.ts +216 -0
  112. package/src/components/workspace/types.ts +11 -4
  113. package/src/contexts/DesignSystemConfiguration.tsx +12 -5
  114. package/src/contexts/DialogContext.tsx +19 -5
  115. package/src/contexts/FloatingWindowContext.tsx +2 -0
  116. package/src/contexts/GlobalInputBlurContext.tsx +2 -0
  117. package/src/contexts/ImageDataContext.tsx +2 -0
  118. package/src/contexts/LinkComponentContext.tsx +17 -0
  119. package/src/contexts/OpenPortalsContext.tsx +2 -0
  120. package/src/contexts/PortalScopeContext.tsx +2 -0
  121. package/src/hooks/mergeEventHandlers.ts +1 -1
  122. package/src/hooks/useHover.ts +30 -17
  123. package/src/hooks/useIndent.ts +5 -8
  124. package/src/hooks/useLabel.ts +2 -0
  125. package/src/hooks/useObjectURL.ts +5 -3
  126. package/src/hooks/usePlatform.ts +5 -3
  127. package/src/hooks/useTheme.ts +3 -1
  128. package/src/index.css +43 -10
  129. package/src/index.tsx +15 -7
  130. package/src/utils/classNames.ts +56 -9
  131. package/src/utils/combobox.ts +2 -0
  132. package/src/utils/editableBlockStyles.ts +2 -2
  133. package/src/utils/formatByteSize.ts +9 -0
  134. package/src/utils/inputs.ts +8 -6
  135. package/src/utils/sketchColor.ts +0 -34
  136. package/src/utils/withSeparatorElements.ts +1 -1
  137. package/tailwind.config.ts +1 -0
  138. package/src/components/FillInputField.tsx +0 -37
  139. package/src/components/FillPreviewBackground.tsx +0 -132
  140. package/src/components/GradientPicker.tsx +0 -88
  141. package/src/hooks/usePreservePanelSize.tsx +0 -183
  142. package/src/mediaQuery.ts +0 -13
  143. package/src/utils/getGradientBackground.tsx +0 -31
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { cssVarNames, cssVars } from "../theme";
3
+ import { cx } from "../utils/classNames";
3
4
  import { Divider, DividerVertical } from "./Divider";
4
5
  import { Spacer } from "./Spacer";
5
6
 
@@ -9,6 +10,9 @@ export interface BaseToolbarProps {
9
10
  right?: React.ReactNode;
10
11
  logo?: React.ReactNode;
11
12
  showDivider?: boolean;
13
+ className?: string;
14
+ innerClassName?: string;
15
+ enableAbsoluteBreakpoint?: boolean;
12
16
  }
13
17
 
14
18
  const toolbarStyle = {
@@ -16,51 +20,80 @@ const toolbarStyle = {
16
20
  [cssVarNames.colors.inputBackground]: "transparent",
17
21
  };
18
22
 
23
+ export function BaseToolbarContainer({
24
+ children,
25
+ showDivider = true,
26
+ className,
27
+ innerClassName,
28
+ enableAbsoluteBreakpoint = true,
29
+ }: {
30
+ children: React.ReactNode;
31
+ showDivider?: boolean;
32
+ className?: string;
33
+ innerClassName?: string;
34
+ enableAbsoluteBreakpoint?: boolean;
35
+ }) {
36
+ return (
37
+ <div className={cx("n-flex n-flex-col", className)} style={toolbarStyle}>
38
+ <div
39
+ className={cx(
40
+ "n-flex n-items-center n-bg-sidebar-background n-flex-none n-relative",
41
+ enableAbsoluteBreakpoint && "n-@container/toolbar",
42
+ innerClassName
43
+ )}
44
+ style={{
45
+ flex: `0 0 ${cssVars.spacing.toolbarHeight}`,
46
+ }}
47
+ >
48
+ {children}
49
+ </div>
50
+ {showDivider && <Divider variant="strong" />}
51
+ </div>
52
+ );
53
+ }
54
+
19
55
  export function BaseToolbar({
20
56
  children,
21
57
  left,
22
58
  right,
23
59
  logo,
24
60
  showDivider = true,
61
+ className,
62
+ innerClassName,
63
+ enableAbsoluteBreakpoint = true,
25
64
  }: BaseToolbarProps) {
26
65
  return (
27
- <div className="flex flex-col" style={toolbarStyle}>
66
+ <BaseToolbarContainer
67
+ showDivider={showDivider}
68
+ className={className}
69
+ innerClassName={innerClassName}
70
+ enableAbsoluteBreakpoint={enableAbsoluteBreakpoint}
71
+ >
72
+ {logo && (
73
+ <>
74
+ {logo}
75
+ <DividerVertical />
76
+ </>
77
+ )}
78
+ <Spacer.Horizontal size={10} />
79
+ {left && (
80
+ <>
81
+ <div className="n-flex n-gap-toolbar-separator">{left}</div>
82
+ <Spacer.Horizontal size={10} />
83
+ </>
84
+ )}
28
85
  <div
29
- className="flex items-center pr-2.5 bg-sidebar-background flex-none relative @container/toolbar"
30
- style={{
31
- flex: `0 0 ${cssVars.spacing.toolbarHeight}`,
32
- }}
86
+ className={
87
+ "n-flex n-items-center n-justify-center n-text-text-muted n-pointer-events-none @2xl/toolbar:n-absolute n-inset-0"
88
+ }
33
89
  >
34
- {logo && (
35
- <>
36
- {logo}
37
- <DividerVertical />
38
- </>
39
- )}
40
- <Spacer.Horizontal size={10} />
41
- {left && (
42
- <>
43
- <div className="flex gap-toolbar-separator">{left}</div>
44
- <Spacer.Horizontal size={10} />
45
- </>
46
- )}
47
- <div className="flex items-center justify-center text-text-muted pointer-events-none @2xl/toolbar:absolute inset-0">
48
- <div
49
- style={{
50
- display: "flex",
51
- alignItems: "center",
52
- justifyContent: "center",
53
- pointerEvents: "all",
54
- }}
55
- >
56
- {children}
57
- </div>
90
+ <div className="n-flex n-items-center n-justify-center n-pointer-events-auto">
91
+ {children}
58
92
  </div>
59
- <Spacer.Horizontal />
60
- <Spacer.Horizontal size={10} />
61
- <div className="flex gap-toolbar-separator">{right}</div>
62
93
  </div>
63
- {showDivider && <Divider variant="strong" />}
64
- </div>
94
+ <Spacer.Horizontal />
95
+ <Spacer.Horizontal size={10} />
96
+ <div className="n-flex n-gap-toolbar-separator">{right}</div>
97
+ </BaseToolbarContainer>
65
98
  );
66
99
  }
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { forwardRef } from "react";
2
4
 
3
5
  import React from "react";
@@ -19,9 +21,9 @@ export const BreadcrumbText = forwardRef(function BreadcrumbText(
19
21
  {...props}
20
22
  variant="small"
21
23
  className={cx(
22
- "whitespace-pre leading-[15px] select-none",
24
+ "n-whitespace-pre n-leading-[15px] n-select-none",
23
25
  onClick &&
24
- "hover:text-breadcrumb-text-hover hover:cursor-pointer hover:whitespace-pre hover:select-none",
26
+ "hover:n-text-breadcrumb-text-hover hover:n-cursor-pointer hover:n-whitespace-pre hover:n-select-none",
25
27
  className
26
28
  )}
27
29
  ref={ref}
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, {
2
4
  CSSProperties,
3
5
  ForwardedRef,
@@ -12,7 +14,7 @@ import { Spacer } from "./Spacer";
12
14
  import { Tooltip } from "./Tooltip";
13
15
  import type { MenuItemIcon } from "./internal/Menu";
14
16
 
15
- type ButtonVariant = "normal" | "thin" | "floating" | "none";
17
+ type ButtonVariant = "normal" | "thin" | "floating" | "none" | "ghost";
16
18
 
17
19
  type ButtonSize = "small" | "normal" | "large" | "floating";
18
20
 
@@ -20,27 +22,29 @@ type ButtonColorScheme = "primary" | "secondary";
20
22
 
21
23
  const colorSchemeStyles: Record<ButtonColorScheme | "default", string> = {
22
24
  default:
23
- "bg-button-background text-button-text hover:bg-button-background-hover active:bg-button-background-active",
24
- primary: "bg-primary text-white hover:bg-primary-light active:bg-primary",
25
+ "n-bg-button-background n-text-button-text hover:n-bg-button-background-hover active:n-bg-button-background-active",
26
+ primary:
27
+ "n-bg-primary n-text-white hover:n-bg-primary-light active:n-bg-primary",
25
28
  secondary:
26
- "bg-secondary text-white hover:bg-secondary-light active:bg-secondary",
29
+ "n-bg-secondary n-text-white hover:n-bg-secondary-light active:n-bg-secondary",
27
30
  };
28
31
 
29
32
  const variantStyles: Record<ButtonVariant, string> = {
30
33
  normal: "",
31
34
  floating:
32
- "bg-floating-button text-text shadow-[0_1px_2px_rgba(0,0,0,0.1)] hover:opacity-80 active:opacity-90",
33
- thin: "bg-transparent text-text hover:bg-input-background-light",
34
- none: "bg-transparent text-text hover:bg-input-background-light",
35
+ "n-bg-floating-button n-text-text n-shadow-[0_1px_2px_rgba(0,0,0,0.1)] hover:n-opacity-80 active:n-opacity-90",
36
+ thin: "n-bg-transparent n-text-text hover:n-bg-input-background-light",
37
+ none: "n-bg-transparent n-text-text hover:n-bg-input-background-light",
38
+ ghost: "n-bg-transparent n-text-text hover:n-bg-input-background-light",
35
39
  };
36
40
 
37
41
  const sizeStyles: Record<ButtonSize | "thin" | "none", string> = {
38
- small: "px-[2px] font-sans text-[10px] font-medium",
39
- normal: "px-[6px] py-1 font-sans text-button font-medium",
40
- large: "p-3 px-4 font-sans text-heading4 font-medium",
41
- thin: "px-[1px] py-1 font-sans text-button font-medium",
42
- floating: "px-1 py-0 font-sans text-[12px] font-medium",
43
- none: "py-0 px-0",
42
+ small: "n-px-[2px] n-font-sans n-text-[10px] n-font-medium",
43
+ normal: "n-px-[6px] n-py-1 n-font-sans n-text-button n-font-medium",
44
+ large: "n-p-3 n-px-4 n-font-sans n-text-heading4 n-font-medium",
45
+ thin: "n-px-[1px] n-py-1 n-font-sans n-text-button n-font-medium",
46
+ floating: "n-px-1 n-py-0 n-font-sans n-text-[12px] n-font-medium",
47
+ none: "n-py-0 n-px-0",
44
48
  };
45
49
 
46
50
  export interface ButtonProps
@@ -97,11 +101,12 @@ export const Button = forwardRef(function Button(
97
101
  const colorScheme = colorSchemeProp ?? "default";
98
102
 
99
103
  const baseClassName = cx(
100
- "no-underline leading-[1] relative border-0 outline-none select-none text-left rounded flex items-center justify-center [&>*]:pointer-events-none [-webkit-app-region:no-drag] focus:ring-2 focus:ring-primary focus:shadow-[0_0_0_1px_var(--n-popover-background)_inset] transition-all",
101
- disabled ? "opacity-25" : "",
102
- !className?.includes("flex") ? "flex-none" : "",
104
+ "n-no-underline n-leading-[1] n-relative n-outline-none n-select-none n-text-left n-rounded n-flex n-items-center n-justify-center [&>*]:n-pointer-events-none n-[-webkit-app-region:no-drag] focus:n-ring-2 focus:n-ring-primary focus:n-shadow-[0_0_0_1px_var(--n-popover-background)_inset] n-transition-all",
105
+ variant === "ghost" ? "n-border" : "n-border-0",
106
+ disabled ? "n-opacity-25" : "",
107
+ !className?.includes("flex") ? "n-flex-none" : "",
103
108
  active
104
- ? "bg-button-background-active text-button-text-active hover:bg-button-background-active-hover"
109
+ ? "n-bg-button-background-active n-text-button-text-active hover:n-bg-button-background-active-hover"
105
110
  : variantStyles[variant],
106
111
  sizeStyles[
107
112
  variant === "thin"
@@ -112,9 +117,11 @@ export const Button = forwardRef(function Button(
112
117
  ? "none"
113
118
  : size
114
119
  ],
115
- variant === "normal" && (children ? "min-w-[31px]" : "min-w-[27px]"),
116
- colorSchemeStyles[colorScheme],
117
- !children && (icon || iconRight) && !(icon && iconRight) && "w-[27px]"
120
+ variant === "normal" && (children ? "n-min-w-[31px]" : "n-min-w-[27px]"),
121
+ variant === "ghost"
122
+ ? "n-border-solid n-border-input-background hover:n-bg-input-background-light"
123
+ : colorSchemeStyles[colorScheme],
124
+ !children && (icon || iconRight) && !(icon && iconRight) && "n-w-[27px]"
118
125
  );
119
126
 
120
127
  const buttonClassName = useMemo(() => {
@@ -140,7 +147,7 @@ export const Button = forwardRef(function Button(
140
147
  {...rest}
141
148
  >
142
149
  <span
143
- className="min-h-[19px] flex items-center flex-1 justify-center leading-[15px]"
150
+ className="n-min-h-[19px] n-flex n-items-center n-flex-1 n-justify-center n-leading-[15px]"
144
151
  style={contentStyle}
145
152
  >
146
153
  {icon && renderIcon(icon)}
@@ -160,5 +167,3 @@ export const Button = forwardRef(function Button(
160
167
  buttonElement
161
168
  );
162
169
  });
163
-
164
- export default Button;
@@ -8,9 +8,9 @@ interface CheckboxProps
8
8
  }
9
9
 
10
10
  const colorSchemeStyles = {
11
- normal: "text-text-subtle",
12
- primary: "text-primary",
13
- secondary: "text-secondary",
11
+ normal: "n-text-text-subtle",
12
+ primary: "n-text-primary",
13
+ secondary: "n-text-secondary",
14
14
  };
15
15
 
16
16
  export const Checkbox = React.memo(
@@ -22,7 +22,7 @@ export const Checkbox = React.memo(
22
22
  return (
23
23
  <div
24
24
  className={cx(
25
- "flex-none relative inline-flex w-[19px] h-[19px]",
25
+ "n-flex-none n-relative n-inline-flex n-w-[19px] n-h-[19px]",
26
26
  colorSchemeStyles[colorScheme],
27
27
  className
28
28
  )}
@@ -31,42 +31,13 @@ export const Checkbox = React.memo(
31
31
  <input
32
32
  ref={ref}
33
33
  type="checkbox"
34
- className={`
35
- w-full
36
- h-full
37
- peer
38
- appearance-none
39
- m-0
40
- rounded
41
- bg-input-background
42
- border
43
- border-divider
44
- disabled:opacity-50
45
- cursor-pointer
46
- disabled:cursor-not-allowed
47
- focus:ring-2
48
- focus:ring-primary
49
- focus:ring-offset-1
50
- focus:z-interactable
51
- transition-all
52
- `}
34
+ className={`n-w-full n-h-full n-peer n-appearance-none n-m-0 n-rounded n-bg-input-background n-border n-border-divider disabled:n-opacity-50 n-cursor-pointer disabled:n-cursor-not-allowed focus:n-ring-2 focus:n-ring-primary focus:n-ring-offset-1 focus:n-z-interactable n-transition-all`}
53
35
  aria-checked={props.checked}
54
36
  {...props}
55
37
  id={id}
56
38
  />
57
39
  <svg
58
- className={`
59
- absolute
60
- inset-0
61
- h-input-height
62
- w-input-height
63
- pointer-events-none
64
- stroke-white
65
- opacity-0
66
- peer-checked:opacity-100
67
- z-label
68
- stroke-[1.3px]
69
- `}
40
+ className={`n-absolute n-inset-0 n-h-input-height n-w-input-height n-pointer-events-none n-stroke-white n-opacity-0 peer-checked:n-opacity-100 n-z-label stroke-[1.3px]`}
70
41
  viewBox="0 0 16 16"
71
42
  fill="none"
72
43
  xmlns="http://www.w3.org/2000/svg"
@@ -1,67 +1,81 @@
1
+ "use client";
2
+
1
3
  import { Cross1Icon, PlusIcon } from "@noya-app/noya-icons";
2
4
  import React, { forwardRef, memo } from "react";
3
5
  import { useHover } from "../hooks/useHover";
4
6
  import { cssVars } from "../theme";
5
7
  import { cx } from "../utils/classNames";
6
8
 
7
- type ChipColorScheme = "primary" | "secondary" | "error";
8
- type ChipSize = "small" | "medium" | "large";
9
+ type ChipColorScheme = "primary" | "secondary" | "info" | "error";
10
+ type ChipSize = "small" | "medium" | "large" | "xl";
9
11
  type ChipVariant = "solid" | "outlined" | "ghost";
10
12
 
11
13
  // Hoisted constants for styles
12
14
  const BASE_STYLES =
13
- "inline-flex items-center whitespace-pre rounded select-none leading-[15px]";
15
+ "n-inline-flex n-items-center n-whitespace-pre n-rounded n-select-none n-leading-[15px]";
14
16
 
15
17
  const SIZE_STYLES = {
16
- large: "text-[11px] p-[4px_8px]",
17
- medium: "text-[10px] p-[2px_6px]",
18
- small: "text-[9px] p-[0px_4px]",
18
+ xl: "n-text-[12px] n-p-[6px_12px]",
19
+ large: "n-text-[11px] n-p-[4px_8px]",
20
+ medium: "n-text-[10px] n-p-[2px_6px]",
21
+ small: "n-text-[9px] n-p-[0px_4px]",
19
22
  } as const;
20
23
 
21
24
  const SHADOW_STYLES = {
22
- primary: "shadow-[0_0_0_1px_var(--n-chip-primary-shadow)_inset]",
23
- secondary: "shadow-[0_0_0_1px_var(--n-chip-secondary-shadow)_inset]",
24
- error: "shadow-[0_0_0_1px_var(--n-chip-error-shadow)_inset]",
25
- default: "shadow-[0_0_0_1px_var(--n-chip-default-shadow)_inset]",
25
+ primary: "n-shadow-[0_0_0_1px_var(--n-chip-primary-shadow)_inset]",
26
+ secondary: "n-shadow-[0_0_0_1px_var(--n-chip-secondary-shadow)_inset]",
27
+ info: "n-shadow-[0_0_0_1px_var(--n-chip-default-shadow)_inset]",
28
+ error: "n-shadow-[0_0_0_1px_var(--n-chip-error-shadow)_inset]",
29
+ default: "n-shadow-[0_0_0_1px_var(--n-chip-default-shadow)_inset]",
26
30
  } as const;
27
31
 
28
32
  const COLOR_STYLES = {
29
33
  primary: {
30
- text: "text-primary",
31
- bg: "bg-primary-pastel",
32
- subtle: "bg-[var(--n-chip-primary-bg)]",
34
+ text: "n-text-primary",
35
+ bg: "n-bg-primary-pastel",
36
+ subtle: "n-bg-[var(--n-chip-primary-bg)]",
33
37
  },
34
38
  secondary: {
35
- text: "text-secondary",
36
- bg: "bg-secondary-pastel",
37
- subtle: "bg-[var(--n-chip-secondary-bg)]",
39
+ text: "n-text-secondary",
40
+ bg: "n-bg-secondary-pastel",
41
+ subtle: "n-bg-[var(--n-chip-secondary-bg)]",
42
+ },
43
+ info: {
44
+ text: "n-text-[#4071e8]",
45
+ bg: "n-bg-[#dce8ff]",
46
+ subtle: "n-bg-[var(--n-banner-info-border)]",
38
47
  },
39
48
  error: {
40
- text: "text-text",
41
- bg: "bg-[rgb(255,219,219)]",
42
- subtle: "bg-[var(--n-chip-error-bg)]",
49
+ text: "n-text-text",
50
+ bg: "n-bg-[rgb(255,219,219)]",
51
+ subtle: "n-bg-[var(--n-chip-error-bg)]",
43
52
  },
44
53
  default: {
45
- text: "text-text",
46
- bg: "bg-input-background",
47
- subtle: "bg-[var(--n-chip-default-bg)]",
54
+ text: "n-text-text",
55
+ bg: "n-bg-input-background",
56
+ subtle: "n-bg-[var(--n-chip-default-bg)]",
48
57
  },
49
58
  } as const;
50
59
 
51
- const ICON_BASE_STYLES = "relative opacity-50 cursor-pointer hover:opacity-85";
60
+ const ICON_BASE_STYLES =
61
+ "n-relative n-opacity-50 n-cursor-pointer hover:n-opacity-85";
52
62
 
53
63
  const ICON_SIZE_STYLES = {
64
+ xl: {
65
+ add: "n-ml-[-2px] n-mr-[2px] -n-top-[1px] n-scale-[0.75]",
66
+ delete: "n-mr-[-2px] n-ml-[2px] n-scale-[0.75]",
67
+ },
54
68
  large: {
55
- add: "ml-[-2px] mr-[2px] -top-[1px] scale-[0.75]",
56
- delete: "mr-[-2px] ml-[2px] scale-[0.75]",
69
+ add: "n-ml-[-2px] n-mr-[2px] -n-top-[1px] n-scale-[0.75]",
70
+ delete: "n-mr-[-2px] n-ml-[2px] n-scale-[0.75]",
57
71
  },
58
72
  medium: {
59
- add: "ml-[-2px] mr-[-2px] scale-[0.60]",
60
- delete: "mr-[-2px] scale-[0.60]",
73
+ add: "n-ml-[-2px] n-mr-[-2px] n-scale-[0.60]",
74
+ delete: "n-mr-[-2px] n-scale-[0.60]",
61
75
  },
62
76
  small: {
63
- add: "ml-[-2px] mr-[-2px] scale-[0.60]",
64
- delete: "mr-[-2px] scale-[0.60]",
77
+ add: "n-ml-[-2px] n-mr-[-2px] n-scale-[0.60]",
78
+ delete: "n-mr-[-2px] n-scale-[0.60]",
65
79
  },
66
80
  } as const;
67
81
 
@@ -121,16 +135,16 @@ export const Chip = memo(
121
135
  variant === "solid"
122
136
  ? `${colors.text} ${colors.bg}`
123
137
  : variant === "outlined"
124
- ? `${colors.text} bg-transparent ${SHADOW_STYLES[colorScheme ?? "default"]} hover:${colors.subtle} hover:shadow-none`
138
+ ? `${colors.text} n-bg-transparent ${SHADOW_STYLES[colorScheme ?? "default"]} hover:${colors.subtle} hover:n-shadow-none`
125
139
  : "";
126
140
 
127
141
  const chipClasses = cx(
128
142
  BASE_STYLES,
129
143
  SIZE_STYLES[size],
130
- monospace && "font-mono",
144
+ monospace && "n-font-mono",
131
145
  variantClasses,
132
- isInteractive && "cursor-pointer hover:opacity-85",
133
- disabled && "opacity-50",
146
+ isInteractive && "n-cursor-pointer hover:n-opacity-85",
147
+ disabled && "n-opacity-50",
134
148
  className
135
149
  );
136
150
 
@@ -1,13 +1,14 @@
1
+ "use client";
2
+
3
+ import { Size } from "@noya-app/noya-geometry";
1
4
  import { forwardRefGeneric } from "@noya-app/react-utils";
2
- import React from "react";
3
- import { Grid } from "../components/Grid";
4
- import { MenuItem } from "../components/internal/Menu";
5
- import {
6
- ListView,
7
- ListViewRootProps,
8
- ListViewRowProps,
9
- } from "../components/ListView";
5
+ import React, { CSSProperties } from "react";
6
+ import { Grid } from "./Grid";
7
+ import { MenuItem } from "./internal/Menu";
10
8
  import { List } from "./List";
9
+ import { ListView } from "./ListView";
10
+ import { ListViewRootProps } from "./listView/ListViewRoot";
11
+ import { ListViewRowProps } from "./listView/ListViewRow";
11
12
  import { SharedDragProps } from "./sorting/DragRegistration";
12
13
 
13
14
  export type CollectionViewType = "grid" | "list";
@@ -33,8 +34,10 @@ export type CollectionRenderActionProps<T> = {
33
34
  export interface CollectionProps<T, M extends string = string> {
34
35
  className?: string;
35
36
  items: T[];
37
+ virtualized?: Size;
36
38
  getId: (item: T) => string;
37
39
  getName: (item: T) => string;
40
+ getHref?: (item: T) => string;
38
41
  getExpanded?: (item: T) => boolean | undefined;
39
42
  getPlaceholder?: (item: T) => string;
40
43
  /**
@@ -59,12 +62,16 @@ export interface CollectionProps<T, M extends string = string> {
59
62
  | "menu"
60
63
  | ((props: CollectionRenderActionProps<T>) => React.ReactNode);
61
64
  renderDetail?: (item: T, selected: boolean) => React.ReactNode;
65
+ /** Optional right-side content renderer for each item */
66
+ renderRight?: (item: T, selected: boolean) => React.ReactNode;
62
67
  /** Callback when selection changes. If not provided, selection will be disabled. */
63
68
  onSelectionChange?: (
64
69
  selectedItemIds: string[],
65
70
  event?: ListView.ClickInfo
66
71
  ) => void;
67
72
  scrollable?: boolean;
73
+ itemStyle?: CSSProperties;
74
+ itemClassName?: string;
68
75
  itemRoleDescription?: string;
69
76
  /** Position of the detail content. Defaults to 'end'. */
70
77
  detailPosition?: "end" | "below";
@@ -79,18 +86,19 @@ export interface CollectionProps<T, M extends string = string> {
79
86
  /** For testing: Override the drop indicator state with a specific item ID */
80
87
  testShowDropIndicatorId?: string;
81
88
  setExpanded?: (item: T, expanded: boolean) => void;
82
- acceptsDrop?: ListViewRootProps["acceptsDrop"];
83
- onMoveItem?: ListViewRootProps["onMoveItem"];
84
- getDropTargetParentIndex?: ListViewRootProps["getDropTargetParentIndex"];
89
+ acceptsDrop?: ListViewRootProps<T>["acceptsDrop"];
90
+ onMoveItem?: ListViewRootProps<T>["onMoveItem"];
91
+ getDropTargetParentIndex?: ListViewRootProps<T>["getDropTargetParentIndex"];
85
92
  getDepth?: (item: T) => number;
86
93
  onFilesDrop?: (event: React.DragEvent<Element>) => void;
87
94
  getRenamable?: (item: T) => boolean;
88
95
  /** Currently selected file IDs */
89
96
  selectedIds?: string[];
90
97
  viewType?: CollectionViewType;
98
+ onClick?: () => void;
91
99
  onClickItem?: (itemId: string) => void;
92
100
  onDoubleClickItem?: (itemId: string) => void;
93
- renderEmptyState?: () => React.ReactElement;
101
+ renderEmptyState?: () => React.ReactElement<any>;
94
102
  /** @default false */
95
103
  sortable?: boolean;
96
104
  dragIndicatorStyle?: ListViewRowProps<M>["dragIndicatorStyle"];
@@ -1,9 +1,16 @@
1
+ import { getContrastColorForBackground } from "@noya-app/noya-color";
2
+ import { CheckIcon } from "@noya-app/noya-icons";
1
3
  import React, { forwardRef } from "react";
2
- import { cssVars } from "../theme";
3
4
 
4
- export type ColorSwatchSize = "xsmall" | "small" | "medium" | "large";
5
+ export type ColorSwatchSize =
6
+ | "xxsmall"
7
+ | "xsmall"
8
+ | "small"
9
+ | "medium"
10
+ | "large";
5
11
 
6
- export const colorSwatchSizeMap = {
12
+ export const colorSwatchSizeMap: Record<ColorSwatchSize, number> = {
13
+ xxsmall: 15,
7
14
  xsmall: 21,
8
15
  small: 27,
9
16
  medium: 36,
@@ -14,32 +21,45 @@ type ColorSwatchProps = {
14
21
  color: string;
15
22
  size?: ColorSwatchSize;
16
23
  checked?: boolean;
24
+ as?: React.ElementType;
17
25
  } & React.ButtonHTMLAttributes<HTMLButtonElement>;
18
26
 
19
27
  export const ColorSwatch = forwardRef(function ColorSwatch(
20
- { color, size = "small", checked, style, ...props }: ColorSwatchProps,
28
+ {
29
+ color,
30
+ size = "small",
31
+ checked,
32
+ style,
33
+ as: Component = "button",
34
+ ...props
35
+ }: ColorSwatchProps,
21
36
  ref: React.ForwardedRef<HTMLButtonElement>
22
37
  ) {
23
38
  const sizePx = colorSwatchSizeMap[size];
39
+ const checkedColor = getContrastColorForBackground(color);
24
40
 
25
41
  return (
26
- <button
27
- {...props}
28
- ref={ref}
29
- style={{
30
- background: color,
31
- width: sizePx,
32
- height: sizePx,
33
- borderRadius: "4px",
34
- outline: checked
35
- ? `2px solid ${cssVars.colors.primary}`
36
- : "1px solid rgba(0,0,0,0.1)",
37
- outlineOffset: -1,
38
- boxShadow: checked
39
- ? `inset 0 0 0 2px ${cssVars.colors.background}`
40
- : undefined,
41
- ...style,
42
- }}
43
- />
42
+ <div className="n-relative" style={{ width: sizePx, height: sizePx }}>
43
+ <Component
44
+ {...props}
45
+ ref={ref}
46
+ style={{
47
+ display: "inline-block",
48
+ background: color,
49
+ width: sizePx,
50
+ height: sizePx,
51
+ borderRadius: "4px",
52
+ outline: "1px solid rgba(0,0,0,0.1)",
53
+ outlineOffset: -1,
54
+ ...style,
55
+ }}
56
+ />
57
+ {checked && (
58
+ <CheckIcon
59
+ color={checkedColor}
60
+ className="n-absolute n-top-1/2 n-left-1/2 -n-translate-x-1/2 -n-translate-y-1/2"
61
+ />
62
+ )}
63
+ </div>
44
64
  );
45
65
  });
@@ -1,4 +1,6 @@
1
- import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
1
+ "use client";
2
+
3
+ import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
2
4
  import React, { useMemo } from "react";
3
5
  import { cx } from "../utils/classNames";
4
6
  import {
@@ -52,14 +54,14 @@ export function ColorSwatchControl(props: ColorSwatchControlProps) {
52
54
  {options.map((option) => {
53
55
  const item = (
54
56
  <ColorSwatch
55
- key={option as string}
57
+ key={`${option as string}-color-swatch`}
56
58
  color={option as string}
57
59
  size={size}
58
60
  onFocus={onFocus}
59
61
  onBlur={onBlur}
60
62
  disabled={disabled}
61
63
  // TODO: decide on "checked" visual treatment
62
- className={cx(readOnly ? "cursor-default" : "cursor-pointer")}
64
+ className={cx(readOnly ? "n-cursor-default" : "n-cursor-pointer")}
63
65
  checked={value === option}
64
66
  />
65
67
  );
@@ -67,7 +69,7 @@ export function ColorSwatchControl(props: ColorSwatchControlProps) {
67
69
  <ToggleGroupPrimitive.Item
68
70
  asChild
69
71
  value={option as string}
70
- key={option as string}
72
+ key={`${option as string}-toggle-group-item`}
71
73
  disabled={readOnly}
72
74
  >
73
75
  {item}
@@ -81,7 +83,7 @@ export function ColorSwatchControl(props: ColorSwatchControlProps) {
81
83
  type="single"
82
84
  value={value as string}
83
85
  onValueChange={onValueChange}
84
- className="w-full"
86
+ className="n-w-full"
85
87
  disabled={readOnly}
86
88
  >
87
89
  {content}