Core API
bash
npm install @document-explorer/coreFramework-free, zero runtime dependencies, no DOM access. Every renderer is a thin subscriber to what lives here — and it is perfectly usable on its own.
createExplorerStore(config)
ts
import { createExplorerStore } from '@document-explorer/core';
const store = createExplorerStore({
data: apiRows,
mapper,
rootLabel: 'Home',
initialCwd: null,
view: 'list',
sort: { key: 'name', direction: 'asc', foldersFirst: true },
selection: { enabled: true, mode: 'multiple' },
search: { scope: 'global', searchableFields: ['metadata.owner'] },
events: { onDocumentOpen: (item) => open(item.viewUrl) },
});Reading
ts
store.getState(); // ExplorerState — cwd, query, sort, view, selection, status
store.getSnapshot(); // ExplorerSnapshot — the render-ready view
store.subscribe(fn); // returns an unsubscribe functiongetSnapshot() memoizes the filter → sort pipeline and is referentially stable until something actually changes, so it drops straight into useSyncExternalStore or an Angular signal.
ts
interface ExplorerSnapshot {
state: ExplorerState;
index: DocumentIndex;
items: DocumentItem[]; // filtered and sorted rows
breadcrumbs: Breadcrumb[];
selectedItems: DocumentItem[];
warnings: NormalizeWarning[];
isEmpty: boolean;
isSearching: boolean;
totalCount: number;
}Actions
ts
store.actions.setData(rows, mapper);
store.actions.openFolder(id); // null for the root
store.actions.navigateUp();
store.actions.open(item); // folder → navigate, file → onDocumentOpen
store.actions.setQuery('spec');
store.actions.setSort('size'); // a key toggles direction; or pass a full SortState
store.actions.setView('grid');
store.actions.toggleSelect(item);
store.actions.selectOnly(item);
store.actions.selectRange(item);
store.actions.selectAll();
store.actions.clearSelection();
store.actions.setSelectionConfig({ enabled: false });
store.actions.setStatus('loading');Standalone functions
Mapping and normalizing
ts
resolveMapper(mapper); // (raw, index) => DocumentItem
normalize(data, { mapper }); // { index, items, warnings }Tree
ts
getChildren(index, parentId);
getDescendants(index, parentId);
getAncestors(index, id);
getBreadcrumbs(index, id, rootLabel);
getParentId(index, id);
isWithin(index, candidateId, folderId);Search, sort, selection
ts
searchDocuments(items, query, { searchableFields, matchMode });
matchesQuery(item, query, options); // for highlighting
sortDocuments(items, { key, direction, foldersFirst });
toggleSort(currentSort, key);
compareBy(a, b, key);
toggleSelection(state, item, config);
selectRange(state, item, visible, config);
selectAll(state, visible, config);
isSelectable(item, config);Keyboard
ts
const navigator = createKeyboardNavigator();
const intent = navigator.resolve(event, { items, activeIndex, columns }, Date.now());
// { type: 'move' | 'open' | 'toggleSelect' | 'selectAll' | 'navigateUp' | 'clearSelection', … }
isHandledKey(event); // whether to preventDefaultevent need only carry key, shiftKey, ctrlKey, metaKey and altKey — core never touches the DOM.
File types and formatting
ts
registerFileType({ extensions: ['rs'], label: 'Rust', iconKey: 'code' });
resolveFileType(item); // { label, iconKey, color, previewable }
formatBytes(2450000, 'en-US'); // '2.3 MB'
formatDate(item.updatedAt); // '2 hours ago' / 'Aug 20'Paths
ts
getValueAtPath(item, 'metadata.owner');The single dot-path accessor, reused by the mapper, columns, search and sort — so a value you can display is a value you can sort and search.