feat: add getPropertyHandler func to viz api (#1803)

* feat: add getPropertyHandler func to ext if missing

* chore: fix comments

* chore: convert hypercube handler inputs type to object

* chore: fix failed test cases

---------

Co-authored-by: Donya Mashaallahpoor <dmh@qlik.com>
This commit is contained in:
Tobias Åström
2025-09-24 19:15:35 +02:00
committed by GitHub
parent 13e8e1a552
commit 3fab46f787
5 changed files with 230 additions and 148 deletions

View File

@@ -120,6 +120,6 @@ const conversion = {
hypercube,
};
export { utils, arrayUtil };
export { utils, arrayUtil, helpers };
export default conversion;

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-underscore-dangle */
import EventEmitter from 'node-event-emitter';
import { convertTo as conversionConvertTo } from '@nebula.js/conversion';
import { convertTo as conversionConvertTo, helpers } from '@nebula.js/conversion';
import { HyperCubeHandler } from '@nebula.js/supernova';
import glueCell from './components/glue';
import getPatches from './utils/patcher';
import validatePlugins from './plugins/plugins';
@@ -112,6 +113,38 @@ export default function viz({
}
return false;
},
/**
* Gets the generic hypercube handlers
* @private
* @returns {Promise<object|undefined>} methods to handle hypercube dimensions and measures definitions and properties.
*/
async getHypercubePropertyHandler() {
await rendered;
const qae = cellRef.current.getQae();
if (!qae?.data?.targets?.[0]?.propertyPath) {
return undefined;
}
const path = qae.data.targets[0].propertyPath;
const dataDefinition = cellRef.current.getExtensionDefinition().data;
const properties = await model.getEffectiveProperties();
if (path && dataDefinition) {
const args = {
app: model.app,
dimensionDefinition: dataDefinition.dimensions,
measureDefinition: dataDefinition.measures,
dimensionProperties: properties.qHyperCubeDef?.qDimensions?.[0] || helpers.getDefaultDimension(),
measureProperties: properties.qHyperCubeDef?.qMeasures?.[0] || helpers.getDefaultMeasure(),
globalChangeListeners: undefined,
path,
};
return new HyperCubeHandler(args);
}
return undefined;
},
toggleFocus(focus) {
cellRef.current.toggleFocus(focus);
},

View File

@@ -1,5 +1,5 @@
import HyperCubeHandler from './handler/hypercube-handler';
import create from './creator';
import HyperCubeHandler from './handler/hypercube-handler';
// import translator from './translator';
import qae from './qae';
@@ -71,8 +71,7 @@ export default function generatorFn(UserSN, galaxy) {
* @param {ObjectSelections} p.selections
*/
create(params) {
const ss = create(generator, params, galaxy);
return ss;
return create(generator, params, galaxy);
},
definition: galaxy.flags.isEnabled('NEBULA_DATA_HANDLERS')
? {

View File

@@ -7,7 +7,17 @@ jest.mock('../utils/hypercube-helper/add-main-dimension', () => jest.fn());
describe('HyperCube Handlers', () => {
let handler;
let properties;
let index = 1;
let index;
const opts = {
app: {}, // mock app
dimensionDefinition: { max: 10 },
measureDefinition: { max: 10 },
dimensionProperties: {},
measureProperties: {},
globalChangeListeners: [],
path: '',
};
beforeEach(() => {
index = 1;
@@ -24,7 +34,8 @@ describe('HyperCube Handlers', () => {
},
},
};
handler = new HyperCubeHandler(properties);
handler = new HyperCubeHandler(opts);
});
afterEach(() => {
@@ -111,13 +122,13 @@ describe('HyperCube Handlers', () => {
});
describe('addDimension', () => {
let dimension;
let dimensionProp;
beforeEach(() => {
dimension = { qDef: { cId: 'dim3' }, qOtherTotalSpec: {} };
dimensionProp = { dimension: { qDef: { cId: 'dim3' }, qOtherTotalSpec: {} }, alternative: false, index };
jest.spyOn(hcUtils, 'isDimensionAlternative').mockReturnValue(false);
jest.spyOn(hcUtils, 'addAlternativeDimension').mockResolvedValue(dimension);
addMainDimension.mockResolvedValue(dimension);
jest.spyOn(hcUtils, 'addAlternativeDimension').mockResolvedValue(dimensionProp.dimension);
addMainDimension.mockResolvedValue(dimensionProp.dimension);
});
afterEach(() => {
@@ -127,20 +138,21 @@ describe('HyperCube Handlers', () => {
});
test('should add a main dimension when alternative is false', () => {
handler.addDimension(dimension, false, index).then((result) => {
handler.addDimension(dimensionProp).then((result) => {
expect(hcUtils.isDimensionAlternative).toHaveBeenCalledWith(handler, false);
expect(addMainDimension).toHaveBeenCalledWith(handler, dimension, index);
expect(result).toEqual(dimension);
expect(addMainDimension).toHaveBeenCalledWith(handler, dimensionProp.dimension, dimensionProp.index);
expect(result).toEqual(dimensionProp.dimension);
});
});
test('should add an alternative dimension when alternative is true', () => {
jest.spyOn(hcUtils, 'isDimensionAlternative').mockReturnValue(true);
const altDimensionProp = { dimension: dimensionProp.dimension, alternative: true, index };
handler.addDimension(dimension, true, index).then((result) => {
handler.addDimension(altDimensionProp).then((result) => {
expect(hcUtils.isDimensionAlternative).toHaveBeenCalledWith(handler, true);
expect(hcUtils.addAlternativeDimension).toHaveBeenCalledWith(handler, dimension, index);
expect(result).toEqual(dimension);
expect(hcUtils.addAlternativeDimension).toHaveBeenCalledWith(handler, dimensionProp.dimension, index);
expect(result).toEqual(dimensionProp.dimension);
});
});
});
@@ -158,13 +170,13 @@ describe('HyperCube Handlers', () => {
});
test('should return an empty array when newDimensions is empty', async () => {
const result = await handler.addDimensions([]);
const result = await handler.addDimensions({ dimensions: [] });
expect(result).toEqual([]);
});
test('should add dimensions to alternative dimensions when alternative is true', async () => {
const newDimensions = [{ qDef: { cId: 'altDim3' } }, { qDef: { cId: 'altDim4' } }];
const dimensions = await handler.addDimensions(newDimensions, true);
const dimensions = await handler.addDimensions({ dimensions: newDimensions, alternative: true });
expect(dimensions).toEqual([
{ qDef: { cId: 'altDim3' }, qOtherTotalSpec: {} },
@@ -183,7 +195,7 @@ describe('HyperCube Handlers', () => {
handler.maxDimensions = jest.fn().mockReturnValue(4);
handler.autoSortDimension = jest.fn();
const dimensions = await handler.addDimensions(newDimensions, false);
const dimensions = await handler.addDimensions({ dimensions: newDimensions, alternative: false });
expect(handler.autoSortDimension).toHaveBeenCalledTimes(2);
expect(handler.autoSortDimension).toHaveBeenCalledWith({ qDef: { cId: 'dim3' }, qOtherTotalSpec: {} });
expect(handler.autoSortDimension).toHaveBeenCalledWith({ qDef: { cId: 'dim4' }, qOtherTotalSpec: {} });
@@ -205,7 +217,7 @@ describe('HyperCube Handlers', () => {
jest.spyOn(hcUtils, 'isTotalDimensionsExceeded').mockReturnValue(true);
const newDimensions = [{ qDef: { cId: 'dim2' } }];
const result = await handler.addDimensions(newDimensions);
const result = await handler.addDimensions({ dimensions: newDimensions });
expect(result).toEqual([]);
});
@@ -214,7 +226,7 @@ describe('HyperCube Handlers', () => {
handler.maxDimensions = jest.fn().mockReturnValue(1);
const newDimensions = [{ qDef: { cId: 'dim3' } }];
const result = await handler.addDimensions(newDimensions, false);
const result = await handler.addDimensions({ dimensions: newDimensions, alternative: false });
expect(result).toEqual([{ qDef: { cId: 'dim3' }, qOtherTotalSpec: {} }]);
expect(handler.hcProperties.qDimensions).toEqual([{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim2' } }]);
@@ -275,13 +287,13 @@ describe('HyperCube Handlers', () => {
});
test('should return an empty array when new measure is empty', () => {
const result = handler.addMeasures([]);
const result = handler.addMeasures({ measures: [] });
expect(result).toEqual([]);
});
test('should add measures to alternative measures when alternative is true', () => {
const newMeasures = [{ qDef: { cId: 'altMeas3' } }, { qDef: { cId: 'altMeas4' } }];
const measures = handler.addMeasures(newMeasures, true);
const measures = handler.addMeasures({ measures: newMeasures, alternative: true });
expect(measures).toEqual([{ qDef: { cId: 'altMeas3' } }, { qDef: { cId: 'altMeas4' } }]);
expect(handler.hcProperties.qMeasures).toEqual([{ qDef: { cId: 'meas1' } }]);
@@ -299,7 +311,7 @@ describe('HyperCube Handlers', () => {
handler.autoSortDimension = jest.fn();
jest.spyOn(hcUtils, 'isMeasureAlternative').mockReturnValue(false);
const measures = handler.addMeasures(newMeasures, false);
const measures = handler.addMeasures({ measures: newMeasures, alternative: false });
expect(measures).toEqual([
{
qDef: { cId: 'meas2' },
@@ -325,7 +337,7 @@ describe('HyperCube Handlers', () => {
jest.spyOn(hcUtils, 'isTotalMeasureExceeded').mockReturnValue(true);
const newMeasure = [{ qDef: { cId: 'meas2' } }];
const measure = handler.addMeasures(newMeasure);
const measure = handler.addMeasures({ measures: newMeasure });
expect(measure).toEqual([]);
});
@@ -333,7 +345,7 @@ describe('HyperCube Handlers', () => {
handler.maxMeasures = jest.fn().mockReturnValue(1);
const newMeasure = [{ qDef: { cId: 'meas2' } }];
const measure = handler.addMeasures(newMeasure, false);
const measure = handler.addMeasures({ measures: newMeasure, alternative: false });
expect(measure).toEqual([{ qDef: { cId: 'meas2' } }]);
expect(handler.hcProperties.qMeasures).toEqual([{ qDef: { cId: 'meas1' } }]);
@@ -370,7 +382,7 @@ describe('HyperCube Handlers', () => {
jest.spyOn(hcUtils, 'removeMeasureFromColumnSortOrder');
jest.spyOn(hcUtils, 'removeMeasureFromColumnOrder');
const result = await handler.removeMeasures([], false);
const result = await handler.removeMeasures({ indexes: [], alternative: false });
expect(result).toEqual([]);
expect(hcUtils.removeMeasureFromColumnSortOrder).not.toHaveBeenCalled();
@@ -380,7 +392,7 @@ describe('HyperCube Handlers', () => {
test('should return deleted measures if alternative is true', async () => {
const indexes = [2, 0];
const result = await handler.removeMeasures(indexes, true);
const result = await handler.removeMeasures({ indexes, alternative: true });
expect(result).toEqual([{ qDef: { cId: 'altMeas1' } }, { qDef: { cId: 'altMeas3' } }]);
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures).toEqual([{ qDef: { cId: 'altMeas2' } }]);
@@ -396,7 +408,7 @@ describe('HyperCube Handlers', () => {
handler.setProperties(properties);
const indexes = [1, 2];
const result = await handler.removeMeasures(indexes, false);
const result = await handler.removeMeasures({ indexes, alternative: false });
expect(result).toEqual([{ qDef: { cId: 'meas2' } }, { qDef: { cId: 'meas3' } }]);
expect(handler.hcProperties.qMeasures).toEqual([{ qDef: { cId: 'meas1' } }]);
@@ -413,7 +425,7 @@ describe('HyperCube Handlers', () => {
handler.setProperties(properties);
const indexes = [1];
const result = await handler.removeMeasures(indexes, false);
const result = await handler.removeMeasures({ indexes, alternative: false });
expect(result).toEqual([]);
});
@@ -452,7 +464,7 @@ describe('HyperCube Handlers', () => {
const { removeMainDimension } = await import('../utils/hypercube-helper/remove-main-dimension');
const { removeAlternativeDimension } = await import('../utils/hypercube-helper/remove-alternative-dimension');
const result = await handler.removeDimensions([], false);
const result = await handler.removeDimensions({ indexes: [], alternative: false });
expect(result).toEqual([]);
expect(removeAlternativeDimension).not.toHaveBeenCalled();
@@ -462,7 +474,7 @@ describe('HyperCube Handlers', () => {
test('should remove alternative dimensions when alternative is true', async () => {
const indexes = [0, 1];
const result = await handler.removeDimensions(indexes, true);
const result = await handler.removeDimensions({ indexes, alternative: true });
expect(result).toEqual([{ qDef: { cId: 'altDim1' } }, { qDef: { cId: 'altDim2' } }]);
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions).toEqual([{ qDef: { cId: 'altDim3' } }]);
@@ -476,7 +488,7 @@ describe('HyperCube Handlers', () => {
test('should remove main dimensions when alternative is false', async () => {
const indexes = [2, 0];
const result = await handler.removeDimensions(indexes, false);
const result = await handler.removeDimensions({ indexes, alternative: false });
expect(result).toEqual([{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim3' } }]);
expect(handler.hcProperties.qDimensions).toEqual([{ qDef: { cId: 'dim2' } }]);
@@ -500,7 +512,7 @@ describe('HyperCube Handlers', () => {
handler.setProperties(properties);
const indexes = [0, 1];
const result = await handler.removeDimensions(indexes, true);
const result = await handler.removeDimensions({ indexes, alternative: true });
expect(result).toEqual([]);
expect(removeAlternativeDimension).not.toHaveBeenCalled();
@@ -517,7 +529,7 @@ describe('HyperCube Handlers', () => {
handler.setProperties(properties);
const indexes = [0, 1];
const result = await handler.removeDimensions(indexes, false);
const result = await handler.removeDimensions({ indexes, alternative: false });
expect(result).toEqual([]);
expect(removeMainDimension).not.toHaveBeenCalled();
@@ -544,7 +556,7 @@ describe('HyperCube Handlers', () => {
});
test('moves dimension within main', async () => {
await handler.moveDimension(0, 1);
await handler.moveDimension({ fromIndex: 0, toIndex: 1 });
expect(handler.hcProperties.qDimensions).toEqual([
{ qDef: { cId: 'dim2' } },
{ qDef: { cId: 'dim1' } },
@@ -558,7 +570,7 @@ describe('HyperCube Handlers', () => {
});
test('moves dimension from main to alternative', async () => {
await handler.moveDimension(0, 3);
await handler.moveDimension({ fromIndex: 0, toIndex: 3 });
expect(handler.hcProperties.qDimensions).toEqual([
{ qDef: { cId: 'dim2' } },
@@ -573,7 +585,7 @@ describe('HyperCube Handlers', () => {
});
test('moves dimension from alternative to main', async () => {
const result = await handler.moveDimension(3, 2);
const result = await handler.moveDimension({ fromIndex: 3, toIndex: 2 });
expect(handler.hcProperties.qDimensions).toEqual([
{ qDef: { cId: 'dim1' } },
@@ -589,7 +601,7 @@ describe('HyperCube Handlers', () => {
});
test('moves dimension within alternative', async () => {
await handler.moveDimension(4, 3);
await handler.moveDimension({ fromIndex: 4, toIndex: 3 });
expect(handler.hcProperties.qDimensions).toEqual([
{ qDef: { cId: 'dim1' } },
@@ -607,7 +619,7 @@ describe('HyperCube Handlers', () => {
properties.qHyperCubeDef.qDimensions = [];
handler.setProperties(properties);
const result = await handler.moveDimension(0, 1);
const result = await handler.moveDimension({ fromIndex: 0, toIndex: 1 });
expect(handler.hcProperties.qDimensions).toEqual([]);
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions).toEqual([
{ qDef: { cId: 'altDim2' } },
@@ -618,7 +630,7 @@ describe('HyperCube Handlers', () => {
});
test('handles out-of-bounds indices', async () => {
await handler.moveDimension(10, 20);
await handler.moveDimension({ fromIndex: 10, toIndex: 20 });
expect(handler.hcProperties.qDimensions).toEqual([
{ qDef: { cId: 'dim1' } },
{ qDef: { cId: 'dim2' } },
@@ -652,7 +664,7 @@ describe('HyperCube Handlers', () => {
});
test('moves measure within main', async () => {
await handler.moveMeasure(0, 1);
await handler.moveMeasure({ fromIndex: 0, toIndex: 1 });
expect(handler.hcProperties.qMeasures).toEqual([
{ qDef: { cId: 'meas2' } },
{ qDef: { cId: 'meas1' } },
@@ -666,7 +678,7 @@ describe('HyperCube Handlers', () => {
});
test('moves measure from main to alternative', async () => {
await handler.moveMeasure(0, 3);
await handler.moveMeasure({ fromIndex: 0, toIndex: 3 });
expect(handler.hcProperties.qMeasures).toEqual([
{ qDef: { cId: 'meas2' } },
@@ -681,7 +693,7 @@ describe('HyperCube Handlers', () => {
});
test('moves measure from alternative to main', async () => {
const result = await handler.moveMeasure(3, 2);
const result = await handler.moveMeasure({ fromIndex: 3, toIndex: 2 });
expect(handler.hcProperties.qMeasures).toEqual([
{ qDef: { cId: 'meas1' } },
@@ -697,7 +709,7 @@ describe('HyperCube Handlers', () => {
});
test('moves measure within alternative', async () => {
await handler.moveMeasure(4, 3);
await handler.moveMeasure({ fromIndex: 4, toIndex: 3 });
expect(handler.hcProperties.qMeasures).toEqual([
{ qDef: { cId: 'meas1' } },
@@ -715,7 +727,7 @@ describe('HyperCube Handlers', () => {
properties.qHyperCubeDef.qMeasures = [];
handler.setProperties(properties);
const result = await handler.moveMeasure(0, 1);
const result = await handler.moveMeasure({ fromIndex: 0, toIndex: 1 });
expect(handler.hcProperties.qMeasures).toEqual([]);
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures).toEqual([
{ qDef: { cId: 'altMeas2' } },
@@ -726,7 +738,7 @@ describe('HyperCube Handlers', () => {
});
test('handles out-of-bounds indices', async () => {
await handler.moveMeasure(10, 20);
await handler.moveMeasure({ fromIndex: 10, toIndex: 20 });
expect(handler.hcProperties.qMeasures).toEqual([
{ qDef: { cId: 'meas1' } },
{ qDef: { cId: 'meas2' } },

View File

@@ -53,6 +53,50 @@ class HyperCubeHandler extends DataPropertyHandler {
this.path = opts.path;
}
/**
* @private
* @typeof {object} DimensionProp
* @property {qix.NxDimension} dimension
* @property {boolean=} alternative - Whether the dimension is an alternative
* @property {number=} index - Index of the dimension.
*/
/**
* @private
* @typeof {object} MultiDimensionProps
* @property {qix.NxDimension[]} dimensions
* @property {boolean=} alternative - Whether the dimension is an alternative
*/
/**
* @private
* @typeof {object} MeasureProps
* @property {qix.NxMeasure} measure
* @property {boolean=} alternative - Whether the measure is an alternative
* @property {number=} index - Index of the measure.
*/
/**
* @private
* @typeof {object} MultiMeasureProps
* @property {qix.NxMeasure[]} measures
* @property {boolean=} alternative - Whether the measure is an alternative
*/
/**
* @private
* @typeof {object} Indexes
* @property {number} fromIndex - The current index of the field.
* @property {number} toIndex - New index of the field.
*/
/**
* @private
* @typeof {object} MultiFieldsIndexes
* @property {number[]} indexes - Multiple field indexes.
* @property {boolean} alternative - Whether the field is an alternative.
*/
/**
* @private
* @param {object=} properties
@@ -135,9 +179,7 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {qix.NxDimension} dimension
* @param {boolean} alternative
* @param {number=} idx
* @param {DimensionProps} dimensionProps
* @returns {qix.NxDimension} dimension
* @description Adds a dimension to the hypercube and updates the orders of the dimensions.
* If the dimension is an alternative, it will be added to the alternative dimensions.
@@ -145,20 +187,19 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const dimension = hyperCubeHandler.addDimension(dimension, alternative, idx);
*/
addDimension(dimension, alternative, idx) {
const dim = initializeDim(dimension);
addDimension(dimensionProps) {
const dim = initializeDim(dimensionProps.dimension);
if (hcUtils.isDimensionAlternative(this, alternative)) {
return hcUtils.addAlternativeDimension(this, dim, idx);
if (hcUtils.isDimensionAlternative(this, dimensionProps.alternative)) {
return hcUtils.addAlternativeDimension(this, dim, dimensionProps.index);
}
return addMainDimension(this, dim, idx);
return addMainDimension(this, dim, dimensionProps.index);
}
/**
* @private
* @param {qix.NxDimension[]} dimensions
* @param {boolean} alternative
* @param {MultiDimensionProps} multiDimensionProps
* @returns {qix.NxDimension[]} added dimensions
* @description Adds multiple dimensions to the hypercube.
* If the dimensions are alternatives, they will be added to the alternative dimensions.
@@ -167,12 +208,13 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const addedDimensions = await hyperCubeHandler.addDimensions(dimensions, alternative);
*/
async addDimensions(dimensions, alternative = false) {
async addDimensions(multiDimensionProps) {
const existingDimensions = this.getDimensions();
const initialLength = existingDimensions.length;
const addedDimensions = [];
let addedActive = 0;
const { dimensions } = multiDimensionProps;
// eslint-disable-next-line no-restricted-syntax
for await (const dimension of dimensions) {
if (hcUtils.isTotalDimensionsExceeded(this, existingDimensions)) {
@@ -181,7 +223,7 @@ class HyperCubeHandler extends DataPropertyHandler {
const dim = initializeDim(dimension);
if (hcUtils.isDimensionAlternative(this, alternative)) {
if (hcUtils.isDimensionAlternative(this, multiDimensionProps.alternative || false)) {
const altDim = await hcUtils.addAlternativeDimension(this, dim);
addedDimensions.push(altDim);
} else if (existingDimensions.length < this.maxDimensions()) {
@@ -195,26 +237,26 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {number} idx
* @param {boolean} alternative
* @param {DimensionProps} dimensionProps
* @description Removes a dimension from the hypercube by index.
* If the dimension is an alternative, it will be removed from the alternative dimensions.
* @memberof HyperCubeHandler
* @example
* hyperCubeHandler.removeDimension(idx, alternative);
*/
removeDimension(idx, alternative) {
removeDimension(dimensionProps) {
const { alternative, index } = dimensionProps;
if (alternative) {
removeAlternativeDimension(this, idx);
removeAlternativeDimension(this, index);
}
removeMainDimension(this, idx);
removeMainDimension(this, index);
}
/**
* @private
* @param {number[]} indexes
* @param {boolean} alternative
* @param {MultiDimensionProps} multiDimensionProps
* @returns {qix.NxDimension[]} deleted dimensions
* @description Removes multiple dimensions from the hypercube by indexes.
* If the dimensions are alternatives, they will be removed from the alternative dimensions.
@@ -223,25 +265,25 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const deletedDimensions = await hyperCubeHandler.removeDimensions(indexes, alternative);
*/
async removeDimensions(indexes, alternative) {
async removeDimensions(multiDimensionProps) {
const altDimensions = this.getAlternativeDimensions();
const dimensions = this.getDimensions();
if (indexes.length === 0) return [];
if (multiDimensionProps.indexes.length === 0) return [];
let deletedDimensions = [];
// Start deleting from the end of the list first otherwise the idx is messed up
const sortedIndexes = [...indexes].sort((a, b) => b - a);
const sortedIndexes = [...multiDimensionProps.indexes].sort((a, b) => b - a);
if (alternative && altDimensions.length > 0) {
if (multiDimensionProps.alternative && altDimensions.length > 0) {
// Keep the original deleted order
deletedDimensions = hcUtils.getDeletedFields(altDimensions, indexes);
deletedDimensions = hcUtils.getDeletedFields(altDimensions, multiDimensionProps.indexes);
// eslint-disable-next-line no-restricted-syntax
for await (const index of sortedIndexes) {
await removeAlternativeDimension(this, index);
}
} else if (dimensions.length > 0) {
// Keep the original deleted order
deletedDimensions = hcUtils.getDeletedFields(dimensions, indexes);
deletedDimensions = hcUtils.getDeletedFields(dimensions, multiDimensionProps.indexes);
// eslint-disable-next-line no-restricted-syntax
for await (const index of sortedIndexes) {
await removeMainDimension(this, index);
@@ -254,65 +296,66 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* Replaces a dimension in the hypercube.
* @private
* @param {number} index - The index of the dimension to replace.
* @param {qix.NxDimension} dimension - The new dimension to replace the old one.
* @param {DimensionProps} dimensionProps
* @returns {Promise<qix.NxDimension>} replaced dimension.
* @memberof HyperCubeHandler
* @example
* const replacedDimension = await hyperCubeHandler.replaceDimension(index, newDimension);
*/
replaceDimension(index, dimension) {
replaceDimension(dimensionProps) {
const { index, dimension } = dimensionProps;
return this.autoSortDimension(dimension).then(() => hcUtils.replaceDimensionOrder(this, index, dimension));
}
/**
* Reinserts a dimension into the hypercube.
* @private
* @param {qix.NxDimension} dimension - The dimension to reinsert.
* @param {boolean} alternative - Whether the dimension is an alternative.
* @param {number} idx - The index to insert the dimension at.
* @private.
* @param {DimensionProps} dimensionProps
* @returns {Promise<qix.NxDimension>} The reinserted dimension.
* @memberof HyperCubeHandler
* @example
* await hyperCubeHandler.reinsertDimension(dimension, alternative, idx);
*/
reinsertDimension(dimension, alternative, idx) {
const dim = initializeId(dimension);
reinsertDimension(dimensionProps) {
const dim = initializeId(dimensionProps.dimension);
if (hcUtils.isDimensionAlternative(this, alternative)) {
return hcUtils.addAlternativeDimension(this, dim, idx).then(() => {
if (hcUtils.isDimensionAlternative(this, dimensionProps.alternative)) {
return hcUtils.addAlternativeDimension(this, dim, dimensionProps.index).then(() => {
hcUtils.moveDimensionToColumnOrder(this, dim);
});
}
return reinsertMainDimension(this, dim, idx);
return reinsertMainDimension(this, dim, dimensionProps.index);
}
/**
* Moves a dimension within the hypercube.
* @private
* @param {number} fromIndex - The current index of the dimension.
* @param {number} toIndex - The new index of the dimension.
* @param {Indexes} indexes
* @returns {Promise<qix.NxDimension[]>} updated dimensions.
* @memberof HyperCubeHandler
* @example
* await hyperCubeHandler.moveDimension(fromIndex, toIndex);
*/
moveDimension(fromIndex, toIndex) {
moveDimension(indexes) {
const dimensions = this.getDimensions();
const altDimensions = this.getAlternativeDimensions();
if (fromIndex < dimensions.length && toIndex < dimensions.length) {
return Promise.resolve(arrayUtil.move(dimensions, fromIndex, toIndex));
if (indexes.fromIndex < dimensions.length && indexes.toIndex < dimensions.length) {
return Promise.resolve(arrayUtil.move(dimensions, indexes.fromIndex, indexes.toIndex));
}
if (fromIndex < dimensions.length && toIndex >= dimensions.length) {
return hcUtils.moveDimensionFromMainToAlternative(fromIndex, toIndex, dimensions, altDimensions);
if (indexes.fromIndex < dimensions.length && indexes.toIndex >= dimensions.length) {
return hcUtils.moveDimensionFromMainToAlternative(indexes.fromIndex, indexes.toIndex, dimensions, altDimensions);
}
if (fromIndex >= dimensions.length && toIndex < dimensions.length) {
return Promise.resolve(hcUtils.moveMeasureFromAlternativeToMain(fromIndex, toIndex, dimensions, altDimensions));
if (indexes.fromIndex >= dimensions.length && indexes.toIndex < dimensions.length) {
return Promise.resolve(
hcUtils.moveMeasureFromAlternativeToMain(indexes.fromIndex, indexes.toIndex, dimensions, altDimensions)
);
}
return Promise.resolve(hcUtils.moveDimensionWithinAlternative(fromIndex, toIndex, dimensions, altDimensions));
return Promise.resolve(
hcUtils.moveDimensionWithinAlternative(indexes.fromIndex, indexes.toIndex, dimensions, altDimensions)
);
}
/**
@@ -389,10 +432,8 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {qix.NxMeasure} measure
* @param {boolean} alternative
* @param {number=} idx
* @returns {Promise<qix.NxMeasure>} measure
* @param {MeasureProps} measureProps
* @returns {Promise<qix.NxMeasure>} added measure
* @description Adds a measure to the hypercube.
* If the measure is an alternative, it will be added to the alternative measures.
* If the total number of measures exceeds the limit, it will stop adding measures.
@@ -400,15 +441,15 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const measure = hyperCubeHandler.addMeasure(measure, alternative, idx);
*/
addMeasure(measure, alternative, idx) {
const meas = initializeId(measure);
addMeasure(measureProps) {
const meas = initializeId(measureProps.measure);
if (hcUtils.isMeasureAlternative(this, alternative)) {
if (hcUtils.isMeasureAlternative(this, measureProps.alternative)) {
const hcMeasures = this.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures;
return hcUtils.addAlternativeMeasure(meas, hcMeasures, idx);
return hcUtils.addAlternativeMeasure(meas, hcMeasures, measureProps.index);
}
return addMainMeasure(this, meas, idx);
return addMainMeasure(this, meas, measureProps.index);
}
/**
@@ -433,8 +474,7 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {qix.NxMeasure[]} measures
* @param {boolean} alternative
* @param {MultiMeasureProps} multiMeasureProps
* @returns {qix.NxMeasure[]} added measures
* @description Adds multiple measures to the hypercube.
* If the measures are alternatives, they will be added to the alternative measures.
@@ -443,19 +483,19 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const addedMeasures = await hyperCubeHandler.addMeasures(measures, alternative);
*/
addMeasures(measures, alternative = false) {
addMeasures(multiMeasureProps) {
const existingMeasures = this.getMeasures();
const addedMeasures = [];
let addedActive = 0;
measures.forEach(async (measure) => {
multiMeasureProps.measures.forEach(async (measure) => {
if (hcUtils.isTotalMeasureExceeded(this, existingMeasures)) {
return false;
}
const meas = initializeId(measure);
if (hcUtils.isMeasureAlternative(this, alternative)) {
if (hcUtils.isMeasureAlternative(this, multiMeasureProps.alternative || false)) {
hcUtils.addAlternativeMeasure(this, meas);
addedMeasures.push(meas);
} else if (existingMeasures.length < this.maxMeasures()) {
@@ -469,25 +509,25 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {number} idx
* @param {boolean} alternative
* @param {MeasureProps} measureProps
* @description Removes a measure from the hypercube by index.
* If the measure is an alternative, it will be removed from the alternative measures.
* @memberof HyperCubeHandler
* @example
* hyperCubeHandler.removeMeasure(idx, alternative);
*/
removeMeasure(idx, alternative) {
removeMeasure(measureProps) {
const { index, alternative } = measureProps;
if (alternative) {
hcUtils.removeAltMeasureByIndex(this, idx);
hcUtils.removeAltMeasureByIndex(this, index);
}
removeMainMeasure(this, idx);
removeMainMeasure(this, index);
}
/**
* @private
* @param {number[]} indexes
* @param {boolean} alternative
* @param {MultiFieldsIndexes} multiFieldsIndexes
* @returns {Promise<number[]>} deleted measures
* @description Removes multiple measures from the hypercube by indexes.
* If the measures are alternatives, they will be removed from the alternative measures.
@@ -496,21 +536,21 @@ class HyperCubeHandler extends DataPropertyHandler {
* @example
* const deletedMeasures = await hyperCubeHandler.removeMeasures(indexes, alternative);
*/
async removeMeasures(indexes, alternative) {
async removeMeasures(multiFieldsIndexes) {
const measures = this.getMeasures();
const altMeasures = this.getAlternativeMeasures();
let deletedMeasures = [];
if (indexes.length === 0) return deletedMeasures;
if (multiFieldsIndexes.length === 0) return deletedMeasures;
if (alternative && altMeasures.length > 0) {
if (multiFieldsIndexes.alternative && altMeasures.length > 0) {
// Keep the original deleted order
deletedMeasures = hcUtils.getDeletedFields(altMeasures, indexes);
removeAlternativeMeasure(this, indexes);
deletedMeasures = hcUtils.getDeletedFields(altMeasures, multiFieldsIndexes.indexes);
removeAlternativeMeasure(this, multiFieldsIndexes.indexes);
} else if (measures.length > 0) {
// Keep the original deleted order
deletedMeasures = hcUtils.getDeletedFields(measures, indexes);
const sortedIndexes = [...indexes].sort((a, b) => b - a);
deletedMeasures = hcUtils.getDeletedFields(measures, multiFieldsIndexes.indexes);
const sortedIndexes = [...multiFieldsIndexes.indexes].sort((a, b) => b - a);
// eslint-disable-next-line no-restricted-syntax
for await (const index of sortedIndexes) {
@@ -523,68 +563,67 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* @private
* @param {number} index
* @param {qix.NxMeasure} measure
* @param {MeasureProps} measureProps
* @returns {Promise<qix.NxMeasure>} replaced measure
* @description Replaces a measure in the hypercube.
* @memberof HyperCubeHandler
* @example
* const updatedMeasure = await hyperCubeHandler.replaceMeasure(index, measure);
*/
replaceMeasure(index, measure) {
replaceMeasure(measureProps) {
const { index, measure } = measureProps;
return this.autoSortMeasure(measure).then(() => hcUtils.replaceMeasureToColumnOrder(this, index, measure));
}
/**
* @private
* @param {qix.NxMeasure} measure
* @param {boolean} alternative
* @param {number} idx
* @param {MeasureProps} measureProps
* @returns {Promise<qix.NxMeasure>} reinserted measure
* @description Reinserts a measure into the hypercube.
* @memberof HyperCubeHandler
* @example
* const reinsertedMeasure = await hyperCubeHandler.reinsertMeasure(measure, alternative, idx);
*/
reinsertMeasure(measure, alternative, idx) {
const meas = initializeId(measure);
reinsertMeasure(measureProps) {
const meas = initializeId(measureProps.measure);
if (hcUtils.isMeasureAlternative(this, alternative)) {
return hcUtils.addAlternativeMeasure(this, meas, idx);
if (hcUtils.isMeasureAlternative(this, measureProps.alternative)) {
return hcUtils.addAlternativeMeasure(this, meas, measureProps.index);
}
return reinsertMainMeasure(this, meas, idx);
return reinsertMainMeasure(this, meas, measureProps.index);
}
/**
* Moves a measure within the hypercube.
* @private
* @param {number} fromIndex
* @param {number} toIndex
* @param {Indexes} indexes
* @returns {Promise<void>}
* @description Move measure from one index to another
* @memberof HyperCubeHandler
* @example
* const result = await hyperCubeHandler.moveMeasure(0, 1);
*/
moveMeasure(fromIndex, toIndex) {
moveMeasure(indexes) {
const measures = this.getMeasures();
const altMeasures = this.getAlternativeMeasures();
if (fromIndex < measures.length && toIndex < measures.length) {
if (indexes.fromIndex < measures.length && indexes.toIndex < measures.length) {
// Move within main measures
return Promise.resolve(arrayUtil.move(measures, fromIndex, toIndex));
return Promise.resolve(arrayUtil.move(measures, indexes.fromIndex, indexes.toIndex));
}
if (fromIndex < measures.length && toIndex >= measures.length) {
return hcUtils.moveMeasureFromMainToAlternative(fromIndex, toIndex, measures, altMeasures);
if (indexes.fromIndex < measures.length && indexes.toIndex >= measures.length) {
return hcUtils.moveMeasureFromMainToAlternative(indexes.fromIndex, indexes.toIndex, measures, altMeasures);
}
if (fromIndex >= measures.length && toIndex < measures.length) {
return hcUtils.moveMeasureFromAlternativeToMain(fromIndex, toIndex, measures, altMeasures);
if (indexes.fromIndex >= measures.length && indexes.toIndex < measures.length) {
return hcUtils.moveMeasureFromAlternativeToMain(indexes.fromIndex, indexes.toIndex, measures, altMeasures);
}
return Promise.resolve(hcUtils.moveMeasureWithinAlternative(fromIndex, toIndex, measures, altMeasures));
return Promise.resolve(
hcUtils.moveMeasureWithinAlternative(indexes.fromIndex, indexes.toIndex, measures, altMeasures)
);
}
// ----------------------------------
@@ -621,14 +660,13 @@ class HyperCubeHandler extends DataPropertyHandler {
/**
* Changes the sorting order for the hypercube.
* @private
* @param {number} fromIdx - The index to move from.
* @param {number} toIdx - The index to move to.
* @param {Indexes} indexes
* @memberof HyperCubeHandler
* @example
* const newSortingOrder = hyperCubeHandler.changeSorting(0, 1);
*/
changeSorting(fromIdx, toIdx) {
utils.move(this.hcProperties.qInterColumnSortOrder, fromIdx, toIdx);
changeSorting(indexes) {
utils.move(this.hcProperties.qInterColumnSortOrder, indexes.fromIndex, indexes.toIndex);
}
/**