Skip to content

Core API

bash
npm install @document-explorer/core

Framework-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 function

getSnapshot() 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 preventDefault

event 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.

Released under the MIT License.