import { User } from '../application'; import { LimeWebComponentContext, LimeWebComponentPlatform } from '../core'; import { LimeObject, LimeObjectDraft } from '../limeobject'; import { Action } from '../action/action'; /** * The catalog of context entries available to consumers. * * A *context entry* is ambient state available to consumers anywhere in * the component tree, the current user, the object on screen, the * action being filtered. Each key here is a context id; the value type * is what {@link ContextRegistry.get | ContextRegistry.get(key)} (or any * other lookup such as {@link ContextScope.get | ContextScope.get}) * returns for it. * * Plugins add their own keys by extending this interface. * * Use namespaced keys (`mypkg.foo`, `pluginx.bar`) for plugin-added * entries. Two plugins declaring the same key with different value * types will collide at compile time. The built-in keys are * first-party and intentionally use short, unnamespaced ids. The * namespacing convention only applies to non-built-in keys. * * @example Add a plugin context entry * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * // Written once, by the plugin that introduces the entry. TypeScript merges * // these blocks, so each plugin declares only its own keys. * * interface PipelinePosition { * stage: string; * index: number; * } * * declare module '@limetech/lime-web-components' { * interface ContextMap { * 'pluginx.pipelinePosition': PipelinePosition; * } * } * * const registry = platform.get(PlatformServiceName.ContextRegistry); * * // The new key is now typed everywhere, so `position` is a PipelinePosition. * const position = registry.get('pluginx.pipelinePosition'); * ``` * * @see {@link ContextRegistry} for the runtime API. * @see {@link ContextMetadata} for the registration metadata. * * @beta * @group Context */ export interface ContextMap { /** * The currently authenticated {@link User}. */ user: User; /** * The persisted {@link LimeObject} currently in context, as it is * stored on the server. Unsaved form edits never reach this entry; * they are published separately via * {@link ContextMap.limeobjectDraft | limeobjectDraft}. */ limeobject: LimeObject; /** * The {@link Action} currently in context. */ action: Action; /** * The current coworker, the business-side {@link LimeObject} row * linked from {@link User.coworker}, distinct from the * authentication-side {@link User}. Filter primitives substitute * this value for `$me` placeholders. * * App-global in scope, one per session, supplied by the host. The * provider returns `undefined` until the value has loaded; reads * during that window are fail-closed. */ coworker: LimeObject; /** * The in-memory, possibly-edited version of * {@link ContextMap.limeobject | limeobject}: the persisted row * overlaid with the user's unsaved form edits, distinct from the * persisted row supplied via `limeobject`. While creating an object * there is no persisted row, so the draft holds the form state * alone and has no id. * * Surface-scoped: the form component contributes the value via * {@link ContextRegistry.provide | provide}. Reads outside a form * surface fail closed (`undefined`). */ limeobjectDraft: LimeObjectDraft; /** * The application's {@link LimeWebComponentPlatform}. App-global * in scope, one per session. */ platform: LimeWebComponentPlatform; /** * The component's {@link LimeWebComponentContext}, describing * where it sits in the application. Subtree-scoped: descendants * resolve the nearest enclosing value. */ context: LimeWebComponentContext; /** * A single scalar value a component contributes for descendants to * read, an info tile exposing the figure it displays, say, so a * descendant can format, compare, or react to it. * * Subtree-scoped: descendants resolve the nearest enclosing value. * The type is kept wide so any provider can contribute whatever * scalar it has. A provider may set `null` as a present "no value" * reading; an absent value reads as `undefined`, supplied by the * read layer, so it is not part of this type. * * @see {@link ContextRegistry.get} for how absence reads as * `undefined`. */ scalarValue: string | number | boolean | null; }