This commit is contained in:
Gerard Burns 2024-04-04 07:53:30 +01:00
parent b9d36f9802
commit 793e5cb4cb
3 changed files with 186 additions and 64 deletions

View File

@ -11,7 +11,6 @@
let hovering = false let hovering = false
let wrapper let wrapper
let resetWrapper
let currentTooltip let currentTooltip
let previousTooltip let previousTooltip
@ -30,13 +29,19 @@
} }
} }
const updatePosition = (anchor, currentTooltip, previousTooltip, wrapper, resetWrapper) => { const updatePosition = (anchor, currentTooltip, previousTooltip, wrapper) => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (anchor == null || currentTooltip == null || previousTooltip == null || wrapper == null || resetWrapper == null) { if (anchor == null || currentTooltip == null || previousTooltip == null || wrapper == null) {
return; return;
} }
const rect = anchor.getBoundingClientRect(); const rect = anchor.getBoundingClientRect();
const previousStyles = window.getComputedStyle(previousTooltip?.firstChild)
const currentStyles = window.getComputedStyle(currentTooltip?.firstChild)
console.log(previousStyles.backgroundColor);
console.log(currentStyles.backgroundColor);
console.log("")
currentTooltipWidth = currentTooltip.clientWidth currentTooltipWidth = currentTooltip.clientWidth
currentTooltipHeight = currentTooltip.clientHeight currentTooltipHeight = currentTooltip.clientHeight
@ -49,23 +54,32 @@
currentY = rect.y currentY = rect.y
const fadeIn = [{ opacity: "0" }, { opacity: "1" }]; const fadeIn = [{ opacity: "0" }, { opacity: "1" }];
const fadeOut = [{ opacity: "1" }, { opacity: "0" }]; const fadeOut = [{ opacity: "1" }, { opacity: "0" }];
const color = [{ offset: 0, backgroundColor: previousStyles.backgroundColor }, { offset: 0.85, backgroundColor: currentStyles.backgroundColor }, { offset: 1, backgroundColor: "black" }];
const fadeInTiming = { const fadeInTiming = {
duration: 150, duration: 100,
delay: 150, delay: 100,
iterations: 1, iterations: 1,
easing: "ease-in", easing: "ease-in",
fill: "both" fill: "both"
}; };
const fadeOutTiming = { const fadeOutTiming = {
duration: 150, duration: 100,
iterations: 1, iterations: 1,
easing: "ease-in", easing: "ease-in",
fill: "forwards" fill: "forwards"
}; };
const colorTiming = {
duration: 300,
iterations: 1,
easing: "ease-in",
fill: "both"
};
currentTooltip.animate(fadeIn, fadeInTiming); currentTooltip.animate(fadeIn, fadeInTiming);
previousTooltip.animate(fadeOut, fadeOutTiming); previousTooltip.animate(fadeOut, fadeOutTiming);
wrapper.animate(color, colorTiming);
// Bypass animations if the tooltip has only just been opened // Bypass animations if the tooltip has only just been opened
if (initialShow) { if (initialShow) {
@ -74,18 +88,14 @@
previousTooltip.style.visibility = "hidden" previousTooltip.style.visibility = "hidden"
wrapper.style.transition = "none"; wrapper.style.transition = "none";
wrapper.style.width = `${currentTooltipWidth}px` //wrapper.style.width = `${currentTooltipWidth}px`
wrapper.style.height = `${currentTooltipHeight}px` //wrapper.style.height = `${currentTooltipHeight}px`
wrapper.style.top = `${currentY}px` wrapper.style.top = `${currentY}px`
wrapper.style.left = `${currentX}px` wrapper.style.left = `${currentX}px`
resetWrapper.style.transition = "none";
resetWrapper.style.top = `${-currentY}px`
resetWrapper.style.left = `${-currentX}px`
requestAnimationFrame(() => { requestAnimationFrame(() => {
wrapper.style.removeProperty("transition"); wrapper.style.removeProperty("transition");
resetWrapper.style.removeProperty("transition");
}) })
} else { } else {
previousTooltip.style.removeProperty("visibility"); previousTooltip.style.removeProperty("visibility");
@ -93,7 +103,7 @@
}) })
} }
$: updatePosition(anchor, currentTooltip, previousTooltip, wrapper, resetWrapper) $: updatePosition(anchor, currentTooltip, previousTooltip, wrapper)
$: handleVisibilityChange(visible, hovering) $: handleVisibilityChange(visible, hovering)
const handleMouseenter = (e) => { const handleMouseenter = (e) => {
@ -110,10 +120,10 @@
bind:this={wrapper} bind:this={wrapper}
on:mouseenter={handleMouseenter} on:mouseenter={handleMouseenter}
on:mouseleave={handleMouseleave} on:mouseleave={handleMouseleave}
style:width={`${currentTooltipWidth}px`}
style:height={`${currentTooltipHeight}px`}
style:left={`${currentX}px`} style:left={`${currentX}px`}
style:top={`${currentY}px`} style:top={`${currentY}px`}
style:width={`${currentTooltipWidth}px`}
style:height={`${currentTooltipHeight}px`}
class="tooltip" class="tooltip"
class:visible={visible || hovering} class:visible={visible || hovering}
> >
@ -121,15 +131,14 @@
using the same values as the root wrapper, while still being occluded by it. using the same values as the root wrapper, while still being occluded by it.
--> -->
<div class="screenSizeAbsoluteWrapper" <div class="screenSizeAbsoluteWrapper"
bind:this={resetWrapper}
style:left={`${-currentX}px`} style:left={`${-currentX}px`}
style:top={`${-currentY}px`} style:top={`${-currentY}px`}
> >
<div <div
style:left={`${currentX}px`}
style:top={`${currentY}px`}
bind:this={currentTooltip} bind:this={currentTooltip}
class="currentContent" class="currentContent"
style:left={`${currentX}px`}
style:top={`${currentY}px`}
> >
<slot /> <slot />
</div> </div>
@ -153,16 +162,16 @@
background-color: var(--spectrum-global-color-gray-200); background-color: var(--spectrum-global-color-gray-200);
border-radius: 5px; border-radius: 5px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0; opacity: 0;
overflow: hidden; overflow: hidden;
transition: width 300ms ease-in, height 300ms ease-in, top 300ms ease-in, left 300ms ease-in; transition: width 300ms ease-in, height 300ms ease-in, top 300ms ease-in, left 300ms ease-in;
} }
.screenSizeAbsoluteWrapper { .screenSizeAbsoluteWrapper {
width: 200vw;
height: 200vh;
position: absolute; position: absolute;
width: 100vw;
height: 100vh;
transition: top 300ms ease-in, left 300ms ease-in; transition: top 300ms ease-in, left 300ms ease-in;
} }

View File

@ -12,9 +12,12 @@
let contextTooltipId = 0; let contextTooltipId = 0;
let contextTooltipAnchor = null let contextTooltipAnchor = null
let contextTooltipOption = null let currentOption = null
let previousContextTooltipOption = null let previousOption = null
let contextTooltipVisible = false let contextTooltipVisible = false
let currentOptionSupport = {}
let previousOptionSupport = {}
const TypeIconMap = { const TypeIconMap = {
text: "Text", text: "Text",
@ -78,6 +81,8 @@
const getOptionIcon = optionKey => { const getOptionIcon = optionKey => {
const option = schema[optionKey] const option = schema[optionKey]
if (!option) return ""
if (option.autocolumn) { if (option.autocolumn) {
return "MagicWand" return "MagicWand"
} }
@ -94,14 +99,7 @@
const getOptionIconTooltip = optionKey => { const getOptionIconTooltip = optionKey => {
const option = schema[optionKey] const option = schema[optionKey]
return option.type; return option?.type;
}
const getOptionTooltip = optionKey => {
const support = fieldSupport[optionKey]?.support;
const message = fieldSupport[optionKey]?.message;
return message
} }
const isOptionEnabled = optionKey => { const isOptionEnabled = optionKey => {
@ -116,23 +114,67 @@
return true return true
} }
const getSupportLevel = (optionKey) => {
const level = fieldSupport[optionKey]?.level;
if (level === validatorConstants.unsupported) {
return {
class: "supportLevelUnsupported",
icon: "Alert",
iconTooltip: fieldSupport[optionKey]?.message,
text: "Unsupported"
}
}
if (level === validatorConstants.partialSupport) {
return {
class: "supportLevelPartialSupport",
icon: "AlertCheck",
iconTooltip: fieldSupport[optionKey]?.message,
text: "Partial Support"
}
}
if (level === validatorConstants.supported) {
return {
class: "supportLevelSupported",
icon: "CheckmarkCircle",
iconTooltip: fieldSupport[optionKey]?.message,
text: "Supported"
}
}
return {
class: "supportLevelUnsupported",
icon: "Alert",
iconTooltip: "",
text: "Unsupported"
}
}
$: currentOptionSupport = getSupportLevel(currentOption)
$: previousOptionSupport = getSupportLevel(previousOption)
const onOptionMouseenter = (e, option, idx) => { const onOptionMouseenter = (e, option, idx) => {
console.log(option)
contextTooltipId += 1; contextTooltipId += 1;
const invokedContextTooltipId = contextTooltipId const invokedContextTooltipId = contextTooltipId
setTimeout(() => { setTimeout(() => {
if (contextTooltipId === invokedContextTooltipId) { if (contextTooltipId === invokedContextTooltipId) {
contextTooltipAnchor = e.target; contextTooltipAnchor = e.target;
previousContextTooltipOption = contextTooltipOption; previousOption = currentOption;
contextTooltipOption = option; currentOption = option;
contextTooltipVisible = true; contextTooltipVisible = true;
currentOptionSupport = getSupportLevel(currentOption)
previousOptionSupport = getSupportLevel(previousOption)
} }
}, 200) }, 200)
} }
const onOptionMouseleave = (e, option) => { const onOptionMouseleave = (e, option) => {
setTimeout(() => { setTimeout(() => {
if (option === contextTooltipOption) { if (option === currentOption) {
contextTooltipVisible = false; contextTooltipVisible = false;
} }
}, 600) }, 600)
@ -156,39 +198,80 @@
offset={20} offset={20}
> >
<div <div
class="tooltipContents" class={`tooltipContents ${currentOptionSupport.class}`}
> >
{#if contextTooltipOption} <div class={`supportLevel ${currentOptionSupport.class}`}>
<div class="contextTooltipHeader"> <Icon tooltip={currentOptionSupport.iconTooltip} name={currentOptionSupport.icon} />
<Icon name={getOptionIcon(contextTooltipOption)} /> <p>{currentOptionSupport.text}</p>
<span>{contextTooltipOption}</span>
</div> </div>
<p>{getOptionTooltip(contextTooltipOption)}</p> <div class="contextTooltipContent">
<div class="contextTooltipHeader">
<Icon name={getOptionIcon(currentOption)} />
<span>{currentOption}</span>
</div>
{#if fieldSupport[currentOption]?.errors?.length > 0}
{#each (fieldSupport[currentOption].errors) as datum}
<p>{datum}</p>
{/each}
{:else if fieldSupport[currentOption]?.warnings?.length > 0}
{#each (fieldSupport[currentOption].warnings) as datum}
<p>{datum}</p>
{/each}
{/if} {/if}
</div> </div>
</div>
<div slot="previous" <div slot="previous"
class="tooltipContents" class={`tooltipContents ${previousOptionSupport.class}`}
> >
{#if previousContextTooltipOption} <div class={`supportLevel ${previousOptionSupport.class}`}>
<div class="contextTooltipHeader"> <Icon tooltip={previousOptionSupport.iconTooltip} name={previousOptionSupport.icon} />
<Icon name={getOptionIcon(previousContextTooltipOption)} /> <p>{previousOptionSupport.text}</p>
<span>{previousContextTooltipOption}</span>
</div> </div>
<p>{getOptionTooltip(previousContextTooltipOption)}</p> <div class="contextTooltipContent">
<div class="contextTooltipHeader">
<Icon name={getOptionIcon(previousOption)} />
<span>{previousOption}</span>
</div>
{#if fieldSupport[previousOption]?.errors?.length > 0}
{#each (fieldSupport[previousOption].errors) as datum}
<p>{datum}</p>
{/each}
{:else if fieldSupport[previousOption]?.warnings?.length > 0}
{#each (fieldSupport[previousOption].warnings) as datum}
<p>{datum}</p>
{/each}
{/if} {/if}
</div> </div>
</div>
</ContextTooltip> </ContextTooltip>
<style> <style>
.tooltipContents { .tooltipContents {
max-width: 200px; max-width: 400px;
background-color: var(--spectrum-global-color-gray-200); background-color: var(--spectrum-global-color-gray-200);
display: inline-block; display: block;
padding: 12px; padding: 0 0 12px 0 ;
border-radius: 5px; border-radius: 5px;
box-sizing: border-box; box-sizing: border-box;
} }
.tooltipContents.supportLevelUnsupported {
background-color: var(--red);
color: var(--ink)
}
.tooltipContents.supportLevelPartialSupport {
background-color: var(--yellow);
color: var(--ink)
}
.tooltipContents.supportLevelSupported {
background-color: var(--green);
color: var(--ink)
}
.contextTooltipHeader { .contextTooltipHeader {
display: flex; display: flex;
align-items: center; align-items: center;
@ -203,8 +286,12 @@
); );
} }
.contextTooltipContent {
padding: 0px 12px;
}
.contextTooltipHeader :global(svg) { .contextTooltipHeader :global(svg) {
color: var(--background); color: var(--ink);
margin-right: 5px; margin-right: 5px;
} }
@ -213,4 +300,27 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.supportLevel {
display: flex;
align-items: center;
height: var(--spectrum-alias-item-height-m);
padding: 0px var(--spectrum-alias-item-padding-m);
margin-bottom: 12px;
}
.supportLevel :global(svg) {
margin-right: 5px;
}
.supportLevel.supportLevelUnsupported {
background-color: var(--red-light)
}
.supportLevel.supportLevelPartialSupport {
background-color: var(--yellow-light)
}
.supportLevel.supportLevelSupported {
background-color: var(--green-light)
}
</style> </style>

View File

@ -1,3 +1,5 @@
import { capitalize } from 'lodash';
export const constants = { export const constants = {
warning: Symbol("values-validator-warning"), warning: Symbol("values-validator-warning"),
error: Symbol("values-validator-error"), error: Symbol("values-validator-error"),
@ -8,16 +10,17 @@ export const constants = {
export const validators = { export const validators = {
chart: (fieldSchema) => { chart: (fieldSchema) => {
console.log(fieldSchema);
try { try {
const response = { const response = {
support: null, level: null,
message: null, message: null,
warnings: [], warnings: [],
errors: [] errors: []
} }
const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"] const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"]
if (generalUnsupportedFields.includes(fieldSchema.type)) { if (generalUnsupportedFields.includes(fieldSchema.type)) {
response.errors.push(`${fieldSchema.type} columns can not be used as chart inputs.`) response.errors.push(`${capitalize(fieldSchema.type)} columns are unsupported.`)
} }
if (fieldSchema.type === "json") { if (fieldSchema.type === "json") {
@ -26,7 +29,7 @@ export const validators = {
if (fieldSchema.type === "string") { if (fieldSchema.type === "string") {
response.warnings.push( response.warnings.push(
"This column can be used as input for a chart, but non-numeric values may cause unexpected behavior.") "String columns can be used as input for a chart, but non-numeric values may cause unexpected behavior.")
} }
if (fieldSchema.type === "date") { if (fieldSchema.type === "date") {
response.warnings.push( response.warnings.push(
@ -40,13 +43,13 @@ export const validators = {
} }
if (response.errors.length > 0) { if (response.errors.length > 0) {
response.support = constants.unsupported response.level = constants.unsupported
response.message = "This column can not be used as a chart input." response.message = "This column can not be used as a chart input."
} else if (response.warnings.length > 0) { } else if (response.warnings.length > 0) {
response.support = constants.partialSupport response.level = constants.partialSupport
response.message = "This column can be used as a chart input, but certain values may cause issues." response.message = "This column can be used as a chart input, but certain values may cause issues."
} else { } else {
response.support = constants.supported response.level = constants.supported
response.message = "This column can be used as a chart input." response.message = "This column can be used as a chart input."
} }
@ -54,7 +57,7 @@ export const validators = {
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return { return {
support: constants.partialSupport, level: constants.partialSupport,
message: "There was an issue validating this field, it may not be fully supported for use with charts.", message: "There was an issue validating this field, it may not be fully supported for use with charts.",
warnings: [], warnings: [],
errors: [] errors: []