|
1 | 1 | import { isBuiltInDirective } from '@vue/shared' |
2 | | -import { type ComponentInternalInstance, currentInstance } from './component' |
| 2 | +import { |
| 3 | + type ComponentInternalInstance, |
| 4 | + currentInstance, |
| 5 | + isVaporComponent, |
| 6 | +} from './component' |
3 | 7 | import { warn } from './warning' |
| 8 | +import { normalizeBlock } from './dom/element' |
| 9 | +import { getCurrentScope } from '@vue/reactivity' |
| 10 | +import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling' |
4 | 11 |
|
5 | 12 | export type DirectiveModifiers<M extends string = string> = Record<M, boolean> |
6 | 13 |
|
7 | 14 | export interface DirectiveBinding<T = any, V = any, M extends string = string> { |
8 | 15 | instance: ComponentInternalInstance |
9 | | - source?: () => V |
10 | | - value: V |
11 | | - oldValue: V | null |
| 16 | + source: () => V |
12 | 17 | arg?: string |
13 | 18 | modifiers?: DirectiveModifiers<M> |
14 | | - dir: ObjectDirective<T, V, M> |
| 19 | + dir: Directive<T, V, M> |
15 | 20 | } |
16 | 21 |
|
17 | 22 | export type DirectiveBindingsMap = Map<Node, DirectiveBinding[]> |
18 | 23 |
|
19 | | -export type DirectiveHook< |
20 | | - T = any | null, |
21 | | - V = any, |
22 | | - M extends string = string, |
23 | | -> = (node: T, binding: DirectiveBinding<T, V, M>) => void |
24 | | - |
25 | | -// create node -> `created` -> node operation -> `beforeMount` -> node mounted -> `mounted` |
26 | | -// effect update -> `beforeUpdate` -> node updated -> `updated` |
27 | | -// `beforeUnmount`-> node unmount -> `unmounted` |
28 | | -export type DirectiveHookName = |
29 | | - | 'created' |
30 | | - | 'beforeMount' |
31 | | - | 'mounted' |
32 | | - | 'beforeUpdate' |
33 | | - | 'updated' |
34 | | - | 'beforeUnmount' |
35 | | - | 'unmounted' |
36 | | -export type ObjectDirective<T = any, V = any, M extends string = string> = { |
37 | | - [K in DirectiveHookName]?: DirectiveHook<T, V, M> | undefined |
38 | | -} & { |
39 | | - /** Watch value deeply */ |
40 | | - deep?: boolean | number |
41 | | -} |
42 | | - |
43 | | -export type FunctionDirective< |
44 | | - T = any, |
45 | | - V = any, |
46 | | - M extends string = string, |
47 | | -> = DirectiveHook<T, V, M> |
48 | | - |
49 | | -export type Directive<T = any, V = any, M extends string = string> = |
50 | | - | ObjectDirective<T, V, M> |
51 | | - | FunctionDirective<T, V, M> |
| 24 | +export type Directive<T = any, V = any, M extends string = string> = ( |
| 25 | + node: T, |
| 26 | + binding: DirectiveBinding<T, V, M>, |
| 27 | +) => void |
52 | 28 |
|
53 | 29 | export function validateDirectiveName(name: string): void { |
54 | 30 | if (isBuiltInDirective(name)) { |
@@ -77,7 +53,54 @@ export function withDirectives<T extends ComponentInternalInstance | Node>( |
77 | 53 | return nodeOrComponent |
78 | 54 | } |
79 | 55 |
|
80 | | - // NOOP |
| 56 | + let node: Node |
| 57 | + if (isVaporComponent(nodeOrComponent)) { |
| 58 | + const root = getComponentNode(nodeOrComponent) |
| 59 | + if (!root) return nodeOrComponent |
| 60 | + node = root |
| 61 | + } else { |
| 62 | + node = nodeOrComponent |
| 63 | + } |
| 64 | + |
| 65 | + const instance = currentInstance! |
| 66 | + const parentScope = getCurrentScope() |
| 67 | + |
| 68 | + if (__DEV__ && !parentScope) { |
| 69 | + warn(`Directives should be used inside of RenderEffectScope.`) |
| 70 | + } |
| 71 | + |
| 72 | + for (const directive of directives) { |
| 73 | + let [dir, source = () => undefined, arg, modifiers] = directive |
| 74 | + if (!dir) continue |
| 75 | + |
| 76 | + const binding: DirectiveBinding = { |
| 77 | + dir, |
| 78 | + source, |
| 79 | + instance, |
| 80 | + arg, |
| 81 | + modifiers, |
| 82 | + } |
| 83 | + |
| 84 | + callWithAsyncErrorHandling(dir, instance, VaporErrorCodes.DIRECTIVE_HOOK, [ |
| 85 | + node, |
| 86 | + binding, |
| 87 | + ]) |
| 88 | + } |
81 | 89 |
|
82 | 90 | return nodeOrComponent |
83 | 91 | } |
| 92 | + |
| 93 | +function getComponentNode(component: ComponentInternalInstance) { |
| 94 | + if (!component.block) return |
| 95 | + |
| 96 | + const nodes = normalizeBlock(component.block) |
| 97 | + if (nodes.length !== 1) { |
| 98 | + warn( |
| 99 | + `Runtime directive used on component with non-element root node. ` + |
| 100 | + `The directives will not function as intended.`, |
| 101 | + ) |
| 102 | + return |
| 103 | + } |
| 104 | + |
| 105 | + return nodes[0] |
| 106 | +} |
0 commit comments