@floegence/floe-webapp-core 0.30.0 → 0.32.0

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.
@@ -4,6 +4,11 @@ import type { FileItem, ViewMode, ContextMenuCallbacks, ContextMenuItem, FileLis
4
4
  export interface FileBrowserProps {
5
5
  /** File tree data */
6
6
  files: FileItem[];
7
+ /**
8
+ * Controlled current path.
9
+ * When provided, the browser path follows this value and user navigation is emitted via onPathChange.
10
+ */
11
+ path?: string;
7
12
  /** Initial path to display */
8
13
  initialPath?: string;
9
14
  /** Initial view mode */
@@ -12,6 +17,11 @@ export interface FileBrowserProps {
12
17
  initialListColumnRatios?: FileListColumnRatios;
13
18
  /** Callback when navigation occurs */
14
19
  onNavigate?: (path: string) => void;
20
+ /**
21
+ * Callback when the user changes path from inside FileBrowser.
22
+ * Use with `path` for controlled mode.
23
+ */
24
+ onPathChange?: (path: string, source: 'user' | 'programmatic') => void;
15
25
  /** Callback when selection changes */
16
26
  onSelect?: (items: FileItem[]) => void;
17
27
  /** Callback when a file is opened */
@@ -1,23 +1,26 @@
1
- import { createComponent as n, insert as l, addEventListener as P, use as L, memo as w, effect as T, className as W, setStyleProperty as B, template as m, delegateEvents as p } from "solid-js/web";
2
- import { onMount as G, onCleanup as H, createEffect as Q, Show as o } from "solid-js";
1
+ import { createComponent as n, insert as l, addEventListener as P, use as L, memo as w, effect as T, className as W, setStyleProperty as B, template as m, delegateEvents as G } from "solid-js/web";
2
+ import { onMount as H, onCleanup as Q, createEffect as q, Show as a } from "solid-js";
3
3
  import { cn as K } from "../../utils/cn.js";
4
- import { useLayout as q } from "../../context/LayoutContext.js";
5
- import { useFileBrowserDrag as J } from "../../context/FileBrowserDragContext.js";
6
- import { deferAfterPaint as U } from "../../utils/defer.js";
7
- import { FileBrowserProvider as X, useFileBrowser as Y } from "./FileBrowserContext.js";
8
- import { ResizeHandle as Z } from "../layout/ResizeHandle.js";
4
+ import { useLayout as J } from "../../context/LayoutContext.js";
5
+ import { useFileBrowserDrag as U } from "../../context/FileBrowserDragContext.js";
6
+ import { deferAfterPaint as X } from "../../utils/defer.js";
7
+ import { FileBrowserProvider as Y, useFileBrowser as Z } from "./FileBrowserContext.js";
8
+ import { ResizeHandle as p } from "../layout/ResizeHandle.js";
9
9
  import { DirectoryTree as ee } from "./DirectoryTree.js";
10
10
  import { FileListView as te } from "./FileListView.js";
11
11
  import { FileGridView as re } from "./FileGridView.js";
12
12
  import { FileBrowserToolbar as ie } from "./FileBrowserToolbar.js";
13
13
  import { FileContextMenu as ne } from "./FileContextMenu.js";
14
14
  import { DragPreview as le } from "./DragPreview.js";
15
- var oe = /* @__PURE__ */ m('<div class="border-b border-border">'), ae = /* @__PURE__ */ m('<button type=button class="flex items-center justify-center w-5 h-5 rounded cursor-pointer hover:bg-sidebar-accent/80 transition-colors"aria-label="Close sidebar"><svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round class="w-3.5 h-3.5"><path d="M18 6 6 18"></path><path d="m6 6 12 12">'), se = /* @__PURE__ */ m('<div class="absolute inset-0 bg-background/60 backdrop-blur-sm z-[9]">'), de = /* @__PURE__ */ m('<div><div class="flex flex-1 min-h-0 relative"><aside><div class="h-full flex flex-col"><div class="flex items-center justify-between px-3 py-2 border-b border-sidebar-border"><span class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">Explorer</span></div><div class="flex-1 min-h-0 overflow-auto py-1"></div></div></aside><div class="flex-1 min-w-0 flex flex-col"><div class="flex-1 min-h-0"></div><div class="flex items-center justify-between px-3 py-1 border-t border-border text-[10px] text-muted-foreground"><span> items</span><span class="truncate max-w-[200px]">');
15
+ var ae = /* @__PURE__ */ m('<div class="border-b border-border">'), oe = /* @__PURE__ */ m('<button type=button class="flex items-center justify-center w-5 h-5 rounded cursor-pointer hover:bg-sidebar-accent/80 transition-colors"aria-label="Close sidebar"><svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round class="w-3.5 h-3.5"><path d="M18 6 6 18"></path><path d="m6 6 12 12">'), se = /* @__PURE__ */ m('<div class="absolute inset-0 bg-background/60 backdrop-blur-sm z-[9]">'), de = /* @__PURE__ */ m('<div><div class="flex flex-1 min-h-0 relative"><aside><div class="h-full flex flex-col"><div class="flex items-center justify-between px-3 py-2 border-b border-sidebar-border"><span class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">Explorer</span></div><div class="flex-1 min-h-0 overflow-auto py-1"></div></div></aside><div class="flex-1 min-w-0 flex flex-col"><div class="flex-1 min-h-0"></div><div class="flex items-center justify-between px-3 py-1 border-t border-border text-[10px] text-muted-foreground"><span> items</span><span class="truncate max-w-[200px]">');
16
16
  function De(e) {
17
- return n(X, {
17
+ return n(Y, {
18
18
  get files() {
19
19
  return e.files;
20
20
  },
21
+ get path() {
22
+ return e.path;
23
+ },
21
24
  get initialPath() {
22
25
  return e.initialPath;
23
26
  },
@@ -42,6 +45,9 @@ function De(e) {
42
45
  get onNavigate() {
43
46
  return e.onNavigate;
44
47
  },
48
+ get onPathChange() {
49
+ return e.onPathChange;
50
+ },
45
51
  get onSelect() {
46
52
  return e.onSelect;
47
53
  },
@@ -88,9 +94,9 @@ function De(e) {
88
94
  });
89
95
  }
90
96
  function ue(e) {
91
- const r = Y(), O = q(), d = J(), a = () => O.isMobile(), C = () => r.sidebarWidth(), E = () => e.sidebarResizable ?? !0, s = () => (e.enableDragDrop ?? !0) && !!d, u = () => e.instanceId ?? `filebrowser-${Math.random().toString(36).slice(2, 9)}`;
97
+ const r = Z(), O = J(), d = U(), o = () => O.isMobile(), C = () => r.sidebarWidth(), E = () => e.sidebarResizable ?? !0, s = () => (e.enableDragDrop ?? !0) && !!d, u = () => e.instanceId ?? `filebrowser-${Math.random().toString(36).slice(2, 9)}`;
92
98
  let M, I = null, S = null;
93
- G(() => {
99
+ H(() => {
94
100
  if (!d || !s()) return;
95
101
  const i = {
96
102
  instanceId: u(),
@@ -103,12 +109,12 @@ function ue(e) {
103
109
  optimisticInsert: r.optimisticInsert
104
110
  };
105
111
  d.registerInstance(i);
106
- }), H(() => {
112
+ }), Q(() => {
107
113
  d && s() && d.unregisterInstance(u());
108
114
  });
109
115
  let y = !1, f = !1;
110
- Q(() => {
111
- const i = a();
116
+ q(() => {
117
+ const i = o();
112
118
  if (!y) {
113
119
  y = !0, f = i, i && e.hideSidebarOnMobile !== !1 && !r.sidebarCollapsed() && r.toggleSidebar();
114
120
  return;
@@ -116,26 +122,26 @@ function ue(e) {
116
122
  !f && i && e.hideSidebarOnMobile !== !1 && !r.sidebarCollapsed() && r.toggleSidebar(), f = i;
117
123
  });
118
124
  const j = (i) => {
119
- (i.metaKey || i.ctrlKey) && i.key.toLowerCase() === "f" && (i.preventDefault(), r.setFilterActive(!0), U(() => M?.focus()));
120
- }, D = () => !r.sidebarCollapsed() || !a();
125
+ (i.metaKey || i.ctrlKey) && i.key.toLowerCase() === "f" && (i.preventDefault(), r.setFilterActive(!0), X(() => M?.focus()));
126
+ }, D = () => !r.sidebarCollapsed() || !o();
121
127
  return (() => {
122
128
  var i = de(), h = i.firstChild, c = h.firstChild, k = c.firstChild, v = k.firstChild;
123
129
  v.firstChild;
124
130
  var $ = v.nextSibling, x = c.nextSibling, b = x.firstChild, V = b.nextSibling, g = V.firstChild, A = g.firstChild, N = g.nextSibling;
125
- return i.$$keydown = j, l(i, n(o, {
131
+ return i.$$keydown = j, l(i, n(a, {
126
132
  get when() {
127
133
  return e.header;
128
134
  },
129
135
  get children() {
130
- var t = oe();
136
+ var t = ae();
131
137
  return l(t, () => e.header), t;
132
138
  }
133
- }), h), l(v, n(o, {
139
+ }), h), l(v, n(a, {
134
140
  get when() {
135
- return a();
141
+ return o();
136
142
  },
137
143
  get children() {
138
- var t = ae();
144
+ var t = oe();
139
145
  return P(t, "click", r.toggleSidebar, !0), t;
140
146
  }
141
147
  }), null), L((t) => {
@@ -147,21 +153,21 @@ function ue(e) {
147
153
  get enableDragDrop() {
148
154
  return s();
149
155
  }
150
- })), l(c, n(o, {
156
+ })), l(c, n(a, {
151
157
  get when() {
152
- return w(() => !!(E() && D()))() && !a();
158
+ return w(() => !!(E() && D()))() && !o();
153
159
  },
154
160
  get children() {
155
- return n(Z, {
161
+ return n(p, {
156
162
  direction: "horizontal",
157
163
  onResize: (t) => {
158
164
  r.setSidebarWidth(r.sidebarWidth() + t);
159
165
  }
160
166
  });
161
167
  }
162
- }), null), l(h, n(o, {
168
+ }), null), l(h, n(a, {
163
169
  get when() {
164
- return w(() => !!a())() && !r.sidebarCollapsed();
170
+ return w(() => !!o())() && !r.sidebarCollapsed();
165
171
  },
166
172
  get children() {
167
173
  var t = se();
@@ -171,7 +177,7 @@ function ue(e) {
171
177
  filterInputRef: (t) => M = t
172
178
  }), b), L((t) => {
173
179
  I = t;
174
- }, b), l(b, n(o, {
180
+ }, b), l(b, n(a, {
175
181
  get when() {
176
182
  return r.viewMode() === "list";
177
183
  },
@@ -195,14 +201,14 @@ function ue(e) {
195
201
  }
196
202
  });
197
203
  }
198
- })), l(g, () => r.currentFiles().length, A), l(g, n(o, {
204
+ })), l(g, () => r.currentFiles().length, A), l(g, n(a, {
199
205
  get when() {
200
206
  return r.filterQueryApplied().trim();
201
207
  },
202
208
  get children() {
203
209
  return [" ", "(filtered)"];
204
210
  }
205
- }), null), l(g, n(o, {
211
+ }), null), l(g, n(a, {
206
212
  get when() {
207
213
  return r.selectedItems().size > 0;
208
214
  },
@@ -222,7 +228,7 @@ function ue(e) {
222
228
  get hideItems() {
223
229
  return e.hideContextMenuItems;
224
230
  }
225
- }), null), l(i, n(o, {
231
+ }), null), l(i, n(a, {
226
232
  get when() {
227
233
  return s();
228
234
  },
@@ -235,7 +241,7 @@ function ue(e) {
235
241
  "transition-all duration-200 ease-out",
236
242
  "overflow-hidden",
237
243
  // Mobile overlay
238
- a() && !r.sidebarCollapsed() && "absolute inset-y-0 left-0 z-10 shadow-lg"
244
+ o() && !r.sidebarCollapsed() && "absolute inset-y-0 left-0 z-10 shadow-lg"
239
245
  ), z = D() ? `${C()}px` : "0px", F = `${C()}px`;
240
246
  return _ !== t.e && W(i, t.e = _), R !== t.t && W(c, t.t = R), z !== t.a && B(c, "width", t.a = z), F !== t.o && B(k, "width", t.o = F), t;
241
247
  }, {
@@ -246,7 +252,7 @@ function ue(e) {
246
252
  }), i;
247
253
  })();
248
254
  }
249
- p(["keydown", "click"]);
255
+ G(["keydown", "click"]);
250
256
  export {
251
257
  De as FileBrowser
252
258
  };
@@ -3,6 +3,10 @@ import type { FileItem, ViewMode, FileListColumnRatios, FileBrowserContextValue
3
3
  export interface FileBrowserProviderProps {
4
4
  children: JSX.Element;
5
5
  files: FileItem[];
6
+ /**
7
+ * Controlled current path. When provided, FileBrowser follows this value.
8
+ */
9
+ path?: string;
6
10
  initialPath?: string;
7
11
  initialViewMode?: ViewMode;
8
12
  /** Initial list view column ratios (for resizable columns) */
@@ -23,6 +27,7 @@ export interface FileBrowserProviderProps {
23
27
  /** Label for the root/home directory in breadcrumb (default: 'Root') */
24
28
  homeLabel?: string;
25
29
  onNavigate?: (path: string) => void;
30
+ onPathChange?: (path: string, source: 'user' | 'programmatic') => void;
26
31
  onSelect?: (items: FileItem[]) => void;
27
32
  onOpen?: (item: FileItem) => void;
28
33
  }