@noya-app/noya-designsystem 0.1.64 → 0.1.65
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 +13 -10
- package/CHANGELOG.md +12 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +501 -228
- package/dist/index.d.ts +501 -228
- package/dist/index.js +6738 -2571
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6697 -2499
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/components/ActivityIndicator.tsx +9 -4
- package/src/components/ActivityLog.tsx +197 -0
- package/src/components/AnimatePresence.tsx +37 -22
- package/src/components/Avatar.tsx +42 -25
- package/src/components/Banner.tsx +57 -11
- package/src/components/BaseToolbar.tsx +29 -7
- package/src/components/Breadcrumbs.tsx +2 -2
- package/src/components/Button.tsx +23 -24
- package/src/components/Checkbox.tsx +6 -35
- package/src/components/Chip.tsx +45 -33
- package/src/components/Collection.tsx +16 -11
- package/src/components/ColorSwatch.tsx +3 -3
- package/src/components/ColorSwatchControl.tsx +2 -2
- package/src/components/Combobox.tsx +6 -6
- package/src/components/ComboboxMenu.tsx +6 -5
- package/src/components/CommandPalette.tsx +1 -1
- package/src/components/Dialog.tsx +6 -12
- package/src/components/Divider.tsx +8 -8
- package/src/components/DraggableMenuButton.tsx +9 -5
- package/src/components/Drawer.tsx +5 -5
- package/src/components/DropdownMenu.tsx +1 -1
- package/src/components/EditableText.tsx +2 -2
- package/src/components/Fade.tsx +13 -13
- package/src/components/FileUploadIndicator.tsx +11 -11
- package/src/components/FloatingWindow.tsx +2 -2
- package/src/components/Grid.tsx +18 -11
- package/src/components/GridView.tsx +32 -25
- package/src/components/IVirtualizedList.tsx +5 -0
- package/src/components/Icons.tsx +24 -4
- package/src/components/InputField.tsx +19 -19
- package/src/components/InspectorContainer.tsx +2 -2
- package/src/components/InspectorPrimitives.tsx +15 -9
- package/src/components/Label.tsx +4 -3
- package/src/components/LabeledElementView.tsx +3 -3
- package/src/components/LabeledField.tsx +6 -6
- package/src/components/List.tsx +103 -45
- package/src/components/ListMenu.tsx +91 -0
- package/src/components/ListNavigator.tsx +106 -0
- package/src/components/ListView.tsx +20 -1158
- package/src/components/MediaThumbnail.tsx +56 -15
- package/src/components/Message.tsx +5 -5
- package/src/components/Navigator.tsx +461 -0
- package/src/components/NoyaLogo.tsx +1 -1
- package/src/components/Popover.tsx +9 -5
- package/src/components/Progress.tsx +19 -9
- package/src/components/ResizableContainer.tsx +187 -0
- package/src/components/RingProgress.tsx +128 -0
- package/src/components/ScrollArea.tsx +4 -4
- package/src/components/ScrollableSidebar.tsx +10 -0
- package/src/components/SearchCompletionMenu.tsx +3 -3
- package/src/components/Section.tsx +107 -57
- package/src/components/SegmentedControl.tsx +29 -29
- package/src/components/SelectMenu.tsx +13 -20
- package/src/components/SelectionToolbar.tsx +4 -3
- package/src/components/Slider.tsx +9 -9
- package/src/components/Spacer.tsx +2 -2
- package/src/components/StackNavigator.tsx +378 -0
- package/src/components/Stepper.tsx +88 -0
- package/src/components/Switch.tsx +4 -4
- package/src/components/Tabs.tsx +3 -3
- package/src/components/Text.tsx +23 -13
- package/src/components/TextArea.tsx +3 -3
- package/src/components/Toast.tsx +4 -4
- package/src/components/Toolbar.tsx +50 -9
- package/src/components/ToolbarDrawer.tsx +2 -2
- package/src/components/Tooltip.tsx +1 -1
- package/src/components/TreeView.tsx +11 -4
- package/src/components/UserPointer.tsx +4 -4
- package/src/components/ai-assistant/AIAssistantLayout.tsx +8 -8
- package/src/components/blocks/ImageBlockComponent.tsx +13 -7
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +4 -4
- package/src/components/file-explorer/FileExplorerLayout.tsx +20 -8
- package/src/components/internal/Checkmark.tsx +1 -1
- package/src/components/internal/Menu.tsx +22 -18
- package/src/components/internal/MenuViewport.tsx +13 -6
- package/src/components/internal/SelectItem.tsx +3 -3
- package/src/components/internal/TextInput.tsx +5 -1
- package/src/components/listView/ListViewContexts.tsx +68 -0
- package/src/components/listView/ListViewEditableRowTitle.tsx +55 -0
- package/src/components/listView/ListViewRoot.tsx +521 -0
- package/src/components/listView/ListViewRow.tsx +475 -0
- package/src/components/listView/ListViewRowTitle.tsx +21 -0
- package/src/components/listView/types.ts +11 -0
- package/src/components/pipeline/PipelineResultLayout.tsx +6 -6
- package/src/components/sorting/DragRegistration.tsx +2 -1
- package/src/components/sorting/SharedDragProvider.tsx +91 -9
- package/src/components/sorting/Sortable.tsx +71 -17
- package/src/components/workspace/DrawerWorkspaceLayout.tsx +5 -5
- package/src/components/workspace/PanelWorkspaceLayout.tsx +65 -49
- package/src/components/workspace/VerticalTabMenu.tsx +25 -15
- package/src/components/workspace/WorkspaceLayout.tsx +30 -21
- package/src/contexts/DialogContext.tsx +17 -5
- package/src/hooks/useIndent.ts +3 -8
- package/src/index.css +24 -10
- package/src/index.tsx +10 -5
- package/src/utils/classNames.ts +52 -6
- package/src/utils/editableBlockStyles.ts +2 -2
- package/src/utils/formatByteSize.ts +1 -0
- package/src/utils/inputs.ts +8 -6
- package/src/utils/sketchColor.ts +0 -34
- package/tailwind.config.ts +1 -0
- package/src/components/FillInputField.tsx +0 -37
- package/src/components/FillPreviewBackground.tsx +0 -134
- package/src/components/GradientPicker.tsx +0 -90
- package/src/utils/getGradientBackground.tsx +0 -31
|
@@ -43,14 +43,14 @@ export const InspectorContainer = memo(
|
|
|
43
43
|
{header}
|
|
44
44
|
{children ? (
|
|
45
45
|
<ScrollArea>
|
|
46
|
-
<div className="flex flex-col relative">
|
|
46
|
+
<div className="n-flex n-flex-col n-relative">
|
|
47
47
|
{showDividers
|
|
48
48
|
? withSeparatorElements(children, <Divider />)
|
|
49
49
|
: children}
|
|
50
50
|
</div>
|
|
51
51
|
</ScrollArea>
|
|
52
52
|
) : fallback ? (
|
|
53
|
-
<div className="flex flex-col relative h-full">{fallback}</div>
|
|
53
|
+
<div className="n-flex n-flex-col n-relative n-h-full">{fallback}</div>
|
|
54
54
|
) : null}
|
|
55
55
|
</div>
|
|
56
56
|
);
|
|
@@ -5,9 +5,9 @@ import React, {
|
|
|
5
5
|
memo,
|
|
6
6
|
ReactNode,
|
|
7
7
|
} from "react";
|
|
8
|
+
import { cx } from "../utils/classNames";
|
|
8
9
|
import { Spacer } from "./Spacer";
|
|
9
10
|
import { textStyles } from "./Text";
|
|
10
|
-
import { cx } from "../utils/classNames";
|
|
11
11
|
|
|
12
12
|
export const Section = forwardRef<
|
|
13
13
|
HTMLDivElement,
|
|
@@ -15,7 +15,7 @@ export const Section = forwardRef<
|
|
|
15
15
|
>(({ className, ...props }, ref) => (
|
|
16
16
|
<div
|
|
17
17
|
ref={ref}
|
|
18
|
-
className={cx(`flex-none flex flex-col
|
|
18
|
+
className={cx(`n-flex-none n-flex n-flex-col`, className)}
|
|
19
19
|
{...props}
|
|
20
20
|
/>
|
|
21
21
|
));
|
|
@@ -24,7 +24,7 @@ export const SectionHeader = forwardRef<
|
|
|
24
24
|
HTMLDivElement,
|
|
25
25
|
React.HTMLAttributes<HTMLDivElement>
|
|
26
26
|
>(({ className, ...props }, ref) => (
|
|
27
|
-
<div ref={ref} className={cx(`flex items-center
|
|
27
|
+
<div ref={ref} className={cx(`n-flex n-items-center`, className)} {...props} />
|
|
28
28
|
));
|
|
29
29
|
|
|
30
30
|
type TitleTextStyle = "small" | "heading5" | "heading4" | "heading3";
|
|
@@ -38,7 +38,11 @@ export const Title = forwardRef<
|
|
|
38
38
|
<div
|
|
39
39
|
ref={ref}
|
|
40
40
|
className={cx(
|
|
41
|
-
`flex select-none ${
|
|
41
|
+
`n-flex n-select-none ${
|
|
42
|
+
$textStyle
|
|
43
|
+
? `${textStyles[$textStyle]} n-text-text`
|
|
44
|
+
: "n-font-sans n-text-label n-text-text-muted n-font-semibold"
|
|
45
|
+
} `,
|
|
42
46
|
className
|
|
43
47
|
)}
|
|
44
48
|
{...props}
|
|
@@ -51,7 +55,7 @@ export const Row = forwardRef<
|
|
|
51
55
|
>(({ className, ...props }, ref) => (
|
|
52
56
|
<div
|
|
53
57
|
ref={ref}
|
|
54
|
-
className={cx(`flex-1 flex flex-row items-center
|
|
58
|
+
className={cx(`n-flex-1 n-flex n-flex-row n-items-center`, className)}
|
|
55
59
|
{...props}
|
|
56
60
|
/>
|
|
57
61
|
));
|
|
@@ -62,7 +66,7 @@ export const Column = forwardRef<
|
|
|
62
66
|
>(({ className, ...props }, ref) => (
|
|
63
67
|
<div
|
|
64
68
|
ref={ref}
|
|
65
|
-
className={cx(`flex-1 flex flex-col gap-1
|
|
69
|
+
className={cx(`n-flex-1 n-flex n-flex-col n-gap-1`, className)}
|
|
66
70
|
{...props}
|
|
67
71
|
/>
|
|
68
72
|
));
|
|
@@ -74,7 +78,7 @@ export const Checkbox = forwardRef<
|
|
|
74
78
|
<input
|
|
75
79
|
ref={ref}
|
|
76
80
|
type="checkbox"
|
|
77
|
-
className={cx(`m-0 focus:z-interactable
|
|
81
|
+
className={cx(`n-m-0 focus:n-z-interactable`, className)}
|
|
78
82
|
{...props}
|
|
79
83
|
/>
|
|
80
84
|
));
|
|
@@ -85,7 +89,7 @@ export const Text = forwardRef<
|
|
|
85
89
|
>(({ className, ...props }, ref) => (
|
|
86
90
|
<span
|
|
87
91
|
ref={ref}
|
|
88
|
-
className={cx(`font-sans text-heading5 font-normal
|
|
92
|
+
className={cx(`n-font-sans n-text-heading5 n-font-normal`, className)}
|
|
89
93
|
{...props}
|
|
90
94
|
/>
|
|
91
95
|
));
|
|
@@ -123,7 +127,9 @@ export const RowLabel = forwardRef(function RowLabel(
|
|
|
123
127
|
<label
|
|
124
128
|
ref={ref}
|
|
125
129
|
className={cx(
|
|
126
|
-
`font-sans text-label uppercase flex items-center leading-[19px] font-bold text-text-subtle z-label ${
|
|
130
|
+
`n-font-sans n-text-label n-uppercase n-flex n-items-center n-leading-[19px] n-font-bold n-text-text-subtle n-z-label ${
|
|
131
|
+
$textStyle && textStyles[$textStyle]
|
|
132
|
+
} `,
|
|
127
133
|
className
|
|
128
134
|
)}
|
|
129
135
|
{...props}
|
package/src/components/Label.tsx
CHANGED
|
@@ -3,9 +3,10 @@ import { mergeConflictingClassNames } from "../utils/classNames";
|
|
|
3
3
|
|
|
4
4
|
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
|
|
5
5
|
|
|
6
|
-
const labelStyles =
|
|
6
|
+
const labelStyles =
|
|
7
|
+
"n-flex n-items-center n-select-none n-z-label n-relative";
|
|
7
8
|
const labelTextStyles =
|
|
8
|
-
"font-sans text-label
|
|
9
|
+
"n-font-sans n-text-label n-leading-none n-font-semibold n-text-text-muted";
|
|
9
10
|
|
|
10
11
|
export const Label = memo(
|
|
11
12
|
forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
@@ -19,7 +20,7 @@ export const Label = memo(
|
|
|
19
20
|
[
|
|
20
21
|
labelStyles,
|
|
21
22
|
labelTextStyles,
|
|
22
|
-
"text-left min-h-input-height",
|
|
23
|
+
"n-text-left n-min-h-input-height",
|
|
23
24
|
className,
|
|
24
25
|
],
|
|
25
26
|
{
|
|
@@ -152,9 +152,9 @@ export const LabeledElementView = React.memo(function LabeledElementView({
|
|
|
152
152
|
}, [refs, labelElements]);
|
|
153
153
|
|
|
154
154
|
return (
|
|
155
|
-
<div className="flex flex-1 flex-col relative" ref={containerRef}>
|
|
156
|
-
<div className="flex flex-1 items-center">{children}</div>
|
|
157
|
-
<div className="relative overflow-hidden select-none">
|
|
155
|
+
<div className="n-flex n-flex-1 n-flex-col n-relative" ref={containerRef}>
|
|
156
|
+
<div className="n-flex n-flex-1 n-items-center">{children}</div>
|
|
157
|
+
<div className="n-relative n-overflow-hidden n-select-none">
|
|
158
158
|
{labelElements}
|
|
159
159
|
</div>
|
|
160
160
|
</div>
|
|
@@ -15,9 +15,9 @@ import { Label } from "./Label";
|
|
|
15
15
|
|
|
16
16
|
export const labeledFieldStyles = (labelType?: LabelType) =>
|
|
17
17
|
cx(
|
|
18
|
-
"flex flex-row flex-1 flex-wrap items-center gap-x-1.5",
|
|
19
|
-
labelType === "end" && "flex-row-reverse justify-end",
|
|
20
|
-
labelType === "above" && "flex-col items-stretch justify-start"
|
|
18
|
+
"n-flex n-flex-row n-flex-1 n-flex-wrap n-items-center n-gap-x-1.5",
|
|
19
|
+
labelType === "end" && "n-flex-row-reverse n-justify-end",
|
|
20
|
+
labelType === "above" && "n-flex-col n-items-stretch n-justify-start"
|
|
21
21
|
);
|
|
22
22
|
|
|
23
23
|
const labelTypeInset: LabelTypeContextValue = {
|
|
@@ -96,7 +96,7 @@ export const LabeledField = memo(function LabeledField({
|
|
|
96
96
|
{children}
|
|
97
97
|
</LabelTypeContext.Provider>
|
|
98
98
|
) : (
|
|
99
|
-
<div className={cx("flex relative flex-1", className)} style={style}>
|
|
99
|
+
<div className={cx("n-flex n-relative n-flex-1", className)} style={style}>
|
|
100
100
|
{Boolean(indentLevel) && <div style={indentStyle} />}
|
|
101
101
|
<div className={labeledFieldClasses}>
|
|
102
102
|
{labelType !== "none" && (
|
|
@@ -106,7 +106,7 @@ export const LabeledField = memo(function LabeledField({
|
|
|
106
106
|
// and when start/end position with textarea it aligns label to the top of the field
|
|
107
107
|
className={
|
|
108
108
|
labelType === "start" || labelType === "end"
|
|
109
|
-
? "self-start"
|
|
109
|
+
? "n-self-start"
|
|
110
110
|
: undefined
|
|
111
111
|
}
|
|
112
112
|
style={labelStyle}
|
|
@@ -116,7 +116,7 @@ export const LabeledField = memo(function LabeledField({
|
|
|
116
116
|
</Label>
|
|
117
117
|
)}
|
|
118
118
|
{labelType === "start" ? (
|
|
119
|
-
<div className="flex-auto flex" style={fieldStyle}>
|
|
119
|
+
<div className="n-flex-auto n-flex" style={fieldStyle}>
|
|
120
120
|
{children}
|
|
121
121
|
</div>
|
|
122
122
|
) : (
|
package/src/components/List.tsx
CHANGED
|
@@ -5,7 +5,13 @@ import {
|
|
|
5
5
|
memoGeneric,
|
|
6
6
|
useFileDropTarget,
|
|
7
7
|
} from "@noya-app/react-utils";
|
|
8
|
-
import React, {
|
|
8
|
+
import React, {
|
|
9
|
+
memo,
|
|
10
|
+
useImperativeHandle,
|
|
11
|
+
useMemo,
|
|
12
|
+
useRef,
|
|
13
|
+
useState,
|
|
14
|
+
} from "react";
|
|
9
15
|
import { useLinkComponent } from "../contexts/LinkComponentContext";
|
|
10
16
|
import { cssVars } from "../theme";
|
|
11
17
|
import { cx } from "../utils/classNames";
|
|
@@ -16,7 +22,8 @@ import {
|
|
|
16
22
|
CollectionProps,
|
|
17
23
|
CollectionRef,
|
|
18
24
|
} from "./Collection";
|
|
19
|
-
import { ListView
|
|
25
|
+
import { ListView } from "./ListView";
|
|
26
|
+
import { ListViewItemInfo } from "./listView/ListViewRoot";
|
|
20
27
|
import { TreeView } from "./TreeView";
|
|
21
28
|
|
|
22
29
|
const cssVarOverrides = {
|
|
@@ -47,7 +54,10 @@ export const List = memoGeneric(
|
|
|
47
54
|
renderThumbnail,
|
|
48
55
|
renderAction,
|
|
49
56
|
renderDetail,
|
|
57
|
+
renderRight,
|
|
50
58
|
onSelectionChange,
|
|
59
|
+
itemStyle: itemStyleProp,
|
|
60
|
+
itemClassName: itemClassNameProp,
|
|
51
61
|
itemRoleDescription = "clickable item",
|
|
52
62
|
detailPosition = "end",
|
|
53
63
|
size = "medium",
|
|
@@ -67,9 +77,11 @@ export const List = memoGeneric(
|
|
|
67
77
|
getDropTargetParentIndex,
|
|
68
78
|
getPlaceholder,
|
|
69
79
|
onClickItem,
|
|
80
|
+
onClick,
|
|
70
81
|
dragIndicatorStyle,
|
|
71
82
|
sortableId,
|
|
72
83
|
sharedDragProps,
|
|
84
|
+
virtualized,
|
|
73
85
|
} = props;
|
|
74
86
|
|
|
75
87
|
const [internalHoveredId, setHoveredId] = useState<string>();
|
|
@@ -77,6 +89,16 @@ export const List = memoGeneric(
|
|
|
77
89
|
const [dropdownOpenId, setDropdownOpenId] = useState<string>();
|
|
78
90
|
const hoveredId = internalHoveredId ?? testHoveredId;
|
|
79
91
|
const renamingId = testRenamingId ?? internalRenamingId;
|
|
92
|
+
const itemClassName = cx(
|
|
93
|
+
"n-cursor-pointer n-px-1",
|
|
94
|
+
pyMap[size],
|
|
95
|
+
itemClassNameProp
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const itemStyle = useMemo(
|
|
99
|
+
() => ({ ...cssVarOverrides, ...itemStyleProp }),
|
|
100
|
+
[itemStyleProp]
|
|
101
|
+
);
|
|
80
102
|
|
|
81
103
|
const handleSelect = (itemId: string, event?: ListView.ClickInfo) => {
|
|
82
104
|
if (!onSelectionChange) return;
|
|
@@ -124,11 +146,12 @@ export const List = memoGeneric(
|
|
|
124
146
|
variant="bare"
|
|
125
147
|
className={cx(
|
|
126
148
|
className,
|
|
127
|
-
isDropTargetActive && "bg-selected-list-item-background",
|
|
128
|
-
scrollable && "basis-0 min-h-0"
|
|
149
|
+
isDropTargetActive && "n-bg-selected-list-item-background",
|
|
150
|
+
scrollable && "n-basis-0 n-min-h-0"
|
|
129
151
|
)}
|
|
130
152
|
containerRef={fileDropTargetRef}
|
|
131
153
|
scrollable={scrollable}
|
|
154
|
+
virtualized={virtualized}
|
|
132
155
|
data={items}
|
|
133
156
|
keyExtractor={getId}
|
|
134
157
|
role="listbox"
|
|
@@ -142,6 +165,8 @@ export const List = memoGeneric(
|
|
|
142
165
|
getDropTargetParentIndex={getDropTargetParentIndex}
|
|
143
166
|
sharedDragProps={sharedDragProps}
|
|
144
167
|
sortableId={sortableId}
|
|
168
|
+
onPress={onClick}
|
|
169
|
+
itemHeight={itemHeightMap[size]}
|
|
145
170
|
{...dropTargetProps}
|
|
146
171
|
renderItem={(
|
|
147
172
|
item: T,
|
|
@@ -198,9 +223,9 @@ export const List = memoGeneric(
|
|
|
198
223
|
const end =
|
|
199
224
|
action ||
|
|
200
225
|
(detailPosition === "end" && renderDetail?.(item, isSelected));
|
|
226
|
+
const right = renderRight?.(item, isSelected);
|
|
201
227
|
const below =
|
|
202
228
|
detailPosition === "below" && renderDetail?.(item, isSelected);
|
|
203
|
-
|
|
204
229
|
return (
|
|
205
230
|
<ViewComponent.Row
|
|
206
231
|
as={typeof href === "string" ? LinkComponent : undefined}
|
|
@@ -212,12 +237,12 @@ export const List = memoGeneric(
|
|
|
212
237
|
aria-roledescription={itemRoleDescription}
|
|
213
238
|
aria-label={getName(item)}
|
|
214
239
|
aria-selected={isSelected}
|
|
215
|
-
className={
|
|
240
|
+
className={itemClassName}
|
|
216
241
|
testRelativeDropPosition={
|
|
217
242
|
testShowDropIndicatorId === id ? "inside" : undefined
|
|
218
243
|
}
|
|
219
|
-
chevronClassName={cx(expandable && "relative left-2")}
|
|
220
|
-
style={
|
|
244
|
+
chevronClassName={cx(expandable && "n-relative n-left-2")}
|
|
245
|
+
style={itemStyle}
|
|
221
246
|
hovered={isHovered}
|
|
222
247
|
selected={isSelected}
|
|
223
248
|
expanded={expanded}
|
|
@@ -228,6 +253,7 @@ export const List = memoGeneric(
|
|
|
228
253
|
onPress={(e: ListView.ClickInfo) => {
|
|
229
254
|
handleSelect(id, e);
|
|
230
255
|
onClickItem?.(id);
|
|
256
|
+
e.stopPropagation();
|
|
231
257
|
}}
|
|
232
258
|
onDoubleClick={() => {
|
|
233
259
|
onDoubleClickItem?.(id);
|
|
@@ -255,24 +281,24 @@ export const List = memoGeneric(
|
|
|
255
281
|
dragIndicatorStyle={dragIndicatorStyle}
|
|
256
282
|
>
|
|
257
283
|
<div
|
|
258
|
-
className={cx("flex flex-1 items-center px-2", rowGapMap[size])}
|
|
284
|
+
className={cx("n-flex n-flex-1 n-items-center n-px-2", rowGapMap[size])}
|
|
259
285
|
>
|
|
260
286
|
{thumbnail && (
|
|
261
287
|
<div
|
|
262
288
|
className={cx(
|
|
263
|
-
"overflow-hidden flex-none flex items-center justify-center",
|
|
289
|
+
"n-overflow-hidden n-flex-none n-flex n-items-center n-justify-center",
|
|
264
290
|
thumbnailSize === "auto" && thumbnailSizeMap[size],
|
|
265
|
-
isSelected && "text-selected-list-item-text",
|
|
266
|
-
size
|
|
291
|
+
isSelected && "n-text-selected-list-item-text",
|
|
292
|
+
roundedMap[size]
|
|
267
293
|
)}
|
|
268
294
|
tabIndex={-1}
|
|
269
295
|
>
|
|
270
296
|
{thumbnail}
|
|
271
297
|
</div>
|
|
272
298
|
)}
|
|
273
|
-
<div className="flex flex-col flex-1 w-0">
|
|
274
|
-
<div className="flex items-center min-h-input-height flex-1 gap-2">
|
|
275
|
-
<div className="flex-1 w-0 flex items-center" tabIndex={-1}>
|
|
299
|
+
<div className="n-flex n-flex-col n-flex-1 n-w-0">
|
|
300
|
+
<div className="n-flex n-items-center n-min-h-input-height n-flex-1 n-gap-2">
|
|
301
|
+
<div className="n-flex-1 n-w-0 n-flex n-items-center" tabIndex={-1}>
|
|
276
302
|
{isRenaming ? (
|
|
277
303
|
<ViewComponent.EditableRowTitle
|
|
278
304
|
value={getName(item)}
|
|
@@ -291,13 +317,19 @@ export const List = memoGeneric(
|
|
|
291
317
|
<ViewComponent.RowTitle
|
|
292
318
|
className={cx(
|
|
293
319
|
fontStyleMap[size],
|
|
294
|
-
isSelected && "text-selected-list-item-text"
|
|
320
|
+
isSelected && "n-text-selected-list-item-text"
|
|
295
321
|
)}
|
|
296
322
|
>
|
|
297
323
|
{getName(item)}
|
|
298
324
|
</ViewComponent.RowTitle>
|
|
299
325
|
)}
|
|
300
326
|
</div>
|
|
327
|
+
{/* Right content should appear before end/detail when detailPosition is 'end' */}
|
|
328
|
+
{right && detailPosition === "end" && (
|
|
329
|
+
<DetailContainer selected={isSelected}>
|
|
330
|
+
{right}
|
|
331
|
+
</DetailContainer>
|
|
332
|
+
)}
|
|
301
333
|
{end && detailPosition === "end" && (
|
|
302
334
|
<DetailContainer selected={isSelected}>
|
|
303
335
|
{end}
|
|
@@ -310,6 +342,12 @@ export const List = memoGeneric(
|
|
|
310
342
|
</DetailContainer>
|
|
311
343
|
)}
|
|
312
344
|
</div>
|
|
345
|
+
{/* When detail is below, right should still render on the right next to the action */}
|
|
346
|
+
{right && detailPosition === "below" && (
|
|
347
|
+
<DetailContainer selected={isSelected}>
|
|
348
|
+
{right}
|
|
349
|
+
</DetailContainer>
|
|
350
|
+
)}
|
|
313
351
|
{end && detailPosition === "below" && (
|
|
314
352
|
<DetailContainer selected={isSelected}>{end}</DetailContainer>
|
|
315
353
|
)}
|
|
@@ -331,7 +369,7 @@ const DetailContainer = memo(function DetailContainer({
|
|
|
331
369
|
}) {
|
|
332
370
|
return (
|
|
333
371
|
<div
|
|
334
|
-
className={cx("flex items-center gap-2", selected && "text-primary")}
|
|
372
|
+
className={cx("n-flex n-items-center n-gap-2", selected && "n-text-primary")}
|
|
335
373
|
tabIndex={-1}
|
|
336
374
|
>
|
|
337
375
|
{children}
|
|
@@ -340,41 +378,61 @@ const DetailContainer = memo(function DetailContainer({
|
|
|
340
378
|
});
|
|
341
379
|
|
|
342
380
|
const pyMap: Record<CollectionItemSize, string> = {
|
|
343
|
-
xxs: "py-0.5",
|
|
344
|
-
xs: "py-0.5",
|
|
345
|
-
small: "py-
|
|
346
|
-
medium: "py-1",
|
|
347
|
-
mediumLarge: "py-1.5",
|
|
348
|
-
large: "py-2",
|
|
349
|
-
xl: "py-3",
|
|
381
|
+
xxs: "n-py-0.5",
|
|
382
|
+
xs: "n-py-0.5",
|
|
383
|
+
small: "n-py-1",
|
|
384
|
+
medium: "n-py-1",
|
|
385
|
+
mediumLarge: "n-py-1.5",
|
|
386
|
+
large: "n-py-2",
|
|
387
|
+
xl: "n-py-3",
|
|
350
388
|
};
|
|
351
389
|
|
|
352
390
|
const thumbnailSizeMap: Record<CollectionItemSize, string> = {
|
|
353
|
-
xxs: "w-
|
|
354
|
-
xs: "w-5 h-5",
|
|
355
|
-
small: "w-
|
|
356
|
-
medium: "w-8 h-8",
|
|
357
|
-
mediumLarge: "w-12 h-12",
|
|
358
|
-
large: "w-16 h-16",
|
|
359
|
-
xl: "w-24 h-24",
|
|
391
|
+
xxs: "n-w-4 n-h-4",
|
|
392
|
+
xs: "n-w-5 n-h-5",
|
|
393
|
+
small: "n-w-6 n-h-6",
|
|
394
|
+
medium: "n-w-8 n-h-8",
|
|
395
|
+
mediumLarge: "n-w-12 n-h-12",
|
|
396
|
+
large: "n-w-16 n-h-16",
|
|
397
|
+
xl: "n-w-24 n-h-24",
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const roundedMap: Record<CollectionItemSize, string> = {
|
|
401
|
+
xxs: "n-rounded-sm",
|
|
402
|
+
xs: "n-rounded-sm",
|
|
403
|
+
small: "n-rounded",
|
|
404
|
+
medium: "n-rounded",
|
|
405
|
+
mediumLarge: "n-rounded",
|
|
406
|
+
large: "n-rounded",
|
|
407
|
+
xl: "n-rounded",
|
|
360
408
|
};
|
|
361
409
|
|
|
362
410
|
const rowGapMap: Record<CollectionItemSize, string> = {
|
|
363
|
-
xxs: "gap-2",
|
|
364
|
-
xs: "gap-2",
|
|
365
|
-
small: "gap-2",
|
|
366
|
-
medium: "gap-2",
|
|
367
|
-
mediumLarge: "gap-3",
|
|
368
|
-
large: "gap-3",
|
|
369
|
-
xl: "gap-4",
|
|
411
|
+
xxs: "n-gap-2",
|
|
412
|
+
xs: "n-gap-2",
|
|
413
|
+
small: "n-gap-2",
|
|
414
|
+
medium: "n-gap-2",
|
|
415
|
+
mediumLarge: "n-gap-3",
|
|
416
|
+
large: "n-gap-3",
|
|
417
|
+
xl: "n-gap-4",
|
|
370
418
|
};
|
|
371
419
|
|
|
372
420
|
const fontStyleMap: Record<CollectionItemSize, string> = {
|
|
373
|
-
xxs: "font-medium text-list-small",
|
|
374
|
-
xs: "font-medium text-list-small",
|
|
375
|
-
small: "font-medium text-list-small",
|
|
376
|
-
medium: "font-medium",
|
|
377
|
-
mediumLarge: "font-medium",
|
|
378
|
-
large: "font-medium",
|
|
379
|
-
xl: "font-medium text-heading4 text-text",
|
|
421
|
+
xxs: "n-font-medium n-text-list-small",
|
|
422
|
+
xs: "n-font-medium n-text-list-small",
|
|
423
|
+
small: "n-font-medium n-text-list-small",
|
|
424
|
+
medium: "n-font-medium",
|
|
425
|
+
mediumLarge: "n-font-medium",
|
|
426
|
+
large: "n-font-medium",
|
|
427
|
+
xl: "n-font-medium n-text-heading4 n-text-text",
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const itemHeightMap: Record<CollectionItemSize, number> = {
|
|
431
|
+
xxs: 31,
|
|
432
|
+
xs: 31,
|
|
433
|
+
small: 31,
|
|
434
|
+
medium: 40,
|
|
435
|
+
mediumLarge: 60,
|
|
436
|
+
large: 80,
|
|
437
|
+
xl: 120,
|
|
380
438
|
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ArrowRightIcon } from "@noya-app/noya-icons";
|
|
4
|
+
import { chunkBy } from "@noya-app/noya-utils";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import withSeparatorElements from "../utils/withSeparatorElements";
|
|
7
|
+
import { CollectionProps } from "./Collection";
|
|
8
|
+
import { Divider } from "./Divider";
|
|
9
|
+
import { renderIcon } from "./Icons";
|
|
10
|
+
import {
|
|
11
|
+
isSelectableMenuItem,
|
|
12
|
+
isSubMenuItem,
|
|
13
|
+
MenuItem,
|
|
14
|
+
SelectableMenuItem,
|
|
15
|
+
SubMenuItem,
|
|
16
|
+
} from "./internal/Menu";
|
|
17
|
+
import { List } from "./List";
|
|
18
|
+
|
|
19
|
+
export type ListMenuProps<T extends string> = {
|
|
20
|
+
items: MenuItem<T>[];
|
|
21
|
+
selectedIds?: T[];
|
|
22
|
+
onSelect: (item: T) => void;
|
|
23
|
+
renderRight?: CollectionProps<MenuItem<T>, T>["renderRight"];
|
|
24
|
+
className?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function getItemId<T extends string>(
|
|
28
|
+
item: SubMenuItem<T> | SelectableMenuItem<T>
|
|
29
|
+
): string {
|
|
30
|
+
if (item.type === "submenu") {
|
|
31
|
+
return item.id;
|
|
32
|
+
}
|
|
33
|
+
return item.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function ListMenu<T extends string>({
|
|
37
|
+
items,
|
|
38
|
+
selectedIds: selectedIdsProp,
|
|
39
|
+
onSelect,
|
|
40
|
+
renderRight,
|
|
41
|
+
className,
|
|
42
|
+
}: ListMenuProps<T>) {
|
|
43
|
+
const selectedIds = items
|
|
44
|
+
.flatMap((item) =>
|
|
45
|
+
isSelectableMenuItem(item) && item.checked ? [item.value] : []
|
|
46
|
+
)
|
|
47
|
+
.concat(selectedIdsProp as T[]);
|
|
48
|
+
|
|
49
|
+
let chunks = chunkBy(
|
|
50
|
+
items,
|
|
51
|
+
(a, b) => a.type !== "separator" && b.type !== "separator"
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
chunks = chunks.map((chunk) =>
|
|
55
|
+
chunk.filter((item) => item.type !== "separator")
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
chunks = chunks.filter((chunk) => chunk.length > 0);
|
|
59
|
+
|
|
60
|
+
const lists = chunks.map((chunk) => {
|
|
61
|
+
const regularItems = chunk.filter(
|
|
62
|
+
(item) => isSubMenuItem(item) || isSelectableMenuItem(item)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<List
|
|
67
|
+
key={regularItems.map(getItemId).join("-")}
|
|
68
|
+
items={regularItems}
|
|
69
|
+
size="small"
|
|
70
|
+
itemClassName="n-rounded-sm"
|
|
71
|
+
className={className}
|
|
72
|
+
getId={getItemId}
|
|
73
|
+
getName={(item) => item.title.toString()}
|
|
74
|
+
selectedIds={selectedIds}
|
|
75
|
+
renderRight={renderRight}
|
|
76
|
+
renderThumbnail={({ item }) => (
|
|
77
|
+
<div className="n-flex n-flex-1 n-items-center n-justify-center n-bg-divider-subtle n-w-full n-h-full n-rounded-sm">
|
|
78
|
+
{renderIcon(item.icon)}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
renderDetail={(item) => isSubMenuItem(item) && <ArrowRightIcon />}
|
|
82
|
+
onClickItem={(itemId) => onSelect(itemId as T)}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return withSeparatorElements(
|
|
88
|
+
lists,
|
|
89
|
+
<Divider overflow={12} className="n-my-1" />
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { isSubMenuItem, SubMenuItem } from "./internal/Menu";
|
|
5
|
+
import { ListMenu, ListMenuProps } from "./ListMenu";
|
|
6
|
+
import { createNavigator, NavigatorProps } from "./Navigator";
|
|
7
|
+
|
|
8
|
+
export type ListNavigatorProps<T extends string> = Pick<
|
|
9
|
+
ListMenuProps<T>,
|
|
10
|
+
"items" | "renderRight" | "selectedIds"
|
|
11
|
+
> &
|
|
12
|
+
Pick<NavigatorProps<any, any>, "className" | "style"> & {
|
|
13
|
+
title?: React.ReactNode;
|
|
14
|
+
onSelect: (item: T, { popToRoot }: { popToRoot: () => void }) => void;
|
|
15
|
+
renderHeader?: ({
|
|
16
|
+
title,
|
|
17
|
+
onBack,
|
|
18
|
+
}: {
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
onBack?: () => void;
|
|
21
|
+
}) => React.ReactNode;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const ListNavigatorInternal = createNavigator<{
|
|
25
|
+
screen: { id: string | undefined };
|
|
26
|
+
}>();
|
|
27
|
+
|
|
28
|
+
export function ListNavigator<T extends string>({
|
|
29
|
+
title,
|
|
30
|
+
items,
|
|
31
|
+
onSelect,
|
|
32
|
+
className,
|
|
33
|
+
style,
|
|
34
|
+
renderRight,
|
|
35
|
+
renderHeader,
|
|
36
|
+
selectedIds,
|
|
37
|
+
}: ListNavigatorProps<T>) {
|
|
38
|
+
return (
|
|
39
|
+
<ListNavigatorInternal
|
|
40
|
+
initialRoute={{ name: "screen", params: { id: undefined } }}
|
|
41
|
+
renderHeader={
|
|
42
|
+
renderHeader &&
|
|
43
|
+
(({ route, onBack }) => {
|
|
44
|
+
const parentItem = items.find(
|
|
45
|
+
(item): item is SubMenuItem<T> =>
|
|
46
|
+
isSubMenuItem(item) && item.id === route.params.id
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return renderHeader({
|
|
50
|
+
title: parentItem?.title ?? title,
|
|
51
|
+
onBack: parentItem ? onBack : undefined,
|
|
52
|
+
});
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
// Always show header if there is a renderHeader or title.
|
|
56
|
+
// Otherwise, show header if there is more than one item in the stack.
|
|
57
|
+
showHeader={
|
|
58
|
+
Boolean(renderHeader) ||
|
|
59
|
+
Boolean(title) ||
|
|
60
|
+
(({ stack }) => stack.length > 1)
|
|
61
|
+
}
|
|
62
|
+
cardClassName="n-py-1"
|
|
63
|
+
className={className}
|
|
64
|
+
style={style}
|
|
65
|
+
>
|
|
66
|
+
<ListNavigatorInternal.Screen
|
|
67
|
+
name="screen"
|
|
68
|
+
title={({ route }) => {
|
|
69
|
+
const nestedItem = items.find(
|
|
70
|
+
(item): item is SubMenuItem<T> =>
|
|
71
|
+
isSubMenuItem(item) && item.id === route.params.id
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return nestedItem?.title ?? title ?? null;
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{({ route, navigation }) => {
|
|
78
|
+
const nestedItem = items.find(
|
|
79
|
+
(item): item is SubMenuItem<T> =>
|
|
80
|
+
isSubMenuItem(item) && item.id === route.params.id
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ListMenu
|
|
85
|
+
items={nestedItem?.items ?? items}
|
|
86
|
+
selectedIds={selectedIds}
|
|
87
|
+
onSelect={(itemId) => {
|
|
88
|
+
const nestedItem = items.find(
|
|
89
|
+
(item): item is SubMenuItem<T> =>
|
|
90
|
+
isSubMenuItem(item) && item.id === itemId
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (nestedItem) {
|
|
94
|
+
navigation.push("screen", { id: nestedItem.id });
|
|
95
|
+
} else {
|
|
96
|
+
onSelect(itemId as T, { popToRoot: () => navigation.pop() });
|
|
97
|
+
}
|
|
98
|
+
}}
|
|
99
|
+
renderRight={renderRight}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
}}
|
|
103
|
+
</ListNavigatorInternal.Screen>
|
|
104
|
+
</ListNavigatorInternal>
|
|
105
|
+
);
|
|
106
|
+
}
|