UNPKG

igniteui-angular

Version:

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

1,201 lines (1,196 loc) 174 kB
import * as i0 from '@angular/core'; import { Input, HostBinding, Component, Pipe, Injectable, inject, ChangeDetectorRef, ElementRef, LOCALE_ID, EventEmitter, ViewChildren, ViewChild, Output, TemplateRef, Directive, booleanAttribute, ContentChild, NgModule } from '@angular/core'; import { getCurrentResourceStrings, QueryBuilderResourceStringsEN, PlatformUtil, AbsoluteScrollStrategy, VerticalAlignment, HorizontalAlignment, CloseScrollStrategy, ConnectedPositioningStrategy, trackByIdentity, FilteringLogic, DataUtil, AutoPositionStrategy, FilteringExpressionsTree, isTree, GridColumnDataType, IgxStringFilteringOperand, IgxDateTimeFilteringOperand, IgxTimeFilteringOperand, IgxDateFilteringOperand, IgxNumberFilteringOperand, IgxBooleanFilteringOperand, IgxOverlayOutletDirective, IgxPickerClearComponent, IgxPickerToggleComponent, recreateTree } from 'igniteui-angular/core'; import { fromEvent, sampleTime, filter, Subject } from 'rxjs'; import { getLocaleFirstDayOfWeek, NgClass, NgTemplateOutlet, DatePipe } from '@angular/common'; import * as i1 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import { IgxChipComponent } from 'igniteui-angular/chips'; import { IgxDatePickerComponent } from 'igniteui-angular/date-picker'; import { IgxButtonDirective, IgxDateTimeEditorDirective, IgxDragIgnoreDirective, IgxDropDirective, IgxIconButtonDirective, IgxTooltipDirective, IgxTooltipTargetDirective } from 'igniteui-angular/directives'; import { IgxSelectComponent, IgxSelectItemComponent } from 'igniteui-angular/select'; import { IgxTimePickerComponent } from 'igniteui-angular/time-picker'; import { IgxInputDirective, IgxInputGroupComponent, IgxPrefixDirective } from 'igniteui-angular/input-group'; import { IgxIconComponent, IgxIconService } from 'igniteui-angular/icon'; import { IgxComboComponent, IgxComboHeaderDirective } from 'igniteui-angular/combo'; import { IgxCheckboxComponent } from 'igniteui-angular/checkbox'; import { IgxDialogComponent } from 'igniteui-angular/dialog'; import { IgxDropDownComponent, IgxDropDownItemComponent, IgxDropDownItemNavigationDirective } from 'igniteui-angular/drop-down'; import { editor } from '@igniteui/material-icons-extended'; class IgxQueryBuilderHeaderComponent { constructor() { this._resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); /** * Show/hide the legend. * * @example * ```html * <igx-query-builder-header [showLegend]="false"></igx-query-builder-header> * ``` * @deprecated in version 19.1.0. */ this.showLegend = true; } /** * @hidden @internal */ get getClass() { return 'igx-query-builder__header'; } /** * Sets the resource strings. * By default it uses EN resources. * * @deprecated in version 19.1.0. */ set resourceStrings(value) { this._resourceStrings = Object.assign({}, this._resourceStrings, value); } /** * Returns the resource strings. */ get resourceStrings() { return this._resourceStrings; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxQueryBuilderHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.2", type: IgxQueryBuilderHeaderComponent, isStandalone: true, selector: "igx-query-builder-header", inputs: { title: "title", showLegend: "showLegend", resourceStrings: "resourceStrings" }, host: { properties: { "class": "this.getClass" } }, ngImport: i0, template: "<div class=\"igx-query-builder__title\">{{ title }}</div>\n<ng-content></ng-content>\n" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxQueryBuilderHeaderComponent, decorators: [{ type: Component, args: [{ selector: 'igx-query-builder-header', template: "<div class=\"igx-query-builder__title\">{{ title }}</div>\n<ng-content></ng-content>\n" }] }], propDecorators: { getClass: [{ type: HostBinding, args: ['class'] }], title: [{ type: Input }], showLegend: [{ type: Input }], resourceStrings: [{ type: Input }] } }); class IgxFieldFormatterPipe { transform(value, formatter, rowData, fieldData) { return formatter(value, rowData, fieldData); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxFieldFormatterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxFieldFormatterPipe, isStandalone: true, name: "fieldFormatter" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxFieldFormatterPipe, decorators: [{ type: Pipe, args: [{ name: 'fieldFormatter', standalone: true }] }] }); /** * @hidden @internal */ class ExpressionItem { constructor(parent) { this.parent = parent; } } /** * @hidden @internal */ class ExpressionGroupItem extends ExpressionItem { constructor(operator, parent) { super(parent); this.operator = operator; this.children = []; } } /** * @hidden @internal */ class ExpressionOperandItem extends ExpressionItem { constructor(expression, parent) { super(parent); this.expression = expression; } } const IGX_QUERY_BUILDER = 'igx-query-builder'; const IGX_FILTER_TREE = 'igx-filter-tree'; /** * @hidden @internal */ const QueryBuilderSelectors = { DRAG_INDICATOR: 'igx-drag-indicator', CHIP_GHOST: 'igx-chip__ghost', DROP_DOWN_LIST_SCROLL: 'igx-drop-down__list-scroll', DROP_DOWN_ITEM_DISABLED: 'igx-drop-down__item--disabled', FILTER_TREE: IGX_FILTER_TREE, FILTER_TREE_EXPRESSION_CONTEXT_MENU: IGX_FILTER_TREE + '__expression-context-menu', FILTER_TREE_EXPRESSION_ITEM: IGX_FILTER_TREE + '__expression-item', FILTER_TREE_EXPRESSION_ITEM_DROP_GHOST: IGX_FILTER_TREE + '__expression-item-drop-ghost', FILTER_TREE_EXPRESSION_ITEM_KEYBOARD_GHOST: IGX_FILTER_TREE + '__expression-item-keyboard-ghost', FILTER_TREE_EXPRESSION_ITEM_GHOST: IGX_FILTER_TREE + '__expression-item-ghost', FILTER_TREE_EXPRESSION_SECTION: IGX_FILTER_TREE + '__expression-section', FILTER_TREE_LINE_AND: IGX_FILTER_TREE + '__line--and', FILTER_TREE_LINE_OR: IGX_FILTER_TREE + '__line--or', FILTER_TREE_SUBQUERY: IGX_FILTER_TREE + '__subquery', QUERY_BUILDER: IGX_QUERY_BUILDER, QUERY_BUILDER_BODY: IGX_QUERY_BUILDER + '__main', QUERY_BUILDER_HEADER: IGX_QUERY_BUILDER + '__header', QUERY_BUILDER_TREE: IGX_QUERY_BUILDER + '-tree', }; const DEFAULT_SET_Z_INDEX_DELAY = 10; const Z_INDEX_TO_SET = 10010; //overlay z-index is 10005 /** @hidden @internal */ class IgxQueryBuilderDragService { constructor() { this._keyDragCurrentIndex = 0; this._keyDragInitialIndex = 0; this._isKeyDragsFirstMove = true; } /** Get the dragged ghost as a HTMLElement*/ get getDragGhostElement() { return document.querySelector(`.${QueryBuilderSelectors.CHIP_GHOST}[ghostclass="${QueryBuilderSelectors.CHIP_GHOST}"]`); } /** Get the drop ghost chip component */ get getDropGhostElement() { return this._queryBuilderTreeComponent.expressionsChips.find(x => x.data === this.dropGhostExpression); } get getMainExpressionTree() { return this._queryBuilderTreeComponentElRef.nativeElement.querySelector(`.${QueryBuilderSelectors.FILTER_TREE}`); } register(tree, el) { this._queryBuilderTreeComponent = tree; this._queryBuilderTreeComponentElRef = el; } /** When chip is picked up for dragging * * @param sourceDragElement The HTML element of the chip that's been dragged * @param sourceExpressionItem The expressionItem of the chip that's been dragged * @param isKeyboardDrag If it's a mouse drag or keyboard reorder * */ onMoveStart(sourceDragElement, sourceExpressionItem, isKeyboardDrag) { this.resetDragAndDrop(true); this._queryBuilderTreeComponent._expressionTreeCopy = this._queryBuilderTreeComponent._expressionTree; this.isKeyboardDrag = isKeyboardDrag; this._sourceExpressionItem = sourceExpressionItem; this._sourceElement = sourceDragElement; this.listenToKeyboard(); if (!this.isKeyboardDrag) { this._sourceElement.style.display = 'none'; this.setDragGhostZIndex(); } } /** When dragged chip is let go outside a proper drop zone */ onMoveEnd() { if (!this._sourceElement || !this._sourceExpressionItem) { return; } if (this.dropGhostExpression) { //If there is a ghost chip presented to the user, execute drop this.onChipDropped(); } else { this.resetDragAndDrop(true); } this._ghostChipMousemoveSubscription$?.unsubscribe(); this._keyboardSubscription$?.unsubscribe(); } /** When mouse drag enters a chip's area * @param targetDragElement The HTML element of the drop area chip that's been dragged to * @param targetExpressionItem The expressionItem of the drop area chip that's been dragged to */ onChipEnter(targetDragElement, targetExpressionItem) { if (!this._sourceElement || !this._sourceExpressionItem) { return; } //If entering the one that's been picked up don't do any thing if (targetExpressionItem === this.dropGhostExpression) { return; } //Simulate leaving the last entered chip in case of no Leave event triggered due to the artificial drop zone of a north positioned ghost chip if (this._targetExpressionItem) { this.resetDragAndDrop(false); } this._targetExpressionItem = targetExpressionItem; //Determine the middle point of the chip. const appendUnder = this.ghostInLowerPart(targetDragElement); this.renderDropGhostChip(appendUnder); } /** When mouse drag moves in a div's drop area * @param targetDragElement The HTML element of the drop area chip that's been dragged to * @param targetExpressionItem The expressionItem of the drop area chip that's been dragged to */ onDivOver(targetDragElement, targetExpressionItem) { if (this._targetExpressionItem === targetExpressionItem) { this.onChipOver(targetDragElement); } else { this.onChipEnter(targetDragElement, targetExpressionItem); } } /** When mouse drag moves in a chip's drop area * @param targetDragElement The HTML element of the drop area chip that's been dragged to */ onChipOver(targetDragElement) { if (!this._sourceElement || !this._sourceExpressionItem) { return; } //Determine the middle point of the chip. const appendUnder = this.ghostInLowerPart(targetDragElement); this.renderDropGhostChip(appendUnder); } /** When mouse drag leaves a chip's drop area */ onChipLeave() { if (!this._sourceElement || !this._sourceExpressionItem) { return; } //if the drag ghost is on the drop ghost row don't trigger leave if (this.dragGhostIsOnDropGhostRow()) { return; } if (this._targetExpressionItem) { this.resetDragAndDrop(false); } } /** When dragged chip is let go in div's drop area * @param targetExpressionItem The expressionItem of the drop area chip that's been dragged to */ onDivDropped(targetExpressionItem) { if (targetExpressionItem !== this._sourceExpressionItem) { this.onChipDropped(); } } /** When dragged chip is let go in chip's drop area */ onChipDropped() { if (!this._sourceElement || !this._sourceExpressionItem) { return; } //Determine which chip to be focused after drop completes const [dropLocationIndex, _] = this.countChipsBeforeDropLocation(this._queryBuilderTreeComponent.rootGroup); //Delete from old place this._queryBuilderTreeComponent.deleteItem(this._sourceExpressionItem); this.dropGhostExpression = null; this._queryBuilderTreeComponent.focusChipAfterDrag(dropLocationIndex); this.resetDragAndDrop(true); this._queryBuilderTreeComponent.exitEditAddMode(); } /** When mouse drag moves in a AND/OR drop area * @param targetDragElement The HTML element of the drop area chip that's been dragged to * @param targetExpressionItem The expressionItem of the drop area chip that's been dragged to */ onGroupRootOver(targetDragElement, targetExpressionItem) { if (!this._sourceElement || !this._sourceExpressionItem) { return; } let newTargetExpressionItem; if (this.ghostInLowerPart(targetDragElement) || !targetExpressionItem.parent) { //if ghost is in lower part of the AND/OR (or it's the main group) => drop as first child of that group //accounting for the fact that the drop ghost might already be there as first child if (targetExpressionItem.children[0] !== this.dropGhostExpression) { newTargetExpressionItem = targetExpressionItem.children[0]; } else { newTargetExpressionItem = targetExpressionItem.children[1]; } } else { //if ghost is in upper part => drop before the group starts newTargetExpressionItem = targetExpressionItem; } if (this._targetExpressionItem !== newTargetExpressionItem) { this.resetDragAndDrop(false); this._targetExpressionItem = newTargetExpressionItem; this.renderDropGhostChip(false); } } /** When mouse drag moves in 'Add condition' button's drop area * @param addConditionElement The Add condition button HTML Element * @param rootGroup The root group of the query tree */ onAddConditionEnter(addConditionElement, rootGroup) { if (!this._sourceElement || !this._sourceExpressionItem) { return; } const lastElement = addConditionElement.parentElement.previousElementSibling.lastElementChild; //simulate entering in the lower part of the last chip/group this.onChipEnter(lastElement, rootGroup.children[rootGroup.children.length - 1]); } /** When chip's drag indicator is focused * * @param sourceDragElement The HTML element of the chip that's been dragged * @param sourceExpressionItem The expressionItem of the chip that's been dragged * */ onChipDragIndicatorFocus(sourceDragElement, sourceExpressionItem) { //if drag is not underway, already if (!this.getDropGhostElement) { this.onMoveStart(sourceDragElement, sourceExpressionItem, true); } } /** When chip's drag indicator looses focus*/ onChipDragIndicatorFocusOut() { if (this._sourceElement?.style?.display !== 'none') { this.resetDragAndDrop(true); this._keyboardSubscription$?.unsubscribe(); } } /** Upon blurring the tree, if Keyboard drag is underway and the next active item is not the drop ghost's drag indicator icon, cancel the drag&drop procedure*/ onDragFocusOut() { if (this.isKeyboardDrag && this.getDropGhostElement) { //have to wait a tick because upon blur, the next activeElement is always body, right before the next element gains focus setTimeout(() => { if (document.activeElement.className.indexOf(QueryBuilderSelectors.DRAG_INDICATOR) === -1) { this.resetDragAndDrop(true); this._keyboardSubscription$?.unsubscribe(); } }, 0); } } /** Checks if the dragged ghost is horizontally on the same line with the drop ghost*/ dragGhostIsOnDropGhostRow() { const dragGhostBounds = this.getDragGhostElement.getBoundingClientRect(); const dropGhostBounds = this.getDropGhostElement?.nativeElement?.parentElement.getBoundingClientRect(); if (!dragGhostBounds || !dropGhostBounds) { return false; } const tolerance = dragGhostBounds.bottom - dragGhostBounds.top; return !(dragGhostBounds.bottom < dropGhostBounds.top - tolerance || dragGhostBounds.top > dropGhostBounds.bottom + tolerance); } /** Checks if the dragged ghost is north or south of a target element's center*/ ghostInLowerPart(ofElement) { const ghostBounds = this.getDragGhostElement.getBoundingClientRect(); const targetBounds = ofElement.getBoundingClientRect(); return ((ghostBounds.top + ghostBounds.bottom) / 2) >= ((targetBounds.top + targetBounds.bottom) / 2); } /** Make a copy of the _sourceExpressionItem's chip and paste it in the tree north or south of the _targetExpressionItem's chip */ renderDropGhostChip(appendUnder) { if (appendUnder !== this._dropUnder || this.isKeyboardDrag) { this.clearDropGhost(); //Copy dragged chip const dragCopy = { ...this._sourceExpressionItem }; dragCopy.parent = this._targetExpressionItem.parent; this.dropGhostExpression = dragCopy; //Paste chip this._dropUnder = appendUnder; const pasteIndex = this._targetExpressionItem.parent.children.indexOf(this._targetExpressionItem); this._targetExpressionItem.parent.children.splice(pasteIndex + (this._dropUnder ? 1 : 0), 0, dragCopy); } //Put focus on the drag icon of the ghost while performing keyboard drag if (this.isKeyboardDrag) { setTimeout(() => { const dropGhostDragIndicator = this.getDropGhostElement?.nativeElement?.querySelector(`.${QueryBuilderSelectors.DRAG_INDICATOR}`); if (dropGhostDragIndicator) { dropGhostDragIndicator.focus(); } }, 0); } //Attach a mousemove event listener (if not already in place) to the dragged ghost (if present) if (!this.isKeyboardDrag && this.getDragGhostElement && (!this._ghostChipMousemoveSubscription$ || this._ghostChipMousemoveSubscription$?.closed === true)) { const mouseMoves = fromEvent(this.getDragGhostElement, 'mousemove'); //When mouse moves and there is a drop ghost => trigger onChipLeave to check if the drop ghost has to be removed //effectively solving the case when mouse leaves the QB and a drop ghost is still in place this._ghostChipMousemoveSubscription$ = mouseMoves.pipe(sampleTime(100)).subscribe(() => { if (this.getDropGhostElement) { this.onChipLeave(); } }); } this.setDragCursor('grab'); } /** Set the cursor when dragging a ghost*/ setDragCursor(cursor) { if (this.getDragGhostElement) { this.getDragGhostElement.style.cursor = cursor; } } /** Removes the drop ghost expression from the tree and it's chip effectively */ clearDropGhost() { if (this.dropGhostExpression) { const children = this.dropGhostExpression.parent.children; const delIndex = children.indexOf(this.dropGhostExpression); children.splice(delIndex, 1); this.dropGhostExpression = null; } } /** Reset Drag&Drop vars. Optionally the drag source vars too*/ resetDragAndDrop(clearDragged) { this._targetExpressionItem = null; this._dropUnder = null; this.clearDropGhost(); this._keyDragInitialIndex = 0; this._keyDragCurrentIndex = 0; this._possibleDropLocations = null; this._isKeyDragsFirstMove = true; this.setDragCursor('no-drop'); if (this._queryBuilderTreeComponent._expressionTreeCopy) { this._queryBuilderTreeComponent._expressionTree = this._queryBuilderTreeComponent._expressionTreeCopy; } if ((clearDragged || this.isKeyboardDrag) && this._sourceElement) { this._sourceElement.style.display = ''; } if (clearDragged) { this._queryBuilderTreeComponent._expressionTreeCopy = null; this._sourceExpressionItem = null; this._sourceElement = null; } } /** Start listening for drag and drop specific keys */ listenToKeyboard() { this._keyboardSubscription$?.unsubscribe(); this._keyboardSubscription$ = fromEvent(this.getMainExpressionTree, 'keydown') .pipe(filter(e => ['ArrowUp', 'ArrowDown', 'Enter', 'Space', 'Escape', 'Tab'].includes(e.key))) // .pipe(tap(e => { // //Inhibit Tabs if keyboard drag is underway (don't allow to loose focus of the drop ghost's drag indicator) // if (e.key === 'Tab' && this.getDropGhostElement) { // e.preventDefault(); // } // })) .pipe(filter(event => !event.repeat)) .subscribe(e => { if (e.key === 'Escape') { //TODO cancel mouse drag once it's implemented in igx-chip draggable this.resetDragAndDrop(false); //Regain focus on the drag icon after keyboard drag cancel if (this.isKeyboardDrag) { this._sourceElement.firstElementChild.firstElementChild.firstElementChild.firstElementChild.focus(); } } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { this.arrowDrag(e.key); } else if (e.key === 'Enter' || e.key === 'Space') { //this.platform.isActivationKey(eventArgs) Maybe use this rather that Enter/Space? this.onChipDropped(); this._keyboardSubscription$.unsubscribe(); } }); } /** Perform up/down movement of drop ghost along the expression tree*/ arrowDrag(key) { if (!this._sourceElement || !this._sourceExpressionItem) { return; } const rootGroup = this._queryBuilderTreeComponent.rootGroup; if (this._isKeyDragsFirstMove) { this._possibleDropLocations = this.getPossibleDropLocations(rootGroup, true); this._keyDragInitialIndex = this._possibleDropLocations.findIndex(e => e[0] === this._sourceExpressionItem && e[1] === true); this._keyDragCurrentIndex = this._keyDragInitialIndex; if (this._keyDragInitialIndex === -1) { console.error("Dragged expression not found"); } this._sourceElement.style.display = 'none'; } let newKeyIndexOffset = this._keyDragCurrentIndex; if (key === 'ArrowUp') { //decrease index capped at top of tree newKeyIndexOffset && newKeyIndexOffset--; } else if (key === 'ArrowDown') { //increase index capped at bottom of tree newKeyIndexOffset < this._possibleDropLocations.length - 1 && newKeyIndexOffset++; } else { console.error('wrong key'); return; } //if drop location has no change if (newKeyIndexOffset !== this._keyDragCurrentIndex || this._isKeyDragsFirstMove) { this._keyDragCurrentIndex = newKeyIndexOffset; const newDropTarget = this._possibleDropLocations[this._keyDragCurrentIndex]; this._targetExpressionItem = newDropTarget[0]; this.renderDropGhostChip(newDropTarget[1]); //Situations when drop ghost hasn't really moved, run one more time if (this._keyDragCurrentIndex === this._keyDragInitialIndex || (this._isKeyDragsFirstMove && this._keyDragCurrentIndex === this._keyDragInitialIndex - 1)) { this._isKeyDragsFirstMove = false; this.arrowDrag(key); } this._isKeyDragsFirstMove = false; } return; } /** Produces a flat ordered list of possible drop locations as Tuple <[targetExpression, dropUnder]>, while performing the keyboard drag&drop */ getPossibleDropLocations(group, isRoot) { const result = new Array(); //Add dropZone under AND/OR (as first child of group) result.push([group.children[0], false]); for (let i = 0; i < group.children.length; i++) { if (group.children[i] instanceof ExpressionGroupItem) { result.push(...this.getPossibleDropLocations(group.children[i], false)); } else { result.push([group.children[i], true]); } } //Add dropZone under the whole group if (!isRoot) { result.push([group, true]); } return result; } /** Counts how many chips will be in the tree (from top to bottom) before the dropped one */ countChipsBeforeDropLocation(group) { let count = 0, totalCount = 0, targetReached = false; for (let i = 0; i < group.children.length; i++) { const child = group.children[i]; if (targetReached) { break; } if (child instanceof ExpressionGroupItem) { if (child === this._targetExpressionItem) { if (this._dropUnder) { [count] = this.countChipsBeforeDropLocation(child); totalCount += count; } targetReached = true; } else { [count, targetReached] = this.countChipsBeforeDropLocation(child); totalCount += count; } } else { if (child !== this._sourceExpressionItem && //not the hidden source chip child !== this.dropGhostExpression && //not the drop ghost !(child.inEditMode && this._queryBuilderTreeComponent.operandCanBeCommitted() !== true) //not a chip in edit mode that will be discarded ) { totalCount++; } if (child === this._targetExpressionItem) { targetReached = true; if (!this._dropUnder && !(child.inEditMode && this._queryBuilderTreeComponent.operandCanBeCommitted() !== true)) { totalCount--; } } } } totalCount === -1 && totalCount++; return [totalCount, targetReached]; } /** Sets the z-index of the drag ghost with a little delay, since we don't have access to ghostCreated() but we know it's executed right after moveStart() */ setDragGhostZIndex() { if (this._timeoutId) { clearTimeout(this._timeoutId); } this._timeoutId = setTimeout(() => { if (this.getDragGhostElement?.style) { this.getDragGhostElement.style.zIndex = `${Z_INDEX_TO_SET}`; } }, DEFAULT_SET_Z_INDEX_DELAY); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxQueryBuilderDragService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxQueryBuilderDragService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxQueryBuilderDragService, decorators: [{ type: Injectable }] }); const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; const DEFAULT_PIPE_DATE_TIME_FORMAT = 'medium'; const DEFAULT_PIPE_DIGITS_INFO = '1.0-3'; const DEFAULT_CHIP_FOCUS_DELAY = 50; /** @hidden */ class IgxQueryBuilderTreeComponent { /** * @hidden @internal */ get getClass() { return `igx-query-builder-tree--level-${this.level}`; } /** * Returns the parent expression operand. */ get parentExpression() { return this._parentExpression; } /** * Sets the parent expression operand. */ set parentExpression(value) { this._parentExpression = value; } /** * Returns the fields. */ get fields() { if (!this._fields && this.isAdvancedFiltering()) { this._fields = this.entities[0].fields; } return this._fields; } /** * Sets the fields. */ set fields(fields) { this._fields = fields; this._fields = this._fields?.map(f => ({ ...f, filters: this.getFilters(f), pipeArgs: this.getPipeArgs(f) })); if (!this._fields && this.isAdvancedFiltering()) { this._fields = this.entities[0].fields; } } /** * Returns the expression tree. */ get expressionTree() { return this._expressionTree; } /** * Sets the expression tree. */ set expressionTree(expressionTree) { this._expressionTree = expressionTree; if (!expressionTree) { this._selectedEntity = this.isAdvancedFiltering() && this.entities.length === 1 ? this.entities[0] : null; this._selectedReturnFields = this._selectedEntity ? this._selectedEntity.fields?.map(f => f.field) : []; } if (!this._preventInit) { this.init(); } } /** * Gets the `locale` of the query builder. * If not set, defaults to application's locale. */ get locale() { return this._locale; } /** * Sets the `locale` of the query builder. * Expects a valid BCP 47 language tag. */ set locale(value) { this._locale = value; // if value is invalid, set it back to _localeId try { getLocaleFirstDayOfWeek(this._locale); } catch { this._locale = this._localeId; } } /** * Sets the resource strings. * By default it uses EN resources. */ set resourceStrings(value) { this._resourceStrings = Object.assign({}, this._resourceStrings, value); } /** * Returns the resource strings. */ get resourceStrings() { return this._resourceStrings; } set editingInputsContainer(value) { if ((value && !this._editingInputsContainer) || (value && this._editingInputsContainer && this._editingInputsContainer.nativeElement !== value.nativeElement)) { requestAnimationFrame(() => { this.scrollElementIntoView(value.nativeElement); }); } this._editingInputsContainer = value; } /** @hidden */ get editingInputsContainer() { return this._editingInputsContainer; } set currentGroupButtonsContainer(value) { if ((value && !this._currentGroupButtonsContainer) || (value && this._currentGroupButtonsContainer && this._currentGroupButtonsContainer.nativeElement !== value.nativeElement)) { requestAnimationFrame(() => { this.scrollElementIntoView(value.nativeElement); }); } this._currentGroupButtonsContainer = value; } /** @hidden */ get currentGroupButtonsContainer() { return this._currentGroupButtonsContainer; } /** * Returns if the select entity dropdown at the root level is disabled after the initial selection. */ get disableEntityChange() { return !this.parentExpression && this.selectedEntity ? this.queryBuilder.disableEntityChange : false; } /** * Returns if the fields combo at the root level is disabled. */ get disableReturnFieldsChange() { return !this.selectedEntity || this.queryBuilder.disableReturnFieldsChange; } /** * Returns the current level. */ get level() { let parent = this.elRef.nativeElement.parentElement; let _level = 0; while (parent) { if (parent.localName === 'igx-query-builder-tree') { _level++; } parent = parent.parentElement; } return _level; } /** @hidden */ isAdvancedFiltering() { return (this.entities?.length === 1 && !this.entities[0]?.name) || this.entities?.find(e => e.childEntities?.length > 0) !== undefined || (this.entities?.length > 0 && this.queryBuilder?.entities?.length > 0 && this.entities !== this.queryBuilder?.entities); } /** @hidden */ isHierarchicalNestedQuery() { return this.queryBuilder.entities !== this.entities; } /** @hidden */ isSearchValueInputDisabled() { return !this.selectedField || !this.selectedCondition || (this.selectedField && (this.selectedField.filters.condition(this.selectedCondition).isUnary || this.selectedField.filters.condition(this.selectedCondition).isNestedQuery)); } constructor() { this.cdr = inject(ChangeDetectorRef); this.dragService = inject(IgxQueryBuilderDragService); this.platform = inject(PlatformUtil); this.elRef = inject(ElementRef); this._localeId = inject(LOCALE_ID); /** * Sets/gets the search value template. */ this.searchValueTemplate = null; /** * Gets/sets the expected return field. */ this.expectedReturnField = null; /** * Event fired as the expression tree is changed. */ this.expressionTreeChange = new EventEmitter(); /** * Event fired if a nested query builder tree is being edited. */ this.inEditModeChange = new EventEmitter(); /** * @hidden @internal */ this.selectedExpressions = []; /** * @hidden @internal */ this.searchValue = { value: null }; /** * @hidden @internal */ this.initialOperator = 0; /** * @hidden @internal */ this.returnFieldSelectOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; /** * @hidden @internal */ this.entitySelectOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; /** * @hidden @internal */ this.fieldSelectOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; /** * @hidden @internal */ this.conditionSelectOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; /** * @hidden @internal */ this.addExpressionDropDownOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; /** * @hidden @internal */ this.groupContextMenuDropDownOverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, closeOnOutsideClick: true }; this.destroy$ = new Subject(); this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY; this._preventInit = false; this._expandedExpressions = []; this._resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); this._positionSettings = { horizontalStartPoint: HorizontalAlignment.Right, verticalStartPoint: VerticalAlignment.Top }; this._overlaySettings = { closeOnOutsideClick: false, modal: false, positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), scrollStrategy: new CloseScrollStrategy() }; /** * @hidden @internal */ this.deleteItem = (expressionItem, skipEmit = false) => { if (!expressionItem.parent) { this.rootGroup = null; this.currentGroup = null; //this._expressionTree = null; return; } if (expressionItem === this.currentGroup) { this.currentGroup = this.currentGroup.parent; } const children = expressionItem.parent.children; const index = children.indexOf(expressionItem); children.splice(index, 1); const entity = this.expressionTree ? this.expressionTree.entity : null; const returnFields = this.expressionTree ? this.expressionTree.returnFields : null; this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, entity, returnFields); // TODO: don't recreate if not necessary if (!children.length) { this.deleteItem(expressionItem.parent, true); } if (!this.parentExpression && !skipEmit) { this.expressionTreeChange.emit(this._expressionTree); } }; /** * @hidden @internal */ this.focusChipAfterDrag = (index) => { this._lastFocusedChipIndex = index; this.focusEditedExpressionChip(); }; /** rootGroup is recreated after clicking Apply, which sets new expressionTree and calls init()*/ this.trackExpressionItem = trackByIdentity; const elRef = this.elRef; this.locale = this.locale || this._localeId; this.dragService.register(this, elRef); } /** * @hidden @internal */ ngAfterViewInit() { this._overlaySettings.outlet = this.overlayOutlet; this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; this.returnFieldSelectOverlaySettings.outlet = this.overlayOutlet; this.addExpressionDropDownOverlaySettings.outlet = this.overlayOutlet; this.groupContextMenuDropDownOverlaySettings.outlet = this.overlayOutlet; if (this.isAdvancedFiltering() && this.entities?.length === 1) { this.selectedEntity = this.entities[0].name; if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) { this._selectedReturnFields = [this.expectedReturnField]; } } // Trigger additional change detection cycle this.cdr.detectChanges(); } /** * @hidden @internal */ ngOnDestroy() { this.destroy$.next(true); this.destroy$.complete(); } /** * @hidden @internal */ set selectedEntity(value) { this._selectedEntity = this.entities?.find(el => el.name === value); } /** * @hidden @internal */ get selectedEntity() { return this._selectedEntity; } /** * @hidden @internal */ onEntitySelectChanging(event) { event.cancel = true; this._entityNewValue = event.newSelection.value; if (event.oldSelection.value && this.queryBuilder.showEntityChangeDialog) { this.entityChangeDialog.open(); } else { this.onEntityChangeConfirm(); } } /** * @hidden */ onShowEntityChangeDialogChange(eventArgs) { this.queryBuilder.showEntityChangeDialog = !eventArgs.checked; } /** * @hidden */ onEntityChangeCancel() { this.entityChangeDialog.close(); this.entitySelect.close(); this._entityNewValue = null; } /** * @hidden */ onEntityChangeConfirm() { if (this._parentExpression) { this._expressionTree = this.createExpressionTreeFromGroupItem(this.createExpressionGroupItem(this._expressionTree)); } this._selectedEntity = this._entityNewValue; if (!this._selectedEntity.fields) { this._selectedEntity.fields = []; } this.fields = this._entityNewValue ? this._entityNewValue.fields : []; if (this._selectedEntity.fields.find(f => f.field === this.expectedReturnField)) { this._selectedReturnFields = [this.expectedReturnField]; } else { this._selectedReturnFields = this.parentExpression ? [] : this._entityNewValue.fields?.map(f => f.field); } if (this._expressionTree) { this._expressionTree.entity = this._entityNewValue.name; const returnFields = Array.isArray(this._selectedReturnFields) ? this._selectedReturnFields : [this._selectedReturnFields]; this._expressionTree.returnFields = this.fields.length === returnFields.length ? ['*'] : returnFields; this._expressionTree.filteringOperands = []; this._editedExpression = null; if (!this.parentExpression) { this.expressionTreeChange.emit(this._expressionTree); } this.rootGroup = null; this.currentGroup = this.rootGroup; } this._selectedField = null; this.selectedCondition = null; this.searchValue.value = null; this.entityChangeDialog.close(); this.entitySelect.close(); this._entityNewValue = null; this.innerQueryNewExpressionTree = null; this.initExpressionTree(this._selectedEntity.name, this.selectedReturnFields); } /** * @hidden @internal */ set selectedReturnFields(value) { if (this._selectedReturnFields !== value) { this._selectedReturnFields = value; if (this._expressionTree && !this.parentExpression) { this._expressionTree.returnFields = value.length === this.fields.length ? ['*'] : value; this.expressionTreeChange.emit(this._expressionTree); } } } /** * @hidden @internal */ get selectedReturnFields() { if (typeof this._selectedReturnFields == 'string') { return [this._selectedReturnFields]; } return this._selectedReturnFields; } /** * @hidden @internal */ set selectedField(value) { const oldValue = this._selectedField; if (this._selectedField !== value) { this._selectedField = value; if (this._selectedField && !this._selectedField.dataType) { this._selectedField.filters = this.getFilters(this._selectedField); } this.selectDefaultCondition(); if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) { this.searchValue.value = null; this.cdr.detectChanges(); } } } /** * @hidden @internal */ get selectedField() { return this._selectedField; } /** * @hidden @internal * * used by the grid */ setPickerOutlet(outlet) { this.pickerOutlet = outlet; } /** * @hidden @internal * * used by the grid */ get isContextMenuVisible() { return !this.groupContextMenuDropDown.collapsed; } /** * @hidden @internal */ get hasEditedExpression() { return this._editedExpression !== undefined && this._editedExpression !== null; } /** * @hidden @internal */ addCondition(parent, afterExpression, isUIInteraction) { this.cancelOperandAdd(); const operandItem = new ExpressionOperandItem({ fieldName: null, condition: null, conditionName: null, ignoreCase: true, searchVal: null }, parent); const groupItem = new ExpressionGroupItem(this.getOperator(null) ?? FilteringLogic.And, parent); this.contextualGroup = groupItem; this.initialOperator = null; this._lastFocusedChipIndex = this._lastFocusedChipIndex === undefined ? -1 : this._lastFocusedChipIndex; if (parent) { if (afterExpression) { const index = parent.children.indexOf(afterExpression); parent.children.splice(index + 1, 0, operandItem); } else { parent.children.push(operandItem); } this._lastFocusedChipIndex++; } else { this.rootGroup = groupItem; operandItem.parent = groupItem; this.rootGroup.children.push(operandItem); this._lastFocusedChipIndex = 0; } this._focusDelay = 250; if (isUIInteraction && !afterExpression) { this._lastFocusedChipIndex = this.expressionsChips.length; this._focusDelay = DEFAULT_CHIP_FOCUS_DELAY; } this.enterExpressionEdit(operandItem); } /** * @hidden @internal */ addReverseGroup(parent, afterExpression) { parent = parent ?? this.rootGroup; if (parent.operator === FilteringLogic.And) { this.addGroup(FilteringLogic.Or, parent, afterExpression); } else { this.addGroup(FilteringLogic.And, parent, afterExpression); } } /** * @hidden @internal */ endGroup(groupItem) { this.currentGroup = groupItem.parent; } /** * @hidden @internal */ commitExpression() { this.commitOperandEdit(); this.focusEditedExpressionChip(); } /** * @hidden @internal */ discardExpression(expressionItem) { this.cancelOperandEdit(); if (expressionItem && expressionItem.expression.fieldName) { this.focusEditedExpressionChip(); } } /** * @hidden @internal */ commitOperandEdit() { const actualSearchValue = this.searchValue.value; if (this._editedExpression) { this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.conditionName = this.selectedCondition; this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, actualSearchValue) || actualSearchValue; this._editedExpression.fieldLabel = this.selectedField.label ? this.selectedField.label : this.selectedField.header ? this.selectedField.header : this.selectedField.field; const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; if (innerQuery && this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) { innerQuery.exitEditAddMode(); this._editedExpression.expression.searchTree = this.getExpressionTreeCopy(innerQuery.expressionTree); const returnFields = innerQuery.selectedReturnFields.length > 0 ? innerQuery.selectedReturnFields : [innerQuery.fields[0].field]; this._editedExpression.expression.searchTree.returnFields = returnFields; } else { this._editedExpression.expression.searchTree = null; } this.innerQueryNewExpressionTree = null; if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary || this.selectedField.filters.condition(this.selectedCondition)?.isNestedQuery) { this._editedExpression.expression.searchVal = null; } this._editedExpression.inEditMode = false; this._editedExpression = null; } if (this.selectedReturnFields.length === 0) { this.selectedReturnFields = this.fields.map(f => f.field); } this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, this.selectedEntity?.name, this.selectedReturnFields); if (!this.parentExpression) { this.expressionTreeChange.emit(this._expressionTree); } } /** * @hidden @internal */ cancelOperandAdd() { if (this._addModeExpression) { this._addModeExpression.inAddMode = false; this._addModeExpression = null; } } /** * @hidden @internal */ cancelOperandEdit() { if (this.innerQueries) { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; if (innerQuery) { if (innerQuery._editedExpression) { innerQuery.cancelOperandEdit(); } innerQuery.expressionTree = this.getExpressionTreeCopy(this._editedExpression.expression.searchTree); this.innerQueryNewExpressionTree = null; } }