chore: update types and export HyperCubeHandler (#1779)

* chore: updating types index.d.ts for HyperCubeHanlder

* chore: fix dependencies

* chore: remove nudleus dependecies and fixing comments

* chore: disable no-relative-package import

* chore: fix comments

* chore: update types for data-property and hypercube-handler

* chore: convert more types in data-property-handler to object

* chore: fix more comments

* chore: make all types private

* chore: update lockfile

* chore: make a correction

* chore: fix the classes example import path

---------

Co-authored-by: caele <tsm@qlik.com>
This commit is contained in:
Donya MashaallahPoor
2025-09-11 10:03:26 +02:00
committed by GitHub
parent af5566f40c
commit ccf66ebc50
16 changed files with 1013 additions and 221 deletions

View File

@@ -1,6 +1,7 @@
import hypercube from './hypercube'; import hypercube from './hypercube';
import utils from './utils'; import utils from './utils';
import helpers from './helpers'; import helpers from './helpers';
import arrayUtil from './array-util';
const getType = async ({ halo, name, version }) => { const getType = async ({ halo, name, version }) => {
const { types } = halo; const { types } = halo;
@@ -118,4 +119,7 @@ const conversion = {
*/ */
hypercube, hypercube,
}; };
export { utils, arrayUtil };
export default conversion; export default conversion;

View File

@@ -42,6 +42,8 @@ export {
useEmitter, useEmitter,
onTakeSnapshot, onTakeSnapshot,
onContextMenu, onContextMenu,
HyperCubeHandler,
DataPropertyHandler,
} from '@nebula.js/supernova'; } from '@nebula.js/supernova';
// component internals // component internals

View File

@@ -4,6 +4,7 @@
"version": "6.0.0-alpha.2", "version": "6.0.0-alpha.2",
"main": "src/index.js", "main": "src/index.js",
"devDependencies": { "devDependencies": {
"@nebula.js/conversion": "^6.0.0-alpha.2",
"extend": "3.0.2", "extend": "3.0.2",
"node-event-emitter": "0.0.1" "node-event-emitter": "0.0.1"
} }

View File

@@ -34,17 +34,16 @@ describe('DataPropertyHandler', () => {
describe('getDimensions()', () => { describe('getDimensions()', () => {
test('should return null when dimension is undefined', () => { test('should return null when dimension is undefined', () => {
jest.spyOn(handler, 'getDimensions').mockReturnValue([]); jest.spyOn(handler, 'getDimensions').mockReturnValue([]);
const dimension = handler.getDimension(undefined); const dimension = handler.getDimension({});
expect(dimension).toBeFalsy(); expect(dimension).toBeFalsy();
}); });
test('should return dimension when it exists in getDimensions()', () => { test('should return dimension when it exists in getDimensions()', () => {
jest.spyOn(handler, 'getDimensions').mockReturnValue([{ qDef: { cId: 'dim1' } }]); jest.spyOn(handler, 'getDimensions').mockReturnValue([{ qDef: { cId: 'dim1' } }]);
jest.spyOn(handler, 'getAlternativeDimensions').mockReturnValue([{ qDef: { cId: 'altDim1' } }]); jest.spyOn(handler, 'getAlternativeDimensions').mockReturnValue([{ qDef: { cId: 'altDim1' } }]);
const dimension = handler.getDimension({ id: 'dim1' });
const dimension = handler.getDimension('dim1');
expect(dimension).toEqual({ qDef: { cId: 'dim1' } }); expect(dimension).toEqual({ qDef: { cId: 'dim1' } });
const alternativeDimension = handler.getDimension('altDim1'); const alternativeDimension = handler.getDimension({ id: 'altDim1' });
expect(alternativeDimension).toEqual({ qDef: { cId: 'altDim1' } }); expect(alternativeDimension).toEqual({ qDef: { cId: 'altDim1' } });
}); });
}); });
@@ -52,7 +51,7 @@ describe('DataPropertyHandler', () => {
describe('getMeasure()', () => { describe('getMeasure()', () => {
test('should return null when both measures and alternative measures are empty', () => { test('should return null when both measures and alternative measures are empty', () => {
jest.spyOn(handler, 'getMeasures').mockReturnValue([]); jest.spyOn(handler, 'getMeasures').mockReturnValue([]);
const measure = handler.getMeasure(undefined); const measure = handler.getMeasure({});
expect(measure).toBeFalsy(); expect(measure).toBeFalsy();
}); });
@@ -60,8 +59,8 @@ describe('DataPropertyHandler', () => {
jest.spyOn(handler, 'getMeasures').mockReturnValue([{ qDef: { cId: 'measure1' } }]); jest.spyOn(handler, 'getMeasures').mockReturnValue([{ qDef: { cId: 'measure1' } }]);
jest.spyOn(handler, 'getAlternativeMeasures').mockReturnValue([{ qDef: { cId: 'altMeasure1' } }]); jest.spyOn(handler, 'getAlternativeMeasures').mockReturnValue([{ qDef: { cId: 'altMeasure1' } }]);
const measure = handler.getMeasure('measure1'); const measure = handler.getMeasure({ id: 'measure1' });
const alternativeMeasure = handler.getMeasure('altMeasure1'); const alternativeMeasure = handler.getMeasure({ id: 'altMeasure1' });
expect(measure).toEqual({ qDef: { cId: 'measure1' } }); expect(measure).toEqual({ qDef: { cId: 'measure1' } });
expect(alternativeMeasure).toEqual({ qDef: { cId: 'altMeasure1' } }); expect(alternativeMeasure).toEqual({ qDef: { cId: 'altMeasure1' } });
}); });
@@ -75,31 +74,26 @@ describe('DataPropertyHandler', () => {
}); });
test('should create a dimension with default properties when no field is provided', () => { test('should create a dimension with default properties when no field is provided', () => {
const result = handler.createFieldDimension(null, null, { customDefault: 'value' }); const fieldDimension = { field: '', label: '', defaults: { customDefault: 'value' } };
const result = handler.createFieldDimension(fieldDimension);
expect(result.qDef.qFieldDefs).toEqual([null]); expect(result.qDef.qFieldDefs).toEqual(['']);
expect(result.qDef.qFieldLabels).toEqual(['']); expect(result.qDef.qFieldLabels).toEqual(['']);
expect(result.qDef.qSortCriterias).toEqual(sortingProperties); expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
expect(result.qDef.autoSort).toBe(true); expect(result.qDef.autoSort).toBe(true);
expect(result.someProperty).toBe('defaultValue'); expect(result.someProperty).toBe('defaultValue');
expect(result.customDefault).toBe('value'); expect(result.customDefault).toBe('value');
}); expect(result.qOtherTotalSpec).toEqual({});
test('should create a library dimension with default properties', () => {
const result = handler.createLibraryDimension('libraryId', { customDefault: 'value' });
expect(result.qLibraryId).toBe('libraryId');
expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
expect(result.qDef.autoSort).toBe(true);
expect(result.someProperty).toBe('defaultValue');
expect(result.customDefault).toBe('value');
}); });
test('should create a dimension with provided field and label', () => { test('should create a dimension with provided field and label', () => {
const result = handler.createFieldDimension('fieldName', 'fieldLabel', { customDefault: 'value' }); const fieldDimension = { field: 'fieldName', label: 'fieldLabel' };
const result = handler.createFieldDimension(fieldDimension);
expect(result.qDef.qFieldDefs).toEqual(['fieldName']); expect(result.qDef.qFieldDefs).toEqual(['fieldName']);
expect(result.qDef.qFieldLabels).toEqual(['fieldLabel']); expect(result.qDef.qFieldLabels).toEqual(['fieldLabel']);
expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
expect(result.qDef.autoSort).toBe(true);
}); });
}); });
@@ -110,21 +104,17 @@ describe('DataPropertyHandler', () => {
}); });
}); });
test('should delete qFieldDefs and qFieldLabels from the dimension', () => { test('should create dimension and delete qFieldDefs and qFieldLabels from it', () => {
const result = handler.createLibraryDimension('libraryId', {}); const libraryDimension = { id: 'libraryId', defaults: { customDefault: 'value' } };
const result = handler.createLibraryDimension(libraryDimension);
expect(result.qDef.qFieldDefs).toBeUndefined();
expect(result.qDef.qFieldLabels).toBeUndefined();
});
test('should create a library dimension with default properties', () => {
const result = handler.createLibraryDimension('libraryId', { customDefault: 'value' });
expect(result.qLibraryId).toBe('libraryId'); expect(result.qLibraryId).toBe('libraryId');
expect(result.qDef.qSortCriterias).toEqual(sortingProperties); expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
expect(result.qDef.autoSort).toBe(true);
expect(result.someProperty).toBe('defaultValue'); expect(result.someProperty).toBe('defaultValue');
expect(result.customDefault).toBe('value'); expect(result.customDefault).toBe('value');
expect(result.qDef.autoSort).toBe(true);
expect(result.qDef.qFieldDefs).toBeUndefined();
expect(result.qDef.qFieldLabels).toBeUndefined();
}); });
}); });
@@ -136,7 +126,12 @@ describe('DataPropertyHandler', () => {
}); });
test('should create a measure with provided expression and label', () => { test('should create a measure with provided expression and label', () => {
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', { customDefault: 'value' }); const expressionMeasure = {
expression: 'SUM(Sales)',
label: 'Total Sales',
defaults: { customDefault: 'value' },
};
const result = handler.createExpressionMeasure(expressionMeasure);
expect(result.qDef.qDef).toBe('SUM(Sales)'); expect(result.qDef.qDef).toBe('SUM(Sales)');
expect(result.qDef.qLabel).toBe('Total Sales'); expect(result.qDef.qLabel).toBe('Total Sales');
@@ -146,14 +141,16 @@ describe('DataPropertyHandler', () => {
}); });
test('should initialize qDef and qNumFormat if not provided', () => { test('should initialize qDef and qNumFormat if not provided', () => {
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', {}); const expressionMeasure = { expression: 'SUM(Sales)', label: 'Total Sales', defaults: {} };
const result = handler.createExpressionMeasure(expressionMeasure);
expect(result.qDef).toBeDefined(); expect(result.qDef).toBeDefined();
expect(result.qDef.qNumFormat).toBeDefined(); expect(result.qDef.qNumFormat).toBeDefined();
}); });
test('should handle empty defaults gracefully', () => { test('should handle empty defaults gracefully', () => {
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', null); const expressionMeasure = { expression: 'SUM(Sales)', label: 'Total Sales' };
const result = handler.createExpressionMeasure(expressionMeasure);
expect(result.qDef.qDef).toBe('SUM(Sales)'); expect(result.qDef.qDef).toBe('SUM(Sales)');
expect(result.qDef.qLabel).toBe('Total Sales'); expect(result.qDef.qLabel).toBe('Total Sales');
@@ -170,7 +167,8 @@ describe('DataPropertyHandler', () => {
}); });
test('should create a library measure with provided id and defaults', () => { test('should create a library measure with provided id and defaults', () => {
const result = handler.createLibraryMeasure('libraryId', { customDefault: 'value' }); const libraryMeasure = { id: 'libraryId', defaults: { customDefault: 'value' } };
const result = handler.createLibraryMeasure(libraryMeasure);
expect(result.qLibraryId).toBe('libraryId'); expect(result.qLibraryId).toBe('libraryId');
expect(result.qDef.qNumFormat).toBeDefined(); expect(result.qDef.qNumFormat).toBeDefined();
@@ -180,14 +178,16 @@ describe('DataPropertyHandler', () => {
}); });
test('should initialize qDef and qNumFormat if not provided', () => { test('should initialize qDef and qNumFormat if not provided', () => {
const result = handler.createLibraryMeasure('libraryId', {}); const libraryMeasure = { id: 'libraryId', defaults: {} };
const result = handler.createLibraryMeasure(libraryMeasure);
expect(result.qDef).toBeDefined(); expect(result.qDef).toBeDefined();
expect(result.qDef.qNumFormat).toBeDefined(); expect(result.qDef.qNumFormat).toBeDefined();
}); });
test('should delete qDef.qDef and qDef.qLabel from the measure', () => { test('should delete qDef.qDef and qDef.qLabel from the measure', () => {
const result = handler.createLibraryMeasure('libraryId', {}); const libraryMeasure = { id: 'libraryId', defaults: {} };
const result = handler.createLibraryMeasure(libraryMeasure);
expect(result.qDef.qDef).toBeUndefined(); expect(result.qDef.qDef).toBeUndefined();
expect(result.qDef.qLabel).toBeUndefined(); expect(result.qDef.qLabel).toBeUndefined();
@@ -195,14 +195,7 @@ describe('DataPropertyHandler', () => {
}); });
describe('maxMeasures', () => { describe('maxMeasures', () => {
let galaxy;
beforeEach(() => { beforeEach(() => {
galaxy = {
flags: {
isEnabled: jest.fn().mockReturnValue(false),
},
};
handler = new HyperCubeHandler({ handler = new HyperCubeHandler({
measureDefinition: { max: 0 }, measureDefinition: { max: 0 },
dimensionDefinition: { max: 0 }, dimensionDefinition: { max: 0 },
@@ -221,15 +214,6 @@ describe('DataPropertyHandler', () => {
expect(result).toBe(5); expect(result).toBe(5);
}); });
test('should pass properties to measureDefinition.max when feature flag is enabled', () => {
handler.measureDefinition.max = jest.fn().mockReturnValue(5);
galaxy.flags.isEnabled.mockReturnValue(true);
handler.properties = { someProperty: 'value' };
const result = handler.maxMeasures();
expect(result).toBe(5);
});
test('should return measureDefinition.max when it is a valid number', () => { test('should return measureDefinition.max when it is a valid number', () => {
handler.measureDefinition.max = 8; handler.measureDefinition.max = 8;
@@ -260,14 +244,7 @@ describe('DataPropertyHandler', () => {
}); });
describe('maxDimensions', () => { describe('maxDimensions', () => {
let galaxy;
beforeEach(() => { beforeEach(() => {
galaxy = {
flags: {
isEnabled: jest.fn().mockReturnValue(false),
},
};
handler = new HyperCubeHandler({ handler = new HyperCubeHandler({
measureDefinition: { max: 0 }, measureDefinition: { max: 0 },
dimensionDefinition: { max: 0 }, dimensionDefinition: { max: 0 },
@@ -282,15 +259,6 @@ describe('DataPropertyHandler', () => {
expect(result).toBe(5); expect(result).toBe(5);
}); });
test('should pass properties to dimensionDefinition.max when feature flag is enabled', () => {
handler.dimensionDefinition.max = jest.fn().mockReturnValue(10);
galaxy.flags.isEnabled.mockReturnValue(true);
handler.properties = { someProperty: 'value' };
const result = handler.maxDimensions();
expect(result).toBe(10);
});
test('should return dimensionDefinition.max when it is a valid number', () => { test('should return dimensionDefinition.max when it is a valid number', () => {
handler.dimensionDefinition.max = 8; handler.dimensionDefinition.max = 8;

View File

@@ -1,10 +1,38 @@
import extend from 'extend'; import extend from 'extend';
// eslint-disable-next-line import/no-relative-packages import { findFieldById, initializeDim, useMasterNumberFormat } from './utils/field-helper/field-utils';
import isEnabled from '../../../nucleus/src/flags/flags';
import { findFieldById, useMasterNumberFormat } from './utils/field-helper/field-utils';
import { INITIAL_SORT_CRITERIAS } from './utils/constants'; import { INITIAL_SORT_CRITERIAS } from './utils/constants';
import { notSupportedError } from './utils/hypercube-helper/hypercube-utils'; import { notSupportedError } from './utils/hypercube-helper/hypercube-utils';
/**
* @private
* @class DataPropertyHandler
* @description A class to handle data properties for dimensions and measures in a data model.
* @param {object} opts - Parameters to add a hypercube handlers
* @param {qix.Doc} opts.app
* @param {object} opts.dimensionDefinition
* @param {object} opts.measureDefinition
* @param {object} opts.dimensionProperties
* @param {object} opts.measureProperties
* @param {object} opts.globalChangeListeners
* @entry
* @export
* @example
* import DataPropertyHandler from '@nebula.js/stardust';
*
* class PivotHyperCubeHandler extends DataPropertyHandler {
*
* addDimensionAsFirstRow: (hypercube: HyperCubeDef, dimension: NxDimension) => {
* const dimensions = this.getDimensions().length;
* const { qInterColumnSortOrder } = hypercube;
*
* if(dimensions !== 0 && dimensions < this.maxDimensions()) {
* hypercube.qNoOfLeftDims = 1;
* qInterColumnSortOrder?.unshift(dimensions);
* dimensions.splice(0, 0, dimension);
* }
* }
* }
*/
class DataPropertyHandler { class DataPropertyHandler {
constructor(opts) { constructor(opts) {
const options = opts || {}; const options = opts || {};
@@ -18,20 +46,85 @@ class DataPropertyHandler {
this.app = options.app; this.app = options.app;
} }
/**
* @private
* @typeof {object} LibraryDimension
* @property {string} id
* @property {qix.NxDimension=} defaults
*/
/**
* @private
* @typeof {object} FieldDimension
* @property {string} field
* @property {string=} label
* @property {qix.NxDimension=} defaults
*/
/**
* @private
* @typeof {object} LibraryMeasure
* @property {string} id
* @property {qix.NxMeasure=} defaults
*/
/**
* @private
* @typeof {object} ExpressionMeasure
* @property {string} expression
* @property {string=} label
* @property {qix.NxMeasure=} defaults
*/
/**
* Sets the properties for the handler.
* @private
* @param {object=} properties - The properties object to set.
* @description Updates the handler's properties and analysis type flag.
* @memberof DataPropertyHandler
* @example
* handler.setProperties({ metaData: { isAnalysisType: true } });
*/
setProperties(properties) { setProperties(properties) {
this.properties = properties; this.properties = properties;
this.isAnalysisType = this.properties?.metaData?.isAnalysisType; this.isAnalysisType = this.properties?.metaData?.isAnalysisType;
} }
/**
* Sets the global change listeners.
* @private
* @param {Function[]} arr - Array of listener functions.
* @description Assigns global change listeners to the handler.
* @memberof DataPropertyHandler
* @example
* handler.setGlobalChangeListeners([listener1, listener2]);
*/
setGlobalChangeListeners(arr) { setGlobalChangeListeners(arr) {
this.globalChangeListeners = arr; this.globalChangeListeners = arr;
} }
/**
* @private
* @param {object=} layout - The layout object to set.
* @description Sets the layout for the handler.
* @memberof DataPropertyHandler
* @example
* handler.setLayout(layoutObj);
*/
setLayout(layout) { setLayout(layout) {
this.layout = layout; this.layout = layout;
} }
static type() { /**
* @private
* @throws {Error}
* @description Throws an error indicating the method must be overridden.
* @memberof DataPropertyHandler
* @example
* DataPropertyHandler.type(); // Throws error
*/
// eslint-disable-next-line class-methods-use-this
type() {
throw new Error('Must override this method'); throw new Error('Must override this method');
} }
@@ -39,56 +132,138 @@ class DataPropertyHandler {
// ---------------DIMENSION--------------- // ---------------DIMENSION---------------
// --------------------------------------- // ---------------------------------------
static getDimensions() { /**
* @private
* @returns {Array} Empty array.
* @description Returns the default dimension array.
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
getDimensions() {
return []; return [];
} }
getDimension(id) { /**
* Gets a dimension by id from dimensions or alternative dimensions.
* @private
* @param {LibraryDimension} libraryDimension
* @returns {qix.NxDimension} - The found dimension.
* @description Searches for a dimension by id in both main and alternative dimensions.
* @memberof DataPropertyHandler
* @example
* const dim = handler.getDimension({ id: 'dimId' });
*/
getDimension(libraryDimension) {
const dimensions = this.getDimensions(); const dimensions = this.getDimensions();
const alternativeDimensions = this.getAlternativeDimensions(); const alternativeDimensions = this.getAlternativeDimensions();
return findFieldById(dimensions, id) ?? findFieldById(alternativeDimensions, id); return findFieldById(dimensions, libraryDimension.id) ?? findFieldById(alternativeDimensions, libraryDimension.id);
} }
static getAlternativeDimensions() { /**
* Throws an error indicating the method must be implemented in subclasses.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
getAlternativeDimensions() {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
static addDimension() { /**
* Throws an error indicating addDimension is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
addDimension() {
throw notSupportedError; throw notSupportedError;
} }
static addDimensions() { /**
* Throws an error indicating addDimensions is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
addDimensions() {
throw notSupportedError; throw notSupportedError;
} }
static removeDimension() { /**
* Throws an error indicating removeDimension is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
removeDimension() {
throw notSupportedError; throw notSupportedError;
} }
static removeDimensions() { /**
* Throws an error indicating removeDimensions is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
removeDimensions() {
throw notSupportedError; throw notSupportedError;
} }
static autoSortDimension() { /**
* Throws an error indicating autoSortDimension is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
autoSortDimension() {
throw notSupportedError; throw notSupportedError;
} }
static replaceDimension() { /**
* Throws an error indicating replaceDimension is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
replaceDimension() {
throw notSupportedError; throw notSupportedError;
} }
static getSorting() { /**
* Throws an error indicating getSorting is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
getSorting() {
throw notSupportedError; throw notSupportedError;
} }
createLibraryDimension(id, defaults) { /**
const dimension = extend(true, {}, this.dimensionProperties || {}, defaults || {}); * Creates a type of library dimension with a field definition.
* @private
* @param {LibraryDimension} libraryDimension
* @returns {qix.NxDimension} The created dimension object.
* @description Initializes a dimension and applying default properties and sort criteria.
* @memberof DataPropertyHandler
* @example
* const dim = handler.createLibraryDimension({ id:'dimId', { qDef: { cId: 'dim1' } }});
*/
createLibraryDimension(libraryDimension) {
let dimension = extend(true, {}, this.dimensionProperties || {}, libraryDimension.defaults || {});
dimension.qDef = dimension.qDef ?? {}; dimension = initializeDim(dimension);
dimension.qOtherTotalSpec = dimension.qOtherTotalSpec ?? {};
dimension.qLibraryId = id; dimension.qLibraryId = libraryDimension.id;
dimension.qDef.autoSort = true; dimension.qDef.autoSort = true;
dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS; dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS;
@@ -98,87 +273,201 @@ class DataPropertyHandler {
return dimension; return dimension;
} }
createFieldDimension(field, label, defaults) { /**
const dimension = extend(true, {}, this.dimensionProperties || {}, defaults || {}); * Creates a type of field dimension with a field definition.
* @private
* @param {FieldDimension} fieldDimension
* @returns {qix.NxDimension} The created dimension object.
* @description Initializes a dimension with field definitions, labels, and default properties.
* @memberof DataPropertyHandler
* @example
* handler.createFieldDimension({field: 'currentField', label: 'label'});
*/
createFieldDimension(fieldDimension) {
let dimension = extend(true, {}, this.dimensionProperties || {}, fieldDimension.defaults || {});
dimension.qDef = dimension.qDef ?? {}; dimension = initializeDim(dimension);
dimension.qOtherTotalSpec = dimension.qOtherTotalSpec ?? {};
if (!field) { if (!fieldDimension.field) {
dimension.qDef.qFieldDefs = []; dimension.qDef.qFieldDefs = [];
dimension.qDef.qFieldLabels = []; dimension.qDef.qFieldLabels = [];
dimension.qDef.qSortCriterias = []; dimension.qDef.qSortCriterias = [];
} }
dimension.qDef.qFieldDefs = [field]; dimension.qDef.qFieldDefs = [fieldDimension.field];
dimension.qDef.qFieldLabels = label ? [label] : ['']; dimension.qDef.qFieldLabels = fieldDimension.label ? [fieldDimension.label] : [''];
dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS; dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS;
dimension.qDef.autoSort = true; dimension.qDef.autoSort = true;
return dimension; return dimension;
} }
addFieldDimension(field, label, defaults) { /**
const dimension = this.createFieldDimension(field, label, defaults); * Adds a field dimension to the handler.
* @private
* @param {FieldDimension} fieldDimension
* @returns {Promise<qix.NxDimension=>} The result of addDimension.
* @description Creates and adds a field dimension.
* @memberof DataPropertyHandler
* @example
* handler.addFieldDimension({field: 'currentField', label: 'label'});
*/
addFieldDimension(fieldDimension) {
const dimension = this.createFieldDimension(fieldDimension);
return this.addDimension(dimension); return this.addDimension(dimension);
} }
addFieldDimensions(args) { /**
const dimensions = args.map(({ field, label, defaults }) => this.createFieldDimension(field, label, defaults)); * @private
* @param {FieldDimension[]} fieldDimensions - Array of field dimension.
* @returns {Promise<qix.NxDimension[]>} The result of addDimensions.
* @description Creates and adds multiple field dimensions.
* @memberof DataPropertyHandler
* @example
* handler.addFieldDimensions([{ field: 'A', label: 'AA' }, { field: 'B', label: 'BB' }]);
*/
addFieldDimensions(fieldDimensions) {
const dimensions = fieldDimensions.map((fieldDimension) => this.createFieldDimension(fieldDimension));
return this.addDimensions(dimensions); return this.addDimensions(dimensions);
} }
addLibraryDimension(id, defaults) { /**
const dimension = this.createLibraryDimension(id, defaults); * Adds a library dimension to the handler.
* @private
* @param {LibraryDimension} libraryDimension
* @returns {Promise<qix.NxDimension=>} The result of addDimension.
* @description Creates and adds a library dimension.
* @memberof DataPropertyHandler
* @example
* handler.addLibraryDimension({ id: 'A'});
*/
addLibraryDimension(libraryDimension) {
const dimension = this.createLibraryDimension(libraryDimension);
return this.addDimension(dimension); return this.addDimension(dimension);
} }
addLibraryDimensions(args) { /**
const dimensions = args.map(({ id, defaults }) => this.createLibraryDimension(id, defaults)); * Adds multiple library dimensions to the handler.
* @private
* @param {LibraryDimension[]} libraryDimensions - Array of library dimension.
* @returns {Promise<qix.NxDimension[]>} The result of addDimensions.
* @description Creates and adds multiple library dimensions.
* @memberof DataPropertyHandler
* @example
* handler.addLibraryDimensions([{ id: 'A' }, { id: 'B', defaults: { ... } }]);
*/
addLibraryDimensions(libraryDimensions) {
const dimensions = libraryDimensions.map((libraryDimension) => this.createLibraryDimension(libraryDimension));
const result = this.addDimensions(dimensions); const result = this.addDimensions(dimensions);
return result; return result;
} }
async addAltLibraryDimensions(args) { /**
const dimensions = args.map(({ id }) => this.createLibraryDimension(id)); * Adds multiple alternative library dimensions to the handler.
* @private
* @param {LibraryDimension[]} libraryDimensions - Array of library dimension.
* @returns {Promise<qix.NxDimension[]>} The result of addDimensions.
* @description Creates and adds multiple alternative library dimensions.
* @memberof DataPropertyHandler
* @example
* await handler.addAltLibraryDimensions([{ id: 'A' }, { id: 'B', defaults: { ... } }]);
*/
async addAltLibraryDimensions(libraryDimensions) {
const dimensions = libraryDimensions.map((libraryDimension) => this.createLibraryDimension(libraryDimension));
return this.addDimensions(dimensions, true); return this.addDimensions(dimensions, true);
} }
async addAltFieldDimensions(args) { /**
const dimensions = args.map(({ field }) => this.createFieldDimension(field)); * Adds multiple alternative field dimensions to the handler.
* @private
* @param {FieldDimension[]} fieldDimensions - Array of field dimension.
* @returns {Promise<qix.NxDimension[]>} The result of addDimensions.
* @description Creates and adds multiple alternative field dimensions.
* @memberof DataPropertyHandler
* @example
* await handler.addAltFieldDimensions([{ field: 'A', label: 'Label A' }, { field: 'B', label: 'Label B' }]);
*/
async addAltFieldDimensions(fieldDimensions) {
const dimensions = fieldDimensions.map((fieldDimension) => this.createFieldDimension(fieldDimension));
return this.addDimensions(dimensions, true); return this.addDimensions(dimensions, true);
} }
addAlternativeFieldDimension(field, label, defaults) { /**
const dimension = this.createFieldDimension(field, label, defaults); * Adds an alternative field dimension to the handler.
* @private
* @param {FieldDimension} fieldDimension
* @returns {Promise<qix.NxDimension=>} The result of addDimension.
* @description Creates and adds an alternative field dimension.
* @memberof DataPropertyHandler
* @example
* handler.addAlternativeFieldDimension({ field: 'A', label: 'Label A' });
*/
addAlternativeFieldDimension(fieldDimension) {
const dimension = this.createFieldDimension(fieldDimension);
return this.addDimension(dimension, true); return this.addDimension(dimension, true);
} }
addAlternativeLibraryDimension(id, defaults) { /**
const dimension = this.createLibraryDimension(id, defaults); * Adds an alternative library dimension to the handler.
* @private
* @param {LibraryDimension} libraryDimension
* @returns {Promise<qix.NxDimension=>} The result of addDimension.
* @description Creates and adds an alternative library dimension.
* @memberof DataPropertyHandler
* @example
* handler.addAlternativeLibraryDimension([{ id: 'A' }, { id: 'B', defaults: { ... } }]);
*/
addAlternativeLibraryDimension(libraryDimension) {
const dimension = this.createLibraryDimension(libraryDimension);
return this.addDimension(dimension, true); return this.addDimension(dimension, true);
} }
/**
* Gets the minimum number of dimensions allowed.
* @private
* @returns {number} The minimum number of dimensions.
* @description Returns the minimum number of dimensions allowed by the handler.
* @memberof DataPropertyHandler
* @example
* const min = handler.minDimensions();
*/
minDimensions() { minDimensions() {
if (typeof this.dimensionDefinition.min === 'function') { if (typeof this.dimensionDefinition.min === 'function') {
return this.dimensionDefinition.min.call(null, this.properties, this); return this.dimensionDefinition.min.call(null, this.properties, this);
} }
return this.dimensionDefinition.min || 0; return this.dimensionDefinition.min || 0;
} }
/**
* Gets the maximum number of dimensions allowed.
* @private
* @param {number} [decrement=0] - The number to decrement from the current number of measures.
* @returns {number} The maximum number of dimensions allowed.
* @description Checks if the max property is a function and calls it with the current number of measures, or returns a default value.
* @memberof DataPropertyHandler
* @example
* const max = handler.maxDimensions();
*/
maxDimensions(decrement = 0) { maxDimensions(decrement = 0) {
const measureLength = this.getMeasures().length - decrement; const measureLength = this.getMeasures().length - decrement;
if (typeof this.dimensionDefinition.max === 'function') { if (typeof this.dimensionDefinition.max === 'function') {
const dimParams = isEnabled('PS_21371_ANALYSIS_TYPES') ? [measureLength, this.properties] : [measureLength]; const dimParams = [measureLength];
return this.dimensionDefinition.max?.apply(null, dimParams); return this.dimensionDefinition.max?.apply(null, dimParams);
} }
return Number.isNaN(+this.dimensionDefinition.max) ? 10000 : this.dimensionDefinition.max; return Number.isNaN(+this.dimensionDefinition.max) ? 10000 : this.dimensionDefinition.max;
} }
/**
* Checks if a new dimension can be added.
* @private
* @returns {boolean} True if a new dimension can be added, false otherwise.
* @description Returns whether the handler can add another dimension.
* @memberof DataPropertyHandler
* @example
* if (handler.canAddDimension()) { handler.addFieldDimension('A'); }
*/
canAddDimension() { canAddDimension() {
return this.getDimensions().length < this.maxDimensions(); return this.getDimensions().length < this.maxDimensions();
} }
@@ -187,80 +476,183 @@ class DataPropertyHandler {
// ----------------MEASURE---------------- // ----------------MEASURE----------------
// --------------------------------------- // ---------------------------------------
getMeasure(id) { /**
const measures = this.getMeasures(); * @private
const alternativeMeasures = this.getAlternativeMeasures(); * @returns {Array} Empty array.
* @description Returns the default measure array.
return findFieldById(measures, id) ?? findFieldById(alternativeMeasures, id); * @memberof DataPropertyHandler
} */
// eslint-disable-next-line class-methods-use-this
static getMeasures() { getMeasures() {
return []; return [];
} }
static getAlternativeMeasures() { /**
* Throws an error indicating the method must be implemented in subclasses.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
getAlternativeMeasures() {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
static addMeasure() { /**
* Throws an error indicating addMeasure is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
addMeasure() {
throw notSupportedError; throw notSupportedError;
} }
static addMeasures() { /**
* Throws an error indicating addMeasures is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
addMeasures() {
throw notSupportedError; throw notSupportedError;
} }
static removeMeasure() { /**
* Throws an error indicating removeMeasure is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
removeMeasure() {
throw notSupportedError; throw notSupportedError;
} }
static removeMeasures() { /**
* Throws an error indicating removeMeasures is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
removeMeasures() {
throw notSupportedError; throw notSupportedError;
} }
static autoSortMeasure() { /**
* Throws an error indicating autoSortMeasure is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
autoSortMeasure() {
throw notSupportedError; throw notSupportedError;
} }
static replaceMeasure() { /**
* Throws an error indicating replaceMeasure is not supported in the base class.
* @private
* @throws {Error}
* @memberof DataPropertyHandler
*/
// eslint-disable-next-line class-methods-use-this
replaceMeasure() {
throw notSupportedError; throw notSupportedError;
} }
createExpressionMeasure(expression, label, defaults) { /**
const measure = extend(true, {}, this.measureProperties || {}, defaults || {}); * Gets a measure by id from measures or alternative measures.
* @private
* @param {string} libraryDimension - The measure id to find.
* @returns {qix.NxMeasure} The found measure or undefined.
* @description Searches for a measure by id in both main and alternative measures.
* @memberof DataPropertyHandler
* @example
* const measure = handler.getMeasure('measId');
*/
getMeasure(libraryDimension) {
const measures = this.getMeasures();
const alternativeMeasures = this.getAlternativeMeasures();
return findFieldById(measures, libraryDimension.id) ?? findFieldById(alternativeMeasures, libraryDimension.id);
}
/**
* Creates an expression measure.
* @private
* @param {ExpressionMeasure} expressionMeasure
* @returns {Promise<qix.NxMeasure=>} The created measure object.
* @description Initializes a measure with an expression, label, and default properties.
* @memberof DataPropertyHandler
* @example
* const meas = handler.createExpressionMeasure({ expression: 'Sum(Sales)', label: 'Total Sales' });
*/
createExpressionMeasure(expressionMeasure) {
const measure = extend(true, {}, this.measureProperties || {}, expressionMeasure.defaults || {});
measure.qDef = measure.qDef ?? {}; measure.qDef = measure.qDef ?? {};
measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {}; measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {};
measure.qDef.qDef = expression; measure.qDef.qDef = expressionMeasure.expression;
measure.qDef.qLabel = label; measure.qDef.qLabel = expressionMeasure.label;
measure.qDef.autoSort = true; measure.qDef.autoSort = true;
return measure; return measure;
} }
addExpressionMeasure(expression, label, defaults) { /**
const measure = this.createExpressionMeasure(expression, label, defaults); * Adds an expression measure to the handler.
* @private
* @param {ExpressionMeasure} expressionMeasure
* @returns {Promise<qix.NxMeasure=>} The result of addMeasure.
* @description Creates and adds an expression measure.
* @memberof DataPropertyHandler
* @example
* handler.addExpressionMeasure({ expression: 'Sum(Sales)', label: 'Total Sales' });
*/
addExpressionMeasure(expressionMeasure) {
const measure = this.createExpressionMeasure(expressionMeasure);
return this.addMeasure(measure); return this.addMeasure(measure);
} }
addExpressionMeasures(args) { /**
const measures = args.map(({ expression, label, defaults }) => * Adds multiple expression measures to the handler.
this.createExpressionMeasure(expression, label, defaults) * @private
); * @param {ExpressionMeasure[]} expressionMeasures - Array of expression measure.
* @returns {Promise<qix.NxMeasure[]>} The result of addMeasures.
* @description Creates and adds multiple expression measures.
* @memberof DataPropertyHandler
* @example
* handler.addExpressionMeasures([{ expression: 'Sum(A)' }, { expression: 'Sum(B)', label: 'B' }]);
*/
addExpressionMeasures(expressionMeasures) {
const measures = expressionMeasures.map((expressionMeasure) => this.createExpressionMeasure(expressionMeasure));
return this.addMeasures(measures); return this.addMeasures(measures);
} }
createLibraryMeasure(id, defaults) { /**
const measure = extend(true, {}, this.measureProperties || {}, defaults || {}); * Creates a library measure.
* @private
* @param {LibraryMeasure} libraryMeasure
* @returns {qix.NxMeasure} The created measure object.
* @description Initializes a library measure with default properties.
* @memberof DataPropertyHandler
* @example
* const meas = handler.createLibraryMeasure({ id: 'measId', defaults: { qDef: { ... } } });
*/
createLibraryMeasure(libraryMeasure) {
const measure = extend(true, {}, this.measureProperties || {}, libraryMeasure.defaults || {});
measure.qDef = measure.qDef ?? {}; measure.qDef = measure.qDef ?? {};
measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {}; measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {};
if (isEnabled('MASTER_MEASURE_FORMAT')) { useMasterNumberFormat(measure.qDef);
useMasterNumberFormat(measure.qDef);
}
measure.qLibraryId = id; measure.qLibraryId = libraryMeasure.id;
measure.qDef.autoSort = true; measure.qDef.autoSort = true;
delete measure.qDef.qDef; delete measure.qDef.qDef;
@@ -269,36 +661,105 @@ class DataPropertyHandler {
return measure; return measure;
} }
addLibraryMeasure(id, defaults) { /**
const measure = this.createLibraryMeasure(id, defaults); * Adds a library measure to the handler.
* @private
* @param {LibraryMeasure} libraryMeasure
* @returns {Promise<qix.NxMeasure=>} The result of addMeasure.
* @description Creates and adds a library measure.
* @memberof DataPropertyHandler
* @example
* handler.addLibraryMeasure({ id: 'measId', defaults: { qDef: { ... } } });
*/
addLibraryMeasure(libraryMeasure) {
const measure = this.createLibraryMeasure(libraryMeasure);
return this.addMeasure(measure); return this.addMeasure(measure);
} }
addLibraryMeasures(args) { /**
const measures = args.map(({ id, defaults }) => this.createLibraryMeasure(id, defaults)); * Adds multiple library measures to the handler.
* @private
* @param {LibraryMeasure[]} libraryMeasures - Array of library measure.
* @returns {Promise<qix.NxMeasure[]>} The result of addMeasures.
* @description Creates and adds multiple library measures.
* @memberof DataPropertyHandler
* @example
* handler.addLibraryMeasures([{ id: 'A' }, { id: 'B', defaults: { qDef: { ... } } }]);
*/
addLibraryMeasures(libraryMeasures) {
const measures = libraryMeasures.map((libraryMeasure) => this.createLibraryMeasure(libraryMeasure));
return this.addMeasures(measures); return this.addMeasures(measures);
} }
addAltLibraryMeasures(args) { /**
const measures = args.map(({ id }) => this.createLibraryMeasure(id)); * Adds multiple alternative library measures to the handler.
* @private
* @param {LibraryMeasure[]} libraryMeasures - Array of library measure.
* @returns {Promise<qix.NxMeasure[]>} The result of addMeasures.
* @description Creates and adds multiple alternative library measures.
* @memberof DataPropertyHandler
* @example
* handler.addAltLibraryMeasures([{ id: 'A' }, { id: 'B' }]);
*/
addAltLibraryMeasures(libraryMeasures) {
const measures = libraryMeasures.map((libraryMeasure) => this.createLibraryMeasure(libraryMeasure));
return this.addMeasures(measures, true); return this.addMeasures(measures, true);
} }
addAltExpressionMeasures(args) { /**
const measures = args.map(({ expression }) => this.createExpressionMeasure(expression)); * Adds multiple alternative expression measures to the handler.
* @private
* @param {ExpressionMeasure[]} expressionMeasures - Array of expression measure.
* @returns {Promise<qix.NxMeasure[]>} The result of addMeasures.
* @description Creates and adds multiple alternative expression measures.
* @memberof DataPropertyHandler
* @example
* handler.addAltExpressionMeasures([{ expression: 'Sum(A)' }, { expression: 'Sum(B)' }]);
*/
addAltExpressionMeasures(libraryMeasures) {
const measures = libraryMeasures.map((expressionMeasure) => this.createExpressionMeasure(expressionMeasure));
return this.addMeasures(measures, true); return this.addMeasures(measures, true);
} }
addAlternativeExpressionMeasure(expression, label, defaults) { /**
const measure = this.createExpressionMeasure(expression, label, defaults); * Adds an alternative expression measure to the handler.
* @private
* @param {ExpressionMeasure} expressionMeasure
* @returns {Promise<qix.NxMeasure=>} The result of addMeasure.
* @description Creates and adds an alternative expression measure.
* @memberof DataPropertyHandler
* @example
* handler.addAlternativeExpressionMeasure({ expression: 'Sum(Sales)', label: 'Total Sales'});
*/
addAlternativeExpressionMeasure(expressionMeasure) {
const measure = this.createExpressionMeasure(expressionMeasure);
return this.addMeasure(measure, true); return this.addMeasure(measure, true);
} }
addAlternativeLibraryMeasure(id, defaults) { /**
const measure = this.createLibraryMeasure(id, defaults); * Adds an alternative library measure to the handler.
* @private
* @param {LibraryMeasure} libraryMeasure
* @returns {qix.NxMeasure=} The result of addMeasure.
* @description Creates and adds an alternative library measure.
* @memberof DataPropertyHandler
* @example
* handler.addAlternativeLibraryMeasure({ id: 'measId', defaults: { qDef: { ... } } });
*/
addAlternativeLibraryMeasure(libraryMeasure) {
const measure = this.createLibraryMeasure(libraryMeasure);
return this.addMeasure(measure, true); return this.addMeasure(measure, true);
} }
/**
* Gets the minimum number of measures allowed.
* @private
* @returns {number} The minimum number of measures.
* @description Returns the minimum number of measures allowed by the handler.
* @memberof DataPropertyHandler
* @example
* const min = handler.minMeasures();
*/
minMeasures() { minMeasures() {
if (typeof this.measureDefinition.min === 'function') { if (typeof this.measureDefinition.min === 'function') {
return this.measureDefinition.min.call(null, this.properties, this); return this.measureDefinition.min.call(null, this.properties, this);
@@ -306,24 +767,51 @@ class DataPropertyHandler {
return this.measureDefinition.min || 0; return this.measureDefinition.min || 0;
} }
/**
* Gets the maximum number of measures allowed.
* @private
* @param {number} [decrement=0] - The number to decrement from the current number of dimensions.
* @returns {number} The maximum number of measures allowed.
* @description Checks if the max property is a function and calls it with the current number of dimensions, or returns a default value.
* @memberof DataPropertyHandler
* @example
* const max = handler.maxMeasures();
*/
maxMeasures(decrement = 0) { maxMeasures(decrement = 0) {
if (typeof this.measureDefinition.max === 'function') { if (typeof this.measureDefinition.max === 'function') {
const dimLength = this.getDimensions().length - decrement; const dimLength = this.getDimensions().length - decrement;
const measureParams = isEnabled('PS_21371_ANALYSIS_TYPES') ? [dimLength, this.properties] : [dimLength]; const measureParams = [dimLength];
return this.measureDefinition.max.apply(null, measureParams); return this.measureDefinition.max.apply(null, measureParams);
} }
return Number.isNaN(+this.measureDefinition.max) ? 10000 : this.measureDefinition.max; return Number.isNaN(+this.measureDefinition.max) ? 10000 : this.measureDefinition.max;
} }
/**
* Checks if a new measure can be added.
* @private
* @returns {boolean} True if a new measure can be added, false otherwise.
* @description Returns whether the handler can add another measure.
* @memberof DataPropertyHandler
* @example
* if (handler.canAddMeasure()) { handler.addExpressionMeasure('Sum(A)'); }
*/
canAddMeasure() { canAddMeasure() {
return this.getMeasures().length < this.maxMeasures(); return this.getMeasures().length < this.maxMeasures();
// return this.getMeasures().length < 10000;
} }
// --------------------------------------- // ---------------------------------------
// ---------------OTHERS------------------ // ---------------OTHERS------------------
// --------------------------------------- // ---------------------------------------
/**
* Calls all global change listeners with the current properties, handler, and layout.
* @private
* @param {object} layout - The layout object to pass to listeners.
* @description Invokes all registered global change listeners.
* @memberof DataPropertyHandler
* @example
* handler.updateGlobalChangeListeners(layoutObj);
*/
updateGlobalChangeListeners(layout) { updateGlobalChangeListeners(layout) {
if (this.globalChangeListeners) { if (this.globalChangeListeners) {
(this.globalChangeListeners || []).forEach((func) => { (this.globalChangeListeners || []).forEach((func) => {

View File

@@ -1,10 +1,9 @@
// eslint-disable-next-line import/no-relative-packages import { utils, arrayUtil } from '@nebula.js/conversion';
import utils from '../../../conversion/src/utils';
import DataPropertyHandler from './data-property-handler'; import DataPropertyHandler from './data-property-handler';
import * as hcUtils from './utils/hypercube-helper/hypercube-utils'; import * as hcUtils from './utils/hypercube-helper/hypercube-utils';
import getAutoSortLibraryDimension from './utils/field-helper/get-sorted-library-field'; import getAutoSortLibraryDimension from './utils/field-helper/get-sorted-library-field';
import getAutoSortFieldDimension from './utils/field-helper/get-sorted-field'; import getAutoSortDimension from './utils/field-helper/get-sorted-field';
import { initializeField, initializeId } from './utils/field-helper/field-utils'; import { initializeDim, initializeId } from './utils/field-helper/field-utils';
import addMainDimension from './utils/hypercube-helper/add-main-dimension'; import addMainDimension from './utils/hypercube-helper/add-main-dimension';
import addMainMeasure from './utils/hypercube-helper/add-main-measure'; import addMainMeasure from './utils/hypercube-helper/add-main-measure';
import removeMainDimension from './utils/hypercube-helper/remove-main-dimension'; import removeMainDimension from './utils/hypercube-helper/remove-main-dimension';
@@ -13,18 +12,55 @@ import removeMainMeasure from './utils/hypercube-helper/remove-main-measure';
import removeAlternativeDimension from './utils/hypercube-helper/remove-alternative-dimension'; import removeAlternativeDimension from './utils/hypercube-helper/remove-alternative-dimension';
import reinsertMainDimension from './utils/hypercube-helper/reinsert-main-dimension'; import reinsertMainDimension from './utils/hypercube-helper/reinsert-main-dimension';
import reinsertMainMeasure from './utils/hypercube-helper/reinsert-main-measure'; import reinsertMainMeasure from './utils/hypercube-helper/reinsert-main-measure';
// eslint-disable-next-line import/no-relative-packages
import arrayUtil from '../../../conversion/src/array-util';
/**
* HyperCubeHandler for managing hypercube data structure.
* @private
* @class HyperCubeHandler
* @description This class provides methods to handle hypercube properties, dimensions, and measures.
* @param {object} opts Parameters to add a hypercube handlers
* @param {qix.Doc} opts.app
* @param {object} opts.dimensionDefinition
* @param {object} opts.measureDefinition
* @param {object} opts.dimensionProperties
* @param {object} opts.measureProperties
* @param {object} opts.globalChangeListeners
* @param {object} opts.path
* @entry
* @export
* @example
* import { HyperCubeHandler } from '@nebula.js/stardust';
*
* class PivotHyperCubeHandler extends HyperCubeHandler {
*
* adjustPseudoDimOrder: (pseudoIdx?: number) => {
* const numberOfDims = this.getDimensions().length;
* const interColumnSortOrder = this.hcProperties.qInterColumnSortOrder;
*
* if (!interColumnSortOrder) {
* return;
* }
*
* interColumnSortOrder.splice(pseudoIdx || 0, 1);
* interColumnSortOrder.push(numberOfDims);
* interColumnSortOrder.splice((pseudoIdx || -1) + 1, 0, -1);
* };
* }
*/
class HyperCubeHandler extends DataPropertyHandler { class HyperCubeHandler extends DataPropertyHandler {
constructor(opts) { constructor(opts) {
super(opts); super(opts);
this.path = opts.path; this.path = opts.path;
} }
/**
* @private
* @param {object=} properties
* @returns early return if properties is falsy
*/
setProperties(properties) { setProperties(properties) {
if (!properties) { if (!properties) {
return {}; return;
} }
super.setProperties(properties); super.setProperties(properties);
@@ -32,7 +68,7 @@ class HyperCubeHandler extends DataPropertyHandler {
this.hcProperties = this.path ? utils.getValue(properties, `${this.path}.qHyperCubeDef`) : properties.qHyperCubeDef; this.hcProperties = this.path ? utils.getValue(properties, `${this.path}.qHyperCubeDef`) : properties.qHyperCubeDef;
if (!this.hcProperties) { if (!this.hcProperties) {
return {}; return;
} }
hcUtils.setDefaultProperties(this); hcUtils.setDefaultProperties(this);
@@ -41,32 +77,76 @@ class HyperCubeHandler extends DataPropertyHandler {
// Set auto-sort property (compatibility 0.85 -> 0.9), can probably be removed in 1.0 // Set auto-sort property (compatibility 0.85 -> 0.9), can probably be removed in 1.0
this.hcProperties.qDimensions = hcUtils.setFieldProperties(this.hcProperties.qDimensions); this.hcProperties.qDimensions = hcUtils.setFieldProperties(this.hcProperties.qDimensions);
this.hcProperties.qMeasures = hcUtils.setFieldProperties(this.hcProperties.qMeasures); this.hcProperties.qMeasures = hcUtils.setFieldProperties(this.hcProperties.qMeasures);
return {};
} }
// ---------------------------------- // ----------------------------------
// ----------- DIMENSIONS ----------- // ----------- DIMENSIONS -----------
// ---------------------------------- // ----------------------------------
/**
* @private
* @returns {qix.NxDimension[]} dimensions
* @description Returns the dimensions of the hypercube.
* @memberof HyperCubeHandler
* @example
* const dimensions = hyperCubeHandler.getDimensions();
*/
getDimensions() { getDimensions() {
return this.hcProperties ? this.hcProperties.qDimensions : []; return this.hcProperties ? this.hcProperties.qDimensions : [];
} }
/**
* @private
* @returns {qix.NxDimension[]} alternative dimensions
* @description Returns the alternative dimensions of the hypercube.
* @memberof HyperCubeHandler
* @example
* const alternativeDimensions = hyperCubeHandler.getAlternativeDimensions();
*/
getAlternativeDimensions() { getAlternativeDimensions() {
return this.hcProperties?.qLayoutExclude?.qHyperCubeDef?.qDimensions ?? []; return this.hcProperties?.qLayoutExclude?.qHyperCubeDef?.qDimensions ?? [];
} }
/**
* @private
* @param {string} cId
* @returns {qix.NxDimensionInfo} dimension layout
* @description Returns the dimension layout of the hypercube for a given cId.
* @memberof HyperCubeHandler
* @example
* const dimensionLayout = hyperCubeHandler.getDimensionLayout(cId);
*/
getDimensionLayout(cId) { getDimensionLayout(cId) {
return this.getDimensionLayouts().filter((item) => cId === item.cId)[0]; return this.getDimensionLayouts().filter((item) => cId === item.cId)[0];
} }
/**
* @private
* @returns {qix.NxDimensionInfo[]} dimension layouts
* @description Returns the dimension layouts of the hypercube.
* @memberof HyperCubeHandler
* @example
* const dimensionLayouts = hyperCubeHandler.getDimensionLayouts();
*/
getDimensionLayouts() { getDimensionLayouts() {
const hc = hcUtils.getHyperCube(this.layout, this.path); const hc = hcUtils.getHyperCube(this.layout, this.path);
return hc ? hc.qDimensionInfo : []; return hc ? hc.qDimensionInfo : [];
} }
/**
* @private
* @param {qix.NxDimension} dimension
* @param {boolean} alternative
* @param {number=} idx
* @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.
* @memberof HyperCubeHandler
* @example
* const dimension = hyperCubeHandler.addDimension(dimension, alternative, idx);
*/
addDimension(dimension, alternative, idx) { addDimension(dimension, alternative, idx) {
const dim = initializeField(dimension); const dim = initializeDim(dimension);
if (hcUtils.isDimensionAlternative(this, alternative)) { if (hcUtils.isDimensionAlternative(this, alternative)) {
return hcUtils.addAlternativeDimension(this, dim, idx); return hcUtils.addAlternativeDimension(this, dim, idx);
@@ -75,6 +155,18 @@ class HyperCubeHandler extends DataPropertyHandler {
return addMainDimension(this, dim, idx); return addMainDimension(this, dim, idx);
} }
/**
* @private
* @param {qix.NxDimension[]} dimensions
* @param {boolean} alternative
* @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.
* If the total number of dimensions exceeds the limit, it will stop adding dimensions.
* @memberof HyperCubeHandler
* @example
* const addedDimensions = await hyperCubeHandler.addDimensions(dimensions, alternative);
*/
async addDimensions(dimensions, alternative = false) { async addDimensions(dimensions, alternative = false) {
const existingDimensions = this.getDimensions(); const existingDimensions = this.getDimensions();
const initialLength = existingDimensions.length; const initialLength = existingDimensions.length;
@@ -87,7 +179,7 @@ class HyperCubeHandler extends DataPropertyHandler {
return addedDimensions; return addedDimensions;
} }
const dim = initializeField(dimension); const dim = initializeDim(dimension);
if (hcUtils.isDimensionAlternative(this, alternative)) { if (hcUtils.isDimensionAlternative(this, alternative)) {
const altDim = await hcUtils.addAlternativeDimension(this, dim); const altDim = await hcUtils.addAlternativeDimension(this, dim);
@@ -101,6 +193,16 @@ class HyperCubeHandler extends DataPropertyHandler {
return addedDimensions; return addedDimensions;
} }
/**
* @private
* @param {number} idx
* @param {boolean} alternative
* @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(idx, alternative) {
if (alternative) { if (alternative) {
removeAlternativeDimension(this, idx); removeAlternativeDimension(this, idx);
@@ -109,6 +211,18 @@ class HyperCubeHandler extends DataPropertyHandler {
removeMainDimension(this, idx); removeMainDimension(this, idx);
} }
/**
* @private
* @param {number[]} indexes
* @param {boolean} alternative
* @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.
* If the indexes are empty, it will return an empty array.
* @memberof HyperCubeHandler
* @example
* const deletedDimensions = await hyperCubeHandler.removeDimensions(indexes, alternative);
*/
async removeDimensions(indexes, alternative) { async removeDimensions(indexes, alternative) {
const altDimensions = this.getAlternativeDimensions(); const altDimensions = this.getAlternativeDimensions();
const dimensions = this.getDimensions(); const dimensions = this.getDimensions();
@@ -137,10 +251,31 @@ class HyperCubeHandler extends DataPropertyHandler {
return deletedDimensions; return deletedDimensions;
} }
/**
* 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.
* @returns {Promise<qix.NxDimension>} replaced dimension.
* @memberof HyperCubeHandler
* @example
* const replacedDimension = await hyperCubeHandler.replaceDimension(index, newDimension);
*/
replaceDimension(index, dimension) { replaceDimension(index, dimension) {
return this.autoSortDimension(dimension).then(() => hcUtils.replaceDimensionOrder(this, index, dimension)); 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.
* @returns {Promise<qix.NxDimension>} The reinserted dimension.
* @memberof HyperCubeHandler
* @example
* await hyperCubeHandler.reinsertDimension(dimension, alternative, idx);
*/
reinsertDimension(dimension, alternative, idx) { reinsertDimension(dimension, alternative, idx) {
const dim = initializeId(dimension); const dim = initializeId(dimension);
@@ -153,19 +288,26 @@ class HyperCubeHandler extends DataPropertyHandler {
return reinsertMainDimension(this, dim, idx); return reinsertMainDimension(this, dim, idx);
} }
/**
* 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.
* @returns {Promise<qix.NxDimension[]>} updated dimensions.
* @memberof HyperCubeHandler
* @example
* await hyperCubeHandler.moveDimension(fromIndex, toIndex);
*/
moveDimension(fromIndex, toIndex) { moveDimension(fromIndex, toIndex) {
const dimensions = this.getDimensions(); const dimensions = this.getDimensions();
const altDimensions = this.getAlternativeDimensions(); const altDimensions = this.getAlternativeDimensions();
if (fromIndex < dimensions.length && toIndex < dimensions.length) { if (fromIndex < dimensions.length && toIndex < dimensions.length) {
// Move within main dimensions
return Promise.resolve(arrayUtil.move(dimensions, fromIndex, toIndex)); return Promise.resolve(arrayUtil.move(dimensions, fromIndex, toIndex));
} }
if (fromIndex < dimensions.length && toIndex >= dimensions.length) { if (fromIndex < dimensions.length && toIndex >= dimensions.length) {
return hcUtils.moveDimensionFromMainToAlternative(fromIndex, toIndex, dimensions, altDimensions); return hcUtils.moveDimensionFromMainToAlternative(fromIndex, toIndex, dimensions, altDimensions);
} }
if (fromIndex >= dimensions.length && toIndex < dimensions.length) { if (fromIndex >= dimensions.length && toIndex < dimensions.length) {
return Promise.resolve(hcUtils.moveMeasureFromAlternativeToMain(fromIndex, toIndex, dimensions, altDimensions)); return Promise.resolve(hcUtils.moveMeasureFromAlternativeToMain(fromIndex, toIndex, dimensions, altDimensions));
} }
@@ -173,45 +315,112 @@ class HyperCubeHandler extends DataPropertyHandler {
return Promise.resolve(hcUtils.moveDimensionWithinAlternative(fromIndex, toIndex, dimensions, altDimensions)); return Promise.resolve(hcUtils.moveDimensionWithinAlternative(fromIndex, toIndex, dimensions, altDimensions));
} }
/**
* @private
* @param {qix.NxDimension} dimension
* @returns {qix.NxDimension} dimension with auto-sort properties
* @description Automatically sorts the dimension based on its properties.
* If the dimension has a qLibraryId, it will use the library dimension auto-sort.
* Otherwise, it will use the field dimension auto-sort.
* @memberof HyperCubeHandler
* @example
* const sortedDimension = hyperCubeHandler.autoSortDimension(dimension);
*/
autoSortDimension(dimension) { autoSortDimension(dimension) {
if (dimension.qLibraryId) { if (dimension.qLibraryId) {
return getAutoSortLibraryDimension(this, dimension); return getAutoSortLibraryDimension(this, dimension);
} }
return getAutoSortFieldDimension(this, dimension); return getAutoSortDimension(this, dimension);
} }
// ---------------------------------- // ----------------------------------
// ------------ MEASURES ------------ // ------------ MEASURES ------------
// ---------------------------------- // ----------------------------------
/**
* @private
* @returns {qix.NxMeasure[]} measures
* @description Returns the measures of the hypercube.
* @memberof HyperCubeHandler
* @example
* const measures = hyperCubeHandler.getMeasures();
*/
getMeasures() { getMeasures() {
return this.hcProperties ? this.hcProperties.qMeasures : []; return this.hcProperties ? this.hcProperties.qMeasures : [];
} }
/**
* @private
* @returns {qix.NxMeasure[]} alternative measures
* @description Returns the alternative measures of the hypercube.
* @memberof HyperCubeHandler
* @example
* const alternativeMeasures = hyperCubeHandler.getAlternativeMeasures();
*/
getAlternativeMeasures() { getAlternativeMeasures() {
return this.hcProperties?.qLayoutExclude?.qHyperCubeDef?.qMeasures ?? []; return this.hcProperties?.qLayoutExclude?.qHyperCubeDef?.qMeasures ?? [];
} }
/**
* @private
* @returns {qix.NxMeasureInfo[]} measure layouts
* @description Returns the measure layouts of the hypercube.
* @memberof HyperCubeHandler
* @example
* const measureLayouts = hyperCubeHandler.getMeasureLayouts();
*/
getMeasureLayouts() { getMeasureLayouts() {
const hc = hcUtils.getHyperCube(this.layout, this.path); const hc = hcUtils.getHyperCube(this.layout, this.path);
return hc ? hc.qMeasureInfo : []; return hc ? hc.qMeasureInfo : [];
} }
/**
* @private
* @param {string} cId
* @returns {object} measure layout
* @description Returns the measure layout of the hypercube for a given cId.
* @memberof HyperCubeHandler
* @example
* const measureLayout = hyperCubeHandler.getMeasureLayout(cId);
*/
getMeasureLayout(cId) { getMeasureLayout(cId) {
return this.getMeasureLayouts().filter((item) => cId === item.cId)[0]; return this.getMeasureLayouts().filter((item) => cId === item.cId)[0];
} }
/**
* @private
* @param {qix.NxMeasure} measure
* @param {boolean} alternative
* @param {number=} idx
* @returns {Promise<qix.NxMeasure>} 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.
* @memberof HyperCubeHandler
* @example
* const measure = hyperCubeHandler.addMeasure(measure, alternative, idx);
*/
addMeasure(measure, alternative, idx) { addMeasure(measure, alternative, idx) {
const measures = this.getAlternativeMeasure();
const meas = initializeId(measure); const meas = initializeId(measure);
if (hcUtils.isMeasureAlternative(this, measures, alternative)) { if (hcUtils.isMeasureAlternative(this, alternative)) {
return hcUtils.addAlternativeMeasure(this, meas, idx); const hcMeasures = this.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures;
return hcUtils.addAlternativeMeasure(meas, hcMeasures, idx);
} }
return addMainMeasure(this, meas, idx); return addMainMeasure(this, meas, idx);
} }
/**
* @private
* @param {qix.NxMeasure} measure
* @returns {Promise<qix.NxMeasure>} measure with auto-sort properties
* @description Automatically sorts the measure based on its properties.
* It sets the qSortByLoadOrder and qSortByNumeric properties.
* @memberof HyperCubeHandler
* @example
* const sortedMeasure = hyperCubeHandler.autoSortMeasure(measure);
*/
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
autoSortMeasure(measure) { autoSortMeasure(measure) {
const meas = measure; const meas = measure;
@@ -222,6 +431,18 @@ class HyperCubeHandler extends DataPropertyHandler {
return Promise.resolve(meas); return Promise.resolve(meas);
} }
/**
* @private
* @param {qix.NxMeasure[]} measures
* @param {boolean} alternative
* @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.
* If the total number of measures exceeds the limit, it will stop adding measures.
* @memberof HyperCubeHandler
* @example
* const addedMeasures = await hyperCubeHandler.addMeasures(measures, alternative);
*/
addMeasures(measures, alternative = false) { addMeasures(measures, alternative = false) {
const existingMeasures = this.getMeasures(); const existingMeasures = this.getMeasures();
const addedMeasures = []; const addedMeasures = [];
@@ -234,7 +455,7 @@ class HyperCubeHandler extends DataPropertyHandler {
const meas = initializeId(measure); const meas = initializeId(measure);
if (hcUtils.isMeasureAlternative(this, existingMeasures, alternative)) { if (hcUtils.isMeasureAlternative(this, alternative)) {
hcUtils.addAlternativeMeasure(this, meas); hcUtils.addAlternativeMeasure(this, meas);
addedMeasures.push(meas); addedMeasures.push(meas);
} else if (existingMeasures.length < this.maxMeasures()) { } else if (existingMeasures.length < this.maxMeasures()) {
@@ -246,6 +467,16 @@ class HyperCubeHandler extends DataPropertyHandler {
return addedMeasures; return addedMeasures;
} }
/**
* @private
* @param {number} idx
* @param {boolean} alternative
* @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(idx, alternative) {
if (alternative) { if (alternative) {
hcUtils.removeAltMeasureByIndex(this, idx); hcUtils.removeAltMeasureByIndex(this, idx);
@@ -253,13 +484,25 @@ class HyperCubeHandler extends DataPropertyHandler {
removeMainMeasure(this, idx); removeMainMeasure(this, idx);
} }
/**
* @private
* @param {number[]} indexes
* @param {boolean} alternative
* @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.
* If the indexes are empty, it will return an empty array.
* @memberof HyperCubeHandler
* @example
* const deletedMeasures = await hyperCubeHandler.removeMeasures(indexes, alternative);
*/
async removeMeasures(indexes, alternative) { async removeMeasures(indexes, alternative) {
const measures = this.getMeasures(); const measures = this.getMeasures();
const altMeasures = this.getAlternativeMeasures(); const altMeasures = this.getAlternativeMeasures();
if (indexes.length === 0) return [];
let deletedMeasures = []; let deletedMeasures = [];
if (indexes.length === 0) return deletedMeasures;
if (alternative && altMeasures.length > 0) { if (alternative && altMeasures.length > 0) {
// Keep the original deleted order // Keep the original deleted order
deletedMeasures = hcUtils.getDeletedFields(altMeasures, indexes); deletedMeasures = hcUtils.getDeletedFields(altMeasures, indexes);
@@ -278,21 +521,52 @@ class HyperCubeHandler extends DataPropertyHandler {
return deletedMeasures; return deletedMeasures;
} }
/**
* @private
* @param {number} index
* @param {qix.NxMeasure} measure
* @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(index, measure) {
return this.autoSortMeasure(measure).then(() => hcUtils.replaceMeasureToColumnOrder(this, index, measure)); return this.autoSortMeasure(measure).then(() => hcUtils.replaceMeasureToColumnOrder(this, index, measure));
} }
/**
* @private
* @param {qix.NxMeasure} measure
* @param {boolean} alternative
* @param {number} idx
* @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) { reinsertMeasure(measure, alternative, idx) {
const measures = this.getAlternativeMeasures();
const meas = initializeId(measure); const meas = initializeId(measure);
if (hcUtils.isMeasureAlternative(this, measures, alternative)) { if (hcUtils.isMeasureAlternative(this, alternative)) {
return hcUtils.addAlternativeMeasure(this, meas, idx); return hcUtils.addAlternativeMeasure(this, meas, idx);
} }
return reinsertMainMeasure(this, meas, idx); return reinsertMainMeasure(this, meas, idx);
} }
/**
* Moves a measure within the hypercube.
* @private
* @param {number} fromIndex
* @param {number} toIndex
* @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(fromIndex, toIndex) {
const measures = this.getMeasures(); const measures = this.getMeasures();
const altMeasures = this.getAlternativeMeasures(); const altMeasures = this.getAlternativeMeasures();
@@ -317,31 +591,74 @@ class HyperCubeHandler extends DataPropertyHandler {
// ------------ OTHERS---- ---------- // ------------ OTHERS---- ----------
// ---------------------------------- // ----------------------------------
setSorting(ar) { /**
if (ar && ar.length === this.hcProperties.qInterColumnSortOrder.length) { * Sets the sorting order for the hypercube.
this.hcProperties.qInterColumnSortOrder = ar; * @private
* @param {number[]} arr - The new sorting order.
* @memberof HyperCubeHandler
* @example
* const newSortingOrder = [2, 0, 1];
* hyperCubeHandler.setSorting(newSortingOrder);
*/
setSorting(arr) {
if (arr && arr.length === this.hcProperties.qInterColumnSortOrder.length) {
this.hcProperties.qInterColumnSortOrder = arr;
} }
} }
/**
* Gets the sorting order for the hypercube.
* @private
* @returns {number[]} The current sorting order.
* @memberof HyperCubeHandler
* @example
* const currentSortingOrder = hyperCubeHandler.getSorting();
*/
getSorting() { getSorting() {
return this.hcProperties.qInterColumnSortOrder; return this.hcProperties.qInterColumnSortOrder;
} }
/**
* Changes the sorting order for the hypercube.
* @private
* @param {number} fromIdx - The index to move from.
* @param {number} toIdx - The index to move to.
* @memberof HyperCubeHandler
* @example
* const newSortingOrder = hyperCubeHandler.changeSorting(0, 1);
*/
changeSorting(fromIdx, toIdx) { changeSorting(fromIdx, toIdx) {
utils.move(this.hcProperties.qInterColumnSortOrder, fromIdx, toIdx); utils.move(this.hcProperties.qInterColumnSortOrder, fromIdx, toIdx);
} }
/**
* Returns whether the hypercube is in straight mode or pivot mode.
* @private
* @returns {string} 'S' for straight mode, 'P' for pivot mode
* @memberof HyperCubeHandler
*/
IsHCInStraightMode() { IsHCInStraightMode() {
return this.hcProperties.qMode === 'S'; return this.hcProperties.qMode === 'S';
} }
/**
* @private
* @param {boolean} value
* @description This flag indicates whether we enabled HC modifier and have at least one script
* @memberof HyperCubeHandler
*/
setHCEnabled(value) { setHCEnabled(value) {
// this flag indicate whether we enabled HC modifier and have at least one script
if (this.hcProperties) { if (this.hcProperties) {
this.hcProperties.isHCEnabled = value; this.hcProperties.isHCEnabled = value;
} }
} }
/**
* Gets the dynamic scripts for the hypercube.
* @private
* @returns {Array} The dynamic scripts.
* @memberof HyperCubeHandler
*/
getDynamicScripts() { getDynamicScripts() {
return this.hcProperties?.qDynamicScript || []; return this.hcProperties?.qDynamicScript || [];
} }

View File

@@ -12,3 +12,16 @@ export const INITIAL_SORT_CRITERIAS = [
qSortByAscii: 1, qSortByAscii: 1,
}, },
]; ];
export const uid = () => {
const idGen = [
[10, 31],
[0, 31],
[0, 31],
[0, 31],
[0, 31],
[0, 31],
];
const toChar = ([min, max]) => min + ((Math.random() * (max - min)) | 0).toString(32);
return idGen.map(toChar).join('');
};

View File

@@ -1,4 +1,4 @@
import getAutoSortFieldDimension from '../get-sorted-field'; import getAutoSortDimension from '../get-sorted-field';
import findFieldInExpandedList from '../find-field-in-expandedList'; import findFieldInExpandedList from '../find-field-in-expandedList';
import { setAutoSort } from '../field-utils'; import { setAutoSort } from '../field-utils';
@@ -7,7 +7,7 @@ jest.mock('../field-utils', () => ({
setAutoSort: jest.fn(), setAutoSort: jest.fn(),
})); }));
describe('getAutoSortFieldDimension', () => { describe('getAutoSortDimension', () => {
let self; let self;
let dimension; let dimension;
@@ -32,7 +32,7 @@ describe('getAutoSortFieldDimension', () => {
const fieldList = [{ qName: 'field1' }]; const fieldList = [{ qName: 'field1' }];
self.app.getFieldList.mockResolvedValue(fieldList); self.app.getFieldList.mockResolvedValue(fieldList);
const result = await getAutoSortFieldDimension(self, dimension); const result = await getAutoSortDimension(self, dimension);
expect(self.app.getFieldList).toHaveBeenCalled(); expect(self.app.getFieldList).toHaveBeenCalled();
expect(findFieldInExpandedList).toHaveBeenCalledWith('field1', fieldList); expect(findFieldInExpandedList).toHaveBeenCalledWith('field1', fieldList);
@@ -45,7 +45,7 @@ describe('getAutoSortFieldDimension', () => {
self.app.getFieldList.mockResolvedValue(fieldList); self.app.getFieldList.mockResolvedValue(fieldList);
findFieldInExpandedList.mockReturnValue(field); findFieldInExpandedList.mockReturnValue(field);
await getAutoSortFieldDimension(self, dimension); await getAutoSortDimension(self, dimension);
expect(setAutoSort).toHaveBeenCalledWith([field], dimension, self); expect(setAutoSort).toHaveBeenCalledWith([field], dimension, self);
}); });
@@ -54,7 +54,7 @@ describe('getAutoSortFieldDimension', () => {
self.app.getFieldList.mockResolvedValue([]); self.app.getFieldList.mockResolvedValue([]);
findFieldInExpandedList.mockReturnValue(null); findFieldInExpandedList.mockReturnValue(null);
await getAutoSortFieldDimension(self, dimension); await getAutoSortDimension(self, dimension);
expect(setAutoSort).not.toHaveBeenCalled(); expect(setAutoSort).not.toHaveBeenCalled();
}); });
@@ -62,7 +62,7 @@ describe('getAutoSortFieldDimension', () => {
test('should handle empty field list', async () => { test('should handle empty field list', async () => {
self.app.getFieldList.mockResolvedValue([]); self.app.getFieldList.mockResolvedValue([]);
const result = await getAutoSortFieldDimension(self, dimension); const result = await getAutoSortDimension(self, dimension);
expect(result).toBe(dimension); expect(result).toBe(dimension);
expect(setAutoSort).not.toHaveBeenCalled(); expect(setAutoSort).not.toHaveBeenCalled();
@@ -72,7 +72,7 @@ describe('getAutoSortFieldDimension', () => {
dimension.qDef.qFieldDefs = undefined; dimension.qDef.qFieldDefs = undefined;
self.app.getFieldList.mockResolvedValue([]); self.app.getFieldList.mockResolvedValue([]);
const result = await getAutoSortFieldDimension(self, dimension); const result = await getAutoSortDimension(self, dimension);
expect(result).toBe(dimension); expect(result).toBe(dimension);
expect(setAutoSort).not.toHaveBeenCalled(); expect(setAutoSort).not.toHaveBeenCalled();

View File

@@ -1,6 +1,4 @@
// eslint-disable-next-line import/no-relative-packages import { AUTOCALENDAR_NAME, uid } from '../constants';
import uid from '../../../../../nucleus/src/object/uid';
import { AUTOCALENDAR_NAME } from '../constants';
export const getField = (expression) => { export const getField = (expression) => {
let exp = expression; let exp = expression;
@@ -33,7 +31,7 @@ export const initializeId = (field) => ({
}, },
}); });
export const initializeField = (field) => ({ export const initializeDim = (field) => ({
...initializeId(field), ...initializeId(field),
qOtherTotalSpec: field.qOtherTotalSpec ?? {}, qOtherTotalSpec: field.qOtherTotalSpec ?? {},
}); });
@@ -62,6 +60,13 @@ export const setAutoSort = (fields, dimension, self) => {
}); });
}; };
export const isDateField = (field) =>
field?.qDerivedFieldData && (field?.qTags?.indexOf('$date') > -1 || field?.qTags?.indexOf('$timestamp') > -1);
export const isGeoField = (field) => field.qTags.indexOf('$geoname') > -1;
export const trimAutoCalendarName = (fieldName) => (fieldName ? fieldName.split(AUTOCALENDAR_NAME).join('') : '');
export const useMasterNumberFormat = (formatting) => { export const useMasterNumberFormat = (formatting) => {
const format = formatting; const format = formatting;
format.quarantine = { format.quarantine = {
@@ -71,10 +76,3 @@ export const useMasterNumberFormat = (formatting) => {
format.qNumFormat = null; format.qNumFormat = null;
format.isCustomFormatted = undefined; format.isCustomFormatted = undefined;
}; };
export const isDateField = (field) =>
field?.qDerivedFieldData && (field?.qTags?.indexOf('$date') > -1 || field?.qTags?.indexOf('$timestamp') > -1);
export const isGeoField = (field) => field.qTags.indexOf('$geoname') > -1;
export const trimAutoCalendarName = (fieldName) => (fieldName ? fieldName.split(AUTOCALENDAR_NAME).join('') : '');

View File

@@ -1,7 +1,7 @@
import findFieldInExpandedList from './find-field-in-expandedList'; import findFieldInExpandedList from './find-field-in-expandedList';
import { setAutoSort } from './field-utils'; import { setAutoSort } from './field-utils';
function getAutoSortFieldDimension(self, dimension) { function getAutoSortDimension(self, dimension) {
return self.app.getFieldList().then((fieldList) => { return self.app.getFieldList().then((fieldList) => {
const field = dimension?.qDef?.qFieldDefs && findFieldInExpandedList(dimension.qDef.qFieldDefs[0], fieldList); const field = dimension?.qDef?.qFieldDefs && findFieldInExpandedList(dimension.qDef.qFieldDefs[0], fieldList);
if (field) { if (field) {
@@ -11,4 +11,4 @@ function getAutoSortFieldDimension(self, dimension) {
}); });
} }
export default getAutoSortFieldDimension; export default getAutoSortDimension;

View File

@@ -44,7 +44,7 @@ describe('addMainMeasure', () => {
const result = await addMainMeasure(self, newMeasure, index); const result = await addMainMeasure(self, newMeasure, index);
expect(result).toEqual(newMeasure); expect(result).toBeUndefined();
expect(self.autoSortMeasure).not.toHaveBeenCalledWith(newMeasure); expect(self.autoSortMeasure).not.toHaveBeenCalledWith(newMeasure);
expect(self.hcProperties.qInterColumnSortOrder).toEqual([1, 0, 2]); expect(self.hcProperties.qInterColumnSortOrder).toEqual([1, 0, 2]);
}); });

View File

@@ -1,8 +1,9 @@
// eslint-disable-next-line import/no-relative-packages import { uid } from '../../constants';
import uid from '../../../../../../nucleus/src/object/uid';
import * as hcUtils from '../hypercube-utils'; import * as hcUtils from '../hypercube-utils';
jest.mock('../../../../../../nucleus/src/object/uid', () => jest.fn()); jest.mock('../../constants', () => ({
uid: jest.fn(),
}));
describe('replaceDimensionToColumnOrder', () => { describe('replaceDimensionToColumnOrder', () => {
let self; let self;

View File

@@ -9,11 +9,12 @@ function addMainMeasure(self, measure, index) {
return self.autoSortMeasure(measure).then(() => { return self.autoSortMeasure(measure).then(() => {
addMeasureToColumnSortOrder(self, measures); addMeasureToColumnSortOrder(self, measures);
return addMeasureToColumnOrder(self, measure).then(() => measure); addMeasureToColumnOrder(self, measure).then(() => measure);
return measure;
}); });
} }
return measure; return Promise.resolve();
} }
export default addMainMeasure; export default addMainMeasure;

View File

@@ -1,10 +1,5 @@
// eslint-disable-next-line import/no-relative-packages import { arrayUtil, utils } from '@nebula.js/conversion';
import getValue from '../../../../../conversion/src/utils'; import { TOTAL_MAX, uid } from '../constants';
// eslint-disable-next-line import/no-relative-packages
import arrayUtil from '../../../../../conversion/src/array-util';
import { TOTAL_MAX } from '../constants';
// eslint-disable-next-line import/no-relative-packages
import uid from '../../../../../nucleus/src/object/uid';
export const notSupportedError = new Error('Not supported in this object, need to implement in subclass.'); export const notSupportedError = new Error('Not supported in this object, need to implement in subclass.');
@@ -33,7 +28,7 @@ export const getHyperCube = (layout, path) => {
if (!layout) { if (!layout) {
return undefined; return undefined;
} }
return path && getValue(layout, path) ? getValue(layout, path).qHyperCube : layout.qHyperCube; return path && utils.getValue(layout, path) ? utils.getValue(layout, path).qHyperCube : layout.qHyperCube;
}; };
export function setDefaultProperties(self) { export function setDefaultProperties(self) {
@@ -241,7 +236,8 @@ export function isTotalMeasureExceeded(self, measures) {
return altMeasures.length + measures.length >= TOTAL_MAX.MEASURES; return altMeasures.length + measures.length >= TOTAL_MAX.MEASURES;
} }
export function isMeasureAlternative(self, measures, alternative) { export function isMeasureAlternative(self, alternative) {
const measures = self.getMeasures();
return alternative || (self.maxMeasures() <= measures.length && measures.length < TOTAL_MAX.MEASURES); return alternative || (self.maxMeasures() <= measures.length && measures.length < TOTAL_MAX.MEASURES);
} }

View File

@@ -1,6 +1,8 @@
import generator from './generator'; import generator from './generator';
import JSONPatch from './json-patch'; import JSONPatch from './json-patch';
export { default as HyperCubeHandler } from './handler/hypercube-handler';
export { default as DataPropertyHandler } from './handler/data-property-handler';
export { generator, JSONPatch }; export { generator, JSONPatch };
// core hooks // core hooks

View File

@@ -4473,6 +4473,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@nebula.js/supernova@workspace:apis/supernova" resolution: "@nebula.js/supernova@workspace:apis/supernova"
dependencies: dependencies:
"@nebula.js/conversion": "npm:^6.0.0-alpha.2"
extend: "npm:3.0.2" extend: "npm:3.0.2"
node-event-emitter: "npm:0.0.1" node-event-emitter: "npm:0.0.1"
languageName: unknown languageName: unknown