SheetXL - v0.8.0-beta.9+sites.210.e2d420901
    Preparing search index...

    A node in the command scope tree — the pure registry (the new-model counterpart of the existing CommandGroup, but holding definitions instead of instances). A scope holds the within-scope merged ICommandDefinition for each command key; it knows nothing about "active"/focus — which leaf to resolve from is the consumer's anchor (see CommandResolver).

    resolve walks the chain from a given anchor leaf → root, reducing each scope's contribution least→most specific into a derived ResolvedCommand. Two merge levels:

    • within a scope — successive updateCommand calls merge (here);
    • across scopes — the anchor-chain reduction (in resolve).

    Also provides change notification (addListener / invalidate) and per-key execute observation (addExecuteListener). It does NOT dispatch — the resolved command's execute() does (firing the per-key observers).

    Index
    key: string
    name: string

    The scope's name — its key into the flat CommandRegistry (design/COMMAND-DESIGN.md §"Named scopes"). Defaults to key; a distinct name lets two instances with different keys (a double-buffer's two workbooks, a sheet's per-pane scopes) share one registry entry. At every node of resolve/collectKeys, the registry entry for this name is folded in as the least-specific (prototype) layer beneath the node's own defs.

    • Observe executions of a specific command key. Fires for ALL dispatch paths (button, keystroke, programmatic) because the resolved command's execute() fires it — the ripple/close + middleware hook. Root-anchored, so it survives command re-derivation.

      Parameters

      Returns RemoveListener

    • Bound this scope's region to element for origin containment — WITHOUT naming a focus region. The containment half of attachElement, alone.

      Use this when a scope's commands need a UI region but the scope is not a focus leaf: the sheet scope owns the grid's commands, yet focus inside the grid must keep anchoring at the pane's z-stack (a deeper region), not here. Recording it in the root index would make this scope a candidate anchor and change what a focus lands on; bounding only does not.

      Leaving a scope unbounded is a decision, not a default: an element-less scope is unrestricted (see contains), so its shortcuts fire from any origin under the dispatch seam — correct for app-tier commands (ctrl+S saves while the caret is in a task-pane field), wrong for anything that acts on a region's own UI.

      A region is a set of containers, not one subtree. Call this more than once when the region's UI is not contiguous in the DOM — a host that renders workbook chrome (a formula bar, a status bar, a toolbar with a search box) outside the workbook element adds each piece, and a keystroke born there still reaches the region's commands. A host declares those from where the chrome renders, via useCommandElement; the scope's own owner does not have to know about them. Same seam for a portaled float — DOM-outside the region, logically inside it.

      Returns a remover the recording site calls on unmount.

      Parameters

      • element: Element

      Returns RemoveListener

    • Record element → source in the tree's _regions region index — a focus region naming its own focusable element so a consumer's FocusActivation can anchor there on focus. source defaults to this scope (a fixed leaf — the formula-bar editor, the script pane); pass a live AnchorSource (a pane's LayerActivation, or a getter) for a dynamic leaf whose current value tracks a moving stack top. The entry lands on the root (shared by every consumer), so this can be called on any node. Returns a remover the recording site calls on unmount — identity-guarded, so a re-record of the same element by another owner is never clobbered by a stale cleanup.

      Parameters

      Returns RemoveListener

    • All command keys contributed along the chain from anchor → root (deduped, order unspecified). The keystroke dispatcher uses this to enumerate the commands that could match an event at the current anchor (then resolves each to read its shortcut).

      Parameters

      Returns string[]

    • Like collectKeys, but only keys whose contribution somewhere along the chain carries tag (tags accumulate across scopes, so a match at any scope on the chain counts). Enables cross-cutting, tag-driven rules — e.g. "disable every mutator command when the sheet is protected" — without a hand-maintained parallel key list. Order unspecified; deduped.

      Parameters

      Returns string[]

    • (Re)establish this scope's registry subscription. Idempotent. The owning provider's mount effect calls this so a scope disposed on an effect cleanup (React StrictMode's mount→unmount→remount) comes back to life instead of staying permanently deaf to registry changes — the same resurrect contract as CommandResolver.connect. Without it, anything that reaches consumers only through the registry listener (a plugin registering statics after mount, a language-pack switch re-deriving translated chrome) silently stops propagating after the StrictMode cycle. Bumps on re-subscribe: the registry may have changed while deaf.

      Returns void

    • Origin containment — the scope decides (design/COMMAND-DESIGN.md "Core law", consequence 3): whether node counts as within this scope's region, i.e. whether a keystroke born at node may dispatch this scope's contributions. The default policy:

      • this scope has attached element(s) → true iff any of them DOM-contains node. (Plural is the portal seam: a float/popup portaled to body is DOM-outside the region but logically inside it — its owner attaches the portal container as an additional element and keys born there qualify.)
      • no elements of its own → delegate to the parent — the nearest region-owning ancestor decides (a pane's z-chain layers all inherit the pane Stage via the chain base, so the whole z-stack shares one region, and a key a layer deliberately lets propagate — the editor's commit Enter — still qualifies, because it was born inside that Stage).
      • nothing up the whole chain → true (an element-less chain — a sheet/workbook/app-level scope — is unrestricted: its commands may fire from any origin under the dispatch seam).

      It is a method on the scope — not an inline DOM check — precisely so a region owner can make a richer decision than raw containment when the DOM and the logical UI disagree (portals today; anything else tomorrow).

      Parameters

      • node: Node

      Returns boolean

    • Open a tracked CommandContribution on THIS scope: updateCommand through the handle records each key, and remove() removes exactly those — so a contributor (esp. a large one) cleans up with a single call rather than a per-key removeCommand list that's easy to fall out of sync. The handle owns its keys only; it doesn't touch other contributions to this scope.

      Returns CommandContribution

    • Create (and register) a child scope. The bare form deliberately does not bump the version: a fresh child has no definitions, so it changes no resolved command (resolution only sees it once it gets updateCommand — which bumps — or becomes the anchor). This keeps the endorsed pattern of creating a region's scope during render (useMemo(() => parent.createChild(key))) free of a notify-during-render — which would re-enter subscribed consumers (the "setState while rendering" warning). Teardown (dispose) does bump, since it drops contributions.

      contributor — bind a contribution to the child's lifetime (so a scope and its commands are created and torn down as one, instead of a separate useEffect). It runs immediately with a fresh CommandContribution for the new child (plus any forwarded args), and returns only its own external-subscription disposer (e.g. a model listener) — it does not manage command removal. dispose() does the rest automatically: invoke that disposer and clear the child's _defs (so the contributed commands go without a per-key list). Declarative call site: parent.createChild('undo', contributeUndoCommands, { manager, notifier }).

      NOTE: the contributor contributes synchronously, and contributing bumps (notifies). So — like any updateCommand — call this where a contribution-time notify is fine: at mount (in useMemo, no subscribers yet), NOT mid-render with live subscribers.

      Type Parameters

      • A extends any[] = []

      Parameters

      • keyOrOpts: string | { key: string; name?: string }
      • Optionalcontributor: (contribution: CommandContribution, ...args: A) => void | RemoveListener
      • ...args: A

      Returns CommandScope

    • Tear this scope down when the feature/region that owns it unmounts: recursively dispose children, drop this scope's definitions, detach it from its parent (so it stops contributing to resolution), and bump the root version so resolvers re-resolve. The _parent pointer is left intact so a stale anchor still pointing here resolves up the (now-detached) chain rather than throwing — turning a torn-down scope into its parent is the eventual destruction-fallback (design/COMMAND-DESIGN.md, open Q5). Idempotent.

      Returns void

    • The nearest region scope at or above element: walk parentElement to the first attached element and stop (leaf-finding, unbounded — a live source is resolved to its current scope). This is leaf-finding, NOT the resolution walk (design/COMMAND-DESIGN.md §Guardrail): it names the anchor; resolution is the separate anchor → root command-parent walk. undefined if no attached element is on the ancestor chain.

      Parameters

      • element: Element | null

      Returns CommandScope | undefined

    • This scope's own (merged) contribution for a key, or undefined.

      Parameters

      • commandKey: string

      Returns ICommandDefinition<any, any, void> | undefined

    • The region source recorded exactly at element (single-element lookup on the root index), or undefined. The bounded DOM walk-up is the caller's (FocusActivation.findSource, region-bounded; or findScope, unbounded) — this is the per-element primitive both build on.

      Parameters

      • element: Element

      Returns AnchorSource | undefined

    • The flat named registry bound to this tree (from the root), or undefined if none.

      Returns CommandRegistry | undefined

    • Returns number

    • Trigger a re-resolution without changing any contribution — bump version + notify. A contributor calls this when the model/context changed (so dynamic values should re-evaluate) instead of enumerating updateCommands; containers re-resolve on their own cadence (debounce). NOT a substitute for updateCommand (which sets values).

      Returns void

    • Remove this scope's contribution for commandKey.

      Parameters

      • commandKey: string

      Returns void

    • Resolve a command by gathering its contributions along the chain from anchor → root, reducing them least→most specific, and deriving a ResolvedCommand. anchor is the resolution leaf; it defaults to the registry root. A consumer (useCommands({ anchor })) sets it — normally to the focus-driven active scope (an ActiveScope the consumer reads), or to a context menu's snapshot. Returns undefined when no dynamic contribution for the key exists on the chain — a registry (static) catalog entry alone does not resolve (see the "static only if dynamic first" note below). Memoized by version, per anchor.

      origin (keystroke dispatch only — buttons/display omit it) applies origin containment per contribution: a node whose contains rejects the event's origin contributes nothing to this resolve, so the cascade falls through to the permitted portion (or to undefined when everything is omitted). See the gather-loop comment below.

      Type Parameters

      • STATE = any
      • CONTEXT = any
      • ARGS = void

      Parameters

      Returns ResolvedCommand<STATE, CONTEXT, ARGS> | undefined

    • Resolve a command purely from the static registry — the not-in-context primitive a consumer's fallback composes, deliberately bypassing the "static only if a dynamic exists" rule that governs the context resolve. Use it to show a command whose scope isn't currently contributing (a toolbar's disabled button; a table command with no table) — the result is disabled if the static has no callback, or executable if it carries a global one. prefer is the consumer's priority list of names (CommandRegistry.findByKey searches it first, then all names).

      Never call this from dispatch. The keyboard dispatcher resolves only via resolve, so an inert static never binds a shortcut (an idle editor's F4 stays free for redo). The policy — whether to fall back at all, and the prefer list — is entirely the consumer's; the core never decides it. Returns undefined if the key is registered under no name. Memoized by version.

      Type Parameters

      • STATE = any
      • CONTEXT = any
      • ARGS = void

      Parameters

      • commandKey: string
      • Optionalprefer: readonly string[]

      Returns ResolvedCommand<STATE, CONTEXT, ARGS> | undefined

    • Re-attach this scope under a new parent (or detach with null). The primitive the z-ordered layer chain is built from: a layer registry re-parents each layer's scope into a stack ordered by z (higher z → deeper → wins the cascade), so the unchanged resolve (which walks _parent from the anchor) resolves top-of-stack first. This is what lets a stacked layer override a command by stacking, not by focus — and lets a client add a command-bearing layer with no core special-casing.

      Reorders within the same root (the layer stack shares one registry root); _root is fixed at construction, so cross-root moves are rejected. Rejects a cycle. Bumps the root so resolvers re-resolve. No-op if parent is already the parent.

      Parameters

      Returns void

    • Contribute/merge a partial definition for commandKey into THIS scope (within-scope merge). undefined fields are ignored (don't clobber); null is kept (an explicit clear). The cross-scope cascade happens later, in resolve.

      Type Parameters

      • STATE = any
      • CONTEXT = any
      • ARGS = void

      Parameters

      Returns void

    • Register dispatch CommandMiddleware on THIS scope: it auto-wraps every callback this scope authors (so contributor callbacks stay pure — transactional wrapping becomes one use(tx) instead of wrapping each command by hand). Not inherited — a command's middleware is that of the scope whose callback wins resolution, nothing up the chain. First-registered is outermost. Returns a remover.

      Parameters

      Returns RemoveListener