Skip to content

Commit

Permalink
refactor: wave
Browse files Browse the repository at this point in the history
  • Loading branch information
tangjinzhou committed Jan 23, 2022
1 parent 7657157 commit d442db0
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 92 deletions.
5 changes: 4 additions & 1 deletion components/_util/hooks/useConfigInject.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RequiredMark } from '../../form/Form';
import type { ComputedRef, UnwrapRef } from 'vue';
import { computed, inject } from 'vue';
import type { ConfigProviderProps, Direction, SizeType } from '../../config-provider';
import type { ConfigProviderProps, CSPConfig, Direction, SizeType } from '../../config-provider';
import { defaultConfigProvider } from '../../config-provider';
import type { VueNode } from '../type';

Expand All @@ -27,6 +27,7 @@ export default (
getPopupContainer: ComputedRef<ConfigProviderProps['getPopupContainer']>;
getPrefixCls: ConfigProviderProps['getPrefixCls'];
autocomplete: ComputedRef<string>;
csp: ComputedRef<CSPConfig>;
} => {
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
'configProvider',
Expand All @@ -52,6 +53,7 @@ export default (
);
const size = computed(() => props.size || configProvider.componentSize);
const autocomplete = computed(() => props.autocomplete || configProvider.input?.autocomplete);
const csp = computed(() => configProvider.csp);
return {
configProvider,
prefixCls,
Expand All @@ -69,5 +71,6 @@ export default (
rootPrefixCls,
getPrefixCls: configProvider.getPrefixCls,
autocomplete,
csp,
};
};
181 changes: 90 additions & 91 deletions components/_util/wave.jsx → components/_util/wave.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { nextTick, inject, defineComponent } from 'vue';
import { nextTick, defineComponent, getCurrentInstance, onMounted, onBeforeUnmount } from 'vue';
import TransitionEvents from './css-animation/Event';
import raf from './raf';
import { defaultConfigProvider } from '../config-provider';
import { findDOMNode } from './props-util';
let styleForPesudo;
import useConfigInject from './hooks/useConfigInject';
let styleForPesudo: HTMLStyleElement;

// Where el is the DOM element you'd like to test for visibility
function isHidden(element) {
function isHidden(element: HTMLElement) {
if (process.env.NODE_ENV === 'test') {
return false;
}
return !element || element.offsetParent === null;
}
function isNotGrey(color) {
function isNotGrey(color: string) {
// eslint-disable-next-line no-useless-escape
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
Expand All @@ -22,40 +22,51 @@ function isNotGrey(color) {
}
export default defineComponent({
name: 'Wave',
props: ['insertExtraNode'],
setup() {
const configProvider = inject('configProvider', defaultConfigProvider);
return {
configProvider,
};
props: {
insertExtraNode: Boolean,
},
mounted() {
nextTick(() => {
const node = findDOMNode(this);
if (node.nodeType !== 1) {
setup(props, { slots, expose }) {
const instance = getCurrentInstance();
const { csp } = useConfigInject('', props);
expose({
csp,
});
let eventIns = null;
let clickWaveTimeoutId = null;
let animationStartId = null;
let animationStart = false;
let extraNode = null;
let isUnmounted = false;
const onTransitionStart = e => {
if (isUnmounted) return;

const node = findDOMNode(instance);
if (!e || e.target !== node) {
return;
}
this.instance = this.bindAnimationEvent(node);
});
},
beforeUnmount() {
if (this.instance) {
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId);
}
},
methods: {
onClick(node, waveColor) {

if (!animationStart) {
resetEffect(node);
}
};
const onTransitionEnd = (e: any) => {
if (!e || e.animationName !== 'fadeEffect') {
return;
}
resetEffect(e.target);
};
const getAttributeName = () => {
const { insertExtraNode } = props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
};
const onClick = (node: HTMLElement, waveColor: string) => {
if (!node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
return;
}
const { insertExtraNode } = this.$props;
this.extraNode = document.createElement('div');
const extraNode = this.extraNode;
const { insertExtraNode } = props;
extraNode = document.createElement('div');
extraNode.className = 'ant-click-animating-node';
const attributeName = this.getAttributeName();
const attributeName = getAttributeName();
node.removeAttribute(attributeName);
node.setAttribute(attributeName, 'true');
// Not white or transparent or grey
Expand All @@ -69,8 +80,8 @@ export default defineComponent({
waveColor !== 'transparent'
) {
// Add nonce if CSP exist
if (this.csp && this.csp.nonce) {
styleForPesudo.nonce = this.csp.nonce;
if (csp.value?.nonce) {
styleForPesudo.nonce = csp.value.nonce;
}
extraNode.style.borderColor = waveColor;
styleForPesudo.innerHTML = `
Expand All @@ -84,32 +95,26 @@ export default defineComponent({
if (insertExtraNode) {
node.appendChild(extraNode);
}
TransitionEvents.addStartEventListener(node, this.onTransitionStart);
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
},
onTransitionStart(e) {
if (this._.isUnmounted) return;

const node = findDOMNode(this);
if (!e || e.target !== node) {
TransitionEvents.addStartEventListener(node, onTransitionStart);
TransitionEvents.addEndEventListener(node, onTransitionEnd);
};
const resetEffect = (node: HTMLElement) => {
if (!node || node === extraNode || !(node instanceof Element)) {
return;
}

if (!this.animationStart) {
this.resetEffect(node);
const { insertExtraNode } = props;
const attributeName = getAttributeName();
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
if (styleForPesudo) {
styleForPesudo.innerHTML = '';
}
},
onTransitionEnd(e) {
if (!e || e.animationName !== 'fadeEffect') {
return;
if (insertExtraNode && extraNode && node.contains(extraNode)) {
node.removeChild(extraNode);
}
this.resetEffect(e.target);
},
getAttributeName() {
const { insertExtraNode } = this.$props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
},
bindAnimationEvent(node) {
TransitionEvents.removeStartEventListener(node, onTransitionStart);
TransitionEvents.removeEndEventListener(node, onTransitionEnd);
};
const bindAnimationEvent = (node: HTMLElement) => {
if (
!node ||
!node.getAttribute ||
Expand All @@ -118,57 +123,51 @@ export default defineComponent({
) {
return;
}
const onClick = e => {
const newClick = (e: MouseEvent) => {
// Fix radio button click twice
if (e.target.tagName === 'INPUT' || isHidden(e.target)) {
if ((e.target as any).tagName === 'INPUT' || isHidden(e.target as HTMLElement)) {
return;
}
this.resetEffect(node);
resetEffect(node);
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
this.clickWaveTimeoutId = setTimeout(() => this.onClick(node, waveColor), 0);
raf.cancel(this.animationStartId);
this.animationStart = true;
clickWaveTimeoutId = setTimeout(() => onClick(node, waveColor), 0);
raf.cancel(animationStartId);
animationStart = true;

// Render to trigger transition event cost 3 frames. Let's delay 10 frames to reset this.
this.animationStartId = raf(() => {
this.animationStart = false;
animationStartId = raf(() => {
animationStart = false;
}, 10);
};
node.addEventListener('click', onClick, true);
node.addEventListener('click', newClick, true);
return {
cancel: () => {
node.removeEventListener('click', onClick, true);
node.removeEventListener('click', newClick, true);
},
};
},

resetEffect(node) {
if (!node || node === this.extraNode || !(node instanceof Element)) {
return;
}
const { insertExtraNode } = this.$props;
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
if (styleForPesudo) {
styleForPesudo.innerHTML = '';
}
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
node.removeChild(this.extraNode);
};
onMounted(() => {
nextTick(() => {
const node = findDOMNode(instance);
if (node.nodeType !== 1) {
return;
}
eventIns = bindAnimationEvent(node);
});
});
onBeforeUnmount(() => {
if (eventIns) {
eventIns.cancel();
}
TransitionEvents.removeStartEventListener(node, this.onTransitionStart);
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
},
},

render() {
const csp = this.configProvider.csp;
if (csp) {
this.csp = csp;
}
return this.$slots.default?.()[0];
clearTimeout(clickWaveTimeoutId);
isUnmounted = true;
});
return () => {
return slots.default?.()[0];
};
},
});
1 change: 1 addition & 0 deletions components/config-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export const configProviderProps = {
},
csp: {
type: Object as PropType<CSPConfig>,
default: undefined as CSPConfig,
},
input: {
type: Object as PropType<{ autocomplete: string }>,
Expand Down

0 comments on commit d442db0

Please sign in to comment.