@floegence/floe-webapp-core 0.46.5 → 0.46.6
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/dist/components/icons/index.d.ts +1 -0
- package/dist/components/icons/index.js +410 -399
- package/dist/components/ui/FileOpenPicker.d.ts +21 -0
- package/dist/components/ui/FileOpenPicker.js +244 -0
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/picker/PickerBase.d.ts +2 -2
- package/dist/components/ui/picker/PickerBase.js +176 -169
- package/dist/full.js +769 -764
- package/dist/icons.js +177 -176
- package/dist/styles.css +1 -1
- package/dist/ui.js +95 -91
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FileItem } from '../file-browser/types';
|
|
2
|
+
import { type BasePickerProps } from './picker/PickerBase';
|
|
3
|
+
export type FileOpenPickerSelectionMode = 'single' | 'multiple';
|
|
4
|
+
export interface FileOpenPickerProps extends BasePickerProps {
|
|
5
|
+
/** Select one file or an ordered set of files. */
|
|
6
|
+
selectionMode?: FileOpenPickerSelectionMode;
|
|
7
|
+
/** Selected file paths restored whenever the picker opens. */
|
|
8
|
+
initialSelectedPaths?: readonly string[];
|
|
9
|
+
/** Maximum number of files accepted in multiple mode. */
|
|
10
|
+
maxSelections?: number;
|
|
11
|
+
/** Filters files without affecting directory navigation. */
|
|
12
|
+
fileFilter?: (item: FileItem) => boolean;
|
|
13
|
+
/** Called with selected paths in user selection order. */
|
|
14
|
+
onSelect: (paths: string[]) => void;
|
|
15
|
+
/** Empty-state copy for the current directory. */
|
|
16
|
+
emptyText?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function normalizeFileOpenSelection(paths: readonly string[] | undefined, maxSelections?: number): string[];
|
|
19
|
+
export declare function updateFileOpenSelection(current: readonly string[], path: string, mode: FileOpenPickerSelectionMode, maxSelections?: number): string[];
|
|
20
|
+
/** Modal file picker with ordered single or multiple selection. */
|
|
21
|
+
export declare function FileOpenPicker(props: FileOpenPickerProps): import("solid-js").JSX.Element;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { createComponent as o, insert as a, use as B, effect as D, setAttribute as w, className as L, memo as R, template as b, delegateEvents as A } from "solid-js/web";
|
|
2
|
+
import { createSignal as K, createMemo as j, Show as P, For as G } from "solid-js";
|
|
3
|
+
import { cn as O } from "../../utils/cn.js";
|
|
4
|
+
import { deferNonBlocking as H } from "../../utils/defer.js";
|
|
5
|
+
import { Check as U } from "../icons/index.js";
|
|
6
|
+
import { FileItemIcon as V } from "../file-browser/FileIcons.js";
|
|
7
|
+
import { Button as E } from "./Button.js";
|
|
8
|
+
import { Dialog as Y } from "./Dialog.js";
|
|
9
|
+
import { usePickerTree as q, normalizePath as x, PathInputBar as J, PickerBreadcrumb as Q, PickerFolderTree as W, NewFolderSection as X, getParentPath as Z } from "./picker/PickerBase.js";
|
|
10
|
+
var ee = /* @__PURE__ */ b('<div class="flex flex-col gap-2 -mt-1"><div class="flex min-h-[260px] flex-col overflow-hidden rounded border border-border sm:h-[300px] sm:flex-row"><div class="min-h-[140px] min-w-0 flex-1 overflow-y-auto"role=listbox>'), te = /* @__PURE__ */ b('<div class="flex items-center w-full gap-2"><span class="flex-1 text-[11px] text-muted-foreground truncate">'), ne = /* @__PURE__ */ b('<div class="flex h-full items-center justify-center px-4 text-center text-xs text-muted-foreground">'), re = /* @__PURE__ */ b('<span class="ml-auto flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-[10px] text-primary-foreground">'), le = /* @__PURE__ */ b("<button type=button role=option><span class=truncate>");
|
|
11
|
+
function ie(e, u) {
|
|
12
|
+
const d = S(u), f = /* @__PURE__ */ new Set(), m = [];
|
|
13
|
+
for (const n of e ?? []) {
|
|
14
|
+
const h = x(n);
|
|
15
|
+
if (!(h === "/" || f.has(h)) && (f.add(h), m.push(h), m.length >= d))
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
return m;
|
|
19
|
+
}
|
|
20
|
+
function oe(e, u, d, f) {
|
|
21
|
+
const m = x(u);
|
|
22
|
+
if (m === "/") return [...e];
|
|
23
|
+
if (d === "single") return [m];
|
|
24
|
+
const n = e.indexOf(m);
|
|
25
|
+
return n >= 0 ? e.filter((h, v) => v !== n) : e.length >= S(f) ? [...e] : [...e, m];
|
|
26
|
+
}
|
|
27
|
+
function S(e) {
|
|
28
|
+
return e == null || !Number.isFinite(e) ? Number.POSITIVE_INFINITY : Math.max(1, Math.floor(e));
|
|
29
|
+
}
|
|
30
|
+
function xe(e) {
|
|
31
|
+
const u = () => e.selectionMode ?? "single", [d, f] = K([]), n = q({
|
|
32
|
+
initialPath: () => {
|
|
33
|
+
if (e.initialPath) return e.initialPath;
|
|
34
|
+
const t = e.initialSelectedPaths?.[0];
|
|
35
|
+
return t ? Z(t) : "/";
|
|
36
|
+
},
|
|
37
|
+
open: () => e.open,
|
|
38
|
+
files: () => e.files,
|
|
39
|
+
// eslint-disable-next-line solid/reactivity -- filter is a static callback
|
|
40
|
+
filter: e.filter ? (t) => e.filter(t) : void 0,
|
|
41
|
+
// eslint-disable-next-line solid/reactivity -- onExpand is a static callback
|
|
42
|
+
onExpand: e.onExpand,
|
|
43
|
+
// eslint-disable-next-line solid/reactivity -- ensurePath is a static callback
|
|
44
|
+
ensurePath: e.ensurePath,
|
|
45
|
+
homeLabel: () => e.homeLabel,
|
|
46
|
+
homePath: () => e.homePath,
|
|
47
|
+
onReset: () => {
|
|
48
|
+
f(ie(e.initialSelectedPaths, u() === "single" ? 1 : e.maxSelections));
|
|
49
|
+
}
|
|
50
|
+
}), h = j(() => {
|
|
51
|
+
const t = x(n.selectedPath());
|
|
52
|
+
return (t === "/" ? e.files : n.folderIndex().get(t)?.children ?? []).filter((r) => r.type === "file" && (!e.fileFilter || e.fileFilter(r)));
|
|
53
|
+
}), v = (t) => d().indexOf(x(t)), T = () => u() === "multiple" && d().length >= S(e.maxSelections), z = (t) => {
|
|
54
|
+
f((i) => oe(i, t.path, u(), e.maxSelections));
|
|
55
|
+
}, y = (t = d()) => {
|
|
56
|
+
if (t.length === 0 || n.pathPending()) return;
|
|
57
|
+
const i = e.onSelect;
|
|
58
|
+
e.onOpenChange(!1), H(() => i([...t]));
|
|
59
|
+
}, M = (t, i) => {
|
|
60
|
+
let r;
|
|
61
|
+
t.key === "ArrowDown" && (r = Math.min(i + 1, h().length - 1)), t.key === "ArrowUp" && (r = Math.max(i - 1, 0)), t.key === "Home" && (r = 0), t.key === "End" && (r = h().length - 1), !(r == null || r < 0) && (t.preventDefault(), k[r]?.focus());
|
|
62
|
+
}, k = [];
|
|
63
|
+
return o(Y, {
|
|
64
|
+
get open() {
|
|
65
|
+
return e.open;
|
|
66
|
+
},
|
|
67
|
+
get onOpenChange() {
|
|
68
|
+
return e.onOpenChange;
|
|
69
|
+
},
|
|
70
|
+
get title() {
|
|
71
|
+
return e.title ?? "Select File";
|
|
72
|
+
},
|
|
73
|
+
get class() {
|
|
74
|
+
return O("max-w-2xl", e.class);
|
|
75
|
+
},
|
|
76
|
+
get footer() {
|
|
77
|
+
return (() => {
|
|
78
|
+
var t = te(), i = t.firstChild;
|
|
79
|
+
return a(i, (() => {
|
|
80
|
+
var r = R(() => d().length === 0);
|
|
81
|
+
return () => r() ? "No files selected" : `${d().length} selected`;
|
|
82
|
+
})()), a(t, o(E, {
|
|
83
|
+
variant: "ghost",
|
|
84
|
+
size: "sm",
|
|
85
|
+
onClick: () => e.onOpenChange(!1),
|
|
86
|
+
get children() {
|
|
87
|
+
return e.cancelText ?? "Cancel";
|
|
88
|
+
}
|
|
89
|
+
}), null), a(t, o(E, {
|
|
90
|
+
variant: "primary",
|
|
91
|
+
size: "sm",
|
|
92
|
+
onClick: () => y(),
|
|
93
|
+
get disabled() {
|
|
94
|
+
return d().length === 0 || n.pathPending();
|
|
95
|
+
},
|
|
96
|
+
get children() {
|
|
97
|
+
return e.confirmText ?? "Select";
|
|
98
|
+
}
|
|
99
|
+
}), null), t;
|
|
100
|
+
})();
|
|
101
|
+
},
|
|
102
|
+
get children() {
|
|
103
|
+
var t = ee(), i = t.firstChild, r = i.firstChild;
|
|
104
|
+
return a(t, o(J, {
|
|
105
|
+
get value() {
|
|
106
|
+
return n.pathInput;
|
|
107
|
+
},
|
|
108
|
+
onInput: (s) => {
|
|
109
|
+
n.setPathInput(s), n.setPathInputError("");
|
|
110
|
+
},
|
|
111
|
+
get pending() {
|
|
112
|
+
return n.pathPending;
|
|
113
|
+
},
|
|
114
|
+
get error() {
|
|
115
|
+
return n.pathInputError;
|
|
116
|
+
},
|
|
117
|
+
get onGo() {
|
|
118
|
+
return n.handlePathInputGo;
|
|
119
|
+
},
|
|
120
|
+
get onKeyDown() {
|
|
121
|
+
return n.handlePathInputKeyDown;
|
|
122
|
+
}
|
|
123
|
+
}), i), a(t, o(Q, {
|
|
124
|
+
get segments() {
|
|
125
|
+
return n.breadcrumbSegments;
|
|
126
|
+
},
|
|
127
|
+
get onClick() {
|
|
128
|
+
return n.handleBreadcrumbClick;
|
|
129
|
+
}
|
|
130
|
+
}), i), a(i, o(W, {
|
|
131
|
+
get rootFolders() {
|
|
132
|
+
return n.rootFolders;
|
|
133
|
+
},
|
|
134
|
+
get selectedPath() {
|
|
135
|
+
return n.selectedPath;
|
|
136
|
+
},
|
|
137
|
+
get expandedPaths() {
|
|
138
|
+
return n.expandedPaths;
|
|
139
|
+
},
|
|
140
|
+
get revealNonce() {
|
|
141
|
+
return n.revealNonce;
|
|
142
|
+
},
|
|
143
|
+
get onToggle() {
|
|
144
|
+
return n.toggleExpand;
|
|
145
|
+
},
|
|
146
|
+
get onSelect() {
|
|
147
|
+
return n.handleSelectFolder;
|
|
148
|
+
},
|
|
149
|
+
get onSelectRoot() {
|
|
150
|
+
return n.handleSelectRoot;
|
|
151
|
+
},
|
|
152
|
+
get isSelectable() {
|
|
153
|
+
return n.isSelectable;
|
|
154
|
+
},
|
|
155
|
+
get homeLabel() {
|
|
156
|
+
return n.homeLabel;
|
|
157
|
+
},
|
|
158
|
+
class: "min-h-[120px] min-w-0 border-0 border-b border-border rounded-none sm:h-full sm:w-1/2 sm:border-b-0 sm:border-r"
|
|
159
|
+
}), r), a(r, o(P, {
|
|
160
|
+
get when() {
|
|
161
|
+
return h().length > 0;
|
|
162
|
+
},
|
|
163
|
+
get fallback() {
|
|
164
|
+
return (() => {
|
|
165
|
+
var s = ne();
|
|
166
|
+
return a(s, () => e.emptyText ?? "No matching files in this directory"), s;
|
|
167
|
+
})();
|
|
168
|
+
},
|
|
169
|
+
get children() {
|
|
170
|
+
return o(G, {
|
|
171
|
+
get each() {
|
|
172
|
+
return h();
|
|
173
|
+
},
|
|
174
|
+
children: (s, F) => {
|
|
175
|
+
const g = () => v(s.path), I = () => T() && g() < 0;
|
|
176
|
+
return (() => {
|
|
177
|
+
var c = le(), C = c.firstChild;
|
|
178
|
+
return c.$$keydown = (l) => M(l, F()), c.$$dblclick = () => {
|
|
179
|
+
u() === "single" && y([x(s.path)]);
|
|
180
|
+
}, c.$$click = () => z(s), B((l) => {
|
|
181
|
+
k[F()] = l;
|
|
182
|
+
}, c), a(c, o(V, {
|
|
183
|
+
item: s,
|
|
184
|
+
class: "w-4 h-4 flex-shrink-0"
|
|
185
|
+
}), C), a(C, () => s.name), a(c, o(P, {
|
|
186
|
+
get when() {
|
|
187
|
+
return g() >= 0;
|
|
188
|
+
},
|
|
189
|
+
get children() {
|
|
190
|
+
var l = re();
|
|
191
|
+
return a(l, o(P, {
|
|
192
|
+
get when() {
|
|
193
|
+
return u() === "multiple";
|
|
194
|
+
},
|
|
195
|
+
get fallback() {
|
|
196
|
+
return o(U, {
|
|
197
|
+
class: "h-3 w-3"
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
get children() {
|
|
201
|
+
return g() + 1;
|
|
202
|
+
}
|
|
203
|
+
})), l;
|
|
204
|
+
}
|
|
205
|
+
}), null), D((l) => {
|
|
206
|
+
var $ = g() >= 0, p = I(), _ = I() ? "Selection limit reached" : s.name, N = O("flex min-h-9 w-full items-center gap-2 px-2 py-1.5 text-left text-xs", "transition-colors duration-100 hover:bg-accent/60", "focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-45", g() >= 0 && "bg-accent text-accent-foreground font-medium");
|
|
207
|
+
return $ !== l.e && w(c, "aria-selected", l.e = $), p !== l.t && (c.disabled = l.t = p), _ !== l.a && w(c, "title", l.a = _), N !== l.o && L(c, l.o = N), l;
|
|
208
|
+
}, {
|
|
209
|
+
e: void 0,
|
|
210
|
+
t: void 0,
|
|
211
|
+
a: void 0,
|
|
212
|
+
o: void 0
|
|
213
|
+
}), c;
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
})), a(t, o(P, {
|
|
219
|
+
get when() {
|
|
220
|
+
return e.onCreateFolder;
|
|
221
|
+
},
|
|
222
|
+
get children() {
|
|
223
|
+
return o(X, {
|
|
224
|
+
get parentPath() {
|
|
225
|
+
return n.selectedPath;
|
|
226
|
+
},
|
|
227
|
+
get onCreateFolder() {
|
|
228
|
+
return e.onCreateFolder;
|
|
229
|
+
},
|
|
230
|
+
get toDisplayPath() {
|
|
231
|
+
return n.toDisplayPath;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}), null), D(() => w(r, "aria-multiselectable", u() === "multiple" ? "true" : void 0)), t;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
A(["click", "dblclick", "keydown"]);
|
|
240
|
+
export {
|
|
241
|
+
xe as FileOpenPicker,
|
|
242
|
+
ie as normalizeFileOpenSelection,
|
|
243
|
+
oe as updateFileOpenSelection
|
|
244
|
+
};
|
|
@@ -21,6 +21,7 @@ export { Tabs, TabPanel, type TabsProps, type TabPanelProps, type TabItem, type
|
|
|
21
21
|
export { DirectoryPicker, type DirectoryPickerProps } from './DirectoryPicker';
|
|
22
22
|
export { DirectoryInput, type DirectoryInputProps, type DirectoryInputSize, } from './DirectoryInput';
|
|
23
23
|
export { FileSavePicker, type FileSavePickerProps } from './FileSavePicker';
|
|
24
|
+
export { FileOpenPicker, normalizeFileOpenSelection, updateFileOpenSelection, type FileOpenPickerProps, type FileOpenPickerSelectionMode, } from './FileOpenPicker';
|
|
24
25
|
export { type BasePickerProps, type PickerEnsurePath, type PickerEnsurePathOptions, type PickerPathResolveResult, type PickerPathResolveStatus, type PickerPathNavigateReason, } from './picker/PickerBase';
|
|
25
26
|
export { QuoteBlock, type QuoteBlockProps } from './QuoteBlock';
|
|
26
27
|
export { HighlightBlock, InfoBlock, WarningBlock, SuccessBlock, ErrorBlock, NoteBlock, TipBlock, type HighlightBlockProps, type HighlightVariant, } from './HighlightBlock';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared internals for DirectoryPicker and FileSavePicker.
|
|
2
|
+
* Shared internals for DirectoryPicker, FileOpenPicker, and FileSavePicker.
|
|
3
3
|
* NOT exported from the package entry — these are implementation details.
|
|
4
4
|
*/
|
|
5
5
|
import { type Accessor, type JSX } from 'solid-js';
|
|
6
6
|
import type { FileItem } from '../../file-browser/types';
|
|
7
7
|
/**
|
|
8
|
-
* Base props shared by
|
|
8
|
+
* Base props shared by the directory and file pickers.
|
|
9
9
|
* Provides consistent API for file tree navigation.
|
|
10
10
|
*/
|
|
11
11
|
export interface BasePickerProps {
|