@svelte-put/async-stack
Advanced tools
Comparing version 0.0.0-next.1 to 1.0.0-next.1
{ | ||
"name": "@svelte-put/async-stack", | ||
"version": "0.0.0-next.1", | ||
"version": "1.0.0-next.1", | ||
"description": "type-safe and headless builder for async component stack", | ||
@@ -58,3 +58,3 @@ "type": "module", | ||
"peerDependencies": { | ||
"svelte": "^5.0.0-next.160" | ||
"svelte": "^5.0.0-next.164" | ||
}, | ||
@@ -61,0 +61,0 @@ "volta": { |
<div align="center"> | ||
# `@svelte-put/noti` | ||
# `@svelte-put/async-stack` | ||
[![npm.badge]][npm] [![bundlephobia.badge]][bundlephobia] [![docs.badge]][docs] [![repl.badge]][repl] | ||
[![npm.badge]][npm] [![bundlephobia.badge]][bundlephobia] [![docs.badge]][docs] | ||
Type-safe and headless async notification builder | ||
Type-safe and headless async builder for async component stack (notification and modal/dialog systems, for example) | ||
@@ -23,11 +23,11 @@ </div> | ||
<script lang="ts"> | ||
import { controller } from '@svelte-put/noti'; | ||
import { stack } from '@svelte-put/async-stack'; | ||
// any Svelte component to render as notification | ||
import Notification from './Notification.svelte'; | ||
// any Svelte component to render as | ||
import MyComponent from './MyComponent.svelte'; | ||
// define somewhere global, reuse across app | ||
export const notiCtrl = controller() | ||
export const myStack = stack() | ||
// add a minimalistic variant config | ||
.addVariant('info', Notification) | ||
.addVariant('my', MyComponent) | ||
// add a verbose variant config | ||
@@ -37,22 +37,22 @@ .addVariant('special', { | ||
id: 'counter', | ||
component: Notification, | ||
component: MyComponent, | ||
props: { | ||
// inferred from Notification component | ||
special: true, | ||
content: 'A very special notification', | ||
content: 'A very special thing', | ||
}, | ||
}) | ||
// build the actual NotificationController | ||
// build the actual stack | ||
.build(); | ||
onMount(async () => { | ||
// push a special notification | ||
const pushed = notiStore.push('special'); | ||
// push a special item | ||
const pushed = myStack.push('special'); | ||
// wait for some user action for the notification | ||
// wait for some user action for the item | ||
// to be resolved (popped) from within the component | ||
const { userAction } = await pushed.resolution; | ||
// push another notification with custom props | ||
notiStore.push('info', { | ||
// push another item with custom props | ||
notiStack.push('info', { | ||
props: { | ||
@@ -65,7 +65,7 @@ content: 'An example information', | ||
<!-- notification portal, typically setup at somewhere global like root layout --> | ||
<!-- portal -> | ||
<aside class="applicable class"> | ||
{#each notiCtrl.notifications as notification (notification.config.id)} | ||
<div use:notiCtrl.actions.render={notification}> | ||
<!-- notification instances rendered as direct children --> | ||
{#each myStack.items as item (item.config.id)} | ||
<div use:myStack.actions.render={item}> | ||
<!-- StackItem instances rendered as direct children --> | ||
</div> | ||
@@ -81,3 +81,3 @@ {/each} | ||
[github.monorepo]: https://github.com/vnphanquang/svelte-put | ||
[github.changelog]: https://github.com/vnphanquang/svelte-put/blob/main/packages/noti/CHANGELOG.md | ||
[github.changelog]: https://github.com/vnphanquang/svelte-put/blob/main/packages/async-stack/CHANGELOG.md | ||
[github.issues]: https://github.com/vnphanquang/svelte-put/issues?q= | ||
@@ -87,9 +87,8 @@ | ||
[npm.badge]: https://img.shields.io/npm/v/@svelte-put/noti | ||
[npm]: https://www.npmjs.com/package/@svelte-put/noti | ||
[bundlephobia.badge]: https://img.shields.io/bundlephobia/minzip/@svelte-put/noti?label=minzipped | ||
[bundlephobia]: https://bundlephobia.com/package/@svelte-put/noti | ||
[repl]: https://svelte.dev/repl/5beb4357e32e427394f5f6f5ced7b5f1 | ||
[repl.badge]: https://img.shields.io/static/v1?label=&message=Svelte+REPL&logo=svelte&logoColor=fff&color=ff3e00 | ||
[docs]: https://svelte-put.vnphanquang.com/docs/noti | ||
[npm.badge]: https://img.shields.io/npm/v/@svelte-put/async-stack | ||
[npm]: https://www.npmjs.com/package/@svelte-put/async-stack | ||
[bundlephobia.badge]: https://img.shields.io/bundlephobia/minzip/@svelte-put/async-stack?label=minzipped | ||
[bundlephobia]: https://bundlephobia.com/package/@svelte-put/async-stack | ||
[docs]: https://svelte-put.vnphanquang.com/docs/async-stack | ||
[docs.badge]: https://img.shields.io/badge/-Docs%20Site-blue | ||
// Copyright (c) Quang Phan. All rights reserved. Licensed under the MIT license. | ||
export { stack, StackBuilder } from './stack-builder.js' | ||
export { stack } from './stack-builder.js'; | ||
export { Stack } from './stack.svelte.js'; | ||
export { StackItem } from './stack-item.svelte.js'; | ||
export { Stack } from './stack.svelte.js'; | ||
export * from './types.public.js'; | ||
import { Stack } from './stack.svelte.js'; | ||
/** | ||
* @template {Record<string, import('svelte').Component>} [VariantMap={}] | ||
* @template {Record<string, import('svelte').Component<any>>} [VariantMap={}] | ||
*/ | ||
export class StackBuilder { | ||
/** @type {Record<string, import('./types').StackItemVariantConfig<any, any, any>>} */ | ||
/** @type {Record<string, import('./types.package').StackItemVariantConfig<string, import('svelte').Component<any>>>} */ | ||
#variantConfigMap = {}; | ||
/** | ||
* @type {import('./types').StackItemCommonConfig<any, any, any> | undefined} | ||
* @type {import('./types.package').StackItemCommonConfig<string, import('svelte').Component<any>> | undefined} | ||
*/ | ||
@@ -16,3 +16,3 @@ #init; | ||
/** | ||
* @param {import('./types').StackItemCommonConfig<any, any, any>} [init] | ||
* @param {import('./types.package').StackItemCommonConfig<string, import('svelte').Component<any>>} [init] | ||
*/ | ||
@@ -25,7 +25,6 @@ constructor(init) { | ||
* add config for a stack item variant | ||
* @template Resolved | ||
* @template {string} Variant | ||
* @template {import('svelte').Component} UserComponent | ||
* @template {import('svelte').Component<any>} UserComponent | ||
* @param {Variant} variant | ||
* @param {UserComponent | Omit<import('./types').StackItemVariantConfig<Resolved, Variant, UserComponent>, 'variant'>} config | ||
* @param {UserComponent | Omit<import('./types.package').StackItemVariantConfig<Variant, UserComponent>, 'variant'>} config | ||
* @returns {StackBuilder<VariantMap & Record<Variant, UserComponent>> } | ||
@@ -58,3 +57,3 @@ */ | ||
/** | ||
* @param {import('./types').StackItemCommonConfig<any, any, any>} [init] | ||
* @param {import('./types.package').StackItemCommonConfig<string, import('svelte').Component>} [init] | ||
* @returns {StackBuilder} | ||
@@ -61,0 +60,0 @@ */ |
/** | ||
* @template Resolved | ||
* @template {import('svelte').Component} [UserComponent=import('svelte').Component<import('./types.d.ts').StackItemProps<Resolved>>] | ||
* @template {import('svelte').Component<any>} [UserComponent=import('svelte').Component<any>] | ||
* @template [Resolved=import('./types.public').ComponentResolved<UserComponent>] | ||
*/ | ||
export class StackItem { | ||
/** @type {import('./types.d.ts').StackItemState} */ | ||
/** @type {import('./types.package').StackItemState} */ | ||
// eslint-disable-next-line no-undef | ||
state = $state('idle'); | ||
/** @type {Required<import('./types.d.ts').StackItemInstanceConfig<Resolved, string, UserComponent>>} */ | ||
/** @type {Required<import('./types.package').StackItemInstanceConfig<string, UserComponent>>} */ | ||
config; | ||
@@ -28,3 +28,3 @@ | ||
/** | ||
* @param {Required<import('./types.d.ts').StackItemInstanceConfig<Resolved, string, UserComponent>>} config | ||
* @param {Required<import('./types.package').StackItemInstanceConfig<string, UserComponent>>} config | ||
*/ | ||
@@ -50,3 +50,3 @@ constructor(config) { | ||
this.state = 'elapsing'; | ||
} | ||
}; | ||
@@ -58,3 +58,3 @@ pause = () => { | ||
this.state = 'paused'; | ||
} | ||
}; | ||
@@ -70,4 +70,4 @@ /** | ||
return this.resolution; | ||
} | ||
}; | ||
} | ||
@@ -7,6 +7,6 @@ import { mount } from 'svelte'; | ||
/** | ||
* @template {Record<string, import('svelte').Component>} [VariantMap={}] | ||
* @template {Record<string, import('svelte').Component<any>>} [VariantMap={}] | ||
*/ | ||
export class Stack { | ||
/** @type {Record<string, import('./types.d.ts').StackItemVariantConfig<any, any, any>>} */ | ||
/** @type {Record<string, import('./types.package').StackItemVariantConfig<string, import('svelte').Component<any>>>} */ | ||
#variantConfigMap = {}; | ||
@@ -23,3 +23,3 @@ #counter = 0; | ||
/** | ||
* @type {Required<import('./types.d.ts').StackItemCommonConfig<any, any, any>>} | ||
* @type {Required<import('./types.package').StackItemCommonConfig<string, import('svelte').Component<any>>>} | ||
*/ | ||
@@ -37,3 +37,3 @@ // eslint-disable-next-line no-undef | ||
* @param {StackItem<any>} item | ||
* @returns {import('./types.d.ts').StackItemRenderActionReturn} | ||
* @returns {import('./types.package').StackItemRenderActionReturn} | ||
*/ | ||
@@ -54,4 +54,4 @@ render: (node, item) => { | ||
/** | ||
* @param {Record<keyof VariantMap, import('./types.d.ts').StackItemVariantConfig<any, any, any>>} variantConfigMap | ||
* @param {import('./types.d.ts').StackItemCommonConfig<any, any, any>} [init] | ||
* @param {Record<keyof VariantMap, import('./types.package').StackItemVariantConfig<string, import('svelte').Component>>} variantConfigMap | ||
* @param {import('./types.package').StackItemCommonConfig<string, import('svelte').Component<any>>} [init] | ||
*/ | ||
@@ -64,23 +64,23 @@ constructor(variantConfigMap, init) { | ||
/** | ||
* @template {Extract<keyof VariantMap, string>} Variant | ||
* @template {VariantMap[Variant]} [UserComponent=VariantMap[Variant]] | ||
* @template [Resolved=undefined|Awaited<import('svelte').ComponentProps<UserComponent>['item']['resolution']>] | ||
* @overload | ||
* @param {Variant} variant | ||
* @param {import('./types.d.ts').StackItemByVariantPushConfig<Resolved, Variant, UserComponent>} [config] | ||
* @returns {StackItem<Resolved>} | ||
* @param {import('./types.package').StackItemByVariantPushConfig<Variant, UserComponent>} [config] | ||
* @returns {StackItem<UserComponent>} | ||
*/ | ||
/** | ||
* @template {import('svelte').Component} UserComponent | ||
* @template [Resolved=undefined|Awaited<import('svelte').ComponentProps<UserComponent>['item']['resolution']>] | ||
* @template {Extract<keyof VariantMap, string>} Variant | ||
* @template {VariantMap[Variant]} [UserComponent=VariantMap[Variant]] | ||
* @overload | ||
* @param {'custom'} variant | ||
* @param {import('./types.d.ts').StackItemCustomPushConfig<Resolved, UserComponent>} config | ||
* @returns {StackItem<Resolved>} | ||
* @param {import('./types.package').StackItemCustomPushConfig<UserComponent>} config | ||
* @returns {StackItem<UserComponent>} | ||
*/ | ||
/** | ||
* @param {string} variant | ||
* @param {import('./types.d.ts').StackItemByVariantPushConfig<any, string, import('svelte').Component> | import('./types.d.ts').StackItemCustomPushConfig<any, import('svelte').Component>} [config] | ||
* @template {Extract<keyof VariantMap, string>} Variant | ||
* @template {VariantMap[Variant]} [UserComponent=VariantMap[Variant]] | ||
* @param {Variant} variant | ||
* @param {import('./types.package').StackItemByVariantPushConfig<Variant, UserComponent> | import('./types.package').StackItemCustomPushConfig<UserComponent>} [config] | ||
* @returns {StackItem<any>} | ||
@@ -90,5 +90,5 @@ */ | ||
// STEP 1: resolve instance config, merge with common config and variant config, if any | ||
/** @type {import('./types.d.ts').StackItemInstanceConfig<any, any, any>} */ | ||
/** @type {import('./types.package').StackItemInstanceConfig<string, import('svelte').Component<any>>} */ | ||
let instanceConfig; | ||
/** @type {NonNullable<import('./types.d.ts').StackItemCommonConfig<Resolved, string, import('svelte').Component>['id']>} */ | ||
/** @type {NonNullable<import('./types.package').StackItemCommonConfig<string, import('svelte').Component>['id']>} */ | ||
let idResolver; | ||
@@ -98,3 +98,3 @@ | ||
const rConfig = | ||
/** @type {import('./types.d.ts').StackItemCustomPushConfig<any, any>} */ ( | ||
/** @type {import('./types.package').StackItemCustomPushConfig<import('svelte').Component<any>>} */ ( | ||
config | ||
@@ -161,17 +161,19 @@ ); | ||
/** | ||
* @template {import('svelte').Component<any>} [UserComponent=import('svelte').Component] | ||
* @overload | ||
* @param {string} [id] | ||
* @param {any} [detail] | ||
* @returns {void} | ||
* @returns {StackItem<UserComponent> | null} | ||
*/ | ||
/** | ||
* @template {import('svelte').Component<any>} [UserComponent=import('svelte').Component] | ||
* @overload | ||
* @param {import('./types.d.ts').StackItemPopVerboseInput} [config] | ||
* @returns {void} | ||
* @param {import('./types.package').StackItemPopVerboseInput<UserComponent>} [config] | ||
* @returns {StackItem<UserComponent> | null} | ||
*/ | ||
/** | ||
* | ||
* @param {string | import('./types.d.ts').StackItemPopVerboseInput} [config] | ||
* @template {import('svelte').Component<any>} [UserComponent=import('svelte').Component] | ||
* @param {string | import('./types.package').StackItemPopVerboseInput<UserComponent>} [config] | ||
* @param {any} [resolved] | ||
* @returns {void} | ||
* @returns {StackItem<UserComponent> | null} | ||
*/ | ||
@@ -190,8 +192,8 @@ pop(config, resolved) { | ||
/** @type {StackItem<any> | undefined} */ | ||
let pushed; | ||
/** @type {StackItem<UserComponent> | null} */ | ||
let pushed = null; | ||
if (id) { | ||
pushed = this.items.find((n) => n.config.id === id); | ||
pushed = this.items.find((n) => n.config.id === id) ?? null; | ||
} else { | ||
pushed = this.items.at(-1); | ||
pushed = this.items.at(-1) ?? null; | ||
} | ||
@@ -203,2 +205,4 @@ | ||
} | ||
return pushed; | ||
} | ||
@@ -205,0 +209,0 @@ |
declare module '@svelte-put/async-stack' { | ||
import type { Component, ComponentProps } from 'svelte'; | ||
import type { ActionReturn } from 'svelte/action'; | ||
class StackItem<Resolved, UserComponent extends import("svelte").Component = import("svelte").Component<StackItemProps<Resolved>, any, string>> { | ||
function stack(init?: StackItemCommonConfig<string, import("svelte").Component<any, any, string>> | undefined): StackBuilder; | ||
class StackBuilder<VariantMap extends Record<string, import("svelte").Component<any>> = {}> { | ||
constructor(config: Required<StackItemInstanceConfig<Resolved, string, UserComponent>>); | ||
state: StackItemState; | ||
config: Required<StackItemInstanceConfig<Resolved, string, UserComponent>>; | ||
constructor(init?: StackItemCommonConfig<string, import("svelte").Component<any, any, string>> | undefined); | ||
/** | ||
* a promise that resolves when the item is popped or resolved via the .resolve method | ||
* add config for a stack item variant | ||
* */ | ||
resolution: Promise<Resolved | undefined>; | ||
resume: () => void; | ||
pause: () => void; | ||
resolve: (resolved?: Resolved | undefined) => Promise<Resolved | undefined>; | ||
addVariant<Variant extends string, UserComponent extends import("svelte").Component<any>>(variant: Variant, config: UserComponent | Omit<StackItemVariantConfig<Variant, UserComponent>, "variant">): StackBuilder<VariantMap & Record<Variant, UserComponent>>; | ||
/** | ||
* Build the actual stack | ||
* */ | ||
build(): Stack<VariantMap>; | ||
#private; | ||
} | ||
class Stack<VariantMap extends Record<string, import("svelte").Component> = {}> { | ||
class Stack<VariantMap extends Record<string, import("svelte").Component<any>> = {}> { | ||
constructor(variantConfigMap: Record<keyof VariantMap, StackItemVariantConfig<any, any, any>>, init?: StackItemCommonConfig<any, any, any> | undefined); | ||
constructor(variantConfigMap: Record<keyof VariantMap, StackItemVariantConfig<string, import("svelte").Component>>, init?: StackItemCommonConfig<string, import("svelte").Component<any, any, string>> | undefined); | ||
/** | ||
@@ -29,3 +27,3 @@ * the stack items | ||
config: Required<StackItemCommonConfig<any, any, any>>; | ||
config: Required<StackItemCommonConfig<string, import("svelte").Component<any>>>; | ||
actions: { | ||
@@ -38,9 +36,9 @@ /** | ||
push<Variant extends Extract<keyof VariantMap, string>, UserComponent extends VariantMap[Variant] = VariantMap[Variant], Resolved = Awaited<import("svelte").ComponentProps<UserComponent>["item"]["resolution"]> | undefined>(variant: Variant, config?: StackItemByVariantPushConfig<Resolved, Variant, UserComponent> | undefined): StackItem<Resolved>; | ||
push<Variant extends Extract<keyof VariantMap, string>, UserComponent extends VariantMap[Variant] = VariantMap[Variant]>(variant: Variant, config?: StackItemByVariantPushConfig<Variant, UserComponent> | undefined): StackItem<UserComponent>; | ||
push<UserComponent extends VariantMap[Variant] = VariantMap[Variant], Resolved = Awaited<import("svelte").ComponentProps<UserComponent>["item"]["resolution"]> | undefined>(variant: "custom", config: StackItemCustomPushConfig<Resolved, UserComponent>): StackItem<Resolved>; | ||
push<Variant extends Extract<keyof VariantMap, string>, UserComponent extends VariantMap[Variant] = VariantMap[Variant]>(variant: "custom", config: StackItemCustomPushConfig<UserComponent>): StackItem<UserComponent>; | ||
pop(id?: string | undefined, detail?: any): void; | ||
pop<UserComponent extends import("svelte").Component<any> = import("svelte").Component<any, any, string>>(id?: string | undefined, detail?: any): StackItem<UserComponent> | null; | ||
pop(config?: StackItemPopVerboseInput | undefined): void; | ||
pop<UserComponent extends import("svelte").Component<any> = import("svelte").Component<any, any, string>>(config?: StackItemPopVerboseInput<UserComponent> | undefined): StackItem<UserComponent> | null; | ||
/** | ||
@@ -56,21 +54,31 @@ * pause a stack item, if it has a timeout | ||
} | ||
function stack(init?: StackItemCommonConfig<any, any, any> | undefined): StackBuilder; | ||
class StackBuilder<VariantMap extends Record<string, import("svelte").Component> = {}> { | ||
class StackItem<UserComponent extends import("svelte").Component<any> = import("svelte").Component<any, any, string>, Resolved = ComponentResolved<UserComponent>> { | ||
constructor(init?: StackItemCommonConfig<any, any, any> | undefined); | ||
constructor(config: Required<StackItemInstanceConfig<string, UserComponent>>); | ||
state: StackItemState; | ||
config: Required<StackItemInstanceConfig<string, UserComponent>>; | ||
/** | ||
* add config for a stack item variant | ||
* a promise that resolves when the item is popped or resolved via the .resolve method | ||
* */ | ||
addVariant<Resolved, Variant extends string, UserComponent extends import("svelte").Component>(variant: Variant, config: UserComponent | Omit<StackItemVariantConfig<Resolved, Variant, UserComponent>, "variant">): StackBuilder<VariantMap & Record<Variant, UserComponent>>; | ||
/** | ||
* Build the actual stack | ||
* */ | ||
build(): Stack<VariantMap>; | ||
resolution: Promise<Resolved | undefined>; | ||
resume: () => void; | ||
pause: () => void; | ||
resolve: (resolved?: Resolved | undefined) => Promise<Resolved | undefined>; | ||
#private; | ||
} | ||
interface StackItemProps<Resolved> { | ||
/** the stack item instance injected by the stack */ | ||
item: Omit<StackItem<Component<StackItemProps<Resolved>>, Resolved>, '#internals'>; | ||
} | ||
type ComponentResolved<UserComponent extends Component<any>> = | ||
ComponentProps<UserComponent> extends { item: { resolution: Promise<infer Resolved> } } | ||
? Resolved | ||
: any; | ||
type StackItemCommonConfig< | ||
Resolved, | ||
Variant extends string, | ||
UserComponent extends Component, | ||
UserComponent extends Component<any>, | ||
> = { | ||
@@ -93,17 +101,11 @@ /** | ||
| (( | ||
config: Required<Omit<StackItemInstanceConfig<Resolved, Variant, UserComponent>, 'id'>>, | ||
config: Required<Omit<StackItemInstanceConfig<Variant, UserComponent>, 'id'>>, | ||
) => string); | ||
}; | ||
type StackItemProps<Resolved> = { | ||
/** the stack item instance injected by the stack */ | ||
item: Omit<StackItem<Resolved>, '#internals'>; | ||
}; | ||
/** predefined variant config provided while building a {@link Stack} instance */ | ||
type StackItemVariantConfig< | ||
Resolved, | ||
Variant extends string, | ||
UserComponent extends Component, | ||
> = StackItemCommonConfig<Resolved, Variant, UserComponent> & { | ||
UserComponent extends Component<any>, | ||
> = StackItemCommonConfig<Variant, UserComponent> & { | ||
/** string variant representing this config, must be unique within a {@link Stack} instance */ | ||
@@ -119,6 +121,5 @@ variant: Variant; | ||
type StackItemInstanceConfig< | ||
Resolved, | ||
Variant extends string, | ||
UserComponent extends Component, | ||
> = Required<Omit<StackItemVariantConfig<Resolved, Variant, UserComponent>, 'id'>> & { | ||
UserComponent extends Component<any>, | ||
> = Required<Omit<StackItemVariantConfig<Variant, UserComponent>, 'id'>> & { | ||
id: string; | ||
@@ -131,6 +132,5 @@ timeout: number; | ||
type StackItemByVariantPushConfig< | ||
Resolved, | ||
Variant extends string, | ||
UserComponent extends Component, | ||
> = StackItemCommonConfig<Resolved, Variant, UserComponent> & { | ||
UserComponent extends Component<any>, | ||
> = StackItemCommonConfig< Variant, UserComponent> & { | ||
props?: Omit<ComponentProps<UserComponent>, 'item'>; | ||
@@ -140,5 +140,4 @@ }; | ||
type StackItemCustomPushConfig< | ||
Resolved, | ||
UserComponent extends Component, | ||
> = StackItemCommonConfig<Resolved, 'custom', UserComponent> & { | ||
UserComponent extends Component<any>, | ||
> = StackItemCommonConfig<'custom', UserComponent> & { | ||
component: UserComponent; | ||
@@ -148,5 +147,7 @@ props?: Omit<ComponentProps<UserComponent>, 'item'>; | ||
type StackItemPopVerboseInput = { | ||
type StackItemPopVerboseInput < | ||
UserComponent extends Component<any>, | ||
>= { | ||
id?: string; | ||
resolved?: any; | ||
resolved?: ComponentResolved<UserComponent>; | ||
}; | ||
@@ -156,5 +157,5 @@ | ||
export { StackItem, Stack, stack, StackBuilder }; | ||
export { stack, Stack, StackItem, StackItemProps, ComponentResolved }; | ||
} | ||
//# sourceMappingURL=index.d.ts.map |
Sorry, the diff of this file is not supported yet
24853
12
545
90