@noya-app/noya-designsystem 0.1.46 → 0.1.48
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +16 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +208 -98
- package/dist/index.d.ts +208 -98
- package/dist/index.js +2148 -1604
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2153 -1618
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -7
- package/src/__tests__/validateDropIndicator.test.ts +263 -0
- package/src/components/ActionMenu.tsx +40 -0
- package/src/components/ActivityIndicator.tsx +7 -1
- package/src/components/Collection.tsx +12 -2
- package/src/components/Combobox.tsx +14 -1
- package/src/components/ComboboxMenu.tsx +14 -5
- package/src/components/Dialog.tsx +28 -31
- package/src/components/Drawer.tsx +98 -0
- package/src/components/Grid.tsx +57 -27
- package/src/components/GridView.tsx +39 -25
- package/src/components/InputField.tsx +87 -92
- package/src/components/Label.tsx +7 -7
- package/src/components/List.tsx +42 -16
- package/src/components/ListView.tsx +21 -17
- package/src/components/MediaThumbnail.tsx +117 -0
- package/src/components/SearchCompletionMenu.tsx +31 -37
- package/src/components/SelectMenu.tsx +19 -17
- package/src/components/Sortable.tsx +70 -44
- package/src/components/Switch.tsx +1 -1
- package/src/components/internal/Menu.tsx +38 -23
- package/src/components/internal/MenuViewport.tsx +7 -1
- package/src/components/internal/SelectItem.tsx +51 -27
- package/src/components/internal/__tests__/Menu.test.tsx +12 -0
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +86 -0
- package/src/components/workspace/PanelWorkspaceLayout.tsx +102 -0
- package/src/components/{WorkspaceLayout.tsx → workspace/WorkspaceLayout.tsx} +109 -106
- package/src/components/workspace/types.ts +31 -0
- package/src/contexts/DialogContext.tsx +91 -30
- package/src/hooks/usePreservePanelSize.tsx +1 -5
- package/src/index.css +3 -1
- package/src/index.tsx +4 -1
- package/tailwind.config.ts +1 -0
package/src/components/Grid.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useFileDropTarget } from "@noya-app/react-utils";
|
|
2
|
+
import React, { useImperativeHandle, useRef, useState } from "react";
|
|
2
3
|
import {
|
|
3
4
|
forwardRefGeneric,
|
|
4
5
|
memoGeneric,
|
|
5
6
|
} from "../../../noya-react-utils/src/utils/reactGenerics";
|
|
6
7
|
import { cx } from "../utils/classNames";
|
|
7
8
|
import { updateSelection } from "../utils/selection";
|
|
9
|
+
import { ActionMenu } from "./ActionMenu";
|
|
8
10
|
import { CollectionProps, CollectionRef } from "./Collection";
|
|
9
11
|
import { EditableText } from "./EditableText";
|
|
10
12
|
import { GridView } from "./GridView";
|
|
@@ -35,15 +37,15 @@ export const getGridSize = (size: GridViewSize) => {
|
|
|
35
37
|
},
|
|
36
38
|
medium: {
|
|
37
39
|
minColumnWidth: "220px",
|
|
38
|
-
gap:
|
|
40
|
+
gap: 30,
|
|
39
41
|
},
|
|
40
42
|
large: {
|
|
41
43
|
minColumnWidth: "280px",
|
|
42
|
-
gap:
|
|
44
|
+
gap: 40,
|
|
43
45
|
},
|
|
44
46
|
xl: {
|
|
45
47
|
minColumnWidth: "400px",
|
|
46
|
-
gap:
|
|
48
|
+
gap: 48,
|
|
47
49
|
},
|
|
48
50
|
};
|
|
49
51
|
|
|
@@ -83,13 +85,16 @@ export const Grid = memoGeneric(
|
|
|
83
85
|
scrollable = false,
|
|
84
86
|
// acceptsDrop,
|
|
85
87
|
// onMoveItem,
|
|
86
|
-
|
|
88
|
+
onFilesDrop,
|
|
87
89
|
renamable,
|
|
90
|
+
onClickItem,
|
|
88
91
|
onDoubleClickItem,
|
|
92
|
+
renderEmptyState,
|
|
89
93
|
} = props;
|
|
90
94
|
|
|
91
95
|
const [hoveredId, setHoveredId] = useState<string>();
|
|
92
96
|
const [internalRenamingId, setRenamingId] = useState<string>();
|
|
97
|
+
const [dropdownOpenId, setDropdownOpenId] = useState<string>();
|
|
93
98
|
|
|
94
99
|
useImperativeHandle(ref, () => ({
|
|
95
100
|
editName: (id: string) => {
|
|
@@ -104,29 +109,35 @@ export const Grid = memoGeneric(
|
|
|
104
109
|
if (!onSelectionChange) return;
|
|
105
110
|
|
|
106
111
|
setRenamingId(undefined);
|
|
112
|
+
|
|
107
113
|
const newSelectedIds = updateSelection(
|
|
108
114
|
items.map(getId),
|
|
109
115
|
selectedIds,
|
|
110
116
|
itemId,
|
|
111
117
|
event
|
|
112
118
|
);
|
|
119
|
+
|
|
113
120
|
onSelectionChange(newSelectedIds, event);
|
|
114
121
|
};
|
|
115
122
|
|
|
116
|
-
const handleMenuAction = (action: M) => {
|
|
123
|
+
const handleMenuAction = (action: M, actionItem: T) => {
|
|
117
124
|
if (!onSelectMenuItem) return;
|
|
118
125
|
|
|
119
126
|
const selectedItems = items.filter((item) =>
|
|
120
127
|
selectedIds.includes(getId(item))
|
|
121
128
|
);
|
|
122
|
-
|
|
129
|
+
|
|
130
|
+
onSelectMenuItem(
|
|
131
|
+
action,
|
|
132
|
+
selectedItems.length === 0 ? [actionItem] : selectedItems
|
|
133
|
+
);
|
|
123
134
|
};
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
const fileDropTargetRef = useRef<HTMLDivElement>(null);
|
|
137
|
+
const { isDropTargetActive, dropTargetProps } = useFileDropTarget(
|
|
138
|
+
fileDropTargetRef,
|
|
139
|
+
onFilesDrop
|
|
140
|
+
);
|
|
130
141
|
|
|
131
142
|
const { minColumnWidth, gap } = getGridSize(size);
|
|
132
143
|
|
|
@@ -134,23 +145,45 @@ export const Grid = memoGeneric(
|
|
|
134
145
|
<GridView.Root
|
|
135
146
|
minColumnWidth={minColumnWidth}
|
|
136
147
|
scrollable={scrollable}
|
|
137
|
-
className={className}
|
|
138
148
|
gap={gap}
|
|
149
|
+
className={cx(isDropTargetActive && "bg-primary-pastel", className)}
|
|
150
|
+
{...dropTargetProps}
|
|
151
|
+
ref={fileDropTargetRef}
|
|
139
152
|
>
|
|
140
|
-
<GridView.Section>
|
|
153
|
+
<GridView.Section renderEmptyState={renderEmptyState}>
|
|
141
154
|
{items.map((item) => {
|
|
142
155
|
const id = getId(item);
|
|
143
|
-
const
|
|
156
|
+
const isDropdownOpen = dropdownOpenId === id;
|
|
157
|
+
const isHovered =
|
|
158
|
+
testHoveredId === id || currentHoveredId === id || isDropdownOpen;
|
|
144
159
|
const itemIsRenamable = getRenamable?.(item) ?? renamable;
|
|
145
160
|
const isSelected = selectedIds.includes(id);
|
|
146
161
|
const isRenaming = itemIsRenamable && onRename && renamingId === id;
|
|
147
162
|
const isTestingRenaming = testRenamingId === id;
|
|
148
|
-
|
|
149
|
-
|
|
163
|
+
const thumbnail = renderThumbnail?.({
|
|
164
|
+
item,
|
|
165
|
+
selected: isSelected,
|
|
166
|
+
});
|
|
150
167
|
const action =
|
|
151
168
|
(isHovered || isSelected) &&
|
|
152
169
|
!isRenaming &&
|
|
153
|
-
|
|
170
|
+
(typeof renderAction === "function"
|
|
171
|
+
? renderAction(item, isSelected)
|
|
172
|
+
: renderAction === "menu" &&
|
|
173
|
+
menuItems && (
|
|
174
|
+
<ActionMenu
|
|
175
|
+
menuItems={menuItems}
|
|
176
|
+
onSelect={(action) => handleMenuAction(action, item)}
|
|
177
|
+
selected={isSelected}
|
|
178
|
+
onOpenChange={(open) => {
|
|
179
|
+
setDropdownOpenId(open ? id : undefined);
|
|
180
|
+
|
|
181
|
+
if (open && !isSelected) {
|
|
182
|
+
handleSelect(id);
|
|
183
|
+
}
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
));
|
|
154
187
|
|
|
155
188
|
return (
|
|
156
189
|
<GridView.Item<M>
|
|
@@ -196,6 +229,9 @@ export const Grid = memoGeneric(
|
|
|
196
229
|
}
|
|
197
230
|
loading={false}
|
|
198
231
|
selected={isSelected}
|
|
232
|
+
onClick={() => {
|
|
233
|
+
onClickItem?.(id);
|
|
234
|
+
}}
|
|
199
235
|
onSelect={(event) => {
|
|
200
236
|
handleSelect(id, event);
|
|
201
237
|
}}
|
|
@@ -208,24 +244,18 @@ export const Grid = memoGeneric(
|
|
|
208
244
|
onDoubleClickItem?.(id);
|
|
209
245
|
}}
|
|
210
246
|
menuItems={menuItems}
|
|
211
|
-
onSelectMenuItem={handleMenuAction}
|
|
247
|
+
onSelectMenuItem={(action) => handleMenuAction(action, item)}
|
|
212
248
|
action={action}
|
|
213
|
-
testHoveredId={testHoveredId}
|
|
214
249
|
onMenuOpenChange={(open: boolean) => {
|
|
215
250
|
if (open && !isSelected) {
|
|
216
|
-
handleSelect(id
|
|
217
|
-
shiftKey: false,
|
|
218
|
-
altKey: false,
|
|
219
|
-
metaKey: false,
|
|
220
|
-
ctrlKey: false,
|
|
221
|
-
});
|
|
251
|
+
handleSelect(id);
|
|
222
252
|
}
|
|
223
253
|
}}
|
|
224
254
|
>
|
|
225
255
|
{thumbnail && (
|
|
226
256
|
<div
|
|
227
257
|
className={cx(
|
|
228
|
-
"rounded overflow-hidden flex-none flex
|
|
258
|
+
"rounded overflow-hidden flex-none flex w-full",
|
|
229
259
|
isSelected && "text-primary"
|
|
230
260
|
)}
|
|
231
261
|
tabIndex={-1}
|
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
createContext,
|
|
5
5
|
CSSProperties,
|
|
6
6
|
ForwardedRef,
|
|
7
|
+
forwardRef,
|
|
7
8
|
memo,
|
|
8
9
|
ReactNode,
|
|
9
10
|
useCallback,
|
|
@@ -93,7 +94,6 @@ interface ItemProps<MenuItemType extends string = string> {
|
|
|
93
94
|
onMenuOpenChange?: (open: boolean) => void;
|
|
94
95
|
style?: CSSProperties;
|
|
95
96
|
action?: ReactNode;
|
|
96
|
-
testHoveredId?: string;
|
|
97
97
|
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
98
98
|
hovered?: boolean;
|
|
99
99
|
}
|
|
@@ -118,7 +118,6 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
118
118
|
onMenuOpenChange,
|
|
119
119
|
style,
|
|
120
120
|
action,
|
|
121
|
-
testHoveredId,
|
|
122
121
|
onKeyDown,
|
|
123
122
|
hovered = false,
|
|
124
123
|
}: ItemProps<MenuItemType>,
|
|
@@ -128,8 +127,6 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
128
127
|
onHoverChange,
|
|
129
128
|
});
|
|
130
129
|
|
|
131
|
-
const isHovered = testHoveredId ? testHoveredId === id : hovered;
|
|
132
|
-
|
|
133
130
|
const { textPosition, bordered, disabled } = useContext(GridViewContext);
|
|
134
131
|
|
|
135
132
|
const handleClick = useCallback(
|
|
@@ -162,26 +159,27 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
162
159
|
<div
|
|
163
160
|
className={cx(
|
|
164
161
|
"flex flex-col relative focus:shadow-[0px_0px_0px_1px_var(--sidebar-background),_0px_0px_0px_3px_var(--primary)] focus:outline-none rounded p-2 -m-2",
|
|
162
|
+
"cursor-pointer",
|
|
163
|
+
"active:opacity-70",
|
|
165
164
|
selected && "bg-primary-pastel",
|
|
166
|
-
|
|
165
|
+
disabled && "opacity-50",
|
|
166
|
+
hovered && "ring-1 ring-primary ring-inset opacity-85"
|
|
167
167
|
)}
|
|
168
168
|
id={id}
|
|
169
169
|
ref={forwardedRef}
|
|
170
170
|
{...hoverProps}
|
|
171
171
|
tabIndex={disabled ? undefined : 0}
|
|
172
172
|
onKeyDown={handleKeyDown}
|
|
173
|
+
onClick={handleClick}
|
|
174
|
+
onDoubleClick={onDoubleClick}
|
|
175
|
+
onContextMenu={onContextMenu}
|
|
173
176
|
>
|
|
174
177
|
<div
|
|
175
178
|
className={cx(
|
|
176
179
|
"flex items-center justify-center rounded",
|
|
177
|
-
"cursor-pointer",
|
|
178
180
|
bordered ? "border border-divider" : "border border-transparent",
|
|
179
|
-
disabled ? "opacity-50" : "hover:opacity-85 active:opacity-70",
|
|
180
181
|
selected ? "bg-primary-pastel" : "bg-input-background text-text"
|
|
181
182
|
)}
|
|
182
|
-
onClick={handleClick}
|
|
183
|
-
onDoubleClick={onDoubleClick}
|
|
184
|
-
onContextMenu={onContextMenu}
|
|
185
183
|
style={style}
|
|
186
184
|
>
|
|
187
185
|
{children}
|
|
@@ -193,7 +191,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
193
191
|
<ItemDescription>{subtitle || " "}</ItemDescription>
|
|
194
192
|
</>
|
|
195
193
|
)}
|
|
196
|
-
{textPosition === "overlay" &&
|
|
194
|
+
{textPosition === "overlay" && hovered && (title || subtitle) && (
|
|
197
195
|
<div className="absolute inset-0 flex flex-col justify-end items-start p-1 pointer-events-none truncate gap-0.5">
|
|
198
196
|
{title && <ItemTitle showBackground>{title}</ItemTitle>}
|
|
199
197
|
{subtitle && (
|
|
@@ -265,19 +263,27 @@ interface GridViewRootProps extends Partial<GridViewContextValue> {
|
|
|
265
263
|
onClick?: () => void;
|
|
266
264
|
scrollable?: boolean;
|
|
267
265
|
className?: string;
|
|
266
|
+
onDragOver?: React.DragEventHandler<HTMLDivElement>;
|
|
267
|
+
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
268
|
+
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
269
|
+
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
268
270
|
}
|
|
269
271
|
|
|
270
|
-
function GridViewRoot(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
272
|
+
const GridViewRoot = forwardRef(function GridViewRoot(
|
|
273
|
+
{
|
|
274
|
+
minColumnWidth = "220px",
|
|
275
|
+
gap = 20,
|
|
276
|
+
children,
|
|
277
|
+
scrollable,
|
|
278
|
+
onClick,
|
|
279
|
+
textPosition = "below",
|
|
280
|
+
bordered = false,
|
|
281
|
+
disabled = false,
|
|
282
|
+
className,
|
|
283
|
+
...props
|
|
284
|
+
}: GridViewRootProps,
|
|
285
|
+
forwardedRef: ForwardedRef<HTMLDivElement>
|
|
286
|
+
) {
|
|
281
287
|
const handleClick = useCallback(
|
|
282
288
|
(event: React.MouseEvent) => {
|
|
283
289
|
event.stopPropagation();
|
|
@@ -305,9 +311,11 @@ function GridViewRoot({
|
|
|
305
311
|
onClick={handleClick}
|
|
306
312
|
className={cx(
|
|
307
313
|
"flex flex-col -mx-3 -mb-1",
|
|
308
|
-
scrollable ? "
|
|
314
|
+
scrollable ? "basis-0 min-h-0" : "flex-none",
|
|
309
315
|
className
|
|
310
316
|
)}
|
|
317
|
+
{...props}
|
|
318
|
+
ref={forwardedRef}
|
|
311
319
|
>
|
|
312
320
|
{scrollable ? (
|
|
313
321
|
<ScrollArea>
|
|
@@ -319,14 +327,16 @@ function GridViewRoot({
|
|
|
319
327
|
</div>
|
|
320
328
|
</GridViewContext.Provider>
|
|
321
329
|
);
|
|
322
|
-
}
|
|
330
|
+
});
|
|
323
331
|
|
|
324
332
|
function GridViewSection({
|
|
325
333
|
children,
|
|
326
334
|
className,
|
|
335
|
+
renderEmptyState,
|
|
327
336
|
}: {
|
|
328
337
|
children?: ReactNode;
|
|
329
338
|
className?: string;
|
|
339
|
+
renderEmptyState?: () => ReactNode;
|
|
330
340
|
}) {
|
|
331
341
|
const { minColumnWidth, gap } = useContext(GridViewContext);
|
|
332
342
|
|
|
@@ -335,7 +345,11 @@ function GridViewSection({
|
|
|
335
345
|
gap: `${gap}px`,
|
|
336
346
|
};
|
|
337
347
|
|
|
338
|
-
|
|
348
|
+
const isEmpty = React.Children.count(children) === 0;
|
|
349
|
+
|
|
350
|
+
return isEmpty && renderEmptyState ? (
|
|
351
|
+
renderEmptyState()
|
|
352
|
+
) : (
|
|
339
353
|
<div className={cx("grid text-text", className)} style={styles}>
|
|
340
354
|
{children}
|
|
341
355
|
</div>
|