igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,394 lines (1,388 loc) • 394 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, ElementRef, EventEmitter, booleanAttribute, HostListener, HostBinding, Input, Output, Directive, Renderer2, ChangeDetectorRef, DestroyRef, ViewChild, DOCUMENT, ViewContainerRef, NgZone, RendererStyleFlags2, ContentChildren, Pipe, Component, PLATFORM_ID, Injectable, TemplateRef, IterableDiffers, EnvironmentInjector, createComponent, LOCALE_ID, forwardRef, NgModule } from '@angular/core';
import { PlatformUtil, THEME_TOKEN, getComponentTheme, EDITOR_PROVIDER, resizeObservable, getResizeObserver, compareMaps, IgxOverlayService, IgxNavigationService, AbsoluteScrollStrategy, ConnectedPositioningStrategy, VerticalAlignment, HorizontalAlignment, AutoPositionStrategy, first as first$1, DatePart, isDate, DateTimeUtil } from 'igniteui-angular/core';
import { animationFrameScheduler, Subject, noop, fromEvent, interval, takeUntil as takeUntil$1 } from 'rxjs';
import { NgControl, Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import { takeUntil, throttle, throttleTime, filter, first } from 'rxjs/operators';
import * as i1 from '@angular/common';
import { DOCUMENT as DOCUMENT$1, isPlatformBrowser, CommonModule } from '@angular/common';
import { AnimationBuilder, style, animate, useAnimation } from '@angular/animations';
import { IgxIconComponent } from 'igniteui-angular/icon';
import { fadeOut, scaleInCenter } from 'igniteui-angular/animations';
const IgxBaseButtonType = {
Flat: 'flat',
Contained: 'contained',
Outlined: 'outlined'
};
class IgxButtonBaseDirective {
/**
* @hidden
* @internal
*/
onClick(ev) {
this.buttonClick.emit(ev);
this.focused = false;
}
/**
* @hidden
* @internal
*/
onBlur() {
this.focused = false;
}
/**
* @hidden
* @internal
*/
get disabledAttribute() {
return this.disabled || null;
}
constructor() {
this._platformUtil = inject(PlatformUtil);
this.element = inject(ElementRef);
this._viewInit = false;
/**
* Emitted when the button is clicked.
*/
this.buttonClick = new EventEmitter();
/**
* Sets/gets the `role` attribute.
*
* @example
* ```typescript
* this.button.role = 'navbutton';
* let buttonRole = this.button.role;
* ```
*/
this.role = 'button';
/**
* Sets/gets whether the button component is on focus.
* Default value is `false`.
* ```typescript
* this.button.focus = true;
* ```
* ```typescript
* let isFocused = this.button.focused;
* ```
*/
this.focused = false;
/**
* Enables/disables the button.
*
* @example
* ```html
* <button igxButton="fab" disabled></button>
* ```
*/
this.disabled = false;
// In browser, set via native API for immediate effect (no-op on server).
// In SSR there is no paint, so there’s no visual rendering or transitions to suppress.
// Fix style flickering https://github.com/IgniteUI/igniteui-angular/issues/14759
if (this._platformUtil.isBrowser) {
this.element.nativeElement.style.setProperty('--_init-transition', '0s');
}
}
ngAfterViewInit() {
if (this._platformUtil.isBrowser && !this._viewInit) {
this._viewInit = true;
this._animationScheduler = animationFrameScheduler.schedule(() => {
this.element.nativeElement.style.removeProperty('--_init-transition');
});
}
}
ngOnDestroy() {
this._animationScheduler.unsubscribe();
}
/**
* @hidden
* @internal
*/
updateOnKeyUp(event) {
if (event.key === "Tab") {
this.focused = true;
}
}
/**
* Returns the underlying DOM element.
*/
get nativeElement() {
return this.element.nativeElement;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxButtonBaseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.2", type: IgxButtonBaseDirective, isStandalone: true, inputs: { disabled: ["disabled", "disabled", booleanAttribute] }, outputs: { buttonClick: "buttonClick" }, host: { listeners: { "click": "onClick($event)", "blur": "onBlur()", "keyup": "updateOnKeyUp($event)" }, properties: { "attr.role": "this.role", "class.igx-button--focused": "this.focused", "class.igx-button--disabled": "this.disabled", "attr.disabled": "this.disabledAttribute" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxButtonBaseDirective, decorators: [{
type: Directive
}], ctorParameters: () => [], propDecorators: { buttonClick: [{
type: Output
}], role: [{
type: HostBinding,
args: ['attr.role']
}], onClick: [{
type: HostListener,
args: ['click', ['$event']]
}], onBlur: [{
type: HostListener,
args: ['blur']
}], focused: [{
type: HostBinding,
args: ['class.igx-button--focused']
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}, {
type: HostBinding,
args: ['class.igx-button--disabled']
}], disabledAttribute: [{
type: HostBinding,
args: ['attr.disabled']
}], updateOnKeyUp: [{
type: HostListener,
args: ['keyup', ['$event']]
}] } });
const IgxButtonType = {
...IgxBaseButtonType,
FAB: 'fab'
};
/**
* The Button directive provides the Ignite UI Button functionality to every component that's intended to be used as a button.
*
* @igxModule IgxButtonModule
*
* @igxParent Data Entry & Display
*
* @igxTheme igx-button-theme
*
* @igxKeywords button, span, div, click
*
* @remarks
* The Ignite UI Button directive is intended to be used by any button, span or div and turn it into a fully functional button.
*
* @example
* ```html
* <button type="button" igxButton="outlined">A Button</button>
* ```
*/
class IgxButtonDirective extends IgxButtonBaseDirective {
emitSelected() {
this.buttonSelected.emit({
button: this
});
}
/**
* Gets or sets whether the button is selected.
* Mainly used in the IgxButtonGroup component and it will have no effect if set separately.
*
* @example
* ```html
* <button type="button" igxButton="flat" [selected]="button.selected"></button>
* ```
*/
set selected(value) {
if (this._selected !== value) {
this._selected = value;
this._renderer.setAttribute(this.nativeElement, 'data-selected', value.toString());
}
}
get selected() {
return this._selected;
}
constructor() {
super();
this._renderer = inject(Renderer2);
/**
* Called when the button is selected.
*/
this.buttonSelected = new EventEmitter();
/**
* @hidden
* @internal
*/
this._cssClass = 'igx-button';
/**
* @hidden
* @internal
*/
this._selected = false;
}
/**
* Sets the type of the button.
*
* @example
* ```html
* <button type="button" igxButton="outlined"></button>
* ```
*/
set type(type) {
const t = type ? type : IgxButtonType.Flat;
if (this._type !== t) {
this._type = t;
}
}
/**
* Sets the `aria-label` attribute.
*
* @example
* ```html
* <button type="button" igxButton="flat" igxLabel="Label"></button>
* ```
*/
set label(value) {
this._label = value || this._label;
this._renderer.setAttribute(this.nativeElement, 'aria-label', this._label);
}
/**
* @hidden
* @internal
*/
get flat() {
return this._type === IgxButtonType.Flat;
}
/**
* @hidden
* @internal
*/
get contained() {
return this._type === IgxButtonType.Contained;
}
/**
* @hidden
* @internal
*/
get outlined() {
return this._type === IgxButtonType.Outlined;
}
/**
* @hidden
* @internal
*/
get fab() {
return this._type === IgxButtonType.FAB;
}
/**
* @hidden
* @internal
*/
select() {
this.selected = true;
}
/**
* @hidden
* @internal
*/
deselect() {
this.selected = false;
this.focused = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.2", type: IgxButtonDirective, isStandalone: true, selector: "[igxButton]", inputs: { selected: ["selected", "selected", booleanAttribute], type: ["igxButton", "type"], label: ["igxLabel", "label"] }, outputs: { buttonSelected: "buttonSelected" }, host: { listeners: { "click": "emitSelected()" }, properties: { "class.igx-button": "this._cssClass", "class.igx-button--flat": "this.flat", "class.igx-button--contained": "this.contained", "class.igx-button--outlined": "this.outlined", "class.igx-button--fab": "this.fab" } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxButtonDirective, decorators: [{
type: Directive,
args: [{
selector: '[igxButton]',
standalone: true
}]
}], ctorParameters: () => [], propDecorators: { buttonSelected: [{
type: Output
}], _cssClass: [{
type: HostBinding,
args: ['class.igx-button']
}], emitSelected: [{
type: HostListener,
args: ['click']
}], selected: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], type: [{
type: Input,
args: ['igxButton']
}], label: [{
type: Input,
args: ['igxLabel']
}], flat: [{
type: HostBinding,
args: ['class.igx-button--flat']
}], contained: [{
type: HostBinding,
args: ['class.igx-button--contained']
}], outlined: [{
type: HostBinding,
args: ['class.igx-button--outlined']
}], fab: [{
type: HostBinding,
args: ['class.igx-button--fab']
}] } });
const LabelPosition = {
BEFORE: 'before',
AFTER: 'after'
};
let nextId = 0;
class CheckboxBaseDirective {
get checked() {
return this._checked;
}
set checked(value) {
if (this._checked !== value) {
this._checked = value;
this._onChangeCallback(this._checked);
}
}
/**
* Returns reference to the `nativeElement` of the igx-checkbox/igx-switch.
*
* @example
* ```typescript
* let nativeElement = this.component.nativeElement;
* ```
*/
get nativeElement() {
return this.nativeInput.nativeElement;
}
constructor() {
this.cdr = inject(ChangeDetectorRef);
this.themeToken = inject(THEME_TOKEN);
this.ngControl = inject(NgControl, { optional: true, self: true });
/**
* An event that is emitted after the checkbox state is changed.
* Provides references to the `IgxCheckboxComponent` and the `checked` property as event arguments.
*/
// eslint-disable-next-line @angular-eslint/no-output-native
this.change = new EventEmitter();
/**
* @hidden
* @internal
*/
this.destroy$ = new Subject();
/**
* Sets/gets the `id` of the checkbox component.
* If not set, the `id` of the first checkbox component will be `"igx-checkbox-0"`.
*
* @example
* ```html
* <igx-checkbox id="my-first-checkbox"></igx-checkbox>
* ```
* ```typescript
* let checkboxId = this.checkbox.id;
* ```
*/
this.id = `igx-checkbox-${nextId++}`;
/**
* Sets/gets the id of the `label` element.
* If not set, the id of the `label` in the first checkbox component will be `"igx-checkbox-0-label"`.
*
* @example
* ```html
* <igx-checkbox labelId="Label1"></igx-checkbox>
* ```
* ```typescript
* let labelId = this.component.labelId;
* ```
*/
this.labelId = `${this.id}-label`;
/**
* Sets/gets the value of the `tabindex` attribute.
*
* @example
* ```html
* <igx-checkbox [tabindex]="1"></igx-checkbox>
* ```
* ```typescript
* let tabIndex = this.checkbox.tabindex;
* ```
*/
this.tabindex = null;
/**
* Sets/gets the position of the `label`.
* If not set, the `labelPosition` will have value `"after"`.
*
* @example
* ```html
* <igx-checkbox labelPosition="before"></igx-checkbox>
* ```
* ```typescript
* let labelPosition = this.checkbox.labelPosition;
* ```
*/
this.labelPosition = LabelPosition.AFTER;
/**
* Enables/Disables the ripple effect.
* If not set, `disableRipple` will have value `false`.
*
* @example
* ```html
* <igx-checkbox [disableRipple]="true"></igx-checkbox>
* ```
* ```typescript
* let isRippleDisabled = this.checkbox.desableRipple;
* ```
*/
this.disableRipple = false;
/**
* Sets/gets the `aria-labelledby` attribute.
* If not set, the `aria-labelledby` will be equal to the value of `labelId` attribute.
*
* @example
* ```html
* <igx-checkbox aria-labelledby="Checkbox1"></igx-checkbox>
* ```
* ```typescript
* let ariaLabelledBy = this.checkbox.ariaLabelledBy;
* ```
*/
this.ariaLabelledBy = this.labelId;
/**
* Sets/gets the value of the `aria-label` attribute.
*
* @example
* ```html
* <igx-checkbox aria-label="Checkbox1"></igx-checkbox>
* ```
* ```typescript
* let ariaLabel = this.checkbox.ariaLabel;
* ```
*/
this.ariaLabel = null;
/**
* @hidden
* @internal
*/
this.inputId = `${this.id}-input`;
/**
* @hidden
*/
this._onChangeCallback = noop;
/**
* @hidden
*/
this._onTouchedCallback = noop;
/**
* @hidden
* @internal
*/
this._checked = false;
/**
* @hidden
* @internal
*/
this._required = false;
this.elRef = inject(ElementRef);
this.destroyRef = inject(DestroyRef);
if (this.ngControl !== null) {
this.ngControl.valueAccessor = this;
}
this.theme = this.themeToken.theme;
const themeChange = this.themeToken.onChange((theme) => {
if (this.theme !== theme) {
this.theme = theme;
this.cdr.detectChanges();
}
});
this.destroyRef.onDestroy(() => themeChange.unsubscribe());
}
/**
* Sets/gets whether the checkbox is required.
* If not set, `required` will have value `false`.
*
* @example
* ```html
* <igx-checkbox required></igx-checkbox>
* ```
* ```typescript
* let isRequired = this.checkbox.required;
* ```
*/
get required() {
return this._required || this.nativeElement.hasAttribute('required');
}
set required(value) {
if (!value) {
this.nativeElement.removeAttribute('required');
}
this._required = value;
}
/**
* @hidden
* @internal
*/
ngAfterViewInit() {
if (this.ngControl) {
this.ngControl.statusChanges
.pipe(takeUntil(this.destroy$))
.subscribe(this.updateValidityState.bind(this));
if (this.ngControl.control.validator ||
this.ngControl.control.asyncValidator) {
this._required = this.ngControl?.control?.hasValidator(Validators.required);
this.cdr.detectChanges();
}
}
this.setComponentTheme();
}
setComponentTheme() {
if (!this.themeToken.preferToken) {
const theme = getComponentTheme(this.elRef.nativeElement);
if (theme && theme !== this.theme) {
this.theme = theme;
this.cdr.markForCheck();
}
}
}
/** @hidden @internal */
onKeyUp(event) {
event.stopPropagation();
this.focused = true;
}
/** @hidden @internal */
_onCheckboxClick(event) {
// Since the original checkbox is hidden and the label
// is used for styling and to change the checked state of the checkbox,
// we need to prevent the checkbox click event from bubbling up
// as it gets triggered on label click
// NOTE: The above is no longer valid, as the native checkbox is not labeled
// by the SVG anymore.
if (this.disabled || this.readonly) {
// readonly prevents the component from changing state (see toggle() method).
// However, the native checkbox can still be activated through user interaction (focus + space, label click)
// Prevent the native change so the input remains in sync
event.preventDefault();
return;
}
this.nativeElement.focus();
this.indeterminate = false;
this.checked = !this.checked;
this.updateValidityState();
// K.D. March 23, 2021 Emitting on click and not on the setter because otherwise every component
// bound on change would have to perform self checks for weather the value has changed because
// of the initial set on initialization
this.change.emit({
checked: this.checked,
value: this.value,
owner: this,
});
}
/**
* @hidden
* @internal
*/
get ariaChecked() {
if (this.indeterminate) {
return 'mixed';
}
else {
return this.checked;
}
}
/** @hidden @internal */
_onCheckboxChange(event) {
// We have to stop the original checkbox change event
// from bubbling up since we emit our own change event
event.stopPropagation();
}
/** @hidden @internal */
onBlur() {
this.focused = false;
this._onTouchedCallback();
this.updateValidityState();
}
/** @hidden @internal */
writeValue(value) {
this._checked = value;
}
/** @hidden @internal */
get labelClass() {
switch (this.labelPosition) {
case LabelPosition.BEFORE:
return `${this.cssClass}__label--before`;
case LabelPosition.AFTER:
default:
return `${this.cssClass}__label`;
}
}
/** @hidden @internal */
registerOnChange(fn) {
this._onChangeCallback = fn;
}
/** @hidden @internal */
registerOnTouched(fn) {
this._onTouchedCallback = fn;
}
/** @hidden @internal */
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/** @hidden @internal */
getEditElement() {
return this.nativeInput.nativeElement;
}
/**
* @hidden
* @internal
*/
updateValidityState() {
if (this.ngControl) {
if (!this.disabled &&
!this.readonly &&
(this.ngControl.control.touched || this.ngControl.control.dirty)) {
// the control is not disabled and is touched or dirty
this.invalid = this.ngControl.invalid;
}
else {
// if the control is untouched, pristine, or disabled, its state is initial. This is when the user did not interact
// with the checkbox or when the form/control is reset
this.invalid = false;
}
}
else {
this.checkNativeValidity();
}
}
/**
* A function to assign a native validity property of a checkbox.
* This should be used when there's no ngControl
*
* @hidden
* @internal
*/
checkNativeValidity() {
if (!this.disabled &&
this._required &&
!this.checked &&
!this.readonly) {
this.invalid = true;
}
else {
this.invalid = false;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CheckboxBaseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.2", type: CheckboxBaseDirective, isStandalone: true, inputs: { checked: ["checked", "checked", booleanAttribute], id: "id", labelId: "labelId", value: "value", name: "name", tabindex: "tabindex", labelPosition: "labelPosition", disableRipple: ["disableRipple", "disableRipple", booleanAttribute], ariaLabelledBy: ["aria-labelledby", "ariaLabelledBy"], ariaLabel: ["aria-label", "ariaLabel"], required: ["required", "required", booleanAttribute] }, outputs: { change: "change" }, host: { listeners: { "keyup": "onKeyUp($event)", "click": "_onCheckboxClick($event)", "blur": "onBlur()" }, properties: { "attr.id": "this.id" } }, viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["checkbox"], descendants: true, static: true }, { propertyName: "nativeLabel", first: true, predicate: ["label"], descendants: true, static: true }, { propertyName: "placeholderLabel", first: true, predicate: ["placeholderLabel"], descendants: true, static: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CheckboxBaseDirective, decorators: [{
type: Directive
}], ctorParameters: () => [], propDecorators: { change: [{
type: Output
}], nativeInput: [{
type: ViewChild,
args: ['checkbox', { static: true }]
}], nativeLabel: [{
type: ViewChild,
args: ['label', { static: true }]
}], checked: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], placeholderLabel: [{
type: ViewChild,
args: ['placeholderLabel', { static: true }]
}], id: [{
type: HostBinding,
args: ['attr.id']
}, {
type: Input
}], labelId: [{
type: Input
}], value: [{
type: Input
}], name: [{
type: Input
}], tabindex: [{
type: Input
}], labelPosition: [{
type: Input
}], disableRipple: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], ariaLabelledBy: [{
type: Input,
args: ['aria-labelledby']
}], ariaLabel: [{
type: Input,
args: ['aria-label']
}], required: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], onKeyUp: [{
type: HostListener,
args: ['keyup', ['$event']]
}], _onCheckboxClick: [{
type: HostListener,
args: ['click', ['$event']]
}], onBlur: [{
type: HostListener,
args: ['blur']
}] } });
const IgxDividerType = {
SOLID: 'solid',
DASHED: 'dashed'
};
let NEXT_ID$1 = 0;
class IgxDividerDirective {
constructor() {
/**
* Sets/gets the `id` of the divider.
* If not set, `id` will have value `"igx-divider-0"`;
* ```html
* <igx-divider id="my-divider"></igx-divider>
* ```
* ```typescript
* let dividerId = this.divider.id;
* ```
*/
this.id = `igx-divider-${NEXT_ID$1++}`;
/**
* Sets the value of `role` attribute.
* If not the default value of `separator` will be used.
*/
this.role = 'separator';
/**
* Sets the type of the divider. The default value
* is `default`. The divider can also be `dashed`;
* ```html
* <igx-divider type="dashed"></igx-divider>
* ```
*/
this.type = IgxDividerType.SOLID;
/**
* If set to `true` and an `inset` value has been provided,
* the divider will start shrinking from both ends.
* ```html
* <igx-divider [middle]="true"></igx-divider>
* ```
*/
this.middle = false;
/**
* Sets the divider in vertical orientation.
* ```html
* <igx-divider [vertical]="true"></igx-divider>
* ```
*/
this.vertical = false;
/**
* Sets the value of the `inset` attribute.
* If not provided it will be set to `'0'`.
* ```html
* <igx-divider inset="16px"></igx-divider>
* ```
*/
this._inset = '0';
}
get isDashed() {
return this.type === IgxDividerType.DASHED;
}
/**
* Sets the inset of the divider from the side(s).
* If the divider attribute `middle` is set to `true`,
* it will inset the divider on both sides.
* ```typescript
* this.divider.inset = '32px';
* ```
*/
set inset(value) {
this._inset = value;
}
/**
* Gets the current divider inset in terms of
* inset-inline-start representation as applied to the divider.
* ```typescript
* const inset = this.divider.inset;
* ```
*/
get inset() {
return this._inset;
}
/**
* A getter that returns `true` if the type of the divider is `default`;
* ```typescript
* const isDefault = this.divider.isDefault;
* ```
*/
get isSolid() {
return this.type === IgxDividerType.SOLID;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDividerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.2", type: IgxDividerDirective, isStandalone: true, selector: "igx-divider", inputs: { id: "id", role: "role", type: "type", middle: ["middle", "middle", booleanAttribute], vertical: ["vertical", "vertical", booleanAttribute], inset: "inset" }, host: { properties: { "attr.id": "this.id", "attr.role": "this.role", "class.igx-divider": "this.type", "class.igx-divider--dashed": "this.isDashed", "class.igx-divider--inset": "this.middle", "class.igx-divider--vertical": "this.vertical", "style.--inset": "this.inset" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDividerDirective, decorators: [{
type: Directive,
args: [{
selector: 'igx-divider',
standalone: true
}]
}], propDecorators: { id: [{
type: HostBinding,
args: ['attr.id']
}, {
type: Input
}], role: [{
type: HostBinding,
args: ['attr.role']
}, {
type: Input
}], type: [{
type: HostBinding,
args: ['class.igx-divider']
}, {
type: Input
}], isDashed: [{
type: HostBinding,
args: ['class.igx-divider--dashed']
}], middle: [{
type: HostBinding,
args: ['class.igx-divider--inset']
}, {
type: Input,
args: [{ transform: booleanAttribute }]
}], vertical: [{
type: HostBinding,
args: ['class.igx-divider--vertical']
}, {
type: Input,
args: [{ transform: booleanAttribute }]
}], inset: [{
type: HostBinding,
args: ['style.--inset']
}, {
type: Input
}] } });
// @dynamic
class IgxDefaultDropStrategy {
dropAction(_drag, _drop, _atIndex) { }
}
// @dynamic
class IgxAppendDropStrategy {
constructor(_renderer) {
this._renderer = _renderer;
}
dropAction(drag, drop, _atIndex) {
const dragElement = drag.element.nativeElement;
const dropAreaElement = drop.element.nativeElement;
this._renderer.removeChild(dragElement.parentNode, dragElement);
this._renderer.appendChild(dropAreaElement, dragElement);
}
}
// @dynamic
class IgxPrependDropStrategy {
constructor(_renderer) {
this._renderer = _renderer;
}
dropAction(drag, drop, _atIndex) {
const dragElement = drag.element.nativeElement;
const dropAreaElement = drop.element.nativeElement;
this._renderer.removeChild(dragElement.parentNode, dragElement);
if (dropAreaElement.children.length) {
this._renderer.insertBefore(dropAreaElement, dragElement, dropAreaElement.children[0]);
}
else {
this._renderer.appendChild(dropAreaElement, dragElement);
}
}
}
// @dynamic
class IgxInsertDropStrategy {
constructor(_renderer) {
this._renderer = _renderer;
}
dropAction(drag, drop, atIndex) {
if (drag.element.nativeElement.parentElement === drop.element.nativeElement && atIndex === -1) {
return;
}
const dragElement = drag.element.nativeElement;
const dropAreaElement = drop.element.nativeElement;
this._renderer.removeChild(dragElement.parentNode, dragElement);
if (atIndex !== -1 && dropAreaElement.children.length > atIndex) {
this._renderer.insertBefore(dropAreaElement, dragElement, dropAreaElement.children[atIndex]);
}
else {
this._renderer.appendChild(dropAreaElement, dragElement);
}
}
}
var DragScrollDirection;
(function (DragScrollDirection) {
DragScrollDirection[DragScrollDirection["UP"] = 0] = "UP";
DragScrollDirection[DragScrollDirection["DOWN"] = 1] = "DOWN";
DragScrollDirection[DragScrollDirection["LEFT"] = 2] = "LEFT";
DragScrollDirection[DragScrollDirection["RIGHT"] = 3] = "RIGHT";
})(DragScrollDirection || (DragScrollDirection = {}));
var DragDirection;
(function (DragDirection) {
DragDirection[DragDirection["VERTICAL"] = 0] = "VERTICAL";
DragDirection[DragDirection["HORIZONTAL"] = 1] = "HORIZONTAL";
DragDirection[DragDirection["BOTH"] = 2] = "BOTH";
})(DragDirection || (DragDirection = {}));
class IgxDragLocation {
constructor(_pageX, _pageY) {
this._pageX = _pageX;
this._pageY = _pageY;
this.pageX = parseFloat(_pageX);
this.pageY = parseFloat(_pageY);
}
}
class IgxDragHandleDirective {
constructor() {
this.element = inject((ElementRef));
this.baseClass = true;
/**
* @hidden
*/
this.parentDragElement = null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDragHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxDragHandleDirective, isStandalone: true, selector: "[igxDragHandle]", host: { properties: { "class.igx-drag__handle": "this.baseClass" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDragHandleDirective, decorators: [{
type: Directive,
args: [{
selector: '[igxDragHandle]',
standalone: true
}]
}], propDecorators: { baseClass: [{
type: HostBinding,
args: ['class.igx-drag__handle']
}] } });
class IgxDragIgnoreDirective {
constructor() {
this.element = inject((ElementRef));
this.baseClass = true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDragIgnoreDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxDragIgnoreDirective, isStandalone: true, selector: "[igxDragIgnore]", host: { properties: { "class.igx-drag__ignore": "this.baseClass" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDragIgnoreDirective, decorators: [{
type: Directive,
args: [{
selector: '[igxDragIgnore]',
standalone: true
}]
}], propDecorators: { baseClass: [{
type: HostBinding,
args: ['class.igx-drag__ignore']
}] } });
class IgxDragDirective {
/**
* - Save data inside the `igxDrag` directive. This can be set when instancing `igxDrag` on an element.
* ```html
* <div [igxDrag]="{ source: myElement }"></div>
* ```
*
* @memberof IgxDragDirective
*/
set data(value) {
this._data = value;
}
get data() {
return this._data;
}
/**
* Gets the current location of the element relative to the page.
*/
get location() {
return new IgxDragLocation(this.pageX, this.pageY);
}
/**
* Gets the original location of the element before dragging started.
*/
get originLocation() {
return new IgxDragLocation(this.baseOriginLeft, this.baseOriginTop);
}
/**
* @hidden
*/
get pointerEventsEnabled() {
return typeof PointerEvent !== 'undefined';
}
/**
* @hidden
*/
get touchEventsEnabled() {
return 'ontouchstart' in window;
}
/**
* @hidden
*/
get pageX() {
if (this.ghost && this.ghostElement) {
return this.ghostLeft;
}
return this.baseLeft + this.windowScrollLeft;
}
/**
* @hidden
*/
get pageY() {
if (this.ghost && this.ghostElement) {
return this.ghostTop;
}
return this.baseTop + this.windowScrollTop;
}
get baseLeft() {
return this.element.nativeElement.getBoundingClientRect().left;
}
get baseTop() {
return this.element.nativeElement.getBoundingClientRect().top;
}
get baseOriginLeft() {
return this.baseLeft - this.getTransformX(this.element.nativeElement);
}
get baseOriginTop() {
return this.baseTop - this.getTransformY(this.element.nativeElement);
}
set ghostLeft(pageX) {
if (this.ghostElement) {
// We need to take into account marginLeft, since top style does not include margin, but pageX includes the margin.
const ghostMarginLeft = parseInt(this.document.defaultView.getComputedStyle(this.ghostElement)['margin-left'], 10);
// If ghost host is defined it needs to be taken into account.
this.ghostElement.style.left = (pageX - ghostMarginLeft - this._ghostHostX) + 'px';
}
}
get ghostLeft() {
if (this.ghostElement) {
return parseInt(this.ghostElement.style.left, 10) + this._ghostHostX;
}
}
set ghostTop(pageY) {
if (this.ghostElement) {
// We need to take into account marginTop, since top style does not include margin, but pageY includes the margin.
const ghostMarginTop = parseInt(this.document.defaultView.getComputedStyle(this.ghostElement)['margin-top'], 10);
// If ghost host is defined it needs to be taken into account.
this.ghostElement.style.top = (pageY - ghostMarginTop - this._ghostHostY) + 'px';
}
}
get ghostTop() {
if (this.ghostElement) {
return parseInt(this.ghostElement.style.top, 10) + this._ghostHostY;
}
}
get windowScrollTop() {
return this.document.documentElement.scrollTop || window.scrollY;
}
get windowScrollLeft() {
return this.document.documentElement.scrollLeft || window.scrollX;
}
get windowScrollHeight() {
return this.document.documentElement.scrollHeight;
}
get windowScrollWidth() {
return this.document.documentElement.scrollWidth;
}
/**
* Sets the offset of the dragged element relative to the mouse in pixels.
* By default it's taking the relative position to the mouse when the drag started and keeps it the same.
* ```html
* <div #hostDiv></div>
* <div igxDrag [ghostOffsetX]="0">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
set ghostOffsetX(value) {
this._offsetX = parseInt(value, 10);
}
get ghostOffsetX() {
return this._offsetX !== undefined ? this._offsetX : this._defaultOffsetX;
}
/**
* Sets the offset of the dragged element relative to the mouse in pixels.
* By default it's taking the relative position to the mouse when the drag started and keeps it the same.
* ```html
* <div #hostDiv></div>
* <div igxDrag [ghostOffsetY]="0">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
set ghostOffsetY(value) {
this._offsetY = parseInt(value, 10);
}
get ghostOffsetY() {
return this._offsetY !== undefined ? this._offsetY : this._defaultOffsetY;
}
constructor() {
/**
* Sets the tolerance in pixels before drag starts.
* By default the drag starts after the draggable element is moved by 5px.
* ```html
* <div igxDrag [dragTolerance]="100">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
this.dragTolerance = 5;
/**
* Sets the directions that the element can be dragged.
* By default it is set to both horizontal and vertical directions.
* ```html
* <div igxDrag [dragDirection]="dragDir">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public dragDir = DragDirection.HORIZONTAL;
* ```
*
* @memberof IgxDragDirective
*/
this.dragDirection = DragDirection.BOTH;
/**
* Sets whether the base element should be moved, or a ghost element should be rendered that represents it instead.
* By default it is set to `true`.
* If it is set to `false` when dragging the base element is moved instead and no ghost elements are rendered.
* ```html
* <div igxDrag [ghost]="false">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
this.ghost = true;
/**
* Sets a custom class that will be added to the `ghostElement` element.
* ```html
* <div igxDrag [ghostClass]="'ghostElement'">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
this.ghostClass = '';
/**
* Set styles that will be added to the `ghostElement` element.
* ```html
* <div igxDrag [ghostStyle]="{'--ig-size': 'var(--ig-size-small)'}">
* <span>Drag Me!</span>
* </div>
* ```
*
* @memberof IgxDragDirective
*/
this.ghostStyle = {};
/**
* Overrides the scroll container of the dragged element. By default its the window.
*/
this.scrollContainer = null;
/**
* Event triggered when the draggable element drag starts.
* ```html
* <div igxDrag (dragStart)="onDragStart()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public onDragStart(){
* alert("The drag has stared!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.dragStart = new EventEmitter();
/**
* Event triggered when the draggable element has been moved.
* ```html
* <div igxDrag (dragMove)="onDragMove()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public onDragMove(){
* alert("The element has moved!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.dragMove = new EventEmitter();
/**
* Event triggered when the draggable element is released.
* ```html
* <div igxDrag (dragEnd)="onDragEnd()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public onDragEnd(){
* alert("The drag has ended!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.dragEnd = new EventEmitter();
/**
* Event triggered when the draggable element is clicked.
* ```html
* <div igxDrag (dragClick)="onDragClick()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public onDragClick(){
* alert("The element has been clicked!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.dragClick = new EventEmitter();
/**
* Event triggered when the drag ghost element is created.
* ```html
* <div igxDrag (ghostCreate)="ghostCreated()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public ghostCreated(){
* alert("The ghost has been created!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.ghostCreate = new EventEmitter();
/**
* Event triggered when the drag ghost element is created.
* ```html
* <div igxDrag (ghostDestroy)="ghostDestroyed()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public ghostDestroyed(){
* alert("The ghost has been destroyed!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.ghostDestroy = new EventEmitter();
/**
* Event triggered after the draggable element is released and after its animation has finished.
* ```html
* <div igxDrag (transitioned)="onMoveEnd()">
* <span>Drag Me!</span>
* </div>
* ```
* ```typescript
* public onMoveEnd(){
* alert("The move has ended!");
* }
* ```
*
* @memberof IgxDragDirective
*/
this.transitioned = new EventEmitter();
/**
* @hidden
*/
this.baseClass = true;
/**
* @hidden
*/
this.selectDisabled = false;
/**
* @hidden
*/
this.defaultReturnDuration = '0.5s';
/**
* @hidden
*/
this.animInProgress = false;
this.ghostContext = null;
this._startX = 0;
this._startY = 0;
this._lastX = 0;
this._lastY = 0;
this._dragStarted = false;
this._ghostHostX = 0;
this._ghostHostY = 0;
this._pointerDownId = null;
this._clicked = false;
this._lastDropArea = null;
this._destroy = new Subject();
this._removeOnDestroy = true;
this._scrollContainer = null;
this._originalScrollContainerWidth = 0;
this._originalScrollContainerHeight = 0;
this._scrollContainerStep = 5;
this._scrollContainerStepMs = 10;
this._scrollContainerThreshold = 25;
this._containerScrollIntervalId = null;
this.document = inject(DOCUMENT);
this.cdr = inject(ChangeDetectorRef);
this.element = inject(ElementRef);
this.viewContainer = inject(ViewContainerRef);
this.zone = inject(NgZone);
this.renderer = inject(Renderer2);
this.platformUtil = inject(PlatformUtil);
this.onTransitionEnd = this.onTransitionEnd.bind(this);
this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this);
this.onPointerLost = this.onPointerLost.bind(this);
}
/**
* @hidden
*/
ngAfterContentInit() {
if (!this.dragHandles || !this.dragHandles.length) {
// Set user select none to the whole draggable element if no drag handles are defined.
this.selectDisabled = true;
}
// Bind events
this.zone.runOutsideAngular(() => {
if (!this.platformUtil.isBrowser) {
return;
}
const targetElements = this.dragHandles && this.dragHandles.length
? this.dragHandles
.filter(item => item.parentDragElement === null)
.map(item => {
item.parentDragElement = this.element.nativeElement;
return item.element.nativeElement;
})
: [this.element.nativeElement];
targetElements.forEach((element) => {
if (this.pointerEventsEnabled) {
fromEvent(element, 'pointerdown').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerDown(res));
fromEvent(element, 'pointermove').pipe(throttle(() => interval(0, animationFrameScheduler)), takeUntil(this._destroy)).subscribe((res) => this.onPointerMove(res));
fromEvent(element, 'pointerup').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerUp(res));
if (!this.ghost) {
// Do not bind `lostpointercapture` to the target, because we will bind it on the ghost later.
fromEvent(element, 'lostpointercapture').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerLost(res));
}
}
else if (this.touchEventsEnabled) {
fromEvent(element, 'touchstart').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerDown(res));
}
else {
// We don't have pointer events and touch events. Use then mouse events.
fromEvent(element, 'mousedown').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerDown(res));
}
});
// We should bind to document events only once when there are no pointer events.
if (!this.pointerEventsEnabled && this.touchEventsEnabled) {
fromEvent(this.document.defaultView, 'touchmove').pipe(throttle(() => interval(0, animationFrameScheduler)), takeUntil(this._destroy)).subscribe((res) => this.onPointerMove(res));
fromEvent(this.document.defaultView, 'touchend').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerUp(res));
}
else if (!this.pointerEventsEnabled) {
fromEvent(this.document.defaultView, 'mousemove').pipe(throttle(() => interval(0, animationFrameScheduler)), takeUntil(this._destroy)).subscribe((res) => this.onPointerMove(res));
fromEvent(this.document.defaultView, 'mouseup').pipe(takeUntil(this._destroy))
.subscribe((res) => this.onPointerUp(res));
}
this.element.nativeElement.addEventListener('transitionend', this.onTransitionEnd);
});
// Set transition duration to 0s. This also helps with setting `visibility: hidden` to the base to not lag.
this.element.nat