Skip to content

Commit

Permalink
feat(treemap): make treemap compatible with flat data
Browse files Browse the repository at this point in the history
  • Loading branch information
AlasDiablo committed Jul 4, 2024
1 parent 11e005a commit 26ef594
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/app/custom/translations.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -1098,3 +1098,5 @@
"ejs_variable_list" "Data from a routine is displayed using an HTML template based on EJS syntax. You can use these variables to access data and utils:" "L’affichage des données en provenance d’une routine est réalisé à l’aide d’un template HTML utilisant la syntaxe EJS. Vous pouvez utiliser ces variables pour accéder aux données et aux utilitaires :"
"ejs_data" "Variable containing the routine data" "Variable contenant les données de la routine"
"ejs_lodash" "Variable containing the Lodash function" "Variable contenant les fonctions de Lodash"
"treemap_hierarchy_data" "Hierarchy data" "Donnée hiérarchique"
"treemap_flat_data_type" "Data struture" "Struture des données"
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,26 @@ const FormatFieldSetPreview = ({
}) => {
const ReactJson = require('react-json-view').default;

const [datasetName, setDatasetName] = useState(datasets[0].name);
const [datasetName, setDatasetName] = useState('');
const [dataset, setDataset] = useState({});

useEffect(() => {
if (datasets && datasets.length >= 1) {
setDatasetName(datasets[0].name);
}
}, [datasets]);

useEffect(() => {
const newSet = datasets.find((value) => value.name === datasetName);
if (!newSet) {
setDataset({});
return;
}
setDataset({
total: newSet.total,
values: newSet.values,
});
}, [datasetName]);
}, [datasets, datasetName]);

const handleDataSetChange = (event) => {
setDatasetName(event.target.value);
Expand Down
69 changes: 64 additions & 5 deletions src/app/js/formats/vega/component/tree-map/TreeMapAdmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
FormatDataParamsFieldSet,
} from '../../../utils/components/field-set/FormatFieldSets';
import {
Box,
FormControlLabel,
FormGroup,
MenuItem,
Expand All @@ -24,7 +23,11 @@ import {
import VegaAdvancedMode from '../../../utils/components/admin/VegaAdvancedMode';
import ColorPickerParamsAdmin from '../../../utils/components/admin/ColorPickerParamsAdmin';
import AspectRatioSelector from '../../../utils/components/admin/AspectRatioSelector';
import { TreeMapSourceTargetWeight } from '../../../utils/dataSet';
import {
StandardIdValue,
StandardSourceTargetWeight,
TreeMapSourceTargetWeight,
} from '../../../utils/dataSet';
import VegaFieldPreview from '../../../utils/components/field-set/FormatFieldSetPreview';
import VegaToolTips from '../../../utils/components/admin/VegaToolTips';
import { TreeMapAdminView } from './TreeMapView';
Expand All @@ -35,6 +38,8 @@ export const defaultArgs = {
maxSize: 5,
orderBy: 'value/asc',
},
hierarchy: true,
flatType: 'id/value',
advancedMode: false,
advancedModeSpec: null,
tooltip: false,
Expand All @@ -58,6 +63,8 @@ const TreeMapAdmin = (props) => {
} = props;

const {
hierarchy,
flatType,
advancedMode,
advancedModeSpec,
tooltip,
Expand All @@ -70,6 +77,16 @@ const TreeMapAdmin = (props) => {
aspectRatio,
} = args;

const dataset = useMemo(() => {
if (hierarchy) {
return TreeMapSourceTargetWeight;
}
if (flatType === 'id/value') {
return StandardIdValue;
}
return StandardSourceTargetWeight;
}, [hierarchy, flatType]);

const colors = useMemo(() => {
return args.colors || defaultArgs.colors;
}, [args.colors]);
Expand All @@ -85,8 +102,12 @@ const TreeMapAdmin = (props) => {

const specBuilder = new TreeMap();

specBuilder.setHierarchy(hierarchy);
specBuilder.setColors(colors.split(' '));
specBuilder.setTooltip(tooltip);
specBuilder.setThirdTooltip(
hierarchy || (!hierarchy && flatType !== 'id/value'),
);
specBuilder.setTooltipSource(tooltipSource);
specBuilder.setTooltipTarget(tooltipTarget);
specBuilder.setTooltipWeight(tooltipWeight);
Expand All @@ -111,7 +132,15 @@ const TreeMapAdmin = (props) => {
};

const toggleAdvancedMode = () => {
updateAdminArgs('advancedMode', !args.advancedMode, props);
updateAdminArgs('advancedMode', !advancedMode, props);
};

const toggleHierarchy = () => {
updateAdminArgs('hierarchy', !hierarchy, props);
};

const handleFlatType = (e) => {
updateAdminArgs('flatType', e.target.value, props);
};

const clearAdvancedModeSpec = () => {
Expand Down Expand Up @@ -183,6 +212,31 @@ const TreeMapAdmin = (props) => {
label={polyglot.t('advancedMode')}
/>
</FormGroup>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={hierarchy}
onChange={toggleHierarchy}
/>
}
label={polyglot.t('treemap_hierarchy_data')}
/>
</FormGroup>
{!hierarchy ? (
<TextField
fullWidth
select
label={polyglot.t('treemap_flat_data_type')}
onChange={handleFlatType}
value={flatType}
>
<MenuItem value="id/value">_id / value</MenuItem>
<MenuItem value="source/target/weight">
source / target / weight
</MenuItem>
</TextField>
) : null}
{advancedMode ? (
<VegaAdvancedMode
value={spec}
Expand All @@ -199,7 +253,10 @@ const TreeMapAdmin = (props) => {
onValueTitleChange={handleTooltipTarget}
valueTitle={tooltipTarget}
polyglot={polyglot}
thirdValue={true}
thirdValue={
hierarchy ||
(!hierarchy && flatType !== 'id/value')
}
onThirdValueChange={handleTooltipWeight}
thirdValueTitle={tooltipWeight}
/>
Expand Down Expand Up @@ -261,7 +318,7 @@ const TreeMapAdmin = (props) => {
<VegaFieldPreview
args={args}
PreviewComponent={TreeMapAdminView}
datasets={[TreeMapSourceTargetWeight]}
datasets={[dataset]}
showDatasetsSelector={false}
/>
</FormatGroupedFieldSet>
Expand All @@ -276,6 +333,8 @@ TreeMapAdmin.propTypes = {
minValue: PropTypes.number,
orderBy: PropTypes.string,
}),
hierarchy: PropTypes.bool,
flatType: PropTypes.oneOf(['id/value', 'source/target/weight']),
advancedMode: PropTypes.bool,
advancedModeSpec: PropTypes.string,
tooltip: PropTypes.bool,
Expand Down
77 changes: 75 additions & 2 deletions src/app/js/formats/vega/component/tree-map/TreeMapData.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,37 @@ export default class TreeMapData {
*/
filteredNodes;

constructor(data) {
/**
* Those variables are use when we have flat data
* @type {boolean}
*/
hierarchy;
/**
* @type {Map<string, string>}
*/
initialHierarchy;

/**
* @param {Array<{source: string, target: string, weight: string | number, hierarchy?: string}>} data
* @param hierarchy
*/
constructor(data, hierarchy = true) {
this.data = data;
this.idIncrement = 0;
this.ids = new Map();
this.rawNodesAndLeaves = new Map();
this.formattedNodesAndLeaves = new Map();
this.filteredNodes = new Set();
this.hierarchy = hierarchy;

if (!this.hierarchy) {
this.initialHierarchy = new Map();
data.forEach((datum) => {
if (datum.hierarchy) {
this.initialHierarchy.set(datum.target, datum.hierarchy);
}
});
}
}

/**
Expand Down Expand Up @@ -141,7 +165,11 @@ export default class TreeMapData {
continue;
}

datum.hierarchy = this.createHierarchy(datum.parent);
if (this.hierarchy) {
datum.hierarchy = this.createHierarchy(datum.parent);
} else {
datum.hierarchy = this.initialHierarchy.get(datum.name);
}

transformedAndCleanupData.push(datum);
}
Expand All @@ -159,3 +187,48 @@ export default class TreeMapData {
return this.buildReturnable();
}
}

/**
* @param {Array<{_id: string, value: string | number}>} values
* @return {Array<{source: string, target: string, weight: string | number}>}
*/
TreeMapData.transformIdValue = (values) => {
const finalValues = [];
values.forEach((datum) => {
const middleNode = `root_${datum._id}`;
finalValues.push({
source: middleNode,
target: datum._id,
weight: datum.value,
});
finalValues.push({
source: 'root',
target: middleNode,
weight: datum.value,
});
});
return finalValues;
};

/**
* @param {Array<{source: string, target: string, weight: string | number}>} values
* @return {Array<{source: string, target: string, weight: string | number, hierarchy: string}>}
*/
TreeMapData.transformSourceTargetWeight = (values) => {
const finalValues = [];
values.forEach((datum) => {
const leaves = `leaves_${datum.source}_${datum.target}`;
finalValues.push({
source: datum.source,
target: leaves,
weight: datum.weight,
hierarchy: { ...datum },
});
finalValues.push({
source: 'root',
target: datum.source,
weight: datum.weight,
});
});
return finalValues;
};
28 changes: 24 additions & 4 deletions src/app/js/formats/vega/component/tree-map/TreeMapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const TreeMapView = (props) => {
const {
data,
field,
hierarchy,
flatType,
advancedMode,
advancedModeSpec,
tooltip,
Expand All @@ -43,12 +45,22 @@ const TreeMapView = (props) => {
return data;
}

const treeMapDataBuilder = new TreeMapData(data.values);
let values = data.values;
if (!hierarchy) {
if (flatType === 'id/value') {
values = TreeMapData.transformIdValue(data.values);
}
if (flatType === 'source/target/weight') {
values = TreeMapData.transformSourceTargetWeight(data.values);
}
}

const treeMapDataBuilder = new TreeMapData(values, hierarchy);
return {
...data,
values: treeMapDataBuilder.build(),
};
}, [data]);
}, [data, hierarchy, flatType]);

const { ref, width } = useSizeObserver();
const [error, setError] = useState('');
Expand All @@ -69,8 +81,12 @@ const TreeMapView = (props) => {

const specBuilder = new TreeMap();

specBuilder.setHierarchy(hierarchy);
specBuilder.setColors(colors.split(' '));
specBuilder.setTooltip(tooltip);
specBuilder.setThirdTooltip(
hierarchy || (!hierarchy && flatType !== 'id/value'),
);
specBuilder.setTooltipSource(tooltipSource);
specBuilder.setTooltipTarget(tooltipTarget);
specBuilder.setTooltipWeight(tooltipWeight);
Expand All @@ -80,6 +96,8 @@ const TreeMapView = (props) => {
return specBuilder.buildSpec(width);
}, [
width,
hierarchy,
flatType,
advancedMode,
advancedModeSpec,
tooltip,
Expand Down Expand Up @@ -108,9 +126,11 @@ const TreeMapView = (props) => {
};

TreeMapView.propTypes = {
field: fieldPropTypes.isRequired,
resource: PropTypes.object.isRequired,
field: fieldPropTypes,
resource: PropTypes.object,
data: PropTypes.any,
hierarchy: PropTypes.bool,
flatType: PropTypes.oneOf(['id/value', 'source/target/weight']),
advancedMode: PropTypes.bool,
advancedModeSpec: PropTypes.string,
tooltip: PropTypes.bool,
Expand Down
Loading

0 comments on commit 26ef594

Please sign in to comment.