UNPKG

webwriter-chart

Version:

ww-chart is an interactive data visualization widget for the WebWriter tool that implements various charts and diagrams for static and exploratory data visualization.

1,140 lines (1,090 loc) 54.6 kB
import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { randomColor, //csvToDataset, randomNumber, randomString, //readFileInput, } from "../../functions"; import style from "./ScatterDatasetInput.style"; import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"; import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.component.js"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js"; import SlColorPicker from "@shoelace-style/shoelace/dist/components/color-picker/color-picker.component.js"; import SlPopup from "@shoelace-style/shoelace/dist/components/popup/popup.component.js"; import SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.component.js"; import IconTrash from "bootstrap-icons/icons/trash.svg"; import IconPlusCircle from "bootstrap-icons/icons/plus-circle.svg"; import IconCaretdown from "bootstrap-icons/icons/caret-down.svg"; import "../FileUploadButton"; import { SelectDataset } from "./SelectDataset"; import { updateColorForFilter } from "../../helper-components/Charts/Scatterplot/drawScatterplot"; import StateComponent from "../../stateComponent"; @customElement("wwci-scatterdataset-input") export class ScatterDatasetInput extends StateComponent { // Define properties with appropriate types @property({ type: Boolean, attribute: true, reflect: true }) accessor addDatasetPopupOpen = false; @property({ type: Number, attribute: true, reflect: true }) accessor addColumnPopupOpenIndex = -1; @property({ type: Number, attribute: true, reflect: true }) accessor openColumnTypePopupIndexInSet = -1; @property({ type: Number, attribute: true, reflect: true }) accessor openColumnTypePopupIndexInLabels = -1; addNewDataset() { const newDataset = { color: randomColor(), name: randomString(), ids: [], labels: [], typeOfEachData: [], dimensional_data: [], filter: { label: "", type: "none", index_in_labels: -1 }, }; this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: [...this.sharedState.scatterDatasets.sets, newDataset], }, }; this.dispatchStateChange(); } addNumericColumn(index_inset: number) { const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, labels: [...dataset.labels, randomString()], typeOfEachData: [...dataset.typeOfEachData, "number"], dimensional_data: dataset.dimensional_data.map((data) => ({ ...data, data: [...data.data, randomNumber()], })), }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.dispatchStateChange(); } addStringColumn(index_inset: number) { const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, labels: [...dataset.labels, randomString()], typeOfEachData: [...dataset.typeOfEachData, "string"], dimensional_data: dataset.dimensional_data.map((data) => ({ ...data, data: [...data.data, randomString()], })), }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.dispatchStateChange(); } addRow(index_inset: number) { const newId = Math.max(0, ...this.sharedState.scatterDatasets.sets[index_inset].ids) + 1; const newRow = { data: this.sharedState.scatterDatasets.sets[ index_inset ].typeOfEachData.map((d) => d === "number" ? randomNumber() : randomString() ), selected: false, }; const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, ids: [...dataset.ids, newId], dimensional_data: [...dataset.dimensional_data, newRow], }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.updateFilter(index_inset); this.dispatchStateChange(); } updateFilter(index_inset: number) { if ( this.sharedState.scatterDatasets.sets[index_inset].filter && this.sharedState.scatterDatasets.sets[index_inset].filter?.label && this.sharedState.scatterDatasets.sets[index_inset].filter ?.index_in_labels !== -1 && this.sharedState.scatterDatasets.sets[index_inset].filter?.type !== "none" ) { this.sharedState.scatterDatasets.sets[index_inset].filter.type === "number" ? updateColorForFilter( this.sharedState.scatterDatasets.sets[index_inset].filter .index_in_labels, "number", this.sharedState.scatterDatasets.sets[index_inset] ) : updateColorForFilter( this.sharedState.scatterDatasets.sets[index_inset].filter .index_in_labels, "string", this.sharedState.scatterDatasets.sets[index_inset] ); } } deleteRow(index_inset: number, index_indata_scatterplot: number) { const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, ids: dataset.ids.filter( (_, idx) => idx !== index_indata_scatterplot ), dimensional_data: dataset.dimensional_data.filter( (_, idx) => idx !== index_indata_scatterplot ), }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.updateFilter(index_inset); this.dispatchStateChange(); } deleteTableDataset(index_inset: number) { const updatedSets = this.sharedState.scatterDatasets.sets.filter( (_, i) => i !== index_inset ); let updatedDatasetIndexes = this.sharedState.scatterDatasets.selected.dataset_indexes.filter( (index) => index !== index_inset ); // make index > index_inset -1 updatedDatasetIndexes = updatedDatasetIndexes.map((index) => index > index_inset ? index - 1 : index ); // if there is only dataset, remove line datasets and selected axis to None if ( index_inset === this.sharedState.scatterDatasets.selected.dataset_indexes[0] && this.sharedState.scatterDatasets.selected.dataset_indexes.length === 1 ) { updatedDatasetIndexes = []; this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, selected: { ...this.sharedState.scatterDatasets.selected, axis: { x: "None", y: "None", }, }, }, }; // when only one dataset => remove line datasets this.sharedState.lineDatasets.sets = []; } this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, selected: { ...this.sharedState.scatterDatasets.selected, dataset_indexes: updatedDatasetIndexes, }, }, }; this.dispatchStateChange(); } recoverColorForFilter(index_inset: number) { const selectedDataset = this.sharedState.scatterDatasets.sets[index_inset]; const updatedFilter = selectedDataset.dimensional_data.map((d) => ({ ...d, color: selectedDataset.color, })); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: this.sharedState.scatterDatasets.sets.map((dataset, i) => { if (i === index_inset) { return { ...dataset, dimensional_data: updatedFilter, }; } return dataset; }), }, }; } deleteColumn(index_inset: number, index_inlabels: number) { // recover color for filter if the column was selected as filter if ( this.sharedState.scatterDatasets.sets[index_inset].filter ?.index_in_labels === index_inlabels ) { this.recoverColorForFilter(index_inset); } const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, labels: dataset.labels.filter((_, idx) => idx !== index_inlabels), typeOfEachData: dataset.typeOfEachData.filter( (_, idx) => idx !== index_inlabels ), dimensional_data: dataset.dimensional_data.map((data) => ({ ...data, data: data.data.filter((_, idx) => idx !== index_inlabels), })), filter: dataset.filter.index_in_labels === index_inlabels ? { label: "", type: "none", index_in_labels: -1, } : dataset.filter, }; } return dataset; } ); const updatedSelectedAxis = { ...this.sharedState.scatterDatasets.selected.axis, }; // In case the deleted column was selected as x or y axis if ( this.sharedState.scatterDatasets.selected.dataset_indexes.includes( index_inset ) ) { const deletedLabel = this.sharedState.scatterDatasets.sets[index_inset].labels[ index_inlabels ]; if ( this.sharedState.scatterDatasets.selected.axis.x === deletedLabel || this.sharedState.scatterDatasets.selected.axis.y === deletedLabel ) { // if there are multiple datasets, deselect current dataset if ( this.sharedState.scatterDatasets.selected.dataset_indexes.length > 1 ) { const updatedDatasetIndexes = this.sharedState.scatterDatasets.selected.dataset_indexes.filter( (index) => index !== index_inset ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, selected: { ...this.sharedState.scatterDatasets.selected, dataset_indexes: updatedDatasetIndexes, }, }, }; // if there is only one dataset, deselect the axis } else { if ( this.sharedState.scatterDatasets.selected.axis.x === deletedLabel ) { updatedSelectedAxis.x = "None"; } else { updatedSelectedAxis.y = "None"; } } } } this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, selected: { ...this.sharedState.scatterDatasets.selected, axis: updatedSelectedAxis, }, }, }; this.dispatchStateChange(); } changeColor(color: string, index_inset: number) { const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === index_inset) { return { ...dataset, color: color }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; // ensure the color updated, when no filter is selected if ( !this.sharedState.scatterDatasets.sets[index_inset].filter || this.sharedState.scatterDatasets.sets[index_inset].filter.type === "none" || this.sharedState.scatterDatasets.sets[index_inset].filter .index_in_labels === -1 ) { this.recoverColorForFilter(index_inset); } this.dispatchStateChange(); } // !important index_inset is the index of the dataset and index_inset = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse render() { return html`${this.sharedState.teacherOptions.addRemoveData ? html`<div class="btn-wrapper"> <sl-button slot="anchor" label="Add ${this.sharedState.scatterDatasets.sets.length === 0 ? "first" : "new"} Dataset" @click=${() => { this.addNewDataset(); }} >Add ${this.sharedState.scatterDatasets.sets.length === 0 ? "First" : "New"} Dataset</sl-button > <wwci-select-dataset .sharedState=${this.sharedState} ></wwci-select-dataset> </div>` : html``} ${this.sharedState.scatterDatasets.sets .slice() .reverse() .map( (each_dataset, index_reverse) => html` <div class="table-wrapper"> <table> <thead> <tr> <th class="table-cell-name"> <div style="display: flex; flex-direction: row; align-items: center" > <sl-color-picker size="small" ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.changeData )} value="${ this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ].color }" @sl-input=${(e: any) => this.changeColor( e.target.value, this.sharedState.scatterDatasets.sets.length - 1 - index_reverse )} ></sl-color-picker> <sl-icon-button ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.addRemoveData )} src=${IconTrash} label="Settings" @click=${(e) => { e.preventDefault(); // Prevent default selection behavior e.stopPropagation(); // Stop propagation to avoid other side effects this.deleteTableDataset( this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ); }} ></sl-icon-button> <div class="input-wrapper"> <sl-input ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.changeData )} type="text" class="input" value="${ this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets .length - 1 - index_reverse ].name }" @sl-input=${(e: any) => { const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if ( i === this.sharedState.scatterDatasets.sets .length - 1 - index_reverse ) { return { ...dataset, name: e.target.value, }; } return dataset; } ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.dispatchStateChange(); }} ></sl-input> </div> </div> </th> ${this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ].labels.map( (label, index_inlabels) => html`<th class="table-cell-name"> <div style="display: flex; flex-direction: row; align-items: center" > <div class="input-wrapper"> <sl-input ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.changeData )} type="text" value="${label}" @sl-input=${(e: any) => { let newValue = String(e.target.value); const targetIndex = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; const targetDataset = this.sharedState.scatterDatasets.sets[ targetIndex ]; // Check if we need to update the filter and color let updatedFilter = targetDataset.filter; if ( targetDataset.filter?.index_in_labels === index_inlabels ) { updatedFilter = { ...targetDataset.filter, label: newValue, }; } const updatedIndexes = [ ...this.sharedState.scatterDatasets.selected .dataset_indexes, ]; // check for the selected axis if ( this.sharedState.scatterDatasets.selected .dataset_indexes.length === 1 && this.sharedState.scatterDatasets.selected .dataset_indexes[0] === targetIndex ) { if ( this.sharedState.scatterDatasets.selected.axis .x === label ) { this.sharedState.scatterDatasets.selected.axis.x = newValue; } else if ( this.sharedState.scatterDatasets.selected.axis .y === label ) { this.sharedState.scatterDatasets.selected.axis.y = newValue; } if ( this.sharedState.scatterDatasets.selected.axis .x === this.sharedState.scatterDatasets.selected.axis .y ) { this.sharedState.scatterDatasets.selected.axis.y = "None"; this.sharedState.scatterDatasets.selected.axis.x = "None"; } } else if ( this.sharedState.scatterDatasets.selected .dataset_indexes.length > 1 && this.sharedState.scatterDatasets.selected.dataset_indexes.includes( targetIndex ) ) { if ( this.sharedState.scatterDatasets.selected.axis .x === label || this.sharedState.scatterDatasets.selected.axis .y === label ) { const index = updatedIndexes.indexOf(targetIndex); updatedIndexes.splice(index, 1); } } const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => { if (i === targetIndex) { const updatedLabels = dataset.labels.map( (label, labelIndex) => labelIndex === index_inlabels ? newValue : label ); return { ...dataset, labels: updatedLabels, // Update labels immutably filter: updatedFilter, }; } return dataset; } ); const updatedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, selected: { ...this.sharedState.scatterDatasets .selected, dataset_indexes: updatedIndexes, }, }, }; this.sharedState = updatedState; this.dispatchStateChange(); }} @sl-blur=${() => { const targetIndex = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; const dataset = this.sharedState.scatterDatasets.sets[ targetIndex ]; const labels = dataset.labels; const label = labels[index_inlabels]; // if the label is "" or " ", then set it to "Column" if (label === "" || label === " ") { const updatedLabels = labels.map((l, i) => i === index_inlabels ? "newAttribute" : l ); const updatedSets = this.sharedState.scatterDatasets.sets.map( (set, i) => i === targetIndex ? { ...set, labels: updatedLabels } : set ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; // if one of the axis is "" or " ", then set it to "newAttribute" if ( this.sharedState.scatterDatasets.selected.axis .x === label ) { this.sharedState.scatterDatasets.selected.axis.x = "newAttribute"; } else if ( this.sharedState.scatterDatasets.selected.axis .y === label ) { this.sharedState.scatterDatasets.selected.axis.y = "newAttribute"; } this.dispatchStateChange(); } if ( labels.some( (l, i) => l === label && i !== index_inlabels ) ) { const updatedLabels = labels.map((l, i) => i === index_inlabels ? `${label}(1)` : l ); const updatedSets = this.sharedState.scatterDatasets.sets.map( (set, i) => i === targetIndex ? { ...set, labels: updatedLabels } : set ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.dispatchStateChange(); } }} ></sl-input> </div> <sl-popup placement="bottom" strategy="fixed" .active="${this.openColumnTypePopupIndexInLabels === index_inlabels && this.openColumnTypePopupIndexInSet === this.sharedState.scatterDatasets.sets.length - 1 - index_reverse}" > <sl-icon-button slot="anchor" src=${IconCaretdown} label="Toggle dropdown" @click=${(e) => { this.openColumnTypePopupIndexInLabels = index_inlabels; this.openColumnTypePopupIndexInSet = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; e.target.focus(); }} @blur="${() => (this.openColumnTypePopupIndexInSet = -1 && (this.openColumnTypePopupIndexInLabels = -1))}" ></sl-icon-button> <div class="dropdown-content"> <sl-menu-item @mousedown=${(e: MouseEvent) => { if (e.button !== 0) return; e.preventDefault(); e.stopPropagation(); this.deleteColumn( this.sharedState.scatterDatasets.sets.length - 1 - index_reverse, index_inlabels ); this.openColumnTypePopupIndexInLabels = -1; this.openColumnTypePopupIndexInSet = -1; }} ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.addRemoveData )} > <sl-icon-button slot="prefix" src=${IconTrash} label="Settings" ></sl-icon-button> Delete Column </sl-menu-item> <sl-menu-item type="checkbox" .checked=${this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ].typeOfEachData[index_inlabels] === "number" ? false : true} @mousedown=${(e: MouseEvent) => { if (e.button !== 0) return; e.preventDefault(); // Prevent default selection behavior e.stopPropagation(); // Stop propagation to avoid other side effects const targetIndex = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; const targetDataset = this.sharedState.scatterDatasets.sets[ targetIndex ]; // Update the typeOfEachData array immutably const updatedTypeOfEachData = targetDataset.typeOfEachData.map((type, i) => i === index_inlabels ? "string" : type ); // Update the dimensional_data immutably const updatedDimensionalData = targetDataset.dimensional_data.map( (data) => ({ ...data, data: data.data.map((d, i) => i === index_inlabels ? d.toString() : d ), }) ); let updatedFilter = targetDataset.filter; // check if we need to update the filter and color if ( targetDataset.filter?.label === label && targetDataset.filter?.index_in_labels === index_inlabels ) { updatedFilter = { ...targetDataset.filter, type: "string", }; } // Update the sets array immutably const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => i === targetIndex ? { ...dataset, typeOfEachData: updatedTypeOfEachData, dimensional_data: updatedDimensionalData, filter: updatedFilter, } : dataset ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.updateFilter(targetIndex); this.dispatchStateChange(); }} class="button-datatype-category" >Convert column values to categorical</sl-menu-item > <sl-menu-item type="checkbox" .checked=${this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ].typeOfEachData[index_inlabels] === "number" ? true : false} @mousedown=${(e: MouseEvent) => { if (e.button !== 0) return; const targetIndex = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; const targetDataset = this.sharedState.scatterDatasets.sets[ targetIndex ]; // Update the typeOfEachData array immutably const updatedTypeOfEachData = targetDataset.typeOfEachData.map((type, i) => i === index_inlabels ? "number" : type ); // Update the dimensional_data immutably const updatedDimensionalData = targetDataset.dimensional_data.map( (data) => ({ ...data, data: data.data.map((d, i) => i === index_inlabels ? Number(d) ? Number(d) : 0 : d ), }) ); // Check if we need to update the filter and color let updatedFilter = targetDataset.filter; if ( targetDataset.filter?.label === label && targetDataset.filter?.index_in_labels === index_inlabels ) { updatedFilter = { ...targetDataset.filter, type: "number", }; } // Update the sets array immutably const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => i === targetIndex ? { ...dataset, typeOfEachData: updatedTypeOfEachData, dimensional_data: updatedDimensionalData, filter: updatedFilter, } : dataset ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.updateFilter(targetIndex); this.dispatchStateChange(); }} class="button-datatype-number" >Convert column values to numeric</sl-menu-item > </div> </sl-popup> </div> </th>` )} </tr> </thead> <tbody> ${each_dataset.dimensional_data.map( (each_data_scatterplot, index_inscatter) => html` <tr> <td class="table-cell-id"> <div style="display: flex; flex-direction: row; align-items: center" > <sl-icon-button ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.addRemoveData )} src=${IconTrash} label="Settings" @click=${(e) => { e.preventDefault(); // Prevent default selection behavior e.stopPropagation(); // Stop propagation to avoid other side effects this.deleteRow( this.sharedState.scatterDatasets.sets.length - 1 - index_reverse, index_inscatter ); }} ></sl-icon-button> <div class="input-wrapper"> <sl-input ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.changeData )} id="input" type="text" class="input" value="${each_dataset.ids[index_inscatter]}" @sl-input=${(e: any) => { e.target.value = e.target.value.replaceAll( /[^\d.,\-]+/g, "" ); const newNumber = Number(e.target.value); if (isNaN(newNumber)) return; const targetIndex = this.sharedState.scatterDatasets.sets.length - 1 - index_reverse; const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => i === targetIndex ? { ...dataset, ids: dataset.ids.map((id, idx) => idx === index_inscatter ? newNumber : id ), } : dataset ); this.sharedState = { ...this.sharedState, scatterDatasets: { ...this.sharedState.scatterDatasets, sets: updatedSets, }, }; this.dispatchStateChange(); }} ></sl-input> </div> </div> </td> ${each_data_scatterplot.data.map( (data, index_indata) => html` <td class="${this.sharedState.scatterDatasets.sets[ this.sharedState.scatterDatasets.sets.length - 1 - index_reverse ].typeOfEachData[index_indata] === "number" ? "table-cell-number" : "table-cell-category"}" > <div style="display: flex; flex-direction: row; align-items: center" > <div class="input-wrapper"> <sl-input ?disabled=${!( this.sharedState.editable || this.sharedState.teacherOptions.changeData )} id="input-x-${this.sharedState.scatterDatasets .sets.length - 1 - index_reverse}-${index_inscatter}-${index_indata}" type="text" value="${data}" @sl-input=${(e: any) => { const targetIndex = this.sharedState.scatterDatasets.sets .length - 1 - index_reverse; const targetDataset = this.sharedState.scatterDatasets.sets[ targetIndex ]; const updatedDimensionalData = targetDataset.dimensional_data.map( (dimData, idxScatter) => { if (idxScatter !== index_inscatter) return dimData; const updatedData = dimData.data.map( (data, idx) => idx === index_indata ? each_dataset.typeOfEachData[ index_indata ] === "number" ? (() => { e.target.value = e.target.value.replaceAll( /[^\d.,\-]+/g, "" ); const number = Number( e.target.value ); if (isNaN(number)) { return data; } return number; })() : String(e.target.value) : data ); return { ...dimData, data: updatedData, }; } ); let updatedFilter = targetDataset.filter; // Update the filter and color if the column is selected as filter if ( targetDataset.filter?.label === each_dataset.labels[index_indata] && targetDataset.filter?.index_in_labels === index_indata ) { updatedFilter = { ...targetDataset.filter, type: each_dataset.typeOfEachData[ index_indata ] === "number" ? "number" : "string", }; } const updatedSets = this.sharedState.scatterDatasets.sets.map( (dataset, i) => i === targetIndex ? { ...dataset, dimensional_data: updatedD