@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
text/typescript
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 };