@floegence/floe-webapp-core 0.46.4 → 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.
@@ -9,7 +9,7 @@ import { resolveSurfacePortalHost as ie, DIALOG_SURFACE_BOUNDARY_ATTR as O, isSu
9
9
  import { LOCAL_INTERACTION_SURFACE_ATTR as ce } from "./localInteractionSurface.js";
10
10
  import { createFloatingPresence as ue } from "./floatingPresence.js";
11
11
  import { useDialogPlacement as se } from "./DialogPlacementContext.js";
12
- var fe = /* @__PURE__ */ u("<span aria-hidden=true>"), ge = /* @__PURE__ */ u('<h2 class="text-sm font-semibold">'), me = /* @__PURE__ */ u('<p class="mt-0.5 text-xs text-muted-foreground">'), he = /* @__PURE__ */ u('<div class="flex items-start justify-between p-3 border-b border-border"><div>'), be = /* @__PURE__ */ u('<div class="flex items-center justify-end gap-2 p-3 border-t border-border">'), we = /* @__PURE__ */ u('<div><div></div><div class="relative z-[1] flex h-full w-full items-center justify-center"><div><div class="flex-1 overflow-auto overscroll-contain p-3">'), ve = /* @__PURE__ */ u("<div>");
12
+ var fe = /* @__PURE__ */ u("<span aria-hidden=true>"), ge = /* @__PURE__ */ u('<h2 class="text-sm font-semibold">'), me = /* @__PURE__ */ u('<p class="mt-0.5 text-xs text-muted-foreground">'), he = /* @__PURE__ */ u('<div class="flex items-start justify-between p-3 border-b border-border"><div>'), be = /* @__PURE__ */ u('<div class="flex items-center justify-end gap-2 p-3 border-t border-border">'), ve = /* @__PURE__ */ u('<div><div></div><div class="pointer-events-none relative z-[1] flex h-full w-full items-center justify-center"><div><div class="flex-1 overflow-auto overscroll-contain p-3">'), we = /* @__PURE__ */ u("<div>");
13
13
  const ye = {
14
14
  display: "none"
15
15
  };
@@ -35,16 +35,16 @@ function $e(e) {
35
35
  }
36
36
  function _e(e) {
37
37
  const g = se(), d = J(), F = () => `dialog-${d}-title`, D = () => `dialog-${d}-description`;
38
- let w;
39
- const [v, G] = I(null), [M, z] = I(0), [L, Z] = I(0), s = ue({
40
- open: () => e.open && !!v() && M() > 0,
38
+ let v;
39
+ const [w, G] = I(null), [M, z] = I(0), [L, Z] = I(0), s = ue({
40
+ open: () => e.open && !!w() && M() > 0,
41
41
  exitDurationMs: 120
42
- }), y = () => s.mounted() && !!v() && M() > 0, U = (t) => {
42
+ }), y = () => s.mounted() && !!w() && M() > 0, U = (t) => {
43
43
  G(t), xe(() => {
44
- te(v) === t && z((i) => i + 1);
44
+ te(w) === t && z((i) => i + 1);
45
45
  });
46
46
  }, x = H(() => y() ? ie({
47
- owner: v()
47
+ owner: w()
48
48
  }) : {
49
49
  host: null,
50
50
  boundaryHost: null,
@@ -71,8 +71,8 @@ function _e(e) {
71
71
  });
72
72
  }), oe({
73
73
  open: y,
74
- root: () => w,
75
- containsTarget: (t) => n() ? W(t) : typeof Node < "u" && t instanceof Node ? !!w?.contains(t) : !1,
74
+ root: () => v,
75
+ containsTarget: (t) => n() ? W(t) : typeof Node < "u" && t instanceof Node ? !!v?.contains(t) : !1,
76
76
  onClose: () => e.onOpenChange(!1),
77
77
  lockBodyScroll: () => !n(),
78
78
  trapFocus: !0,
@@ -95,7 +95,7 @@ function _e(e) {
95
95
  return V();
96
96
  },
97
97
  get children() {
98
- var t = we(), i = t.firstChild, m = i.nextSibling, o = m.firstChild, _ = o.firstChild;
98
+ var t = ve(), i = t.firstChild, m = i.nextSibling, o = m.firstChild, _ = o.firstChild;
99
99
  f(t, "data-floe-dialog-overlay-root", d), k(t, P({
100
100
  get "data-floe-dialog-mode"() {
101
101
  return n() ? "surface" : "global";
@@ -133,12 +133,12 @@ function _e(e) {
133
133
  },
134
134
  onClick: () => e.onOpenChange(!1)
135
135
  }), !1, !1);
136
- var h = w;
137
- return typeof h == "function" ? E(h, o) : w = o, f(o, "data-floe-dialog-panel", d), k(o, P(() => ({
136
+ var h = v;
137
+ return typeof h == "function" ? E(h, o) : v = o, f(o, "data-floe-dialog-panel", d), k(o, P(() => ({
138
138
  [O]: p()
139
139
  }), {
140
140
  get class() {
141
- return A(n() ? "flex max-h-[calc(100%-1rem)] w-[min(32rem,calc(100%-1rem))] max-w-[calc(100%-1rem)] flex-col" : "w-full max-w-md max-h-[85vh]", "bg-card text-card-foreground rounded-md shadow-lg", "border border-border", "floe-floating-presence floe-floating-dialog-panel", "flex flex-col", e.class);
141
+ return A(n() ? "flex max-h-[calc(100%-1rem)] w-[min(32rem,calc(100%-1rem))] max-w-[calc(100%-1rem)] flex-col" : "w-full max-w-md max-h-[85vh]", "bg-card text-card-foreground rounded-md shadow-lg", "border border-border", "floe-floating-presence floe-floating-dialog-panel", "pointer-events-auto flex flex-col", e.class);
142
142
  },
143
143
  get "data-floating-presence"() {
144
144
  return s.state();
@@ -247,7 +247,7 @@ function Te(e) {
247
247
  })];
248
248
  },
249
249
  get children() {
250
- return e.children ?? ve();
250
+ return e.children ?? we();
251
251
  }
252
252
  });
253
253
  }
@@ -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 DirectoryPicker and FileSavePicker.
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 {