refactor(runtime-vapor): simplify directive mechanism (#278)
* feat: custom directive v2 * wip * fix: directive * fix * refactor * refactor: remove ref for el
This commit is contained in:
parent
eed7d1d4fd
commit
c574faa8f3
|
@ -405,7 +405,7 @@ function getItem(
|
||||||
} else if (typeof source === 'number') {
|
} else if (typeof source === 'number') {
|
||||||
return [idx + 1, idx, undefined]
|
return [idx + 1, idx, undefined]
|
||||||
} else if (isObject(source)) {
|
} else if (isObject(source)) {
|
||||||
if (source && source[Symbol.iterator as any]) {
|
if (source[Symbol.iterator as any]) {
|
||||||
source = Array.from(source as Iterable<any>)
|
source = Array.from(source as Iterable<any>)
|
||||||
return [source[idx], idx, undefined]
|
return [source[idx], idx, undefined]
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -128,7 +128,7 @@ function mountComponent(
|
||||||
}
|
}
|
||||||
|
|
||||||
// hook: beforeMount
|
// hook: beforeMount
|
||||||
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
|
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT)
|
||||||
|
|
||||||
insert(instance.block!, instance.container)
|
insert(instance.block!, instance.container)
|
||||||
|
|
||||||
|
@ -136,7 +136,6 @@ function mountComponent(
|
||||||
invokeLifecycle(
|
invokeLifecycle(
|
||||||
instance,
|
instance,
|
||||||
VaporLifecycleHooks.MOUNTED,
|
VaporLifecycleHooks.MOUNTED,
|
||||||
'mounted',
|
|
||||||
instance => (instance.isMounted = true),
|
instance => (instance.isMounted = true),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
@ -156,7 +155,7 @@ export function unmountComponent(instance: ComponentInternalInstance): void {
|
||||||
const { container, scope } = instance
|
const { container, scope } = instance
|
||||||
|
|
||||||
// hook: beforeUnmount
|
// hook: beforeUnmount
|
||||||
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_UNMOUNT, 'beforeUnmount')
|
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_UNMOUNT)
|
||||||
|
|
||||||
scope.stop()
|
scope.stop()
|
||||||
container.textContent = ''
|
container.textContent = ''
|
||||||
|
@ -165,7 +164,6 @@ export function unmountComponent(instance: ComponentInternalInstance): void {
|
||||||
invokeLifecycle(
|
invokeLifecycle(
|
||||||
instance,
|
instance,
|
||||||
VaporLifecycleHooks.UNMOUNTED,
|
VaporLifecycleHooks.UNMOUNTED,
|
||||||
'unmounted',
|
|
||||||
instance => queuePostFlushCb(() => (instance.isUnmounted = true)),
|
instance => queuePostFlushCb(() => (instance.isUnmounted = true)),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,12 +2,10 @@ import { invokeArrayFns } from '@vue/shared'
|
||||||
import type { VaporLifecycleHooks } from './enums'
|
import type { VaporLifecycleHooks } from './enums'
|
||||||
import { type ComponentInternalInstance, setCurrentInstance } from './component'
|
import { type ComponentInternalInstance, setCurrentInstance } from './component'
|
||||||
import { queuePostFlushCb } from './scheduler'
|
import { queuePostFlushCb } from './scheduler'
|
||||||
import type { DirectiveHookName } from './directives'
|
|
||||||
|
|
||||||
export function invokeLifecycle(
|
export function invokeLifecycle(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
lifecycle: VaporLifecycleHooks,
|
lifecycle: VaporLifecycleHooks,
|
||||||
directive: DirectiveHookName,
|
|
||||||
cb?: (instance: ComponentInternalInstance) => void,
|
cb?: (instance: ComponentInternalInstance) => void,
|
||||||
post?: boolean,
|
post?: boolean,
|
||||||
): void {
|
): void {
|
||||||
|
@ -27,8 +25,6 @@ export function invokeLifecycle(
|
||||||
}
|
}
|
||||||
|
|
||||||
function invokeSub() {
|
function invokeSub() {
|
||||||
instance.comps.forEach(comp =>
|
instance.comps.forEach(comp => invokeLifecycle(comp, lifecycle, cb, post))
|
||||||
invokeLifecycle(comp, lifecycle, directive, cb, post),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,30 @@
|
||||||
import { isBuiltInDirective } from '@vue/shared'
|
import { isBuiltInDirective } from '@vue/shared'
|
||||||
import { type ComponentInternalInstance, currentInstance } from './component'
|
import {
|
||||||
|
type ComponentInternalInstance,
|
||||||
|
currentInstance,
|
||||||
|
isVaporComponent,
|
||||||
|
} from './component'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
import { normalizeBlock } from './dom/element'
|
||||||
|
import { getCurrentScope } from '@vue/reactivity'
|
||||||
|
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
|
||||||
|
|
||||||
export type DirectiveModifiers<M extends string = string> = Record<M, boolean>
|
export type DirectiveModifiers<M extends string = string> = Record<M, boolean>
|
||||||
|
|
||||||
export interface DirectiveBinding<T = any, V = any, M extends string = string> {
|
export interface DirectiveBinding<T = any, V = any, M extends string = string> {
|
||||||
instance: ComponentInternalInstance
|
instance: ComponentInternalInstance
|
||||||
source?: () => V
|
source: () => V
|
||||||
value: V
|
|
||||||
oldValue: V | null
|
|
||||||
arg?: string
|
arg?: string
|
||||||
modifiers?: DirectiveModifiers<M>
|
modifiers?: DirectiveModifiers<M>
|
||||||
dir: ObjectDirective<T, V, M>
|
dir: Directive<T, V, M>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DirectiveBindingsMap = Map<Node, DirectiveBinding[]>
|
export type DirectiveBindingsMap = Map<Node, DirectiveBinding[]>
|
||||||
|
|
||||||
export type DirectiveHook<
|
export type Directive<T = any, V = any, M extends string = string> = (
|
||||||
T = any | null,
|
node: T,
|
||||||
V = any,
|
binding: DirectiveBinding<T, V, M>,
|
||||||
M extends string = string,
|
) => void
|
||||||
> = (node: T, binding: DirectiveBinding<T, V, M>) => void
|
|
||||||
|
|
||||||
// create node -> `created` -> node operation -> `beforeMount` -> node mounted -> `mounted`
|
|
||||||
// effect update -> `beforeUpdate` -> node updated -> `updated`
|
|
||||||
// `beforeUnmount`-> node unmount -> `unmounted`
|
|
||||||
export type DirectiveHookName =
|
|
||||||
| 'created'
|
|
||||||
| 'beforeMount'
|
|
||||||
| 'mounted'
|
|
||||||
| 'beforeUpdate'
|
|
||||||
| 'updated'
|
|
||||||
| 'beforeUnmount'
|
|
||||||
| 'unmounted'
|
|
||||||
export type ObjectDirective<T = any, V = any, M extends string = string> = {
|
|
||||||
[K in DirectiveHookName]?: DirectiveHook<T, V, M> | undefined
|
|
||||||
} & {
|
|
||||||
/** Watch value deeply */
|
|
||||||
deep?: boolean | number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FunctionDirective<
|
|
||||||
T = any,
|
|
||||||
V = any,
|
|
||||||
M extends string = string,
|
|
||||||
> = DirectiveHook<T, V, M>
|
|
||||||
|
|
||||||
export type Directive<T = any, V = any, M extends string = string> =
|
|
||||||
| ObjectDirective<T, V, M>
|
|
||||||
| FunctionDirective<T, V, M>
|
|
||||||
|
|
||||||
export function validateDirectiveName(name: string): void {
|
export function validateDirectiveName(name: string): void {
|
||||||
if (isBuiltInDirective(name)) {
|
if (isBuiltInDirective(name)) {
|
||||||
|
@ -77,7 +53,54 @@ export function withDirectives<T extends ComponentInternalInstance | Node>(
|
||||||
return nodeOrComponent
|
return nodeOrComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOOP
|
let node: Node
|
||||||
|
if (isVaporComponent(nodeOrComponent)) {
|
||||||
|
const root = getComponentNode(nodeOrComponent)
|
||||||
|
if (!root) return nodeOrComponent
|
||||||
|
node = root
|
||||||
|
} else {
|
||||||
|
node = nodeOrComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = currentInstance!
|
||||||
|
const parentScope = getCurrentScope()
|
||||||
|
|
||||||
|
if (__DEV__ && !parentScope) {
|
||||||
|
warn(`Directives should be used inside of RenderEffectScope.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const directive of directives) {
|
||||||
|
let [dir, source = () => undefined, arg, modifiers] = directive
|
||||||
|
if (!dir) continue
|
||||||
|
|
||||||
|
const binding: DirectiveBinding = {
|
||||||
|
dir,
|
||||||
|
source,
|
||||||
|
instance,
|
||||||
|
arg,
|
||||||
|
modifiers,
|
||||||
|
}
|
||||||
|
|
||||||
|
callWithAsyncErrorHandling(dir, instance, VaporErrorCodes.DIRECTIVE_HOOK, [
|
||||||
|
node,
|
||||||
|
binding,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
return nodeOrComponent
|
return nodeOrComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getComponentNode(component: ComponentInternalInstance) {
|
||||||
|
if (!component.block) return
|
||||||
|
|
||||||
|
const nodes = normalizeBlock(component.block)
|
||||||
|
if (nodes.length !== 1) {
|
||||||
|
warn(
|
||||||
|
`Runtime directive used on component with non-element root node. ` +
|
||||||
|
`The directives will not function as intended.`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes[0]
|
||||||
|
}
|
||||||
|
|
|
@ -6,16 +6,18 @@ import {
|
||||||
looseIndexOf,
|
looseIndexOf,
|
||||||
looseToNumber,
|
looseToNumber,
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import type {
|
import type { Directive } from '../directives'
|
||||||
DirectiveBinding,
|
|
||||||
DirectiveHook,
|
|
||||||
DirectiveHookName,
|
|
||||||
ObjectDirective,
|
|
||||||
} from '../directives'
|
|
||||||
import { addEventListener } from '../dom/event'
|
import { addEventListener } from '../dom/event'
|
||||||
import { nextTick } from '../scheduler'
|
import { nextTick } from '../scheduler'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { MetadataKind, getMetadata } from '../componentMetadata'
|
import { MetadataKind, getMetadata } from '../componentMetadata'
|
||||||
|
import {
|
||||||
|
onBeforeMount,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onBeforeUpdate,
|
||||||
|
onMounted,
|
||||||
|
} from '../apiLifecycle'
|
||||||
|
import { renderEffect } from '../renderEffect'
|
||||||
|
|
||||||
type AssignerFn = (value: any) => void
|
type AssignerFn = (value: any) => void
|
||||||
function getModelAssigner(el: Element): AssignerFn {
|
function getModelAssigner(el: Element): AssignerFn {
|
||||||
|
@ -41,12 +43,12 @@ const assigningMap = new WeakMap<HTMLElement, boolean>()
|
||||||
|
|
||||||
// We are exporting the v-model runtime directly as vnode hooks so that it can
|
// We are exporting the v-model runtime directly as vnode hooks so that it can
|
||||||
// be tree-shaken in case v-model is never used.
|
// be tree-shaken in case v-model is never used.
|
||||||
export const vModelText: ObjectDirective<
|
export const vModelText: Directive<
|
||||||
HTMLInputElement | HTMLTextAreaElement,
|
HTMLInputElement | HTMLTextAreaElement,
|
||||||
any,
|
any,
|
||||||
'lazy' | 'trim' | 'number'
|
'lazy' | 'trim' | 'number'
|
||||||
> = {
|
> = (el, { source, modifiers: { lazy, trim, number } = {} }) => {
|
||||||
beforeMount(el, { modifiers: { lazy, trim, number } = {} }) {
|
onBeforeMount(() => {
|
||||||
const assigner = getModelAssigner(el)
|
const assigner = getModelAssigner(el)
|
||||||
assignFnMap.set(el, assigner)
|
assignFnMap.set(el, assigner)
|
||||||
|
|
||||||
|
@ -78,12 +80,15 @@ export const vModelText: ObjectDirective<
|
||||||
// fires "change" instead of "input" on autocomplete.
|
// fires "change" instead of "input" on autocomplete.
|
||||||
addEventListener(el, 'change', onCompositionEnd)
|
addEventListener(el, 'change', onCompositionEnd)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
// set value on mounted so it's after min/max for type="range"
|
|
||||||
mounted(el, { value }) {
|
onMounted(() => {
|
||||||
|
const value = source()
|
||||||
el.value = value == null ? '' : value
|
el.value = value == null ? '' : value
|
||||||
},
|
})
|
||||||
beforeUpdate(el, { value, modifiers: { lazy, trim, number } = {} }) {
|
|
||||||
|
renderEffect(() => {
|
||||||
|
const value = source()
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
|
|
||||||
// avoid clearing unresolved text. #2302
|
// avoid clearing unresolved text. #2302
|
||||||
|
@ -108,29 +113,31 @@ export const vModelText: ObjectDirective<
|
||||||
}
|
}
|
||||||
|
|
||||||
el.value = newValue
|
el.value = newValue
|
||||||
},
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const vModelRadio: ObjectDirective<HTMLInputElement> = {
|
export const vModelRadio: Directive<HTMLInputElement> = (el, { source }) => {
|
||||||
beforeMount(el, { value }) {
|
onBeforeMount(() => {
|
||||||
el.checked = looseEqual(value, getValue(el))
|
el.checked = looseEqual(source(), getValue(el))
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
addEventListener(el, 'change', () => {
|
addEventListener(el, 'change', () => {
|
||||||
assignFnMap.get(el)!(getValue(el))
|
assignFnMap.get(el)!(getValue(el))
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
beforeUpdate(el, { value, oldValue }) {
|
|
||||||
|
renderEffect(() => {
|
||||||
|
const value = source()
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
if (value !== oldValue) {
|
el.checked = looseEqual(value, getValue(el))
|
||||||
el.checked = looseEqual(value, getValue(el))
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const vModelSelect: ObjectDirective<HTMLSelectElement, any, 'number'> = {
|
export const vModelSelect: Directive<HTMLSelectElement, any, 'number'> = (
|
||||||
// <select multiple> value need to be deep traversed
|
el,
|
||||||
deep: true,
|
{ source, modifiers: { number = false } = {} },
|
||||||
beforeMount(el, { value, modifiers: { number = false } = {} }) {
|
) => {
|
||||||
|
onBeforeMount(() => {
|
||||||
|
const value = source()
|
||||||
const isSetModel = isSet(value)
|
const isSetModel = isSet(value)
|
||||||
addEventListener(el, 'change', () => {
|
addEventListener(el, 'change', () => {
|
||||||
const selectedVal = Array.prototype.filter
|
const selectedVal = Array.prototype.filter
|
||||||
|
@ -153,15 +160,17 @@ export const vModelSelect: ObjectDirective<HTMLSelectElement, any, 'number'> = {
|
||||||
})
|
})
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
setSelected(el, value, number)
|
setSelected(el, value, number)
|
||||||
},
|
})
|
||||||
beforeUpdate(el) {
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
},
|
})
|
||||||
updated(el, { value, modifiers: { number = false } = {} }) {
|
|
||||||
|
renderEffect(() => {
|
||||||
if (!assigningMap.get(el)) {
|
if (!assigningMap.get(el)) {
|
||||||
setSelected(el, value, number)
|
setSelected(el, source(), number)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSelected(el: HTMLSelectElement, value: any, number: boolean) {
|
function setSelected(el: HTMLSelectElement, value: any, number: boolean) {
|
||||||
|
@ -223,27 +232,12 @@ function getCheckboxValue(el: HTMLInputElement, checked: boolean) {
|
||||||
return checked
|
return checked
|
||||||
}
|
}
|
||||||
|
|
||||||
const setChecked: DirectiveHook<HTMLInputElement> = (
|
export const vModelCheckbox: Directive<HTMLInputElement> = (el, { source }) => {
|
||||||
el,
|
onBeforeMount(() => {
|
||||||
{ value, oldValue },
|
|
||||||
) => {
|
|
||||||
if (isArray(value)) {
|
|
||||||
el.checked = looseIndexOf(value, getValue(el)) > -1
|
|
||||||
} else if (isSet(value)) {
|
|
||||||
el.checked = value.has(getValue(el))
|
|
||||||
} else if (value !== oldValue) {
|
|
||||||
el.checked = looseEqual(value, getCheckboxValue(el, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const vModelCheckbox: ObjectDirective<HTMLInputElement> = {
|
|
||||||
// #4096 array checkboxes need to be deep traversed
|
|
||||||
deep: true,
|
|
||||||
beforeMount(el, binding) {
|
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
|
|
||||||
addEventListener(el, 'change', () => {
|
addEventListener(el, 'change', () => {
|
||||||
const modelValue = binding.value
|
const modelValue = source()
|
||||||
const elementValue = getValue(el)
|
const elementValue = getValue(el)
|
||||||
const checked = el.checked
|
const checked = el.checked
|
||||||
const assigner = assignFnMap.get(el)!
|
const assigner = assignFnMap.get(el)!
|
||||||
|
@ -269,36 +263,38 @@ export const vModelCheckbox: ObjectDirective<HTMLInputElement> = {
|
||||||
assigner(getCheckboxValue(el, checked))
|
assigner(getCheckboxValue(el, checked))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
// set initial checked on mount to wait for true-value/false-value
|
|
||||||
mounted: setChecked,
|
onMounted(() => {
|
||||||
beforeUpdate(el, binding) {
|
setChecked()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUpdate(() => {
|
||||||
assignFnMap.set(el, getModelAssigner(el))
|
assignFnMap.set(el, getModelAssigner(el))
|
||||||
setChecked(el, binding)
|
setChecked()
|
||||||
},
|
})
|
||||||
|
|
||||||
|
function setChecked() {
|
||||||
|
const value = source()
|
||||||
|
if (isArray(value)) {
|
||||||
|
el.checked = looseIndexOf(value, getValue(el)) > -1
|
||||||
|
} else if (isSet(value)) {
|
||||||
|
el.checked = value.has(getValue(el))
|
||||||
|
} else {
|
||||||
|
el.checked = looseEqual(value, getCheckboxValue(el, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const vModelDynamic: ObjectDirective<
|
export const vModelDynamic: Directive<
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||||
> = {
|
> = (el, binding) => {
|
||||||
beforeMount(el, binding) {
|
const type = el.getAttribute('type')
|
||||||
callModelHook(el, binding, 'beforeMount')
|
const modelToUse = resolveDynamicModel(el.tagName, type)
|
||||||
},
|
modelToUse(el, binding)
|
||||||
mounted(el, binding) {
|
|
||||||
callModelHook(el, binding, 'mounted')
|
|
||||||
},
|
|
||||||
beforeUpdate(el, binding) {
|
|
||||||
callModelHook(el, binding, 'beforeUpdate')
|
|
||||||
},
|
|
||||||
updated(el, binding) {
|
|
||||||
callModelHook(el, binding, 'updated')
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDynamicModel(
|
function resolveDynamicModel(tagName: string, type: string | null): Directive {
|
||||||
tagName: string,
|
|
||||||
type: string | null,
|
|
||||||
): ObjectDirective {
|
|
||||||
switch (tagName) {
|
switch (tagName) {
|
||||||
case 'SELECT':
|
case 'SELECT':
|
||||||
return vModelSelect
|
return vModelSelect
|
||||||
|
@ -315,14 +311,3 @@ function resolveDynamicModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function callModelHook(
|
|
||||||
el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
|
|
||||||
binding: DirectiveBinding,
|
|
||||||
hook: DirectiveHookName,
|
|
||||||
) {
|
|
||||||
const type = el.getAttribute('type')
|
|
||||||
const modelToUse = resolveDynamicModel(el.tagName, type)
|
|
||||||
const fn = modelToUse[hook]
|
|
||||||
fn && fn(el, binding)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
import type { ObjectDirective } from '../directives'
|
import type { Directive } from '../directives'
|
||||||
|
import { renderEffect } from '../renderEffect'
|
||||||
|
|
||||||
const vShowMap = new WeakMap<HTMLElement, string>()
|
export const vShowOriginalDisplay: unique symbol = Symbol('_vod')
|
||||||
|
export const vShowHidden: unique symbol = Symbol('_vsh')
|
||||||
|
|
||||||
export const vShow: ObjectDirective<HTMLElement> = {
|
export interface VShowElement extends HTMLElement {
|
||||||
beforeMount(node, { value }) {
|
// _vod = vue original display
|
||||||
vShowMap.set(node, node.style.display === 'none' ? '' : node.style.display)
|
[vShowOriginalDisplay]: string
|
||||||
setDisplay(node, value)
|
[vShowHidden]: boolean
|
||||||
},
|
|
||||||
|
|
||||||
updated(node, { value, oldValue }) {
|
|
||||||
if (!value === !oldValue) return
|
|
||||||
setDisplay(node, value)
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeUnmount(node, { value }) {
|
|
||||||
setDisplay(node, value)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDisplay(el: HTMLElement, value: unknown): void {
|
export const vShow: Directive<VShowElement> = (el, { source }) => {
|
||||||
el.style.display = value ? vShowMap.get(el)! : 'none'
|
el[vShowOriginalDisplay] = el.style.display === 'none' ? '' : el.style.display
|
||||||
|
renderEffect(() => setDisplay(el, source()))
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDisplay(el: VShowElement, value: unknown): void {
|
||||||
|
el.style.display = value ? el[vShowOriginalDisplay] : 'none'
|
||||||
|
el[vShowHidden] = !value
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,9 +76,6 @@ export {
|
||||||
withDirectives,
|
withDirectives,
|
||||||
type Directive,
|
type Directive,
|
||||||
type DirectiveBinding,
|
type DirectiveBinding,
|
||||||
type DirectiveHook,
|
|
||||||
type ObjectDirective,
|
|
||||||
type FunctionDirective,
|
|
||||||
type DirectiveArguments,
|
type DirectiveArguments,
|
||||||
type DirectiveModifiers,
|
type DirectiveModifiers,
|
||||||
} from './directives'
|
} from './directives'
|
||||||
|
|
Loading…
Reference in New Issue