UNPKG

@stainless-code/vue-layers

Version:

Vue adapter for @stainless-code/layers — call a layer like an async function and await its response.

234 lines 10.1 kB
import { DataTag, DefaultLayerError, ErrorOf, InferValidatorOutput, LayerCallContext, LayerClient, LayerGroupOptions, LayerHandle, LayerKey, LayerOptions, LayerState, OmitKeyof, OpenLayerOptions, ResponseOf, ValidatedLayerHandle, Validator } from "@stainless-code/layers"; import { Component, PropType, Ref, SlotsType } from "vue"; export * from "@stainless-code/layers"; //#region src/index.d.ts /** * Provides a {@link LayerClient} to descendant components. * * @param client Client to provide. A new client is created when omitted. * @returns The provided client. */ declare function provideLayerClient(client?: LayerClient): LayerClient; /** * Reads the nearest {@link LayerClient} from Vue injection context. * * @returns The nearest provided client. */ declare function useLayerClient(): LayerClient; type NoValidateOptions<Opts> = Opts extends { validate: Validator<unknown>; } ? never : Opts; interface UseStackOptions<T = LayerState[]> { stack?: string; select?: (states: LayerState[]) => T; compare?: (a: T, b: T) => boolean; } interface UseLayerStateOptions<Key extends LayerKey, P = unknown, D = unknown, U = LayerState<P, ResponseOf<Key>, ErrorOf<Key>, D>[]> { key: Key; stack?: string; select?: (states: LayerState<P, ResponseOf<Key>, ErrorOf<Key>, D>[]) => U; compare?: (a: U, b: U) => boolean; } type WiredLayerHandle<P, R, E = DefaultLayerError, D = unknown, RP = unknown> = LayerHandle<P, R, E, D, RP> & { state: Readonly<Ref<LayerState<P, R, E, D>[]>>; queued: Readonly<Ref<LayerState<P, R, E, D>[]>>; top: Readonly<Ref<LayerState<P, R, E, D> | null>>; }; type WiredValidatedLayerHandle<V extends Validator<unknown>, R, E = DefaultLayerError, D = unknown, RP = unknown> = ValidatedLayerHandle<V, R, E, D, RP> & { state: Readonly<Ref<LayerState<InferValidatorOutput<V>, R, E, D>[]>>; queued: Readonly<Ref<LayerState<InferValidatorOutput<V>, R, E, D>[]>>; top: Readonly<Ref<LayerState<InferValidatorOutput<V>, R, E, D> | null>>; }; /** * Exposes a {@link LayerClient} stack as a readonly Vue ref. * * Call it inside `setup()` or an `effectScope()` so `onScopeDispose` can clean * up the stack subscription. * * @param opts Options bag: `stack`, `select`, `compare`. * @param client Client to observe. Omit it to use {@link useLayerClient}. * @returns A readonly ref of the selected stack value. * @default `stack` is `"default"`; `select` is identity; `compare` is * `Object.is`. */ declare function useStack<T = LayerState[]>(opts?: UseStackOptions<T>, client?: LayerClient): Readonly<Ref<T>>; /** Exposes a stack's queued snapshot as a readonly Vue ref. */ declare function useQueuedStack<T = LayerState[]>(opts?: UseStackOptions<T>, client?: LayerClient): Readonly<Ref<T>>; /** * Observe all mounted layers matching a key. * * A {@link DataTag} key infers its response and error types. */ declare function useLayerState<Key extends LayerKey, P = unknown, D = unknown, U = LayerState<P, ResponseOf<Key>, ErrorOf<Key>, D>[]>(opts: UseLayerStateOptions<Key, P, D, U>, client?: LayerClient): Readonly<Ref<U>>; /** Observe all queued layers matching a key. */ declare function useLayerQueuedState<Key extends LayerKey, P = unknown, D = unknown, U = LayerState<P, ResponseOf<Key>, ErrorOf<Key>, D>[]>(opts: UseLayerStateOptions<Key, P, D, U>, client?: LayerClient): Readonly<Ref<U>>; /** Wired handle: `createLayer` + reactive `state`/`queued`/`top`. */ declare function useLayer<V extends Validator<unknown>, R, E = DefaultLayerError, D = unknown, RP = unknown>(options: LayerOptions<InferValidatorOutput<V>, R, E, D, RP> & { key: LayerKey; validate: V; }, client?: LayerClient): WiredValidatedLayerHandle<V, R, E, D, RP>; declare function useLayer<P, R, E = DefaultLayerError, D = unknown, RP = unknown>(options: NoValidateOptions<LayerOptions<P, R, E, D, RP> & { key: LayerKey; }>, client?: LayerClient): WiredLayerHandle<P, R, E, D, RP>; /** Render every active layer in a stack with its registered component. */ declare const StackOutlet: import("vue").DefineComponent<import("vue").ExtractPropTypes<{ stack: { type: StringConstructor; default: string; }; rootProps: { type: null; default: undefined; }; }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{ stack: { type: StringConstructor; default: string; }; rootProps: { type: null; default: undefined; }; }>> & Readonly<{}>, { stack: string; rootProps: any; }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; interface StackSubscribeProps<T = LayerState[]> { stack?: string; selector: (states: LayerState[]) => T; } /** * Render a selected stack value through a default scoped slot (cross-adapter * parity). The slot payload is `{ value: unknown }` — `defineComponent` can't * thread the selector's return type through a slot; prefer * `useStack({ stack, select })` in `setup()` for a fully-typed value. * * @example * ```vue * <StackSubscribe :selector="(s) => s.length"> * <template #default="{ value }">{{ value }} open</template> * </StackSubscribe> * ``` */ declare const StackSubscribe: import("vue").DefineComponent<import("vue").ExtractPropTypes<{ stack: { type: StringConstructor; default: string; }; selector: { type: PropType<(states: LayerState[]) => unknown>; required: true; }; }>, () => unknown, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{ stack: { type: StringConstructor; default: string; }; selector: { type: PropType<(states: LayerState[]) => unknown>; required: true; }; }>> & Readonly<{}>, { stack: string; }, SlotsType<{ default: (payload: { value: unknown; }) => unknown; }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; interface StackHandles { states: Readonly<Ref<LayerState[]>>; getCall: (state: LayerState) => LayerCallContext<unknown, unknown>; } /** Return the states and call contexts needed to render a stack headlessly. */ declare function useStackHandles(stack?: string, rootProps?: unknown): StackHandles; interface MutationRun<R> { /** On success, end the layer with `response`; on failure, leave it open and rethrow. */ orEnd: (response: R) => Promise<void>; } interface MutationFlow<R> { /** True while a `run(...)` async action is in flight. Mirrors the layer's `actionStatus: "running"`. */ pending: Readonly<Ref<boolean>>; run: (fn: () => Promise<void> | void) => MutationRun<R>; } /** * Coordinate a layer's pending state with an async mutation and end it on success. * * @example * ```vue * <script setup lang="ts"> * import { type LayerComponentProps, useMutationFlow } from "@stainless-code/vue-layers"; * * const props = defineProps<LayerComponentProps<void, boolean>>(); * const flow = useMutationFlow(props.call); * </script> * * <template> * <button * :disabled="flow.pending.value" * @click="flow.run(() => Promise.resolve()).orEnd(true)" * > * Confirm * </button> * </template> * ``` */ declare function useMutationFlow<P, R, RootProps = unknown>(call: LayerCallContext<P, R, RootProps>): MutationFlow<R>; /** Open a layer on a pre-bound stack, with {@link DataTag} response and error inference. */ interface ScopedOpen { <P, R, E = DefaultLayerError, D = unknown, RootProps = unknown>(options: OmitKeyof<OpenLayerOptions<P, R, E, D, RootProps> & { key: DataTag<LayerKey, R, E>; }, "stack" | "validate">): Promise<R>; <P, R = void, E = DefaultLayerError, D = unknown, RootProps = unknown>(options: OmitKeyof<OpenLayerOptions<P, R, E, D, RootProps>, "stack">): Promise<R>; } interface LayerGroup { open: ScopedOpen; dismissAll: (response?: unknown) => void; states: Readonly<Ref<LayerState[]>>; /** Renders the child stack inline — place inside the parent layer's DOM. */ Outlet: Component<{ rootProps?: unknown; }>; stackId: string; } /** * Create a child stack scoped to the calling layer's lifetime. * * The child stack is disposed and cleared via `cancelAll` when its parent * layer unmounts (`LayerCancelledError`). */ declare function useLayerGroup<P, R, RootProps = unknown>(call: LayerCallContext<P, R, RootProps>, options?: LayerGroupOptions): LayerGroup; interface AppStack { open: ScopedOpen; dismissAll: (response?: unknown) => void; states: Readonly<Ref<LayerState[]>>; } interface AppLayerProps<P, R = void> { /** Layer definition with the stack supplied by the factory. */ options: OmitKeyof<LayerOptions<P, R>, "stack">; /** Controlled visibility. `true` opens the layer; `false` dismisses it. */ open: boolean; payload: P; /** Called when the layer resolves. */ onResolved?: (response: R) => void; } interface StackHook<HostProps> { StackProvider: Component<{ client?: LayerClient; }>; useAppStack: () => AppStack; /** Host props are forwarded to `config.Host` via fallthrough attrs. */ AppHost: Component<HostProps>; AppLayer: <P, R = void>(props: AppLayerProps<P, R>) => null; } /** Create provider, host, controlled-layer, and access hooks bound to one stack. */ declare function createStackHook<HostProps extends object = object>(config?: { client?: LayerClient; stack?: string; Host?: Component<{ default?: unknown; } & HostProps>; }): StackHook<HostProps>; //#endregion export { AppLayerProps, AppStack, LayerGroup, MutationFlow, MutationRun, ScopedOpen, StackHandles, StackHook, StackOutlet, StackSubscribe, StackSubscribeProps, UseLayerStateOptions, UseStackOptions, WiredLayerHandle, WiredValidatedLayerHandle, createStackHook, provideLayerClient, useLayer, useLayerClient, useLayerGroup, useLayerQueuedState, useLayerState, useMutationFlow, useQueuedStack, useStack, useStackHandles };