@danxbot/ui 11.1.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +13494 -12647
- package/dist/index.js.map +1 -1
- package/dist/types/components/EditGutter.d.ts +17 -7
- package/dist/types/components/InlineEdit.d.ts +44 -31
- package/dist/types/components/InlineMarkdownEdit.d.ts +25 -7
- package/dist/types/components/MarkdownEditor.d.ts +7 -0
- package/dist/types/components/edit/activation.d.ts +21 -0
- package/dist/types/components/edit/outcome.d.ts +17 -6
- package/dist/types/components/edit/use-edit-session.d.ts +55 -0
- package/dist/types/components/graph-explorer/GraphExplorer.d.ts +35 -0
- package/dist/types/components/graph-explorer/camera-math.d.ts +50 -0
- package/dist/types/components/graph-explorer/index.d.ts +5 -0
- package/dist/types/components/graph-explorer/tree-layout.d.ts +29 -0
- package/dist/types/components/graph-explorer/types.d.ts +62 -0
- package/dist/types/components/graph-explorer/use-camera.d.ts +31 -0
- package/dist/types/icons/data.d.ts +25 -0
- package/dist/types/index.d.ts +5 -2
- package/package.json +1 -1
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
editing: boolean;
|
|
5
|
-
onEditingChange: (editing: boolean) => void;
|
|
6
|
-
}
|
|
7
|
-
export interface EditGutterProps {
|
|
2
|
+
import { type EditActivation } from "./edit/activation";
|
|
3
|
+
interface EditGutterFieldProps {
|
|
8
4
|
/**
|
|
9
5
|
* The pencil's accessible name — say WHAT it edits ("Edit title"), because
|
|
10
6
|
* a screen reader lists it among every other pencil on the page.
|
|
@@ -15,6 +11,20 @@ export interface EditGutterProps {
|
|
|
15
11
|
* `{(edit) => <InlineEdit {...edit} … />}`.
|
|
16
12
|
*/
|
|
17
13
|
children: (activation: EditActivation) => ReactNode;
|
|
14
|
+
/** Render no pencil — the field cannot be edited here. */
|
|
15
|
+
disabled?: boolean;
|
|
18
16
|
className?: string;
|
|
19
17
|
}
|
|
20
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The gutter keeps `editing` itself (omit both), or the caller owns it: pass
|
|
20
|
+
* `editing` AND `onEditingChange` (`EditActivation`) — the pencil then asks
|
|
21
|
+
* through `onEditingChange(true)` and the field's close arrives through
|
|
22
|
+
* `onEditingChange(false)`. One without the other is a type error and throws
|
|
23
|
+
* at render, for the reason `assertActivationPair` gives.
|
|
24
|
+
*/
|
|
25
|
+
export type EditGutterProps = EditGutterFieldProps & ({
|
|
26
|
+
editing?: undefined;
|
|
27
|
+
onEditingChange?: (editing: boolean) => void;
|
|
28
|
+
} | EditActivation);
|
|
29
|
+
export declare function EditGutter({ label, children, editing: editingProp, onEditingChange, disabled, className }: EditGutterProps): import("react").JSX.Element;
|
|
30
|
+
export {};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { type Ref } from "react";
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { type EditActivation } from "./edit/activation";
|
|
4
|
+
import { type EditCommit } from "./edit/outcome";
|
|
5
|
+
interface InlineEditFieldProps {
|
|
6
6
|
/**
|
|
7
7
|
* The committed value. Controlled — this component never holds the truth,
|
|
8
8
|
* only the draft while somebody is typing one.
|
|
9
9
|
*/
|
|
10
10
|
value: string;
|
|
11
11
|
/**
|
|
12
|
-
* Commit a new value. Called ONLY when the live text
|
|
13
|
-
* `value
|
|
12
|
+
* Commit a new value. Called ONLY when the live text differs both from
|
|
13
|
+
* `value` and from the value editing began from — text the reader did not
|
|
14
|
+
* change is never written back, even if `value` moved underneath the open
|
|
15
|
+
* field (with `autosaveMs`, "unchanged" is measured against the last value
|
|
16
|
+
* this edit committed instead — see "AUTOSAVE").
|
|
14
17
|
*
|
|
15
18
|
* `base` is the `value` this control saw at the moment editing BEGAN —
|
|
16
19
|
* captured once and handed back unchanged regardless of how long the edit or
|
|
@@ -23,7 +26,7 @@ export interface InlineEditProps {
|
|
|
23
26
|
* it settles. Resolve/return `{ refusal }` to reject the value with a
|
|
24
27
|
* reason; throw or reject to report that the save itself failed.
|
|
25
28
|
*/
|
|
26
|
-
onCommit:
|
|
29
|
+
onCommit: EditCommit;
|
|
27
30
|
/**
|
|
28
31
|
* The refusals that are knowable WITHOUT a request. Return the reason, or
|
|
29
32
|
* `null` to accept. Runs after the unchanged check and before `onCommit`.
|
|
@@ -93,7 +96,10 @@ export interface InlineEditProps {
|
|
|
93
96
|
/**
|
|
94
97
|
* This value cannot be edited at all, AND WHY. `{ reason }`, matching
|
|
95
98
|
* `SettingRow.readOnly` — a control that refuses in silence is
|
|
96
|
-
* indistinguishable from one that is broken.
|
|
99
|
+
* indistinguishable from one that is broken. Under controlled activation a
|
|
100
|
+
* request to open (`editing` true) is declined by handing the closed state
|
|
101
|
+
* straight back through `onEditingChange(false)`, so a pencil driving it
|
|
102
|
+
* returns instead of waiting on a field that will never open (DX-3372 H3).
|
|
97
103
|
*/
|
|
98
104
|
readOnly?: {
|
|
99
105
|
reason: string;
|
|
@@ -102,33 +108,13 @@ export interface InlineEditProps {
|
|
|
102
108
|
* What is shown when the commit REJECTS (a thrown error or a rejected
|
|
103
109
|
* promise — never a `{ refusal }`, which renders its own text).
|
|
104
110
|
*
|
|
105
|
-
* @default "Could not save. Your text is still here — press
|
|
111
|
+
* @default "Could not save. Your text is still here — press <key> to try again.",
|
|
112
|
+
* where <key> is the key that commits in this configuration: Enter, or
|
|
113
|
+
* Ctrl+Enter for a `multiline` field without `autosaveMs`.
|
|
106
114
|
*/
|
|
107
115
|
failureMessage?: string;
|
|
108
116
|
/** The rejected error, handed on so it can be logged or reported. */
|
|
109
117
|
onError?: (error: unknown) => void;
|
|
110
|
-
/**
|
|
111
|
-
* CONTROLLED ACTIVATION (DX-3372). Pass it and the control never opens
|
|
112
|
-
* ITSELF — no click, Enter or F2 — because something else (the pencil of an
|
|
113
|
-
* `EditGutter`, a menu item, a shortcut) is the way in. Setting it `true`
|
|
114
|
-
* opens the field, focused, caret at the end; the control asks to close
|
|
115
|
-
* through `onEditingChange(false)` once a commit lands or Escape cancels.
|
|
116
|
-
* Setting it `false` yourself while it is open abandons the draft exactly as
|
|
117
|
-
* Escape would — nothing is written. At rest the element leaves the tab
|
|
118
|
-
* order and drops its hover affordance, so the trigger is the one stop.
|
|
119
|
-
*
|
|
120
|
-
* Omit it for the self-opening control (click, Enter or F2).
|
|
121
|
-
*/
|
|
122
|
-
editing?: boolean;
|
|
123
|
-
/**
|
|
124
|
-
* Fires exactly when the control itself starts or ends editing — never while
|
|
125
|
-
* a read-only control merely declines to open, and never for a change the
|
|
126
|
-
* caller made through `editing`. Use it to suspend any re-render-from-server
|
|
127
|
-
* logic of your own for as long as someone is actually looking at the
|
|
128
|
-
* field; see the module comment. With `editing` controlled, this is how the
|
|
129
|
-
* control hands the closed state back.
|
|
130
|
-
*/
|
|
131
|
-
onEditingChange?: (editing: boolean) => void;
|
|
132
118
|
/** Render as a different element — see `Button`'s own `render` for the shape. */
|
|
133
119
|
render?: useRender.RenderProp;
|
|
134
120
|
/**
|
|
@@ -143,4 +129,31 @@ export interface InlineEditProps {
|
|
|
143
129
|
messageClassName?: string;
|
|
144
130
|
ref?: Ref<HTMLElement>;
|
|
145
131
|
}
|
|
146
|
-
|
|
132
|
+
/**
|
|
133
|
+
* SELF-ACTIVATED: omit `editing` and the control opens itself on click, Enter
|
|
134
|
+
* or F2. `onEditingChange` then only observes — it fires when the control
|
|
135
|
+
* starts editing and when it stops, so a caller can suspend its own
|
|
136
|
+
* re-render-from-server logic for exactly as long as someone is typing.
|
|
137
|
+
*/
|
|
138
|
+
interface InlineEditSelfActivated {
|
|
139
|
+
editing?: undefined;
|
|
140
|
+
onEditingChange?: (editing: boolean) => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The props. `editing` + `onEditingChange` are a discriminated pair
|
|
144
|
+
* (DX-3372 review P0): either omit `editing` (self-activated, above) or pass
|
|
145
|
+
* BOTH (`EditActivation` — controlled). Controlled, the control never opens
|
|
146
|
+
* itself; `true` opens it focused with the caret at the end; it hands the
|
|
147
|
+
* closed state back through `onEditingChange(false)` after a landed commit,
|
|
148
|
+
* Escape, or a refused opening (read-only). Setting `editing` false yourself
|
|
149
|
+
* ends the edit exactly as Escape does — nothing more is written. A save that
|
|
150
|
+
* is already out when you do cannot be recalled: it still reaches your
|
|
151
|
+
* `onCommit` promise, but its outcome no longer touches the field — no
|
|
152
|
+
* message, no focus move, no second `onEditingChange(false)` — and a commit
|
|
153
|
+
* from the next opening waits for it before starting (see
|
|
154
|
+
* `edit/use-edit-session.ts`). At rest a controlled control leaves the tab
|
|
155
|
+
* order and drops its hover affordance, so the trigger is the one stop.
|
|
156
|
+
*/
|
|
157
|
+
export type InlineEditProps = InlineEditFieldProps & (InlineEditSelfActivated | EditActivation);
|
|
158
|
+
export declare function InlineEdit(props: InlineEditProps): import("react").JSX.Element;
|
|
159
|
+
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { ImageUploader } from "../lib/markdown/image-pipeline";
|
|
3
|
-
import { type
|
|
4
|
-
|
|
3
|
+
import { type EditActivation } from "./edit/activation";
|
|
4
|
+
import { type EditCommit } from "./edit/outcome";
|
|
5
|
+
export interface InlineMarkdownEditProps extends EditActivation {
|
|
5
6
|
/** The committed markdown. Controlled — the field only ever holds a draft. */
|
|
6
7
|
value: string;
|
|
7
8
|
/**
|
|
@@ -10,11 +11,18 @@ export interface InlineMarkdownEditProps {
|
|
|
10
11
|
* began from; return/resolve `{ refusal }` to refuse with a reason, throw or
|
|
11
12
|
* reject to report a failed save.
|
|
12
13
|
*/
|
|
13
|
-
onCommit:
|
|
14
|
-
/**
|
|
14
|
+
onCommit: EditCommit;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the editor is open — REQUIRED, see the module comment. The field
|
|
17
|
+
* hands the closed state back through `onEditingChange(false)` once a commit
|
|
18
|
+
* lands, on Escape, and when asked to open while `readOnly`. Setting it
|
|
19
|
+
* `false` yourself drops the draft exactly as Escape does. A save already
|
|
20
|
+
* out when you do cannot be recalled: your `onCommit` promise still settles,
|
|
21
|
+
* but its outcome no longer touches the field — no message, no focus move,
|
|
22
|
+
* no `onEditingChange(false)` — and a commit from the next opening waits for
|
|
23
|
+
* it before starting.
|
|
24
|
+
*/
|
|
15
25
|
editing: boolean;
|
|
16
|
-
/** The field asks to close through this, once a commit lands or Escape cancels. */
|
|
17
|
-
onEditingChange: (editing: boolean) => void;
|
|
18
26
|
/** The accessible name of the field and of its editor. */
|
|
19
27
|
label: string;
|
|
20
28
|
/** Shown at rest and in the editor when the value is empty. @default "Empty" */
|
|
@@ -25,13 +33,23 @@ export interface InlineMarkdownEditProps {
|
|
|
25
33
|
toolbar?: ReactNode;
|
|
26
34
|
/** Stores a pasted/dropped/picked image — see `MarkdownEditorProps.uploadImage`. */
|
|
27
35
|
uploadImage?: ImageUploader;
|
|
36
|
+
/**
|
|
37
|
+
* This value cannot be edited at all, AND WHY — `InlineEdit.readOnly`'s
|
|
38
|
+
* shape and rule: asked to open, the field hands `onEditingChange(false)`
|
|
39
|
+
* straight back, and the reason is the rest view's accessible description.
|
|
40
|
+
*/
|
|
41
|
+
readOnly?: {
|
|
42
|
+
reason: string;
|
|
43
|
+
};
|
|
28
44
|
/**
|
|
29
45
|
* What is shown when the commit REJECTS (never a `{ refusal }`, which
|
|
30
46
|
* renders its own text).
|
|
47
|
+
*
|
|
48
|
+
* @default "Could not save. Your text is still here — press Ctrl+Enter to try again."
|
|
31
49
|
*/
|
|
32
50
|
failureMessage?: string;
|
|
33
51
|
/** The rejected error, handed on so it can be logged or reported. */
|
|
34
52
|
onError?: (error: unknown) => void;
|
|
35
53
|
className?: string;
|
|
36
54
|
}
|
|
37
|
-
export declare function InlineMarkdownEdit(
|
|
55
|
+
export declare function InlineMarkdownEdit(props: InlineMarkdownEditProps): import("react").JSX.Element;
|
|
@@ -15,6 +15,13 @@ export interface MarkdownEditorHandle {
|
|
|
15
15
|
* `"end"` (snapped to a real caret stop); omit it to leave the caret wherever
|
|
16
16
|
* the browser puts it. DX-3372: `InlineMarkdownEdit` opens with the caret at
|
|
17
17
|
* the end, the same place `InlineEdit` puts it.
|
|
18
|
+
*
|
|
19
|
+
* THROWS rather than returning quietly (DX-3372 review P0) when it cannot do
|
|
20
|
+
* what it was asked: the surface is not built yet (it is created in an
|
|
21
|
+
* effect, so a render or a layout effect is too early), the editor is
|
|
22
|
+
* read-only, focus did not land on the surface, or the caret could not be
|
|
23
|
+
* placed. A caller that goes on to assume focus is inside would otherwise
|
|
24
|
+
* wait for a blur that never comes.
|
|
18
25
|
*/
|
|
19
26
|
focus: (caret?: "start" | "end") => void;
|
|
20
27
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controlled activation pair every pencil-activated field takes, and the
|
|
3
|
+
* pair `EditGutter` hands out — so wiring a pencil to a field is one spread.
|
|
4
|
+
*
|
|
5
|
+
* `editing` says whether the field is open. The field calls
|
|
6
|
+
* `onEditingChange(false)` when it closes itself (a landed commit, Escape) and
|
|
7
|
+
* when it declines to open (read-only); it never calls `onEditingChange(true)`
|
|
8
|
+
* for an opening the caller made. The pair is all-or-nothing: a controlled
|
|
9
|
+
* `editing` with no way to hand the closed state back would leave the field
|
|
10
|
+
* shut after its first commit while the caller still believes it is open.
|
|
11
|
+
*/
|
|
12
|
+
export interface EditActivation {
|
|
13
|
+
editing: boolean;
|
|
14
|
+
onEditingChange: (editing: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Throws when a caller controls `editing` without `onEditingChange`. The
|
|
18
|
+
* types already forbid it; this covers a caller that got past them (a cast,
|
|
19
|
+
* a spread of `any`, plain JavaScript). DX-3372 review P0.
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertActivationPair(component: string, editing: boolean | undefined, onEditingChange: ((editing: boolean) => void) | undefined): void;
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The caller's refusal of a value, with the reason that will be shown.
|
|
3
3
|
*/
|
|
4
|
-
export interface
|
|
4
|
+
export interface EditRefusal {
|
|
5
5
|
refusal: string;
|
|
6
6
|
}
|
|
7
7
|
/** What `onCommit` may report. Returning nothing accepts the value. */
|
|
8
|
-
export type
|
|
8
|
+
export type EditOutcome = void | EditRefusal;
|
|
9
9
|
/** Every in-place editor's `onCommit`: the new value and the value editing began from. */
|
|
10
|
-
export type
|
|
11
|
-
export declare function isRefusal(outcome:
|
|
12
|
-
export declare function isPromise(outcome:
|
|
13
|
-
|
|
10
|
+
export type EditCommit = (value: string, base: string) => EditOutcome | Promise<EditOutcome>;
|
|
11
|
+
export declare function isRefusal(outcome: EditOutcome): outcome is EditRefusal;
|
|
12
|
+
export declare function isPromise(outcome: EditOutcome | Promise<EditOutcome>): outcome is Promise<EditOutcome>;
|
|
13
|
+
/**
|
|
14
|
+
* The key that retries a failed save in a given field: Enter where Enter
|
|
15
|
+
* commits, Ctrl+Enter where Enter inserts a newline. (Both fields accept
|
|
16
|
+
* Cmd+Enter as well as Ctrl+Enter, so "Ctrl+Enter" is true on every platform.)
|
|
17
|
+
*/
|
|
18
|
+
export type RetryKey = "Enter" | "Ctrl+Enter";
|
|
19
|
+
/**
|
|
20
|
+
* The default failure message. It names the retry key because blur never
|
|
21
|
+
* re-attempts a failed value — DX-3372 review L1: the single shared sentence
|
|
22
|
+
* said "press Enter" in fields where Enter inserts a newline.
|
|
23
|
+
*/
|
|
24
|
+
export declare function defaultFailureMessage(retryKey: RetryKey): string;
|
|
14
25
|
/**
|
|
15
26
|
* A refusal nobody can read leaves the field open with nothing beside it — the
|
|
16
27
|
* exact silent failure the outcome contract exists to rule out — so an empty
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type EditCommit, type EditOutcome } from "./outcome";
|
|
2
|
+
export type SaveResult = {
|
|
3
|
+
outcome: EditOutcome;
|
|
4
|
+
} | {
|
|
5
|
+
error: unknown;
|
|
6
|
+
};
|
|
7
|
+
export interface EditSessionOptions {
|
|
8
|
+
/** Named in the thrown message when a refusal carries an empty reason. */
|
|
9
|
+
component: string;
|
|
10
|
+
/** The committed value — "unchanged" is measured against it. Read live. */
|
|
11
|
+
value: string;
|
|
12
|
+
onCommit: EditCommit;
|
|
13
|
+
onError?: (error: unknown) => void;
|
|
14
|
+
failureMessage: string;
|
|
15
|
+
/** End editing after a commit that landed (or had nothing to write). */
|
|
16
|
+
close: () => void;
|
|
17
|
+
/** Put focus back on the field after a refusal or a failure. */
|
|
18
|
+
refocus: () => void;
|
|
19
|
+
}
|
|
20
|
+
export interface EditSession {
|
|
21
|
+
pending: boolean;
|
|
22
|
+
refusal: string | null;
|
|
23
|
+
failure: string | null;
|
|
24
|
+
/** The message on screen, if any — a refusal or a failure. */
|
|
25
|
+
blocked: string | null;
|
|
26
|
+
/** The value this session began from — `onCommit`'s `base`. */
|
|
27
|
+
readonly base: string;
|
|
28
|
+
/** Start a new session. `refusal` seeds the message slot (live validation). */
|
|
29
|
+
begin: (base: string, refusal?: string | null) => void;
|
|
30
|
+
/** Finish the current session: every save still out becomes stale. */
|
|
31
|
+
end: () => void;
|
|
32
|
+
/** Commit `next` — unchanged check (see the module comment), `validate`, `onCommit`, outcome. */
|
|
33
|
+
commit: (next: string, validate?: (next: string) => string | null) => void;
|
|
34
|
+
refuse: (reason: string, source: "validate" | "onCommit") => void;
|
|
35
|
+
fail: (error: unknown) => void;
|
|
36
|
+
setPending: (pending: boolean) => void;
|
|
37
|
+
isPending: () => boolean;
|
|
38
|
+
setRefusal: (refusal: string | null) => void;
|
|
39
|
+
clearFailure: () => void;
|
|
40
|
+
/** The current session's token, for a caller that tracks its own save. */
|
|
41
|
+
token: () => number;
|
|
42
|
+
/** Whether a save from any session is still out. */
|
|
43
|
+
busy: () => boolean;
|
|
44
|
+
/** The in-flight save, settled or not — chain after it to stay in order. */
|
|
45
|
+
inFlight: () => Promise<void> | null;
|
|
46
|
+
/**
|
|
47
|
+
* Track a save sent under `token`. `onResult` runs only if that session is
|
|
48
|
+
* still current when it settles; otherwise `onStale` does (after a stale
|
|
49
|
+
* rejection has reached `onError`).
|
|
50
|
+
*/
|
|
51
|
+
track: (save: Promise<EditOutcome>, token: number, onResult: (result: SaveResult) => void, onStale?: (result: SaveResult) => void) => void;
|
|
52
|
+
/** Run `fn` once no save is out — dropped if this session ends first. */
|
|
53
|
+
whenIdle: (fn: () => void) => void;
|
|
54
|
+
}
|
|
55
|
+
export declare function useEditSession(options: EditSessionOptions): EditSession;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CanvasNode } from "../node-canvas/types";
|
|
2
|
+
import type { CrossLink, ExplorerNodeRenderer, ExplorerTree, GraphExplorerVariant } from "./types";
|
|
3
|
+
export interface GraphExplorerProps<Extra = unknown> {
|
|
4
|
+
tree: ExplorerTree<Extra>;
|
|
5
|
+
/** Secondary relationships drawn over the fixed layout. Never affects positions. */
|
|
6
|
+
crossLinks?: readonly CrossLink[];
|
|
7
|
+
/** The accessible name of the whole structure. Required — see `NodeCanvasProps.label`. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** The node body. Defaults to the label, wrapped, never truncated. */
|
|
10
|
+
renderNode?: ExplorerNodeRenderer<Extra>;
|
|
11
|
+
/** @default "explorer" */
|
|
12
|
+
variant?: GraphExplorerVariant;
|
|
13
|
+
/** Which node the camera centres on, with a "you are here" highlight. Controlled. */
|
|
14
|
+
focusId?: string | null;
|
|
15
|
+
/** Initial `focusId` for uncontrolled use. */
|
|
16
|
+
defaultFocusId?: string | null;
|
|
17
|
+
/** Fired when the controlled `focusId` would change — a node click, or `flyTo` via the keyboard. Never fired with `null`: nothing in this component ever clears focus, only moves it. */
|
|
18
|
+
onFocusIdChange?: (id: string) => void;
|
|
19
|
+
/** Nodes styled as already seen. */
|
|
20
|
+
visitedIds?: ReadonlySet<string> | readonly string[];
|
|
21
|
+
/** Fired when an ordinary node is clicked or activated by keyboard. */
|
|
22
|
+
onNodeClick?: (node: CanvasNode<Extra>) => void;
|
|
23
|
+
/** `variant="minimap"` only: the expand affordance's handler. Defaults to an in-place full-screen dialog. */
|
|
24
|
+
onExpand?: () => void;
|
|
25
|
+
/** Along-axis extent of a node that does not need more, in px. @default LAYOUT_DEFAULTS.nodeWidth */
|
|
26
|
+
nodeWidth?: number;
|
|
27
|
+
/** Cross-axis extent BEFORE a taller node's own content grows it. @default LAYOUT_DEFAULTS.nodeHeight */
|
|
28
|
+
nodeHeight?: number;
|
|
29
|
+
/** `variant="explorer"` only: height of the viewport. @default "32rem" */
|
|
30
|
+
height?: string;
|
|
31
|
+
/** Nothing pans, zooms or clicks. Reading is unaffected. */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
className?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function GraphExplorer<Extra = unknown>({ tree, crossLinks, label, renderNode, variant, focusId: controlledFocusId, defaultFocusId, onFocusIdChange, visitedIds, onNodeClick, onExpand, nodeWidth, nodeHeight, height, disabled, className, }: GraphExplorerProps<Extra>): import("react").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface Point {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}
|
|
5
|
+
export interface Box {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
export interface Size {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
export interface CameraState {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
scale: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ScaleBounds {
|
|
21
|
+
minScale?: number;
|
|
22
|
+
maxScale?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare const CAMERA_DEFAULTS: {
|
|
25
|
+
readonly minScale: 0.15;
|
|
26
|
+
readonly maxScale: 3;
|
|
27
|
+
readonly fitMaxScale: 1;
|
|
28
|
+
readonly fitPadding: 64;
|
|
29
|
+
};
|
|
30
|
+
export declare function clampScale(scale: number, bounds?: ScaleBounds): number;
|
|
31
|
+
/** Screen point → the content point currently sitting under it. */
|
|
32
|
+
export declare function toContentPoint(camera: CameraState, screenPoint: Point): Point;
|
|
33
|
+
/** Content point → where it currently sits on screen. */
|
|
34
|
+
export declare function toScreenPoint(camera: CameraState, contentPoint: Point): Point;
|
|
35
|
+
/**
|
|
36
|
+
* Zoom by `factor`, holding `screenPoint` fixed over the same content point it
|
|
37
|
+
* covered before — the standard "zoom toward the cursor" behaviour a wheel or
|
|
38
|
+
* a pinch both want. `factor` > 1 zooms in.
|
|
39
|
+
*/
|
|
40
|
+
export declare function zoomAtPoint(camera: CameraState, screenPoint: Point, factor: number, bounds?: ScaleBounds): CameraState;
|
|
41
|
+
/** A camera that puts `target`'s centre in the middle of `viewport`, at `scale`. */
|
|
42
|
+
export declare function centerOn(target: Box | Point, viewport: Size, scale: number): CameraState;
|
|
43
|
+
export interface FitOptions extends ScaleBounds {
|
|
44
|
+
/** @default CAMERA_DEFAULTS.fitPadding */
|
|
45
|
+
padding?: number;
|
|
46
|
+
/** @default CAMERA_DEFAULTS.fitMaxScale — never zoom IN past this to fit a small box. */
|
|
47
|
+
maxScale?: number;
|
|
48
|
+
}
|
|
49
|
+
/** A camera that fits the whole of `box` inside `viewport`, centred, padded. */
|
|
50
|
+
export declare function fitBox(box: Box, viewport: Size, options?: FitOptions): CameraState;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { GraphExplorer, type GraphExplorerProps } from "./GraphExplorer";
|
|
2
|
+
export { layoutExplorerTree, neighbourhoodTree, edgePath } from "./tree-layout";
|
|
3
|
+
export { CAMERA_DEFAULTS, centerOn, clampScale, fitBox, toContentPoint, toScreenPoint, zoomAtPoint } from "./camera-math";
|
|
4
|
+
export type { Box, CameraState, FitOptions, Point, ScaleBounds, Size } from "./camera-math";
|
|
5
|
+
export type { CrossLink, ExplorerLayout, ExplorerNodeRenderContext, ExplorerNodeRenderer, ExplorerTree, GraphExplorerVariant, TreeNode, } from "./types";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { edgePath } from "../node-canvas/layout";
|
|
2
|
+
import type { LayoutOptions } from "../node-canvas/types";
|
|
3
|
+
import type { CrossLink, ExplorerLayout, ExplorerTree } from "./types";
|
|
4
|
+
/** Re-exported for a consumer drawing `edges`/`crossLinks` into an SVG. */
|
|
5
|
+
export { edgePath };
|
|
6
|
+
/**
|
|
7
|
+
* Lay out a tree, left to right, from a parent backbone alone.
|
|
8
|
+
*
|
|
9
|
+
* `crossLinks` are resolved against the ALREADY-FIXED node positions and
|
|
10
|
+
* returned separately (`ExplorerLayout.crossLinks`) — they never reach
|
|
11
|
+
* `layoutGraph`, so they can never move a node and can never close a cycle
|
|
12
|
+
* for the layout engine to condense. A cross-link naming an id not in the
|
|
13
|
+
* tree, or naming a node as linked to itself, draws nothing — the same
|
|
14
|
+
* "dangling edge draws nothing" rule `node-canvas/layout.ts#resolveScope`
|
|
15
|
+
* uses for a group boundary.
|
|
16
|
+
*/
|
|
17
|
+
export declare function layoutExplorerTree<Extra>(tree: ExplorerTree<Extra>, crossLinks?: readonly CrossLink[], options?: LayoutOptions): ExplorerLayout<Extra>;
|
|
18
|
+
/**
|
|
19
|
+
* The subtree a minimap draws: `focusId`'s ancestor chain up to its root,
|
|
20
|
+
* `focusId`'s immediate siblings, and `focusId`'s immediate children — "the
|
|
21
|
+
* focused node's ancestors, siblings and children" the card asks for, kept
|
|
22
|
+
* small enough to read at the minimap's compact size. `focusId` itself is
|
|
23
|
+
* always included. Parent links are REWRITTEN to stay inside this subset: the
|
|
24
|
+
* topmost ancestor kept becomes a root (`parentId: null`) here even if its
|
|
25
|
+
* real parent exists outside the shown set, so `layoutExplorerTree` never
|
|
26
|
+
* sees a dangling parent reference. Returns `null` when `focusId` is not in
|
|
27
|
+
* `tree` at all — nothing sensible to show.
|
|
28
|
+
*/
|
|
29
|
+
export declare function neighbourhoodTree<Extra>(tree: ExplorerTree<Extra>, focusId: string): ExplorerTree<Extra> | null;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { CanvasEdge, CanvasNode, CanvasPlacedEdge, LayoutOptions, PlacedNode } from "../node-canvas/types";
|
|
3
|
+
/**
|
|
4
|
+
* One node of the tree. Same `Extra`-carrying shape as `CanvasNode` — a
|
|
5
|
+
* consumer's own fields come back untouched through `renderNode` — plus the
|
|
6
|
+
* one thing a tree node has that a general graph node does not: its parent.
|
|
7
|
+
*/
|
|
8
|
+
export type TreeNode<Extra = unknown> = CanvasNode<Extra> & {
|
|
9
|
+
/** The node this one hangs from. `null` for a root — a tree may have more than one. */
|
|
10
|
+
parentId: string | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* A secondary relationship drawn OVER the fixed tree layout — never fed to
|
|
14
|
+
* the layout engine, so it can never move a node and can never close a cycle
|
|
15
|
+
* for dagre to condense. `kind` carries no meaning the library assigns, same
|
|
16
|
+
* rule as `CanvasEdge.kind`.
|
|
17
|
+
*/
|
|
18
|
+
export interface CrossLink {
|
|
19
|
+
from: string;
|
|
20
|
+
to: string;
|
|
21
|
+
kind?: string;
|
|
22
|
+
}
|
|
23
|
+
/** The whole tree. Never mutated by the component. */
|
|
24
|
+
export interface ExplorerTree<Extra = unknown> {
|
|
25
|
+
nodes: readonly TreeNode<Extra>[];
|
|
26
|
+
}
|
|
27
|
+
/** What `layoutExplorerTree` hands back: the fixed layout, ready to draw. */
|
|
28
|
+
export interface ExplorerLayout<Extra = unknown> {
|
|
29
|
+
nodes: PlacedNode<Extra>[];
|
|
30
|
+
/** The parent → child backbone, drawn as the primary edges. */
|
|
31
|
+
edges: CanvasPlacedEdge[];
|
|
32
|
+
/** `CrossLink`s resolved to drawn curves. Never affects `nodes`' positions. */
|
|
33
|
+
crossLinks: CanvasPlacedEdge[];
|
|
34
|
+
/** Extent of the laid-out tree — the plane the camera's `fitAll` fits to. */
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}
|
|
38
|
+
export type { CanvasEdge, CanvasNode, LayoutOptions, PlacedNode, CanvasPlacedEdge };
|
|
39
|
+
/** Which surface this render is: the full pannable explorer, or the compact strip. */
|
|
40
|
+
export type GraphExplorerVariant = "explorer" | "minimap";
|
|
41
|
+
/**
|
|
42
|
+
* What `renderNode` is told about the node it is drawing — the explorer's own
|
|
43
|
+
* facts (camera focus, visited, hover neighbourhood), analogous to
|
|
44
|
+
* `NodeRenderContext` in `node-canvas/types.ts` but keyed on the camera's
|
|
45
|
+
* `focusId` rather than a drilled-into scope.
|
|
46
|
+
*/
|
|
47
|
+
export interface ExplorerNodeRenderContext {
|
|
48
|
+
/** This node carries the canvas's single roving tab stop. */
|
|
49
|
+
isTabStop: boolean;
|
|
50
|
+
/** This IS the controlled `focusId` — the "you are here" node. */
|
|
51
|
+
isFocused: boolean;
|
|
52
|
+
/** In the caller-supplied `visitedIds`. */
|
|
53
|
+
isVisited: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Hops from whichever node is currently the reference point — the hovered
|
|
56
|
+
* node while one is hovered, else the focused node — along either edge
|
|
57
|
+
* direction (backbone or cross-link). `null` when nothing is being
|
|
58
|
+
* referenced, or this node is further away than the explorer tracks.
|
|
59
|
+
*/
|
|
60
|
+
distance: number | null;
|
|
61
|
+
}
|
|
62
|
+
export type ExplorerNodeRenderer<Extra = unknown> = (node: CanvasNode<Extra>, context: ExplorerNodeRenderContext) => ReactNode;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Box, type CameraState } from "./camera-math";
|
|
2
|
+
export interface CameraClickEvent {
|
|
3
|
+
/** Client coordinates, for the caller to hit-test against its own elements. */
|
|
4
|
+
target: HTMLElement;
|
|
5
|
+
}
|
|
6
|
+
export interface UseCameraOptions {
|
|
7
|
+
/** Content-space bounding box of the whole drawing. Drives `fitAll`. */
|
|
8
|
+
contentBox: Box;
|
|
9
|
+
/** A node's content-space box, for `flyTo`. `null` for an id not laid out. */
|
|
10
|
+
boxOf: (id: string) => Box | null;
|
|
11
|
+
/** A press that released without travelling past `CLICK_DISTANCE`. */
|
|
12
|
+
onClick?: (event: CameraClickEvent) => void;
|
|
13
|
+
/** Nothing pans, zooms or clicks. Reading the plane still works. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Changing this value re-fits the camera to `contentBox` — a new tree. */
|
|
16
|
+
resetKey?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface UseCameraResult {
|
|
19
|
+
camera: CameraState;
|
|
20
|
+
/** A pointer is actively panning or pinching right now. */
|
|
21
|
+
panning: boolean;
|
|
22
|
+
/** True for the duration of a programmatic move — see the module doc. */
|
|
23
|
+
animating: boolean;
|
|
24
|
+
bindViewport: (node: HTMLDivElement | null) => void;
|
|
25
|
+
zoomIn: () => void;
|
|
26
|
+
zoomOut: () => void;
|
|
27
|
+
fitAll: () => void;
|
|
28
|
+
/** Animate the camera to centre node `id`. No-op if `id` isn't laid out yet. */
|
|
29
|
+
flyTo: (id: string, scale?: number) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function useCamera(options: UseCameraOptions): UseCameraResult;
|
|
@@ -630,6 +630,31 @@ export declare const coreIcons: {
|
|
|
630
630
|
}], readonly ["path", {
|
|
631
631
|
readonly d: "M15 3v18";
|
|
632
632
|
}]];
|
|
633
|
+
readonly crosshair: readonly [readonly ["circle", {
|
|
634
|
+
readonly cx: "12";
|
|
635
|
+
readonly cy: "12";
|
|
636
|
+
readonly r: "10";
|
|
637
|
+
}], readonly ["line", {
|
|
638
|
+
readonly x1: "22";
|
|
639
|
+
readonly x2: "18";
|
|
640
|
+
readonly y1: "12";
|
|
641
|
+
readonly y2: "12";
|
|
642
|
+
}], readonly ["line", {
|
|
643
|
+
readonly x1: "6";
|
|
644
|
+
readonly x2: "2";
|
|
645
|
+
readonly y1: "12";
|
|
646
|
+
readonly y2: "12";
|
|
647
|
+
}], readonly ["line", {
|
|
648
|
+
readonly x1: "12";
|
|
649
|
+
readonly x2: "12";
|
|
650
|
+
readonly y1: "6";
|
|
651
|
+
readonly y2: "2";
|
|
652
|
+
}], readonly ["line", {
|
|
653
|
+
readonly x1: "12";
|
|
654
|
+
readonly x2: "12";
|
|
655
|
+
readonly y1: "22";
|
|
656
|
+
readonly y2: "18";
|
|
657
|
+
}]];
|
|
633
658
|
readonly "delete-column": readonly [readonly ["path", {
|
|
634
659
|
readonly d: "M2 12h6";
|
|
635
660
|
}], readonly ["path", {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -21,9 +21,11 @@ export { Skeleton, SkeletonText, LoadingOverlay, type SkeletonProps, type Skelet
|
|
|
21
21
|
export { ErrorBoundary, type ErrorBoundaryProps } from "./components/ErrorBoundary";
|
|
22
22
|
export { Field, FieldSet, type FieldProps, type FieldSetProps } from "./components/Field";
|
|
23
23
|
export { Input, Textarea, type InputProps, type TextareaProps } from "./components/Input";
|
|
24
|
-
export { InlineEdit, type InlineEditProps
|
|
25
|
-
export { EditGutter, type EditGutterProps
|
|
24
|
+
export { InlineEdit, type InlineEditProps } from "./components/InlineEdit";
|
|
25
|
+
export { EditGutter, type EditGutterProps } from "./components/EditGutter";
|
|
26
26
|
export { InlineMarkdownEdit, type InlineMarkdownEditProps } from "./components/InlineMarkdownEdit";
|
|
27
|
+
export type { EditActivation } from "./components/edit/activation";
|
|
28
|
+
export type { EditCommit, EditOutcome, EditRefusal } from "./components/edit/outcome";
|
|
27
29
|
export { SecretField, type SecretFieldProps, type SecretValue } from "./components/SecretField";
|
|
28
30
|
export { SettingRow, SettingGroup, type SettingRowProps, type SettingGroupProps, type SettingRowControlRenderProps, } from "./components/SettingRow";
|
|
29
31
|
export { DescriptionList, DescriptionListItem, type DescriptionListProps, type DescriptionListItemProps, type DescriptionListEntry, } from "./components/DescriptionList";
|
|
@@ -127,6 +129,7 @@ export { ProvenanceDisclosure, ProvenanceDetail, ProvenanceBadge, ConfidenceRead
|
|
|
127
129
|
export { confidenceLevel, CONFIDENCE_LEVEL_LABELS, type ConfidenceLevel } from "./lib/confidence";
|
|
128
130
|
export { createSimStore, resetSimStores, useSimSettings, setSimSettings, getNetworkSpeed, getDataSource, simulateLatency, SimNetworkError, type SimStore, type SimStoreOptions, type SimSettings, type NetworkSpeed, type DataSource, type SimNamespace, } from "./lib/sim";
|
|
129
131
|
export { NodeCanvas, LAYOUT_DEFAULTS, edgePath, layoutGraph, neighbourhood, type NodeCanvasProps, type CanvasCycleScope, type CanvasEdge, type CanvasGraph, type CanvasGroup, type CanvasLayout, type CanvasNode, type CanvasOrientation, type CanvasPlacedEdge, type CanvasScope, type LayoutOptions, type NodeRenderContext, type NodeRenderer, type PlacedNode, } from "./components/node-canvas";
|
|
132
|
+
export { GraphExplorer, layoutExplorerTree, neighbourhoodTree, CAMERA_DEFAULTS, centerOn, clampScale, fitBox, toContentPoint, toScreenPoint, zoomAtPoint, type GraphExplorerProps, type Box, type CameraState, type FitOptions, type Point, type ScaleBounds, type Size, type CrossLink, type ExplorerLayout, type ExplorerNodeRenderContext, type ExplorerNodeRenderer, type ExplorerTree, type GraphExplorerVariant, type TreeNode as ExplorerTreeNode, } from "./components/graph-explorer";
|
|
130
133
|
export { defineRecordType, resetRecordTypes, useRecord, useRecords, useStaleness, write, writeAll, peekRecord, readState, readStaleness, subscribeToKey, subscribeToStaleness, retainKeys, sweepRecords, setRecordStoreTenant, getRecordStoreTenant, flushRecordNotifications, resetRecordStore, recordStoreStats, UNKNOWN_STATE, normalizeTypeName, normalizeTimestamp, recordKey, isRecordRef, type RecordRef, type ReadOptions, type RecordPayload, type RecordState, type RecordTypeOptions, type RecordTypeToken, type StalenessEntry, type WriteAllResult, type WriteOptions, type WriteResult, type WriteSource, type WriteVerdict, } from "./lib/records";
|
|
131
134
|
export { RealtimeProvider, useRealtime, useRealtimeEpoch, useRealtimeHealth, useRealtimeRegistry, SubscriptionRegistry, normalizeScope, scopeKey, subscriptionKey, readRealtimeEpoch, subscribeToRealtimeEpoch, subscribeToRealtimeEvents, resetRealtimeSignals, type RealtimeChannel, type RealtimeConnectionState, type RealtimeDiagnostic, type RealtimeEvent, type RealtimeHealth, type RealtimeProviderProps, type RealtimeScope, type RealtimeScopeInput, type RealtimeStatus, type RealtimeTransport, type RegistryOptions, type SubscriptionHealth, type SubscriptionLease, type SubscriptionRequest, } from "./lib/realtime";
|
|
132
135
|
export { useRecordRead, useRecordListRead, useRecordStoreSweep, applyMutationResult, optimisticWrite, beginOptimisticWrite, resetOptimisticWrites, type OptimisticHandle, type OptimisticWriteOptions, type OptimisticWriteSpec, type SlotDescriptor, acquireSlot, appendCreated, emptySlotSnapshot, readSlot, resetSlots, slotKeys, subscribeToSlot, writeSlot, RefetchRunner, registerActiveRead, resetActiveReads, type ActiveRead, type ListFetch, type ListFetchResult, type ListReadOptions, type ListReadResult, type ReadError, type RecordFetch, type RecordReadOptions, type RecordReadResult, type RunnerOptions, type SlotSnapshot, } from "./lib/reads";
|