@noya-app/noya-designsystem 0.1.62 → 0.1.64
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/.eslintrc.js +1 -5
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-lint.log +0 -1
- package/CHANGELOG.md +23 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +299 -179
- package/dist/index.d.ts +299 -179
- package/dist/index.js +3772 -2999
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3485 -2720
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -22
- package/src/__tests__/validateDropIndicator.test.ts +26 -41
- package/src/__tests__/workspaceLayout.test.ts +281 -0
- package/src/components/ActionMenu.tsx +2 -0
- package/src/components/ActivityIndicator.tsx +5 -3
- package/src/components/AnimatePresence.tsx +6 -4
- package/src/components/Avatar.tsx +4 -2
- package/src/components/BaseToolbar.tsx +45 -34
- package/src/components/Breadcrumbs.tsx +2 -0
- package/src/components/Button.tsx +35 -21
- package/src/components/Chip.tsx +2 -0
- package/src/components/Collection.tsx +4 -1
- package/src/components/ColorSwatch.tsx +42 -22
- package/src/components/ColorSwatchControl.tsx +5 -3
- package/src/components/Combobox.tsx +2 -0
- package/src/components/CommandPalette.tsx +2 -0
- package/src/components/ContextMenu.tsx +3 -1
- package/src/components/Dialog.tsx +3 -1
- package/src/components/DimensionInput.tsx +3 -1
- package/src/components/Divider.tsx +10 -6
- package/src/components/DraggableMenuButton.tsx +22 -4
- package/src/components/Drawer.tsx +11 -3
- package/src/components/DropdownMenu.tsx +34 -6
- package/src/components/EditableText.tsx +2 -0
- package/src/components/FileUploadIndicator.tsx +108 -0
- package/src/components/FillPreviewBackground.tsx +2 -0
- package/src/components/FloatingWindow.tsx +2 -0
- package/src/components/GradientPicker.tsx +2 -0
- package/src/components/Grid.tsx +47 -38
- package/src/components/GridView.tsx +33 -11
- package/src/components/IconButton.tsx +4 -2
- package/src/components/Icons.tsx +1 -1
- package/src/components/InputField.tsx +7 -5
- package/src/components/Label.tsx +1 -3
- package/src/components/LabeledElementView.tsx +6 -2
- package/src/components/LabeledField.tsx +38 -34
- package/src/components/List.tsx +10 -2
- package/src/components/ListView.tsx +27 -14
- package/src/components/MediaThumbnail.tsx +31 -2
- package/src/components/Message.tsx +8 -9
- package/src/components/Popover.tsx +15 -4
- package/src/components/Progress.tsx +20 -7
- package/src/components/ScrollArea.tsx +3 -1
- package/src/components/SearchCompletionMenu.tsx +2 -0
- package/src/components/Section.tsx +2 -0
- package/src/components/SegmentedControl.tsx +43 -17
- package/src/components/SelectMenu.tsx +6 -4
- package/src/components/SelectionToolbar.tsx +31 -8
- package/src/components/Slider.tsx +4 -2
- package/src/components/Spacer.tsx +0 -1
- package/src/components/Switch.tsx +3 -1
- package/src/components/Tabs.tsx +48 -0
- package/src/components/Text.tsx +6 -5
- package/src/components/TextArea.tsx +2 -0
- package/src/components/Toast.tsx +1 -1
- package/src/components/Toolbar.tsx +113 -12
- package/src/components/ToolbarDrawer.tsx +29 -0
- package/src/components/Tooltip.tsx +9 -9
- package/src/components/TreeView.tsx +2 -0
- package/src/components/UserPointer.tsx +2 -0
- package/src/components/ai-assistant/AIAssistantLayout.tsx +2 -0
- package/src/components/blocks/ImageBlockComponent.tsx +156 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +2 -0
- package/src/components/file-explorer/FileExplorerLayout.tsx +14 -6
- package/src/components/internal/Checkmark.tsx +24 -0
- package/src/components/internal/Menu.tsx +21 -3
- package/src/components/internal/MenuViewport.tsx +8 -5
- package/src/components/internal/SelectItem.tsx +4 -23
- package/src/components/internal/TextInput.tsx +2 -0
- package/src/components/sorting/DragRegistration.tsx +8 -4
- package/src/components/sorting/SharedDragProvider.tsx +43 -18
- package/src/components/sorting/Sortable.tsx +19 -10
- package/src/components/sorting/createSharedDrag.tsx +7 -6
- package/src/components/sorting/sorting.ts +6 -9
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +113 -28
- package/src/components/workspace/PanelWorkspaceLayout.tsx +149 -16
- package/src/components/workspace/VerticalTabMenu.tsx +36 -0
- package/src/components/workspace/WorkspaceLayout.tsx +132 -47
- package/src/components/workspace/constants.ts +4 -0
- package/src/components/workspace/panelStorage.ts +216 -0
- package/src/components/workspace/renderPanelChildren.tsx +12 -0
- package/src/components/workspace/types.ts +30 -7
- package/src/contexts/DesignSystemConfiguration.tsx +12 -5
- package/src/contexts/DialogContext.tsx +2 -0
- package/src/contexts/FloatingWindowContext.tsx +2 -0
- package/src/contexts/GlobalInputBlurContext.tsx +2 -0
- package/src/contexts/ImageDataContext.tsx +2 -0
- package/src/contexts/LinkComponentContext.tsx +17 -0
- package/src/contexts/OpenPortalsContext.tsx +2 -0
- package/src/contexts/PortalScopeContext.tsx +2 -0
- package/src/hooks/mergeEventHandlers.ts +1 -1
- package/src/hooks/useHover.ts +30 -17
- package/src/hooks/useIndent.ts +2 -0
- package/src/hooks/useLabel.ts +12 -10
- package/src/hooks/useObjectURL.ts +5 -3
- package/src/hooks/usePlatform.ts +5 -3
- package/src/hooks/useTheme.ts +3 -1
- package/src/index.css +25 -4
- package/src/index.tsx +7 -2
- package/src/utils/classNames.ts +4 -3
- package/src/utils/combobox.ts +2 -0
- package/src/utils/editableBlockStyles.ts +10 -0
- package/src/utils/formatByteSize.ts +8 -0
- package/src/utils/withSeparatorElements.ts +1 -1
- package/src/hooks/usePreservePanelSize.tsx +0 -183
- package/src/hooks/useWindowSize.tsx +0 -26
- package/src/mediaQuery.ts +0 -13
package/src/components/Grid.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
forwardRefGeneric,
|
|
3
5
|
memoGeneric,
|
|
@@ -70,6 +72,7 @@ export const Grid = memoGeneric(
|
|
|
70
72
|
items,
|
|
71
73
|
getId,
|
|
72
74
|
getName,
|
|
75
|
+
getHref,
|
|
73
76
|
// expandable,
|
|
74
77
|
// getExpanded,
|
|
75
78
|
getRenamable,
|
|
@@ -84,7 +87,7 @@ export const Grid = memoGeneric(
|
|
|
84
87
|
onSelectionChange,
|
|
85
88
|
selectedIds = emptyArray,
|
|
86
89
|
// itemRoleDescription = "clickable item",
|
|
87
|
-
|
|
90
|
+
detailPosition = "below",
|
|
88
91
|
size = "medium",
|
|
89
92
|
testHoveredId,
|
|
90
93
|
testRenamingId,
|
|
@@ -169,6 +172,7 @@ export const Grid = memoGeneric(
|
|
|
169
172
|
<GridView.Section>
|
|
170
173
|
{items.map((item) => {
|
|
171
174
|
const id = getId(item);
|
|
175
|
+
const href = getHref?.(item);
|
|
172
176
|
const isDropdownOpen = dropdownOpenId === id;
|
|
173
177
|
const isHovered =
|
|
174
178
|
testHoveredId === id ||
|
|
@@ -206,9 +210,18 @@ export const Grid = memoGeneric(
|
|
|
206
210
|
/>
|
|
207
211
|
));
|
|
208
212
|
|
|
213
|
+
const subtitle = (
|
|
214
|
+
<span
|
|
215
|
+
className={cx(isSelected && "text-selected-list-item-text")}
|
|
216
|
+
>
|
|
217
|
+
{renderDetail?.(item, isSelected)}
|
|
218
|
+
</span>
|
|
219
|
+
);
|
|
220
|
+
|
|
209
221
|
return (
|
|
210
222
|
<GridView.Item<M>
|
|
211
223
|
key={id}
|
|
224
|
+
href={href}
|
|
212
225
|
id={id}
|
|
213
226
|
role="option"
|
|
214
227
|
aria-selected={isSelected}
|
|
@@ -219,44 +232,40 @@ export const Grid = memoGeneric(
|
|
|
219
232
|
setHoveredId(isHovering ? id : undefined);
|
|
220
233
|
}}
|
|
221
234
|
title={
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
onRename
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
isSelected && "text-selected-list-item-text"
|
|
255
|
-
)}
|
|
256
|
-
>
|
|
257
|
-
{renderDetail?.(item, isSelected)}
|
|
258
|
-
</span>
|
|
235
|
+
<div className="flex items-center gap-2 flex-row">
|
|
236
|
+
<EditableText
|
|
237
|
+
className={cx(detailPosition === "end" && "flex-1")}
|
|
238
|
+
tabIndex={isRenaming ? undefined : -1}
|
|
239
|
+
value={getName(item)}
|
|
240
|
+
focused={isRenaming}
|
|
241
|
+
testFocused={isTestingRenaming}
|
|
242
|
+
readOnly={!itemIsRenamable || !onRename}
|
|
243
|
+
onSubmit={(value) => {
|
|
244
|
+
onRename?.(item, value);
|
|
245
|
+
setRenamingId(undefined);
|
|
246
|
+
}}
|
|
247
|
+
onBlur={() => {
|
|
248
|
+
setRenamingId(undefined);
|
|
249
|
+
}}
|
|
250
|
+
// max height ensures preview text and input are one line
|
|
251
|
+
textClassName="bg-listview-editing-background max-h-input-height"
|
|
252
|
+
>
|
|
253
|
+
{(props) => (
|
|
254
|
+
<span
|
|
255
|
+
{...props}
|
|
256
|
+
className={cx(
|
|
257
|
+
isSelected
|
|
258
|
+
? "text-selected-list-item-text"
|
|
259
|
+
: "text-text",
|
|
260
|
+
"truncate select-none"
|
|
261
|
+
)}
|
|
262
|
+
/>
|
|
263
|
+
)}
|
|
264
|
+
</EditableText>
|
|
265
|
+
{detailPosition === "end" && subtitle}
|
|
266
|
+
</div>
|
|
259
267
|
}
|
|
268
|
+
subtitle={detailPosition === "below" && subtitle}
|
|
260
269
|
loading={false}
|
|
261
270
|
selected={isSelected}
|
|
262
271
|
onClick={() => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { KeyModifiers } from "@noya-app/noya-keymap";
|
|
2
4
|
import { forwardRefGeneric, memoGeneric } from "@noya-app/react-utils";
|
|
3
5
|
import React, {
|
|
@@ -11,6 +13,7 @@ import React, {
|
|
|
11
13
|
useContext,
|
|
12
14
|
useMemo,
|
|
13
15
|
} from "react";
|
|
16
|
+
import { useLinkComponent } from "../contexts/LinkComponentContext";
|
|
14
17
|
import { useHover } from "../hooks/useHover";
|
|
15
18
|
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
16
19
|
import withSeparatorElements from "../utils/withSeparatorElements";
|
|
@@ -79,6 +82,7 @@ const SectionTitle = ({
|
|
|
79
82
|
|
|
80
83
|
interface ItemProps<MenuItemType extends string = string> {
|
|
81
84
|
id: string;
|
|
85
|
+
href?: string;
|
|
82
86
|
title?: ReactNode;
|
|
83
87
|
subtitle?: ReactNode;
|
|
84
88
|
loading?: boolean;
|
|
@@ -105,6 +109,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
105
109
|
>(
|
|
106
110
|
{
|
|
107
111
|
id,
|
|
112
|
+
href,
|
|
108
113
|
title,
|
|
109
114
|
subtitle,
|
|
110
115
|
loading,
|
|
@@ -125,23 +130,27 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
125
130
|
testShowInsideDropIndicator: showInsideDropIndicator = false,
|
|
126
131
|
...props
|
|
127
132
|
}: ItemProps<MenuItemType>,
|
|
128
|
-
forwardedRef: ForwardedRef<
|
|
133
|
+
forwardedRef: ForwardedRef<HTMLElement>
|
|
129
134
|
) {
|
|
130
135
|
const { hoverProps } = useHover({
|
|
131
136
|
onHoverChange,
|
|
132
137
|
});
|
|
133
138
|
|
|
134
|
-
const { textPosition, bordered, disabled } =
|
|
139
|
+
const { textPosition, bordered, disabled, alwaysShowSubtitles } =
|
|
140
|
+
useContext(GridViewContext);
|
|
135
141
|
|
|
136
142
|
const handleClick = useCallback(
|
|
137
143
|
(event: React.MouseEvent) => {
|
|
138
144
|
event.stopPropagation();
|
|
139
|
-
|
|
145
|
+
|
|
146
|
+
if (!href) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
}
|
|
140
149
|
|
|
141
150
|
onClick?.(event);
|
|
142
151
|
onSelect?.(event);
|
|
143
152
|
},
|
|
144
|
-
[onClick, onSelect]
|
|
153
|
+
[href, onClick, onSelect]
|
|
145
154
|
);
|
|
146
155
|
|
|
147
156
|
const handleKeyDown = useCallback(
|
|
@@ -159,8 +168,11 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
159
168
|
[onSelect, onKeyDown]
|
|
160
169
|
);
|
|
161
170
|
|
|
171
|
+
const LinkComponent = useLinkComponent();
|
|
172
|
+
const Component = typeof href === "string" ? LinkComponent : "div";
|
|
173
|
+
|
|
162
174
|
let element = (
|
|
163
|
-
<
|
|
175
|
+
<Component
|
|
164
176
|
{...props}
|
|
165
177
|
className={cx(
|
|
166
178
|
"flex flex-col relative rounded p-2 -m-2 text-text",
|
|
@@ -173,7 +185,8 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
173
185
|
"focus:ring-2 focus:ring-primary focus:ring-inset focus:outline-none"
|
|
174
186
|
)}
|
|
175
187
|
id={id}
|
|
176
|
-
|
|
188
|
+
href={href}
|
|
189
|
+
ref={forwardedRef as ForwardedRef<HTMLAnchorElement & HTMLDivElement>}
|
|
177
190
|
{...hoverProps}
|
|
178
191
|
tabIndex={disabled ? undefined : 0}
|
|
179
192
|
onKeyDown={handleKeyDown}
|
|
@@ -194,8 +207,12 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
194
207
|
<>
|
|
195
208
|
<Spacer.Vertical size={8} />
|
|
196
209
|
<ItemTitle>{title || " "}</ItemTitle>
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
{(alwaysShowSubtitles || subtitle) && (
|
|
211
|
+
<>
|
|
212
|
+
<Spacer.Vertical size={2} />
|
|
213
|
+
<ItemDescription>{subtitle || " "}</ItemDescription>
|
|
214
|
+
</>
|
|
215
|
+
)}
|
|
199
216
|
</>
|
|
200
217
|
)}
|
|
201
218
|
{textPosition === "overlay" && hovered && (title || subtitle) && (
|
|
@@ -216,7 +233,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
216
233
|
<ActivityIndicator opacity={0.5} size={13} />
|
|
217
234
|
</div>
|
|
218
235
|
)}
|
|
219
|
-
</
|
|
236
|
+
</Component>
|
|
220
237
|
);
|
|
221
238
|
|
|
222
239
|
if (menuItems && onSelectMenuItem) {
|
|
@@ -255,6 +272,7 @@ type GridViewContextValue = {
|
|
|
255
272
|
bordered: boolean;
|
|
256
273
|
disabled: boolean;
|
|
257
274
|
gap: number;
|
|
275
|
+
alwaysShowSubtitles: boolean;
|
|
258
276
|
};
|
|
259
277
|
|
|
260
278
|
const GridViewContext = createContext<GridViewContextValue>({
|
|
@@ -263,6 +281,7 @@ const GridViewContext = createContext<GridViewContextValue>({
|
|
|
263
281
|
bordered: false,
|
|
264
282
|
disabled: false,
|
|
265
283
|
gap: 0,
|
|
284
|
+
alwaysShowSubtitles: false,
|
|
266
285
|
});
|
|
267
286
|
|
|
268
287
|
interface GridViewRootProps extends Partial<GridViewContextValue> {
|
|
@@ -277,6 +296,7 @@ interface GridViewRootProps extends Partial<GridViewContextValue> {
|
|
|
277
296
|
role?: string;
|
|
278
297
|
renderEmptyState?: () => ReactNode;
|
|
279
298
|
contentClassName?: string;
|
|
299
|
+
alwaysShowSubtitles?: boolean;
|
|
280
300
|
}
|
|
281
301
|
|
|
282
302
|
const GridViewRoot = forwardRef(function GridViewRoot(
|
|
@@ -291,6 +311,7 @@ const GridViewRoot = forwardRef(function GridViewRoot(
|
|
|
291
311
|
disabled = false,
|
|
292
312
|
className,
|
|
293
313
|
contentClassName,
|
|
314
|
+
alwaysShowSubtitles = false,
|
|
294
315
|
...props
|
|
295
316
|
}: GridViewRootProps,
|
|
296
317
|
forwardedRef: ForwardedRef<HTMLDivElement>
|
|
@@ -306,14 +327,15 @@ const GridViewRoot = forwardRef(function GridViewRoot(
|
|
|
306
327
|
);
|
|
307
328
|
|
|
308
329
|
const contextValue = useMemo(
|
|
309
|
-
() => ({
|
|
330
|
+
(): GridViewContextValue => ({
|
|
310
331
|
minColumnWidth,
|
|
311
332
|
textPosition,
|
|
312
333
|
bordered,
|
|
313
334
|
disabled,
|
|
314
335
|
gap,
|
|
336
|
+
alwaysShowSubtitles,
|
|
315
337
|
}),
|
|
316
|
-
[bordered, disabled, gap, minColumnWidth, textPosition]
|
|
338
|
+
[bordered, disabled, gap, minColumnWidth, textPosition, alwaysShowSubtitles]
|
|
317
339
|
);
|
|
318
340
|
|
|
319
341
|
const mergedClassName = useMemo(() => {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import * as React from "react";
|
|
2
4
|
import { cssVars } from "../theme";
|
|
3
|
-
import { Button,
|
|
5
|
+
import { Button, ButtonProps } from "./Button";
|
|
4
6
|
import { IconName, Icons } from "./Icons";
|
|
5
7
|
|
|
6
|
-
type Props = Omit<
|
|
8
|
+
type Props = Omit<ButtonProps, "children" | "variant" | "flex" | "size"> & {
|
|
7
9
|
iconName: IconName;
|
|
8
10
|
className?: string;
|
|
9
11
|
style?: React.CSSProperties;
|
package/src/components/Icons.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { DropdownChevronIcon } from "@noya-app/noya-icons";
|
|
2
4
|
import { memoGeneric, useMergeRefs, useSize } from "@noya-app/react-utils";
|
|
3
5
|
import { Property } from "csstype";
|
|
@@ -14,11 +16,11 @@ import React, {
|
|
|
14
16
|
useMemo,
|
|
15
17
|
useState,
|
|
16
18
|
} from "react";
|
|
17
|
-
import { useLabel,
|
|
19
|
+
import { useLabel, useLabelType } from "../hooks/useLabel";
|
|
18
20
|
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
19
21
|
import handleNudge from "../utils/handleNudge";
|
|
20
22
|
import { getInsetEndStyles, InputSize, startBaseStyles } from "../utils/inputs";
|
|
21
|
-
import { Button,
|
|
23
|
+
import { Button, ButtonProps } from "./Button";
|
|
22
24
|
import { DropdownMenu } from "./DropdownMenu";
|
|
23
25
|
import { MenuItem } from "./internal/Menu";
|
|
24
26
|
import TextInput, { TextInputProps } from "./internal/TextInput";
|
|
@@ -101,7 +103,7 @@ const InputFieldDropdownMenu = memoGeneric(function InputFieldDropdownMenu<
|
|
|
101
103
|
const InputFieldButton = memo(
|
|
102
104
|
forwardRef(function InputFieldButton(
|
|
103
105
|
props: React.ButtonHTMLAttributes<HTMLButtonElement> &
|
|
104
|
-
|
|
106
|
+
ButtonProps & {
|
|
105
107
|
variant?: "floating" | "normal";
|
|
106
108
|
},
|
|
107
109
|
forwardedRef: ForwardedRef<HTMLButtonElement>
|
|
@@ -537,7 +539,7 @@ function InputFieldRoot({
|
|
|
537
539
|
const measuredInputSize = useSize(inputRef, "width");
|
|
538
540
|
const measuredStartSize = useSize(startRef, "width");
|
|
539
541
|
const measuredEndSize = useSize(endRef, "width");
|
|
540
|
-
const
|
|
542
|
+
const labelType = useLabelType();
|
|
541
543
|
const { label } = useLabel({ fieldId: id });
|
|
542
544
|
|
|
543
545
|
const endStyles = getInsetEndStyles(size);
|
|
@@ -592,7 +594,7 @@ function InputFieldRoot({
|
|
|
592
594
|
>
|
|
593
595
|
{labelProp
|
|
594
596
|
? insetLabel
|
|
595
|
-
: label &&
|
|
597
|
+
: label && labelType === "inset" && insetLabel}
|
|
596
598
|
{end}
|
|
597
599
|
</span>
|
|
598
600
|
)}
|
package/src/components/Label.tsx
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React, { LabelHTMLAttributes, forwardRef, memo } from "react";
|
|
2
2
|
import { mergeConflictingClassNames } from "../utils/classNames";
|
|
3
3
|
|
|
4
|
-
export type LabelPosition = "inset" | "start" | "end" | "above";
|
|
5
|
-
|
|
6
4
|
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
|
|
7
5
|
|
|
8
6
|
const labelStyles = "flex items-center select-none z-label relative";
|
|
9
7
|
const labelTextStyles =
|
|
10
|
-
"font-sans text-label uppercase leading-
|
|
8
|
+
"font-sans text-label uppercase leading-none font-bold text-text-muted";
|
|
11
9
|
|
|
12
10
|
export const Label = memo(
|
|
13
11
|
forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import * as kiwi from "kiwi.js";
|
|
2
4
|
import * as React from "react";
|
|
3
5
|
|
|
@@ -13,11 +15,13 @@ export const LabeledElementView = React.memo(function LabeledElementView({
|
|
|
13
15
|
const elementIds: string[] = React.Children.toArray(children)
|
|
14
16
|
.flatMap((child) =>
|
|
15
17
|
React.isValidElement(child) && child.type === React.Fragment
|
|
16
|
-
? (child.props.children as React.ReactNode[])
|
|
18
|
+
? ((child as any).props.children as React.ReactNode[])
|
|
17
19
|
: [child]
|
|
18
20
|
)
|
|
19
21
|
.map((child) =>
|
|
20
|
-
React.isValidElement(child) && "id" in child
|
|
22
|
+
React.isValidElement(child) && "id" in (child as any).props
|
|
23
|
+
? (child as any).props.id
|
|
24
|
+
: null
|
|
21
25
|
)
|
|
22
26
|
.filter((id) => !!id);
|
|
23
27
|
const serializedIds = elementIds.join(",");
|
|
@@ -1,30 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { memo, useMemo } from "react";
|
|
2
4
|
import { useIndent } from "../hooks/useIndent";
|
|
3
5
|
import {
|
|
4
6
|
LabelContext,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
LabelType,
|
|
8
|
+
LabelTypeContext,
|
|
9
|
+
LabelTypeContextValue,
|
|
10
|
+
useLabelType,
|
|
8
11
|
useLabelWidth,
|
|
9
12
|
} from "../hooks/useLabel";
|
|
10
13
|
import { cx } from "../utils/classNames";
|
|
11
|
-
import { Label
|
|
14
|
+
import { Label } from "./Label";
|
|
12
15
|
|
|
13
|
-
export const labeledFieldStyles = (
|
|
16
|
+
export const labeledFieldStyles = (labelType?: LabelType) =>
|
|
14
17
|
cx(
|
|
15
18
|
"flex flex-row flex-1 flex-wrap items-center gap-x-1.5",
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
labelType === "end" && "flex-row-reverse justify-end",
|
|
20
|
+
labelType === "above" && "flex-col items-stretch justify-start"
|
|
18
21
|
);
|
|
19
22
|
|
|
20
|
-
const
|
|
21
|
-
|
|
23
|
+
const labelTypeInset: LabelTypeContextValue = {
|
|
24
|
+
type: "inset",
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
export const LabeledField = memo(function LabeledField({
|
|
25
28
|
children,
|
|
26
29
|
label,
|
|
27
|
-
|
|
30
|
+
labelType: labelTypeProp,
|
|
28
31
|
fieldId,
|
|
29
32
|
minWidth = 100,
|
|
30
33
|
className,
|
|
@@ -34,7 +37,7 @@ export const LabeledField = memo(function LabeledField({
|
|
|
34
37
|
}: {
|
|
35
38
|
children: React.ReactNode;
|
|
36
39
|
label: React.ReactNode;
|
|
37
|
-
|
|
40
|
+
labelType?: LabelType;
|
|
38
41
|
labelStyle?: React.CSSProperties;
|
|
39
42
|
/** Used for both the `id` of the field and the `htmlFor` of the label, unless `id` and `htmlFor` are explicitly provided */
|
|
40
43
|
fieldId?: string;
|
|
@@ -47,13 +50,12 @@ export const LabeledField = memo(function LabeledField({
|
|
|
47
50
|
const labelWidthFromContext = useLabelWidth();
|
|
48
51
|
const labelWidth = labelWidthFromContext ?? minWidth;
|
|
49
52
|
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
labelPositionProp ?? labelPositionFromContext ?? "start";
|
|
53
|
+
const labelTypeFromContext = useLabelType();
|
|
54
|
+
const labelType = labelTypeProp ?? labelTypeFromContext;
|
|
53
55
|
|
|
54
56
|
const labeledFieldClasses = useMemo(
|
|
55
|
-
() => labeledFieldStyles(
|
|
56
|
-
[
|
|
57
|
+
() => labeledFieldStyles(labelType),
|
|
58
|
+
[labelType]
|
|
57
59
|
);
|
|
58
60
|
|
|
59
61
|
const labelContextValue = useMemo(
|
|
@@ -89,29 +91,31 @@ export const LabeledField = memo(function LabeledField({
|
|
|
89
91
|
return (
|
|
90
92
|
<LabelContext.Provider value={labelContextValue}>
|
|
91
93
|
{/* let components individually handle inset labels using info from context*/}
|
|
92
|
-
{
|
|
93
|
-
<
|
|
94
|
+
{labelType === "inset" ? (
|
|
95
|
+
<LabelTypeContext.Provider value={labelTypeInset}>
|
|
94
96
|
{children}
|
|
95
|
-
</
|
|
97
|
+
</LabelTypeContext.Provider>
|
|
96
98
|
) : (
|
|
97
99
|
<div className={cx("flex relative flex-1", className)} style={style}>
|
|
98
100
|
{Boolean(indentLevel) && <div style={indentStyle} />}
|
|
99
101
|
<div className={labeledFieldClasses}>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
{labelType !== "none" && (
|
|
103
|
+
<Label
|
|
104
|
+
htmlFor={fieldId}
|
|
105
|
+
// we do this so presence avatars are aligned to the right when position is above
|
|
106
|
+
// and when start/end position with textarea it aligns label to the top of the field
|
|
107
|
+
className={
|
|
108
|
+
labelType === "start" || labelType === "end"
|
|
109
|
+
? "self-start"
|
|
110
|
+
: undefined
|
|
111
|
+
}
|
|
112
|
+
style={labelStyle}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
{label}
|
|
116
|
+
</Label>
|
|
117
|
+
)}
|
|
118
|
+
{labelType === "start" ? (
|
|
115
119
|
<div className="flex-auto flex" style={fieldStyle}>
|
|
116
120
|
{children}
|
|
117
121
|
</div>
|
package/src/components/List.tsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
forwardRefGeneric,
|
|
3
5
|
memoGeneric,
|
|
4
6
|
useFileDropTarget,
|
|
5
7
|
} from "@noya-app/react-utils";
|
|
6
8
|
import React, { memo, useImperativeHandle, useRef, useState } from "react";
|
|
9
|
+
import { useLinkComponent } from "../contexts/LinkComponentContext";
|
|
7
10
|
import { cssVars } from "../theme";
|
|
8
11
|
import { cx } from "../utils/classNames";
|
|
9
12
|
import { updateSelection } from "../utils/selection";
|
|
@@ -32,6 +35,7 @@ export const List = memoGeneric(
|
|
|
32
35
|
items,
|
|
33
36
|
getId,
|
|
34
37
|
getName,
|
|
38
|
+
getHref,
|
|
35
39
|
expandable,
|
|
36
40
|
getExpanded,
|
|
37
41
|
getRenamable,
|
|
@@ -101,8 +105,9 @@ export const List = memoGeneric(
|
|
|
101
105
|
};
|
|
102
106
|
|
|
103
107
|
const ViewComponent = expandable ? TreeView : ListView;
|
|
108
|
+
const LinkComponent = useLinkComponent();
|
|
104
109
|
|
|
105
|
-
const fileDropTargetRef = useRef<HTMLDivElement>(null);
|
|
110
|
+
const fileDropTargetRef = useRef<HTMLDivElement | null>(null);
|
|
106
111
|
const { isDropTargetActive, dropTargetProps } = useFileDropTarget(
|
|
107
112
|
fileDropTargetRef,
|
|
108
113
|
onFilesDrop
|
|
@@ -144,6 +149,7 @@ export const List = memoGeneric(
|
|
|
144
149
|
{ isDragOverlay }: ListViewItemInfo
|
|
145
150
|
) => {
|
|
146
151
|
const id = getId(item);
|
|
152
|
+
const href = getHref?.(item);
|
|
147
153
|
const isDropdownOpen = dropdownOpenId === id;
|
|
148
154
|
const isHovered =
|
|
149
155
|
(hoveredId === id || isDropdownOpen) && !isDragOverlay;
|
|
@@ -197,6 +203,8 @@ export const List = memoGeneric(
|
|
|
197
203
|
|
|
198
204
|
return (
|
|
199
205
|
<ViewComponent.Row
|
|
206
|
+
as={typeof href === "string" ? LinkComponent : undefined}
|
|
207
|
+
href={href}
|
|
200
208
|
id={id}
|
|
201
209
|
key={id}
|
|
202
210
|
role="option"
|
|
@@ -355,7 +363,7 @@ const rowGapMap: Record<CollectionItemSize, string> = {
|
|
|
355
363
|
xxs: "gap-2",
|
|
356
364
|
xs: "gap-2",
|
|
357
365
|
small: "gap-2",
|
|
358
|
-
medium: "gap-
|
|
366
|
+
medium: "gap-2",
|
|
359
367
|
mediumLarge: "gap-3",
|
|
360
368
|
large: "gap-3",
|
|
361
369
|
xl: "gap-4",
|