@geminilight/mindos 0.6.16 → 0.6.17
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/app/app/globals.css +5 -2
- package/app/components/CustomSelect.tsx +64 -36
- package/app/components/DirPicker.tsx +111 -81
- package/app/components/ImportModal.tsx +18 -11
- package/package.json +1 -1
package/app/app/globals.css
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
--color-input: var(--input);
|
|
27
27
|
--color-border: var(--border);
|
|
28
28
|
--color-destructive: var(--destructive);
|
|
29
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
29
30
|
--color-success: var(--success);
|
|
30
31
|
--color-error: var(--error);
|
|
31
32
|
--color-amber-foreground: var(--amber-foreground);
|
|
@@ -69,7 +70,8 @@ body {
|
|
|
69
70
|
--muted-foreground: #685f52;
|
|
70
71
|
--accent: #d9d3c6;
|
|
71
72
|
--accent-foreground: #1c1a17;
|
|
72
|
-
--destructive: oklch(0.
|
|
73
|
+
--destructive: oklch(0.56 0.14 24);
|
|
74
|
+
--destructive-foreground: #ffffff;
|
|
73
75
|
--border: rgba(28, 26, 23, 0.1);
|
|
74
76
|
--input: rgba(28, 26, 23, 0.12);
|
|
75
77
|
--ring: var(--amber);
|
|
@@ -106,7 +108,8 @@ body {
|
|
|
106
108
|
--muted-foreground: #8a8275;
|
|
107
109
|
--accent: #2e2b22;
|
|
108
110
|
--accent-foreground: #e8e4dc;
|
|
109
|
-
--destructive: oklch(0.
|
|
111
|
+
--destructive: oklch(0.56 0.14 22);
|
|
112
|
+
--destructive-foreground: #ffffff;
|
|
110
113
|
--border: rgba(232, 228, 220, 0.08);
|
|
111
114
|
--input: rgba(232, 228, 220, 0.1);
|
|
112
115
|
--ring: var(--amber);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
4
5
|
import { ChevronDown, Check } from 'lucide-react';
|
|
5
6
|
|
|
6
7
|
export interface SelectOption {
|
|
@@ -50,7 +51,7 @@ export default function CustomSelect({
|
|
|
50
51
|
}: CustomSelectProps) {
|
|
51
52
|
const [open, setOpen] = useState(false);
|
|
52
53
|
const [highlightIdx, setHighlightIdx] = useState(-1);
|
|
53
|
-
const [
|
|
54
|
+
const [panelPos, setPanelPos] = useState<{ top: number; left: number; width: number; flipUp: boolean } | null>(null);
|
|
54
55
|
const btnRef = useRef<HTMLButtonElement>(null);
|
|
55
56
|
const listRef = useRef<HTMLDivElement>(null);
|
|
56
57
|
|
|
@@ -122,20 +123,33 @@ export default function CustomSelect({
|
|
|
122
123
|
if (el) el.scrollIntoView({ block: 'nearest' });
|
|
123
124
|
}, [open, highlightIdx]);
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
const calcPosition = useCallback(() => {
|
|
127
|
+
if (!btnRef.current) return;
|
|
128
|
+
const rect = btnRef.current.getBoundingClientRect();
|
|
129
|
+
const maxH = size === 'sm' ? 200 : 260;
|
|
130
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
131
|
+
const spaceAbove = rect.top;
|
|
132
|
+
setPanelPos({
|
|
133
|
+
top: spaceBelow < maxH + 8 && spaceAbove > spaceBelow ? rect.top : rect.bottom,
|
|
134
|
+
left: rect.left,
|
|
135
|
+
width: rect.width,
|
|
136
|
+
flipUp: spaceBelow < maxH + 8 && spaceAbove > spaceBelow,
|
|
137
|
+
});
|
|
138
|
+
}, [size]);
|
|
139
|
+
|
|
140
|
+
// Initialize highlight + position when opening; reposition on scroll/resize
|
|
126
141
|
useEffect(() => {
|
|
127
|
-
if (open) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}, [open, allOptions, value, size]);
|
|
142
|
+
if (!open) { setPanelPos(null); return; }
|
|
143
|
+
const idx = allOptions.findIndex(o => o.value === value);
|
|
144
|
+
setHighlightIdx(idx >= 0 ? idx : 0);
|
|
145
|
+
calcPosition();
|
|
146
|
+
window.addEventListener('scroll', calcPosition, true);
|
|
147
|
+
window.addEventListener('resize', calcPosition);
|
|
148
|
+
return () => {
|
|
149
|
+
window.removeEventListener('scroll', calcPosition, true);
|
|
150
|
+
window.removeEventListener('resize', calcPosition);
|
|
151
|
+
};
|
|
152
|
+
}, [open, allOptions, value, calcPosition]);
|
|
139
153
|
|
|
140
154
|
const isSm = size === 'sm';
|
|
141
155
|
|
|
@@ -147,9 +161,9 @@ export default function CustomSelect({
|
|
|
147
161
|
? 'absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none'
|
|
148
162
|
: 'absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none';
|
|
149
163
|
|
|
150
|
-
const
|
|
151
|
-
?
|
|
152
|
-
:
|
|
164
|
+
const listBaseCls = isSm
|
|
165
|
+
? 'fixed z-[9999] overflow-y-auto rounded-md border border-border bg-card shadow-lg py-0.5'
|
|
166
|
+
: 'fixed z-[9999] overflow-y-auto rounded-lg border border-border bg-card shadow-lg py-1';
|
|
153
167
|
|
|
154
168
|
const itemBaseCls = isSm
|
|
155
169
|
? 'w-full flex items-center gap-1.5 px-2 py-1 text-2xs text-left transition-colors cursor-pointer'
|
|
@@ -184,6 +198,38 @@ export default function CustomSelect({
|
|
|
184
198
|
);
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
const listPortal = open && panelPos && createPortal(
|
|
202
|
+
<div
|
|
203
|
+
ref={listRef}
|
|
204
|
+
className={listBaseCls}
|
|
205
|
+
role="listbox"
|
|
206
|
+
style={{
|
|
207
|
+
left: panelPos.left,
|
|
208
|
+
minWidth: panelPos.width,
|
|
209
|
+
maxHeight: isSm ? 200 : 260,
|
|
210
|
+
...(panelPos.flipUp
|
|
211
|
+
? { bottom: window.innerHeight - panelPos.top + 4 }
|
|
212
|
+
: { top: panelPos.top + 4 }),
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
{options.map((item, idx) => {
|
|
216
|
+
if (isGroup(item)) {
|
|
217
|
+
return (
|
|
218
|
+
<div key={item.label}>
|
|
219
|
+
{idx > 0 && <div className="my-0.5 border-t border-border/50" />}
|
|
220
|
+
<div className={`py-1 text-2xs font-medium text-muted-foreground uppercase tracking-wider ${isSm ? 'px-2' : 'px-3'}`}>
|
|
221
|
+
{item.label}
|
|
222
|
+
</div>
|
|
223
|
+
{item.options.map(renderOption)}
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return renderOption(item);
|
|
228
|
+
})}
|
|
229
|
+
</div>,
|
|
230
|
+
document.body,
|
|
231
|
+
);
|
|
232
|
+
|
|
187
233
|
return (
|
|
188
234
|
<div className="relative">
|
|
189
235
|
<button
|
|
@@ -204,25 +250,7 @@ export default function CustomSelect({
|
|
|
204
250
|
className={`${chevronCls} transition-transform duration-150 ${open ? 'rotate-180' : ''}`}
|
|
205
251
|
/>
|
|
206
252
|
</button>
|
|
207
|
-
|
|
208
|
-
{open && (
|
|
209
|
-
<div ref={listRef} className={listCls} role="listbox">
|
|
210
|
-
{options.map((item, idx) => {
|
|
211
|
-
if (isGroup(item)) {
|
|
212
|
-
return (
|
|
213
|
-
<div key={item.label}>
|
|
214
|
-
{idx > 0 && <div className="my-0.5 border-t border-border/50" />}
|
|
215
|
-
<div className={`py-1 text-2xs font-medium text-muted-foreground uppercase tracking-wider ${isSm ? 'px-2' : 'px-3'}`}>
|
|
216
|
-
{item.label}
|
|
217
|
-
</div>
|
|
218
|
-
{item.options.map(renderOption)}
|
|
219
|
-
</div>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
return renderOption(item);
|
|
223
|
-
})}
|
|
224
|
-
</div>
|
|
225
|
-
)}
|
|
253
|
+
{listPortal}
|
|
226
254
|
</div>
|
|
227
255
|
);
|
|
228
256
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
4
5
|
import { Folder, ChevronDown, ChevronRight, Check } from 'lucide-react';
|
|
5
6
|
import { stripEmoji } from '@/lib/utils';
|
|
6
7
|
|
|
@@ -18,27 +19,44 @@ interface DirPickerProps {
|
|
|
18
19
|
const PANEL_MAX_H = 200;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
* Hierarchical directory picker —
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* Hierarchical directory picker — trigger button stays in layout flow;
|
|
23
|
+
* the expanded panel renders via portal with position:fixed so it escapes
|
|
24
|
+
* any ancestor overflow:hidden / overflow:auto containers.
|
|
24
25
|
*/
|
|
25
26
|
export default function DirPicker({ dirPaths, value, onChange, rootLabel = 'Root' }: DirPickerProps) {
|
|
26
27
|
const [expanded, setExpanded] = useState(false);
|
|
27
28
|
const [browsing, setBrowsing] = useState(value);
|
|
28
|
-
const [
|
|
29
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
29
|
+
const [panelPos, setPanelPos] = useState<{ top: number; left: number; width: number; flipUp: boolean } | null>(null);
|
|
30
30
|
const btnRef = useRef<HTMLButtonElement>(null);
|
|
31
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
31
32
|
|
|
32
33
|
useEffect(() => { setBrowsing(value); }, [value]);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!expanded || !btnRef.current) return;
|
|
35
|
+
const calcPosition = useCallback(() => {
|
|
36
|
+
if (!btnRef.current) return;
|
|
37
37
|
const rect = btnRef.current.getBoundingClientRect();
|
|
38
38
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
39
39
|
const spaceAbove = rect.top;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const flip = spaceBelow < PANEL_MAX_H + 8 && spaceAbove > spaceBelow;
|
|
41
|
+
setPanelPos({
|
|
42
|
+
top: flip ? rect.top : rect.bottom,
|
|
43
|
+
left: rect.left,
|
|
44
|
+
width: rect.width,
|
|
45
|
+
flipUp: flip,
|
|
46
|
+
});
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
// Recalculate position on open, scroll, and resize
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!expanded) { setPanelPos(null); return; }
|
|
52
|
+
calcPosition();
|
|
53
|
+
window.addEventListener('scroll', calcPosition, true);
|
|
54
|
+
window.addEventListener('resize', calcPosition);
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener('scroll', calcPosition, true);
|
|
57
|
+
window.removeEventListener('resize', calcPosition);
|
|
58
|
+
};
|
|
59
|
+
}, [expanded, calcPosition]);
|
|
42
60
|
|
|
43
61
|
const collapse = useCallback(() => setExpanded(false), []);
|
|
44
62
|
|
|
@@ -48,9 +66,12 @@ export default function DirPicker({ dirPaths, value, onChange, rootLabel = 'Root
|
|
|
48
66
|
if (e.key === 'Escape') { e.preventDefault(); collapse(); }
|
|
49
67
|
};
|
|
50
68
|
const handleClick = (e: MouseEvent) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
const target = e.target as Node;
|
|
70
|
+
if (
|
|
71
|
+
btnRef.current?.contains(target) ||
|
|
72
|
+
panelRef.current?.contains(target)
|
|
73
|
+
) return;
|
|
74
|
+
collapse();
|
|
54
75
|
};
|
|
55
76
|
document.addEventListener('keydown', handleKey);
|
|
56
77
|
document.addEventListener('mousedown', handleClick);
|
|
@@ -87,9 +108,82 @@ export default function DirPicker({ dirPaths, value, onChange, rootLabel = 'Root
|
|
|
87
108
|
? value.split('/').map(s => stripEmoji(s)).join(' / ')
|
|
88
109
|
: '/ ' + rootLabel;
|
|
89
110
|
|
|
111
|
+
const panel = expanded && panelPos && createPortal(
|
|
112
|
+
<div
|
|
113
|
+
ref={panelRef}
|
|
114
|
+
className="fixed z-[9999] rounded-lg border border-[var(--amber)] bg-card shadow-lg overflow-hidden flex flex-col"
|
|
115
|
+
style={{
|
|
116
|
+
left: panelPos.left,
|
|
117
|
+
width: panelPos.width,
|
|
118
|
+
maxHeight: PANEL_MAX_H,
|
|
119
|
+
...(panelPos.flipUp
|
|
120
|
+
? { bottom: window.innerHeight - panelPos.top + 4 }
|
|
121
|
+
: { top: panelPos.top + 4 }),
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
{/* Breadcrumb */}
|
|
125
|
+
<div className="flex items-center gap-0.5 px-3 py-1.5 bg-muted/30 border-b border-border overflow-x-auto text-xs shrink-0">
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
onClick={() => navigateTo(-1)}
|
|
129
|
+
className={`shrink-0 px-1.5 py-0.5 rounded transition-colors ${
|
|
130
|
+
browsing === '' ? 'text-[var(--amber)] font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
131
|
+
}`}
|
|
132
|
+
>
|
|
133
|
+
/ {rootLabel}
|
|
134
|
+
</button>
|
|
135
|
+
{segments.map((seg, i) => (
|
|
136
|
+
<span key={i} className="flex items-center gap-0.5 shrink-0">
|
|
137
|
+
<ChevronRight size={10} className="text-muted-foreground/50" />
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
onClick={() => navigateTo(i)}
|
|
141
|
+
className={`px-1.5 py-0.5 rounded transition-colors truncate max-w-[100px] ${
|
|
142
|
+
i === segments.length - 1 ? 'text-[var(--amber)] font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
143
|
+
}`}
|
|
144
|
+
>
|
|
145
|
+
{seg}
|
|
146
|
+
</button>
|
|
147
|
+
</span>
|
|
148
|
+
))}
|
|
149
|
+
</div>
|
|
150
|
+
{/* Child directories */}
|
|
151
|
+
{children.length > 0 ? (
|
|
152
|
+
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
153
|
+
{children.map(childPath => {
|
|
154
|
+
const childName = childPath.split('/').pop() || childPath;
|
|
155
|
+
const hasChildren = dirPaths.some(p => p.startsWith(childPath + '/'));
|
|
156
|
+
return (
|
|
157
|
+
<button
|
|
158
|
+
key={childPath}
|
|
159
|
+
type="button"
|
|
160
|
+
onClick={() => drillInto(childPath)}
|
|
161
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-foreground hover:bg-muted/60 transition-colors"
|
|
162
|
+
>
|
|
163
|
+
<Folder size={12} className="shrink-0 text-[var(--amber)]" />
|
|
164
|
+
<span className="flex-1 text-left truncate">{childName}</span>
|
|
165
|
+
{hasChildren && <ChevronRight size={11} className="shrink-0 text-muted-foreground/40" />}
|
|
166
|
+
</button>
|
|
167
|
+
);
|
|
168
|
+
})}
|
|
169
|
+
</div>
|
|
170
|
+
) : (
|
|
171
|
+
<div className="px-3 py-2 text-xs text-muted-foreground/50 text-center">—</div>
|
|
172
|
+
)}
|
|
173
|
+
{/* Confirm & collapse */}
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
onClick={collapse}
|
|
177
|
+
className="w-full py-1.5 flex items-center justify-center gap-1 text-xs font-medium text-[var(--amber)] border-t border-border hover:bg-muted/30 transition-colors shrink-0"
|
|
178
|
+
>
|
|
179
|
+
<Check size={12} />
|
|
180
|
+
</button>
|
|
181
|
+
</div>,
|
|
182
|
+
document.body,
|
|
183
|
+
);
|
|
184
|
+
|
|
90
185
|
return (
|
|
91
|
-
|
|
92
|
-
{/* Trigger — always in document flow */}
|
|
186
|
+
<>
|
|
93
187
|
<button
|
|
94
188
|
ref={btnRef}
|
|
95
189
|
type="button"
|
|
@@ -107,71 +201,7 @@ export default function DirPicker({ dirPaths, value, onChange, rootLabel = 'Root
|
|
|
107
201
|
className={`shrink-0 text-muted-foreground transition-transform duration-150 ${expanded ? 'rotate-180' : ''}`}
|
|
108
202
|
/>
|
|
109
203
|
</button>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{expanded && (
|
|
113
|
-
<div className={`absolute z-50 left-0 right-0 rounded-lg border border-[var(--amber)] bg-card shadow-lg overflow-hidden max-h-[200px] flex flex-col ${
|
|
114
|
-
flipUp ? 'bottom-full mb-1' : 'top-full mt-1'
|
|
115
|
-
}`}>
|
|
116
|
-
{/* Breadcrumb */}
|
|
117
|
-
<div className="flex items-center gap-0.5 px-3 py-1.5 bg-muted/30 border-b border-border overflow-x-auto text-xs shrink-0">
|
|
118
|
-
<button
|
|
119
|
-
type="button"
|
|
120
|
-
onClick={() => navigateTo(-1)}
|
|
121
|
-
className={`shrink-0 px-1.5 py-0.5 rounded transition-colors ${
|
|
122
|
-
browsing === '' ? 'text-[var(--amber)] font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
123
|
-
}`}
|
|
124
|
-
>
|
|
125
|
-
/ {rootLabel}
|
|
126
|
-
</button>
|
|
127
|
-
{segments.map((seg, i) => (
|
|
128
|
-
<span key={i} className="flex items-center gap-0.5 shrink-0">
|
|
129
|
-
<ChevronRight size={10} className="text-muted-foreground/50" />
|
|
130
|
-
<button
|
|
131
|
-
type="button"
|
|
132
|
-
onClick={() => navigateTo(i)}
|
|
133
|
-
className={`px-1.5 py-0.5 rounded transition-colors truncate max-w-[100px] ${
|
|
134
|
-
i === segments.length - 1 ? 'text-[var(--amber)] font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
135
|
-
}`}
|
|
136
|
-
>
|
|
137
|
-
{seg}
|
|
138
|
-
</button>
|
|
139
|
-
</span>
|
|
140
|
-
))}
|
|
141
|
-
</div>
|
|
142
|
-
{/* Child directories */}
|
|
143
|
-
{children.length > 0 ? (
|
|
144
|
-
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
145
|
-
{children.map(childPath => {
|
|
146
|
-
const childName = childPath.split('/').pop() || childPath;
|
|
147
|
-
const hasChildren = dirPaths.some(p => p.startsWith(childPath + '/'));
|
|
148
|
-
return (
|
|
149
|
-
<button
|
|
150
|
-
key={childPath}
|
|
151
|
-
type="button"
|
|
152
|
-
onClick={() => drillInto(childPath)}
|
|
153
|
-
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-foreground hover:bg-muted/60 transition-colors"
|
|
154
|
-
>
|
|
155
|
-
<Folder size={12} className="shrink-0 text-[var(--amber)]" />
|
|
156
|
-
<span className="flex-1 text-left truncate">{childName}</span>
|
|
157
|
-
{hasChildren && <ChevronRight size={11} className="shrink-0 text-muted-foreground/40" />}
|
|
158
|
-
</button>
|
|
159
|
-
);
|
|
160
|
-
})}
|
|
161
|
-
</div>
|
|
162
|
-
) : (
|
|
163
|
-
<div className="px-3 py-2 text-xs text-muted-foreground/50 text-center">—</div>
|
|
164
|
-
)}
|
|
165
|
-
{/* Confirm & collapse */}
|
|
166
|
-
<button
|
|
167
|
-
type="button"
|
|
168
|
-
onClick={collapse}
|
|
169
|
-
className="w-full py-1.5 flex items-center justify-center gap-1 text-xs font-medium text-[var(--amber)] border-t border-border hover:bg-muted/30 transition-colors shrink-0"
|
|
170
|
-
>
|
|
171
|
-
<Check size={12} />
|
|
172
|
-
</button>
|
|
173
|
-
</div>
|
|
174
|
-
)}
|
|
175
|
-
</div>
|
|
204
|
+
{panel}
|
|
205
|
+
</>
|
|
176
206
|
);
|
|
177
207
|
}
|
|
@@ -73,11 +73,18 @@ export default function ImportModal({ open, onClose, defaultSpace, initialFiles,
|
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
if (!open) return;
|
|
75
75
|
const handler = (e: KeyboardEvent) => {
|
|
76
|
-
if (e.key
|
|
76
|
+
if (e.key !== 'Escape') return;
|
|
77
|
+
if (showDiscard) {
|
|
78
|
+
e.stopPropagation();
|
|
79
|
+
setShowDiscard(false);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
e.stopPropagation();
|
|
83
|
+
handleClose();
|
|
77
84
|
};
|
|
78
85
|
window.addEventListener('keydown', handler, true);
|
|
79
86
|
return () => window.removeEventListener('keydown', handler, true);
|
|
80
|
-
}, [open, handleClose]);
|
|
87
|
+
}, [open, handleClose, showDiscard]);
|
|
81
88
|
|
|
82
89
|
const checkConflicts = useCallback(async (fileNames: string[], space: string) => {
|
|
83
90
|
try {
|
|
@@ -197,15 +204,6 @@ export default function ImportModal({ open, onClose, defaultSpace, initialFiles,
|
|
|
197
204
|
|
|
198
205
|
return (
|
|
199
206
|
<>
|
|
200
|
-
<ConfirmDialog
|
|
201
|
-
open={showDiscard}
|
|
202
|
-
title={t.fileImport.discardTitle}
|
|
203
|
-
message={t.fileImport.discardMessage(im.files.length)}
|
|
204
|
-
confirmLabel={t.fileImport.discardConfirm}
|
|
205
|
-
cancelLabel={t.fileImport.discardCancel}
|
|
206
|
-
onConfirm={doClose}
|
|
207
|
-
onCancel={() => setShowDiscard(false)}
|
|
208
|
-
/>
|
|
209
207
|
<div
|
|
210
208
|
ref={overlayRef}
|
|
211
209
|
className={`fixed inset-0 z-50 modal-backdrop flex items-center justify-center p-4 transition-opacity duration-200 ${closing ? 'opacity-0' : 'opacity-100'}`}
|
|
@@ -507,6 +505,15 @@ export default function ImportModal({ open, onClose, defaultSpace, initialFiles,
|
|
|
507
505
|
</div>
|
|
508
506
|
</div>
|
|
509
507
|
</div>
|
|
508
|
+
<ConfirmDialog
|
|
509
|
+
open={showDiscard}
|
|
510
|
+
title={t.fileImport.discardTitle}
|
|
511
|
+
message={t.fileImport.discardMessage(im.files.length)}
|
|
512
|
+
confirmLabel={t.fileImport.discardConfirm}
|
|
513
|
+
cancelLabel={t.fileImport.discardCancel}
|
|
514
|
+
onConfirm={doClose}
|
|
515
|
+
onCancel={() => setShowDiscard(false)}
|
|
516
|
+
/>
|
|
510
517
|
</>
|
|
511
518
|
);
|
|
512
519
|
}
|
package/package.json
CHANGED