diff --git a/packages/core/package.json b/packages/core/package.json index 294ac9d86..736b56661 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -71,7 +71,7 @@ "vue": "^3.3.0" }, "dependencies": { - "@vueuse/core": "^10.5.0", + "@vueuse/core": "^14.1.0", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", diff --git a/packages/core/src/composables/useWatchProps.ts b/packages/core/src/composables/useWatchProps.ts index 4dd9da6a6..d7aca8ec6 100644 --- a/packages/core/src/composables/useWatchProps.ts +++ b/packages/core/src/composables/useWatchProps.ts @@ -29,20 +29,23 @@ export function useWatchProps( let immediateStore = !!(store.nodes.value.length || store.edges.value.length) // eslint-disable-next-line prefer-const - pauseModel = watchPausable([models.modelValue, () => models.modelValue?.value?.length], ([elements]) => { - if (elements && Array.isArray(elements)) { - pauseStore?.pause() - - store.setElements(elements) - - // only trigger store watcher immediately if we actually set any elements to the store - if (!pauseStore && !immediateStore && elements.length) { - immediateStore = true - } else { - pauseStore?.resume() + pauseModel = watchPausable( + [() => models.modelValue?.value, () => models.modelValue?.value?.length] as const, + ([elements]) => { + if (elements && Array.isArray(elements)) { + pauseStore?.pause() + + store.setElements(elements) + + // only trigger store watcher immediately if we actually set any elements to the store + if (!pauseStore && !immediateStore && elements.length) { + immediateStore = true + } else { + pauseStore?.resume() + } } - } - }) + }, + ) pauseStore = watchPausable( [store.nodes, store.edges, () => store.edges.value.length, () => store.nodes.value.length], @@ -75,7 +78,7 @@ export function useWatchProps( let immediateStore = !!store.nodes.value.length // eslint-disable-next-line prefer-const - pauseModel = watchPausable([models.nodes, () => models.nodes?.value?.length], ([nodes]) => { + pauseModel = watchPausable([() => models.nodes?.value, () => models.nodes?.value?.length] as const, ([nodes]) => { if (nodes && Array.isArray(nodes)) { pauseStore?.pause() @@ -121,7 +124,7 @@ export function useWatchProps( let immediateStore = !!store.edges.value.length // eslint-disable-next-line prefer-const - pauseModel = watchPausable([models.edges, () => models.edges?.value?.length], ([edges]) => { + pauseModel = watchPausable([() => models.edges?.value, () => models.edges?.value?.length] as const, ([edges]) => { if (edges && Array.isArray(edges)) { pauseStore?.pause() diff --git a/packages/core/src/types/hooks.ts b/packages/core/src/types/hooks.ts index 03e8f1d17..dedc94fb9 100644 --- a/packages/core/src/types/hooks.ts +++ b/packages/core/src/types/hooks.ts @@ -33,8 +33,8 @@ export interface EdgeUpdateEvent { } export interface FlowEvents { - nodesChange: NodeChange[] - edgesChange: EdgeChange[] + nodesChange: [NodeChange[]] + edgesChange: [EdgeChange[]] nodeDoubleClick: NodeMouseEvent nodeClick: NodeMouseEvent nodeMouseEnter: NodeMouseEvent @@ -44,8 +44,8 @@ export interface FlowEvents { nodeDragStart: NodeDragEvent nodeDrag: NodeDragEvent nodeDragStop: NodeDragEvent - nodesInitialized: GraphNode[] - updateNodeInternals: string[] + nodesInitialized: [GraphNode[]] + updateNodeInternals: [string[]] miniMapNodeClick: NodeMouseEvent miniMapNodeDoubleClick: NodeMouseEvent miniMapNodeMouseEnter: NodeMouseEvent diff --git a/packages/core/src/utils/createExtendedEventHook.ts b/packages/core/src/utils/createExtendedEventHook.ts index 6ed1c2c6a..76eac78c9 100644 --- a/packages/core/src/utils/createExtendedEventHook.ts +++ b/packages/core/src/utils/createExtendedEventHook.ts @@ -1,13 +1,17 @@ -import type { EventHook } from '@vueuse/core' +import type { EventHook, EventHookOn, EventHookTrigger } from '@vueuse/core' import { tryOnScopeDispose } from '@vueuse/core' +// Extract VueUse's actual callback type for T (includes tuple/void/any behavior) +type HookCallback = Parameters>[0] +type HookArgs = Parameters> + export interface EventHookExtended extends EventHook { /** true if any user listeners are registered (emitter ignored) */ hasListeners: () => boolean /** current user listeners (read-only; do not mutate externally) */ - listeners: ReadonlySet<(param: T) => void> + listeners: ReadonlySet> /** wire a single external emitter (e.g., for `emit`) */ - setEmitter: (fn: (param: T) => void) => void + setEmitter: (fn: HookCallback) => void /** remove the external emitter */ removeEmitter: () => void /** wire a function to detect if any emit listeners exist (e.g., for `$listeners` in Vue 2) */ @@ -16,23 +20,21 @@ export interface EventHookExtended extends EventHook { removeHasEmitListeners: () => void } -type Handler = (param: T) => any | Promise - -const noop: Handler = () => {} +function noop(..._args: any[]) {} -export function createExtendedEventHook(defaultHandler?: (param: T) => void): EventHookExtended { - const listeners = new Set() - let emitter: Handler = noop +export function createExtendedEventHook(defaultHandler?: HookCallback): EventHookExtended { + const listeners = new Set>() + let emitter: HookCallback = noop as HookCallback let hasEmitListeners = () => false const hasListeners = () => listeners.size > 0 || hasEmitListeners() - const setEmitter = (fn: Handler) => { + const setEmitter = (fn: HookCallback) => { emitter = fn } const removeEmitter = () => { - emitter = noop + emitter = noop as HookCallback } const setHasEmitListeners = (fn: () => boolean) => { @@ -43,11 +45,11 @@ export function createExtendedEventHook(defaultHandler?: (param: T) => hasEmitListeners = () => false } - const off = (fn: Handler) => { + const off = (fn: HookCallback) => { listeners.delete(fn) } - const on = (fn: Handler) => { + const on: EventHookOn = (fn) => { listeners.add(fn) const offFn = () => off(fn) @@ -56,15 +58,12 @@ export function createExtendedEventHook(defaultHandler?: (param: T) => return { off: offFn } } - /** - * Trigger order: - * 1) If any user listeners OR an emitter exist -> call all of those (defaultHandler is skipped) - * 2) Else (no listeners and no emitter) -> call defaultHandler (if provided) - * - * Errors are isolated via allSettled so one failing handler doesn't break others. - */ - const trigger = (param: T) => { - const queue: Handler[] = [emitter] + const clear = () => { + listeners.clear() + } + + const trigger: EventHookTrigger = async (...args: HookArgs) => { + const queue: HookCallback[] = [emitter] if (hasListeners()) { queue.push(...listeners) @@ -72,13 +71,14 @@ export function createExtendedEventHook(defaultHandler?: (param: T) => queue.push(defaultHandler) } - return Promise.allSettled(queue.map((fn) => fn(param))) + return Promise.all(queue.map((fn) => fn(...args))) } return { on, off, trigger, + clear, hasListeners, listeners, setEmitter, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a793ac53..3f2626f7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,7 +95,7 @@ importers: version: 6.0.3(typedoc@0.26.11(typescript@5.4.5)) unplugin-auto-import: specifier: ^0.18.3 - version: 0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3) + version: 0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@14.1.0(vue@3.5.12(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3) unplugin-icons: specifier: ^0.20.0 version: 0.20.0(@vue/compiler-sfc@3.5.12)(vue-template-compiler@2.7.14)(webpack-sources@3.2.3) @@ -224,7 +224,7 @@ importers: version: 5.1.4(vite@5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.21.0))(vue@3.5.11(typescript@5.4.5)) unplugin-auto-import: specifier: ^0.18.3 - version: 0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@11.2.0(vue@3.5.11(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3) + version: 0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@14.1.0(vue@3.5.11(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3) vite: specifier: ^5.4.8 version: 5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.21.0) @@ -291,8 +291,8 @@ importers: packages/core: dependencies: '@vueuse/core': - specifier: ^10.5.0 - version: 10.5.0(vue@3.3.4) + specifier: ^14.1.0 + version: 14.1.0(vue@3.3.4) d3-drag: specifier: ^3.0.0 version: 3.0.0 @@ -2341,12 +2341,12 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/web-bluetooth@0.0.18': - resolution: {integrity: sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==} - '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@types/yauzl@2.10.1': resolution: {integrity: sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==} @@ -2720,12 +2720,14 @@ packages: '@vue/typescript@1.8.16': resolution: {integrity: sha512-ywbY4bS4YJw8gYyPpOhwyutqzl0lqkYI7l3waZkOcQG4ZYgiu6KyHZc3aagEbH8saFQTQxi5+I3ATUN5KwfvNw==} - '@vueuse/core@10.5.0': - resolution: {integrity: sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==} - '@vueuse/core@11.2.0': resolution: {integrity: sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==} + '@vueuse/core@14.1.0': + resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==} + peerDependencies: + vue: ^3.5.0 + '@vueuse/integrations@11.2.0': resolution: {integrity: sha512-zGXz3dsxNHKwiD9jPMvR3DAxQEOV6VWIEYTGVSB9PNpk4pTWR+pXrHz9gvXWcP2sTk3W2oqqS6KwWDdntUvNVA==} peerDependencies: @@ -2767,18 +2769,20 @@ packages: universal-cookie: optional: true - '@vueuse/metadata@10.5.0': - resolution: {integrity: sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==} - '@vueuse/metadata@11.2.0': resolution: {integrity: sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==} - '@vueuse/shared@10.5.0': - resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==} + '@vueuse/metadata@14.1.0': + resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==} '@vueuse/shared@11.2.0': resolution: {integrity: sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==} + '@vueuse/shared@14.1.0': + resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==} + peerDependencies: + vue: ^3.5.0 + '@windicss/config@1.9.3': resolution: {integrity: sha512-u8GUjsfC9r5X1AGYhzb1lX3zZj8wqk6SH1DYex8XUGmZ1M2UpvnUPOFi63XFViduspQ6l2xTX84QtG+lUzhEoQ==} @@ -7613,17 +7617,6 @@ packages: '@vue/composition-api': optional: true - vue-demi@0.14.6: - resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - vue-devtools-stub@0.1.0: resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} @@ -9866,10 +9859,10 @@ snapshots: '@types/unist@3.0.3': {} - '@types/web-bluetooth@0.0.18': {} - '@types/web-bluetooth@0.0.20': {} + '@types/web-bluetooth@0.0.21': {} + '@types/yauzl@2.10.1': dependencies: '@types/node': 20.14.2 @@ -10466,27 +10459,6 @@ snapshots: transitivePeerDependencies: - typescript - '@vueuse/core@10.5.0(vue@3.3.4)': - dependencies: - '@types/web-bluetooth': 0.0.18 - '@vueuse/metadata': 10.5.0 - '@vueuse/shared': 10.5.0(vue@3.3.4) - vue-demi: 0.14.6(vue@3.3.4) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/core@11.2.0(vue@3.5.11(typescript@5.4.5))': - dependencies: - '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 11.2.0 - '@vueuse/shared': 11.2.0(vue@3.5.11(typescript@5.4.5)) - vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - optional: true - '@vueuse/core@11.2.0(vue@3.5.12(typescript@5.4.5))': dependencies: '@types/web-bluetooth': 0.0.20 @@ -10497,6 +10469,29 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/core@14.1.0(vue@3.3.4)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.3.4) + vue: 3.3.4 + + '@vueuse/core@14.1.0(vue@3.5.11(typescript@5.4.5))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.5.11(typescript@5.4.5)) + vue: 3.5.11(typescript@5.4.5) + optional: true + + '@vueuse/core@14.1.0(vue@3.5.12(typescript@5.4.5))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.5.12(typescript@5.4.5)) + vue: 3.5.12(typescript@5.4.5) + optional: true + '@vueuse/integrations@11.2.0(focus-trap@7.6.0)(vue@3.5.12(typescript@5.4.5))': dependencies: '@vueuse/core': 11.2.0(vue@3.5.12(typescript@5.4.5)) @@ -10508,31 +10503,30 @@ snapshots: - '@vue/composition-api' - vue - '@vueuse/metadata@10.5.0': {} - '@vueuse/metadata@11.2.0': {} - '@vueuse/shared@10.5.0(vue@3.3.4)': + '@vueuse/metadata@14.1.0': {} + + '@vueuse/shared@11.2.0(vue@3.5.12(typescript@5.4.5))': dependencies: - vue-demi: 0.14.6(vue@3.3.4) + vue-demi: 0.14.10(vue@3.5.12(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/shared@11.2.0(vue@3.5.11(typescript@5.4.5))': + '@vueuse/shared@14.1.0(vue@3.3.4)': dependencies: - vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue + vue: 3.3.4 + + '@vueuse/shared@14.1.0(vue@3.5.11(typescript@5.4.5))': + dependencies: + vue: 3.5.11(typescript@5.4.5) optional: true - '@vueuse/shared@11.2.0(vue@3.5.12(typescript@5.4.5))': + '@vueuse/shared@14.1.0(vue@3.5.12(typescript@5.4.5))': dependencies: - vue-demi: 0.14.10(vue@3.5.12(typescript@5.4.5)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue + vue: 3.5.12(typescript@5.4.5) + optional: true '@windicss/config@1.9.3': dependencies: @@ -15562,7 +15556,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-auto-import@0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@11.2.0(vue@3.5.11(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3): + unplugin-auto-import@0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@14.1.0(vue@3.5.11(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@4.21.0) @@ -15574,12 +15568,12 @@ snapshots: unplugin: 1.14.1(webpack-sources@3.2.3) optionalDependencies: '@nuxt/kit': 3.13.2(magicast@0.3.5)(rollup@4.21.0)(webpack-sources@3.2.3) - '@vueuse/core': 11.2.0(vue@3.5.11(typescript@5.4.5)) + '@vueuse/core': 14.1.0(vue@3.5.11(typescript@5.4.5)) transitivePeerDependencies: - rollup - webpack-sources - unplugin-auto-import@0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@11.2.0(vue@3.5.12(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3): + unplugin-auto-import@0.18.3(@nuxt/kit@3.13.2(rollup@4.21.0)(webpack-sources@3.2.3))(@vueuse/core@14.1.0(vue@3.5.12(typescript@5.4.5)))(rollup@4.21.0)(webpack-sources@3.2.3): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@4.21.0) @@ -15591,7 +15585,7 @@ snapshots: unplugin: 1.14.1(webpack-sources@3.2.3) optionalDependencies: '@nuxt/kit': 3.13.2(magicast@0.3.5)(rollup@4.21.0)(webpack-sources@3.2.3) - '@vueuse/core': 11.2.0(vue@3.5.12(typescript@5.4.5)) + '@vueuse/core': 14.1.0(vue@3.5.12(typescript@5.4.5)) transitivePeerDependencies: - rollup - webpack-sources @@ -16030,10 +16024,6 @@ snapshots: dependencies: vue: 3.5.12(typescript@5.4.5) - vue-demi@0.14.6(vue@3.3.4): - dependencies: - vue: 3.3.4 - vue-devtools-stub@0.1.0: {} vue-eslint-parser@9.3.1(eslint@8.51.0):