@noya-app/noya-designsystem 0.1.30 → 0.1.32

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 (64) hide show
  1. package/.turbo/turbo-build.log +20 -12
  2. package/.turbo/turbo-lint.log +15 -1
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +13 -1
  5. package/dist/index.css +1 -0
  6. package/dist/index.d.ts +238 -627
  7. package/dist/index.js +4259 -2862
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +4159 -2764
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +17 -7
  12. package/src/components/ActivityIndicator.tsx +4 -36
  13. package/src/components/Avatar.tsx +63 -62
  14. package/src/components/Button.tsx +53 -170
  15. package/src/components/Chip.tsx +117 -150
  16. package/src/components/ContextMenu.tsx +13 -35
  17. package/src/components/Dialog.tsx +66 -54
  18. package/src/components/Divider.tsx +31 -41
  19. package/src/components/DraggableMenuButton.tsx +29 -30
  20. package/src/components/DropdownMenu.tsx +30 -40
  21. package/src/components/FillInputField.tsx +16 -26
  22. package/src/components/FillPreviewBackground.tsx +18 -22
  23. package/src/components/FloatingWindow.tsx +4 -7
  24. package/src/components/GridView.tsx +70 -200
  25. package/src/components/IconButton.tsx +1 -5
  26. package/src/components/InputField.tsx +258 -234
  27. package/src/components/InputFieldWithCompletions.tsx +20 -27
  28. package/src/components/InspectorContainer.tsx +4 -9
  29. package/src/components/InspectorPrimitives.tsx +135 -109
  30. package/src/components/Label.tsx +5 -36
  31. package/src/components/LabeledElementView.tsx +5 -26
  32. package/src/components/ListView.tsx +239 -167
  33. package/src/components/Popover.tsx +15 -39
  34. package/src/components/Progress.tsx +21 -44
  35. package/src/components/RadioGroup.tsx +6 -71
  36. package/src/components/ScrollArea.tsx +11 -39
  37. package/src/components/SelectMenu.tsx +72 -32
  38. package/src/components/Slider.tsx +7 -43
  39. package/src/components/Spacer.tsx +6 -18
  40. package/src/components/Switch.tsx +4 -42
  41. package/src/components/Text.tsx +31 -66
  42. package/src/components/TextArea.tsx +5 -31
  43. package/src/components/Toast.tsx +15 -57
  44. package/src/components/Tooltip.tsx +4 -13
  45. package/src/components/WorkspaceLayout.tsx +27 -17
  46. package/src/components/internal/Menu.tsx +37 -83
  47. package/src/contexts/DesignSystemConfiguration.tsx +1 -47
  48. package/src/contexts/DialogContext.tsx +2 -10
  49. package/src/hooks/useDarkMode.ts +14 -0
  50. package/src/index.css +108 -0
  51. package/src/index.tsx +4 -5
  52. package/src/theme/index.ts +4 -16
  53. package/src/utils/tailwind.ts +17 -0
  54. package/tailwind.config.ts +223 -0
  55. package/tailwind.d.ts +11 -0
  56. package/tsconfig.json +4 -1
  57. package/tsup.config.ts +16 -0
  58. package/dist/index.d.mts +0 -1455
  59. package/src/components/Grid.tsx +0 -54
  60. package/src/components/Select.tsx +0 -183
  61. package/src/components/Stack.tsx +0 -155
  62. package/src/theme/dark.ts +0 -45
  63. package/src/theme/light.ts +0 -226
  64. package/src/utils/breakpoints.ts +0 -45
@@ -1,29 +1,30 @@
1
1
  import { DragHandleDots2Icon } from "@noya-app/noya-icons";
2
- import React, { memo, useCallback, useState } from "react";
3
- import styled from "styled-components";
4
- import { useDesignSystemTheme } from "../contexts/DesignSystemConfiguration";
2
+ import React, { forwardRef, memo, useCallback, useState } from "react";
3
+ import { cn } from "../utils/tailwind";
5
4
  import { DropdownMenu } from "./DropdownMenu";
6
5
  import { MenuItem } from "./internal/Menu";
7
6
 
8
- const DotButtonElement = styled.div(({ theme }) => ({
9
- cursor: "pointer",
10
- borderRadius: "4px",
11
- display: "flex",
12
- alignItems: "center",
13
- justifyContent: "center",
14
- padding: "2px 0",
15
- "&:hover": {
16
- background: theme.colors.inputBackgroundLight,
17
- },
18
- "&:active": {
19
- background: theme.colors.activeBackground,
20
- },
21
- }));
7
+ const DotButton = forwardRef<
8
+ HTMLDivElement,
9
+ React.HTMLAttributes<HTMLDivElement>
10
+ >(({ className, ...props }, ref) => (
11
+ <div
12
+ ref={ref}
13
+ className={`cursor-pointer rounded flex items-center justify-center py-[2px] hover:bg-input-background-light active:bg-active-background ${className ?? ""}`}
14
+ {...props}
15
+ />
16
+ ));
22
17
 
23
- const TriggerElement = styled.div({
24
- pointerEvents: "none",
25
- height: "15px",
26
- });
18
+ const Trigger = forwardRef<
19
+ HTMLDivElement,
20
+ React.HTMLAttributes<HTMLDivElement>
21
+ >(({ className, ...props }, ref) => (
22
+ <div
23
+ ref={ref}
24
+ className={cn("pointer-events-none h-[15px]", className)}
25
+ {...props}
26
+ />
27
+ ));
27
28
 
28
29
  /**
29
30
  * A button that opens a menu when clicked, but also allows dragging the
@@ -39,9 +40,8 @@ export const DraggableMenuButton = memo(function DraggableMenuButton<
39
40
  onSelect?: (value: T) => void;
40
41
  isVisible?: boolean;
41
42
  }) {
42
- const color = useDesignSystemTheme().colors.icon;
43
43
  const [open, setOpen] = useState(false);
44
- const [downPosition, setDownPosition] = React.useState<{
44
+ const [downPosition, setDownPosition] = useState<{
45
45
  x: number;
46
46
  y: number;
47
47
  } | null>(null);
@@ -70,7 +70,6 @@ export const DraggableMenuButton = memo(function DraggableMenuButton<
70
70
 
71
71
  const dx = event.clientX - downPosition.x;
72
72
  const dy = event.clientY - downPosition.y;
73
-
74
73
  const distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
75
74
 
76
75
  if (distance < 5) {
@@ -90,7 +89,7 @@ export const DraggableMenuButton = memo(function DraggableMenuButton<
90
89
  );
91
90
 
92
91
  return (
93
- <DotButtonElement
92
+ <DotButton
94
93
  onPointerDownCapture={handlePointerDownCapture}
95
94
  onPointerUp={handlePointerUp}
96
95
  onClick={(event) => {
@@ -106,17 +105,17 @@ export const DraggableMenuButton = memo(function DraggableMenuButton<
106
105
  onSelect={onSelect}
107
106
  shouldBindKeyboardShortcuts={false}
108
107
  >
109
- <TriggerElement>
108
+ <Trigger>
110
109
  <DragHandleDots2Icon
111
- color={isVisible || open ? color : "transparent"}
110
+ color={isVisible || open ? "var(--icon)" : "transparent"}
112
111
  />
113
- </TriggerElement>
112
+ </Trigger>
114
113
  </DropdownMenu>
115
114
  ) : (
116
115
  <DragHandleDots2Icon
117
- color={isVisible || open ? color : "transparent"}
116
+ color={isVisible || open ? "var(--icon)" : "transparent"}
118
117
  />
119
118
  )}
120
- </DotButtonElement>
119
+ </DotButton>
121
120
  );
122
121
  });
@@ -10,7 +10,6 @@ import React, {
10
10
  useCallback,
11
11
  useMemo,
12
12
  } from "react";
13
- import styled from "styled-components";
14
13
  import { MenuItemProps, MenuProps } from "./ContextMenu";
15
14
  import { renderIcon } from "./Icons";
16
15
  import { Spacer } from "./Spacer";
@@ -23,28 +22,6 @@ import {
23
22
  styles,
24
23
  } from "./internal/Menu";
25
24
 
26
- /* ----------------------------------------------------------------------------
27
- * Separator
28
- * ------------------------------------------------------------------------- */
29
-
30
- const SeparatorElement = styled(RadixDropdownMenu.Separator)(
31
- styles.separatorStyle
32
- );
33
-
34
- /* ----------------------------------------------------------------------------
35
- * Item
36
- * ------------------------------------------------------------------------- */
37
-
38
- const ItemElement = styled(RadixDropdownMenu.Item)(styles.itemStyle);
39
-
40
- const CheckboxItemElement = styled(RadixDropdownMenu.CheckboxItem)(
41
- styles.itemStyle
42
- );
43
-
44
- const StyledItemIndicator = styled(RadixDropdownMenu.ItemIndicator)(
45
- styles.itemIndicatorStyle
46
- );
47
-
48
25
  const DropdownMenuItem = memo(function ContextMenuItem<T extends string>({
49
26
  value,
50
27
  children,
@@ -64,14 +41,15 @@ const DropdownMenuItem = memo(function ContextMenuItem<T extends string>({
64
41
 
65
42
  if (checked) {
66
43
  return (
67
- <CheckboxItemElement
44
+ <RadixDropdownMenu.CheckboxItem
68
45
  checked={checked}
69
46
  disabled={disabled}
70
47
  onSelect={handleSelectItem}
48
+ className={styles.itemStyle({ disabled })}
71
49
  >
72
- <StyledItemIndicator>
50
+ <RadixDropdownMenu.ItemIndicator className={styles.itemIndicatorStyle}>
73
51
  <CheckIcon />
74
- </StyledItemIndicator>
52
+ </RadixDropdownMenu.ItemIndicator>
75
53
  {icon && (
76
54
  <>
77
55
  {renderIcon(icon)}
@@ -79,12 +57,16 @@ const DropdownMenuItem = memo(function ContextMenuItem<T extends string>({
79
57
  </>
80
58
  )}
81
59
  {children}
82
- </CheckboxItemElement>
60
+ </RadixDropdownMenu.CheckboxItem>
83
61
  );
84
62
  }
85
63
 
86
64
  const element = (
87
- <ItemElement disabled={disabled} onSelect={handleSelectItem}>
65
+ <RadixDropdownMenu.Item
66
+ disabled={disabled}
67
+ onSelect={handleSelectItem}
68
+ className={styles.itemStyle({ disabled })}
69
+ >
88
70
  {indented && (
89
71
  <Spacer.Horizontal size={CHECKBOX_WIDTH - CHECKBOX_RIGHT_INSET} />
90
72
  )}
@@ -109,7 +91,7 @@ const DropdownMenuItem = memo(function ContextMenuItem<T extends string>({
109
91
  <ChevronRightIcon />
110
92
  </>
111
93
  )}
112
- </ItemElement>
94
+ </RadixDropdownMenu.Item>
113
95
  );
114
96
 
115
97
  if (items && items.length > 0) {
@@ -132,8 +114,14 @@ const DropdownMenuItem = memo(function ContextMenuItem<T extends string>({
132
114
  * Root
133
115
  * ------------------------------------------------------------------------- */
134
116
 
135
- const Content = styled(RadixDropdownMenu.Content)(styles.contentStyle);
136
- const SubContent = styled(RadixDropdownMenu.SubContent)(styles.contentStyle);
117
+ type DropdownRootProps<T extends string> = MenuProps<T> & {
118
+ open?: boolean;
119
+ onCloseAutoFocus?: React.EventHandler<SyntheticEvent<unknown>>;
120
+ emptyState?: React.ReactNode;
121
+ } & Pick<
122
+ ComponentProps<typeof RadixDropdownMenu.Content>,
123
+ "side" | "sideOffset" | "align" | "alignOffset" | "collisionPadding"
124
+ >;
137
125
 
138
126
  const DropdownMenuRoot = forwardRef(function DropdownMenuRoot<T extends string>(
139
127
  {
@@ -150,13 +138,8 @@ const DropdownMenuRoot = forwardRef(function DropdownMenuRoot<T extends string>(
150
138
  align,
151
139
  alignOffset,
152
140
  collisionPadding = 8,
153
- }: MenuProps<T> & {
154
- open?: boolean;
155
- onCloseAutoFocus?: React.EventHandler<SyntheticEvent<unknown>>;
156
- } & Pick<
157
- ComponentProps<typeof RadixDropdownMenu.Content>,
158
- "side" | "sideOffset" | "align" | "alignOffset" | "collisionPadding"
159
- >,
141
+ emptyState,
142
+ }: DropdownRootProps<T>,
160
143
  forwardedRef: ForwardedRef<HTMLElement>
161
144
  ) {
162
145
  const hasCheckedItem = items.some(
@@ -179,7 +162,9 @@ const DropdownMenuRoot = forwardRef(function DropdownMenuRoot<T extends string>(
179
162
  const TriggerComponent = isNested
180
163
  ? RadixDropdownMenu.SubTrigger
181
164
  : RadixDropdownMenu.Trigger;
182
- const ContentComponent = isNested ? SubContent : Content;
165
+ const ContentComponent = isNested
166
+ ? RadixDropdownMenu.SubContent
167
+ : RadixDropdownMenu.Content;
183
168
 
184
169
  const contentStyle = useMemo(() => ({ zIndex: 1000 }), []);
185
170
 
@@ -190,6 +175,7 @@ const DropdownMenuRoot = forwardRef(function DropdownMenuRoot<T extends string>(
190
175
  </TriggerComponent>
191
176
  <RadixDropdownMenu.Portal>
192
177
  <ContentComponent
178
+ className={styles.contentStyle}
193
179
  side={side}
194
180
  sideOffset={sideOffset}
195
181
  align={align}
@@ -198,9 +184,13 @@ const DropdownMenuRoot = forwardRef(function DropdownMenuRoot<T extends string>(
198
184
  collisionPadding={collisionPadding}
199
185
  {...({ onCloseAutoFocus } as any)}
200
186
  >
187
+ {items.length === 0 ? emptyState : null}
201
188
  {items.map((item, index) =>
202
189
  item === SEPARATOR_ITEM ? (
203
- <SeparatorElement key={index} />
190
+ <RadixDropdownMenu.Separator
191
+ key={index}
192
+ className={styles.separatorStyle}
193
+ />
204
194
  ) : (
205
195
  <DropdownMenuItem
206
196
  key={item.value ?? index}
@@ -1,43 +1,33 @@
1
1
  import { Sketch } from "@noya-app/noya-file-format";
2
- import React, { CSSProperties, ForwardedRef, forwardRef, memo } from "react";
3
- import styled from "styled-components";
2
+ import React, { forwardRef, memo } from "react";
4
3
  import { SketchPattern } from "../utils/sketchPattern";
5
4
  import { FillPreviewBackground } from "./FillPreviewBackground";
6
5
 
7
- const Container = styled.button<{ $flex?: CSSProperties["flex"] }>(
8
- ({ theme, $flex }) => ({
9
- outline: "none",
10
- padding: 0,
11
- width: "50px",
12
- height: "27px",
13
- borderRadius: "4px",
14
- overflow: "hidden",
15
- border: "none",
16
- boxShadow: `0 0 0 1px ${theme.colors.divider} inset`,
17
- background: "transparent",
18
- "&:focus": {
19
- boxShadow: `0 0 0 1px ${theme.colors.sidebar.background}, 0 0 0 3px ${theme.colors.primary}`,
20
- },
21
- position: "relative",
22
- flex: $flex,
23
- })
24
- );
6
+ const FillButton = forwardRef<
7
+ HTMLButtonElement,
8
+ React.ButtonHTMLAttributes<HTMLButtonElement>
9
+ >(({ className, ...props }, ref) => (
10
+ <button
11
+ ref={ref}
12
+ className={`outline-none p-0 w-[50px] h-[27px] rounded overflow-hidden border-none shadow-[0_0_0_1px_var(--divider)_inset] bg-transparent relative focus:shadow-[0_0_0_1px_var(--sidebar-background),0_0_0_3px_var(--primary)] ${className ?? ""}`}
13
+ {...props}
14
+ />
15
+ ));
25
16
 
26
17
  interface Props {
27
18
  id?: string;
28
19
  value?: Sketch.Color | Sketch.Gradient | SketchPattern;
29
- flex?: CSSProperties["flex"];
30
20
  }
31
21
 
32
22
  export const FillInputField = memo(
33
- forwardRef(function FillInputField(
34
- { id, value, flex, ...rest }: Props,
35
- ref: ForwardedRef<HTMLButtonElement>
23
+ forwardRef<HTMLButtonElement, Props>(function FillInputField(
24
+ { id, value, ...rest },
25
+ ref
36
26
  ) {
37
27
  return (
38
- <Container ref={ref} id={id} $flex={flex} {...rest}>
28
+ <FillButton ref={ref} id={id} {...rest}>
39
29
  <FillPreviewBackground value={value} />
40
- </Container>
30
+ </FillButton>
41
31
  );
42
32
  })
43
33
  );
@@ -1,12 +1,12 @@
1
1
  import { Sketch } from "@noya-app/noya-file-format";
2
2
  // import { useSketchImage } from 'noya-renderer';
3
3
  import React, { memo, useMemo } from "react";
4
- import styled, { useTheme } from "styled-components";
5
4
  import { useImageData } from "../contexts/ImageDataContext";
6
5
  import { useObjectURL } from "../hooks/useObjectURL";
7
6
  import { getGradientBackground } from "../utils/getGradientBackground";
8
7
  import { sketchColorToRgbaString } from "../utils/sketchColor";
9
8
  import { SketchPattern } from "../utils/sketchPattern";
9
+ import { cn } from "../utils/tailwind";
10
10
 
11
11
  const dotsHorizontalSvg = (fillColor: string) => `
12
12
  <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15' fill='${fillColor}'>
@@ -14,27 +14,15 @@ const dotsHorizontalSvg = (fillColor: string) => `
14
14
  </svg>
15
15
  `;
16
16
 
17
- const Background = styled.span<{ background: string }>(({ background }) => ({
18
- background,
19
- position: "absolute",
20
- inset: 0,
21
- }));
22
-
23
- const HorizontalDotsBackground = memo(function HorizontalDotsBackground() {
24
- const { inputBackground, placeholderDots } = useTheme().colors;
25
-
26
- const background = useMemo(
27
- () =>
28
- [
29
- `center url("data:image/svg+xml;utf8,${dotsHorizontalSvg(
30
- placeholderDots
31
- )}") no-repeat`,
32
- inputBackground,
33
- ].join(","),
34
- [inputBackground, placeholderDots]
17
+ const Background = memo(function Background({background}: {background?: string}) {
18
+ return (
19
+ <span
20
+ className={cn(
21
+ "absolute inset-0",
22
+ background ? `bg-[${background}]` : "bg-input-background"
23
+ )}
24
+ />
35
25
  );
36
-
37
- return <Background background={background} />;
38
26
  });
39
27
 
40
28
  function getPatternSizeAndPosition(
@@ -108,10 +96,18 @@ interface Props {
108
96
  value?: Sketch.Color | Sketch.Gradient | SketchPattern;
109
97
  }
110
98
 
99
+ const background = `center url("data:image/svg+xml;utf8,${dotsHorizontalSvg(
100
+ "var(--placeholder-dots)"
101
+ )}") no-repeat`;
102
+
111
103
  export const FillPreviewBackground = memo(function FillPreviewBackground({
112
104
  value,
113
105
  }: Props) {
114
- if (!value) return <HorizontalDotsBackground />;
106
+ if (!value) return (
107
+ <Background
108
+ background={background}
109
+ />
110
+ );
115
111
 
116
112
  switch (value._class) {
117
113
  case "color":
@@ -3,7 +3,6 @@ import {
3
3
  IconButton,
4
4
  Small,
5
5
  useCurrentFloatingWindowInternal,
6
- useDesignSystemTheme,
7
6
  useFloatingWindowManager,
8
7
  } from "@noya-app/noya-designsystem";
9
8
  import React, { useCallback, useRef, useState } from "react";
@@ -125,7 +124,7 @@ function defaultRenderToolbar({
125
124
  }) {
126
125
  return (
127
126
  <>
128
- <Small flex="1" fontWeight="500" color="text">
127
+ <Small className="flex-1 font-medium text-text">
129
128
  {title}
130
129
  </Small>
131
130
  {toolbarContent}
@@ -269,8 +268,6 @@ export const FloatingWindow: React.FC<FloatingWindowProps> = ({
269
268
  };
270
269
  }, [handleMouseMove, handleMouseUp]);
271
270
 
272
- const theme = useDesignSystemTheme();
273
-
274
271
  const floatingWindowManager = useFloatingWindowManager();
275
272
  const currentFloatingWindow = useCurrentFloatingWindowInternal();
276
273
 
@@ -288,14 +285,14 @@ export const FloatingWindow: React.FC<FloatingWindowProps> = ({
288
285
  left: position.x,
289
286
  width: size.width,
290
287
  height: size.height,
291
- outline: `1px solid ${theme.colors.divider}`,
292
- backgroundColor: theme.colors.canvas.background,
288
+ outline: `1px solid var(--divider)`,
289
+ backgroundColor: "var(--canvas-background)",
293
290
  }}
294
291
  >
295
292
  <div
296
293
  style={{
297
294
  ...styles.toolbar,
298
- backgroundColor: theme.colors.sidebar.background,
295
+ backgroundColor: "var(--sidebar-background)",
299
296
  }}
300
297
  onMouseDown={handleMouseDown}
301
298
  >