-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Rendering tooltips and dataView with slots #838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 8.0
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
The limitation is that the tooltip detached from the current component tree, not provide/inject will try teleport next
How about we go over the external API first? |
We could consider supporting multiple or dynamic slots—e.g., tooltip, tooltip-series-line, etc.—which could then map to the corresponding tooltip configurations at different levels. The idea would be to let slots fully override the tooltip formatters.
From what I’ve seen, showTip is triggered whenever the pointer moves within the chart. Could you clarify what specific information is missing for our use case?
If we can implement tooltips as slots, we should be able to make the content reactive to async states or even render The most tricky part I can think of is when and how to update the slot props when |
I also considered this, but then I remembered that For multiple series of the same type but with different formatters, should we add a modifier like Most series support formatter for each single datum. But I think users can achieve similar functionality by using a series-level tooltip formatter and
I think the slotProps should be the
We could override users' formatter(params) {
// slotName.slotProps is a shallowRef
slotName.slotProps.value = params
return toHtml(slotContent)
} The slot props would be updated whenever |
How about use the property path of the
<template #tooltip />
<template #tooltip:xAxis[1] />
<template #tooltip:series[3] />
<template #tooltip:series[3].data[2] />
<template #tooltip:baseOption />
<template #tooltip:[1].option /> |
I feel the base functionality is mostly complete. However, it currently doesn't support dynamically changing slot names or adding/removing slots. With the current approach, implementing this might be tricky. For example: <script setup>
// Work
const slotNames = ref(['tooltip', 'tooltip:series[0]'])
// Don't work
setTimeout(()=>{
slotNames.value.push('tooltip:series[1]')
},1000)
</script>
<template>
<v-chart>
<template v-for="slotName in slotNames" #[slotName]={ params } >
<!-- content -->
</template>
</v-chart>
</template> Edit: Supported in the latest commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the design and implementation is pretty good now. Left a few small questions.
* chore: fix warnings and errors in demo (#839) * chore: suppress warning in demo * chore: prevent multiple intializations of esbuild-wasm in demo HMR * feat: dynamically update the theme (#841) Co-authored-by: GU Yiling <justice360@gmail.com> * feat: add dataView slot * vibe docs --------- Co-authored-by: GU Yiling <justice360@gmail.com>
if (isValidSlotName(key)) { | ||
return true; | ||
} | ||
console.warn(`[vue-echarts] Invalid slot name: ${key}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can import the warn
util from vue
as it will show component stack trace.
console.warn(`[vue-echarts] Invalid slot name: ${key}`); | ||
return false; | ||
}); | ||
if (newSlotNames.join() !== slotNames.join()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd better implement a isSameSet
for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have another option: how do we support [contentToOption](https://echarts.apache.org/zh/option.html#toolbox.feature.dataView.contentToOption)
?
const SLOT_PATH_MAP = { | ||
tooltip: ["tooltip", "formatter"], | ||
dataView: ["toolbox", "feature", "dataView", "optionToContent"], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as const
or we should make this config as SLOT_CONFIG
which includes slot paths and propNames, etc.
tooltip: ["tooltip", "formatter"], | ||
dataView: ["toolbox", "feature", "dataView", "optionToContent"], | ||
}; | ||
type SlotPrefix = keyof typeof SLOT_PATH_MAP; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type SlotPrefix = keyof typeof SLOT_PATH_MAP; | |
type SlotPrefix = keyof typeof SLOT_PATH_MAP; | |
type SlotName = `${SlotPrefx}-${string}`; |
}; | ||
type SlotPrefix = keyof typeof SLOT_PATH_MAP; | ||
|
||
function isValidSlotName(key: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function isValidSlotName(key: string) { | |
function isValidSlotName(key: string): key is SlotName { |
Object.entries(slots) | ||
.filter(([key]) => isValidSlotName(key)) | ||
.map(([key, slot]) => { | ||
const propName = key.startsWith("tooltip") ? "params" : "option"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be defined in slot configs instead of being hard-coded here.
.map(([key, slot]) => { | ||
const propName = key.startsWith("tooltip") ? "params" : "option"; | ||
const slotContent = initialized[key] | ||
? slot?.({ [propName]: params[key] }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second thought, is the additional object wrapper necessary here? Can we just use slot?.(params[key])
here?
Summary
This PR proposes a new
echarts-tooltip
component. The component provides aformatter
method that can be used with ECharts'tooltip.formatter
option, allowing users to define tooltip content using Vue's template syntax.The slot props are what users passed to the
formatter
method. The component teleports its slot content to a detached DOM element so that it does not exist in the html body.Example
For a practical example, see the Line Chart in the demo preview which features a Pie Chart as its tooltip.
Design Rationale
Why a Separate Component?
VChart
would make it difficult to handle these various use cases intuitively.The
showTip
event doesn't provide sufficient information for implementing the approach suggested in Use Web Components without native class support detection #836 (comment).We may need to support ECharts' asynchronous tooltip formatting:
By making it a separate component, users can:
@Justineo , do you have any other ideas for us to try?