@noya-app/noya-designsystem 0.1.48 → 0.1.50

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 (55) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +19 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +347 -173
  5. package/dist/index.d.ts +347 -173
  6. package/dist/index.js +3602 -2811
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +4761 -3993
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +6 -5
  11. package/src/__tests__/validateDropIndicator.test.ts +4 -4
  12. package/src/components/ActionMenu.tsx +15 -4
  13. package/src/components/Avatar.tsx +2 -2
  14. package/src/components/Banner.tsx +29 -0
  15. package/src/components/BaseToolbar.tsx +2 -2
  16. package/src/components/Button.tsx +2 -2
  17. package/src/components/Checkbox.tsx +2 -2
  18. package/src/components/Chip.tsx +15 -14
  19. package/src/components/Collection.tsx +25 -2
  20. package/src/components/Combobox.tsx +22 -23
  21. package/src/components/ComboboxMenu.tsx +1 -1
  22. package/src/components/Dialog.tsx +2 -2
  23. package/src/components/DimensionInput.tsx +14 -12
  24. package/src/components/EditableText.tsx +3 -1
  25. package/src/components/FillInputField.tsx +2 -2
  26. package/src/components/Grid.tsx +131 -113
  27. package/src/components/GridView.tsx +36 -18
  28. package/src/components/InputField.tsx +116 -171
  29. package/src/components/Label.tsx +14 -73
  30. package/src/components/LabeledField.tsx +13 -2
  31. package/src/components/List.tsx +106 -47
  32. package/src/components/ListView.tsx +52 -31
  33. package/src/components/MediaThumbnail.tsx +14 -6
  34. package/src/components/Message.tsx +8 -8
  35. package/src/components/NoyaLogo.tsx +41 -0
  36. package/src/components/Popover.tsx +9 -6
  37. package/src/components/Section.tsx +172 -0
  38. package/src/components/SegmentedControl.tsx +1 -1
  39. package/src/components/SelectMenu.tsx +9 -4
  40. package/src/components/Slider.tsx +16 -7
  41. package/src/components/Sortable.tsx +186 -47
  42. package/src/components/UserPointer.tsx +1 -1
  43. package/src/components/ai-assistant/AIAssistantLayout.tsx +102 -0
  44. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +71 -0
  45. package/src/components/file-explorer/FileExplorerLayout.tsx +71 -0
  46. package/src/components/internal/Menu.tsx +20 -10
  47. package/src/components/internal/SelectItem.tsx +3 -2
  48. package/src/components/pipeline/PipelineResultLayout.tsx +32 -0
  49. package/src/index.css +118 -112
  50. package/src/index.tsx +9 -0
  51. package/src/theme/index.ts +2 -0
  52. package/src/utils/classNames.ts +76 -3
  53. package/src/utils/inputs.ts +21 -0
  54. package/src/utils/moveTreeItem.ts +99 -0
  55. package/tailwind.config.ts +82 -75
@@ -0,0 +1,41 @@
1
+ import { cssVars } from "@noya-app/noya-designsystem";
2
+ import React, { forwardRef } from "react";
3
+
4
+ export const Logo = forwardRef(function Logo(
5
+ props: React.ComponentProps<"svg">,
6
+ ref: React.Ref<SVGSVGElement>
7
+ ) {
8
+ const fill = props.fill || cssVars.colors.logoFill;
9
+
10
+ return (
11
+ <svg
12
+ ref={ref}
13
+ viewBox="0 0 24 26"
14
+ fill="none"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ {...props}
17
+ className="w-6 aspect-[24/26]"
18
+ >
19
+ <path
20
+ d="M6.5 3H2V12H5V6H6.5C7.32843 6 8 6.67157 8 7.5V12H11V7.5C11 5.01472 8.98528 3 6.5 3Z"
21
+ fill={fill}
22
+ />
23
+ <path
24
+ fillRule="evenodd"
25
+ clipRule="evenodd"
26
+ d="M17.5 12C19.9853 12 22 9.98528 22 7.5C22 5.01472 19.9853 3 17.5 3C15.0147 3 13 5.01472 13 7.5C13 9.98528 15.0147 12 17.5 12ZM17.5 9C18.3284 9 19 8.32843 19 7.5C19 6.67157 18.3284 6 17.5 6C16.6716 6 16 6.67157 16 7.5C16 8.32843 16.6716 9 17.5 9Z"
27
+ fill={fill}
28
+ />
29
+ <path
30
+ d="M5 14H2V18.5C2 20.9853 4.01472 23 6.5 23H8V26H11V17H8V20H6.5C5.67157 20 5 19.3284 5 18.5V14Z"
31
+ fill={fill}
32
+ />
33
+ <path
34
+ fillRule="evenodd"
35
+ clipRule="evenodd"
36
+ d="M17.5 23C15.0147 23 13 20.9853 13 18.5C13 16.0147 15.0147 14 17.5 14C19.9853 14 22 16.0147 22 18.5V23H17.5ZM17.5 20C18.3284 20 19 19.3284 19 18.5C19 17.6716 18.3284 17 17.5 17C16.6716 17 16 17.6716 16 18.5C16 19.3284 16.6716 20 17.5 20Z"
37
+ fill={fill}
38
+ />
39
+ </svg>
40
+ );
41
+ });
@@ -32,6 +32,12 @@ interface Props
32
32
  className?: string;
33
33
  }
34
34
 
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]",
37
+ large: "w-[680px]",
38
+ normal: "w-[240px]",
39
+ };
40
+
35
41
  export function Popover({
36
42
  children,
37
43
  trigger,
@@ -56,12 +62,9 @@ export function Popover({
56
62
  <PopoverPrimitive.Portal>
57
63
  <PopoverPrimitive.Content
58
64
  className={cx(
59
- "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]",
60
- variant === "large"
61
- ? "w-[680px]"
62
- : variant === "normal"
63
- ? "w-[240px]"
64
- : "",
65
+ popoverStyle.base,
66
+ variant === "large" && popoverStyle.large,
67
+ variant === "normal" && popoverStyle.normal,
65
68
  className
66
69
  )}
67
70
  side={side}
@@ -0,0 +1,172 @@
1
+ import {
2
+ Button,
3
+ IconButton,
4
+ IndentContext,
5
+ INPUT_HEIGHT,
6
+ InspectorPrimitives,
7
+ Spacer,
8
+ textStyles,
9
+ } from "@noya-app/noya-designsystem";
10
+ import { clientStorage, usePersistentStateString } from "@noya-app/react-utils";
11
+ import React, { CSSProperties, memo, useMemo } from "react";
12
+
13
+ type TitleIconOptions = {
14
+ inlineBlockWrapper?: boolean;
15
+ };
16
+
17
+ export type SectionProps = {
18
+ children: React.ReactNode;
19
+ title?: React.ReactNode;
20
+ onClickTitle?: () => void;
21
+ titleTextStyle?: "small" | "heading5" | "heading4" | "heading3";
22
+ right?: React.ReactNode;
23
+ hideRightWhenCollapsed?: boolean;
24
+ extraPadding?: boolean;
25
+ className?: string;
26
+ style?: CSSProperties;
27
+ titleIcon?: React.ReactNode;
28
+ titleIconOptions?: TitleIconOptions;
29
+ };
30
+
31
+ const titleIconStyle: CSSProperties = {
32
+ marginRight: "8px",
33
+ alignSelf: "flex-start",
34
+ position: "relative",
35
+ top: "2px",
36
+ };
37
+
38
+ const titleIconWrapperStyle: CSSProperties = {
39
+ display: "inline-block",
40
+ };
41
+
42
+ const SectionInternal = ({
43
+ children,
44
+ title,
45
+ onClickTitle,
46
+ titleTextStyle = "heading4",
47
+ right,
48
+ extraPadding,
49
+ className,
50
+ style: styleProp,
51
+ titleIcon,
52
+ titleIconOptions = {
53
+ inlineBlockWrapper: true,
54
+ },
55
+ }: SectionProps) => {
56
+ const style: CSSProperties = useMemo(
57
+ () => ({
58
+ display: "flex",
59
+ flexDirection: "column",
60
+ gap: "8px",
61
+ paddingTop:
62
+ titleTextStyle === "heading3"
63
+ ? "12px"
64
+ : title && extraPadding
65
+ ? "24px"
66
+ : "12px",
67
+ paddingBottom:
68
+ titleTextStyle === "heading3"
69
+ ? "12px"
70
+ : title && extraPadding
71
+ ? "12px"
72
+ : "12px",
73
+ ...styleProp,
74
+ }),
75
+ [titleTextStyle, title, extraPadding, styleProp]
76
+ );
77
+
78
+ const hasChildren = React.Children.toArray(children).some((child) => !!child);
79
+
80
+ const headerStyle: CSSProperties = useMemo(
81
+ () => ({
82
+ marginBottom: hasChildren ? "4px" : undefined,
83
+ minHeight: INPUT_HEIGHT,
84
+ }),
85
+ [hasChildren]
86
+ );
87
+
88
+ return (
89
+ <div style={style} className={className}>
90
+ {(title || titleIcon || right) && (
91
+ <InspectorPrimitives.SectionHeader style={headerStyle}>
92
+ {titleIcon && (
93
+ <div style={titleIconStyle} className={textStyles[titleTextStyle]}>
94
+ {titleIconOptions.inlineBlockWrapper ? (
95
+ <div style={titleIconWrapperStyle}>{titleIcon}</div>
96
+ ) : (
97
+ titleIcon
98
+ )}
99
+ </div>
100
+ )}
101
+ {title && (
102
+ <>
103
+ {onClickTitle ? (
104
+ <Button variant="none" onClick={onClickTitle}>
105
+ <InspectorPrimitives.Title $textStyle={titleTextStyle}>
106
+ {title}
107
+ </InspectorPrimitives.Title>
108
+ </Button>
109
+ ) : (
110
+ <InspectorPrimitives.Title $textStyle={titleTextStyle}>
111
+ {title}
112
+ </InspectorPrimitives.Title>
113
+ )}
114
+ </>
115
+ )}
116
+ <Spacer.Horizontal />
117
+ {right}
118
+ </InspectorPrimitives.SectionHeader>
119
+ )}
120
+ <IndentContext.Provider value={{ indentLevel: 1 }}>
121
+ {children}
122
+ </IndentContext.Provider>
123
+ </div>
124
+ );
125
+ };
126
+
127
+ type ExpandableSectionProps = {
128
+ storageKey: string;
129
+ initialVisibility?: "show" | "hide";
130
+ };
131
+
132
+ const ExpandableSection = ({
133
+ storageKey,
134
+ initialVisibility = "show",
135
+ ...props
136
+ }: SectionProps & ExpandableSectionProps) => {
137
+ const [visibility, setVisibility] = usePersistentStateString<"show" | "hide">(
138
+ clientStorage,
139
+ storageKey,
140
+ initialVisibility
141
+ );
142
+ const expanded = visibility === "show";
143
+
144
+ return (
145
+ <SectionInternal
146
+ {...props}
147
+ right={!props.hideRightWhenCollapsed || expanded ? props.right : null}
148
+ title={
149
+ <div className="flex gap-1 items-center">
150
+ {props.title}
151
+ <IconButton
152
+ iconName={expanded ? "ChevronDownIcon2" : "ChevronRightIcon2"}
153
+ onClick={() => setVisibility(expanded ? "hide" : "show")}
154
+ />
155
+ </div>
156
+ }
157
+ >
158
+ {expanded && props.children}
159
+ </SectionInternal>
160
+ );
161
+ };
162
+
163
+ export const Section = memo(function InspectorSection({
164
+ storageKey,
165
+ ...props
166
+ }: SectionProps & Partial<ExpandableSectionProps>) {
167
+ return storageKey ? (
168
+ <ExpandableSection {...props} storageKey={storageKey} />
169
+ ) : (
170
+ <SectionInternal {...props} />
171
+ );
172
+ });
@@ -141,7 +141,7 @@ export const SegmentedControl = memoGeneric(function SegmentedControl<
141
141
  value={value}
142
142
  onValueChange={handleValueChange}
143
143
  className={cx(
144
- `flex items-stretch flex-auto appearance-none relative outline-none min-h-[27px] rounded bg-input-background py-[2px] px-[2px]`,
144
+ `flex items-stretch flex-auto appearance-none relative outline-none min-h-input-height rounded bg-input-background py-[2px] px-[2px]`,
145
145
  className
146
146
  )}
147
147
  style={style}
@@ -15,6 +15,7 @@ import React, {
15
15
  import { useLabel, useLabelPosition } from "../hooks/useLabel";
16
16
  import { cx } from "../utils/classNames";
17
17
 
18
+ import { getInsetEndStyles } from "../utils/inputs";
18
19
  import { Button } from "./Button";
19
20
  import { renderIcon } from "./Icons";
20
21
  import {
@@ -24,7 +25,7 @@ import {
24
25
  styles,
25
26
  } from "./internal/Menu";
26
27
  import { MenuComponents, MenuViewport } from "./internal/MenuViewport";
27
- import { InsetLabel } from "./Label";
28
+ import { Label } from "./Label";
28
29
 
29
30
  type Props<T extends string> = {
30
31
  id?: string;
@@ -57,6 +58,8 @@ const scrollButtonStyles = `
57
58
  hover:text-text
58
59
  `;
59
60
 
61
+ const insetEndStyles = getInsetEndStyles();
62
+
60
63
  interface SelectMenuTriggerProps {
61
64
  id?: string;
62
65
  style?: React.CSSProperties;
@@ -101,9 +104,11 @@ const SelectMenuTrigger = memoGeneric(function SelectMenuTrigger({
101
104
  >
102
105
  {icon && <span className="pr-1.5">{renderIcon(icon)}</span>}
103
106
  {label && labelPosition === "inset" && (
104
- <InsetLabel className="pr-4" htmlFor={id}>
105
- {label}
106
- </InsetLabel>
107
+ <div className={insetEndStyles}>
108
+ <Label className="pr-4 text-text-disabled" htmlFor={id}>
109
+ {label}
110
+ </Label>
111
+ </div>
107
112
  )}
108
113
  <span className="flex-1 flex">
109
114
  <Select.Value placeholder={placeholder} />
@@ -2,7 +2,8 @@ import * as RadixSlider from "@radix-ui/react-slider";
2
2
  import React, { FocusEventHandler, memo, useCallback, useMemo } from "react";
3
3
  import { useLabel, useLabelPosition } from "../hooks/useLabel";
4
4
  import { cx } from "../utils/classNames";
5
- import { InsetLabel } from "./Label";
5
+ import { getInsetEndStyles } from "../utils/inputs";
6
+ import { Label } from "./Label";
6
7
 
7
8
  type ColorScheme = "primary" | "secondary";
8
9
 
@@ -28,6 +29,8 @@ const thumbStyle = {
28
29
  width: THUMB_WIDTH,
29
30
  };
30
31
 
32
+ const insetEndStyles = getInsetEndStyles();
33
+
31
34
  export const Slider = memo(function Slider({
32
35
  style,
33
36
  className,
@@ -95,7 +98,7 @@ export const Slider = memo(function Slider({
95
98
  value={arrayValue}
96
99
  onValueChange={handleValueChange}
97
100
  className={cx(
98
- "flex relative items-center select-none touch-none h-[27px] rounded overflow-hidden flex-grow max-h-[27px]",
101
+ "flex relative items-center select-none touch-none h-input-height rounded overflow-hidden flex-grow max-h-input-height",
99
102
  className
100
103
  )}
101
104
  style={style}
@@ -105,7 +108,11 @@ export const Slider = memo(function Slider({
105
108
  >
106
109
  <RadixSlider.Track className="flex-grow h-full rounded overflow-hidden bg-input-background">
107
110
  {label && labelPosition === "inset" && (
108
- <InsetLabel htmlFor={id}>{label}</InsetLabel>
111
+ <div className={insetEndStyles}>
112
+ <Label htmlFor={id} className="text-text-disabled">
113
+ {label}
114
+ </Label>
115
+ </div>
109
116
  )}
110
117
  <div
111
118
  style={trackFillStyle}
@@ -117,14 +124,16 @@ export const Slider = memo(function Slider({
117
124
  />
118
125
  </RadixSlider.Track>
119
126
  {label && labelPosition === "inset" && colorScheme !== undefined && (
120
- <InsetLabel className="text-white overflow-hidden" style={labelStyle}>
121
- {label}
122
- </InsetLabel>
127
+ <div className={insetEndStyles}>
128
+ <Label className="text-white overflow-hidden" style={labelStyle}>
129
+ {label}
130
+ </Label>
131
+ </div>
123
132
  )}
124
133
  <RadixSlider.Thumb
125
134
  style={thumbStyle}
126
135
  className={cx(
127
- "block h-[27px] rounded border border-solid bg-slider-thumb-background transition-colors focus:border-primary focus:border-2 outline-none",
136
+ "block h-input-height rounded border border-solid bg-slider-thumb-background transition-colors focus:border-primary focus:border-2 outline-none",
128
137
  colorScheme === undefined && "border-slider-border",
129
138
  colorScheme === "primary" && "border-primary",
130
139
  colorScheme === "secondary" && "border-secondary"