swarm-ui: rewire a notification flow by drag or keyboard
The flow editor can now change a flow's shape on the canvas, not only through the inspector's fields: - drag a box onto another to move it and its branch there; a drop the save would refuse shows the target dashed red and gives the reason; - a palette of node kinds adds a node below the picked box on click, or below any box it is dragged onto; it offers what the controller lists; - arrow keys walk the tree, M picks a box up and Enter drops it under the focused one, Alt+Up/Down reorder a node among its siblings, Delete removes a branch; focus follows the edit; - each box with siblings says when it runs, and the inspector can move it earlier or later, since the router runs children in edge order; - request_reviewer is a second leaf kind beside todo, with a reviewer field; - leaving with unsaved edits asks first, also for in-app links and the back button (a router hook runs navigation past a page's guard).
This commit is contained in:
parent
e01770c056
commit
68e289fff5
2 changed files with 1072 additions and 133 deletions
20
frontend/packages/swarm-ui/src/shell/navGuard.ts
Normal file
20
frontend/packages/swarm-ui/src/shell/navGuard.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// A page holding work that leaving would lose registers a guard here, and
|
||||
// every navigation inside the app asks it first. The guard returns false to
|
||||
// stop the move, typically to show its own "leave without saving?" prompt and
|
||||
// carry on from there once the person answers.
|
||||
//
|
||||
// Closing the tab and the browser back button never reach this: a page that
|
||||
// cares handles `beforeunload` and `popstate` itself.
|
||||
type Guard = (to: string) => boolean;
|
||||
|
||||
let current: Guard | null = null;
|
||||
|
||||
// Install `guard`, or pass null to remove it.
|
||||
export function setNavGuard(guard: Guard | null): void {
|
||||
current = guard;
|
||||
}
|
||||
|
||||
// Whether a move to `to` may go ahead now.
|
||||
export function mayNavigate(to: string): boolean {
|
||||
return current === null || current(to);
|
||||
}
|
||||
16
frontend/packages/swarm-ui/src/shell/useGuardedLocation.ts
Normal file
16
frontend/packages/swarm-ui/src/shell/useGuardedLocation.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// The router's location hook, with each in-app navigation put past the page's
|
||||
// nav guard first. Pass it to `<Router hook={useGuardedLocation}>`.
|
||||
import { useBrowserLocation } from "wouter-preact/use-browser-location";
|
||||
import { mayNavigate } from "./navGuard.js";
|
||||
|
||||
type Navigate = ReturnType<typeof useBrowserLocation>[1];
|
||||
|
||||
export function useGuardedLocation(
|
||||
...args: Parameters<typeof useBrowserLocation>
|
||||
): [string, Navigate] {
|
||||
const [path, navigate] = useBrowserLocation(...args);
|
||||
const guarded: Navigate = (to, opts) => {
|
||||
if (mayNavigate(String(to))) navigate(to, opts);
|
||||
};
|
||||
return [path, guarded];
|
||||
}
|
||||
Loading…
Reference in a new issue