UNPKG

@progress/kendo-angular-grid

Version:

Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.

202 lines (201 loc) 6.02 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { InjectionToken } from '@angular/core'; import { isDocumentAvailable } from '@progress/kendo-angular-common'; import { merge, of } from 'rxjs'; export { isChanged, anyChanged, hasObservers } from '@progress/kendo-angular-common'; const EMPTY_REGEX = /^\s*$/; /** * @hidden */ export const isPresent = (value) => value !== null && value !== undefined; /** * @hidden */ export const isBlank = (value) => value === null || value === undefined; /** * @hidden */ export const isArray = (value) => Array.isArray(value); /** * @hidden */ export const isTruthy = (value) => !!value; /** * @hidden */ export const isNullOrEmptyString = (value) => isBlank(value) || EMPTY_REGEX.test(value); /** * @hidden */ export const observe = (list) => merge(of(list), list.changes); /** * @hidden */ export const isUniversal = () => typeof document === 'undefined'; /** * @hidden */ export const isString = (value) => typeof value === 'string'; /** * @hidden */ export const isNumber = (value) => typeof value === "number" && !isNaN(value); /** * @hidden */ export const extractFormat = (format) => { if (isString(format) && !isNullOrEmptyString(format) && format.startsWith('{0:')) { return format.slice(3, format.length - 1); } return format; }; /** * @hidden */ export const not = (fn) => (...args) => !fn(...args); /** * @hidden */ export const or = (...conditions) => (value) => conditions.reduce((acc, x) => acc || x(value), false); /** * @hidden */ export const and = (...conditions) => (value) => conditions.reduce((acc, x) => acc && x(value), true); /** * @hidden */ export const Skip = new InjectionToken("Skip"); /** * @hidden */ export const createPromise = () => { let resolveFn, rejectFn; const promise = new Promise((resolve, reject) => { resolveFn = (data) => { resolve(data); return promise; }; rejectFn = (data) => { reject(data); return promise; }; }); promise.resolve = resolveFn; promise.reject = rejectFn; return promise; }; /** @hidden */ export const iterator = getIterator(); // TODO: Move to kendo-common function getIterator() { if (typeof Symbol === 'function' && Symbol.iterator) { return Symbol.iterator; } const keys = Object.getOwnPropertyNames(Map.prototype); const proto = Map.prototype; for (let i = 0; i < keys.length; ++i) { const key = keys[i]; if (key !== 'entries' && key !== 'size' && proto[key] === proto.entries) { return key; } } } const FRAME_DURATION = 1000 / 60; const wnd = typeof window !== 'undefined' ? window : {}; /** @hidden */ export const requestAnimationFrame = wnd.requestAnimationFrame || wnd.msRequestAnimationFrame || (callback => setTimeout(callback, FRAME_DURATION)); /** @hidden */ export const cancelAnimationFrame = wnd.cancelAnimationFrame || wnd.msCancelRequestAnimationFrame || clearTimeout; /** * @hidden */ export const detectIE = () => { if (!isDocumentAvailable()) { return; } const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); const trident = ua.indexOf('Trident/'); return msie > 0 || trident > 0; }; /** * @hidden */ export const nodesToArray = (nodes) => [].slice.call(nodes); /** * @hidden */ export const replaceMessagePlaceholder = (message, name, value) => (message ?? '').replace(new RegExp(`{\\s*${name}\\s*}`, 'g'), value); /** * @hidden */ export const recursiveFlatMap = (item) => isGroupResult(item) ? item.items.flatMap(recursiveFlatMap) : [{ ...item }]; /** * @hidden */ export const isGroupResult = (obj) => { return 'aggregates' in obj && 'items' in obj && 'field' in obj && 'value' in obj; }; /** * @hidden */ export const roundDown = (value) => Math.floor(value * 100) / 100; /** * @hidden */ export const defaultCellRowSpan = (row, column, data) => { const field = column.field; let rowspan = 1; const rowIndex = row.index; if (!data[0].items) { //If no groups are present. rowspan = findRowSpan(data, rowIndex, field); } else { // If groups are present. const group = row.dataItem.group; if (field === group.data.field) { // Set the rowspan to the number of data items in the same group if the column containing the current row matches the grouping field. rowspan = group.data.items.length; } else { //The index of the current row in the group. const rowIndex = row.dataItem.group.data.items.indexOf(row.dataItem.data); const groupItems = row.dataItem.group.data.items; rowspan = findRowSpan(groupItems, rowIndex, field); } } return rowspan; }; /** * @hidden * Returns the rowspan number based on the provided data array, index of the item, and field that is checked. */ const findRowSpan = (data, index, field) => { let rowspan = 1; if (typeof data[index][field]?.getTime === 'function') { while (data[index][field].getTime() === data[index + 1]?.[field].getTime()) { rowspan++; index++; } } else { while (data[index][field] === data[index + 1]?.[field]) { rowspan++; index++; } } return rowspan; }; /** * @hidden * Determines whether selection of multiple ranges is enabled in the selectable settings. */ export const isMultipleRangesEnabled = (selectableSettings) => { return selectableSettings && typeof selectableSettings === 'object' && selectableSettings.selectable.multipleRanges; };