|
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