refactor: rename options to option to align with ECharts itself

- removed built-in styles
- use ECharts' built-in throttle
- generate bundled .d.ts, hack three methods for now
- loadingOptions can be any object type
This commit is contained in:
Justineo
2021-02-19 17:54:08 +08:00
parent ee12ad9658
commit 419d79f86f
10 changed files with 258 additions and 507 deletions

View File

@ -11,9 +11,9 @@ import {
onUnmounted,
h,
PropType
} from "vue";
} from "vue-demi";
import { init as initChart } from "echarts/core";
import { EChartsType, OptionType } from "@/types";
import { EChartsType, OptionType } from "./types";
import {
usePublicAPI,
useAutoresize,
@ -21,7 +21,6 @@ import {
useLoading,
loadingProps
} from "./composables";
import "./style.css";
type InitParameters = Parameters<typeof initChart>;
type ThemeParameter = InitParameters[1];
@ -30,7 +29,7 @@ type InitOptsParameter = InitParameters[2];
export default defineComponent({
name: "echarts",
props: {
options: Object as PropType<OptionType>,
option: Object as PropType<OptionType>,
theme: {
type: [Object, String] as PropType<ThemeParameter>
},
@ -47,12 +46,12 @@ export default defineComponent({
) as InitOptsParameter;
const root = ref<HTMLElement>();
const chart = shallowRef<EChartsType>();
const manualOptions = shallowRef<OptionType>();
const realOptions = computed(
() => manualOptions.value || props.options || Object.create(null)
const manualOption = shallowRef<OptionType>();
const realOption = computed(
() => manualOption.value || props.option || Object.create(null)
);
function init(options?: OptionType) {
function init(option?: OptionType) {
if (chart.value || !root.value) {
return;
}
@ -83,18 +82,18 @@ export default defineComponent({
}
});
instance.setOption(options || realOptions.value, true);
instance.setOption(option || realOption.value, true);
}
function mergeOptions(options: OptionType, ...rest: any[]) {
function setOption(option: OptionType, ...rest: any[]) {
if (props.manualUpdate) {
manualOptions.value = options;
manualOption.value = option;
}
if (!chart.value) {
init(options);
init(option);
} else {
chart.value.setOption(options, ...rest);
chart.value.setOption(option, ...rest);
}
}
@ -114,18 +113,18 @@ export default defineComponent({
loading,
loadingOptions
} = toRefs(props);
let unwatchOptions: (() => void) | null = null;
let unwatchOption: (() => void) | null = null;
watch(
manualUpdate,
manualUpdate => {
if (typeof unwatchOptions === "function") {
unwatchOptions();
unwatchOptions = null;
if (typeof unwatchOption === "function") {
unwatchOption();
unwatchOption = null;
}
if (!manualUpdate) {
unwatchOptions = watch(
() => props.options,
unwatchOption = watch(
() => props.option,
(val, oldVal) => {
if (!val) {
return;
@ -133,12 +132,12 @@ export default defineComponent({
if (!chart.value) {
init();
} else {
// mutating `options` will lead to merging
// mutating `option` will lead to merging
// replacing it with new reference will lead to not merging
// eg.
// `this.options = Object.assign({}, this.options, { ... })`
// `this.option = Object.assign({}, this.option, { ... })`
// will trigger `this.chart.setOption(val, true)
// `this.options.title.text = 'Trends'`
// `this.option.title.text = 'Trends'`
// will trigger `this.chart.setOption(val, false)`
chart.value.setOption(val, val !== oldVal);
}
@ -170,10 +169,10 @@ export default defineComponent({
useLoading(chart, loading, loadingOptions);
useAutoresize(chart, autoresize, root, realOptions);
useAutoresize(chart, autoresize, root, realOption);
onMounted(() => {
if (props.options) {
if (props.option) {
init();
}
});
@ -182,7 +181,7 @@ export default defineComponent({
return {
root,
mergeOptions,
setOption,
...publicApi
};
},

View File

@ -1,11 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Ref } from "vue";
import { EChartsType, OptionType } from "@/types";
import { Ref } from "vue-demi";
import { EChartsType, OptionType } from "../types";
const METHOD_NAMES = [
"getWidth",
"getHeight",
"getDom",
"getOption",
"resize",
"dispatchAction",
@ -24,7 +23,7 @@ type PublicMethods = Pick<EChartsType, MethodName>;
export function usePublicAPI(
chart: Ref<EChartsType | undefined>,
init: (options?: OptionType) => void
init: (option?: OptionType) => void
) {
function makePublicMethod<T extends MethodName>(
name: T
@ -41,6 +40,12 @@ export function usePublicAPI(
};
}
function makeAnyMethod<T extends MethodName>(
name: T
): (...args: any[]) => ReturnType<EChartsType[T]> {
return makePublicMethod(name) as any;
}
function makePublicMethods(): PublicMethods {
const methods = Object.create(null);
METHOD_NAMES.forEach(name => {
@ -50,5 +55,10 @@ export function usePublicAPI(
return methods as PublicMethods;
}
return makePublicMethods();
return {
...makePublicMethods(),
dispatchAction: makeAnyMethod("dispatchAction"),
getDataURL: makeAnyMethod("getDataURL"),
getConnectedDataURL: makeAnyMethod("getConnectedDataURL")
};
}

View File

@ -1,12 +1,13 @@
import { Ref, watch } from "vue";
import { Ref, watch } from "vue-demi";
import { throttle } from "echarts/core";
import { addListener, removeListener, ResizeCallback } from "resize-detector";
import { EChartsType, OptionsType } from "@/types";
import { EChartsType, OptionType } from "../types";
export function useAutoresize(
chart: Ref<EChartsType | undefined>,
autoresize: Ref<boolean>,
root: Ref<HTMLElement | undefined>,
options: Ref<OptionsType>
option: Ref<OptionType>
): void {
let resizeListener: ResizeCallback | null = null;
let lastArea = 0;
@ -22,16 +23,16 @@ export function useAutoresize(
watch([root, chart, autoresize], ([root, chart, autoresize], _, cleanup) => {
if (root && chart && autoresize) {
lastArea = getArea();
resizeListener = () => {
resizeListener = throttle(() => {
if (lastArea === 0) {
chart.setOption(Object.create(null), true);
chart.resize();
chart.setOption(options.value, true);
chart.setOption(option.value, true);
} else {
chart.resize();
}
lastArea = getArea();
};
}, 100);
addListener(root, resizeListener);
}

View File

@ -1,25 +1,10 @@
import { Ref, PropType, watchEffect } from "vue";
import { EChartsType } from "@/types";
export interface LoadingOptions {
text?: string;
color?: string;
textColor?: string;
maskColor?: string;
zlevel?: number;
fontSize?: number;
showSpinner?: boolean;
spinnerRadius?: number;
lineWidth?: number;
fontWeight?: string | number;
fontStyle?: string;
fontFamily?: string;
}
import { Ref, watchEffect } from "vue-demi";
import { EChartsType } from "../types";
export function useLoading(
chart: Ref<EChartsType | undefined>,
loading: Ref<boolean>,
loadingOptions?: Ref<LoadingOptions | undefined>
loadingOptions?: Ref<object | undefined>
): void {
watchEffect(() => {
const instance = chart.value;
@ -37,5 +22,5 @@ export function useLoading(
export const loadingProps = {
loading: Boolean,
loadingOptions: Object as PropType<LoadingOptions>
loadingOptions: Object
};

View File

@ -11,14 +11,14 @@
<button @click="mutate" :disabled="useRef">Mutate data</button>
<button @click="set" :disabled="!useRef">Set data</button>
<button @click="mutateLoadingOptions" :disabled="!loading">
Mutate loading options
Mutate loading option
</button>
</div>
<v-chart
style="width: 100%; height: 400px"
ref="foo"
:autoresize="autoresize"
:options="realOptions"
:option="realOption"
:loading="loading"
:loading-options="loadingOptions"
:theme="defaultTheme ? null : 'dark'"
@ -36,49 +36,48 @@ import * as echarts from "echarts/core";
import { GridComponent } from "echarts/components";
import { LineChart } from "echarts/charts";
import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
echarts.use([GridComponent, LineChart, CanvasRenderer, SVGRenderer]);
export default defineComponent({
name: "App",
components: {
VChart
VChart,
},
setup() {
const foo = ref();
const options = reactive({
const option = reactive({
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value"
type: "value",
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: "line"
}
]
type: "line",
},
],
});
const optionsRef = ref({
const optionRef = ref({
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value"
type: "value",
},
series: [
{
data: [233, 128, 184, 302, 208, 287, 212],
type: "line"
}
]
type: "line",
},
],
});
const loadingOptions = reactive({
text: "正在加载..."
text: "正在加载...",
});
const autoresize = ref<boolean>(true);
@ -87,26 +86,26 @@ export default defineComponent({
const useRef = ref<boolean>(false);
const loading = ref<boolean>(false);
const initOptions = computed(() => ({
renderer: useSvg.value ? "svg" : "canvas"
renderer: useSvg.value ? "svg" : "canvas",
}));
const realOptions = computed(() =>
useRef.value ? optionsRef.value : options
const realOption = computed(() =>
useRef.value ? optionRef.value : option
);
function mutate() {
options.series[0].data = [150, 230, 224, 218, 135, 147, 260].map(
val => val + Math.round(50 * Math.random())
option.series[0].data = [150, 230, 224, 218, 135, 147, 260].map(
(val) => val + Math.round(50 * Math.random())
);
}
function set() {
optionsRef.value = {
optionRef.value = {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value"
type: "value",
},
series: [
{
@ -117,11 +116,11 @@ export default defineComponent({
302 + Math.round(50 * Math.random()),
208 + Math.round(50 * Math.random()),
287 + Math.round(50 * Math.random()),
212 + Math.round(50 * Math.random())
212 + Math.round(50 * Math.random()),
],
type: "line"
}
]
type: "line",
},
],
};
}
@ -135,7 +134,7 @@ export default defineComponent({
}
return {
realOptions,
realOption,
autoresize,
defaultTheme,
useSvg,
@ -147,8 +146,8 @@ export default defineComponent({
loadingOptions,
mutateLoadingOptions,
foo,
log
log,
};
}
},
});
</script>

View File

@ -1,4 +0,0 @@
.echarts {
width: 400px;
height: 300px;
}