@oruga-ui/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
1 lines • 13.3 kB
Source Map (JSON)
{"version":3,"file":"useProgrammatic-B50-6K60.mjs","sources":["../../src/components/programmatic/InstanceRegistry.ts","../../src/components/programmatic/ProgrammaticComponent.ts","../../src/components/programmatic/useProgrammatic.ts"],"sourcesContent":["import type { ComponentInternalInstance } from \"vue\";\n\nexport default class InstanceRegistry<T = ComponentInternalInstance> {\n entries: Array<T>;\n\n constructor() {\n this.entries = [];\n }\n\n /** Returns the number of registered active instances. */\n count(): number {\n return this.entries.length;\n }\n\n /** Returns the first registered active instance. */\n fist(): T | undefined {\n return this.entries.at(0);\n }\n\n /** Returns the last registered active instance. */\n last(): T | undefined {\n return this.entries.at(-1);\n }\n\n /** Adds a new instance to the instance stack. */\n add(entry: T): void {\n this.entries.push(entry);\n }\n\n /** Removes an instance from the instance stack. */\n remove(entry: T): void {\n const index = this.entries.indexOf(entry);\n this.entries.splice(index, 1);\n }\n\n /** Call a function for every registered active instance. */\n walk(callback: (value: T) => boolean | void): void {\n // Walk a copy of the array so that the callback is allowed to remove the instance\n this.entries = [...this.entries].filter((e) => {\n const ret = callback(e);\n return !(ret === true);\n });\n }\n}\n","import {\n createVNode,\n defineComponent,\n getCurrentInstance,\n onMounted,\n onUnmounted,\n type ComponentInternalInstance,\n type VNode,\n type VNodeTypes,\n} from \"vue\";\nimport type { ComponentProps } from \"vue-component-type-helpers\";\nimport type InstanceRegistry from \"@/components/programmatic/InstanceRegistry\";\nimport { isClient } from \"@/utils/ssr\";\n\nexport type ProgrammaticComponentProps<C extends VNodeTypes> = {\n /**\n * Component to be injected.\n * Terminate the component by emitting a 'close' event — emits('close')\n */\n component: C;\n /**\n * Props to be binded to the injected component.\n * Both attributes and properties can be used in props.\n * Vue automatically picks the right way to assign it.\n * `class` and `style` have the same object / array value support like in templates.\n * Event listeners should be passed as onXxx.\n * @see https://vuejs.org/api/render-function.html#h\n */\n props?: ComponentProps<C> | { container?: HTMLElement };\n /** Programmatic component registry instance */\n registry?: InstanceRegistry<ComponentInternalInstance>;\n};\n\nexport type CloseEventArgs<T extends VNodeTypes> =\n Parameters<ComponentProps<T>[\"onClose\"]> extends never\n ? unknown[]\n : Parameters<ComponentProps<T>[\"onClose\"]>;\n\nexport type ProgrammaticComponentEmits<C extends VNodeTypes> = {\n /**\n * On component close event.\n * This get called when the component emits `close` or the exposed `close` function get called.\n */\n close?: (...args: CloseEventArgs<C>) => void;\n /** On component destroy event which get called when the component should be destroyed. */\n destroy?: () => void;\n};\n\n// there is a bug with functional defineComponent and extracting the exposed type\n// export type ProgrammaticComponentExpose = ComponentExposed<\n// typeof ProgrammaticComponent\n// >;\n\nexport type ProgrammaticComponentExpose<C extends VNodeTypes> = {\n /** Call the close event of the component. */\n close: (...args: CloseEventArgs<C>) => void;\n /** Promise which get resolved on the close event. */\n promise: Promise<CloseEventArgs<C>>;\n};\n\nexport const ProgrammaticComponent = defineComponent<\n ProgrammaticComponentProps<VNodeTypes>,\n ProgrammaticComponentEmits<VNodeTypes>\n>(\n <C extends VNodeTypes>(\n props: ProgrammaticComponentProps<C>,\n { expose, emit, slots },\n ) => {\n // getting a hold of the internal instance in setup()\n const vm = getCurrentInstance();\n if (!vm)\n throw new Error(\"ProgrammaticComponent initialisation failed.\");\n\n // create response promise\n let resolve: (args: CloseEventArgs<C>) => void;\n const promise = new Promise<CloseEventArgs<C>>((p1) => (resolve = p1));\n\n // add component instance to instance register\n onMounted(() => props.registry?.add(vm));\n\n // remove component instance from instance register\n onUnmounted(() => props.registry?.remove(vm));\n\n function close(...args: CloseEventArgs<C>): void {\n // emit `onClose` event\n emit(\"close\", ...args);\n\n // call promise resolve\n resolve(args);\n\n // emit `destory` event after animation is finished\n setTimeout(() => {\n if (isClient)\n window.requestAnimationFrame(() => emit(\"destroy\"));\n else emit(\"destroy\");\n });\n }\n\n /** expose public functionalities for programmatic usage */\n expose({ close, promise } satisfies ProgrammaticComponentExpose<C>);\n\n // return render function which renders given component\n return (): VNode =>\n createVNode(\n props.component,\n { ...props.props, onClose: close },\n slots[\"default\"],\n );\n },\n {\n name: \"ProgrammaticApp\",\n // manual runtime props declaration is currently still needed.\n props: [\"component\", \"props\", \"registry\"],\n // manual runtime emits declaration\n emits: [\"close\", \"destroy\"],\n // manual runtime slot declaration\n slots: [\"default\"],\n },\n);\n","import {\n createApp,\n toValue,\n type App,\n type ComponentInternalInstance,\n type EmitsToProps,\n type MaybeRefOrGetter,\n type VNode,\n type VNodeTypes,\n} from \"vue\";\n\nimport InstanceRegistry from \"@/components/programmatic/InstanceRegistry\";\nimport { VueInstance } from \"@/utils/plugins\";\nimport { useTeleportDefault, resolveElement } from \"@/composables\";\n\nimport {\n ProgrammaticComponent,\n type ProgrammaticComponentProps,\n type ProgrammaticComponentEmits,\n type ProgrammaticComponentExpose,\n} from \"./ProgrammaticComponent\";\n\ndeclare module \"../../index\" {\n interface OrugaProgrammatic {\n programmatic: typeof ComponentProgrammatic;\n }\n}\n\n/** programmatic global instance registry if no custom is defined */\nconst registry = new InstanceRegistry<ComponentInternalInstance>();\n\n/** useProgrammatic composable `open` function options */\nexport type ProgrammaticOptions<C extends VNodeTypes> = {\n /**\n * Specify a target the component get rendered into.\n * @default `document.body`\n */\n target?: MaybeRefOrGetter<string | HTMLElement | null>;\n /**\n * Specify the template `id` for the programmatic container element.\n * @default `programmatic-app`\n */\n appId?: string;\n} & Omit<ProgrammaticComponentProps<C>, \"component\"> & // component props\n EmitsToProps<Omit<ProgrammaticComponentEmits<C>, \"destroy\">>; // component emit props\n\n/** public options interface for programmatically called components */\nexport type ProgrammaticComponentOptions<C extends VNodeTypes> = EmitsToProps<\n Pick<ProgrammaticComponentEmits<C>, \"close\">\n> &\n // make the type extendable\n Record<string, any>;\n\n/** useProgrammatic composable `open` function return value */\nexport type ProgrammaticExpose<C extends VNodeTypes = VNode> =\n ProgrammaticComponentExpose<C>;\n\nexport const ComponentProgrammatic = {\n /** Returns the number of registered active instances. */\n count: registry.count,\n /**\n * Create a new programmatic component instance.\n * @param component component to render\n * @param options render options\n */\n open<C extends VNodeTypes>(\n component: C,\n options?: ProgrammaticOptions<C>,\n ): ProgrammaticExpose<C> {\n options = { registry, ...options };\n\n const targetQuery = toValue(options.target);\n // define the target container\n const target: HTMLElement | null =\n // either by a given query selector / element\n (targetQuery && resolveElement(targetQuery)) ||\n // or by the default teleport target config\n resolveElement(useTeleportDefault());\n if (!target)\n throw new Error(\"ComponentProgrammatic - no target is defined.\");\n\n // create app container\n let container: HTMLDivElement | undefined =\n document.createElement(\"div\");\n container.id = options.appId || \"programmatic-app\";\n\n // place the app container into the target element\n target.appendChild(container);\n\n // clear instance handler\n function onDestroy(): void {\n // destroy app/component\n if (app) {\n app.unmount();\n app = undefined;\n }\n // clear container\n if (container && target) {\n target.removeChild(container);\n container = undefined;\n }\n }\n\n // create a new vue app instance with the ProgrammaticComponent as root\n let app: App | undefined = createApp(ProgrammaticComponent, {\n registry: options.registry, // programmatic registry instance - can be overriden by given in options\n component, // the component which should be rendered\n props: { ...options.props, container: target }, // component props including the target as `container`\n onClose: options.onClose, // custom onClose handler\n onDestroy, // node destory cleanup handler\n });\n\n // share the current context to the new app instance if running inside a nother app\n if (VueInstance)\n app._context = Object.assign(app._context, VueInstance._context);\n\n // render the new vue instance into the container\n const instance = app.mount(container);\n\n // return exposed programmatic functionalities from the mounted component instance\n return instance as unknown as ProgrammaticExpose<C>;\n },\n /** close the last registred instance in the global programmatic instance registry */\n close(...args: unknown[]): void {\n registry.last()?.exposed?.close(...args);\n },\n /** close all instances in the global programmatic instance registry */\n closeAll(...args: unknown[]): void {\n registry.walk((entry) => entry.exposed?.close(...args));\n },\n};\n\nexport default ComponentProgrammatic;\n"],"names":[],"mappings":"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"}