@mozaic-ds/chart
Version:
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
263 lines (249 loc) • 7.28 kB
text/typescript
import { reactive, ref, type Ref } from 'vue';
import PatternFunctions from './PatternFunctions';
import { addAlpha } from './ColorFunctions';
import type { HTMLLegendPlugin } from '../types/Chart';
import {
getOrCreateLegendList,
createHtmlLegendListElement,
createLegendElementWithPatterns,
createLegendElementWithCheckbox,
createHtmlLegendItemText,
createLegendElementWithSquareArea,
type ChartItem
} from './ChartsCommonLegend';
const { getPatternIndexWithShift } = PatternFunctions();
export default function () {
const borderWidth = ref(2);
const onHoverIndex: { dataSetIndex: number; columnIndex: number } = reactive({
dataSetIndex: -1,
columnIndex: -1
});
interface Dataset {
data: any;
label: string;
type: any;
fill?: boolean;
}
function getHtmlLegendPlugin(
legendContainer: Ref,
selectMode: Ref<boolean>,
onHoverIndex: any,
disableAccessibility: Ref<boolean>,
patternsColors: Ref<string[]>,
patternsList: Ref<
((
hover: boolean,
color: string,
disableAccessibility: boolean
) => CanvasPattern)[]
>
): HTMLLegendPlugin[] {
return [
{
id: 'htmlLegend',
afterUpdate(chart: any) {
const ul: HTMLLIElement = getOrCreateLegendList(
legendContainer,
'column'
);
ul.style.display = 'flex';
ul.style.margin = '1.375rem 1.0625rem';
ul.style.flexDirection = 'row-reverse';
ul.style.flexWrap = 'wrap';
ul.style.justifyContent = 'flex-end';
while (ul.firstChild) {
ul.firstChild.remove();
}
const items: ChartItem[] =
chart.options.plugins.legend.labels.generateLabels(chart);
items
.sort((a, b) => b.datasetIndex - a.datasetIndex)
.forEach((item: ChartItem): void => {
const li: HTMLElement = createHtmlLegendListElement(
chart,
selectMode,
item.datasetIndex
);
let liContent: HTMLElement;
if (!selectMode.value) {
if (item?.lineCap) {
liContent = createLegendElementWithSquareArea(item);
} else {
liContent = createLegendElementWithPatterns(
item,
chart,
{ datasetIndex: -1 },
disableAccessibility.value,
patternsColors.value,
patternsList.value,
false
);
}
} else {
liContent = createLegendElementWithCheckbox(
chart,
item,
selectMode,
{ datasetIndex: -1 },
patternsColors.value,
false
);
}
liContent.style.boxSizing = 'border-box';
li.style.marginRight = '10px';
li.style.marginBottom = '2px';
li.appendChild(liContent);
li.appendChild(createHtmlLegendItemText(item));
ul.appendChild(li);
});
}
}
];
}
function privateGetHtmlLegendPlugin(
legendContainer: Ref,
selectMode: Ref<boolean>,
disableAccessibility: Ref<boolean>,
patternsColors: Ref<string[]>,
patternsList: Ref<
((
hover: boolean,
color: string,
disableAccessibility: boolean
) => CanvasPattern)[]
>
) {
return getHtmlLegendPlugin(
legendContainer,
selectMode,
onHoverIndex,
disableAccessibility,
patternsColors,
patternsList
);
}
function getMixedDatasets(
datasets: Dataset[],
disableAccessibility: boolean,
patternsColors: string[],
patternsList: ((
hover: boolean,
color: string,
disableAccessibility: boolean
) => CanvasPattern)[],
patternShifting?: number
) {
// Hack to force refresh
const borderWithValue = borderWidth.value;
return datasets.map((dataset, index) => {
const isBarChart = dataset.type === 'bar';
return {
type: dataset.type,
fill: isBarChart ? null : false,
borderWidth: function () {
return disableAccessibility && isBarChart ? 1 : borderWithValue;
},
borderColor: function (context: any) {
return disableAccessibility && isBarChart
? '#00000000'
: isBarChart
? getBorderColor(
index,
context.index,
patternsColors,
patternShifting
)
: patternsColors[index % patternsColors.length];
},
backgroundColor: function (context: any) {
return getPattern(
index,
context.index,
disableAccessibility,
patternsColors,
patternsList,
patternShifting
);
},
yAxisID: isBarChart ? 'A' : 'B',
pointStyle: index % 2 === 0 ? 'rectRot' : 'circle',
data: dataset.data,
label: dataset.label,
pointBackgroundColor: '#FFFFFF',
pointRadius: 5,
order: dataset.type === 'line' ? 0 : 1
};
});
}
function getBorderColor(
dataSetIndex: number,
contextIndex: number,
patternsColors: string[],
patternShifting?: number
) {
const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
if (displayFullOpacity(dataSetIndex, contextIndex)) {
return patternsColors[index];
} else {
return addAlpha(patternsColors[index], 0.2);
}
}
function getPattern(
dataSetIndex: number,
contextIndex: number,
disableAccessibility: boolean,
patternsColors: string[],
patternsList: ((
hover: boolean,
color: string,
disableAccessibility: boolean
) => CanvasPattern)[],
patternShifting?: number
) {
const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
if (displayFullOpacity(dataSetIndex, contextIndex)) {
return patternsList[index](
false,
patternsColors[index],
disableAccessibility
);
} else {
return patternsList[index](
true,
patternsColors[index],
disableAccessibility
);
}
}
function displayFullOpacity(
dataSetIndex: number,
contextIndex: number
): boolean {
return (
nothingHovered() ||
columnHovered(dataSetIndex, contextIndex) ||
legendHovered(dataSetIndex)
);
}
function nothingHovered(): boolean {
return onHoverIndex.dataSetIndex < 0;
}
function columnHovered(dataSetIndex: number, contextIndex: number): boolean {
return (
onHoverIndex.dataSetIndex === dataSetIndex &&
onHoverIndex.columnIndex === contextIndex
);
}
function legendHovered(dataSetIndex: number): boolean {
return (
onHoverIndex.dataSetIndex === dataSetIndex && onHoverIndex.columnIndex < 0
);
}
return {
privateGetHtmlLegendPlugin,
getMixedDatasets,
getBorderColor,
getPattern,
onHoverIndex
};
}