@noya-app/noya-designsystem 0.1.63 → 0.1.64

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 (112) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +13 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +169 -127
  5. package/dist/index.d.ts +169 -127
  6. package/dist/index.js +3131 -2642
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +2941 -2459
  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 +5 -3
  15. package/src/components/AnimatePresence.tsx +6 -4
  16. package/src/components/Avatar.tsx +4 -2
  17. package/src/components/BaseToolbar.tsx +45 -34
  18. package/src/components/Breadcrumbs.tsx +2 -0
  19. package/src/components/Button.tsx +9 -3
  20. package/src/components/Chip.tsx +2 -0
  21. package/src/components/Collection.tsx +4 -1
  22. package/src/components/ColorSwatch.tsx +42 -22
  23. package/src/components/ColorSwatchControl.tsx +5 -3
  24. package/src/components/Combobox.tsx +2 -0
  25. package/src/components/CommandPalette.tsx +2 -0
  26. package/src/components/ContextMenu.tsx +3 -1
  27. package/src/components/Dialog.tsx +3 -1
  28. package/src/components/DimensionInput.tsx +2 -0
  29. package/src/components/Divider.tsx +10 -6
  30. package/src/components/DraggableMenuButton.tsx +2 -0
  31. package/src/components/Drawer.tsx +11 -3
  32. package/src/components/DropdownMenu.tsx +3 -1
  33. package/src/components/EditableText.tsx +2 -0
  34. package/src/components/FileUploadIndicator.tsx +108 -0
  35. package/src/components/FillPreviewBackground.tsx +2 -0
  36. package/src/components/FloatingWindow.tsx +2 -0
  37. package/src/components/GradientPicker.tsx +2 -0
  38. package/src/components/Grid.tsx +47 -38
  39. package/src/components/GridView.tsx +33 -11
  40. package/src/components/IconButton.tsx +2 -0
  41. package/src/components/Icons.tsx +1 -1
  42. package/src/components/InputField.tsx +2 -0
  43. package/src/components/Label.tsx +1 -1
  44. package/src/components/LabeledElementView.tsx +6 -2
  45. package/src/components/LabeledField.tsx +2 -0
  46. package/src/components/List.tsx +10 -2
  47. package/src/components/ListView.tsx +27 -14
  48. package/src/components/MediaThumbnail.tsx +31 -2
  49. package/src/components/Message.tsx +8 -9
  50. package/src/components/Popover.tsx +3 -1
  51. package/src/components/Progress.tsx +20 -7
  52. package/src/components/ScrollArea.tsx +3 -1
  53. package/src/components/SearchCompletionMenu.tsx +2 -0
  54. package/src/components/Section.tsx +2 -0
  55. package/src/components/SegmentedControl.tsx +42 -16
  56. package/src/components/SelectMenu.tsx +3 -1
  57. package/src/components/SelectionToolbar.tsx +3 -1
  58. package/src/components/Slider.tsx +3 -1
  59. package/src/components/Switch.tsx +3 -1
  60. package/src/components/Tabs.tsx +3 -1
  61. package/src/components/Text.tsx +6 -5
  62. package/src/components/TextArea.tsx +2 -0
  63. package/src/components/Toast.tsx +1 -1
  64. package/src/components/Toolbar.tsx +8 -2
  65. package/src/components/ToolbarDrawer.tsx +29 -0
  66. package/src/components/Tooltip.tsx +1 -1
  67. package/src/components/TreeView.tsx +2 -0
  68. package/src/components/UserPointer.tsx +2 -0
  69. package/src/components/ai-assistant/AIAssistantLayout.tsx +2 -0
  70. package/src/components/blocks/ImageBlockComponent.tsx +156 -0
  71. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +2 -0
  72. package/src/components/file-explorer/FileExplorerLayout.tsx +2 -0
  73. package/src/components/internal/Checkmark.tsx +24 -0
  74. package/src/components/internal/Menu.tsx +2 -1
  75. package/src/components/internal/MenuViewport.tsx +4 -2
  76. package/src/components/internal/SelectItem.tsx +4 -23
  77. package/src/components/internal/TextInput.tsx +2 -0
  78. package/src/components/sorting/DragRegistration.tsx +8 -4
  79. package/src/components/sorting/SharedDragProvider.tsx +43 -18
  80. package/src/components/sorting/Sortable.tsx +19 -10
  81. package/src/components/sorting/createSharedDrag.tsx +7 -6
  82. package/src/components/sorting/sorting.ts +6 -9
  83. package/src/components/workspace/DrawerWorkspaceLayout.tsx +107 -26
  84. package/src/components/workspace/PanelWorkspaceLayout.tsx +98 -65
  85. package/src/components/workspace/VerticalTabMenu.tsx +36 -0
  86. package/src/components/workspace/WorkspaceLayout.tsx +46 -42
  87. package/src/components/workspace/constants.ts +4 -0
  88. package/src/components/workspace/panelStorage.ts +216 -0
  89. package/src/components/workspace/types.ts +11 -4
  90. package/src/contexts/DesignSystemConfiguration.tsx +12 -5
  91. package/src/contexts/DialogContext.tsx +2 -0
  92. package/src/contexts/FloatingWindowContext.tsx +2 -0
  93. package/src/contexts/GlobalInputBlurContext.tsx +2 -0
  94. package/src/contexts/ImageDataContext.tsx +2 -0
  95. package/src/contexts/LinkComponentContext.tsx +17 -0
  96. package/src/contexts/OpenPortalsContext.tsx +2 -0
  97. package/src/contexts/PortalScopeContext.tsx +2 -0
  98. package/src/hooks/mergeEventHandlers.ts +1 -1
  99. package/src/hooks/useHover.ts +30 -17
  100. package/src/hooks/useIndent.ts +2 -0
  101. package/src/hooks/useLabel.ts +2 -0
  102. package/src/hooks/useObjectURL.ts +5 -3
  103. package/src/hooks/usePlatform.ts +5 -3
  104. package/src/hooks/useTheme.ts +3 -1
  105. package/src/index.css +19 -0
  106. package/src/index.tsx +5 -2
  107. package/src/utils/classNames.ts +4 -3
  108. package/src/utils/combobox.ts +2 -0
  109. package/src/utils/formatByteSize.ts +8 -0
  110. package/src/utils/withSeparatorElements.ts +1 -1
  111. package/src/hooks/usePreservePanelSize.tsx +0 -183
  112. package/src/mediaQuery.ts +0 -13
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { memoGeneric } from "@noya-app/react-utils";
2
- import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
4
+ import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
3
5
  import React, {
4
6
  createContext,
5
7
  forwardRef,
@@ -15,7 +17,7 @@ import { SelectableMenuItem } from "./internal/Menu";
15
17
  import { Tooltip } from "./Tooltip";
16
18
 
17
19
  type SegmentedControlColorScheme = "primary" | "secondary";
18
- type SegmentedControlVariant = "default" | "tabs";
20
+ type SegmentedControlVariant = "default" | "tabs" | "buttons";
19
21
 
20
22
  type SegmentedControlContextValue = {
21
23
  /** @default primary */
@@ -75,26 +77,49 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
75
77
  className={cx(
76
78
  "group",
77
79
  "flex items-center justify-center",
78
- "font-sans text-button relative flex-1 appearance-none text-segmented-control-item focus:outline-none transition-colors",
79
- variant === "tabs" ? "font-semibold" : "font-normal",
80
+ "font-sans text-button relative flex-1 appearance-none focus:outline-none transition-colors",
81
+ // text color
82
+ variant === "buttons"
83
+ ? "text-button-text"
84
+ : "text-segmented-control-item",
85
+ // font weight
86
+ variant === "tabs"
87
+ ? "font-semibold"
88
+ : variant === "buttons"
89
+ ? "font-medium"
90
+ : "font-normal",
91
+ // focus ring
80
92
  variant === "tabs"
81
93
  ? ""
82
- : colorScheme === "secondary"
83
- ? "focus:ring-2 focus:ring-secondary focus:ring-offset-1"
84
- : "focus:ring-2 focus:ring-primary focus:ring-offset-1",
94
+ : variant === "buttons"
95
+ ? "focus-visible:ring-2 focus-visible:ring-primary focus:shadow-[0_0_0_1px_var(--n-popover-background)_inset] transition-all"
96
+ : colorScheme === "secondary"
97
+ ? "focus:ring-2 focus:ring-secondary focus:ring-offset-1"
98
+ : "focus:ring-2 focus:ring-primary focus:ring-offset-1",
99
+ // checked
85
100
  variant === "tabs"
86
101
  ? "aria-checked:text-primary"
87
- : colorScheme === "secondary"
88
- ? "aria-checked:bg-secondary aria-checked:text-white"
89
- : colorScheme === "primary"
90
- ? "aria-checked:bg-primary aria-checked:text-white"
91
- : "aria-checked:bg-segmented-control-item-active-background aria-checked:text-segmented-control-item-active-text",
102
+ : variant === "buttons"
103
+ ? "active:text-button-text-active active:bg-button-background-active aria-checked:text-button-text-active aria-checked:bg-button-background-active"
104
+ : colorScheme === "secondary"
105
+ ? "aria-checked:bg-secondary aria-checked:text-white"
106
+ : colorScheme === "primary"
107
+ ? "aria-checked:bg-primary aria-checked:text-white"
108
+ : "aria-checked:bg-segmented-control-item-active-background aria-checked:text-segmented-control-item-active-text",
92
109
  "focus:z-interactable",
93
- variant === "default"
94
- ? "px-1 rounded aria-checked:shadow-segment"
95
- : "px-1.5 border-y-2 border-y-transparent aria-checked:border-b-primary",
110
+ // checked default (all colorSchemes)
96
111
  variant === "default" &&
97
- "border-r border-divider last:border-r-0 aria-checked:border-r-transparent [&:has(+[aria-checked=true])]:border-r-transparent",
112
+ "aria-checked:border-r-transparent [&:has(+[aria-checked=true])]:border-r-transparent aria-checked:shadow-segment",
113
+ // hover
114
+ variant === "buttons" && "hover:bg-button-background-hover",
115
+ // spacing and borders
116
+ variant === "default"
117
+ ? "px-1 rounded border-r border-divider last:border-r-0"
118
+ : variant === "buttons"
119
+ ? "px-1 rounded-sm max-w-fit"
120
+ : "px-1.5 border-y-2 border-y-transparent aria-checked:border-b-primary",
121
+ // icon only buttons
122
+ variant === "buttons" && title === "" && icon && "min-w-input-height",
98
123
  className
99
124
  )}
100
125
  {...props}
@@ -155,6 +180,7 @@ export const SegmentedControl = memoGeneric(function SegmentedControl<
155
180
  variant === "default" &&
156
181
  "grid min-h-input-height bg-input-background rounded py-0.5 px-0.5",
157
182
  variant === "tabs" && "flex gap-1.5 min-h-[33px]",
183
+ variant === "buttons" && "flex gap-1.5 min-h-input-height",
158
184
  className
159
185
  )}
160
186
  style={{
@@ -1,10 +1,12 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  ChevronDownIcon,
3
5
  ChevronUpIcon,
4
6
  DropdownChevronIcon,
5
7
  } from "@noya-app/noya-icons";
6
8
  import { memoGeneric } from "@noya-app/react-utils";
7
- import * as Select from "@radix-ui/react-select";
9
+ import { Select } from "radix-ui";
8
10
  import { type SelectProps } from "@radix-ui/react-select";
9
11
  import React, {
10
12
  CSSProperties,
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  cssVarNames,
3
5
  cx,
@@ -76,7 +78,7 @@ export const SelectionToolbarContainer = memo(
76
78
  collisionPadding={10}
77
79
  contentEditable={false}
78
80
  style={{
79
- zIndex: 1000,
81
+ zIndex: 10,
80
82
  opacity: size ? 1 : 0,
81
83
  transition: `opacity ${transitionDuration}s ease-in-out`,
82
84
  [cssVarNames.colors.inputBackground]: "transparent",
@@ -1,4 +1,6 @@
1
- import * as RadixSlider from "@radix-ui/react-slider";
1
+ "use client";
2
+
3
+ import { Slider as RadixSlider } from "radix-ui";
2
4
  import React, { FocusEventHandler, memo, useCallback, useMemo } from "react";
3
5
  import { useLabel } from "../hooks/useLabel";
4
6
  import { cx } from "../utils/classNames";
@@ -1,4 +1,6 @@
1
- import * as SwitchPrimitive from "@radix-ui/react-switch";
1
+ "use client";
2
+
3
+ import { Switch as SwitchPrimitive } from "radix-ui";
2
4
  import * as React from "react";
3
5
  import { cx } from "../utils/classNames";
4
6
 
@@ -11,6 +11,7 @@ export type TabsProps<T extends string = string> = {
11
11
  onValueChange?: (value: T) => void;
12
12
  items: MenuItem<T>[];
13
13
  renderContent: ({ value }: { value: T }) => React.ReactNode;
14
+ variant?: "default" | "tabs" | "buttons";
14
15
  } & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
15
16
 
16
17
  export function Tabs<T extends string = string>({
@@ -18,6 +19,7 @@ export function Tabs<T extends string = string>({
18
19
  onValueChange,
19
20
  items,
20
21
  renderContent,
22
+ variant = "tabs",
21
23
  ...props
22
24
  }: TabsProps<T>) {
23
25
  const selectableItems = items.filter(isSelectableMenuItem);
@@ -35,7 +37,7 @@ export function Tabs<T extends string = string>({
35
37
  className="flex-1"
36
38
  value={activeTab}
37
39
  onValueChange={setActiveTab}
38
- variant="tabs"
40
+ variant={variant}
39
41
  items={selectableItems}
40
42
  />
41
43
  </div>
@@ -1,4 +1,6 @@
1
- import React, { ForwardedRef, forwardRef, ReactHTML, ReactNode } from "react";
1
+ "use client";
2
+
3
+ import React, { ForwardedRef, forwardRef, ReactNode } from "react";
2
4
  import { ThemeColor } from "../theme";
3
5
  import { camelToKebabCase, KebabToCamelCase } from "../theme/themeUtils";
4
6
  import { cx } from "../utils/classNames";
@@ -17,7 +19,7 @@ type Variant =
17
19
  | "code"
18
20
  | "label";
19
21
 
20
- const elements: Record<Variant, keyof ReactHTML> = {
22
+ const elements: Record<Variant, React.HTMLElementType> = {
21
23
  title: "h1",
22
24
  subtitle: "h1",
23
25
  heading1: "h1",
@@ -48,7 +50,7 @@ export const textStyles: Record<Variant, string> = {
48
50
  } as const;
49
51
 
50
52
  export type TextProps = {
51
- as?: keyof ReactHTML;
53
+ as?: React.HTMLElementType;
52
54
  href?: string;
53
55
  className?: string;
54
56
  style?: React.CSSProperties;
@@ -78,11 +80,10 @@ export const Text = forwardRef(function Text(
78
80
  }: TextProps,
79
81
  forwardedRef: ForwardedRef<HTMLElement>
80
82
  ) {
81
- const Component = as ?? elements[variant] ?? "span";
83
+ const Component = (as ?? elements[variant] ?? "span") as "span";
82
84
 
83
85
  return (
84
86
  <Component
85
- // @ts-expect-error expected to be many semantic html elements
86
87
  ref={forwardedRef}
87
88
  className={cx(
88
89
  `${textStyles[variant]} text-${camelToKebabCase(String(color)) ?? "text"} `,
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { assignRef } from "@noya-app/react-utils";
2
4
  import React, {
3
5
  ComponentProps,
@@ -1,4 +1,4 @@
1
- import * as ToastPrimitive from "@radix-ui/react-toast";
1
+ import { Toast as ToastPrimitive } from "radix-ui";
2
2
  import React, { ReactNode } from "react";
3
3
  import { IconButton } from "./IconButton";
4
4
  import { textStyles } from "./Text";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
2
4
  import React, { ComponentProps, ReactNode } from "react";
3
5
 
@@ -17,6 +19,7 @@ import {
17
19
  } from "./internal/Menu";
18
20
  import { Popover } from "./Popover";
19
21
  import { Spacer } from "./Spacer";
22
+ import { ToolbarDrawer } from "./ToolbarDrawer";
20
23
  import { Tooltip } from "./Tooltip";
21
24
 
22
25
  const SIDE_OFFSET = 6;
@@ -144,11 +147,12 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
144
147
  activeValue?: T;
145
148
  tooltipSide?: ComponentProps<typeof Tooltip>["side"];
146
149
  }) {
150
+ const isActive = item.checked || activeValue === item.value;
147
151
  const content = (
148
152
  <Button
149
153
  as={as}
150
154
  disabled={item.disabled}
151
- active={item.checked || activeValue === item.value}
155
+ active={isActive}
152
156
  icon={item.icon}
153
157
  {...(as === "a" && { href: item.value })}
154
158
  onClick={() => {
@@ -161,7 +165,9 @@ export const ToolbarMenuButton = memoGeneric(function ToolbarMenuButton<
161
165
  </Button>
162
166
  );
163
167
 
164
- return item.tooltip ? (
168
+ return item.drawer && isActive ? (
169
+ <ToolbarDrawer trigger={content}>{item.drawer}</ToolbarDrawer>
170
+ ) : item.tooltip ? (
165
171
  <Tooltip sideOffset={SIDE_OFFSET} content={item.tooltip} side={tooltipSide}>
166
172
  {content}
167
173
  </Tooltip>
@@ -0,0 +1,29 @@
1
+ import React, { ForwardedRef, forwardRef, memo, ReactNode } from "react";
2
+
3
+ type ToolbarDrawerProps = {
4
+ children: ReactNode;
5
+ trigger: ReactNode;
6
+ sideOffset?: number;
7
+ };
8
+
9
+ export const ToolbarDrawer = memo(
10
+ forwardRef(function ToolbarDrawer(
11
+ { children, trigger, sideOffset = 8 }: ToolbarDrawerProps,
12
+ ref: ForwardedRef<HTMLDivElement>
13
+ ) {
14
+ return (
15
+ <div className="relative" ref={ref}>
16
+ <div
17
+ className="absolute bottom-full left-1/2 -translate-x-1/2 rounded-t-lg border border-toolbar-drawer-border border-b-transparent bg-toolbar-drawer-background -z-10"
18
+ style={{
19
+ marginBottom: `${sideOffset}px`,
20
+ boxShadow: "0px -2px 12px 0px var(--n-toolbar-drawer-shadow)",
21
+ }}
22
+ >
23
+ {children}
24
+ </div>
25
+ {trigger}
26
+ </div>
27
+ );
28
+ })
29
+ );
@@ -1,4 +1,4 @@
1
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
1
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
2
2
  import * as React from "react";
3
3
  import { ComponentProps } from "react";
4
4
  import {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { forwardRefGeneric, memoGeneric } from "@noya-app/react-utils";
2
4
  import React, { ForwardedRef, ReactNode, useCallback, useContext } from "react";
3
5
  import { IconButton } from "./IconButton";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, { memo, useMemo } from "react";
2
4
  import { cx } from "../utils/classNames";
3
5
  import { colorFromString } from "../utils/colorFromString";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { IconButton } from "../IconButton";
2
4
 
3
5
  import { cssVars } from "../../theme";
@@ -0,0 +1,156 @@
1
+ import { ActivityIndicator, cx } from "@noya-app/noya-designsystem";
2
+ import { ImageIcon } from "@noya-app/noya-icons";
3
+ import React, { ForwardedRef, forwardRef } from "react";
4
+
5
+ type ImageState = "loading" | "error" | "loaded";
6
+
7
+ const placeholderClassName =
8
+ "cursor-pointer flex items-center justify-center p-4 border border-dashed border-indigo-100 rounded-md bg-indigo-50";
9
+
10
+ export function decodeImageSrc(src?: string): string | undefined {
11
+ if (!src) return undefined;
12
+
13
+ try {
14
+ return decodeURIComponent(src);
15
+ } catch (error) {
16
+ return undefined;
17
+ }
18
+ }
19
+
20
+ const ImageLoadingComponent = forwardRef(function ImageLoadingComponent(
21
+ props: React.HTMLAttributes<HTMLSpanElement>,
22
+ forwardedRef: ForwardedRef<HTMLSpanElement>
23
+ ) {
24
+ return (
25
+ <span
26
+ {...props}
27
+ ref={forwardedRef}
28
+ contentEditable={false}
29
+ className={cx(placeholderClassName, props.className)}
30
+ >
31
+ <ActivityIndicator />
32
+ </span>
33
+ );
34
+ });
35
+
36
+ const ImagePlaceholderComponent = forwardRef(function ImagePlaceholderComponent(
37
+ props: React.HTMLAttributes<HTMLSpanElement>,
38
+ forwardedRef: ForwardedRef<HTMLSpanElement>
39
+ ) {
40
+ return (
41
+ <span
42
+ {...props}
43
+ ref={forwardedRef}
44
+ contentEditable={false}
45
+ className={cx(placeholderClassName, props.className)}
46
+ >
47
+ <ImageIcon className="w-[30px] h-[30px] text-indigo-900 pointer-events-none" />
48
+ </span>
49
+ );
50
+ });
51
+
52
+ const ImageErrorComponent = forwardRef(function ImageErrorComponent(
53
+ { src, ...props }: React.HTMLAttributes<HTMLSpanElement> & { src?: string },
54
+ forwardedRef: ForwardedRef<HTMLSpanElement>
55
+ ) {
56
+ return (
57
+ <span
58
+ {...props}
59
+ ref={forwardedRef}
60
+ contentEditable={false}
61
+ className={cx(placeholderClassName, props.className)}
62
+ >
63
+ <span className="text-cm-error font-mono text-sm">
64
+ Invalid image URL: {src ?? "No URL provided"}
65
+ </span>
66
+ </span>
67
+ );
68
+ });
69
+
70
+ const ImageDisplayComponent = forwardRef(function ImageDisplayComponent(
71
+ {
72
+ src,
73
+ alt,
74
+ title,
75
+ ...props
76
+ }: React.DetailedHTMLProps<
77
+ React.ImgHTMLAttributes<HTMLImageElement>,
78
+ HTMLImageElement
79
+ >,
80
+ forwardedRef: ForwardedRef<HTMLImageElement>
81
+ ) {
82
+ return (
83
+ <img
84
+ {...props}
85
+ ref={forwardedRef}
86
+ src={src}
87
+ alt={alt ?? ""}
88
+ title={title ?? undefined}
89
+ className={cx(
90
+ "cursor-pointer max-w-full max-h-[720px] object-contain mx-auto my-8",
91
+ props.className
92
+ )}
93
+ />
94
+ );
95
+ });
96
+
97
+ const ImageBlockComponent_ = forwardRef(function ImageBlockComponent(
98
+ {
99
+ src,
100
+ alt,
101
+ title,
102
+ imageState,
103
+ ...props
104
+ }: React.DetailedHTMLProps<
105
+ React.ImgHTMLAttributes<HTMLImageElement>,
106
+ HTMLImageElement
107
+ > & { imageState?: ImageState },
108
+ forwardedRef: ForwardedRef<HTMLImageElement>
109
+ ) {
110
+ if (imageState === "loading") {
111
+ return (
112
+ <ImageLoadingComponent
113
+ {...props}
114
+ ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
115
+ />
116
+ );
117
+ }
118
+
119
+ if (!src) {
120
+ return (
121
+ <ImagePlaceholderComponent
122
+ {...props}
123
+ ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
124
+ />
125
+ );
126
+ }
127
+
128
+ const decodedSrc = decodeImageSrc(src.toString());
129
+
130
+ if (!decodedSrc || imageState === "error") {
131
+ return (
132
+ <ImageErrorComponent
133
+ {...props}
134
+ src={src.toString()}
135
+ ref={forwardedRef as ForwardedRef<HTMLSpanElement>}
136
+ />
137
+ );
138
+ }
139
+
140
+ return (
141
+ <ImageDisplayComponent
142
+ {...props}
143
+ ref={forwardedRef}
144
+ src={src}
145
+ alt={alt}
146
+ title={title}
147
+ />
148
+ );
149
+ });
150
+
151
+ export const ImageBlockComponent = Object.assign(ImageBlockComponent_, {
152
+ Loading: ImageLoadingComponent,
153
+ Placeholder: ImagePlaceholderComponent,
154
+ Error: ImageErrorComponent,
155
+ Display: ImageDisplayComponent,
156
+ });
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React from "react";
2
4
  import { INPUT_HEIGHT } from "../../theme";
3
5
  import { Avatar, AvatarProps, AvatarStack } from "../Avatar";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  ActivityIndicator,
3
5
  Banner,
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+
3
+ export function Checkmark(props: React.SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg
6
+ viewBox="0 0 15 15"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ aria-hidden="true"
10
+ width={15}
11
+ height={15}
12
+ {...props}
13
+ >
14
+ <path
15
+ className="scale-90 origin-center"
16
+ d="M3 8L6 11L12 5"
17
+ strokeWidth="1.1"
18
+ strokeLinecap="round"
19
+ strokeLinejoin="round"
20
+ stroke="currentColor"
21
+ />
22
+ </svg>
23
+ );
24
+ }
@@ -68,7 +68,7 @@ export const separator: SeparatorItem = {
68
68
 
69
69
  type FalsyReactNode = false | "" | null | undefined;
70
70
 
71
- export type MenuItemIcon = IconName | React.ReactElement | FalsyReactNode;
71
+ export type MenuItemIcon = IconName | React.ReactElement<any> | FalsyReactNode;
72
72
 
73
73
  type BaseMenuItem = {
74
74
  checked?: boolean;
@@ -93,6 +93,7 @@ export type SelectableMenuItem<T extends string> = BaseMenuItem & {
93
93
  value: T;
94
94
  testSelected?: boolean;
95
95
  tooltip?: ReactNode;
96
+ drawer?: ReactNode;
96
97
  };
97
98
 
98
99
  export type SubMenuItem<T extends string> = BaseMenuItem & {
@@ -1,6 +1,8 @@
1
+ "use client";
2
+
1
3
  import { ChevronRightIcon } from "@noya-app/noya-icons";
2
- import * as RadixContextMenu from "@radix-ui/react-context-menu";
3
- import * as RadixDropdownMenu from "@radix-ui/react-dropdown-menu";
4
+ import { ContextMenu as RadixContextMenu } from "radix-ui";
5
+ import { DropdownMenu as RadixDropdownMenu } from "radix-ui";
4
6
  import React from "react";
5
7
  import {
6
8
  portalScopeProps,
@@ -1,9 +1,12 @@
1
+ "use client";
2
+
1
3
  import { renderIcon } from "../Icons";
2
4
 
3
- import * as Select from "@radix-ui/react-select";
5
+ import { Select } from "radix-ui";
4
6
  import React, { useCallback } from "react";
5
7
  import { cx } from "../../utils/classNames";
6
8
  import { Spacer } from "../Spacer";
9
+ import { Checkmark } from "./Checkmark";
7
10
  import {
8
11
  CHECKBOX_INDENT_WIDTH,
9
12
  CHECKBOX_RIGHT_INSET,
@@ -133,25 +136,3 @@ export const SelectItem = React.forwardRef(
133
136
  );
134
137
  }
135
138
  );
136
-
137
- function Checkmark() {
138
- return (
139
- <svg
140
- viewBox="0 0 15 15"
141
- fill="none"
142
- xmlns="http://www.w3.org/2000/svg"
143
- aria-hidden="true"
144
- width={15}
145
- height={15}
146
- >
147
- <path
148
- className="scale-90 origin-center"
149
- d="M3 8L6 11L12 5"
150
- strokeWidth="1.1"
151
- strokeLinecap="round"
152
- strokeLinejoin="round"
153
- stroke="currentColor"
154
- />
155
- </svg>
156
- );
157
- }
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { composeRefs } from "@radix-ui/react-compose-refs";
2
4
  import React, {
3
5
  AriaRole,
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  AcceptsDrop,
3
5
  DragState,
@@ -5,7 +7,7 @@ import {
5
7
  RelativeDropPosition,
6
8
  } from "./sorting";
7
9
 
8
- import { Active, Over, UniqueIdentifier } from "@dnd-kit/core";
10
+ import { ClientRect, UniqueIdentifier } from "@dnd-kit/core";
9
11
  import { AcceptsFromList } from "./sorting";
10
12
 
11
13
  import { Point } from "@noya-app/noya-geometry";
@@ -13,8 +15,9 @@ import React from "react";
13
15
 
14
16
  export type GetDropIndicatorParameters = {
15
17
  absolutePosition: Point;
16
- active: Active;
17
- over: Over;
18
+ activeIndex: number;
19
+ overIndex: number;
20
+ overRect: ClientRect;
18
21
  sourceListId: string;
19
22
  targetListId: string;
20
23
  };
@@ -27,7 +30,8 @@ export interface RegisteredList {
27
30
  acceptsFromList: AcceptsFromList;
28
31
  acceptsDrop: AcceptsDrop;
29
32
  getDropIndicator: (
30
- parameters: GetDropIndicatorParameters
33
+ parameters: GetDropIndicatorParameters,
34
+ acceptsDrop: AcceptsDrop
31
35
  ) => RelativeDropPosition | undefined;
32
36
  }
33
37