Enterprise file-browser composed from mp-splitter, mp-treeview, and mp-datatable Lit web components. Tree on the left, file list on the right, breadcrumb above, toolbar with search, view-mode toggle, and file operations. The live demo below is fully interactive: drag-drop files from your OS, rename inline, multi-select, cut/copy/paste, switch the language, simulate read-only permissions, or toggle on the lazy-load tree mode.
The component is intentionally event-driven: it renders the UX and emits intents, leaving every consumer to wire the backend, dialog style, and toast system of their own product. The recipes below cover the seven integration points a typical admin portal needs.
Every mutating action fires (operation) with a discriminated-union payload. The component is fire-and-forget — your handler runs the backend call, optionally marks the affected rows as pending, and reports failures through reportError() (which re-fires (errorReported) for your toast bus).
Drag-drop and the touch upload button both fire (uploadRequest) carrying pre-registered UploadEntry IDs. Drive your real upload (XHR / fetch / S3 SDK), then push percentage updates into the component via reportUploadProgress(id, pct, status?). The uploads signal on the component reflects the in-flight set so you can render a progress UI wherever you like (the live demo above renders it directly below the file-manager).
Most enterprise apps want their own modal styling instead of the browser dialogs. Wire a DialogResolver to your modal service (typically a thin wrapper around bs-modal). When unset, the component falls back to the native dialogs so basic usage still works.
When a paste or upload would overwrite a same-name entry in the target folder, the component calls conflictResolver once per conflict. Returning 'skip' filters the source out of the operation; 'rename' includes the consumer's chosen newName in the resulting (operation) / (uploadRequest) payload. Unset = silent replace (preserves the v1 behaviour).
Real file systems aren't materialisable up front. Bind a loadChildren async callback and seed [nodes] with only the top-level entries. The component marks every unloaded folder with a chevron, shows a spinner during the fetch, and merges the returned children back into its store — no consumer-side coordination beyond returning the right data.
Every visible string + ARIA label routes through FileManagerMessages. Pass a partial overrides object via [messages]; unset keys fall back to DEFAULT_FILE_MANAGER_MESSAGES (English). Locale switching works at runtime — the demo above uses three locales.
Beyond the global [allowOperations] input, every FileSystemNode can carry its own allowOperations partial. The toolbar / context-menu buttons gate against the worst case across the current selection (a multi-row delete is disabled if any selected row is locked).
Failures from your backend never need to reach into the component's UI directly. Call fm.reportError(message, nodeId?) from your handler and listen on (errorReported) at the template level — that's where you wire the toast / snackbar.
kind in onOperation + the upload pipeline.(errorReported); route to your snackbar.dialogResolver to bs-modal if you don't want native browser dialogs.conflictResolver if "silent overwrite" isn't your spec.allowOperations from your auth layer.[messages].[loadChildren] if folder cardinality is unbounded.