Chart builder: discoverable entry points — Build Chart door, dataset picker, no-datasets state

This commit is contained in:
2026-06-12 21:46:25 +03:00
parent dbb4522d78
commit ca70b3e491
22 changed files with 625 additions and 75 deletions
+6
View File
@@ -31,6 +31,12 @@ function snapshotOf(name: ModalName | null): string | null {
/** Open `name`, optionally with a sub-target (dataset id, source key). */
export function openModal(name: ModalName, arg?: string): void {
// TODO: setActiveModal fires the UrlStateSync subscribers before init() has
// loaded the target, so a navigable modal pushes one hash derived from the
// *previous* modal state (e.g. a stale `#datasets/dataset-N/build`) before the
// correct one — a spurious Back step. Swapping the two lines or suppressing
// sync until init completes would fix it; check init implementations don't
// assume the modal is already active.
useAppStore.getState().setActiveModal(name);
getModalConfig(name)?.init?.(arg);
stateSnapshot = snapshotOf(name);
+8 -1
View File
@@ -58,7 +58,7 @@ export function deriveViewState(): ViewState {
if (modal && getModalConfig(modal)?.isUrlNavigable) {
if (modal === 'chartBuilder') {
const datasetId = useChartBuilderStore.getState().datasetId;
return datasetId !== null ? { kind: 'dataset-build', datasetId } : { kind: 'datasets' };
return datasetId !== null ? { kind: 'dataset-build', datasetId } : { kind: 'build' };
}
if (modal === 'datasets') {
const ds = useDatasetStore.getState();
@@ -136,6 +136,13 @@ export function applyView(view: ViewState): void {
app.setActiveModal('datasets');
useDatasetStore.getState().startCreate();
return;
case 'build':
// The no-target builder door. init(null) picks the freshest dataset, so
// with any datasets present the derived view immediately self-corrects to
// `dataset-build`; only an empty dataset library stays on `#build`.
useChartBuilderStore.getState().init(null);
app.setActiveModal('chartBuilder');
return;
}
} finally {
applying = false;
+4 -2
View File
@@ -71,8 +71,10 @@ const MODAL_REGISTRY: Partial<Record<ModalName, ModalConfig>> = {
getState: () => ({ name: useExtractStore.getState().name }),
},
// Opened from a selected dataset's "Build Chart" action; `arg` is its id. Loads
// the dataset and pre-populates a smart default config (§06). Applies on Create
// Opened from a dataset's "Build Chart" action (`arg` is its id) or from the
// library / onboarding doors with no arg — init(null) then picks the most
// recently modified dataset (§06 → Opening). Loads the dataset and
// pre-populates a smart default config (§06). Applies on Create
// (a new snippet), so there is nothing transient to lose on close — no getState.
// Backdrop dismissal is off: the config is real in-progress work, and a stray
// click outside this large surface shouldn't throw it away (Escape/× still close).