@noya-app/noya-designsystem 0.1.30 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-designsystem",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "@dnd-kit/sortable": "4.0.0",
16
16
  "@noya-app/noya-colorpicker": "0.1.11",
17
17
  "@noya-app/noya-utils": "0.1.2",
18
- "@noya-app/noya-geometry": "0.1.4",
18
+ "@noya-app/noya-geometry": "0.1.5",
19
19
  "@noya-app/noya-icons": "0.1.4",
20
20
  "@noya-app/noya-keymap": "0.1.2",
21
21
  "@radix-ui/primitive": "^1.0.0",
@@ -46,8 +46,9 @@
46
46
  "@types/react-dom": "*",
47
47
  "@types/react-virtualized": "^9.21.21",
48
48
  "@types/react-window": "^1.8.5",
49
+ "esbuild-plugin-react-virtualized": "1.0.4",
49
50
  "react-virtualized": "9.22.5",
50
- "esbuild-plugin-react-virtualized": "1.0.4"
51
+ "tailwindcss": "^3.4.14"
51
52
  },
52
53
  "peerDependencies": {
53
54
  "react": "*",
@@ -19,7 +19,7 @@ type ButtonVariant =
19
19
  | "floating"
20
20
  | "none";
21
21
 
22
- type ButtonSize = "normal" | "large";
22
+ type ButtonSize = "small" | "normal" | "large";
23
23
 
24
24
  /* ----------------------------------------------------------------------------
25
25
  * Element
@@ -135,8 +135,8 @@ export const ButtonElement = styled.button<{
135
135
  background: "white",
136
136
  color: theme.colors.text,
137
137
  boxShadow: "0 1px 2px rgba(0, 0, 0, 0.1)",
138
- fontSize: "12px",
139
- padding: "0px 4px",
138
+ fontSize: $size === "small" ? "10px" : "12px",
139
+ padding: $size === "small" ? "0px 2px" : "0px 4px",
140
140
  "&:hover": {
141
141
  opacity: 0.8,
142
142
  },
@@ -184,7 +184,9 @@ export interface ButtonRootProps {
184
184
  children: ReactNode;
185
185
  active?: boolean;
186
186
  disabled?: boolean;
187
+ /** @default normal */
187
188
  variant?: ButtonVariant;
189
+ /** @default normal */
188
190
  size?: ButtonSize;
189
191
  tooltip?: ReactNode;
190
192
  onClick?: (event: React.MouseEvent) => void;
@@ -32,8 +32,8 @@ export type InputFieldSize = "small" | "medium" | "large";
32
32
 
33
33
  type InputFieldContextValue = {
34
34
  labelPosition: LabelPosition;
35
- labelSize: number;
36
- hasLabel: boolean;
35
+ labelSize?: number;
36
+ buttonSize?: number;
37
37
  hasDropdown: boolean;
38
38
  /** @default medium */
39
39
  size: InputFieldSize;
@@ -42,12 +42,13 @@ type InputFieldContextValue = {
42
42
  inputRef?: ForwardedRef<HTMLInputElement>;
43
43
  setInputRef?: (ref: ForwardedRef<HTMLInputElement>) => void;
44
44
  setLabelWidth?: (width: number) => void;
45
+ setButtonWidth?: (width: number) => void;
45
46
  };
46
47
 
47
48
  const InputFieldContext = createContext<InputFieldContextValue>({
48
49
  labelPosition: "end",
49
- labelSize: 6,
50
- hasLabel: false,
50
+ labelSize: undefined,
51
+ buttonSize: undefined,
51
52
  hasDropdown: false,
52
53
  size: "medium",
53
54
  isFocused: false,
@@ -62,26 +63,31 @@ const LabelContainer = styled.label<{
62
63
  pointerEvents: Property.PointerEvents;
63
64
  $labelPosition: LabelPosition;
64
65
  $hasDropdown: boolean;
65
- }>(({ theme, $labelPosition, $hasDropdown, pointerEvents }) => ({
66
- ...theme.textStyles.label,
67
- fontWeight: "bold",
68
- color: theme.colors.textDisabled,
69
- position: "absolute",
70
- top: 0,
71
- right: $labelPosition === "end" ? 0 : undefined,
72
- bottom: 0,
73
- left: $labelPosition === "start" ? 0 : undefined,
74
- display: "flex",
75
- alignItems: "center",
76
- pointerEvents,
77
- userSelect: "none",
78
- ...($labelPosition === "start"
79
- ? { justifyContent: "flex-start", paddingLeft: "6px" }
80
- : {
81
- justifyContent: "flex-end",
82
- paddingRight: $hasDropdown ? "16px" : "6px",
83
- }),
84
- }));
66
+ $size: InputFieldSize;
67
+ }>(({ theme, $labelPosition, $hasDropdown, pointerEvents, $size }) => {
68
+ const offsetRelativeToSize =
69
+ $size === "large" ? 6 : $size === "medium" ? 2 : 0;
70
+ return {
71
+ ...theme.textStyles.label,
72
+ fontWeight: "bold",
73
+ color: theme.colors.textDisabled,
74
+ position: "absolute",
75
+ top: 0,
76
+ bottom: 0,
77
+ right: $labelPosition === "end" ? offsetRelativeToSize : undefined,
78
+ left: $labelPosition === "start" ? offsetRelativeToSize : undefined,
79
+ display: "flex",
80
+ alignItems: "center",
81
+ pointerEvents,
82
+ userSelect: "none",
83
+ ...($labelPosition === "start"
84
+ ? { justifyContent: "flex-start", paddingLeft: "6px" }
85
+ : {
86
+ justifyContent: "flex-end",
87
+ paddingRight: $hasDropdown ? "16px" : "6px",
88
+ }),
89
+ };
90
+ });
85
91
 
86
92
  interface InputFieldLabelProps {
87
93
  children?: ReactNode;
@@ -89,12 +95,13 @@ interface InputFieldLabelProps {
89
95
  style?: React.CSSProperties;
90
96
  }
91
97
 
98
+ /** Inserts a Label at the start or end of the input given a `labelPosition`on InputField.Root. Should not be used with an InputField.Button */
92
99
  const InputFieldLabel = memo(
93
100
  forwardRef(function InputFieldLabel(
94
101
  { children = false, pointerEvents = "none", style }: InputFieldLabelProps,
95
102
  forwardedRef: ForwardedRef<HTMLLabelElement>
96
103
  ) {
97
- const { labelPosition, hasDropdown, setLabelWidth } =
104
+ const { labelPosition, hasDropdown, setLabelWidth, size } =
98
105
  useContext(InputFieldContext);
99
106
 
100
107
  const ref = useRef<HTMLLabelElement | null>(null);
@@ -111,6 +118,7 @@ const InputFieldLabel = memo(
111
118
 
112
119
  return (
113
120
  <LabelContainer
121
+ $size={size}
114
122
  ref={(element) => {
115
123
  ref.current = element;
116
124
  assignRef(forwardedRef, element);
@@ -180,51 +188,74 @@ const ButtonContainer = styled.span<{ $size: InputFieldSize }>(
180
188
  ({ theme, $size }) => ({
181
189
  position: "absolute",
182
190
  right: $size === "large" ? "9px" : $size === "medium" ? "4px" : "2px",
183
- top: $size === "large" ? "8px" : $size === "medium" ? "4px" : "2px",
191
+ top: $size === "large" ? "10px" : $size === "medium" ? "4px" : "2px",
184
192
  })
185
193
  );
186
194
 
187
- const InputFieldButton = memo(function InputFieldButton({
188
- children,
189
- onClick,
190
- }: {
191
- children?: ReactNode;
192
- onClick?: () => void;
193
- }) {
194
- const { size, inputRef } = useContext(InputFieldContext);
195
-
196
- const defaultHandleClick = useCallback(
197
- (event: React.MouseEvent) => {
198
- if (inputRef && typeof inputRef !== "function") {
199
- inputRef.current?.focus();
200
- // Select all text
201
- inputRef.current?.setSelectionRange(0, inputRef.current.value.length);
202
- }
195
+ /** Inserts a Button at the end of the input. Should not be used with an InputField.Label */
196
+ const InputFieldButton = memo(
197
+ forwardRef(function InputFieldButton(
198
+ {
199
+ children,
200
+ onClick,
201
+ }: {
202
+ children?: ReactNode;
203
+ onClick?: () => void;
204
+ },
205
+ forwardedRef: ForwardedRef<HTMLButtonElement>
206
+ ) {
207
+ const { size, inputRef, setButtonWidth } = useContext(InputFieldContext);
208
+
209
+ const ref = useRef<HTMLButtonElement | null>(null);
210
+
211
+ useLayoutEffect(() => {
212
+ if (!setButtonWidth) return;
213
+
214
+ const width = ref.current?.getBoundingClientRect().width;
215
+
216
+ if (!width) return;
203
217
 
218
+ setButtonWidth(width);
219
+ }, [setButtonWidth]);
220
+
221
+ const defaultHandleClick = useCallback(
222
+ (event: React.MouseEvent) => {
223
+ if (inputRef && typeof inputRef !== "function") {
224
+ inputRef.current?.focus();
225
+ // Select all text
226
+ inputRef.current?.setSelectionRange(0, inputRef.current.value.length);
227
+ }
228
+
229
+ event.preventDefault();
230
+ event.stopPropagation();
231
+ },
232
+ [inputRef]
233
+ );
234
+
235
+ const handlePointerDown = useCallback((event: React.PointerEvent) => {
204
236
  event.preventDefault();
205
237
  event.stopPropagation();
206
- },
207
- [inputRef]
208
- );
209
-
210
- const handlePointerDown = useCallback((event: React.PointerEvent) => {
211
- event.preventDefault();
212
- event.stopPropagation();
213
- }, []);
238
+ }, []);
214
239
 
215
- return (
216
- <ButtonContainer $size={size}>
217
- <Button
218
- variant="floating"
219
- onClick={onClick ?? defaultHandleClick}
220
- onPointerDown={handlePointerDown}
221
- tabIndex={-1}
222
- >
223
- {children}
224
- </Button>
225
- </ButtonContainer>
226
- );
227
- });
240
+ return (
241
+ <ButtonContainer $size={size}>
242
+ <Button
243
+ ref={(element) => {
244
+ ref.current = element;
245
+ assignRef(forwardedRef, element);
246
+ }}
247
+ variant="floating"
248
+ onClick={onClick ?? defaultHandleClick}
249
+ onPointerDown={handlePointerDown}
250
+ tabIndex={-1}
251
+ size={size === "medium" ? "normal" : size}
252
+ >
253
+ {children}
254
+ </Button>
255
+ </ButtonContainer>
256
+ );
257
+ })
258
+ );
228
259
 
229
260
  /* ----------------------------------------------------------------------------
230
261
  * Input
@@ -243,8 +274,7 @@ export const InputElement = styled(TextInput)<{
243
274
  disabled?: boolean;
244
275
  readOnly?: boolean;
245
276
  $labelPosition: LabelPosition;
246
- $labelSize: number;
247
- $hasLabel: boolean;
277
+ $labelSize?: number;
248
278
  $hasDropdown: boolean;
249
279
  $textAlign?: Property.TextAlign;
250
280
  $variant?: InputFieldVariant;
@@ -256,7 +286,6 @@ export const InputElement = styled(TextInput)<{
256
286
  $hasDropdown,
257
287
  $textAlign,
258
288
  disabled,
259
- $hasLabel,
260
289
  readOnly,
261
290
  $variant = "normal",
262
291
  $size,
@@ -289,11 +318,11 @@ export const InputElement = styled(TextInput)<{
289
318
  $size === "large" ? "10px" : $size === "medium" ? "4px" : "1px",
290
319
  paddingLeft:
291
320
  ($size === "large" ? 10 : $size === "medium" ? 6 : 4) +
292
- ($hasLabel && $labelPosition === "start" ? 6 + $labelSize : 0) +
321
+ ($labelSize && $labelPosition === "start" ? 6 + $labelSize : 0) +
293
322
  "px",
294
323
  paddingRight:
295
324
  ($size === "large" ? 10 : $size === "medium" ? 6 : 4) +
296
- ($hasLabel && $labelPosition === "end" ? 6 + $labelSize : 0) +
325
+ ($labelSize && $labelPosition === "end" ? 6 + $labelSize : 0) +
297
326
  ($hasDropdown ? 11 : 0) +
298
327
  "px",
299
328
  background: theme.colors.inputBackground,
@@ -322,6 +351,7 @@ export const InputElement = styled(TextInput)<{
322
351
  };
323
352
  });
324
353
 
354
+ /** Should always be wrapped in InputField.Root */
325
355
  const InputFieldInput = forwardRef(function InputFieldInput(
326
356
  // onFocusChange should only be passed from the root, never directly
327
357
  {
@@ -338,7 +368,7 @@ const InputFieldInput = forwardRef(function InputFieldInput(
338
368
  labelPosition,
339
369
  labelSize,
340
370
  hasDropdown,
341
- hasLabel,
371
+ buttonSize,
342
372
  size,
343
373
  onFocusChange,
344
374
  setInputRef,
@@ -359,8 +389,7 @@ const InputFieldInput = forwardRef(function InputFieldInput(
359
389
  <InputElement
360
390
  ref={forwardedRef}
361
391
  $labelPosition={labelPosition}
362
- $labelSize={labelSize}
363
- $hasLabel={hasLabel}
392
+ $labelSize={labelSize || buttonSize}
364
393
  $hasDropdown={hasDropdown}
365
394
  $size={size}
366
395
  $variant={variant}
@@ -376,7 +405,6 @@ const InputFieldTypeahead = (props: { prefix: string; value: string }) => {
376
405
  labelPosition,
377
406
  labelSize,
378
407
  hasDropdown,
379
- hasLabel,
380
408
  size,
381
409
  // onFocusChange,
382
410
  } = useContext(InputFieldContext);
@@ -386,7 +414,6 @@ const InputFieldTypeahead = (props: { prefix: string; value: string }) => {
386
414
  as="span"
387
415
  $labelPosition={labelPosition}
388
416
  $labelSize={labelSize}
389
- $hasLabel={hasLabel}
390
417
  $hasDropdown={hasDropdown}
391
418
  $size={size}
392
419
  // onFocusChange={onFocusChange}
@@ -529,15 +556,16 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
529
556
  * Root
530
557
  * ------------------------------------------------------------------------- */
531
558
 
532
- const RootContainer = styled.div<{ $width?: number; $flex?: string }>(
533
- ({ theme, $flex, $width }) => ({
534
- flex: $flex ?? "1",
535
- display: "flex",
536
- flexDirection: "row",
537
- position: "relative",
538
- maxWidth: typeof $width === "number" ? `${$width}px` : undefined,
539
- })
540
- );
559
+ const RootContainer = styled.div<{
560
+ $width?: number;
561
+ $flex?: string;
562
+ }>(({ theme, $flex, $width }) => ({
563
+ flex: $flex ?? "1",
564
+ display: "flex",
565
+ flexDirection: "row",
566
+ position: "relative",
567
+ maxWidth: typeof $width === "number" ? `${$width}px` : undefined,
568
+ }));
541
569
 
542
570
  interface InputFieldRootProps {
543
571
  id?: string;
@@ -574,13 +602,11 @@ function InputFieldRoot({
574
602
  const hasDropdown = childrenArray.some(
575
603
  (child) => isValidElement(child) && child.type === InputFieldDropdownMenu
576
604
  );
577
- const hasLabel = childrenArray.some(
578
- (child) => isValidElement(child) && child.type === InputFieldLabel
579
- );
580
605
 
581
606
  const [isFocused, setIsFocused] = React.useState(false);
582
607
 
583
608
  const [measuredLabelSize, setMeasuredLabelSize] = React.useState<number>();
609
+ const [measuredButtonSize, setMeasuredButtonSize] = React.useState<number>();
584
610
  const [measuredWidth, setMeasuredWidth] = React.useState<number>();
585
611
 
586
612
  const handleFocusChange = useCallback(
@@ -606,22 +632,23 @@ function InputFieldRoot({
606
632
  const contextValue = useMemo(
607
633
  (): InputFieldContextValue => ({
608
634
  labelPosition,
609
- labelSize: measuredLabelSize ?? labelSize ?? 6,
635
+ labelSize: measuredLabelSize ?? labelSize,
636
+ buttonSize: measuredButtonSize,
610
637
  hasDropdown,
611
- hasLabel,
612
638
  size,
613
639
  isFocused,
614
640
  onFocusChange: handleFocusChange,
615
641
  inputRef,
616
642
  setInputRef,
617
643
  setLabelWidth: setMeasuredLabelSize,
644
+ setButtonWidth: setMeasuredButtonSize,
618
645
  }),
619
646
  [
620
647
  labelPosition,
621
648
  measuredLabelSize,
622
649
  labelSize,
650
+ measuredButtonSize,
623
651
  hasDropdown,
624
- hasLabel,
625
652
  size,
626
653
  isFocused,
627
654
  handleFocusChange,
@@ -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 (
@@ -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, 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
  )}
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";