Skip to content

Commit

Permalink
Merge pull request #212 from Tencent/refactor/loading
Browse files Browse the repository at this point in the history
refactor(loading): use composition API to refactor loading
  • Loading branch information
PengYYYYY committed Jan 18, 2022
2 parents 83aaaf0 + 48382b0 commit baee597
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 146 deletions.
1 change: 0 additions & 1 deletion examples/loading/demos/attach.vue
@@ -1,7 +1,6 @@
<template>
<div class="tdesign-demo-block-column">
<div id="alice" class="loading-attach-demo__title">Hello, I'm Alice.</div>
<div id="bob" class="loading-attach-demo__title">Hi, I'm Bob.</div>
<t-loading attach="#alice" show-overlay></t-loading>
</div>
</template>
Expand Down
53 changes: 8 additions & 45 deletions src/loading/icon/gradient.tsx
@@ -1,57 +1,20 @@
import { defineComponent } from 'vue';
import { defineComponent, onMounted, getCurrentInstance } from 'vue';
import { prefix } from '../../config';
import setStyle from '../../utils/set-style';
import circleAdapter from '../../_common/js/loading/circle-adapter';

const name = `${prefix}-loading__gradient`;
const classname = `${prefix}-icon-loading`;

export default defineComponent({
name,
mounted() {
this.$nextTick(() => {
this.updateColor();
});
},
updated() {
this.updateColor();
},
methods: {
updateColor() {
let basicStyle = {};
const circleElem = this.$refs.circle as HTMLElement;
if (!circleElem) {
return;
}

const { color, fontSize } = window.getComputedStyle(circleElem);

// to fix the browser compat of foreignObject in Safari,
// https://bugs.webkit.org/show_bug.cgi?id=23113
const ua = window?.navigator?.userAgent;
const isSafari = /Safari/.test(ua) && !/Chrome/.test(ua);
if (isSafari) {
basicStyle = {
transformOrigin: '-1px -1px',
transform: `scale(${parseInt(fontSize, 10) / 14})`,
};
}
if (color) {
const matched = color.match(/[\d.]+/g);
const endColor = `rgba(${matched[0]}, ${matched[1]}, ${matched[2]}, 0)`;
setStyle(circleElem, {
...basicStyle,
background: `conic-gradient(from 90deg at 50% 50%,${endColor} 0deg, ${color} 360deg)`,
});
} else {
setStyle(circleElem, {
...basicStyle,
background: '',
});
}
},
setup() {
onMounted(() => {
const circleElem = getCurrentInstance().refs.circle as HTMLElement;
circleAdapter(circleElem);
});
},
render() {
const classes = [name, classname];
const classes = [name, `${prefix}-icon-loading`];
return (
<svg
class={classes}
Expand Down
165 changes: 76 additions & 89 deletions src/loading/loading.tsx
@@ -1,12 +1,12 @@
import { defineComponent } from 'vue';
import { defineComponent, ref, computed, watch, onMounted } from 'vue';
import GradientIcon from './icon/gradient';
import { prefix } from '../config';
import { SIZE_CLASSNAMES } from '../utils/classnames';
import { addClass, removeClass } from '../utils/dom';
import { renderTNodeJSX, renderContent } from '../utils/render-tnode';
import TransferDom from '../utils/transfer-dom';
import props from './props';
import { ClassName, Styles } from '../common';
import { Styles } from '../common';

const name = `${prefix}-loading`;
const centerClass = `${prefix}-loading--center`;
Expand All @@ -19,112 +19,99 @@ const inheritColorClass = `${prefix}-loading--inherit-color`;

export default defineComponent({
name: 'TLoading',

directives: {
TransferDom,
},
props,
setup(props, { slots }) {
const delayShowLoading = ref(false);

props: {
...props,
},

data() {
return {
delayShowLoading: false,
const countDelay = () => {
delayShowLoading.value = false;
const timer = setTimeout(() => {
delayShowLoading.value = true;
clearTimeout(timer);
}, props.delay);
};
},

computed: {
styles(): Styles {
// 延时计时是否完成。用于控制延时计时结束前不能显示加载态
const delayCounted = computed(() => Boolean(!props.delay || (props.delay && delayShowLoading.value)));

// loading style
const styles = computed(() => {
const styles: Styles = {};
if (this.zIndex !== undefined) {
styles.zIndex = this.zIndex;
if (props.zIndex !== undefined) {
styles.zIndex = props.zIndex;
}
if (!['small', 'medium', 'large'].includes(this.size)) {
styles['font-size'] = this.size;
if (!['small', 'medium', 'large'].includes(props.size)) {
styles['font-size'] = props.size;
}
return styles;
},
showText(): boolean {
return Boolean(this.text || this.$slots.text);
},
baseClasses(): ClassName {
return [centerClass, SIZE_CLASSNAMES[this.size], { [inheritColorClass]: this.inheritColor }];
},
hasContent(): boolean {
return Boolean(this.default || this.$slots.default || this.content || this.$slots.content);
},
withContentClasses(): ClassName {
return this.baseClasses.concat([
name,
fullClass,
{
[overlayClass]: this.showOverlay,
},
]);
},
fullScreenClasses(): ClassName {
return [name, fullscreenClass, centerClass, overlayClass];
},
attachClasses(): ClassName {
return this.baseClasses.concat([name, fullClass, { [overlayClass]: this.showOverlay }]);
},
normalClasses(): ClassName {
return this.baseClasses.concat([name]);
},
lockFullscreen(): boolean {
return this.preventScrollThrough && this.fullscreen;
},
// 延时计时是否完成。用于控制延时计时结束前不能显示加载态
delayCounted(): boolean {
return Boolean(!this.delay || (this.delay && this.delayShowLoading));
},
showWrapLoading(): boolean {
return this.hasContent && this.loading && this.delayCounted;
},
showNormalLoading(): boolean {
return !this.hasContent && this.loading && this.delayCounted;
},
},
});

watch: {
loading: {
handler(value) {
if (value) {
this.countDelay();
this.lockFullscreen && addClass(document.body, lockClass);
return;
}
this.lockFullscreen && removeClass(document.body, lockClass);
},
immediate: true,
},
},
const hasContent = computed(() => Boolean(props.default || slots.default || props.content || slots.content));
const lockFullscreen = computed(() => props.preventScrollThrough && props.fullscreen);
const showText = computed(() => Boolean(props.text || slots.text));
const showWrapLoading = computed(() => hasContent.value && props.loading && delayCounted.value);
const showNormalLoading = computed(() => !hasContent.value && props.loading && delayCounted.value);

created() {
this.delay && this.countDelay();
},
const classes = computed(() => {
const baseClasses = [centerClass, SIZE_CLASSNAMES[props.size], { [inheritColorClass]: props.inheritColor }];
const fullScreenClasses = [name, fullscreenClass, centerClass, overlayClass];

methods: {
countDelay() {
this.delayShowLoading = false;
const timer = setTimeout(() => {
this.delayShowLoading = true;
clearTimeout(timer);
}, this.delay);
},
},
return {
baseClasses,
attachClasses: baseClasses.concat([name, fullClass, { [overlayClass]: props.showOverlay }]),
withContentClasses: baseClasses.concat([
name,
fullClass,
{
[overlayClass]: props.showOverlay,
},
]),
fullScreenClasses,
normalClasses: baseClasses.concat([name]),
};
});

const loadingRef = computed(() => props.loading);
watch([loadingRef], (value) => {
if (value) {
countDelay();
lockFullscreen.value && addClass(document.body, lockClass);
} else {
lockFullscreen.value && removeClass(document.body, lockClass);
}
});

onMounted(() => {
props.delay && countDelay();
});

return {
delayShowLoading,
styles,
showText,
hasContent,
classes,
lockFullscreen,
showWrapLoading,
showNormalLoading,
};
},
render() {
const { fullScreenClasses, baseClasses, withContentClasses, attachClasses, normalClasses } = this.classes;

const defaultIndicator = <GradientIcon size={this.size} />;
const indicator = this.loading && renderTNodeJSX(this, 'indicator', defaultIndicator);
const text = this.showText && <div class={`${prefix}-loading__text`}>{renderTNodeJSX(this, 'text')}</div>;

// full screen loading
if (this.fullscreen) {
if (!this.loading) return null;
return (
<div class={this.fullScreenClasses} style={this.styles} v-transfer-dom={this.attach}>
<div class={this.baseClasses}>
<div class={fullScreenClasses} style={this.styles} v-transfer-dom={this.attach}>
<div class={baseClasses}>
{indicator}
{text}
</div>
Expand All @@ -138,7 +125,7 @@ export default defineComponent({
<div class={relativeClass}>
{renderContent(this, 'default', 'content')}
{this.showWrapLoading && (
<div class={this.withContentClasses} style={this.styles}>
<div class={withContentClasses} style={this.styles}>
{indicator}
{text}
</div>
Expand All @@ -150,7 +137,7 @@ export default defineComponent({
// transfer parent node
if (this.attach) {
return (
<div class={this.attachClasses} style={this.styles} v-transfer-dom={this.attach}>
<div class={attachClasses} style={this.styles} v-transfer-dom={this.attach}>
{indicator}
{text}
</div>
Expand All @@ -159,7 +146,7 @@ export default defineComponent({

// Normal Loading without overlay or content
return (
<div class={this.normalClasses} style={this.styles}>
<div class={normalClasses} style={this.styles}>
{indicator}
{text}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/loading/plugin.tsx
@@ -1,4 +1,4 @@
import { App, Plugin, createApp, defineComponent, h } from 'vue';
import { App, Plugin, createApp, defineComponent, h, reactive } from 'vue';
import LoadingComponent from './loading';
import { prefix } from '../config';
import { getAttach, removeClass } from '../utils/dom';
Expand All @@ -9,11 +9,11 @@ const lockClass = `${prefix}-loading--lock`;
let fullScreenLoadingInstance: LoadingInstance = null;

function createLoading(props: TdLoadingProps): LoadingInstance {
const options = { ...props };
const component = defineComponent({
data() {
setup() {
const loadingOptions = reactive(props);
return {
loadingOptions: options as Record<string, any>,
loadingOptions,
};
},
render() {
Expand Down
1 change: 0 additions & 1 deletion test/ssr/__snapshots__/ssr.test.js.snap
Expand Up @@ -19854,7 +19854,6 @@ exports[`ssr snapshot test renders ./examples/list/demos/stripe.vue correctly 1`
exports[`ssr snapshot test renders ./examples/loading/demos/attach.vue correctly 1`] = `
<div class="tdesign-demo-block-column">
<div id="alice" class="loading-attach-demo__title">Hello, I&#39;m Alice.</div>
<div id="bob" class="loading-attach-demo__title">Hi, I&#39;m Bob.</div>
<div class="t-loading--center t-size-m t-loading t-loading--full t-loading__overlay" style=""><svg class="t-loading__gradient t-icon-loading" viewbox="0 0 14 14" version="1.1" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" size="medium">
<foreignObject x="1" y="1" width="12" height="12">
<div class="t-loading__gradient-conic"></div>
Expand Down
6 changes: 0 additions & 6 deletions test/unit/loading/__snapshots__/demo.test.js.snap
Expand Up @@ -10,12 +10,6 @@ exports[`Loading Loading attachVue demo works fine 1`] = `
>
Hello, I'm Alice.
</div>
<div
class="loading-attach-demo__title"
id="bob"
>
Hi, I'm Bob.
</div>
<div
class="t-loading--center t-size-m t-loading t-loading--full t-loading__overlay v-transfer-dom"
>
Expand Down

0 comments on commit baee597

Please sign in to comment.