Repository navigation
GridCore - Fix eslint and add types to leaf helpers (m_aggregate_calculator, m_utils, m_accessibility, m_export) #35464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
b0283c9
5764edc
426a41d
05bba47
0f0c79b
626378f
30e0c08
b50ddf0
837f7e3
64d0488
b26f007
51acc47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,46 +5,52 @@ import { isFunction } from '@js/core/utils/type'; | |
|
|
||
| import type { Aggregate } from './summary/types'; | ||
|
|
||
| function depthFirstSearch(i, depth, root, callback) { | ||
| // NOTE: only the deepest groups hold rows in items; the groups above hold groups. | ||
| // Rows can be primitives (e.g. a store of numbers with the 'this' selector) | ||
| interface AggregateNode { | ||
| items: unknown[]; | ||
| aggregates?: unknown[]; | ||
| } | ||
|
|
||
| interface Aggregator { | ||
| seed?: number | unknown[] | ((groupIndex?: number) => unknown); | ||
| step: (accumulator: unknown, value: unknown) => unknown; | ||
| finalize?: (accumulator: unknown) => unknown; | ||
| } | ||
|
|
||
| interface NormalizedAggregate { | ||
| selector: (data: unknown) => unknown; | ||
| aggregator: Aggregator; | ||
| skipEmptyValues: boolean | undefined; | ||
| } | ||
|
|
||
| const depthFirstSearch = ( | ||
| i: number, | ||
| depth: number, | ||
| root: AggregateNode, | ||
| callback: (node: AggregateNode) => void, | ||
| ): void => { | ||
| let j = 0; | ||
| if (i < depth) { | ||
| for (; j < root.items.length; j++) { | ||
| depthFirstSearch(i + 1, depth, root.items[j], callback); | ||
| for (; j < root.items.length; j += 1) { | ||
| // NOTE: above the given depth, items are groups | ||
| depthFirstSearch(i + 1, depth, root.items[j] as AggregateNode, callback); | ||
| } | ||
| } | ||
|
|
||
| if (i === depth) { | ||
| callback(root); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| // NOTE: https://github.com/jquery/jquery/blame/master/src/core.js#L392 | ||
| function map(array, callback) { | ||
| let i; | ||
| const isEmpty = (x: unknown): boolean => Number.isNaN(x) || (x === '') || (x === null) || (x === undefined); | ||
|
|
||
| if ('map' in array) { | ||
| return array.map(callback); | ||
| } | ||
| const isCount = (aggregator: Aggregator): boolean => aggregator === aggregators.count; | ||
|
|
||
| const result = new Array(array.length); | ||
| // eslint-disable-next-line guard-for-in | ||
| for (i in array) { | ||
| result[i] = callback(array[i], i); | ||
| } | ||
| const normalizeAggregate = (aggregate: Aggregate): NormalizedAggregate => { | ||
| // @ts-expect-error badly typed compileGetter | ||
| const selector = compileGetter(aggregate.selector) as (data: unknown) => unknown; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| function isEmpty(x) { | ||
| return (x !== x) || (x === '') || (x === null) || (x === undefined); | ||
| } | ||
|
|
||
| function isCount(aggregator) { | ||
| return aggregator === aggregators.count; | ||
| } | ||
|
|
||
| function normalizeAggregate(aggregate) { | ||
| const selector = compileGetter(aggregate.selector); | ||
| const skipEmptyValues = 'skipEmptyValues' in aggregate | ||
| ? aggregate.skipEmptyValues | ||
| : true; | ||
|
|
@@ -59,32 +65,33 @@ function normalizeAggregate(aggregate) { | |
|
|
||
| return { | ||
| selector, | ||
| // @ts-expect-error aggregators[name] is untyped; local aggregates always have one | ||
| aggregator, | ||
| skipEmptyValues, | ||
| }; | ||
| } | ||
| }; | ||
|
|
||
| export default class AggregateCalculator { | ||
| private readonly _data: any; | ||
| private readonly _data: unknown[]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we get better type from usage and docs?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By checking usage of _data in source code its type should be: However, I have chosen to type
I'm OK with both approaches. What do you think?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we already typed data in |
||
|
|
||
| private readonly _groupLevel: number; | ||
|
|
||
| private readonly _totalAggregates: Aggregate[]; | ||
| private readonly _totalAggregates: NormalizedAggregate[]; | ||
|
|
||
| private readonly _groupAggregates: Aggregate[]; | ||
| private readonly _groupAggregates: NormalizedAggregate[]; | ||
|
|
||
| private _totals: unknown[]; | ||
|
|
||
| constructor(options: { | ||
| data: any; | ||
| data: unknown[]; | ||
| groupLevel: number; | ||
| totalAggregates: Aggregate[]; | ||
| groupAggregates: Aggregate[]; | ||
| }) { | ||
| this._data = options.data; | ||
| this._groupLevel = options.groupLevel; | ||
| this._totalAggregates = map(options.totalAggregates, normalizeAggregate); | ||
| this._groupAggregates = map(options.groupAggregates, normalizeAggregate); | ||
| this._totalAggregates = options.totalAggregates.map(normalizeAggregate); | ||
| this._groupAggregates = options.groupAggregates.map(normalizeAggregate); | ||
| this._totals = []; | ||
| } | ||
|
|
||
|
|
@@ -102,31 +109,37 @@ export default class AggregateCalculator { | |
| return this._totals; | ||
| } | ||
|
|
||
| private _aggregate(aggregates, data, container) { | ||
| private _aggregate( | ||
| aggregates: NormalizedAggregate[], | ||
| data: AggregateNode, | ||
| container: unknown[], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we get better type from usage and docs?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
| ): void { | ||
| const length = data.items ? data.items.length : 0; | ||
|
|
||
| for (let i = 0; i < aggregates.length; i++) { | ||
| for (let i = 0; i < aggregates.length; i += 1) { | ||
| if (isCount(aggregates[i].aggregator)) { | ||
| container[i] = (container[i] || 0) + length; | ||
| container[i] = (container[i] as number | undefined ?? 0) + length; | ||
| // eslint-disable-next-line no-continue | ||
| continue; | ||
| } | ||
|
|
||
| for (let j = 0; j < length; j++) { | ||
| for (let j = 0; j < length; j += 1) { | ||
| this._accumulate(i, aggregates[i], container, data.items[j]); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private _calculateTotals(level, data) { | ||
| private _calculateTotals(level: number, root: AggregateNode): void { | ||
| if (level === 0) { | ||
| this._totals = this._seed(this._totalAggregates); | ||
| } | ||
|
|
||
| if (level === this._groupLevel) { | ||
| this._aggregate(this._totalAggregates, data, this._totals); | ||
| this._aggregate(this._totalAggregates, root, this._totals); | ||
| } else { | ||
| for (let i = 0; i < data.items.length; i++) { | ||
| this._calculateTotals(level + 1, data.items[i]); | ||
| for (const item of root.items) { | ||
| // NOTE: above the group level, items are groups | ||
| this._calculateTotals(level + 1, item as AggregateNode); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -135,45 +148,54 @@ export default class AggregateCalculator { | |
| } | ||
| } | ||
|
|
||
| private _calculateGroups(root) { | ||
| private _calculateGroups(root: AggregateNode): void { | ||
| const maxLevel = this._groupLevel; | ||
| let currentLevel = maxLevel + 1; | ||
|
|
||
| const seedFn = this._seed.bind(this, this._groupAggregates); | ||
| const stepFn = this._aggregate.bind(this, this._groupAggregates); | ||
| const finalizeFn = this._finalize.bind(this, this._groupAggregates); | ||
|
|
||
| function aggregator(node) { | ||
| node.aggregates = seedFn(currentLevel - 1); | ||
| const aggregator = (node: AggregateNode): void => { | ||
| const aggregates = seedFn(currentLevel - 1); | ||
| node.aggregates = aggregates; | ||
|
|
||
| if (currentLevel === maxLevel) { | ||
| stepFn(node, node.aggregates); | ||
| stepFn(node, aggregates); | ||
| } else { | ||
| depthFirstSearch(currentLevel, maxLevel, node, (innerNode) => { | ||
| stepFn(innerNode, node.aggregates); | ||
| stepFn(innerNode, aggregates); | ||
| }); | ||
| } | ||
|
|
||
| node.aggregates = finalizeFn(node.aggregates); | ||
| } | ||
| node.aggregates = finalizeFn(aggregates); | ||
| }; | ||
|
|
||
| while (--currentLevel > 0) { | ||
| currentLevel -= 1; | ||
| while (currentLevel > 0) { | ||
| depthFirstSearch(0, currentLevel, root, aggregator); | ||
| currentLevel -= 1; | ||
| } | ||
| } | ||
|
|
||
| private _seed(aggregates, groupIndex?) { | ||
| return map(aggregates, (aggregate) => { | ||
| private _seed(aggregates: NormalizedAggregate[], groupIndex?: number): unknown[] { | ||
| return aggregates.map((aggregate) => { | ||
| const { aggregator } = aggregate; | ||
| const seed = 'seed' in aggregator | ||
| ? isFunction(aggregator.seed) ? aggregator.seed(groupIndex) : aggregator.seed | ||
| : NaN; | ||
|
|
||
| return seed; | ||
| if (!('seed' in aggregator)) { | ||
| return NaN; | ||
| } | ||
|
|
||
| return isFunction(aggregator.seed) ? aggregator.seed(groupIndex) : aggregator.seed; | ||
| }); | ||
| } | ||
|
|
||
| private _accumulate(aggregateIndex, aggregate, results, item) { | ||
| private _accumulate( | ||
| aggregateIndex: number, | ||
| aggregate: NormalizedAggregate, | ||
| results: unknown[], | ||
| item: unknown, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we get better types from usage and docs?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ): void { | ||
| const value = aggregate.selector(item); | ||
| const { aggregator } = aggregate; | ||
| const { skipEmptyValues } = aggregate; | ||
|
|
@@ -182,15 +204,15 @@ export default class AggregateCalculator { | |
| return; | ||
| } | ||
|
|
||
| if (results[aggregateIndex] !== results[aggregateIndex]) { | ||
| if (Number.isNaN(results[aggregateIndex])) { | ||
| results[aggregateIndex] = value; | ||
| } else { | ||
| results[aggregateIndex] = aggregator.step(results[aggregateIndex], value); | ||
| } | ||
| } | ||
|
|
||
| private _finalize(aggregates, results) { | ||
| return map(aggregates, (aggregate, index) => { | ||
| private _finalize(aggregates: NormalizedAggregate[], results: unknown[]): unknown[] { | ||
| return aggregates.map((aggregate, index) => { | ||
| const fin = aggregate.aggregator.finalize; | ||
| return fin | ||
| ? fin(results[index]) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,20 @@ | ||
| import { normalizeSortingInfo } from '@js/common/data/utils'; | ||
| import { isDefined } from '@ts/core/utils/m_type'; | ||
| import type { StoreLoadOptions } from '@ts/data/data_source/types'; | ||
| import type { DataFilter } from '@ts/grids/grid_core/filter/types'; | ||
| import { combineFilters } from '@ts/grids/grid_core/filter/utils'; | ||
|
|
||
| import type { Column } from './types'; | ||
|
|
||
| export function createGroupFilter(path, storeLoadOptions) { | ||
| export function createGroupFilter( | ||
| path: unknown[], | ||
| storeLoadOptions: Pick<StoreLoadOptions, 'group' | 'filter'>, | ||
| ): DataFilter { | ||
| const groups = normalizeSortingInfo(storeLoadOptions.group); | ||
|
|
||
| const filter: any = []; | ||
| const filter: unknown[] = []; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we use here some type from our existing filter types or get one from usage and docs?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing filter types don't fit here. The group selector can be a function (e.g. |
||
|
|
||
| for (let i = 0; i < path.length; i++) { | ||
| for (let i = 0; i < path.length; i += 1) { | ||
| filter.push([groups[i].selector, '=', path[i]]); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,46 @@ | ||
| import type { dxElementWrapper } from '@js/core/renderer'; | ||
| import type { DxEvent } from '@js/events'; | ||
| import * as accessibility from '@js/ui/shared/accessibility'; | ||
|
|
||
| export const registerKeyboardAction = function (viewName, instance, $element, selector, action) { | ||
| import type { View } from './m_modules'; | ||
|
Copilot marked this conversation as resolved.
Outdated
|
||
|
|
||
| interface KeyDownArgs { | ||
| event: unknown; | ||
|
Tucchhaa marked this conversation as resolved.
Outdated
|
||
| handled: boolean; | ||
| } | ||
|
|
||
| export const registerKeyboardAction = ( | ||
| viewName: string, | ||
| instance: View, | ||
| $element: dxElementWrapper, | ||
| selector: string | undefined, | ||
| action: (args: { event: DxEvent }) => void, | ||
| ): void => { | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| let executeKeyDown = (args) => {}; | ||
| let executeKeyDown = (args: KeyDownArgs): void => {}; | ||
|
|
||
| const keyboardController = instance.getController('keyboardNavigation'); | ||
| if (instance.option('useLegacyKeyboardNavigation') || (keyboardController && !keyboardController.isKeyboardEnabled())) { | ||
| if ( | ||
| instance.option('useLegacyKeyboardNavigation') | ||
| || (keyboardController && !keyboardController.isKeyboardEnabled()) | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| if (viewName === 'filterPanel') { | ||
| executeKeyDown = (args) => { | ||
| executeKeyDown = (args: KeyDownArgs): void => { | ||
| instance.executeAction('onKeyDown', args); | ||
| }; | ||
|
|
||
| instance.createAction('onKeyDown'); | ||
| } | ||
|
|
||
| accessibility.registerKeyboardAction(viewName, instance, $element, selector, action, executeKeyDown); | ||
| accessibility.registerKeyboardAction( | ||
| viewName, | ||
| instance, | ||
| $element, | ||
| selector, | ||
| action, | ||
| executeKeyDown, | ||
| ); | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.