@noya-app/noya-designsystem 0.1.29 → 0.1.31

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.
@@ -1,11 +1,13 @@
1
1
  import { DropdownChevronIcon } from "@noya-app/noya-icons";
2
2
  import * as Select from "@radix-ui/react-select";
3
- import React, { CSSProperties, memo } from "react";
3
+ import { type SelectProps } from "@radix-ui/react-select";
4
+ import React, { CSSProperties, memo, useMemo } from "react";
4
5
  import { styled } from "styled-components";
5
6
  import { Button } from "./Button";
6
7
  import { renderIcon } from "./Icons";
7
8
  import { Spacer } from "./Spacer";
8
9
  import { MenuItem, RegularMenuItem, styles } from "./internal/Menu";
10
+ import { Stack } from "./Stack";
9
11
 
10
12
  type Props<T extends string> = {
11
13
  id?: string;
@@ -17,7 +19,8 @@ type Props<T extends string> = {
17
19
  placeholder?: string;
18
20
  disabled?: boolean;
19
21
  readOnly?: boolean;
20
- };
22
+ label?: React.ReactNode;
23
+ } & Pick<SelectProps, "open">;
21
24
 
22
25
  const readOnlyStyle: CSSProperties = {
23
26
  justifyContent: "flex-start",
@@ -28,6 +31,25 @@ const flexStyle: CSSProperties = {
28
31
  flex: 1,
29
32
  };
30
33
 
34
+ const textStyle: CSSProperties = {
35
+ fontSize: "0.85rem",
36
+ };
37
+
38
+ const SelectLabel = styled.label(({ theme }) => ({
39
+ ...theme.textStyles.label,
40
+ color: theme.colors.textDisabled,
41
+ lineHeight: "19px",
42
+ position: "absolute",
43
+ inset: "0",
44
+ fontWeight: "bold",
45
+ fontSize: "60%",
46
+ pointerEvents: "none",
47
+ display: "flex",
48
+ alignItems: "center",
49
+ justifyContent: "end",
50
+ paddingRight: "24px",
51
+ }));
52
+
31
53
  export const SelectMenu = memo(function SelectMenu<T extends string = string>({
32
54
  id,
33
55
  style,
@@ -38,6 +60,8 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
38
60
  placeholder,
39
61
  disabled,
40
62
  readOnly,
63
+ label,
64
+ open,
41
65
  }: Props<T>) {
42
66
  const selectedItem = menuItems.find(
43
67
  (item): item is RegularMenuItem<T> =>
@@ -45,11 +69,11 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
45
69
  );
46
70
  const icon = selectedItem?.icon;
47
71
 
48
- if (readOnly) {
49
- return (
72
+ const readOnlyButton = useMemo(
73
+ () => (
50
74
  <Button
51
75
  id={id}
52
- style={style}
76
+ style={{ ...style, ...flexStyle }}
53
77
  contentStyle={readOnlyStyle}
54
78
  className={className}
55
79
  disabled={disabled}
@@ -60,15 +84,23 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
60
84
  <Spacer.Horizontal inline size={6} />
61
85
  </>
62
86
  )}
63
- <span style={flexStyle}>{selectedItem?.title ?? value}</span>
87
+ <span style={{ ...flexStyle, ...textStyle }}>
88
+ {selectedItem?.title ?? value}
89
+ </span>
64
90
  </Button>
65
- );
66
- }
91
+ ),
92
+ [icon, id, style, className, disabled, value, selectedItem]
93
+ );
67
94
 
68
- return (
69
- <Select.Root value={value} onValueChange={onSelect}>
95
+ const trigger = useMemo(
96
+ () => (
70
97
  <Select.SelectTrigger asChild>
71
- <Button id={id} style={style} className={className} disabled={disabled}>
98
+ <Button
99
+ id={id}
100
+ style={{ ...textStyle, ...style, ...flexStyle }}
101
+ className={className}
102
+ disabled={disabled}
103
+ >
72
104
  {icon && (
73
105
  <>
74
106
  {renderIcon(icon)}
@@ -82,6 +114,31 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
82
114
  <DropdownChevronIcon />
83
115
  </Button>
84
116
  </Select.SelectTrigger>
117
+ ),
118
+ [icon, id, style, className, disabled, placeholder]
119
+ );
120
+
121
+ if (readOnly) {
122
+ return label ? (
123
+ <Stack.V position="relative">
124
+ {readOnlyButton}
125
+ <SelectLabel htmlFor={id}>{label}</SelectLabel>
126
+ </Stack.V>
127
+ ) : (
128
+ readOnlyButton
129
+ );
130
+ }
131
+
132
+ return (
133
+ <Select.Root value={value} onValueChange={onSelect} open={open}>
134
+ {label ? (
135
+ <Stack.V position="relative" style={flexStyle}>
136
+ {trigger}
137
+ <SelectLabel htmlFor={id}>{label}</SelectLabel>
138
+ </Stack.V>
139
+ ) : (
140
+ trigger
141
+ )}
85
142
  <Select.Portal>
86
143
  <SelectContent>
87
144
  <SelectViewport>
@@ -126,7 +183,7 @@ const SelectItem = React.forwardRef(
126
183
  <Spacer.Horizontal size={8} />
127
184
  </>
128
185
  )}
129
- <Select.ItemText>{children}</Select.ItemText>
186
+ <Select.ItemText style={textStyle}>{children}</Select.ItemText>
130
187
  </StyledItem>
131
188
  );
132
189
  }
@@ -12,7 +12,7 @@ import { BreakpointCollection, mergeBreakpoints } from "../utils/breakpoints";
12
12
  import withSeparatorElements from "../utils/withSeparatorElements";
13
13
 
14
14
  interface StyleProps {
15
- display?: "flex" | "inline-flex" | "none" | "block";
15
+ display?: CSSProperties["display"];
16
16
  visibility?: CSSProperties["visibility"];
17
17
  position?: CSSProperties["position"];
18
18
  zIndex?: CSSProperties["zIndex"];
@@ -78,6 +78,7 @@ interface Props extends StyleProps {
78
78
  breakpoints?: StackBreakpointList | null | false;
79
79
  href?: string; // Shouldn't be here, ideally
80
80
  tabIndex?: number;
81
+ style?: StyleProps;
81
82
  }
82
83
 
83
84
  const Element = styled.div<{
@@ -98,6 +99,10 @@ const StackBase = forwardRef(function StackBase(
98
99
  breakpoints,
99
100
  tabIndex,
100
101
  href,
102
+ paddingHorizontal,
103
+ paddingVertical,
104
+ padding,
105
+ style,
101
106
  ...rest
102
107
  }: Props,
103
108
  forwardedRef: ForwardedRef<HTMLElement>
@@ -110,7 +115,11 @@ const StackBase = forwardRef(function StackBase(
110
115
  display: "flex",
111
116
  position: "relative",
112
117
  alignItems: "stretch",
118
+ padding:
119
+ padding ||
120
+ `${paddingVertical ?? 0} ${paddingHorizontal ?? 0} ${paddingVertical ?? 0} ${paddingHorizontal ?? 0}`,
113
121
  ...rest,
122
+ ...style,
114
123
  };
115
124
 
116
125
  return (
@@ -44,6 +44,7 @@ const SwitchThumb = styled(SwitchPrimitive.Thumb)({
44
44
  interface Props {
45
45
  value: boolean;
46
46
  onChange: (value: boolean) => void;
47
+ /** @default normal */
47
48
  colorScheme?: SwitchColorScheme;
48
49
  disabled?: boolean;
49
50
  }
@@ -3,7 +3,13 @@ import {
3
3
  useDesignSystemTheme,
4
4
  } from "@noya-app/noya-designsystem";
5
5
  import { useKeyboardShortcuts } from "@noya-app/noya-keymap";
6
- import React, { forwardRef, memo, useImperativeHandle, useRef } from "react";
6
+ import React, {
7
+ forwardRef,
8
+ useCallback,
9
+ useImperativeHandle,
10
+ useRef,
11
+ useState,
12
+ } from "react";
7
13
  import {
8
14
  ImperativePanelGroupHandle,
9
15
  ImperativePanelHandle,
@@ -98,7 +104,17 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
98
104
  const leftSidebarRef = useRef<ImperativePanelHandle>(null);
99
105
  const rightSidebarRef = useRef<ImperativePanelHandle>(null);
100
106
 
101
- usePreservePanelSize(panelGroupRef, onChangeLayoutState);
107
+ const [internalLayoutState, setInternalLayoutState] =
108
+ useState<PanelLayoutState | null>(null);
109
+ const handleChangeLayoutState = useCallback(
110
+ (state: PanelLayoutState) => {
111
+ setInternalLayoutState(state);
112
+ onChangeLayoutState?.(state);
113
+ },
114
+ [onChangeLayoutState]
115
+ );
116
+
117
+ usePreservePanelSize(panelGroupRef, handleChangeLayoutState);
102
118
 
103
119
  const theme = useDesignSystemTheme();
104
120
 
@@ -207,7 +223,9 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
207
223
  position: "relative",
208
224
  }}
209
225
  >
210
- {leftPanelContent}
226
+ {internalLayoutState?.leftSidebarCollapsed
227
+ ? null
228
+ : leftPanelContent}
211
229
  </Panel>
212
230
  <PanelResizeHandle
213
231
  style={{
@@ -255,7 +273,9 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
255
273
  position: "relative",
256
274
  }}
257
275
  >
258
- {rightPanelContent}
276
+ {internalLayoutState?.rightSidebarCollapsed
277
+ ? null
278
+ : rightPanelContent}
259
279
  </Panel>
260
280
  </>
261
281
  )}
@@ -264,10 +284,13 @@ const WorkspaceLayoutWithTheme = forwardRef(function WorkspaceLayoutWithTheme(
264
284
  );
265
285
  });
266
286
 
267
- export const WorkspaceLayout = memo(function WorkspaceLayout(props: Props) {
287
+ export const WorkspaceLayout = forwardRef(function WorkspaceLayout(
288
+ props: Props,
289
+ forwardedRef: React.ForwardedRef<IWorkspaceLayout>
290
+ ) {
268
291
  return (
269
292
  <DesignSystemThemeProvider>
270
- <WorkspaceLayoutWithTheme {...props} />
293
+ <WorkspaceLayoutWithTheme {...props} ref={forwardedRef} />
271
294
  </DesignSystemThemeProvider>
272
295
  );
273
296
  });
package/src/index.tsx CHANGED
@@ -41,7 +41,6 @@ export * from "./components/Popover";
41
41
  export * from "./components/Progress";
42
42
  export * from "./components/RadioGroup";
43
43
  export * from "./components/ScrollArea";
44
- export * from "./components/Select";
45
44
  export * from "./components/SelectMenu";
46
45
  export * from "./components/Slider";
47
46
  export * from "./components/Sortable";
@@ -76,6 +75,7 @@ export * as darkTheme from "./theme/dark";
76
75
  export * as lightTheme from "./theme/light";
77
76
  export * from "./utils/createSectionedMenu";
78
77
  export * from "./utils/getGradientBackground";
78
+ export * from "./hooks/usePreservePanelSize";
79
79
  // Utils
80
80
  export * from "./utils/completions";
81
81
  export * from "./utils/fuzzyScorer";
@@ -84,6 +84,5 @@ export * from "./utils/sketchPattern";
84
84
  export { default as withSeparatorElements } from "./utils/withSeparatorElements";
85
85
 
86
86
  // Avoid dependency cycles
87
- export * from "./components/ArrayController";
88
87
  export * from "./components/DimensionInput";
89
88
  export * as InspectorPrimitives from "./components/InspectorPrimitives";
@@ -0,0 +1,283 @@
1
+ import { Config } from "tailwindcss";
2
+
3
+ export default {
4
+ content: ["./src/**/*.{ts,tsx}"],
5
+ // if you add the dark className anywhere in the DOM tree all descendants will use dark style variables
6
+ darkMode: "class",
7
+ theme: {
8
+ extend: {
9
+ // colors should alway have a flat structure with the exception of dark/DEFAULT variables
10
+ // keys should always be kebab-case, since that's the tailwind convention
11
+ colors: {
12
+ "logo-fill": {
13
+ DEFAULT: "rgb(150, 152, 172)",
14
+ dark: "rgb(248, 248, 250)",
15
+ },
16
+ "logo-highlight": {
17
+ DEFAULT: "rgb(150, 152, 172)",
18
+ dark: "rgb(248, 248, 250)",
19
+ },
20
+ background: "rgb(255, 255, 255)",
21
+ text: {
22
+ DEFAULT: "rgb(38, 48, 83)",
23
+ dark: "rgb(248, 248, 250)",
24
+ },
25
+ "text-muted": {
26
+ DEFAULT: "rgb(107, 113, 136)",
27
+ dark: "rgb(180, 179, 182)",
28
+ },
29
+ "text-subtle": {
30
+ DEFAULT: "rgb(117, 121, 129)",
31
+ dark: "rgb(180, 179, 182)",
32
+ },
33
+ "text-disabled": {
34
+ DEFAULT: "rgb(150, 152, 172)",
35
+ dark: "rgb(100, 99, 102)",
36
+ },
37
+ "text-decorative": {
38
+ light: "rgb(168, 185, 212)",
39
+ },
40
+ "divider-subtle": {
41
+ DEFAULT: "rgba(30, 50, 100, 0.04)",
42
+ dark: "rgba(255, 255, 255, 0.04)",
43
+ },
44
+ divider: {
45
+ DEFAULT: "rgba(30, 50, 100, 0.07)",
46
+ dark: "rgba(255, 255, 255, 0.08)",
47
+ },
48
+ "divider-strong": {
49
+ DEFAULT: "rgba(30, 50, 100, 0.09)",
50
+ dark: "rgba(0, 0, 0, 1)",
51
+ },
52
+ primary: {
53
+ DEFAULT: "rgb(103, 70, 255)",
54
+ dark: "rgb(119, 66, 255)",
55
+ },
56
+ "primary-light": {
57
+ DEFAULT: "rgb(147, 86, 255)",
58
+ dark: "rgb(134, 86, 255)",
59
+ },
60
+ "primary-pastel": "rgba(234, 230, 255)",
61
+ secondary: "rgb(0, 151, 117)",
62
+ "secondary-light": "rgb(0, 160, 129)",
63
+ "secondary-pastel": "rgb(205, 238, 231)",
64
+ "secondary-bright": {
65
+ DEFAULT: "#0ab557",
66
+ dark: "#36fe91",
67
+ },
68
+ "input-background": {
69
+ DEFAULT: "rgb(240, 242, 246)",
70
+ dark: "rgba(181, 178, 255, 0.08)",
71
+ },
72
+ "input-background-light": {
73
+ DEFAULT: "rgb(243, 245, 249)",
74
+ dark: "rgba(181, 178, 255, 0.10)",
75
+ },
76
+ "code-background": {
77
+ DEFAULT: "rgb(250, 250, 250)",
78
+ dark: "rgb(20, 19, 23)",
79
+ },
80
+ "code-background-decorative-dark": "#435080",
81
+ "selected-background": "rgb(242, 245, 250)",
82
+ "breadcrumb-text": {
83
+ DEFAULT: "rgb(107, 113, 136)",
84
+ dark: "rgb(180, 179, 182)",
85
+ },
86
+ "breadcrumb-text-hover": "rgb(117, 121, 129)",
87
+ "breadcrumb-icon": "rgb(129, 131, 165)",
88
+ "canvas-background": {
89
+ DEFAULT: "rgb(249, 249, 249)",
90
+ dark: "rgb(20, 19, 23)",
91
+ },
92
+ "canvas-grid": {
93
+ DEFAULT: "rgba(0, 0, 0, 0.05)",
94
+ dark: "rgba(0, 0, 0, 0.1)",
95
+ },
96
+ "sidebar-background": {
97
+ DEFAULT: "rgb(255, 255, 255)",
98
+ dark: "rgb(34, 33, 39)",
99
+ },
100
+ "sidebar-background-transparent": {
101
+ DEFAULT: "rgba(255, 255, 255, 0.85)",
102
+ dark: "rgba(34, 33, 39, 0.95)",
103
+ },
104
+ "popover-background": {
105
+ DEFAULT: "rgb(252, 252, 252)",
106
+ dark: "rgb(34, 33, 39)",
107
+ },
108
+ "popover-divider": {
109
+ DEFAULT: "transparent",
110
+ dark: "rgba(255, 255, 255, 0.08)",
111
+ },
112
+ "listview-raised-background": {
113
+ DEFAULT: "rgba(0, 0, 0, 0.03)",
114
+ dark: "rgba(181, 178, 255, 0.1)",
115
+ },
116
+ "listview-editing-background": {
117
+ DEFAULT: "#fff",
118
+ dark: "#000",
119
+ },
120
+ "slider-background": {
121
+ DEFAULT: "white",
122
+ dark: "#BBB",
123
+ },
124
+ "slider-border": "#BBB",
125
+ "radio-group-background": {
126
+ DEFAULT: "white",
127
+ dark: "rgba(181, 178, 255, 0.08)",
128
+ },
129
+ mask: {
130
+ DEFAULT: "rgb(12, 193, 67)",
131
+ dark: "rgb(102, 187, 106)",
132
+ },
133
+ "transparent-checker": {
134
+ DEFAULT: "rgba(255, 255, 255, 0.8)",
135
+ dark: "rgba(255, 255, 255, 0.3)",
136
+ },
137
+ scrollbar: {
138
+ DEFAULT: "rgba(199, 199, 199, 0.8)",
139
+ dark: "rgba(199, 199, 199, 0.2)",
140
+ },
141
+ "placeholder-dots": {
142
+ DEFAULT: "rgba(0, 0, 0, 0.3)",
143
+ dark: "rgba(255, 255, 255, 0.3)",
144
+ },
145
+ "drag-outline": {
146
+ DEFAULT: "rgb(103, 70, 255)",
147
+ dark: "white",
148
+ },
149
+ "active-background": {
150
+ DEFAULT: "rgba(0, 0, 0, 0.1)",
151
+ dark: "rgba(181, 178, 255, 0.08)",
152
+ },
153
+ "thumbnail-background": {
154
+ DEFAULT: "#f0efff",
155
+ dark: "#1f1d33",
156
+ },
157
+ "thumbnail-shadow": {
158
+ DEFAULT: "#D3CEED66",
159
+ dark: "#1f1d3366",
160
+ },
161
+ "inline-code-text": "rgb(103, 70, 255)",
162
+ "inline-code-background": "rgb(240, 242, 246)",
163
+ "text-link": "rgb(103, 70, 255)",
164
+ "text-link-focused": "rgb(147, 86, 255)",
165
+ },
166
+ fontFamily: {
167
+ sans: [
168
+ "__Inter_6b0edc",
169
+ "__Inter_Fallback_6b0edc",
170
+ "-apple-system",
171
+ "BlinkMacSystemFont",
172
+ "Helvetica Neue",
173
+ "Segoe UI",
174
+ "Roboto",
175
+ "Helvetica",
176
+ "Arial",
177
+ "sans-serif",
178
+ ],
179
+ mono: ["Menlo", "Monaco", "Consolas", "Courier New", "monospace"],
180
+ },
181
+ // Type scale
182
+ // The last one, 0.85, I just eyeballed
183
+ // old: typeScale = [3.052, 2.441, 1.953, 1.563, 1.25, 1, 0.85]; // Major third
184
+ fontSize: {
185
+ title: [
186
+ "3.052rem",
187
+ {
188
+ lineHeight: "1.4",
189
+ letterSpacing: "-0.05em",
190
+ },
191
+ ],
192
+ subtitle: [
193
+ "1.563rem",
194
+ {
195
+ lineHeight: "1.75",
196
+ letterSpacing: "-0.05em",
197
+ },
198
+ ],
199
+ heading1: [
200
+ "1.953rem",
201
+ {
202
+ lineHeight: "1.6",
203
+ letterSpacing: "-0.025em",
204
+ },
205
+ ],
206
+ heading2: [
207
+ "1.563rem",
208
+ {
209
+ lineHeight: "1.5",
210
+ letterSpacing: "-0.025em",
211
+ },
212
+ ],
213
+ heading3: [
214
+ "1.25rem",
215
+ {
216
+ lineHeight: "1.4",
217
+ letterSpacing: "-0.025em",
218
+ },
219
+ ],
220
+ heading4: [
221
+ "1rem",
222
+ {
223
+ lineHeight: "1.4",
224
+ },
225
+ ],
226
+ heading5: [
227
+ "0.85rem",
228
+ {
229
+ lineHeight: "1.4",
230
+ },
231
+ ],
232
+ body: [
233
+ "1",
234
+ {
235
+ lineHeight: "1.4",
236
+ },
237
+ ],
238
+ small: [
239
+ "1",
240
+ {
241
+ lineHeight: "19px",
242
+ },
243
+ ],
244
+ code: [
245
+ "90%",
246
+ {
247
+ lineHeight: "1.5",
248
+ },
249
+ ],
250
+ button: [
251
+ "0.8rem",
252
+ {
253
+ lineHeight: "1.4",
254
+ letterSpacing: "-0.2px",
255
+ },
256
+ ],
257
+ label: [
258
+ "0.62rem",
259
+ {
260
+ lineHeight: "19px",
261
+ letterSpacing: "-0.3px",
262
+ },
263
+ ],
264
+ },
265
+ spacing: {
266
+ nano: "2px",
267
+ micro: "4px",
268
+ small: "8px",
269
+ medium: "16px",
270
+ large: "32px",
271
+ xlarge: "64px",
272
+ xxlarge: "128px",
273
+ "inset-top": "46px",
274
+ "sidebar-width": "260px",
275
+ "toolbar-height": "8px",
276
+ "toolbar-separator": "8px",
277
+ "inspector-h-separator": "8px",
278
+ "inspector-v-separator": "10px",
279
+ "dialog-padding": "16px",
280
+ },
281
+ },
282
+ },
283
+ } as Config;
package/tailwind.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Get the config from your actual config file
2
+ import tailwindConfig from "./tailwind.config";
3
+
4
+ type ExtractThemeTypes<T> = T extends { theme: { extend: infer E } }
5
+ ? E
6
+ : never;
7
+
8
+ declare module "tailwindcss/types/generated/default-theme" {
9
+ export interface DefaultTheme
10
+ extends ExtractThemeTypes<typeof tailwindConfig> {}
11
+ }
package/tsconfig.json CHANGED
@@ -1,3 +1,6 @@
1
1
  {
2
- "extends": "@repo/typescript-config/library.json"
2
+ "extends": "@repo/typescript-config/library.json",
3
+ "compilerOptions": {
4
+ "types": ["tailwindcss/types"],
5
+ },
3
6
  }