budibase/packages/standard-components/src/Chart/Chart.svelte

170 lines
4.3 KiB
Svelte

<script context="module">
//expose chart types for use or reference outside compnent
export const chartTypes = britecharts ? Object.keys(britecharts) : null
//expose chart color schemas for use or reference outside compnent
export const colorSchemas = britecharts
? britecharts.colors.colorSchemas
: null
//export color gradients for use or reference outside the component
export const colorGradients = britecharts
? britecharts.colors.colorGradients
: null
export const getColorSchema = color =>
color ? colorSchemas[color] : colorSchemas["britecharts"]
export const getChartGradient = gradient =>
gradient ? colorGradients[gradient] : null
</script>
<script>
import { beforeUpdate } from "svelte"
import britecharts from "britecharts"
import { select } from "d3-selection"
import { onMount } from "svelte"
import shortid from "shortid"
const _id = shortid.generate()
export let type = "bar"
export let data = []
export let tooltipProps = null
export let legendProps = null
export let useTooltip = false
export let useLegend = false
export let tooltip = null //can bind to outside the component and therefore access
let chartElement = null
let chartContainer = null
let tooltipContainer = null
let legendContainer = null
let legend = null
let chart = chartTypes.includes(type) ? britecharts[type]() : null
const chartClass = `chart-container-${_id}`
const legendClass = `legend-container-${_id}`
onMount(() => {
if (chart) {
chartContainer = select(`.${chartClass}`)
bindChartUIProps()
bindChartEvents()
chartContainer.datum(data).call(chart)
bindChartTooltip()
bindChartLegend()
} else {
console.error("Britecharts could not be found")
}
})
function bindChartTooltip() {
if (useTooltip) {
tooltip = britecharts.miniTooltip()
bindProps(tooltip, tooltipProps)
tooltipContainer = select(`.${chartClass} .metadata-group`)
tooltipContainer.datum([]).call(tooltip)
}
}
function bindChartLegend() {
if (useLegend) {
if (!Array.isArray(data)) {
console.warn("Cannot use legend as data is not an array")
return
}
let excludeProps = []
legend = britecharts.legend()
if (!legendProps || !legendProps.width) {
excludeProps = ["width"]
legend.width(chart.width())
}
if (legendProps) {
bindProps(legend, legendProps, excludeProps)
}
legendContainer = select(`.${legendClass}`)
legendContainer.datum(data).call(legend)
}
}
function bindChartEvents() {
if ($$props.on) {
const events = Object.entries($$props.on)
for (let [type, fn] of events) {
if (fn) {
chart.on(type, fn)
}
}
}
}
function bindChartUIProps() {
const excludeProps = [
"data",
"type",
"on",
"useTooltip",
"tooltip",
"tooltipProps",
"legendProps",
"useLegend",
]
if (!$$props.width) {
chart.width(chartElement.getBoundingClientRect().width)
}
bindProps(chart, $$props, excludeProps)
}
function bindProps(element, elProps, excludeArray) {
if (elProps) {
const props = excludeArray
? Object.entries(excludeProps(elProps, excludeArray))
: Object.entries(elProps)
const validElementProps = Object.getOwnPropertyNames(element)
for (let [prop, value] of props) {
if (validElementProps.includes(prop)) {
if (!!value) {
chart[prop](value)
}
} else {
console.warn(
`${type} - ${prop} is an unrecognised chart prop and wont be applied`
)
}
}
}
}
function excludeProps(props, propsToExclude) {
const modifiedProps = {}
for (const prop in props) {
if (!propsToExclude.includes(prop)) {
modifiedProps[prop] = props[prop]
}
}
return modifiedProps
}
$: validChartProps = chart ? Object.getOwnPropertyNames(chart) : null
</script>
<div bind:this={chartElement} class={chartClass} />
{#if useLegend}
<div class={legendClass} />
{/if}