UNPKG

igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,391 lines (1,376 loc) 319 kB
import * as i0 from '@angular/core'; import { Injectable, inject, HostListener, Input, Directive, InjectionToken, isDevMode, PLATFORM_ID, NgZone, DOCUMENT, EventEmitter, Output, Component, TemplateRef, ElementRef, ApplicationRef, ViewContainerRef, createComponent } from '@angular/core'; import { isPlatformBrowser, formatDate as formatDate$1, CurrencyPipe, FormatWidth, getLocaleDateFormat, formatPercent, formatNumber, getLocaleCurrencyCode } from '@angular/common'; import { mergeWith } from 'lodash-es'; import { Observable, NEVER, Subject, fromEvent, BehaviorSubject } from 'rxjs'; import { ɵgetDOM as _getDOM } from '@angular/platform-browser'; import { AnimationBuilder } from '@angular/animations'; import { takeUntil, filter } from 'rxjs/operators'; import { scaleOutVerTop, scaleInVerTop, AnimationUtil, fadeOut, fadeIn, slideOutTop, slideInTop, slideOutBottom, slideInBottom, scaleOutHorRight, scaleInHorRight, scaleOutHorLeft, scaleInHorLeft, scaleOutVerBottom, scaleInVerBottom } from 'igniteui-angular/animations'; /** * Common service to be injected between components where those implementing common * ToggleView interface can register and toggle directives can call their methods. * TODO: Track currently active? Events? */ class IgxNavigationService { constructor() { this.navs = {}; } add(id, navItem) { this.navs[id] = navItem; } remove(id) { delete this.navs[id]; } get(id) { if (id) { return this.navs[id]; } } toggle(id, ...args) { if (this.navs[id]) { return this.navs[id].toggle(...args); } } open(id, ...args) { if (this.navs[id]) { return this.navs[id].open(...args); } } close(id, ...args) { if (this.navs[id]) { return this.navs[id].close(...args); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); /** * Directive that can toggle targets through provided NavigationService. * * Usage: * ``` * <button type="button" igxNavToggle="ID">Toggle</button> * ``` * Where the `ID` matches the ID of compatible `IToggleView` component. */ class IgxNavigationToggleDirective { constructor() { const nav = inject(IgxNavigationService); this.state = nav; } toggleNavigationDrawer() { this.state.toggle(this.target, true); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationToggleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxNavigationToggleDirective, isStandalone: true, selector: "[igxNavToggle]", inputs: { target: ["igxNavToggle", "target"] }, host: { listeners: { "click": "toggleNavigationDrawer()" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationToggleDirective, decorators: [{ type: Directive, args: [{ selector: '[igxNavToggle]', standalone: true }] }], ctorParameters: () => [], propDecorators: { target: [{ type: Input, args: ['igxNavToggle'] }], toggleNavigationDrawer: [{ type: HostListener, args: ['click'] }] } }); /** * Directive that can close targets through provided NavigationService. * * Usage: * ``` * <button type="button" igxNavClose="ID">Close</button> * ``` * Where the `ID` matches the ID of compatible `IToggleView` component. */ class IgxNavigationCloseDirective { constructor() { const nav = inject(IgxNavigationService); this.state = nav; } closeNavigationDrawer() { this.state.close(this.target, true); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationCloseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxNavigationCloseDirective, isStandalone: true, selector: "[igxNavClose]", inputs: { target: ["igxNavClose", "target"] }, host: { listeners: { "click": "closeNavigationDrawer()" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxNavigationCloseDirective, decorators: [{ type: Directive, args: [{ selector: '[igxNavClose]', standalone: true }] }], ctorParameters: () => [], propDecorators: { target: [{ type: Input, args: ['igxNavClose'] }], closeNavigationDrawer: [{ type: HostListener, args: ['click'] }] } }); var DateRangeType; (function (DateRangeType) { DateRangeType[DateRangeType["After"] = 0] = "After"; DateRangeType[DateRangeType["Before"] = 1] = "Before"; DateRangeType[DateRangeType["Between"] = 2] = "Between"; DateRangeType[DateRangeType["Specific"] = 3] = "Specific"; DateRangeType[DateRangeType["Weekdays"] = 4] = "Weekdays"; DateRangeType[DateRangeType["Weekends"] = 5] = "Weekends"; })(DateRangeType || (DateRangeType = {})); /** * @hidden @internal * * Enumeration representing the possible predefined size options of the grid. * - Small: This is the smallest size with 32px row height. Left and Right paddings are 12px. Minimal column width is 56px. * - Medium: This is the middle size with 40px row height. Left and Right paddings are 16px. Minimal column width is 64px. * - Large: this is the default Grid size with the lowest intense and row height equal to 50px. Left and Right paddings are 24px. Minimal column width is 80px. */ const Size = { Small: '1', Medium: '2', Large: '3' }; /** * Enumeration representing the days of the week. */ var WEEKDAYS; (function (WEEKDAYS) { WEEKDAYS[WEEKDAYS["SUNDAY"] = 0] = "SUNDAY"; WEEKDAYS[WEEKDAYS["MONDAY"] = 1] = "MONDAY"; WEEKDAYS[WEEKDAYS["TUESDAY"] = 2] = "TUESDAY"; WEEKDAYS[WEEKDAYS["WEDNESDAY"] = 3] = "WEDNESDAY"; WEEKDAYS[WEEKDAYS["THURSDAY"] = 4] = "THURSDAY"; WEEKDAYS[WEEKDAYS["FRIDAY"] = 5] = "FRIDAY"; WEEKDAYS[WEEKDAYS["SATURDAY"] = 6] = "SATURDAY"; })(WEEKDAYS || (WEEKDAYS = {})); /* Copyright (c) 2014-2020 Denis Pushkarev * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE */ // Note: Originally copied from core-js-pure package and modified. (https://github.com/zloirock/core-js) const queue = {}; let counter = 0; let eventListenerAdded = false; const run = (id) => { if (queue.hasOwnProperty(id)) { const fn = queue[id]; delete queue[id]; fn(); } }; const listener = (event) => run(event.data); // Use function instead of arrow function to workaround an issue in codesandbox function setImmediate(cb, ...args) { if (window.setImmediate) { return window.setImmediate(cb); } if (!eventListenerAdded) { eventListenerAdded = true; window.addEventListener('message', listener, false); } queue[++counter] = () => { cb.apply(undefined, args); }; const windowLocation = window.location; window.postMessage(counter + '', windowLocation.protocol + '//' + windowLocation.host); return counter; } function clearImmediate(id) { if (window.clearImmediate) { return window.clearImmediate(id); } delete queue[id]; } /** @hidden @internal */ const ELEMENTS_TOKEN = /*@__PURE__*/ new InjectionToken('elements environment'); /** * @hidden */ const showMessage = (message, isMessageShown) => { if (!isMessageShown && isDevMode()) { console.warn(message); } return true; }; /** * * @hidden @internal */ const getResizeObserver = () => globalThis.window?.ResizeObserver; /** * @hidden */ function cloneArray(array, deep = false) { return deep ? (array ?? []).map(cloneValue) : (array ?? []).slice(); } /** * @hidden */ function areEqualArrays(arr1, arr2) { if (arr1.length !== arr2.length) return false; for (let i = 0; i < arr1.length; i++) { if (arr1[i] !== arr2[i]) return false; } return true; } /** * Doesn't clone leaf items * * @hidden */ const cloneHierarchicalArray = (array, childDataKey) => { const result = []; if (!array) { return result; } for (const item of array) { const clonedItem = cloneValue(item); if (Array.isArray(item[childDataKey])) { clonedItem[childDataKey] = cloneHierarchicalArray(clonedItem[childDataKey], childDataKey); } result.push(clonedItem); } return result; }; /** * Creates an object with prototype from provided source and copies * all properties descriptors from provided source * @param obj Source to copy prototype and descriptors from * @returns New object with cloned prototype and property descriptors */ const copyDescriptors = (obj) => { if (obj) { return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); } }; /** * Deep clones all first level keys of Obj2 and merges them to Obj1 * * @param obj1 Object to merge into * @param obj2 Object to merge from * @returns Obj1 with merged cloned keys from Obj2 * @hidden */ const mergeObjects = (obj1, obj2) => mergeWith(obj1, obj2, (objValue, srcValue) => { if (Array.isArray(srcValue)) { return objValue = srcValue; } }); /** * Creates deep clone of provided value. * Supports primitive values, dates and objects. * If passed value is array returns shallow copy of the array. * * @param value value to clone * @returns Deep copy of provided value * @hidden */ const cloneValue = (value) => { if (isDate(value)) { return new Date(value.getTime()); } if (Array.isArray(value)) { return value.slice(); } if (value instanceof Map || value instanceof Set) { return value; } if (isObject(value)) { const result = {}; for (const key of Object.keys(value)) { if (key === "externalObject") { continue; } result[key] = cloneValue(value[key]); } return result; } return value; }; /** * Creates deep clone of provided value. * Supports primitive values, dates and objects. * If passed value is array returns shallow copy of the array. * For Objects property values and references are cached and reused. * This allows for circular references to same objects. * * @param value value to clone * @param cache map of cached values already parsed * @returns Deep copy of provided value * @hidden */ const cloneValueCached = (value, cache) => { if (isDate(value)) { return new Date(value.getTime()); } if (Array.isArray(value)) { return [...value]; } if (value instanceof Map || value instanceof Set) { return value; } if (isObject(value)) { if (cache.has(value)) { return cache.get(value); } const result = {}; cache.set(value, result); for (const key of Object.keys(value)) { result[key] = cloneValueCached(value[key], cache); } return result; } return value; }; /** * Parse provided input to Date. * * @param value input to parse * @returns Date if parse succeed or null * @hidden */ const parseDate = (value) => { // if value is Invalid Date return null if (isDate(value)) { return !isNaN(value.getTime()) ? value : null; } return value ? new Date(value) : null; }; /** * Returns an array with unique dates only. * * @param columnValues collection of date values (might be numbers or ISO 8601 strings) * @returns collection of unique dates. * @hidden */ const uniqueDates = (columnValues) => columnValues.reduce((a, c) => { if (!a.cache[c.label]) { a.result.push(c); } a.cache[c.label] = true; return a; }, { result: [], cache: {} }).result; /** * Checks if provided variable is Object * * @param value Value to check * @returns true if provided variable is Object * @hidden */ const isObject = (value) => !!(value && value.toString() === '[object Object]'); /** * Checks if provided variable is Date * * @param value Value to check * @returns true if provided variable is Date * @hidden */ const isDate = (value) => { return Object.prototype.toString.call(value) === "[object Date]"; }; /** * Checks if the two passed arguments are equal * Currently supports date objects * * @param obj1 * @param obj2 * @returns: `boolean` * @hidden */ const isEqual = (obj1, obj2) => { if (isDate(obj1) && isDate(obj2)) { return obj1.getTime() === obj2.getTime(); } return obj1 === obj2; }; /** * Limits a number to a range between a minimum and a maximum value. * * @param number * @param min * @param max * @returns: `number` * @hidden */ const clamp = (number, min, max) => Math.max(min, Math.min(number, max)); /** * Utility service taking care of various utility functions such as * detecting browser features, general cross browser DOM manipulation, etc. * * @hidden @internal */ class PlatformUtil { constructor() { this.platformId = inject(PLATFORM_ID); this.isBrowser = isPlatformBrowser(this.platformId); this.isIOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window); this.isSafari = this.isBrowser && /Safari[\/\s](\d+\.\d+)/.test(navigator.userAgent); this.isFirefox = this.isBrowser && /Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent); this.isEdge = this.isBrowser && /Edge[\/\s](\d+\.\d+)/.test(navigator.userAgent); this.isChromium = this.isBrowser && (/Chrom|e?ium/g.test(navigator.userAgent) || /Google Inc/g.test(navigator.vendor)) && !/Edge/g.test(navigator.userAgent); this.browserVersion = this.isBrowser ? parseFloat(navigator.userAgent.match(/Version\/([\d.]+)/)?.at(1)) : 0; /** @hidden @internal */ this.isElements = inject(ELEMENTS_TOKEN, { optional: true }); this.KEYMAP = { ENTER: 'Enter', SPACE: ' ', ESCAPE: 'Escape', ARROW_DOWN: 'ArrowDown', ARROW_UP: 'ArrowUp', ARROW_LEFT: 'ArrowLeft', ARROW_RIGHT: 'ArrowRight', END: 'End', HOME: 'Home', PAGE_DOWN: 'PageDown', PAGE_UP: 'PageUp', F2: 'F2', TAB: 'Tab', SEMICOLON: ';', // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values#editing_keys DELETE: 'Delete', BACKSPACE: 'Backspace', CONTROL: 'Control', X: 'x', Y: 'y', Z: 'z' }; } /** * @hidden @internal * Returns the actual size of the node content, using Range * ```typescript * let range = document.createRange(); * let column = this.grid.columnList.filter(c => c.field === 'ID')[0]; * * let size = getNodeSizeViaRange(range, column.cells[0].nativeElement); * * @remarks * The last parameter is useful when the size of the element to measure is modified by a * parent element that has explicit size. In such cases the calculated size is never lower * and the function may instead remove the parent size while measuring to get the correct value. * ``` */ getNodeSizeViaRange(range, node, sizeHoldingNode) { let overflow = null; let nodeStyles; if (!this.isFirefox) { overflow = node.style.overflow; // we need that hack - otherwise content won't be measured correctly in IE/Edge node.style.overflow = 'visible'; } if (sizeHoldingNode) { const style = sizeHoldingNode.style; nodeStyles = [style.width, style.minWidth, style.flexBasis]; style.width = ''; style.minWidth = ''; style.flexBasis = ''; } range.selectNodeContents(node); const scale = node.getBoundingClientRect().width / node.offsetWidth; const width = range.getBoundingClientRect().width / scale; if (!this.isFirefox) { // we need that hack - otherwise content won't be measured correctly in IE/Edge node.style.overflow = overflow; } if (sizeHoldingNode) { sizeHoldingNode.style.width = nodeStyles[0]; sizeHoldingNode.style.minWidth = nodeStyles[1]; sizeHoldingNode.style.flexBasis = nodeStyles[2]; } return width; } /** * Returns true if the current keyboard event is an activation key (Enter/Space bar) * * @hidden * @internal * * @memberof PlatformUtil */ isActivationKey(event) { return event.key === this.KEYMAP.ENTER || event.key === this.KEYMAP.SPACE; } /** * Returns true if the current keyboard event is a combination that closes the filtering UI of the grid. (Escape/Ctrl+Shift+L) * * @hidden * @internal * @param event * @memberof PlatformUtil */ isFilteringKeyCombo(event) { return event.key === this.KEYMAP.ESCAPE || (event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 'l'); } /** * @hidden @internal */ isLeftClick(event) { return event.button === 0; } /** * @hidden @internal */ isNavigationKey(key) { return [ this.KEYMAP.HOME, this.KEYMAP.END, this.KEYMAP.SPACE, this.KEYMAP.ARROW_DOWN, this.KEYMAP.ARROW_LEFT, this.KEYMAP.ARROW_RIGHT, this.KEYMAP.ARROW_UP ].includes(key); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PlatformUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PlatformUtil, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PlatformUtil, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); /** * @hidden */ const flatten = (arr) => { let result = []; arr.forEach(el => { result.push(el); if (el.children) { const children = Array.isArray(el.children) ? el.children : el.children.toArray(); result = result.concat(flatten(children)); } }); return result; }; const HORIZONTAL_NAV_KEYS = new Set(['arrowleft', 'left', 'arrowright', 'right', 'home', 'end']); const NAVIGATION_KEYS = new Set([ 'down', 'up', 'left', 'right', 'arrowdown', 'arrowup', 'arrowleft', 'arrowright', 'home', 'end', 'space', 'spacebar', ' ' ]); const ACCORDION_NAVIGATION_KEYS = new Set('up down arrowup arrowdown home end'.split(' ')); const ROW_EXPAND_KEYS = new Set('right down arrowright arrowdown'.split(' ')); const ROW_COLLAPSE_KEYS = new Set('left up arrowleft arrowup'.split(' ')); const ROW_ADD_KEYS = new Set(['+', 'add', '≠', '±', '=']); const SUPPORTED_KEYS = new Set([...Array.from(NAVIGATION_KEYS), ...Array.from(ROW_ADD_KEYS), 'enter', 'f2', 'escape', 'esc', 'pagedown', 'pageup']); const HEADER_KEYS = new Set([...Array.from(NAVIGATION_KEYS), 'escape', 'esc', 'l', /** This symbol corresponds to the Alt + L combination under MAC. */ '¬']); /** * @hidden * @internal * * Creates a new ResizeObserver on `target` and returns it as an Observable. * Run the resizeObservable outside angular zone, because it patches the MutationObserver which causes an infinite loop. * Related issue: https://github.com/angular/angular/issues/31712 */ const resizeObservable = (target) => { const resizeObserver = getResizeObserver(); // check whether we are on server env or client env if (resizeObserver) { return new Observable((observer) => { const instance = new resizeObserver((entries) => { observer.next(entries); }); instance.observe(target); const unsubscribe = () => instance.disconnect(); return unsubscribe; }); } // if on a server env return a empty observable that does not complete immediately return NEVER; }; /** * @hidden * @internal * * Compares two maps. */ const compareMaps = (map1, map2) => { if (!map2) { return !map1; } if (map1.size !== map2.size) { return false; } let match = true; const keys = Array.from(map2.keys()); for (const key of keys) { if (map1.has(key)) { match = map1.get(key) === map2.get(key); } else { match = false; } if (!match) { break; } } return match; }; function _isObject(entity) { return entity != null && typeof entity === 'object'; } function columnFieldPath(path) { return path?.split('.') ?? []; } /** * Given a property access path in the format `x.y.z` resolves and returns * the value of the `z` property in the passed object. * * @hidden * @internal */ function resolveNestedPath(obj, pathParts, defaultValue) { if (!_isObject(obj) || pathParts.length < 1) { return defaultValue; } let current = obj; for (const key of pathParts) { if (_isObject(current) && key in current) { current = current[key]; } else { return defaultValue; } } return current; } /** * * Given a property access path in the format `x.y.z` and a value * this functions builds and returns an object following the access path. * * @example * ```typescript * console.log('x.y.z.', 42); * >> { x: { y: { z: 42 } } } * ``` * * @hidden * @internal */ const reverseMapper = (path, value) => { const obj = {}; const parts = path?.split('.') ?? []; let _prop = parts.shift(); let mapping; // Initial binding for first level bindings obj[_prop] = value; mapping = obj; parts.forEach(prop => { // Start building the hierarchy mapping[_prop] = {}; // Go down a level mapping = mapping[_prop]; // Bind the value and move the key mapping[prop] = value; _prop = prop; }); return obj; }; const yieldingLoop = (count, chunkSize, callback, done) => { let i = 0; const chunk = () => { const end = Math.min(i + chunkSize, count); for (; i < end; ++i) { callback(i); } if (i < count) { setImmediate(chunk); } else { done(); } }; chunk(); }; const isConstructor = (ref) => typeof ref === 'function' && Boolean(ref.prototype) && Boolean(ref.prototype.constructor); /** * Similar to Angular's formatDate. However it will not throw on `undefined | null | ''` instead * coalescing to an empty string. */ const formatDate = (value, format, locale, timezone) => { if (value === null || value === undefined || value === '') { return ''; } return formatDate$1(value, format, locale, timezone); }; const formatCurrency = new CurrencyPipe(undefined).transform; /** Converts pixel values to their rem counterparts for a base value */ const rem = (value) => { const base = parseFloat(globalThis.window?.getComputedStyle(globalThis.document?.documentElement).getPropertyValue('--ig-base-font-size')); return Number(value) / base; }; /** Get the size of the component as derived from the CSS size variable */ function getComponentSize(el) { return globalThis.window?.getComputedStyle(el).getPropertyValue('--component-size'); } /** Get the first item in an array */ function first(arr) { return arr.at(0); } /** Get the last item in an array */ function last(arr) { return arr.at(-1); } /** Calculates the modulo of two numbers, ensuring a non-negative result. */ function modulo(n, d) { return ((n % d) + d) % d; } /** * Splits an array into chunks of length `size` and returns a generator * yielding each chunk. * The last chunk may contain less than `size` elements. * * @example * ```typescript * const arr = [0,1,2,3,4,5,6,7,8,9]; * * Array.from(chunk(arr, 2)) // [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]] * Array.from(chunk(arr, 3)) // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] * Array.from(chunk([], 3)) // [] * Array.from(chunk(arr, -3)) // Error * ``` */ function* intoChunks(arr, size) { if (size < 1) { throw new Error('size must be an integer >= 1'); } for (let i = 0; i < arr.length; i += size) { yield arr.slice(i, i + size); } } /** * @param size * @returns string that represents the --component-size default value */ function getComponentCssSizeVar(size) { switch (size) { case "1": return 'var(--ig-size, var(--ig-size-small))'; case "2": return 'var(--ig-size, var(--ig-size-medium))'; default: return 'var(--ig-size, var(--ig-size-large))'; } } /** * @param path - The URI path to be normalized. * @returns string encoded using the encodeURI function. */ function normalizeURI(path) { return path?.split('/').map(encodeURI).join('/'); } function getComponentTheme(el) { return globalThis.window ?.getComputedStyle(el) .getPropertyValue('--theme') .trim(); } /** * Collection re-created w/ the built in track by identity will always log * warning even for valid cases of recalculating all collection items. * See https://github.com/angular/angular/blob/55581b4181639568fb496e91055142a1b489e988/packages/core/src/render3/instructions/control_flow.ts#L393-L409 * Current solution explicit track function doing the same as suggested in: * https://github.com/angular/angular/issues/56471#issuecomment-2180315803 * This should be used with moderation and when necessary. * @internal */ function trackByIdentity(item) { return item; } /** @hidden */ class IgxSelectionAPIService { constructor() { /** * If primaryKey is defined, then multiple selection is based on the primaryKey, and it is array of numbers, strings, etc. * If the primaryKey is omitted, then selection is based on the item data */ this.selection = new Map(); } /** * Get current component selection. * * @param componentID ID of the component. */ get(componentID) { return this.selection.get(componentID); } /** * Set new component selection. * * @param componentID ID of the component. * @param newSelection The new component selection to be set. */ set(componentID, newSelection) { if (!componentID) { throw Error('Invalid value for component id!'); } this.selection.set(componentID, newSelection); } /** * Clears selection for component. * * @param componentID ID of the component. */ clear(componentID) { this.selection.set(componentID, this.get_empty()); } /** * Removes selection for a component. * @param componentID */ delete(componentID) { this.selection.delete(componentID); } /** * Get current component selection length. * * @param componentID ID of the component. */ size(componentID) { const sel = this.get(componentID); return sel ? sel.size : 0; } /** * Creates new selection that consist of the new item added to the current component selection. * The returned collection is new Set, * therefore if you want to update component selection you need to call in addition the set_selection() method * or instead use the select_item() one. * * @param componentID ID of the component, which we add new item to. * @param itemID ID of the item to add to component selection. * @param sel Used internally only by the selection (add_items method) to accumulate selection for multiple items. * * @returns Selection after the new item is added. */ add_item(componentID, itemID, sel) { if (!sel) { sel = new Set(this.get(componentID)); } if (sel === undefined) { sel = this.get_empty(); } sel.add(itemID); return sel; } /** * Creates new selection that consist of the new items added to the current component selection. * The returned collection is new Set, * therefore if you want to update component selection you need to call in addition the set_selection() method * or instead use the select_items() one. * * @param componentID ID of the component, which we add new items to. * @param itemIDs Array of IDs of the items to add to component selection. * @param clearSelection If true it will clear previous selection. * * @returns Selection after the new items are added. */ add_items(componentID, itemIDs, clearSelection) { let selection; if (clearSelection) { selection = this.get_empty(); } else if (itemIDs && itemIDs.length === 0) { selection = new Set(this.get(componentID)); } itemIDs.forEach((item) => selection = this.add_item(componentID, item, selection)); return selection; } /** * Add item to the current component selection. * * @param componentID ID of the component, which we add new item to. * @param itemID ID of the item to add to component selection. * @param sel Used internally only by the selection (select_items method) to accumulate selection for multiple items. */ select_item(componentID, itemID, sel) { this.set(componentID, this.add_item(componentID, itemID, sel)); } /** * Add items to the current component selection. * * @param componentID ID of the component, which we add new items to. * @param itemIDs Array of IDs of the items to add to component selection. * @param clearSelection If true it will clear previous selection. */ select_items(componentID, itemID, clearSelection) { this.set(componentID, this.add_items(componentID, itemID, clearSelection)); } /** * Creates new selection that consist of the new items excluded from the current component selection. * The returned collection is new Set, * therefore if you want to update component selection you need to call in addition the set_selection() method * or instead use the deselect_item() one. * * @param componentID ID of the component, which we remove items from. * @param itemID ID of the item to remove from component selection. * @param sel Used internally only by the selection (delete_items method) to accumulate deselected items. * * @returns Selection after the item is removed. */ delete_item(componentID, itemID, sel) { if (!sel) { sel = new Set(this.get(componentID)); } if (sel === undefined) { return; } sel.delete(itemID); return sel; } /** * Creates new selection that consist of the new items removed to the current component selection. * The returned collection is new Set, * therefore if you want to update component selection you need to call in addition the set_selection() method * or instead use the deselect_items() one. * * @param componentID ID of the component, which we remove items from. * @param itemID ID of the items to remove from component selection. * * @returns Selection after the items are removed. */ delete_items(componentID, itemIDs) { let selection; itemIDs.forEach((deselectedItem) => selection = this.delete_item(componentID, deselectedItem, selection)); return selection; } /** * Remove item from the current component selection. * * @param componentID ID of the component, which we remove item from. * @param itemID ID of the item to remove from component selection. * @param sel Used internally only by the selection (deselect_items method) to accumulate selection for multiple items. */ deselect_item(componentID, itemID, sel) { this.set(componentID, this.delete_item(componentID, itemID, sel)); } /** * Remove items to the current component selection. * * @param componentID ID of the component, which we add new items to. * @param itemIDs Array of IDs of the items to add to component selection. */ deselect_items(componentID, itemID, _clearSelection) { this.set(componentID, this.delete_items(componentID, itemID)); } /** * Check if the item is selected in the component selection. * * @param componentID ID of the component. * @param itemID ID of the item to search. * * @returns If item is selected. */ is_item_selected(componentID, itemID) { const sel = this.get(componentID); if (!sel) { return false; } return sel.has(itemID); } /** * Get first element in the selection. * This is correct when we have only one item in the collection (for single selection purposes) * and the method returns that item. * * @param componentID ID of the component. * * @returns First element in the set. */ first_item(componentID) { const sel = this.get(componentID); if (sel && sel.size > 0) { return sel.values().next().value; } } /** * Returns whether all items are selected. * * @param componentID ID of the component. * @param dataCount: number Number of items in the data. * * @returns If all items are selected. */ are_all_selected(componentID, dataCount) { return dataCount > 0 && dataCount === this.size(componentID); } /** * Returns whether any of the items is selected. * * @param componentID ID of the component. * @param data Entire data array. * * @returns If there is any item selected. */ are_none_selected(componentID) { return this.size(componentID) === 0; } /** * Get all primary key values from a data array. If there isn't a primary key defined that the entire data is returned instead. * * @param data Entire data array. * @param primaryKey Data primary key. * * @returns Array of identifiers, either primary key values or the entire data array. */ get_all_ids(data, primaryKey) { // If primaryKey is 0, this should still map to the property return primaryKey !== undefined && primaryKey !== null ? data.map((x) => x[primaryKey]) : data; } /** * Returns empty selection collection. * * @returns empty set. */ get_empty() { return new Set(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxSelectionAPIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxSelectionAPIService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxSelectionAPIService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); /** * Injection token is used to inject the EditorProvider token into components * * @hidden @internal */ const EDITOR_PROVIDER = new InjectionToken('EditorProvider'); const EVENT_SUFFIX = 'precise'; /** * Touch gestures manager based on Hammer.js * Use with caution, this will track references for single manager per element. Very TBD. Much TODO. * * @hidden */ class HammerGesturesManager { static { this.Hammer = typeof window !== 'undefined' ? window.Hammer : null; } constructor() { this._zone = inject(NgZone); this.doc = inject(DOCUMENT); this.platformUtil = inject(PlatformUtil); /** * Event option defaults for each recognizer, see http://hammerjs.github.io/api/ for API listing. */ this.hammerOptions = {}; this._hammerManagers = []; this.platformBrowser = this.platformUtil.isBrowser; if (this.platformBrowser && HammerGesturesManager.Hammer) { this.hammerOptions = { // D.P. #447 Force TouchInput due to PointerEventInput bug (https://github.com/hammerjs/hammer.js/issues/1065) // see https://github.com/IgniteUI/igniteui-angular/issues/447#issuecomment-324601803 inputClass: HammerGesturesManager.Hammer.TouchInput, recognizers: [ [HammerGesturesManager.Hammer.Pan, { threshold: 0 }], [HammerGesturesManager.Hammer.Swipe, { direction: HammerGesturesManager.Hammer.DIRECTION_HORIZONTAL }], [HammerGesturesManager.Hammer.Tap], [HammerGesturesManager.Hammer.Tap, { event: 'doubletap', taps: 2 }, ['tap']] ] }; } } supports(eventName) { return eventName.toLowerCase().endsWith('.' + EVENT_SUFFIX); } /** * Add listener extended with options for Hammer.js. Will use defaults if none are provided. * Modeling after other event plugins for easy future modifications. */ addEventListener(element, eventName, eventHandler, options = null) { if (!this.platformBrowser) { return; } // Creating the manager bind events, must be done outside of angular return this._zone.runOutsideAngular(() => { if (!HammerGesturesManager.Hammer) { //no hammer return; } let mc = this.getManagerForElement(element); if (mc === null) { // new Hammer is a shortcut for Manager with defaults mc = new HammerGesturesManager.Hammer(element, Object.assign(this.hammerOptions, options)); this.addManagerForElement(element, mc); } const handler = (eventObj) => this._zone.run(() => eventHandler(eventObj)); mc.on(eventName, handler); return () => mc.off(eventName, handler); }); } /** * Add listener extended with options for Hammer.js. Will use defaults if none are provided. * Modeling after other event plugins for easy future modifications. * * @param target Can be one of either window, body or document(fallback default). */ addGlobalEventListener(target, eventName, eventHandler) { if (!this.platformBrowser || !HammerGesturesManager.Hammer) { return; } const element = this.getGlobalEventTarget(target); // Creating the manager bind events, must be done outside of angular return this.addEventListener(element, eventName, eventHandler); } /** * Exposes [Dom]Adapter.getGlobalEventTarget to get global event targets. * Supported: window, document, body. Defaults to document for invalid args. * * @param target Target name */ getGlobalEventTarget(target) { return _getDOM().getGlobalEventTarget(this.doc, target); } /** * Set HammerManager options. * * @param element The DOM element used to create the manager on. * * ### Example * * ```ts * manager.setManagerOption(myElem, "pan", { pointers: 1 }); * ``` */ setManagerOption(element, event, options) { const manager = this.getManagerForElement(element); manager.get(event).set(options); } /** * Add an element and manager map to the internal collection. * * @param element The DOM element used to create the manager on. */ addManagerForElement(element, manager) { this._hammerManagers.push({ element, manager }); } /** * Get HammerManager for the element or null * * @param element The DOM element used to create the manager on. */ getManagerForElement(element) { const result = this._hammerManagers.filter(value => value.element === element); return result.length ? result[0].manager : null; } /** * Destroys the HammerManager for the element, removing event listeners in the process. * * @param element The DOM element used to create the manager on. */ removeManagerForElement(element) { let index = null; for (let i = 0; i < this._hammerManagers.length; i++) { if (element === this._hammerManagers[i].element) { index = i; break; } } if (index !== null) { const item = this._hammerManagers.splice(index, 1)[0]; // destroy also item.manager.destroy(); } } /** Destroys all internally tracked HammerManagers, removing event listeners in the process. */ destroy() { for (const item of this._hammerManagers) { item.manager.destroy(); } this._hammerManagers = []; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: HammerGesturesManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: HammerGesturesManager }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: HammerGesturesManager, decorators: [{ type: Injectable }], ctorParameters: () => [] }); /** * Stripped-down HammerJS annotations. */ /* csSuppress */ /** @hidden @internal */ class IgxActionStripToken { } /* csSuppress */ /** * Abstract class defining the contract for components that provide actions to the action strip. * This allows the action strip to remain standalone and not be aware of specific implementations. * @hidden @internal */ class IgxActionStripActionsToken { } /** * Templates the default toggle icon in the picker. * * @remarks Can be applied to IgxDatePickerComponent, IgxTimePickerComponent, IgxDateRangePickerComponent * * @example * ```html * <igx-date-range-picker> * <igx-picker-toggle igxSuffix> * <igx-icon>calendar_view_day</igx-icon> * </igx-picker-toggle> * </igx-date-range-picker> * ``` */ class IgxPickerToggleComponent { constructor() { this.clicked = new EventEmitter(); } onClick(event) { // do not focus input on click event.stopPropagation(); this.clicked.emit(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.2", type: IgxPickerToggleComponent, isStandalone: true, selector: "igx-picker-toggle", outputs: { clicked: "clicked" }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerToggleComponent, decorators: [{ type: Component, args: [{ template: `<ng-content></ng-content>`, selector: 'igx-picker-toggle', standalone: true }] }], propDecorators: { clicked: [{ type: Output }], onClick: [{ type: HostListener, args: ['click', ['$event']] }] } }); /** * Templates the default clear icon in the picker. * * @remarks Can be applied to IgxDatePickerComponent, IgxTimePickerComponent, IgxDateRangePickerComponent * * @example * ```html * <igx-date-picker> * <igx-picker-clear igxSuffix> * <igx-icon>delete</igx-icon> * </igx-picker-clear> * </igx-date-picker> * ``` */ class IgxPickerClearComponent extends IgxPickerToggleComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerClearComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.2", type: IgxPickerClearComponent, isStandalone: true, selector: "igx-picker-clear", usesInheritance: true, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerClearComponent, decorators: [{ type: Component, args: [{ template: `<ng-content></ng-content>`, selector: 'igx-picker-clear', standalone: true }] }] }); /** * IgxPickerActionsDirective can be used to re-template the dropdown/dialog action buttons. * * @remarks Can be applied to IgxDatePickerComponent, IgxTimePickerComponent, IgxDateRangePickerComponent * */ class IgxPickerActionsDirective { constructor() { this.template = inject(TemplateRef); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerActionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxPickerActionsDirective, isStandalone: true, selector: "[igxPickerActions]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPickerActionsDirective, decorators: [{ type: Directive, args: [{ selector: '[igxPickerActions]', standalone: true }] }] }); /** Header orientation in `dialog` mode. */ const PickerHeaderOrientation = { Horizontal: 'horizontal', Vertical: 'vertical' }; /** Calendar orientation. */ const PickerCalendarOrientation = { Horizontal: 'horizontal', Vertical: 'vertical' }; /** * This enumeration is used to configure whether the date/time picker has an editable input with drop down * or is readonly - the date/time is selected only through a dialog. */ const PickerInteractionMode = { DropDown: 'dropdown', Dialog: 'dialog' }; /** * Specify a particular date, time or AmPm part. */ var DatePart; (function (DatePart) { DatePart["Date"] = "date"; DatePart["Month"] = "month"; DatePart["Year"] = "year"; DatePart["Hours"] = "hours"; DatePart["Minutes"] = "minutes"; DatePart["Seconds"] = "seconds"; DatePart["FractionalSeconds"] = "fractionalSeconds"; DatePart["AmPm"] = "ampm"; DatePart["Literal"] = "literal"; })(DatePart || (DatePart = {})); /** * Minimal type stubs for grid types to avoid circular dependencies. * These are simple interfaces that core uses for typing only. * The actual implementations are in igniteui-angular/grids. */ /* mustCoerceToInt */ /** * Enumeration representing the possible positions for pinning columns. * - Start: Columns are pinned to the start of the grid. * - End: Columns are pinned to the end of the grid. */ var ColumnPinningPosition; (function (ColumnPinningPosition) { ColumnPinningPosition[ColumnPinningPosition["Start"] = 0] = "Start"; ColumnPinningPosition[ColumnPinningPosition["End"] = 1] = "End"; })(ColumnPinningPosition || (ColumnPinningPosition = {})); /** * Enumeration representing different calculation modes for grid summaries. * - rootLevelOnly: Summaries are calculated only for the r