@noya-app/noya-designsystem 0.1.51 → 0.1.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +10 -13
  2. package/CHANGELOG.md +11 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +285 -13
  5. package/dist/index.d.ts +285 -13
  6. package/dist/index.js +3264 -578
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +3235 -550
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +12 -9
  11. package/postcss.config.js +7 -0
  12. package/src/components/ActionMenu.tsx +8 -3
  13. package/src/components/Banner.tsx +2 -2
  14. package/src/components/BaseToolbar.tsx +6 -2
  15. package/src/components/Breadcrumbs.tsx +32 -2
  16. package/src/components/Button.tsx +30 -19
  17. package/src/components/Chip.tsx +3 -0
  18. package/src/components/ColorSwatch.tsx +42 -0
  19. package/src/components/ColorSwatchControl.tsx +92 -0
  20. package/src/components/Dialog.tsx +1 -1
  21. package/src/components/FillInputField.tsx +1 -1
  22. package/src/components/Grid.tsx +11 -4
  23. package/src/components/GridView.tsx +1 -1
  24. package/src/components/LabeledField.tsx +4 -1
  25. package/src/components/List.tsx +12 -7
  26. package/src/components/ListView.tsx +7 -4
  27. package/src/components/MediaThumbnail.tsx +44 -10
  28. package/src/components/Popover.tsx +1 -1
  29. package/src/components/SegmentedControl.tsx +1 -1
  30. package/src/components/SelectionToolbar.tsx +70 -0
  31. package/src/components/Switch.tsx +1 -1
  32. package/src/components/Text.tsx +2 -2
  33. package/src/components/Toast.tsx +13 -7
  34. package/src/components/Toolbar.tsx +11 -1
  35. package/src/components/Tooltip.tsx +4 -2
  36. package/src/components/catppuccin/fileIcons.ts +2430 -0
  37. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +3 -1
  38. package/src/components/internal/Menu.tsx +5 -4
  39. package/src/components/internal/MenuViewport.tsx +2 -0
  40. package/src/components/pipeline/PipelineResultLayout.tsx +18 -0
  41. package/src/contexts/DialogContext.tsx +20 -5
  42. package/src/index.css +101 -39
  43. package/src/index.tsx +3 -0
  44. package/src/theme/index.ts +6 -1
  45. package/src/theme/themeUtils.ts +10 -3
  46. package/src/utils/classNames.ts +3 -2
  47. package/src/utils/moveTreeItem.ts +4 -4
  48. package/tsup.config.ts +1 -1
@@ -1,9 +1,15 @@
1
- import { ImageIcon, SpeakerLoudIcon, VideoIcon } from "@noya-app/noya-icons";
1
+ import {
2
+ FileIcon,
3
+ ImageIcon,
4
+ SpeakerLoudIcon,
5
+ VideoIcon,
6
+ } from "@noya-app/noya-icons";
2
7
  import React, { memo, useMemo } from "react";
3
8
  import { cssVars } from "../theme";
4
9
  import { cx } from "../utils/classNames";
5
10
  import { CollectionItemSize } from "./Collection";
6
11
  import { IconName, Icons } from "./Icons";
12
+ import { fileExtensions, fileNames } from "./catppuccin/fileIcons";
7
13
 
8
14
  type ColorScheme = "primary" | "secondary" | "icon" | "warning";
9
15
 
@@ -31,6 +37,8 @@ type MediaThumbnailProps = {
31
37
  */
32
38
  iconName?: IconName;
33
39
  size?: CollectionItemSize;
40
+
41
+ fileName?: string;
34
42
  };
35
43
 
36
44
  export const getThumbnailColors = (
@@ -39,8 +47,8 @@ export const getThumbnailColors = (
39
47
  ) => {
40
48
  if (selected) {
41
49
  return {
42
- icon: cssVars.colors.primary,
43
- background: cssVars.colors.primaryPastel,
50
+ icon: cssVars.colors.selectedListItemText,
51
+ background: cssVars.colors.selectedListItemIconBackground,
44
52
  };
45
53
  }
46
54
 
@@ -75,8 +83,9 @@ export const MediaThumbnail = memo(function MediaThumbnail({
75
83
  className,
76
84
  colorScheme = "icon",
77
85
  url,
78
- iconName = "FileIcon",
86
+ iconName,
79
87
  size = "medium",
88
+ fileName,
80
89
  }: MediaThumbnailProps) {
81
90
  const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
82
91
  colorScheme,
@@ -95,6 +104,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
95
104
  switch (contentType) {
96
105
  case "image":
97
106
  if (!url) return <ImageIcon color={iconColor} style={iconStyles} />;
107
+
98
108
  return (
99
109
  <img
100
110
  src={url}
@@ -103,18 +113,32 @@ export const MediaThumbnail = memo(function MediaThumbnail({
103
113
  tabIndex={-1}
104
114
  />
105
115
  );
106
-
107
116
  case "video":
108
117
  return <VideoIcon color={iconColor} style={iconStyles} />;
109
-
110
118
  case "audio":
111
119
  return <SpeakerLoudIcon color={iconColor} style={iconStyles} />;
112
-
113
120
  default:
114
- const Icon = Icons[iconName];
115
- return <Icon color={iconColor} style={iconStyles} />;
121
+ if (iconName) {
122
+ const Icon = Icons[iconName];
123
+ return <Icon color={iconColor} style={iconStyles} />;
124
+ }
125
+
126
+ if (fileName) {
127
+ const fileIcon = findFileIcon(fileName);
128
+ if (fileIcon) {
129
+ return (
130
+ <img
131
+ src={`https://api.iconify.design/catppuccin/${fileIcon}.svg?height=none&box=1`}
132
+ alt=""
133
+ className="w-4 h-4"
134
+ />
135
+ );
136
+ }
137
+ }
138
+
139
+ return <FileIcon color={iconColor} style={iconStyles} />;
116
140
  }
117
- }, [contentType, url, iconColor, iconStyles, iconName]);
141
+ }, [contentType, url, iconColor, iconStyles, iconName, fileName]);
118
142
 
119
143
  return (
120
144
  <div
@@ -137,3 +161,13 @@ const iconSizeMap: Record<CollectionItemSize, number | string> = {
137
161
  large: "max(15px, 20%)",
138
162
  xl: "max(15px, 20%)",
139
163
  };
164
+
165
+ function findFileIcon(fileName: string) {
166
+ const fileIcon = fileNames[fileName];
167
+ if (fileIcon) return fileIcon;
168
+ const fileExtensionIndex = fileName.lastIndexOf(".");
169
+ if (fileExtensionIndex === -1) return undefined;
170
+ const fileExtension = fileName.slice(fileExtensionIndex + 1);
171
+ const fileExtensionIcon = fileExtensions[fileExtension];
172
+ return fileExtensionIcon;
173
+ }
@@ -33,7 +33,7 @@ interface Props
33
33
  }
34
34
 
35
35
  export const popoverStyle = {
36
- base: "rounded font-[14px] bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2),0_0_12px_rgba(0,0,0,0.1)] max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
36
+ base: "rounded font-[14px] bg-popover-background shadow-popover max-h-[600px] overflow-y-auto text-text-muted z-[1000]",
37
37
  large: "w-[680px]",
38
38
  normal: "w-[240px]",
39
39
  };
@@ -80,7 +80,7 @@ const SegmentedControlItem = forwardRef(function SegmentedControlItem<
80
80
  ? "aria-checked:bg-primary aria-checked:text-white"
81
81
  : "aria-checked:bg-background aria-checked:text-text",
82
82
  "focus:z-interactable",
83
- "aria-checked:shadow-[0_1px_1px_rgba(0,0,0,0.1)]",
83
+ "aria-checked:shadow-segment",
84
84
  "whitespace-pre",
85
85
  className
86
86
  )}
@@ -0,0 +1,70 @@
1
+ import { MenuItem, ToolbarMenu } from "@noya-app/noya-designsystem";
2
+ import { Rect } from "@noya-app/noya-geometry";
3
+ import { memoGeneric, useSize } from "@noya-app/react-utils";
4
+ import * as PopperPrimitive from "@radix-ui/react-popper";
5
+ import type { MeasurableElement } from "@radix-ui/utils";
6
+ import React, { CSSProperties, useMemo } from "react";
7
+
8
+ // Create a Measurable from a Rect
9
+ const createVirtualElement = (rect: Rect): MeasurableElement => ({
10
+ getBoundingClientRect: () => ({
11
+ width: rect.width,
12
+ height: rect.height,
13
+ x: rect.x,
14
+ y: rect.y,
15
+ top: rect.y,
16
+ right: rect.x + rect.width,
17
+ bottom: rect.y + rect.height,
18
+ left: rect.x,
19
+ toJSON: () => null,
20
+ }),
21
+ });
22
+
23
+ type SelectionToolbarProps<T extends string> = {
24
+ rect: Rect;
25
+ menuItems: MenuItem<T>[];
26
+ onSelectMenuItem: (item: T) => void;
27
+ };
28
+
29
+ export const SelectionToolbar = memoGeneric(function SelectionToolbar<
30
+ T extends string,
31
+ >({ rect, menuItems, onSelectMenuItem }: SelectionToolbarProps<T>) {
32
+ const containerRef = React.useRef<HTMLDivElement>(null);
33
+ const size = useSize(containerRef, "width");
34
+
35
+ // Create a virtual reference element from the rect
36
+ const virtualRef = useMemo(
37
+ () => ({
38
+ current: createVirtualElement(rect),
39
+ }),
40
+ [rect]
41
+ );
42
+
43
+ return (
44
+ <PopperPrimitive.Root>
45
+ <PopperPrimitive.Anchor virtualRef={virtualRef} />
46
+ <PopperPrimitive.Content
47
+ side="top"
48
+ sideOffset={10}
49
+ align="center"
50
+ avoidCollisions
51
+ collisionPadding={10}
52
+ data-editor-overlay
53
+ contentEditable={false}
54
+ style={{
55
+ zIndex: 1000,
56
+ opacity: size ? 1 : 0,
57
+ transition: "opacity 0.2s ease-in-out",
58
+ ["--n-input-background" as keyof CSSProperties]: "transparent",
59
+ }}
60
+ >
61
+ <div
62
+ ref={containerRef}
63
+ className="flex gap-1 bg-popover-background rounded shadow-popover p-1"
64
+ >
65
+ <ToolbarMenu items={menuItems} onSelectMenuItem={onSelectMenuItem} />
66
+ </div>
67
+ </PopperPrimitive.Content>
68
+ </PopperPrimitive.Root>
69
+ );
70
+ });
@@ -38,7 +38,7 @@ export const Switch = function Switch({
38
38
  onChange(newValue);
39
39
  }}
40
40
  className={cx(
41
- "all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer ring-offset-background [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary transition-all",
41
+ "all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer ring-offset-background [-webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary transition-all",
42
42
  colorScheme === "secondary" && "data-[state=checked]:bg-secondary",
43
43
  "focus:z-interactable",
44
44
  className
@@ -33,8 +33,8 @@ const elements: Record<Variant, keyof ReactHTML> = {
33
33
  };
34
34
 
35
35
  export const textStyles: Record<Variant, string> = {
36
- title: "font-sans text-title font-bold sm:text-[36px]",
37
- subtitle: "font-sans text-subtitle font-medium sm:text-[18px]",
36
+ title: "font-sans text-title font-bold",
37
+ subtitle: "font-sans text-subtitle font-medium",
38
38
  heading1: "font-sans text-heading1 font-semibold",
39
39
  heading2: "font-sans text-heading2 font-medium",
40
40
  heading3: "font-sans text-heading3 font-normal",
@@ -1,7 +1,7 @@
1
- import * as ToastPrimitive from '@radix-ui/react-toast';
2
- import React, { ReactNode } from 'react';
3
- import { IconButton } from './IconButton';
4
- import { textStyles } from './Text';
1
+ import * as ToastPrimitive from "@radix-ui/react-toast";
2
+ import React, { ReactNode } from "react";
3
+ import { IconButton } from "./IconButton";
4
+ import { textStyles } from "./Text";
5
5
 
6
6
  export const Toast = ({
7
7
  title,
@@ -15,15 +15,21 @@ export const Toast = ({
15
15
  }) => {
16
16
  return (
17
17
  <ToastPrimitive.Root
18
- className="px-2 py-2.5 grid grid-cols-[auto_max-content] gap-x-2.5 items-center rounded text-sm bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2),0_0_12px_rgba(0,0,0,0.1)] text-text-muted"
18
+ className="px-2 py-2.5 grid grid-cols-[auto_max-content] gap-x-2.5 items-center rounded text-sm bg-popover-background shadow-popover text-text-muted"
19
19
  {...props}
20
20
  >
21
21
  {title && (
22
- <ToastPrimitive.Title className={`mb-[5px] ${textStyles.label} font-bold text-text`}>
22
+ <ToastPrimitive.Title
23
+ className={`mb-[5px] ${textStyles.label} font-bold text-text`}
24
+ >
23
25
  {title}
24
26
  </ToastPrimitive.Title>
25
27
  )}
26
- <ToastPrimitive.Description className={`${textStyles.small} text-text-muted`}>{content}</ToastPrimitive.Description>
28
+ <ToastPrimitive.Description
29
+ className={`${textStyles.small} text-text-muted`}
30
+ >
31
+ {content}
32
+ </ToastPrimitive.Description>
27
33
  {children && (
28
34
  <ToastPrimitive.Action asChild altText="">
29
35
  {children}
@@ -15,6 +15,7 @@ import {
15
15
  MenuItem,
16
16
  } from "./internal/Menu";
17
17
  import { Spacer } from "./Spacer";
18
+ import { Tooltip } from "./Tooltip";
18
19
 
19
20
  export interface ToolbarProps<T extends string = string> {
20
21
  children?: React.ReactNode;
@@ -81,6 +82,7 @@ export function Toolbar<T extends string = string>({
81
82
 
82
83
  const activeButtonStyle = {
83
84
  backgroundColor: cssVars.colors.primaryPastel,
85
+ color: cssVars.colors.primary,
84
86
  };
85
87
 
86
88
  export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
@@ -100,7 +102,7 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
100
102
  }
101
103
 
102
104
  if (isSelectableMenuItem(item)) {
103
- return (
105
+ const content = (
104
106
  <Button
105
107
  key={i}
106
108
  disabled={item.disabled}
@@ -116,6 +118,14 @@ export const ToolbarMenu = memoGeneric(function ToolbarMenu<T extends string>({
116
118
  {item.icon && renderIcon(item.icon)}
117
119
  </Button>
118
120
  );
121
+
122
+ return item.tooltip ? (
123
+ <Tooltip sideOffset={10} content={item.tooltip} key={i}>
124
+ {content}
125
+ </Tooltip>
126
+ ) : (
127
+ content
128
+ );
119
129
  }
120
130
 
121
131
  return (
@@ -9,11 +9,13 @@ import { textStyles } from "./Text";
9
9
  interface Props {
10
10
  children: React.ReactNode;
11
11
  content: React.ReactNode;
12
+ sideOffset?: number;
12
13
  }
13
14
 
14
15
  export const Tooltip = React.memo(function Tooltip({
15
16
  children,
16
17
  content,
18
+ sideOffset = 2,
17
19
  }: Props) {
18
20
  return (
19
21
  <TooltipPrimitive.Provider>
@@ -23,9 +25,9 @@ export const Tooltip = React.memo(function Tooltip({
23
25
  <TooltipPrimitive.Content
24
26
  side="bottom"
25
27
  align="center"
26
- sideOffset={2}
28
+ sideOffset={sideOffset}
27
29
  collisionPadding={8}
28
- className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-[0_2px_4px_rgba(0,0,0,0.2)_0_0_12px_rgba(0,0,0,0.1)] border border-solid border-divider-strong z-menu`}
30
+ className={`${textStyles.small} text-text rounded-[3px] py-small px-medium bg-popover-background shadow-popover border border-solid border-divider-strong z-menu`}
29
31
  >
30
32
  {content}
31
33
  {/* <Arrow /> */}