@angular/core#AfterContentInit TypeScript Examples

The following examples show how to use @angular/core#AfterContentInit. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: accordion.component.ts    From canopy with Apache License 2.0 6 votes vote down vote up
@Component({
  selector: 'lg-accordion',
  templateUrl: './accordion.component.html',
  styleUrls: [ './accordion.component.scss' ],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [ { provide: LG_ACCORDION, useExisting: LgAccordionComponent } ],
})
export class LgAccordionComponent implements AfterContentInit {
  @HostBinding('class.lg-accordion') class = true;
  @HostBinding('id') @Input() id = `lg-accordion-${nextUniqueId++}`;
  @Input() headingLevel: HeadingLevel;
  @Input() multi = true;

  @ContentChildren(forwardRef(() => LgAccordionPanelHeadingComponent), {
    descendants: true,
  })
  panelHeadings: QueryList<LgAccordionPanelHeadingComponent>;

  ngAfterContentInit() {
    this.panelHeadings.forEach(panelHeading => {
      panelHeading.headingLevel = this.headingLevel;
    });
  }
}
Example #2
Source File: ngx-detect-hide-on-scroll.directive.ts    From Elastos.Essentials.App with MIT License 6 votes vote down vote up
/**
 * Use this on scrollable elements to notify [ngxHideOnScroll] on render updates (e.g *ngIfs)
 */
@Directive({ selector: '[ngxDetectHideOnScroll]' })
export class NgxDetectHideOnScrollDirective implements AfterContentInit, OnDestroy {
  constructor(private s: NgxHideOnScrollService) {}

  ngAfterContentInit() {
    // wait a tick to let initted element render
    setTimeout(() => this.s.scrollingElementsDetected$.next());
  }
  ngOnDestroy() {
    this.s.scrollingElementsDetected$.next();
  }
}
Example #3
Source File: option-group.component.ts    From alauda-ui with MIT License 6 votes vote down vote up
@Component({
  selector: 'aui-option-group',
  templateUrl: './option-group.component.html',
  styleUrls: ['./option-group.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: false,
})
export class OptionGroupComponent<T> implements AfterContentInit {
  @ContentChildren(forwardRef(() => OptionComponent))
  options: QueryList<OptionComponent<T>>;

  hasVisibleOption$: Observable<boolean>;

  ngAfterContentInit() {
    this.hasVisibleOption$ = this.options.changes.pipe(
      startWith(this.options),
      switchMap((options: QueryList<OptionComponent<T>>) =>
        options.length > 0
          ? combineLatest(options.map(node => node.visible$))
          : of([false]),
      ),
      map(visible => visible.some(Boolean)),
      publishRef(),
    );
  }
}
Example #4
Source File: focus-key-list.directive.ts    From sba-angular with MIT License 6 votes vote down vote up
@Directive({
    selector: "[sqFocusKeyList]"
})
export class FocusKeyListDirective implements OnChanges, AfterContentInit {
    @Input() activeItem = -1;
    @Input() withWrap = true;
    @Output() itemSelect = new EventEmitter<number>();
    @HostBinding("attr.role") role = "list";
    @ContentChildren(FocusKeyListItemDirective) components: QueryList<FocusKeyListItemDirective>;
    protected keyManager: FocusKeyManager<FocusKeyListItemDirective>;

    ngOnChanges() {
        if (this.keyManager) {
            this.keyManager.setActiveItem(this.activeItem);
        }
    }

    ngAfterContentInit() {
        this.keyManager = new FocusKeyManager<FocusKeyListItemDirective>(this.components);
        if (this.withWrap) {
            this.keyManager.withWrap();
        }
        if (this.activeItem >= 0 && this.components.length > 0) {
            Utils.delay().then(() => {
                this.keyManager.setActiveItem(this.activeItem);
            });
        }
    }

    @HostListener("keydown", ["$event"])
    onKeydown(event) {
        this.keyManager.onKeydown(event);
        this.itemSelect.emit(this.keyManager.activeItemIndex !== null ? this.keyManager.activeItemIndex : undefined);
    }
}
Example #5
Source File: input-group.component.ts    From alauda-ui with MIT License 6 votes vote down vote up
@Component({
  selector: 'aui-input-group',
  templateUrl: './input-group.component.html',
  styleUrls: ['./input-group.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: false,
})
export class InputGroupComponent implements AfterContentInit {
  bem: Bem = buildBem('aui-input-group');

  @ContentChildren(InputAddonBeforeDirective)
  private readonly addonBeforeRefs: QueryList<InputAddonBeforeDirective>;

  @ContentChildren(InputAddonAfterDirective)
  private readonly addonAfterRefs: QueryList<InputAddonAfterDirective>;

  @ContentChildren(InputPrefixDirective)
  private readonly prefixRefs: QueryList<InputPrefixDirective>;

  @ContentChildren(InputSuffixDirective)
  private readonly suffixRefs: QueryList<InputSuffixDirective>;

  @ContentChild(InputComponent, { static: false })
  inputRef: InputComponent;

  hasAddonBefore$: Observable<boolean>;
  hasAddonAfter$: Observable<boolean>;
  hasPrefix$: Observable<boolean>;
  hasSuffix$: Observable<boolean>;

  ngAfterContentInit() {
    this.hasAddonBefore$ = watchContentExist(this.addonBeforeRefs);
    this.hasAddonAfter$ = watchContentExist(this.addonAfterRefs);
    this.hasPrefix$ = watchContentExist(this.prefixRefs);
    this.hasSuffix$ = watchContentExist(this.suffixRefs);
  }
}
Example #6
Source File: network.component.ts    From blockcore-hub with MIT License 5 votes vote down vote up
@Component({
    selector: 'app-network',
    templateUrl: './network.component.html',
    styleUrls: ['./network.component.scss'],
    encapsulation: ViewEncapsulation.None
})
export class NetworkComponent implements OnInit, OnDestroy, AfterContentInit {
    @HostBinding('class.network') hostClass = true;

    columns = 4;

    gridByBreakpoint = {
        xl: 8,
        lg: 6,
        md: 4,
        sm: 2,
        xs: 1
    };

    private mediaObservable;

    constructor(
        private globalService: GlobalService,
        private apiService: ApiService,
        private readonly cd: ChangeDetectorRef,
        public mediaObserver: MediaObserver,
        public router: Router,
        public walletService: WalletService) {

    }

    ngOnInit() {
    }

    ngAfterContentInit() {
        // There is a bug with this, does not always trigger when navigating back and forth, and resizing.
        // This means the number of grids sometimes defaults to 4, even though the screen is small.
        this.mediaObservable = this.mediaObserver.media$.subscribe((change: MediaChange) => {
            this.columns = this.gridByBreakpoint[change.mqAlias];
        });
    }

    ngOnDestroy() {
        this.mediaObservable.unsubscribe();
    }

    details() {
        this.router.navigateByUrl('/network/details');
    }
}
Example #7
Source File: np-menubar.component.ts    From np-ui-lib with MIT License 5 votes vote down vote up
@Component({
  selector: "np-menubar",
  templateUrl: "./np-menubar.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
})
export class NpMenubarComponent implements AfterContentInit, OnDestroy {
  private static controlCount = 1;

  @Input() items: NpMenuItem[];
  @Input() orientation: 'horizontal' | 'vertical';
  @Input() isPanelMenu: boolean;
  @Input() styleClass: string;
  @Input() inputId: string = `np-menubar_${NpMenubarComponent.controlCount++}`;

  @Output() onClickMenuItem: EventEmitter<any> = new EventEmitter();

  subscription: Subscription;

  ngAfterContentInit(): void {
    if (!this.isPanelMenu) {
      this.subscription = this.onClickMenuItem.subscribe(() => {
        this.items.forEach((element: NpMenuItem) => {
          if (element.items) {
            this._collapseMenu(element);
          }
        });
      });
      this.orientation = this.orientation ? this.orientation : "vertical";
    } else {
      this.orientation = null;
    }
  }

  ngOnDestroy(): void {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }

  _collapseMenu(item: NpMenuItem): void {
    item.items.forEach((element: NpMenuItem) => {
      if (element.items) {
        this._collapseMenu(element);
      }
    });
    item.isChildVisible = false;
  }

  _onClickMenuItem(item: NpMenuItem): void {
    this.onClickMenuItem.emit(item);
  }
}
Example #8
Source File: network.component.ts    From EXOS-Core with MIT License 5 votes vote down vote up
@Component({
    selector: 'app-network',
    templateUrl: './network.component.html',
    styleUrls: ['./network.component.scss'],
    encapsulation: ViewEncapsulation.None
})
export class NetworkComponent implements OnInit, OnDestroy, AfterContentInit {
    @HostBinding('class.network') hostClass = true;

    columns = 4;

    gridByBreakpoint = {
        xl: 8,
        lg: 6,
        md: 4,
        sm: 2,
        xs: 1
    };

    private mediaObservable;

    constructor(
        private globalService: GlobalService,
        private apiService: ApiService,
        private readonly cd: ChangeDetectorRef,
        public mediaObserver: MediaObserver,
        public router: Router,
        public walletService: WalletService,
        public localeService: LocaleService) {

    }

    ngOnInit() {
    }

    ngAfterContentInit() {
        // There is a bug with this, does not always trigger when navigating back and forth, and resizing.
        // This means the number of grids sometimes defaults to 4, even though the screen is small.
        this.mediaObservable = this.mediaObserver.media$.subscribe((change: MediaChange) => {
            this.columns = this.gridByBreakpoint[change.mqAlias];
        });
    }

    ngOnDestroy() {
        this.mediaObservable.unsubscribe();
    }

    details() {
        this.router.navigateByUrl('/network-details');
    }
}
Example #9
Source File: color-toggle.component.ts    From angular-material-components with MIT License 5 votes vote down vote up
@Component({
  selector: 'ngx-mat-color-toggle',
  templateUrl: './color-toggle.component.html',
  styleUrls: ['./color-toggle.component.scss'],
  host: {
    'class': 'ngx-mat-color-toggle',
    // Always set the tabindex to -1 so that it doesn't overlap with any custom tabindex the
    // consumer may have provided, while still being able to receive focus.
    '[attr.tabindex]': '-1',
    '[class.ngx-mat-color-toggle-active]': 'picker && picker.opened',
    '[class.mat-accent]': 'picker && picker.color === "accent"',
    '[class.mat-warn]': 'picker && picker.color === "warn"',
    '(focus)': '_button.focus()',
  },
  exportAs: 'ngxMatColorPickerToggle',
  encapsulation: ViewEncapsulation.None
})
export class NgxMatColorToggleComponent implements OnInit, AfterContentInit, OnChanges, OnDestroy {

  private _stateChanges = Subscription.EMPTY;

  @Input('for') picker: NgxMatColorPickerComponent;
  @Input() tabIndex: number;

  @Input() get disabled(): boolean {
    if (this._disabled == null && this.picker) {
      return this.picker.disabled;
    }
  }
  set disabled(value: boolean) {
    this._disabled = value;
  }
  private _disabled: boolean;

  @ViewChild('button') _button: MatButton;

  constructor(private _cd: ChangeDetectorRef) { }

  ngOnInit() {
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['picker']) {
      this._watchStateChanges();
    }
  }

  ngOnDestroy() {
    this._stateChanges.unsubscribe();
  }

  ngAfterContentInit() {
    this._watchStateChanges();
  }

  public open(event: Event): void {
    if (this.picker && !this.disabled) {
      this.picker.open();
      event.stopPropagation();
    }
  }

  private _watchStateChanges() {
    const disabled$ = this.picker ? this.picker._disabledChange : of();
    const inputDisabled$ = this.picker && this.picker._pickerInput ?
      this.picker._pickerInput._disabledChange : of();

    const pickerToggled$ = this.picker ?
      merge(this.picker.openedStream, this.picker.closedStream) : of();
    this._stateChanges.unsubscribe();

    this._stateChanges = merge(disabled$, inputDisabled$, pickerToggled$).subscribe(() => this._cd.markForCheck());
  }

}
Example #10
Source File: validators.ts    From alauda-ui with MIT License 5 votes vote down vote up
@Directive({
  selector:
    // eslint-disable-next-line @angular-eslint/directive-selector
    'aui-select[ngModel][includes],aui-select[formControl][includes],aui-select[formControlName][includes]',
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => IncludesDirective),
      multi: true,
    },
  ],
})
export class IncludesDirective<T> implements Validator, AfterContentInit {
  @Input()
  get includes() {
    return this._includes;
  }

  set includes(val: boolean | '') {
    this._includes = coerceAttrBoolean(val);
    if (this.onValidatorChange) {
      this.onValidatorChange();
    }
  }

  @Input()
  trackFn: TrackFn<T>;

  private _includes = false;

  onValidatorChange: () => void;

  constructor(private readonly selectRef: SelectComponent<T>) {}

  ngAfterContentInit() {
    this.selectRef.contentOptions.changes.subscribe(() => {
      if (this.onValidatorChange) {
        this.onValidatorChange();
      }
    });
  }

  registerOnValidatorChange(fn: () => void) {
    this.onValidatorChange = fn;
  }

  validate(control: AbstractControl): ValidationErrors {
    if (!this.selectRef.contentOptions || !control.value) {
      return;
    }
    return !this.includes
      ? null
      : AuiSelectValidators.includes(
          this.selectRef.contentOptions
            .filter(option => !option.disabled)
            .map(option => option.value),
          this.trackFn,
        )(control);
  }
}
Example #11
Source File: tutorial-dialog.component.ts    From bdc-walkthrough with MIT License 5 votes vote down vote up
@Component({
  selector: 'bdc-walk-dialog',
  templateUrl: './tutorial-dialog.component.html',
  styleUrls: ['./tutorial-dialog.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class BdcWalkDialogComponent implements AfterContentInit, OnDestroy, OnChanges {
  @Input() name: string;
  @Input() mustCompleted: { [taskName: string]: any | boolean } = {};
  @Input() mustNotDisplaying: string[] = [];
  @Input() width = '500px';
  @Output() opened = new EventEmitter<void>();
  @Output() closed = new EventEmitter<void>();
  @ViewChild(TemplateRef, {static: true}) templateRef: TemplateRef<any>;

  dialogRef: MatDialogRef<any>;
  componentSubscription: Subscription;

  constructor(private dialog: MatDialog,
              private tutorialService: BdcWalkService) { }

  ngAfterContentInit() {
    this.componentSubscription = this.tutorialService.changes.subscribe(() => this._sync());
  }

  ngOnChanges(): void {
    this._sync();
  }

  ngOnDestroy() {
    if (this.componentSubscription) {
      this.componentSubscription.unsubscribe();
    }

    this._close();
  }

  getValue(taskName: string): any {
    return this.tutorialService.getTaskCompleted(taskName);
  }

  close(setTasks: { [taskName: string]: any | boolean } = {}) {
    this.tutorialService.logUserAction(this.name, BdcDisplayEventAction.UserClosed);
    this.tutorialService.setTaskCompleted(this.name);
    this.tutorialService.setTasks(setTasks);
  }

  private _open() {
    this.dialogRef = this.dialog.open(this.templateRef, {width: this.width, disableClose: true, restoreFocus: false, panelClass: 'bdc-walk-dialog'});
    this.opened.emit();
  }

  private _close() {
    if (this.dialogRef) {
      this.dialogRef.close();
      this.dialogRef = null;
      this.closed.emit();
    }
  }

  private _sync() {
    if (this.name) {
      if (!this.tutorialService.getTaskCompleted(this.name) &&
        !this.tutorialService.disabled &&
        this.tutorialService.evalMustCompleted(this.mustCompleted) &&
        this.tutorialService.evalMustNotDisplaying(this.mustNotDisplaying)) {

        if (!this.dialogRef) {
          this._open();
          this.tutorialService.setIsDisplaying(this.name, true);
        }
      } else if (this.dialogRef) {
        this._close();
        this.tutorialService.setIsDisplaying(this.name, false);
      }
    }
  }
}
Example #12
Source File: breadcrumb.component.ts    From alauda-ui with MIT License 5 votes vote down vote up
@Component({
  selector: 'aui-breadcrumb',
  templateUrl: './breadcrumb.component.html',
  styleUrls: ['./breadcrumb.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: false,
})
export class BreadcrumbComponent implements AfterContentInit {
  @Input()
  get separator() {
    return this._separator;
  }

  set separator(val) {
    if (val === this._separator) {
      return;
    }
    this._separator = val;
    if (this.items) {
      this.items.forEach(item => {
        item.separator = val;
      });
    }
  }

  @Input()
  get separatorIcon() {
    return this._separatorIcon;
  }

  set separatorIcon(val) {
    if (val === this._separatorIcon) {
      return;
    }
    this._separatorIcon = val;
    if (this.items) {
      this.items.forEach(item => {
        item.separatorIcon = val;
      });
    }
  }

  @ContentChildren(BreadcrumbItemComponent)
  items: QueryList<BreadcrumbItemComponent>;

  private _separator = '/';
  private _separatorIcon = '';

  ngAfterContentInit() {
    this.items.forEach(item => {
      item.separator = this.separator;
      item.separatorIcon = this.separatorIcon;
    });
  }
}
Example #13
Source File: modal.component.ts    From canopy with Apache License 2.0 5 votes vote down vote up
@Component({
  selector: 'lg-modal',
  templateUrl: './modal.component.html',
  styleUrls: [ './modal.component.scss' ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LgModalComponent implements OnInit, AfterContentInit, OnDestroy {
  private subscription: Subscription;
  isOpen: boolean;
  @Input() id: string;
  @Output() open: EventEmitter<void> = new EventEmitter();
  @Output() closed: EventEmitter<void> = new EventEmitter();

  @ContentChild(forwardRef(() => LgModalHeaderComponent))
  modalHeaderComponent: LgModalHeaderComponent;
  @ContentChild(forwardRef(() => LgModalBodyComponent))
  modalBodyComponent: LgModalBodyComponent;

  constructor(private cdr: ChangeDetectorRef, private modalService: LgModalService) {}

  @HostListener('keydown', [ '$event' ]) onKeydown(event: KeyboardEvent): void {
    if (event.key === keyName.KEY_ESCAPE && this.isOpen) {
      this.modalService.close(this.id);
    }
  }

  // onOverlayClick and onModalClick add the following functionality:
  // clicking outside the modal closes the modal unless specified
  // otherwise using closeOnOverlayClick.
  // We specifically listen to the `mousedown` event because with
  // the `click` event a user could click inside the modal and
  // drag the mouse on the overlay causing the modal to close.
  @HostListener('mousedown') onOverlayClick(): void {
    this.modalService.close(this.id);
  }

  ngOnInit(): void {
    this.subscription = this.modalService
      .isOpen$(this.id)
      .pipe(
        map(isOpen => {
          this.isOpen = isOpen;

          const bodyEl: HTMLBodyElement = document.querySelector('body');

          if (isOpen) {
            bodyEl.style.overflow = 'hidden';
            this.open.emit();
          } else {
            this.closed.emit();
            bodyEl.style.overflow = '';
          }

          this.cdr.detectChanges();
        }),
      )
      .subscribe();
  }

  ngAfterContentInit(): void {
    this.modalHeaderComponent.id = `lg-modal-header-${this.id}`;
    this.modalHeaderComponent.modalId = this.id;
    this.modalBodyComponent.id = `lg-modal-body-${this.id}`;
  }

  onModalClick(event: Event): void {
    event.stopPropagation();
  }

  ngOnDestroy(): void {
    this.modalService.remove(this.id);
    this.subscription.unsubscribe();
  }
}
Example #14
Source File: autofocus.directive.ts    From jira-clone-angular with MIT License 5 votes vote down vote up
@Directive({
  selector: '[jAutofocus]'
})
export class AutofocusDirective implements AfterContentInit, OnDestroy {
  @Input('jAutofocus') enable: boolean | string;
  @Input() timerDelay: number = BASE_TIMER_DELAY;

  private elementRef: ElementRef;
  private timer: any;

  constructor(elementRef: ElementRef) {
    this.elementRef = elementRef;
    this.timer = null;
  }

  public ngAfterContentInit(): void {
    this.setDefaultValue();
    if (this.enable) {
      this.startFocusWorkflow();
    }
  }

  public ngOnDestroy(): void {
    this.stopFocusWorkflow();
  }

  private setDefaultValue() {
    if (this.enable === false) {
      return;
    }
    this.enable = true;
  }

  private startFocusWorkflow(): void {
    if (this.timer) {
      return;
    }

    this.timer = setTimeout((): void => {
      this.timer = null;
      this.elementRef.nativeElement.focus();
    }, this.timerDelay);
  }

  private stopFocusWorkflow(): void {
    clearTimeout(this.timer);
    this.timer = null;
  }
}
Example #15
Source File: np-data-grid.component.ts    From np-ui-lib with MIT License 4 votes vote down vote up
@Component({
  selector: "np-data-grid",
  templateUrl: "np-data-grid.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
})
export class NpDataGridComponent
  implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
  private static controlCount = 1;

  @Input() inputId: string = `np-data-grid_${NpDataGridComponent.controlCount++}`;
  @Input() columns: Column[];
  @Input() dataSource: BehaviorSubject<DataSource>;
  @Input() height: number;
  @Input() width: number;
  @Input() multiColumnSortEnable: boolean;
  @Input() masterDetailTemplate: TemplateRef<any>;
  @Input() singleRowExpand: boolean = false;
  @Input() expandRowOnClick: boolean = false;
  @Input() singleRowSelectEnable: boolean = false;
  @Input() multiRowSelectEnable: boolean = false;
  @Input() selectRowOnClick: boolean = false;
  @Input() key: string;
  @Input() showColumnChooser: boolean;
  @Input() title: string = "";
  @Input() enableStateStoring: boolean;
  @Input() isReadOnlyStates: boolean = false;
  @Input() noDataMessage: string;
  @Input() showFilters = true;
  @Input() dateFormat = "dd/MM/yyyy";
  @Input() showSummary: boolean = false;
  @Input() summaryTemplate: TemplateRef<any>;
  @Input() allowColumnResize: boolean = false;
  @Input() allowColumnReorder: boolean = false;
  @Input() isServerOperations: boolean = false;
  @Input() isODataOperations: boolean = false;
  @Input() allowExport: boolean = false;
  @Input() isServerExport: boolean = false;
  @Input() showToolBar: boolean = false;
  @Input() pageSize: number = 10;
  @Input() styleClass: string;

  @Output() onInit: EventEmitter<any> = new EventEmitter();
  @Output() onAfterInit: EventEmitter<any> = new EventEmitter();
  @Output() onLoadData: EventEmitter<LoadOptions> = new EventEmitter();
  @Output() onSelect: EventEmitter<any> = new EventEmitter();
  @Output() onDeselect: EventEmitter<any> = new EventEmitter();
  @Output() onRowClick: EventEmitter<any> = new EventEmitter();
  @Output() onStatesUpdate: EventEmitter<any> = new EventEmitter();
  @Output() onServerExport: EventEmitter<LoadOptions> = new EventEmitter();

  @ViewChild("columnChooserTemplate") columnChooserTemplate: TemplateRef<any>;
  @ViewChild("gridPaginator") gridPaginator: NpPaginatorComponent;

  gridColumns: Column[];
  dataSourceClone: DataSource;
  subscription: Subscription;
  currentViewData: any[];
  totalRow: number = 0;
  filtersList: any[];
  sortColumnList: any[];
  filterColumnList: any[];
  isFilterAvailable: boolean;
  enableMasterDetail: boolean = false;
  openRowKeys: any[] = [];
  selectedRowKeys: any[] = [];
  isAllSelected: boolean;
  keyColumnName: string;
  dataTypes = DataTypes;
  sortDirections = SortDirections;
  isOpenColumnChooser: boolean = false;
  visibleColumns: any[] = [];
  stateList: State[];
  currentStateName: string;
  summaryData: any;
  searchColumnsKeyword: string;
  isDataSourceInit: boolean = false;
  _colSpanForDetailRow: number = 0;

  private columnChooserTemplatePortal: TemplatePortal<any>;
  private columnChooserOverlayRef: OverlayRef;

  constructor(
    private filterService: NpFilterService,
    private utilityService: NpGridUtilityService,
    private oDataService: NpODataService,
    private fileService: NpFileService,
    public overlay: Overlay,
    private viewContainerRef: ViewContainerRef,
    private overlayPositionBuilder: OverlayPositionBuilder,
    private dialogService: NpDialogService,
    private translationsService: NpTranslationsService,
    private elementRef: ElementRef
  ) {
    this.sortColumnList = [];
    this.filtersList = Filters;
    this.filterColumnList = [];
    this.stateList = [];
    this.currentStateName = "";
    this.isFilterAvailable = false;
    this.showFilters = true;
  }

  ngOnInit(): void {
    this.onInit.emit();
  }

  ngAfterContentInit(): void {
    if (this.masterDetailTemplate) {
      this.enableMasterDetail = true;
    }
    this._setColumns();
    if (this.key) {
      this.keyColumnName = this.key;
    } else {
      this.keyColumnName = this.gridColumns[0].dataField;
    }
    this._subscribeDataSource();
  }

  ngAfterViewInit(): void {
    const positionStrategy = this.overlayPositionBuilder
      .flexibleConnectedTo(
        this.elementRef.nativeElement.querySelector("#btn-column-chooser")
      )
      .withPositions(TopBottomOverlayPositions);
    this.columnChooserOverlayRef = this.overlay.create({
      positionStrategy,
      hasBackdrop: true,
      backdropClass: "np-grid-backdrop",
      scrollStrategy: this.overlay.scrollStrategies.reposition(),
    });
    this.columnChooserTemplatePortal = new TemplatePortal(
      this.columnChooserTemplate,
      this.viewContainerRef
    );
    this.columnChooserOverlayRef
      .backdropClick()
      .subscribe(() => this._closeColumnChooser());
    this.onAfterInit.emit();
  }

  ngOnDestroy(): void {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }

  getSelectedRowKeys(): any[] {
    return this.selectedRowKeys;
  }

  selectRowByKey(key: any): void {
    if (this.selectedRowKeys.indexOf(key) === -1) {
      this.selectedRowKeys.push(key);
    }
  }

  deselectRowByKey(key: any): void {
    const idx = this.selectedRowKeys.indexOf(key);
    if (idx >= 0) {
      this.selectedRowKeys.splice(idx, 1);
    }
  }

  reset(): void {
    this._setColumns();
    this.filterColumnList = [];
    this.sortColumnList = [];
    this.selectedRowKeys = [];
    this.openRowKeys = [];
    this.isAllSelected = false;
    this.isOpenColumnChooser = false;
    this.currentStateName = "";
    this._closeColumnChooser();
    if (this.isServerOperations) {
      this.gridPaginator.loadPage(1);
    } else {
      this._resetDataSource();
      this.gridPaginator.loadPage(1);
    }
  }

  selectAll(): void {
    this._selectAll();
  }

  deselectAll(): void {
    this._deselectAll();
  }

  hideColumnByIndex(idx: number): void {
    this.gridColumns[idx].visible = false;
    this._setVisibleColumns();
  }

  showColumnByIndex(idx: number): void {
    this.gridColumns[idx].visible = true;
    this._setVisibleColumns();
  }

  hideColumnByDataField(dataField: string): void {
    for (const element of this.gridColumns) {
      if (element.dataField === dataField) {
        element.visible = false;
      }
    }
    this._setVisibleColumns();
  }

  showColumnByDataField(dataField: string): void {
    for (const element of this.gridColumns) {
      if (element.dataField === dataField) {
        element.visible = true;
      }
    }
    this._setVisibleColumns();
  }

  goToPage(pageNumber: number): void {
    this.gridPaginator.loadPage(pageNumber);
  }

  sortByColumn(dataField: string, direction: SortDirections): void {
    const sortColumn = this.utilityService.custFind(
      this.gridColumns,
      (element: Column) => {
        return element.dataField === dataField;
      }
    );
    sortColumn.sortDirection = direction;
    this._onSort(sortColumn);
  }

  filterByColumn(dataField: string, keyword: string, type: FilterTypes): void {
    const filterColumn = this.utilityService.custFind(
      this.gridColumns,
      (element: Column) => {
        return element.dataField === dataField;
      }
    );
    filterColumn.filterString = keyword;
    filterColumn.filterType = type;
    this._onFilter(filterColumn, true);
  }

  getTotalRows(): number {
    return this.totalRow;
  }

  getCurrentPageNumber(): number {
    return this.gridPaginator.currentPage;
  }

  getPageSize(): number {
    return this.pageSize;
  }

  getTotalPages(): number {
    return this.gridPaginator.totalPages;
  }

  closeAllChild(): void {
    this.openRowKeys = [];
  }

  getFilterColumns(): any[] {
    return this.filterColumnList;
  }

  getSortColumns(): any[] {
    return this.sortColumnList;
  }

  getColumns(): Column[] {
    return this._cloneColumns(this.gridColumns);
  }

  setColumns(columns: Column[]): void {
    this.gridColumns = this._cloneColumns(columns);
    const currentFilterColumnList = [];
    for (const element of this.gridColumns) {
      if (
        element.filterOperator &&
        element.filterValue &&
        element.filterValue.toString().length > 0
      ) {
        currentFilterColumnList.push({
          dataField: element.dataField,
          filterOperator: element.filterOperator,
          filterValue: element.filterValue,
          dataType: element.dataType,
        });
      }
    }
    this.filterColumnList = currentFilterColumnList;
    const currentSortColumnList = [];
    for (const element of this.gridColumns) {
      if (element.sortEnable && element.sortDirection) {
        currentSortColumnList.push({
          dataField: element.dataField,
          sortDirection: element.sortDirection,
        });
      }
    }
    this.sortColumnList = currentSortColumnList;
    if (!this.isServerOperations) {
      this._filterDataSource();
      this._sortDataSource();
    }
    this.gridPaginator.loadPage(1);
    this._setVisibleColumns();
    this.selectedRowKeys = [];
    this.openRowKeys = [];
  }

  loadStateByName(stateName: string): void {
    const state = this.stateList.filter((element: State) => element.name === stateName);
    if (state && state.length > 0) {
      this.currentStateName = stateName;
      this.setColumns(state[0].columns);
    } else {
      throw new Error("Data grid State not found");
    }
  }

  getCurrentStateName(): string {
    return this.currentStateName;
  }

  removeAllSorting(): void {
    this._removeAllSorting();
    this.gridPaginator.loadPage(1);
  }

  removeAllFilters(): void {
    this._removeAllFilters();
    this.gridPaginator.loadPage(1);
  }

  getAllState(): State[] {
    return this.stateList;
  }

  setAllState(states: State[]): void {
    this.stateList = states;
  }

  refresh(): void {
    this._onRefresh();
  }

  _subscribeDataSource(): void {
    this.subscription = this.dataSource.subscribe((ds: DataSource) => {
      if (ds === undefined || ds === null) {
        return;
      }
      if (this.isServerOperations) {
        // to export to csv, this change has all data
        if (ds.isAllPages) {
          this.fileService.downloadCSVFile(
            ds.data,
            this.visibleColumns,
            this.dateFormat
          );
          return;
        }
        this.currentViewData = ds.data;
        this.summaryData = ds.summary;
        this.totalRow = ds.total;
        if (this.isAllSelected) {
          for (const element of this.currentViewData) {
            if (
              this.selectedRowKeys.indexOf(element[this.keyColumnName]) === -1
            ) {
              this.selectedRowKeys.push(element[this.keyColumnName]);
            }
          }
        }
      } else {
        this.dataSourceClone = new DataSource(
          ds.data,
          ds.data.length,
          ds.summary
        );
        this.totalRow = ds.data.length;
        this.summaryData = ds.summary;
        this.isDataSourceInit = true;
        this.gridPaginator.refresh();
      }
    });
  }

  _onPageChange(options: any): void {
    if (this.isServerOperations) {
      const loadOpt = new LoadOptions();
      if (this.isODataOperations) {
        const top = options.pageSize;
        const skip = (options.currentPage - 1) * options.pageSize;
        loadOpt.odataQuery = this.oDataService.buildQuery(
          top,
          skip,
          this.sortColumnList,
          this.filterColumnList
        );
        loadOpt.pageNumber = 0;
        loadOpt.pageSize = 0;
        loadOpt.sortColumns = [];
        loadOpt.filterColumns = [];
        loadOpt.isAllPages = false;
      } else {
        loadOpt.pageNumber = options.currentPage;
        loadOpt.pageSize = options.pageSize;
        loadOpt.sortColumns = this.sortColumnList;
        loadOpt.filterColumns = this.filterColumnList;
        loadOpt.isAllPages = false;
        loadOpt.odataQuery = "";
      }
      this.onLoadData.emit(loadOpt);
    } else {
      if (!this.isDataSourceInit) {
        return;
      }
      const start = (options.currentPage - 1) * options.pageSize;
      const end = Math.min(start + options.pageSize - 1, this.totalRow - 1);
      this.currentViewData = this.dataSourceClone.data.slice(start, end + 1);
    }
  }

  _setColumns(): void {
    const result = [];
    for (const element of this.columns) {
      result.push(new Column(element));
    }
    this.gridColumns = result;
    this._setVisibleColumns();
  }

  _setVisibleColumns(): void {
    this.visibleColumns = this.utilityService.custFilter(
      this.gridColumns,
      (element: Column) => element.visible === true
    );
    this.isFilterAvailable =
      this.utilityService.custFilter(this.visibleColumns, (element: Column) => element.filterEnable === true).length > 0;
    this._colSpanForDetailRow =
      this.visibleColumns.length + (this._allowRowSelection() ? 1 : 0) + 1;
  }

  _onCellClick($event: any, column: Column, data: any): void {
    if (column.onCellClick !== undefined) {
      column.onCellClick($event, column, data);
    }
  }

  // on first click ascending, on second click descending and on third click remove sorting
  _onSort(column: Column): void {
    if (!column.sortEnable) {
      return;
    }
    // if sort direction is descending then remove sorting from column
    if (column.sortDirection === SortDirections.Descending) {
      this._removeSortingFromColumn(column);
      return;
    }
    const sortOrder =
      column.sortDirection === SortDirections.Ascending
        ? SortDirections.Descending
        : SortDirections.Ascending;
    if (!this.multiColumnSortEnable) {
      this._removeAllSorting();
    }
    column.sortDirection = sortOrder;
    if (this.multiColumnSortEnable) {
      const list = [];
      for (const element of this.sortColumnList) {
        if (element.dataField !== column.dataField) {
          list.push(element);
        }
      }
      this.sortColumnList = list;
    }
    this.sortColumnList.push({
      dataField: column.dataField,
      sortDirection: column.sortDirection,
    });
    this.selectedRowKeys = [];
    this.isAllSelected = false;
    this.openRowKeys = [];
    if (!this.isServerOperations) {
      this._sortDataSource();
    }
    this.gridPaginator.loadPage(1);
  }

  _sortDataSource(): void {
    let data = this.dataSourceClone.data;
    for (const element of this.sortColumnList) {
      data = this.utilityService.custSort(
        data,
        element.dataField,
        element.sortDirection
      );
    }
    this.dataSourceClone.data = data;
  }

  _removeAllSorting(): void {
    for (const element of this.gridColumns) {
      element.sortDirection = null;
    }
    this.sortColumnList = [];
  }

  _removeSortingFromColumn(column: Column): void {
    column.sortDirection = null;
    const list = [];
    for (const element of this.sortColumnList) {
      if (element.dataField !== column.dataField) {
        list.push(element);
      }
    }
    this.sortColumnList = list;

    if (!this.isServerOperations) {
      this._resetDataSource();
      this._filterDataSource();
      for (const element of this.sortColumnList) {
        this.dataSourceClone.data = this.utilityService.custSort(
          this.dataSourceClone.data,
          element.dataField,
          element.sortDirection
        );
      }
    }
    this.gridPaginator.loadPage(1);
  }

  _resetDataSource(): void {
    this.dataSourceClone.data = this.dataSource.getValue().data;
    this.totalRow = this.dataSourceClone.data.length;
  }

  _onFilter(column: Column, isForceFilter: boolean = false): void {
    if (column.filterOperator && column.filterOperator === FilterTypes.Reset) {
      column.filterOperator = undefined;
      column.filterValue = undefined;
      isForceFilter = true;
    }
    if (
      !isForceFilter &&
      (column.filterValue === undefined ||
        column.filterValue === null ||
        column.filterValue.length === 0 ||
        column.filterOperator === undefined ||
        column.filterOperator === null)
    ) {
      return;
    }
    const currentFilterList = [];
    for (const element of this.gridColumns) {
      if (
        element.filterOperator === undefined ||
        element.filterOperator === null ||
        element.filterValue === undefined ||
        element.filterValue === null ||
        element.filterValue.toString().length === 0
      ) {
        continue;
      } else {
        currentFilterList.push({
          dataField: element.dataField,
          filterOperator: element.filterOperator,
          filterValue: element.filterValue,
          dataType: element.dataType,
        });
      }
    }
    this.filterColumnList = currentFilterList;
    this.selectedRowKeys = [];
    this.isAllSelected = false;
    this.openRowKeys = [];
    if (!this.isServerOperations) {
      this._filterDataSource();
      this._sortDataSource();
    }
    this.gridPaginator.loadPage(1);
  }

  _filterDataSource(): void {
    const data = this.filterService.filterData(
      this.filterColumnList,
      this.dataSource.getValue().data
    );
    this.dataSourceClone.data = data;
    this.totalRow = data.length;
  }

  _removeAllFilters(): void {
    for (const element of this.gridColumns) {
      element.filterOperator = null;
      element.filterValue = null;
    }
    this.filterColumnList = [];
  }

  _onClickExpandRow(data: any): void {
    if (this.expandRowOnClick === true) {
      return;
    }
    const keyValue = data[this.keyColumnName];
    this._expandRow(keyValue);
  }

  _expandRow(keyValue: any): void {
    if (this.singleRowExpand === true) {
      this.openRowKeys = [keyValue];
    } else {
      this.openRowKeys.push(keyValue);
    }
  }

  _onClickCollapseRow(data: any): void {
    if (this.expandRowOnClick === true) {
      return;
    }
    const keyValue = data[this.keyColumnName];
    this._collapseRow(keyValue);
  }

  _collapseRow(keyValue: any): void {
    const idx = this.openRowKeys.indexOf(keyValue);
    this.openRowKeys.splice(idx, 1);
  }

  _onClickSelectAll(checked: boolean): void {
    if (this.singleRowSelectEnable) {
      return;
    }
    if (checked) {
      this._selectAll();
    } else {
      this._deselectAll();
    }
  }

  _deselectAll(): void {
    const selectedRows = this.selectedRowKeys;
    this.selectedRowKeys = [];
    this.isAllSelected = false;
    if (this.onDeselect !== undefined) {
      const event = { data: selectedRows };
      this.onDeselect.emit(event);
    }
  }

  _selectAll(): void {
    const key = this.keyColumnName;
    const selectedKeys = [];
    if (this.isServerOperations) {
      for (const element of this.currentViewData) {
        selectedKeys.push(element[key]);
      }
    } else {
      for (const element of this.dataSourceClone.data) {
        selectedKeys.push(element[key]);
      }
    }
    this.selectedRowKeys = selectedKeys;
    this.isAllSelected = true;
    if (this.onSelect !== undefined) {
      const event = { data: selectedKeys };
      this.onSelect.emit(event);
    }
  }

  _onClickSelectRow(checked: boolean, data: any): void {
    if (this.selectRowOnClick === true) {
      return;
    }
    const keyValue = data[this.keyColumnName];
    this._selectRow(keyValue, checked);
  }

  _selectRow(keyValue: any, checked: boolean): void {
    if (this.singleRowSelectEnable) {
      this.selectedRowKeys = [];
      if (checked) {
        this.selectedRowKeys.push(keyValue);
      }
    } else {
      if (checked) {
        this.selectedRowKeys.push(keyValue);
      } else {
        const idx = this.selectedRowKeys.indexOf(keyValue);
        this.selectedRowKeys.splice(idx, 1);
      }
    }
    if (checked) {
      if (this.onSelect !== undefined) {
        this.onSelect.emit(keyValue);
      }
    } else {
      this.isAllSelected = false;
      if (this.onDeselect !== undefined) {
        this.onDeselect.emit(keyValue);
      }
    }
  }

  _isSelected(data: any): boolean {
    const keyValue = data[this.keyColumnName];
    return this.selectedRowKeys.indexOf(keyValue) > -1;
  }

  _isOpenDetailRow(data: any): boolean {
    if (!this.enableMasterDetail) {
      return false;
    }
    const keyValue = data[this.keyColumnName];
    return this.openRowKeys.indexOf(keyValue) > -1;
  }

  _rowClick(event: any, data: any): void {
    if (this.masterDetailTemplate && this.expandRowOnClick) {
      if (this._isOpenDetailRow(data)) {
        this._collapseRow(data[this.keyColumnName]);
      } else {
        this._expandRow(data[this.keyColumnName]);
      }
    }
    if (
      (this.singleRowSelectEnable || this.multiRowSelectEnable) &&
      this.selectRowOnClick
    ) {
      if (this._isSelected(data)) {
        this._selectRow(data[this.keyColumnName], false);
      } else {
        this._selectRow(data[this.keyColumnName], true);
      }
    }
    if (this.onRowClick) {
      event.data = data;
      this.onRowClick.emit(event);
    }
  }

  _onColumnChoosing(checked: boolean, col: Column): void {
    col.visible = checked;
    this._setVisibleColumns();
  }

  _openColumnChooser(): void {
    if (!this.columnChooserOverlayRef.hasAttached()) {
      this.columnChooserOverlayRef.attach(this.columnChooserTemplatePortal);
    }
  }

  _closeColumnChooser(): void {
    this.columnChooserOverlayRef.detach();
  }

  _dropColumn(event: CdkDragDrop<string[]>): void {
    moveItemInArray(this.gridColumns, event.previousIndex, event.currentIndex);
    this._setVisibleColumns();
  }

  _saveState(): void {
    const columns = this._cloneColumns(this.gridColumns);
    const currentStateName = this.currentStateName;
    let editedState;
    for (const element of this.stateList) {
      if (element.name === currentStateName) {
        element.columns = columns;
        editedState = element;
        this.dialogService.open(
          this.translationsService.translate("Saved_Successfully"),
          new NpDialogConfig({ type: "alert" }),
          null
        );
      }
    }
    if (this.onStatesUpdate) {
      this.onStatesUpdate.emit({ action: "edit", state: editedState });
    }
  }

  _openDialogAddNewState(): void {
    const promptAddNewState = this.dialogService.open(
      this.translationsService.translate("Add_New_State"),
      new NpDialogConfig({ type: "prompt" }),
      null
    );
    promptAddNewState.onClose.subscribe((data) => {
      if (data != undefined && data != null && data.trim().length > 0) {
        this._addState(data);
      }
    });
  }

  _addState(stateName: string): void {
    const name = stateName.trim();
    const state = this.stateList.filter((element: State) => element.name === name);
    if (state && state.length > 0) {
      this.dialogService.open(
        this.translationsService.translate("State_Name_Already_Exists"),
        new NpDialogConfig({ type: "alert" }),
        null
      );
      return;
    }
    const columns = this._cloneColumns(this.gridColumns);
    const newState = new State(name, columns);
    this.stateList.push(newState);
    this.currentStateName = name;
    if (this.onStatesUpdate) {
      this.onStatesUpdate.emit({ action: "add", state: newState });
    }
  }

  _deleteState(): void {
    const currentStateName = this.currentStateName;
    const list = [];
    let deletedState;
    for (const element of this.stateList) {
      if (element.name !== currentStateName) {
        list.push(element);
      } else {
        deletedState = element;
      }
    }
    this.stateList = list;

    if (this.stateList.length > 0) {
      this.currentStateName = this.stateList[0].name;
    } else {
      this.currentStateName = "";
    }
    this._loadState();
    if (this.onStatesUpdate) {
      this.onStatesUpdate.emit({ action: "delete", state: deletedState });
    }
    this.dialogService.open(
      this.translationsService.translate("Deleted_Successfully"),
      new NpDialogConfig({ type: "alert" }),
      null
    );
  }

  _loadState(): void {
    const currentStateName = this.currentStateName;
    if (currentStateName === "") {
      this.reset();
      return;
    }
    this.loadStateByName(currentStateName);
  }

  _cloneColumns(cols: Column[]): Column[] {
    const result: Column[] = [];
    for (const element of cols) {
      result.push(new Column(element));
    }
    return result;
  }

  _onRefresh(): void {
    this.gridPaginator.refresh();
  }

  _onResetColumns(): void {
    this.reset();
  }

  _resizeColumn($event: any, column: Column): void {
    let currentWidth = column.width;
    if (isNaN(currentWidth)) {
      currentWidth =
        $event.source.element.nativeElement.parentElement.offsetWidth;
    }
    column.width = (isNaN(currentWidth) ? 0 : currentWidth) + $event.distance.x;
    $event.source.reset();
  }

  _exportToFile(): void {
    const loadOpt = new LoadOptions();
    if (this.isServerExport) {
      if (this.isODataOperations) {
        loadOpt.odataQuery = this.oDataService.buildQuery(
          0,
          0,
          this.sortColumnList,
          this.filterColumnList,
          "allpages"
        );
        loadOpt.isAllPages = true;
      } else {
        loadOpt.pageNumber = 1;
        loadOpt.pageSize = this.totalRow;
        loadOpt.sortColumns = this.sortColumnList;
        loadOpt.filterColumns = this.filterColumnList;
        loadOpt.isAllPages = true;
      }
      this.onServerExport.emit(loadOpt);
      return;
    }
    if (this.isServerOperations) {
      if (this.isODataOperations) {
        loadOpt.odataQuery = this.oDataService.buildQuery(
          0,
          0,
          this.sortColumnList,
          this.filterColumnList,
          "allpages"
        );
        loadOpt.isAllPages = true;
      } else {
        loadOpt.pageNumber = 1;
        loadOpt.pageSize = this.totalRow;
        loadOpt.sortColumns = this.sortColumnList;
        loadOpt.filterColumns = this.filterColumnList;
        loadOpt.isAllPages = true;
      }
      this.onLoadData.emit(loadOpt);
    } else {
      this.fileService.downloadCSVFile(
        this.dataSourceClone.data,
        this.visibleColumns,
        this.dateFormat
      );
    }
  }

  _showAllColumns(): void {
    this.gridColumns.forEach((element: Column) => {
      element.visible = true;
    });
    this._setVisibleColumns();
  }

  _clearColumnSearch(): void {
    this.searchColumnsKeyword = null;
  }

  _allowRowSelection(): boolean {
    return this.singleRowSelectEnable || this.multiRowSelectEnable;
  }

  _trackBy(index: number): number {
    return index;
  }

  _isAnyRowSelected(): boolean {
    return (
      !this.isAllSelected &&
      this.selectedRowKeys &&
      this.selectedRowKeys.length > 0
    );
  }

  _onClickCheckbox($event: any): void {
    if (this.selectRowOnClick) {
      $event.preventDefault();
    }
  }
}
Example #16
Source File: pager-items-comp.ts    From ui-pager with Apache License 2.0 4 votes vote down vote up
@Component({
    template: ''
})
export abstract class TemplatedItemsComponent implements DoCheck, OnDestroy, AfterContentInit {
    public abstract get nativeElement(): Pager;

    protected templatedItemsView: Pager;
    protected _items: any;
    protected _differ: IterableDiffer<KeyedTemplate>;
    protected _templateMap: Map<string, KeyedTemplate>;
    private _selectedIndex: number;
    @ViewChild('loader', { read: ViewContainerRef, static: false }) loader: ViewContainerRef;

    @Output()
    public setupItemView = new EventEmitter<SetupItemViewArgs>();

    @ContentChild(TemplateRef, { static: false }) itemTemplateQuery: TemplateRef<ItemContext>;

    itemTemplate: TemplateRef<ItemContext>;

    @Input()
    get items() {
        return this._items;
    }

    set items(value: any) {
        this._items = value;
        let needDiffer = true;
        if (value instanceof ObservableArray) {
            needDiffer = false;
        }
        if (needDiffer && !this._differ && isListLikeIterable(value)) {
            this._differ = this._iterableDiffers.find(this._items).create((_index, item) => item);
        }

        this.templatedItemsView.items = this._items;
    }

    @Input()
    get selectedIndex(): number {
        return this._selectedIndex;
    }

    set selectedIndex(value) {
        this._selectedIndex = value;
        this.templatedItemsView.selectedIndex = this._selectedIndex;
    }

    ngAfterViewInit() {
        if (!!this._selectedIndex) {
            setTimeout(() => {
                if (isIOS) {
                    this.templatedItemsView.scrollToIndexAnimated(this._selectedIndex, false);
                }
                this.templatedItemsView.selectedIndex = this._selectedIndex;
            });
        }
    }

    constructor(_elementRef: ElementRef, private _iterableDiffers: IterableDiffers) {
        this.templatedItemsView = _elementRef.nativeElement;

        this.templatedItemsView.on('itemLoading', this.onItemLoading, this);
        this.templatedItemsView.on('itemDisposing', this.onItemDisposing, this);
    }

    ngAfterContentInit() {
        if (Trace.isEnabled()) {
            PagerLog('TemplatedItemsView.ngAfterContentInit()');
        }
        this.setItemTemplates();
    }

    ngOnDestroy() {
        this.templatedItemsView.off('itemLoading', this.onItemLoading, this);
        this.templatedItemsView.off('itemDisposing', this.onItemDisposing, this);
    }

    private setItemTemplates() {
        if (!this.items) return;
        // The itemTemplateQuery may be changed after list items are added that contain <template> inside,
        // so cache and use only the original template to avoid errors.
        this.itemTemplate = this.itemTemplateQuery;

        if (this._templateMap) {
            if (Trace.isEnabled()) {
                PagerLog('Setting templates');
            }

            const templates: KeyedTemplate[] = [];
            this._templateMap.forEach((value) => {
                templates.push(value);
            });
            this.templatedItemsView.itemTemplates = templates;
        }
    }

    public registerTemplate(key: string, template: TemplateRef<ItemContext>) {
        if (Trace.isEnabled()) {
            PagerLog(`registerTemplate for key: ${key}`);
        }

        if (!this._templateMap) {
            this._templateMap = new Map<string, KeyedTemplate>();
        }

        const keyedTemplate = {
            key,
            createView: this.getItemTemplateViewFactory(template)
        };

        this._templateMap.set(key, keyedTemplate);
    }

    @profile
    public onItemLoading(args: ItemEventData) {
        if (!args.view && !this.itemTemplate) {
            return;
        }

        if (!this.items) return;

        const index = args.index;
        const items = (args.object as any).items;
        const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
        let viewRef: EmbeddedViewRef<ItemContext>;

        if (args.view) {
            if (Trace.isEnabled()) {
                PagerLog(`onItemLoading: ${index} - Reusing existing view`);
            }

            viewRef = args.view[NG_VIEW];
            // Getting angular view from original element (in cases when ProxyViewContainer
            // is used NativeScript internally wraps it in a StackLayout)
            if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
                viewRef = args.view.getChildAt(0)[NG_VIEW];
            }

            if (!viewRef && Trace.isEnabled()) {
                PagerError(`ViewReference not found for item ${index}. View recycling is not working`);
            }
        }

        if (!viewRef) {
            if (Trace.isEnabled()) {
                PagerLog(`onItemLoading: ${index} - Creating view from template`);
            }

            viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ItemContext(), 0);
            args.view = getItemViewRoot(viewRef);
            args.view[NG_VIEW] = viewRef;
        }

        this.setupViewRef(viewRef, currentItem, index);

        this.detectChangesOnChild(viewRef, index);
    }

    @profile
    public onItemDisposing(args: ItemEventData) {
        if (!args.view) {
            return;
        }
        let viewRef: EmbeddedViewRef<ItemContext>;

        if (args.view) {
            if (Trace.isEnabled()) {
                PagerLog(`onItemDisposing: ${args.index} - Removing angular view`);
            }

            viewRef = args.view[NG_VIEW];
            // Getting angular view from original element (in cases when ProxyViewContainer
            // is used NativeScript internally wraps it in a StackLayout)
            if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
                viewRef = args.view.getChildAt(0)[NG_VIEW];
            }

            if (!viewRef && Trace.isEnabled()) {
                PagerError(`ViewReference not found for item ${args.index}. View disposing is not working`);
            }
        }

        if (viewRef) {
            if (Trace.isEnabled()) {
                PagerLog(`onItemDisposing: ${args.index} - Disposing view reference`);
            }

            viewRef.destroy();
        }
    }

    public setupViewRef(viewRef: EmbeddedViewRef<ItemContext>, data: any, index: number): void {
        const context = viewRef.context;
        context.$implicit = data;
        context.item = data;
        context.index = index;
        context.even = index % 2 === 0;
        context.odd = !context.even;

        this.setupItemView.next({
            view: viewRef,
            data,
            index,
            context
        });
    }

    protected getItemTemplateViewFactory(template: TemplateRef<ItemContext>): () => View {
        return () => {
            const viewRef = this.loader.createEmbeddedView(template, new ItemContext(), 0);
            const resultView = getItemViewRoot(viewRef);
            resultView[NG_VIEW] = viewRef;

            return resultView;
        };
    }

    @profile
    private detectChangesOnChild(viewRef: EmbeddedViewRef<ItemContext>, index: number) {
        if (Trace.isEnabled()) {
            PagerLog(`Manually detect changes in child: ${index}`);
        }

        viewRef.markForCheck();
        viewRef.detectChanges();
    }

    ngDoCheck() {
        if (this._differ) {
            if (Trace.isEnabled()) {
                PagerLog('ngDoCheck() - execute differ');
            }

            const changes = this._differ.diff(this._items);
            if (changes) {
                if (Trace.isEnabled()) {
                    PagerLog('ngDoCheck() - refresh');
                }

                this.templatedItemsView.refresh();
            }
        }
    }
}
Example #17
Source File: np-color-picker.component.ts    From np-ui-lib with MIT License 4 votes vote down vote up
@Component({
  selector: "np-color-picker",
  templateUrl: "np-color-picker.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NpColorPickerComponent),
      multi: true,
    },
  ],
})
export class NpColorPickerComponent
  implements ControlValueAccessor, AfterViewInit, AfterContentInit {
  private static controlCount = 1;

  /* forma can be 'hex' or 'rgb' */
  @Input() format: string = "hex";
  @Input() colors: string[];
  @Input() defaultOpen: boolean;
  @Input() readOnly: boolean;
  @Input() autoFocus: boolean;
  @Input() tabIndex: number;
  @Input() styleClass: string;
  @Input() inputId: string = `np-color-picker_${NpColorPickerComponent.controlCount++}`;

  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Output() onFocus: EventEmitter<any> = new EventEmitter();
  @Output() onBlur: EventEmitter<any> = new EventEmitter();

  @ViewChild("templatePortalContent") templatePortalContent: TemplateRef<any>;
  @ViewChild("control") inputViewChild: ElementRef;

  constructor(
    public overlay: Overlay,
    private viewContainerRef: ViewContainerRef,
    private overlayPositionBuilder: OverlayPositionBuilder,
    private elementRef: ElementRef
  ) { }

  isOpen: boolean = false;
  stripColor: string;
  currentCursorColor: string;
  xColorCursor: string;
  yColorCursor: string;
  innerValue: string;
  isDisabled: boolean = false;
  currentHex: string = "";
  currentR: number;
  currentG: number;
  currentB: number;
  focused: boolean = false;

  private templatePortal: TemplatePortal<any>;
  private overlayRef: OverlayRef;
  private onChangeCallback: (_: any) => void = () => { };
  private onTouchedCallback: () => void = () => { };

  ngAfterContentInit(): void {
    if (!this.colors) {
      if (this.format === "hex") {
        this.colors = [
          "#FF0000",
          "#FF7F00",
          "#FFFF00",
          "#7FFF00",
          "#00FF00",
          "#00FF7F",
          "#00FFFF",
          "#007FFF",
          "#0000FF",
          "#7F00FF",
          "#FF00FF",
          "#FF007F",
          "#f44336",
          "#e91e63",
          "#9c27b0",
          "#673ab7",
          "#3f51b5",
          "#2196f3",
          "#03a9f4",
          "#00bcd4",
          "#009688",
          "#4caf50",
          "#8bc34a",
          "#cddc39",
          "#ffeb3b",
          "#ffc107",
          "#ff9800",
          "#ff5722",
          "#795548",
          "#9e9e9e",
          "#607d8b",
          "#000000",
        ];
      } else {
        this.colors = [
          "rgb(255,0,0)",
          "rgb(255,127,0)",
          "rgb(255,255,0)",
          "rgb(127,255,0)",
          "rgb(0,255,0)",
          "rgb(0,255,127)",
          "rgb(0,255,255)",
          "rgb(0,127,255)",
          "rgb(0,0,255)",
          "rgb(127,0,255)",
          "rgb(255,0,255)",
          "rgb(255,0,127)",
          "rgb(244,67,54)",
          "rgb(244,67,54)",
          "rgb(156,39,176)",
          "rgb(103,58,183)",
          "rgb(63,81,181)",
          "rgb(33,150,243)",
          "rgb(3,169,244)",
          "rgb(0,188,212)",
          "rgb(0,150,136)",
          "rgb(76,175,80)",
          "rgb(139,195,74)",
          "rgb(205,220,57)",
          "rgb(255,235,59)",
          "rgb(255,193,7)",
          "rgb(255,152,0)",
          "rgb(255,87,34)",
          "rgb(121,85,72)",
          "rgb(158,158,158)",
          "rgb(96,125,139)",
          "rgb(0,0,0)",
        ];
      }
    }
  }

  ngAfterViewInit(): void {
    const positionStrategy = this.overlayPositionBuilder
      .flexibleConnectedTo(this.elementRef)
      .withPositions(TopBottomOverlayPositions);
    this.overlayRef = this.overlay.create({
      positionStrategy,
      hasBackdrop: true,
      backdropClass: "np-color-picker-backdrop",
      scrollStrategy: this.overlay.scrollStrategies.reposition(),
      panelClass: this.styleClass,
    });
    this.templatePortal = new TemplatePortal(
      this.templatePortalContent,
      this.viewContainerRef
    );
    this.overlayRef.backdropClick().subscribe(() => this._close());

    if (this.defaultOpen) {
      setTimeout(() => {
        this._updateStripCanvas();
        this._updateBlockCanvas();
      }, 10);
    }
  }

  get value(): string {
    return this.innerValue ? this.innerValue : null;
  }

  set value(v: string) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.stripColor = v;
      this._setCurrentValues(v);
      this.onChangeCallback(v);
      this.onChange.emit(v);
    }
  }

  writeValue(v: string): void {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.stripColor = v;
      this._setCurrentValues(v);
    }
  }

  registerOnChange(fn: any): void {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouchedCallback = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  focus(): void {
    this.inputViewChild.nativeElement.focus();
  }

  _toggleColorPicker(): void {
    if (this.isOpen) {
      this._close();
    } else {
      this._open();
    }
  }

  _open(): void {
    if (this.defaultOpen === true || this.isDisabled || this.readOnly) {
      return;
    }
    this.isOpen = true;
    this.stripColor = this.value;
    if (!this.overlayRef.hasAttached()) {
      this.overlayRef.attach(this.templatePortal);
    }
    setTimeout(() => {
      this._updateStripCanvas();
      this._updateBlockCanvas();
    }, 10);
  }

  _close(): void {
    if (this.defaultOpen) {
      return;
    }
    this.isOpen = false;
    this.overlayRef.detach();
    this.inputViewChild.nativeElement.focus();
  }

  _updateStripCanvas(): void {
    let strip: HTMLCanvasElement;
    if (this.defaultOpen) {
      strip = this.elementRef.nativeElement.querySelector(
        ".np-color-picker-strip"
      ) as HTMLCanvasElement;
    } else {
      strip = this.overlayRef.overlayElement.querySelector(
        ".np-color-picker-strip"
      ) as HTMLCanvasElement;
    }
    const ctx2 = strip.getContext("2d");
    ctx2.rect(0, 0, 25, 170);
    const grd1 = ctx2.createLinearGradient(0, 0, 0, 170);
    grd1.addColorStop(0, "rgba(255, 0, 0, 1)");
    grd1.addColorStop(0.17, "rgba(255, 255, 0, 1)");
    grd1.addColorStop(0.34, "rgba(0, 255, 0, 1)");
    grd1.addColorStop(0.51, "rgba(0, 255, 255, 1)");
    grd1.addColorStop(0.68, "rgba(0, 0, 255, 1)");
    grd1.addColorStop(0.85, "rgba(255, 0, 255, 1)");
    grd1.addColorStop(1, "rgba(255, 0, 0, 1)");
    ctx2.fillStyle = grd1;
    ctx2.fill();
  }

  _updateBlockCanvas(): void {
    let block: HTMLCanvasElement;
    if (this.defaultOpen) {
      block = this.elementRef.nativeElement.querySelector(
        ".np-color-picker-block"
      ) as HTMLCanvasElement;
    } else {
      block = this.overlayRef.overlayElement.querySelector(
        ".np-color-picker-block"
      ) as HTMLCanvasElement;
    }
    const ctx1 = block.getContext("2d");

    ctx1.fillStyle = this.stripColor
      ? this.stripColor
      : this.value
        ? this.value
        : "rgb(255,0,0)";
    ctx1.fillRect(0, 0, 170, 170);

    const grdWhite = ctx1.createLinearGradient(0, 0, 170, 0);
    grdWhite.addColorStop(0, "rgba(255,255,255,1)");
    grdWhite.addColorStop(1, "rgba(255,255,255,0)");
    ctx1.fillStyle = grdWhite;
    ctx1.fillRect(0, 0, 170, 170);

    const grdBlack = ctx1.createLinearGradient(0, 0, 0, 170);
    grdBlack.addColorStop(0, "rgba(0,0,0,0)");
    grdBlack.addColorStop(1, "rgba(0,0,0,1)");
    ctx1.fillStyle = grdBlack;
    ctx1.fillRect(0, 0, 170, 170);
  }

  _clickStripeColor(e: any): void {
    const imageData = this._getColorFromClickevent(e, ".np-color-picker-strip");
    this.stripColor =
      this.format === "rgb"
        ? `rgb(${imageData[0]},${imageData[1]},${imageData[2]})`
        : this._rgbToHex(imageData[0], imageData[1], imageData[2]);
    this._updateBlockCanvas();
  }

  _clickBlockColor(e: any): void {
    const imageData = this._getColorFromClickevent(e, ".np-color-picker-block");
    this.value =
      this.format === "rgb"
        ? `rgb(${imageData[0]},${imageData[1]},${imageData[2]})`
        : this._rgbToHex(imageData[0], imageData[1], imageData[2]);
  }

  _rgbToHex(r: any, g: any, b: any): string {
    const red = this._convertNumberToHex(r);
    const green = this._convertNumberToHex(g);
    const blue = this._convertNumberToHex(b);
    return `#${red}${green}${blue}`;
  }

  _convertNumberToHex(num: any): string {
    let hex = Number(num).toString(16);
    if (hex.length < 2) {
      hex = `0${hex}`;
    }
    return hex;
  }

  _hexToRgb(hex: string): any {
    if (!hex) {
      return null;
    }
    const r = parseInt(hex.substring(1, 3), 16);
    const g = parseInt(hex.substring(3, 5), 16);
    const b = parseInt(hex.substring(5, 7), 16);
    return { r, g, b };
  }

  _onClickColorBlock(color: string): void {
    this.value = color;
    this.stripColor = this.value;
    this._updateBlockCanvas();
  }

  _clear(): void {
    if (this.isDisabled || this.readOnly) {
      return;
    }
    this.value = null;
    this._close();
  }

  _getColorFromClickevent(e: any, clickedElement: string): any {
    let strip: HTMLCanvasElement;
    if (this.defaultOpen) {
      strip = this.elementRef.nativeElement.querySelector(
        clickedElement
      ) as HTMLCanvasElement;
    } else {
      strip = this.overlayRef.overlayElement.querySelector(
        clickedElement
      ) as HTMLCanvasElement;
    }
    const ctx2 = strip.getContext("2d");
    const x = e.offsetX;
    const y = e.offsetY;
    return ctx2.getImageData(x, y, 1, 1).data;
  }

  _onKeydown(event: KeyboardEvent): void {
    if (event.key === "Tab" || event.key === "Escape") {
      this._close();
    }
    if (event.key === "ArrowDown") {
      this._open();
      event.preventDefault();
    }
  }

  _onChangeHex(event: any): void {
    let val = event.target.value;
    if (val && val.charAt(0) !== "#") {
      val = `#${event.target.value}`;
    }
    if (this.format === "hex") {
      this.value = val;
    } else {
      const rgb = this._hexToRgb(val);
      this.value = `rgb(${rgb.r},${rgb.g},${rgb.b})`;
    }
    this._updateBlockCanvas();
  }

  _onChangeR(event: any): void {
    if (this.format === "hex") {
      this.value = this._rgbToHex(
        event.target.value,
        this.currentG,
        this.currentB
      );
    } else {
      this.value = `rgb(${event.target.value},${this.currentG},${this.currentB})`;
    }
    this._updateBlockCanvas();
  }

  _onChangeG(event: any): void {
    if (this.format === "hex") {
      this.value = this._rgbToHex(
        this.currentR,
        event.target.value,
        this.currentB
      );
    } else {
      this.value = `rgb(${this.currentR},${event.target.value},${this.currentB})`;
    }
    this._updateBlockCanvas();
  }

  _onChangeB(event: any): void {
    if (this.format === "hex") {
      this.value = this._rgbToHex(
        this.currentR,
        this.currentG,
        event.target.value
      );
    } else {
      this.value = `rgb(${this.currentR},${this.currentG},${event.target.value})`;
    }
    this._updateBlockCanvas();
  }

  _setCurrentValues(val: string): void {
    if (!val) {
      this.currentHex = "";
      this.currentR = null;
      this.currentG = null;
      this.currentB = null;
      return;
    }
    if (this.format === "hex") {
      this.currentHex = val;
      const rgb = this._hexToRgb(val);
      if (rgb) {
        this.currentR = rgb.r;
        this.currentG = rgb.g;
        this.currentB = rgb.b;
      }
    } else {
      const rgb = val
        .replace("rgb", "")
        .replace("(", "")
        .replace(")", "")
        .replace(" ", "");
      const rgbAray = rgb.split(",");
      this.currentR = Number(rgbAray[0]);
      this.currentG = Number(rgbAray[1]);
      this.currentB = Number(rgbAray[2]);
      this.currentHex = this._rgbToHex(
        this.currentR,
        this.currentG,
        this.currentB
      );
    }
  }

  _onBlur($event: any): void {
    this.focused = false;
    this.onTouchedCallback();
    this.onBlur.emit($event);
  }

  _onFocus($event: any): void {
    this.focused = true;
    this.onFocus.emit($event);
  }

  _onMouseLeaveStrip(): void {
    this.currentCursorColor = undefined;
  }

  _onMouseLeaveBlock(): void {
    this.currentCursorColor = undefined;
  }

  _onMouseOverStrip(e: any): void {
    this.xColorCursor = `${e.pageX}px`;
    this.yColorCursor = `${e.pageY}px`;
    const imageData = this._getColorFromClickevent(e, ".np-color-picker-strip");
    this.currentCursorColor =
      this.format === "rgb"
        ? `rgb(${imageData[0]},${imageData[1]},${imageData[2]})`
        : this._rgbToHex(imageData[0], imageData[1], imageData[2]);
  }

  _onMouseOverBlock(e: any): void {
    this.xColorCursor = `${e.pageX}px`;
    this.yColorCursor = `${e.pageY}px`;
    const imageData = this._getColorFromClickevent(e, ".np-color-picker-block");
    this.currentCursorColor =
      this.format === "rgb"
        ? `rgb(${imageData[0]},${imageData[1]},${imageData[2]})`
        : this._rgbToHex(imageData[0], imageData[1], imageData[2]);
  }
}
Example #18
Source File: calendar.ts    From angular-material-components with MIT License 4 votes vote down vote up
/**
 * A calendar that is used as part of the datepicker.
 * @docs-private
 */
@Component({
  selector: 'ngx-mat-calendar',
  templateUrl: 'calendar.html',
  styleUrls: ['calendar.scss'],
  host: {
    'class': 'mat-calendar',
  },
  exportAs: 'ngxMatCalendar',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxMatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDestroy, OnChanges {
  /** An input indicating the type of the header component, if set. */
  @Input() headerComponent: ComponentType<any>;

  /** A portal containing the header component type for this calendar. */
  _calendarHeaderPortal: Portal<any>;

  private _intlChanges: Subscription;

  /**
   * Used for scheduling that focus should be moved to the active cell on the next tick.
   * We need to schedule it, rather than do it immediately, because we have to wait
   * for Angular to re-evaluate the view children.
   */
  private _moveFocusOnNextTick = false;

  /** A date representing the period (month or year) to start the calendar in. */
  @Input()
  get startAt(): D | null { return this._startAt; }
  set startAt(value: D | null) {
    this._startAt = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
  }
  private _startAt: D | null;

  /** Whether the calendar should be started in month or year view. */
  @Input() startView: MatCalendarView = 'month';

  /** The currently selected date. */
  @Input()
  get selected(): D | null { return this._selected; }
  set selected(value: D | null) {
    this._selected = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
  }
  private _selected: D | null;

  /** The minimum selectable date. */
  @Input()
  get minDate(): D | null { return this._minDate; }
  set minDate(value: D | null) {
    this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
  }
  private _minDate: D | null;

  /** The maximum selectable date. */
  @Input()
  get maxDate(): D | null { return this._maxDate; }
  set maxDate(value: D | null) {
    this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
  }
  private _maxDate: D | null;

  /** Function used to filter which dates are selectable. */
  @Input() dateFilter: (date: D) => boolean;

  /** Function that can be used to add custom CSS classes to dates. */
  @Input() dateClass: (date: D) => MatCalendarCellCssClasses;

  /** Emits when the currently selected date changes. */
  @Output() readonly selectedChange: EventEmitter<D> = new EventEmitter<D>();

  /**
   * Emits the year chosen in multiyear view.
   * This doesn't imply a change on the selected date.
   */
  @Output() readonly yearSelected: EventEmitter<D> = new EventEmitter<D>();

  /**
   * Emits the month chosen in year view.
   * This doesn't imply a change on the selected date.
   */
  @Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();

  /** Emits when any date is selected. */
  @Output() readonly _userSelection: EventEmitter<void> = new EventEmitter<void>();

  /** Reference to the current month view component. */
  @ViewChild(NgxMatMonthView) monthView: NgxMatMonthView<D>;

  /** Reference to the current year view component. */
  @ViewChild(NgxMatYearView) yearView: NgxMatYearView<D>;

  /** Reference to the current multi-year view component. */
  @ViewChild(NgxMatMultiYearView) multiYearView: NgxMatMultiYearView<D>;

  /**
   * The current active date. This determines which time period is shown and which date is
   * highlighted when using keyboard navigation.
   */
  get activeDate(): D { return this._clampedActiveDate; }
  set activeDate(value: D) {
    this._clampedActiveDate = this._dateAdapter.clampDate(value, this.minDate, this.maxDate);
    this.stateChanges.next();
    this._changeDetectorRef.markForCheck();
  }
  private _clampedActiveDate: D;

  /** Whether the calendar is in month view. */
  get currentView(): MatCalendarView { return this._currentView; }
  set currentView(value: MatCalendarView) {
    this._currentView = value;
    this._moveFocusOnNextTick = true;
    this._changeDetectorRef.markForCheck();
  }
  private _currentView: MatCalendarView;

  /**
   * Emits whenever there is a state change that the header may need to respond to.
   */
  stateChanges = new Subject<void>();

  constructor(_intl: MatDatepickerIntl,
    @Optional() private _dateAdapter: NgxMatDateAdapter<D>,
    @Optional() @Inject(NGX_MAT_DATE_FORMATS) private _dateFormats: NgxMatDateFormats,
    private _changeDetectorRef: ChangeDetectorRef) {

    if (!this._dateAdapter) {
      throw createMissingDateImplError('NgxDateAdapter');
    }

    if (!this._dateFormats) {
      throw createMissingDateImplError('NGX_MAT_DATE_FORMATS');
    }

    this._intlChanges = _intl.changes.subscribe(() => {
      _changeDetectorRef.markForCheck();
      this.stateChanges.next();
    });
  }

  ngAfterContentInit() {
    this._calendarHeaderPortal = new ComponentPortal(this.headerComponent || NgxMatCalendarHeader);
    this.activeDate = this.startAt || this._dateAdapter.today();

    // Assign to the private property since we don't want to move focus on init.
    this._currentView = this.startView;
  }

  ngAfterViewChecked() {
    if (this._moveFocusOnNextTick) {
      this._moveFocusOnNextTick = false;
      this.focusActiveCell();
    }
  }

  ngOnDestroy() {
    this._intlChanges.unsubscribe();
    this.stateChanges.complete();
  }

  ngOnChanges(changes: SimpleChanges) {
    const change =
      changes['minDate'] || changes['maxDate'] || changes['dateFilter'];

    if (change && !change.firstChange) {
      const view = this._getCurrentViewComponent();

      if (view) {
        // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are
        // passed down to the view via data bindings which won't be up-to-date when we call `_init`.
        this._changeDetectorRef.detectChanges();
        view._init();
      }
    }

    this.stateChanges.next();
  }

  focusActiveCell() {
    this._getCurrentViewComponent()._focusActiveCell();
  }

  /** Updates today's date after an update of the active date */
  updateTodaysDate() {
    let view = this.currentView == 'month' ? this.monthView :
      (this.currentView == 'year' ? this.yearView : this.multiYearView);

    view.ngAfterContentInit();
  }

  /** Handles date selection in the month view. */
  _dateSelected(date: D | null): void {
    if (date && !this._dateAdapter.sameDate(date, this.selected)) {
      this.selectedChange.emit(date);
    }
  }

  /** Handles year selection in the multiyear view. */
  _yearSelectedInMultiYearView(normalizedYear: D) {
    this.yearSelected.emit(normalizedYear);
  }

  /** Handles month selection in the year view. */
  _monthSelectedInYearView(normalizedMonth: D) {
    this.monthSelected.emit(normalizedMonth);
  }

  _userSelected(): void {
    this._userSelection.emit();
  }

  /** Handles year/month selection in the multi-year/year views. */
  _goToDateInView(date: D, view: 'month' | 'year' | 'multi-year'): void {
    this.activeDate = date;
    this.currentView = view;
  }

  /**
   * @param obj The object to check.
   * @returns The given object if it is both a date instance and valid, otherwise null.
   */
  private _getValidDateOrNull(obj: any): D | null {
    return (this._dateAdapter.isDateInstance(obj) && this._dateAdapter.isValid(obj)) ? obj : null;
  }

  /** Returns the component instance that corresponds to the current calendar view. */
  private _getCurrentViewComponent() {
    return this.monthView || this.yearView || this.multiYearView;
  }
}
Example #19
Source File: np-date-picker.component.ts    From np-ui-lib with MIT License 4 votes vote down vote up
@Component({
  selector: "np-date-picker",
  templateUrl: "./np-date-picker.component.html",
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.Default,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NpDatePickerComponent),
      multi: true,
    },
  ],
})
export class NpDatePickerComponent
  implements ControlValueAccessor, AfterViewInit, AfterContentInit {
  private static controlCount = 1;

  @Input() minDate: Date;
  @Input() maxDate: Date;
  @Input() format: string = "dd/MM/yyyy";
  @Input() defaultOpen: boolean = false;
  @Input() showTodayButton: boolean = false;
  @Input() disableWeekDays: string[] = [];
  @Input() disableDates: Date[] = [];
  @Input() dateLabels: any[] = [];
  @Input() dateClass: any;
  @Input() isStartMonthWithMonday = false;
  @Input() placeholder: string = "";
  @Input() readOnly: boolean;
  @Input() autoFocus: boolean;
  @Input() tabIndex: number;
  @Input() styleClass: string;
  @Input() inputId: string = `np-date-picker_${NpDatePickerComponent.controlCount++}`;

  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Output() onFocus: EventEmitter<any> = new EventEmitter();
  @Output() onBlur: EventEmitter<any> = new EventEmitter();

  @ViewChild("templatePortalContent") templatePortalContent: TemplateRef<any>;
  @ViewChild("control") inputViewChild: ElementRef;

  weekDays: string[];
  monthsList: any[];
  months: any[];
  years: number[] = [];
  currentMonthWeeks: any;
  currentMonth: number;
  currentYear: number;
  today: Date;
  isOpen: boolean = false;
  innerValue: Date;
  isDisabled: boolean = false;
  originalWeekDays: string[] = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
  focused: boolean = false;

  private templatePortal: TemplatePortal<any>;
  private overlayRef: OverlayRef;
  private onChangeCallback: (_: any) => void = () => { };
  private onTouchedCallback: () => void = () => { };

  constructor(
    public overlay: Overlay,
    private viewContainerRef: ViewContainerRef,
    private overlayPositionBuilder: OverlayPositionBuilder,
    private elementRef: ElementRef,
    private utility: NpUtilityService
  ) {
    this.monthsList = [
      { key: 0, value: "January" },
      { key: 1, value: "February" },
      { key: 2, value: "March" },
      { key: 3, value: "April" },
      { key: 4, value: "May" },
      { key: 5, value: "June" },
      { key: 6, value: "July" },
      { key: 7, value: "August" },
      { key: 8, value: "September" },
      { key: 9, value: "October" },
      { key: 10, value: "November" },
      { key: 11, value: "December" },
    ];
    this.today = new Date();
    this.today.setHours(0, 0, 0, 0);
  }

  ngAfterContentInit(): void {
    if (this.isStartMonthWithMonday) {
      this.weekDays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
    } else {
      this.weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
    }
    if (this.defaultOpen) {
      this._resetVariables();
    }
  }

  ngAfterViewInit(): void {
    const positionStrategy = this.overlayPositionBuilder
      .flexibleConnectedTo(this.elementRef)
      .withPositions(TopBottomOverlayPositions);
    this.overlayRef = this.overlay.create({
      positionStrategy,
      hasBackdrop: true,
      backdropClass: "np-date-picker-backdrop",
      scrollStrategy: this.overlay.scrollStrategies.reposition(),
      panelClass: this.styleClass,
    });
    this.templatePortal = new TemplatePortal(
      this.templatePortalContent,
      this.viewContainerRef
    );
    this.overlayRef.backdropClick().subscribe(() => this._close());
  }

  get value(): Date {
    return this.innerValue ? this.innerValue : null;
  }

  set value(v: Date) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
      this.onChange.emit(v);
    }
  }

  writeValue(v: Date): void {
    if (v !== this.innerValue) {
      this.innerValue = v;
      if (this._checkIsFullDateDisabled(v)) {
        this.value = null;
      }
    }
  }

  registerOnChange(fn: any): void {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouchedCallback = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  focus(): void {
    this.inputViewChild.nativeElement.focus();
  }

  _resetVariables(): void {
    if (this.value) {
      if (this.minDate && this.minDate > this.value) {
        this.value = null;
      }
      if (this.maxDate && this.maxDate < this.value) {
        this.value = null;
      }
    }

    let currentDate =
      this.value && this.value.toString() !== "Invalid Date"
        ? this.value
        : this.today;
    if (this.minDate && currentDate < this.minDate) {
      currentDate = this.minDate;
    }
    if (this.maxDate && currentDate > this.maxDate) {
      currentDate = this.maxDate;
    }
    this.currentMonth = currentDate.getMonth();
    this.currentYear = currentDate.getFullYear();

    this._calculateDays();
  }

  _calculateDays(): void {
    const weeks = [];
    weeks[0] = [];
    const daysInMonth = this._getCurrentMonthData();
    const firstWeekDayOfMonth = new Date(
      this.currentYear,
      this.currentMonth,
      1
    ).getDay();
    // push extra values up to week days match to start date if month
    for (let index = 0; index < firstWeekDayOfMonth; index++) {
      weeks[0].push({ disabled: true });
    }
    if (this.isStartMonthWithMonday) {
      // if start with monday then
      if (firstWeekDayOfMonth === 0) {
        weeks[0].push(
          { disabled: true },
          { disabled: true },
          { disabled: true },
          { disabled: true },
          { disabled: true },
          { disabled: true }
        );
      } else {
        weeks[0].pop();
      }
    }
    let j = 0;
    // push all dates in month
    daysInMonth.forEach((element: any) => {
      if (weeks[j].length === 7) {
        j++;
        weeks[j] = [];
      }
      weeks[j].push(element);
    });
    this.currentMonthWeeks = weeks;
  }

  _getCurrentMonthData(): any[] {
    const data = [];
    const totalDaysInMonth = this._daysInCurrentMonth();
    for (let i = 1; i <= totalDaysInMonth; i++) {
      const date = new Date(this.currentYear, this.currentMonth, i);
      data.push({
        date,
        day: i,
        disabled: this._checkIsFullDateDisabled(date),
        today: this._compareDate(this.today, date),
        selected: this._compareDate(this.value, date),
        toolTip: this._getTooltip(date),
      });
    }
    return data;
  }

  _daysInCurrentMonth(): number {
    let days = 0;
    switch (this.currentMonth) {
      case 0: // Jan
      case 2: // Mar
      case 4: // May
      case 6: // Jul
      case 7: // Aug
      case 9: // Oct
      case 11: // Dec
        days = 31;
        break;
      case 3: // Apr
      case 5: // Jun
      case 8: // Sept
      case 10: // Nov
        days = 30;
        break;
      case 1: // Feb
        days = this.currentYear % 4 === 0 ? 29 : 28;
        break;
    }
    return days;
  }

  _prevMonth(): void {
    if (this.currentMonth === 0) {
      this.currentMonth = 11;
      this.currentYear = this.currentYear - 1;
    } else {
      this.currentMonth = this.currentMonth - 1;
    }
    this._calculateDays();
  }

  _nextMonth(): void {
    if (this.currentMonth === 11) {
      this.currentMonth = 0;
      this.currentYear = this.currentYear + 1;
    } else {
      this.currentMonth = this.currentMonth + 1;
    }
    this._calculateDays();
  }

  _selectDate(date: Date): void {
    if (
      date === null ||
      this._checkIsFullDateDisabled(date) ||
      this.isDisabled ||
      this.readOnly
    ) {
      return;
    }
    this._setSelectedDate(date);
    this._close();
  }

  _selectMonth($event: any): void {
    this.currentMonth = Number($event.target.value);
    this._calculateDays();
  }

  _changeYear($event: any): void {
    this.currentYear = Number($event.target.value.trim());
    this._calculateDays();
  }

  _toggleDatePicker(): void {
    if (this.isOpen) {
      this._close();
    } else {
      this._open();
    }
  }

  _open(): void {
    if (this.defaultOpen === true || this.isDisabled || this.readOnly) {
      return;
    }
    this.isOpen = true;
    this._resetVariables();

    if (!this.overlayRef.hasAttached()) {
      this.overlayRef.attach(this.templatePortal);
    }
  }

  _close(): void {
    if (this.defaultOpen) {
      return;
    }
    this.isOpen = false;
    this.overlayRef.detach();
    this.inputViewChild.nativeElement.focus();
  }

  _setToday(): void {
    if (this._checkIsFullDateDisabled(this.today)) {
      return;
    }
    this._setSelectedDate(this.today);
    this._calculateDays();
    this._close();
  }

  _clear(): void {
    if (this.isDisabled || this.readOnly) {
      return;
    }
    this._setSelectedDate(null);
    this._calculateDays();
    this._close();
  }

  _getTooltip(currentDate: Date): void {
    if (currentDate && this.dateLabels && this.dateLabels.length > 0) {
      const dateLabel: any = this.dateLabels.find((item) => this._compareDate(item.date, currentDate));
      return dateLabel ? dateLabel.label : null;
    }
    return null;
  }

  _checkIsWeekDayDisabled(index: number): boolean {
    const day = this.originalWeekDays[index];
    return this.disableWeekDays.indexOf(day) > -1;
  }

  _checkIsFullDateDisabled(date: Date): boolean {
    if (date === undefined || date === null) {
      return false;
    }
    if (this.minDate && date < this.minDate) {
      return true;
    }
    if (this.maxDate && date > this.maxDate) {
      return true;
    }
    if (this._checkIsWeekDayDisabled(date.getDay())) {
      return true;
    }
    return (this.disableDates.findIndex((item) => this._compareDate(item, date))) > -1;
  }

  _setSelectedDate(date: Date): void {
    if (date) {
      date.setHours(0, 0, 0, 0);
    }
    if (this._checkIsFullDateDisabled(date)) {
      return;
    }
    this.value = date;
    this._resetVariables();
  }

  _onKeydown(event: KeyboardEvent): void {
    if (event.key === "Tab" || event.key === "Escape") {
      this._close();
    }
    if (event.key === "ArrowDown") {
      this._open();
      event.preventDefault();
    }
  }

  _onInputChange($event: any): void {
    const date = this.utility.ReverseFormatDate(
      $event.target.value.trim(),
      this.format
    );
    if (date === "Invalid Date") {
      $event.target.value = "";
      this.value = null;
      return;
    }
    if (this._checkIsFullDateDisabled(date)) {
      this.value = null;
      return;
    }
    this.value = date;
    this._resetVariables();
  }

  _compareDate(dateA: Date, dateB: Date): boolean {
    if (!dateA || !dateB) {
      return false;
    }
    if (
      dateA.getFullYear() === dateB.getFullYear() &&
      dateA.getMonth() === dateB.getMonth() &&
      dateA.getDate() === dateB.getDate()
    ) {
      return true;
    }
    return false;
  }

  _onBlur($event: any): void {
    this.focused = false;
    this.onTouchedCallback();
    this.onBlur.emit($event);
  }

  _onFocus($event: any): void {
    this.focused = true;
    this.onFocus.emit($event);
  }

  _getDateClass(item: any): string {
    if (this.dateClass) {
      return (
        `np-date-picker-day np-day-${item.day} ` + this.dateClass(item.date)
      ).trim();
    }
    return `np-date-picker-day np-day-${item.day}`;
  }
}
Example #20
Source File: app-sidenav.component.ts    From youpez-admin with MIT License 4 votes vote down vote up
@Component({
  selector: 'youpez-sidenav',
  templateUrl: './app-sidenav.component.html',
  styleUrls: ['./app-sidenav.component.scss'],
})
export class AppSidenavComponent implements OnInit, OnDestroy, AfterContentInit, OnChanges, AfterViewInit, AfterViewChecked {

  @Input('opened') originalOpened: boolean
  @Input() direction: DirectionType = 'left'
  @Input() size: SizeType = 'md'
  @Input('mode') originalMode: ModeType = 'over'
  @Input() breakpoint: string = ''
  @Input() transparent: boolean = false
  @Input() toggleableBtn: boolean = false
  @Input() toggleableBtnAlwaysVisible: boolean = false
  @Input('hoverAble') originalHoverAble: boolean = false
  @Input() hoverAbleBreakpoint: string = 'md'
  @Input() optionalClass: string = ''
  @Input() initWidth: string = ''
  @Input() hoverDelay: number = 100
  @Input() minDimension: number = null

  @Output() open: EventEmitter<any> = new EventEmitter<any>()
  @Output() close: EventEmitter<any> = new EventEmitter<any>()
  @Output() init: EventEmitter<any> = new EventEmitter<any>()
  @Output() change: EventEmitter<any> = new EventEmitter<any>()
  @Output() resizeEnd: EventEmitter<any> = new EventEmitter<any>()
  @Output() resizing: EventEmitter<any> = new EventEmitter<any>()
  @Output() visibleChange: EventEmitter<any> = new EventEmitter<any>()

  @ViewChild('sideNavEl', {static: true}) sideNavEl: ElementRef

  public rendered: boolean = false
  public width: string = ''
  public height: string = ''
  public mode: string = 'over'
  public hoverEvent: boolean = false
  public hoverAble: boolean = false
  public opened: boolean = false
  public openedMemo:boolean = null
  public isMouseEntered: boolean = false
  public resizeEdges = {
    bottom: false,
    right: false,
    top: false,
    left: false,
  }

  private lock: boolean = false
  private hoverTimeout = null
  private breakpointSub: Subscription
  private dimensions = {
    width: {
      docs: '170',
      xsm: '200',
      sm: '300',
      md: '300',
      lg: '500',
      xl: '800',
      custom1: '260',
      sideBar1: '240',
      sideBar2: '300',
      mail: '380',
      mini: '100',
    },
    height: {},
  }

  constructor(public element: ElementRef,
              private appBreakpointService: AppBreakpointService,
              private appSidenavService: AppSidenavService,) {
  }

  ngOnInit() {
    this.firstInit()
    //CURRENTLY: https://github.com/angular/flex-layout/issues/1059
    this.breakpointSub = this.appBreakpointService.$windowWidth
      .subscribe((width: any) => {
        if (!width) {
          return
        }
        if (this.breakpoint && this.breakpoint === 'md') {
          if (width <= 960) {
            this.mode = 'over'
            this.hoverAble = false
            this.opened = false
            this.openedMemo = this.originalOpened

            setTimeout(() => {
              this.visibleChange.emit(false)
            }, 1)
          }
          else {
            this.mode = this.originalMode
            this.hoverAble = this.originalHoverAble
            this.opened = this.openedMemo || this.originalOpened

            setTimeout(() => {
              this.visibleChange.emit(this.openedMemo || this.originalOpened)
            }, 1)
          }
          this.change.emit()
        }
      })
  }

  private firstInit() {
    let {initWidth, size, originalMode, originalHoverAble, originalOpened, direction} = this

    if (initWidth) {
      this.setWidth(initWidth)
      this.setHeight(initWidth)
    }
    else {
      this.setWidth(this.dimensions.width[size])
      this.setHeight(this.dimensions.width[size])
    }

    this.mode = originalMode
    this.hoverAble = originalHoverAble
    this.opened = originalOpened

    if (originalMode === 'side') {
      if (direction === 'right') {
        this.resizeEdges.left = true
      }
      if (direction === 'left') {
        this.resizeEdges.right = true
      }
      if (direction === 'top') {
        this.resizeEdges.bottom = true
      }
      if (direction === 'bottom') {
        this.resizeEdges.top = true
      }
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    const opened: SimpleChange = changes.originalOpened

    if (opened !== undefined && opened.previousValue !== undefined) {
      const currentOpened = opened.currentValue
      if (currentOpened) {
        this.onOpen()
      }
      else {
        this.onClose()
      }
    }
  }

  ngOnDestroy(): void {
    if (this.breakpointSub) {
      this.breakpointSub.unsubscribe()
    }
  }

  ngAfterContentInit() {
    if (this.originalMode !== 'side') {
      setTimeout(() => {
        this.rendered = true
      }, 450)
    }
    else {
      this.rendered = true
    }
  }

  ngAfterViewInit() {

  }

  ngAfterViewChecked() {

  }

  setWidth(width) {
    this.width = width
    if (Number(this.width) <= 200) {
      this.onClose()
      setTimeout(() => {
        this.width = '200'
      }, 1000)
    }
  }

  setHeight(height) {
    this.height = height
  }

  sendChange(changes = null) {
    this.change.emit()
  }

  setPanelStyle(panel: PanelType) {
    let panelCss = ''
    if (panel === 'panel') {
      panelCss = 'app-sidenav-v2--panel'
    }
    if (panel === 'solid') {
      panelCss = 'app-sidenav-v2--solid-border'
    }
    this.optionalClass = this.optionalClass + ' ' + panelCss
  }

  getWidth() {
    return this.width
  }

  getMainCSSclass() {
    return `app-sidenav-v2 ${this.optionalClass}`
  }

  getHeight() {
    return this.height
  }

  getMinDimension() {
    return this.minDimension
  }

  onClose() {
    this.opened = false
    this.visibleChange.emit(false)
    this.close.emit()
  }

  onOpen() {
    this.opened = true
    this.visibleChange.emit(true)
    this.open.emit()
  }

  onToggle() {
    if (this.opened) {
      this.onClose()
      this.onMouseLeave('')
    }
    else {
      this.onOpen()
    }
  }

  isOpened() {
    return this.opened
  }

  emitInitForParent() {
    this.init.emit()
  }

  onForceHover(bool = true) {
    if (this.hoverAble) {
      if (!this.isMouseEntered) {
        this.hoverEvent = bool
      }
    }
    else {
      this.onOpen()
    }
  }

  onMouseEnter(event) {
    this.isMouseEntered = true
    if (this.hoverTimeout) {
      clearTimeout(this.hoverTimeout)
    }
    this.hoverTimeout = setTimeout(() => {
      const element = event.target
      if (element.classList.contains('app-sidenav-v2__inner')) {
        this.hoverEvent = true
      }
    }, this.hoverDelay)
  }

  onMouseLeave(event) {
    if (this.lock) {
      return
    }
    this.isMouseEntered = false
    if (this.hoverTimeout) {
      clearTimeout(this.hoverTimeout)
    }
    this.hoverEvent = false
  }

  onResizeEnd(event: ResizeEvent) {
    const {edges, rectangle} = event
    const {right, left, bottom, top} = edges
    const {width, height} = rectangle

    const calcWidth = Number(width)
    const calcHeight = Number(height)

    this.setWidth(calcWidth)
    this.setHeight(calcHeight)

    this.sendChange()


    if (this.direction === 'top' || this.direction === 'bottom') {
      this.resizeEnd.next(calcHeight)
    }
    else {
      this.resizeEnd.next(calcWidth)
    }

    this.lock = false
  }

  onResize(event: ResizeEvent) {
    const {edges, rectangle} = event
    const {right, left, bottom, top} = edges
    const {width, height} = rectangle

    const calcWidth = Number(width)
    const calcHeight = Number(height)

    this.setWidth(calcWidth)
    this.setHeight(calcHeight)

    this.sendChange({windowResize: false})

    if (this.direction === 'top' || this.direction === 'bottom') {
      this.resizing.next(calcHeight)
    }
    else {
      this.resizing.next(calcWidth)
    }
  }

  onResizeStart(event: ResizeEvent) {
    this.onForceHover(true)
    this.lock = true
  }

  onSetDefaultWidth(event) {
    this.setWidth(this.dimensions.width[this.size])
  }
}
Example #21
Source File: facet-card.ts    From sba-angular with MIT License 4 votes vote down vote up
@Component({
    selector: "sq-facet-card",
    templateUrl: "./facet-card.html",
    styles: [`
        .cursor-default {cursor: default;}
    `]
})
export class BsFacetCard implements OnInit, OnDestroy, AfterContentInit {

    /**
     * Title of this facet (optional)
     */
    @Input() title: string;

    /**
     * Tooltip of this facet (defaults to title)
     */
    @Input() tooltip: string;

    /**
     * Icon of this facet, in a form of a span class name (optional)
     */
    @Input() icon: string;

    /**
     * Bootstrap theme name (light, dark...)
     */
    @Input() buttonsStyle: string;

    /**
     * List of custom actions for this facet (optional)
     */
    @Input() actions: Action[] = [];

    /**
     * Whether the [actions]="..." passed by binding should be displayed before or after
     * the actions from the inner facet component
     */
    @Input() actionsFirst = true;

    /**
     * Size of the custom actions
     */
    @Input() actionsSize = "sm";

    /**
     * Whether the facet can be collapsed (default: true)
     */
    @Input() collapsible: boolean = true;

    /**
     * Whether the facet starts collapsed (if collapsible / default: false)
     */
    @Input() startCollapsed: boolean = false;

    /**
     * Whether other actions should be hidden when the facet is collapsed (default: true)
     */
    @Input() hideActionsCollapsed: boolean = true;

    /**
     * Whether the facet can be expanded (default: false)
     */
    @Input() expandable: boolean = false;

    /**
     * Whether the facet starts expanded (if expandable / default: false)
     */
    @Input() startExpanded: boolean = false;

    /**
     * Facet will collapse automatically once clicking outside of it
     */
    @Input() collapseOnClickOutside: boolean = false;

    /**
     * Whether the facet starts with opened settings (default: false)
     */
    @Input() startSettingsOpened: boolean = false;

    /**
     * Event triggered when the facet gets expanded or reduced
     */
    @Output() facetExpanded = new EventEmitter<"expanded"|"reduced">();

    /**
     * Event triggered when the facet gets expanded or reduced
     */
    @Output() facetCollapsed = new EventEmitter<"collapsed"|"expanded">();

    /**
     * Event triggered when the facet gets expanded or reduced
     */
    @Output() settingsOpened = new EventEmitter<"opened"|"saved"|"canceled">();

    /**
     * Reference to the child facet inserted by transclusion (ng-content)
     */
    @ContentChild("facet", {static: false}) public facetComponent: AbstractFacet;

    @HostBinding('class.collapsed') _collapsed: boolean;
    @HostBinding('class.expanded') _expanded: boolean;
    @HostBinding('class.settings-opened') _settingsOpened: boolean;

    @HostBinding('hidden') get hidden(): boolean {
        return !!this.facetComponent && !!this.facetComponent.isHidden && this.facetComponent.isHidden();
    }

    public readonly collapseAction;
    public readonly expandAction;
    public readonly settingsAction;

    private actionChangedSubscription: Subscription;

    constructor(
        private changeDetectorRef: ChangeDetectorRef
    ){

        this.collapseAction = new Action({
            action: (action) => {
                this._collapsed = !this._collapsed;
                this.facetCollapsed.next(this._collapsed ? "collapsed" : "expanded");
                if(!!this.facetComponent){
                    this.facetComponent.onCollapse(this._collapsed);
                }
                action.update();
            },
            updater: (action) => {
                action.icon = this._collapsed ? "fas fa-chevron-down" : "fas fa-chevron-up";
                action.title = this._collapsed ? 'msg#facetCard.expand' : 'msg#facetCard.collapse';
            }
        });

        this.expandAction = new Action({
            action: (action) => {
                this._expanded = !this._expanded;
                this.facetExpanded.next(this._expanded ? "expanded" : "reduced");
                if(!!this.facetComponent){
                    this.facetComponent.onExpand(this._expanded);
                }
                action.update();
            },
            updater: (action) => {
                action.icon = this._expanded ? "fas fa-compress" : "fas fa-expand";
                action.title = this._expanded ? "msg#facetCard.reduce" : "msg#facetCard.enlarge";
            }
        });

        this.settingsAction = new Action({
            action: (action) => {
                this._settingsOpened = !this._settingsOpened;
                this.settingsOpened.next(this._settingsOpened? "opened" : "saved");
                if(!!this.facetComponent){
                    this.facetComponent.onOpenSettings(this._settingsOpened);
                }
                action.update();
            },
            updater: (action) => {
                action.icon = this._settingsOpened ? "far fa-save" : "fas fa-cog";
                action.title = this._settingsOpened ? "msg#facetCard.saveSettings" : "msg#facetCard.openSettings";
            }
        });

    }

    ngOnInit(){
        // Initialize actions
        this._collapsed = this.startCollapsed;
        this._expanded = this.startExpanded;
        this._settingsOpened = this.startSettingsOpened;

        this.collapseAction.update();
        this.expandAction.update();
        this.settingsAction.update();
    }

    ngAfterContentInit(){
        if(this.facetComponent) {
            this.actionChangedSubscription = this.facetComponent.actionsChanged.subscribe((actions) => {
                this.allActions.forEach(action => action.update());
                this.changeDetectorRef.markForCheck();
            });
        }
        else {
            console.warn("No #facet component is defined in this facet card: ", this.title);
        }
    }

    ngOnDestroy(){
        if(this.actionChangedSubscription){
            this.actionChangedSubscription.unsubscribe();
        }
    }

    public get allActions() : Action[] {
        if(this.hideActionsCollapsed && this._collapsed) return [this.collapseAction]; // Hide other actions if collapsed
        let actions = [] as Action[];
        if(this.actionsFirst) {
            actions.push(...this.actions);
        }
        if(this.facetComponent) actions = actions.concat(this.facetComponent.actions);
        if(this.hasSettings) actions.push(this.settingsAction);
        if(this.expandable) actions.push(this.expandAction);
        if(this.collapsible) actions.push(this.collapseAction);
        if(!this.actionsFirst) {
            actions.push(...this.actions);
        }
        return actions;
    }

    public get hasSettings(){
        return !!this.facetComponent && !!this.facetComponent.settingsTpl;
    }

    @HostListener('window:click', ['$event'])
    clickOut() {
        if (this.collapseOnClickOutside) {
            this._collapsed = true;
            this.collapseAction.update();
            this.expandAction.update();
            this.settingsAction.update();
        }
    }
}
Example #22
Source File: vg-player.ts    From ngx-videogular with MIT License 4 votes vote down vote up
@Component({
  selector: 'vg-player',
  encapsulation: ViewEncapsulation.None,
  template: `<ng-content></ng-content>`,
  styles: [`
        vg-player {
            font-family: 'videogular';
            position: relative;
            display: flex;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: black;
        }
        vg-player.fullscreen {
            position: fixed;
            left: 0;
            top: 0;
        }
        vg-player.native-fullscreen.controls-hidden {
            cursor: none;
        }
    ` ],
  providers: [VgAPI, VgFullscreenAPI, VgControlsHidden]
})
// tslint:disable:component-class-suffix
 // tslint:disable:no-output-on-prefix
export class VgPlayer implements AfterContentInit, OnDestroy {
  elem: HTMLElement;

  @HostBinding('class.fullscreen') isFullscreen = false;
  @HostBinding('class.native-fullscreen') isNativeFullscreen = false;
  @HostBinding('class.controls-hidden') areControlsHidden = false;
  @HostBinding('style.z-index') zIndex: string;


  @Output()
  onPlayerReady: EventEmitter<any> = new EventEmitter();

  @Output()
  onMediaReady: EventEmitter<any> = new EventEmitter();

  @ContentChildren(VgMedia)
  medias: QueryList<VgMedia>;

  subscriptions: Subscription[] = [];

  constructor(ref: ElementRef, public api: VgAPI, public fsAPI: VgFullscreenAPI, private controlsHidden: VgControlsHidden) {
    this.elem = ref.nativeElement;

    this.api.registerElement(this.elem);
  }

  ngAfterContentInit() {
    this.medias.toArray().forEach((media) => {
      this.api.registerMedia(media);
    });

    this.fsAPI.init(this.elem, this.medias);

    this.subscriptions.push(this.fsAPI.onChangeFullscreen.subscribe(this.onChangeFullscreen.bind(this)));
    this.subscriptions.push(this.controlsHidden.isHidden.subscribe(this.onHideControls.bind(this)));

    this.api.onPlayerReady(this.fsAPI);
    this.onPlayerReady.emit(this.api);
  }

  onChangeFullscreen(fsState: boolean) {
    if (!this.fsAPI.nativeFullscreen) {
      this.isFullscreen = fsState;
      this.zIndex = fsState ? VgUtils.getZIndex().toString() : 'auto';
    } else {
      this.isNativeFullscreen = fsState;
    }
  }

  onHideControls(hidden: boolean) {
    this.areControlsHidden = hidden;
  }

  ngOnDestroy() {
    this.subscriptions.forEach(s => s.unsubscribe());
  }
}
Example #23
Source File: index.ts    From nativescript-plugins with Apache License 2.0 4 votes vote down vote up
@Directive()
export abstract class AccordionItemsComponent implements DoCheck, OnDestroy, AfterContentInit {
  public abstract get nativeElement(): any;

  protected accordionItemsView: any;
  protected _items: any;
  protected _differ: IterableDiffer<KeyedTemplate>;
  protected _templateHeaderMap: Map<string, KeyedTemplate>;
  protected _templateItemHeaderMap: Map<string, KeyedTemplate>;
  protected _templateItemContentMap: Map<string, KeyedTemplate>;
  protected _templateFooterMap: Map<string, KeyedTemplate>;

  @ViewChild('loader', {read: ViewContainerRef, static: false})
  loader: ViewContainerRef;

  @Output()
  public setupItemView = new EventEmitter<SetupItemViewArgs>();

  @ContentChild(TemplateRef, {static: false})
  itemTemplateQuery: TemplateRef<ItemContext>;

  headerTemplate: TemplateRef<ItemContext>;

  itemHeaderTemplate: TemplateRef<ItemContext>;

  itemContentTemplate: TemplateRef<ItemContext>;

  footerTemplate: TemplateRef<ItemContext>;

  @Input()
  get items() {
    return this._items;
  }

  set items(value: any) {
    this._items = value;
    let needDiffer = true;
    if (value instanceof ObservableArray) {
      needDiffer = false;
    }
    if (needDiffer && !this._differ && isListLikeIterable(value)) {
      this._differ = this._iterableDiffers.find(this._items)
        .create((_index, item) => {
          return item;
        });
    }

    this.accordionItemsView.items = this._items;
  }

  constructor(_elementRef: ElementRef,
              private _iterableDiffers: IterableDiffers) {
    this.accordionItemsView = _elementRef.nativeElement;
    this.accordionItemsView.on('headerLoading', this.onHeaderLoading, this);
    this.accordionItemsView.on('itemHeaderLoading', this.onItemHeaderLoading, this);
    this.accordionItemsView.on('itemContentLoading', this.onItemContentLoading, this);
    this.accordionItemsView.on('footerLoading', this.onFooterLoading, this);

  }

  ngAfterContentInit() {
    this.setItemTemplates();
  }

  ngOnDestroy() {
    this.accordionItemsView.off('headerLoading', this.onHeaderLoading, this);
    this.accordionItemsView.off('itemHeaderLoading', this.onItemHeaderLoading, this);
    this.accordionItemsView.off('itemContentLoading', this.onItemContentLoading, this);
    this.accordionItemsView.off('footerLoading', this.onFooterLoading, this);
  }

  private setItemTemplates() {

    this.itemHeaderTemplate = this.itemTemplateQuery;

    this.accordionItemsView._getHasHeader = () => {
      return false;
    };

    this.accordionItemsView._getHasFooter = () => {
      return false;
    };
    if (this._templateHeaderMap) {
      const templates: KeyedTemplate[] = [];
      this._templateHeaderMap.forEach(value => {
        templates.push(value);
      });

      if (templates.length === 1) {
        this.accordionItemsView.headerTemplateSelector = (item: any, index: number, items: any) => {
          return 'header';
        };
      }

      if (templates.length > 0) {
        this.accordionItemsView._getHasHeader = () => {
          return true;
        };
      }
      this.accordionItemsView.headerTemplates = templates;
    }

    if (this._templateItemHeaderMap) {
      const templates: KeyedTemplate[] = [];
      this._templateItemHeaderMap.forEach(value => {
        templates.push(value);
      });

      this.accordionItemsView.itemHeaderTemplates = templates;

      if (templates.length === 1) {
        this.accordionItemsView.itemHeaderTemplateSelector = (item: any, index: number, items: any) => {
          return 'title';
        };
      }

    } else {
      this.getItemTemplateViewFactory(this.itemHeaderTemplate);
    }

    if (this._templateItemContentMap) {
      const templates: KeyedTemplate[] = [];
      this._templateItemContentMap.forEach(value => {
        templates.push(value);
      });

      if (templates.length === 1) {
        this.accordionItemsView.itemContentTemplateSelector = (item: any, parentIndex: number, index: number, items: any) => {
          return 'content';
        };
      }

      this.accordionItemsView.itemContentTemplates = templates;
    }

    if (this._templateFooterMap) {
      const templates: KeyedTemplate[] = [];
      this._templateFooterMap.forEach(value => {
        templates.push(value);
      });

      if (templates.length === 1) {
        this.accordionItemsView.footerTemplateSelector = (item: any, index: number, items: any) => {
          return 'footer';
        };
      }

      if (templates.length > 0) {
        this.accordionItemsView._getHasFooter = () => {
          return true;
        };
      }

      this.accordionItemsView.footerTemplates = templates;
    }
  }

  public registerTemplate(key: string, template: TemplateRef<ItemContext>) {

    if (key === 'header' || key.startsWith('header-')) {
      if (!this._templateHeaderMap) {
        this._templateHeaderMap = new Map<string, KeyedTemplate>();
      }

      const keyedTemplate = {
        key,
        createView: this.getItemTemplateViewFactory(template)
      };

      this._templateHeaderMap.set(key, keyedTemplate);
    }

    if (key === 'title' || key.startsWith('title-')) {
      if (!this._templateItemHeaderMap) {
        this._templateItemHeaderMap = new Map<string, KeyedTemplate>();
      }

      const keyedTemplate = {
        key,
        createView: this.getItemTemplateViewFactory(template)
      };

      this._templateItemHeaderMap.set(key, keyedTemplate);
    }

    if (key === 'content' || key.startsWith('content-')) {
      if (!this._templateItemContentMap) {
        this._templateItemContentMap = new Map<string, KeyedTemplate>();
      }

      const keyedTemplate = {
        key,
        createView: this.getChildItemTemplateViewFactory(template)
      };

      this._templateItemContentMap.set(key, keyedTemplate);
    }

    if (key === 'footer' || key.startsWith('footer-')) {
      if (!this._templateFooterMap) {
        this._templateFooterMap = new Map<string, KeyedTemplate>();
      }

      const keyedTemplate = {
        key,
        createView: this.getItemTemplateViewFactory(template)
      };

      this._templateFooterMap.set(key, keyedTemplate);
    }
  }

  @profile
  public onHeaderLoading(args: ItemEventData) {
    if (!args.view && !this.headerTemplate) {
      return;
    }

    const index = args.index;
    const items = (<any>args.object).items;
    const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
    let viewRef: EmbeddedViewRef<ItemContext>;

    if (args.view) {

      viewRef = args.view[NG_VIEW];
      // Getting angular view from original element (in cases when ProxyViewContainer
      // is used NativeScript internally wraps it in a StackLayout)
      if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
        viewRef = args.view.getChildAt(0)[NG_VIEW];
      }
    }

    if (!viewRef) {
      viewRef = this.loader.createEmbeddedView(this.headerTemplate, new ItemContext(), 0);
      args.view = getItemViewRoot(viewRef);
      args.view[NG_VIEW] = viewRef;
    }

    this.setupViewRef(viewRef, currentItem, index);

    this.detectChangesOnChild(viewRef, index);
  }

  @profile
  public onItemHeaderLoading(args: ItemEventData) {
    if (!args.view && !this.itemHeaderTemplate) {
      return;
    }

    const index = args.index;
    const items = (<any>args.object).items;
    const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
    let viewRef: EmbeddedViewRef<ItemContext>;

    if (args.view) {

      viewRef = args.view[NG_VIEW];
      // Getting angular view from original element (in cases when ProxyViewContainer
      // is used NativeScript internally wraps it in a StackLayout)
      if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
        viewRef = args.view.getChildAt(0)[NG_VIEW];
      }
    }

    if (!viewRef) {
      viewRef = this.loader.createEmbeddedView(this.itemHeaderTemplate, new ItemContext(), 0);
      args.view = getItemViewRoot(viewRef);
      args.view[NG_VIEW] = viewRef;
    }

    this.setupViewRef(viewRef, currentItem, index);

    this.detectChangesOnChild(viewRef, index);
  }

  @profile
  public onItemContentLoading(args: ItemEventData) {
    if (!args.view && !this.itemContentTemplate) {
      return;
    }

    const index = args.index;
    const childIndex = (args as any).childIndex;
    const childItems = this.accordionItemsView.childItems;
    const items = (<any>args.object).items;
    const currentItem = typeof items.getItem === 'function' ? items.getItem(index)[childItems][childIndex] : items[index][childItems][childIndex];

    let viewRef: EmbeddedViewRef<ChildItemContext>;

    if (args.view) {

      viewRef = args.view[NG_VIEW];
      // Getting angular view from original element (in cases when ProxyViewContainer
      // is used NativeScript internally wraps it in a StackLayout)
      if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
        viewRef = args.view.getChildAt(0)[NG_VIEW];
      }
    }

    if (!viewRef) {
      viewRef = this.loader.createEmbeddedView(this.itemContentTemplate, new ChildItemContext(), 0);
      args.view = getItemViewRoot(viewRef);
      args.view[NG_VIEW] = viewRef;
    }

    this.setupChildViewRef(viewRef, currentItem, index, childIndex);

    this.detectChangesOnChild(viewRef, index);
  }

  @profile
  public onFooterLoading(args: ItemEventData) {
    if (!args.view && !this.footerTemplate) {
      return;
    }

    const index = args.index;
    const items = (<any>args.object).items;
    const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
    let viewRef: EmbeddedViewRef<ItemContext>;

    if (args.view) {

      viewRef = args.view[NG_VIEW];
      // Getting angular view from original element (in cases when ProxyViewContainer
      // is used NativeScript internally wraps it in a StackLayout)
      if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
        viewRef = args.view.getChildAt(0)[NG_VIEW];
      }
    }

    if (!viewRef) {
      viewRef = this.loader.createEmbeddedView(this.footerTemplate, new ItemContext(), 0);
      args.view = getItemViewRoot(viewRef);
      args.view[NG_VIEW] = viewRef;
    }

    this.setupViewRef(viewRef, currentItem, index);

    this.detectChangesOnChild(viewRef, index);
  }


  public setupViewRef(viewRef: EmbeddedViewRef<ItemContext>, data: any, index: number): void {
    const context = viewRef.context;
    context.$implicit = data;
    context.item = data;
    context.index = index;
    context.even = (index % 2 === 0);
    context.odd = !context.even;

    this.setupItemView.next({view: viewRef, data: data, index: index, context: context});
  }

  public setupChildViewRef(viewRef: EmbeddedViewRef<ChildItemContext>, data: any, parentIndex: number, index: number): void {
    const context = viewRef.context;
    context.$implicit = data;
    context.item = data;
    context.parentIndex = parentIndex;
    context.index = index;
    context.even = (index % 2 === 0);
    context.odd = !context.even;

    this.setupItemView.next({view: viewRef, data: data, index: index, context: context});
  }

  protected getItemTemplateViewFactory(template: TemplateRef<ItemContext>): () => View {
    return () => {
      const viewRef = this.loader.createEmbeddedView(template, new ItemContext(), 0);
      const resultView = getItemViewRoot(viewRef);
      resultView[NG_VIEW] = viewRef;

      return resultView;
    };
  }

  protected getChildItemTemplateViewFactory(template: TemplateRef<ChildItemContext>): () => View {
    return () => {
      const viewRef = this.loader.createEmbeddedView(template, new ChildItemContext(), 0);
      const resultView = getItemViewRoot(viewRef);
      resultView[NG_VIEW] = viewRef;

      return resultView;
    };
  }

  @profile
  private detectChangesOnChild(viewRef: EmbeddedViewRef<ItemContext>, index: number) {

    viewRef.markForCheck();
    viewRef.detectChanges();
  }

  ngDoCheck() {
    if (this._differ) {
      const changes = this._differ.diff(this._items);
      if (changes) {
        this.accordionItemsView.refresh();
      }
    }
  }
}
Example #24
Source File: dashboard.component.ts    From ng-application-builder with MIT License 4 votes vote down vote up
@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements AfterContentInit {

  appConfiguration: AppConfiguration = new AppConfiguration();

  margin = { top: 20, right: 90, bottom: 30, left: 90 };
  width = 0;
  height = 0;
  i = 0;
  duration = 750;
  root: any;
  tree: any;
  svg: any;
  treemap: any;
  selectedNode: any;

  selectedNodeName = '';
  newNodeName = '';

  name: string;

  isMenuOpen = false;

  showSpinner = false;

  constructor(
    private dataService: DataService,
    public dialog: MatDialog) { }

  ngAfterContentInit() {
    this.appConfiguration.nodeConfiguration = new NodeConfiguration();
    this.appConfiguration.nodeConfiguration.name = 'app';
    this.appConfiguration.nodeConfiguration.type = NodeType.module;


    this.width = 960 - this.margin.right - this.margin.left;
    this.height = 500 - this.margin.top - this.margin.bottom;

    console.log('initialized...');

    this.svg = d3.select('svg')
      .on('click', () => {
        if (this.isMenuOpen) {
          d3.select('#my_custom_menu')
            .style('display', 'none');
          this.isMenuOpen = false;
        }
      })
      // .attr('width', this.width + this.margin.right + this.margin.left)
      // .attr('height', this.height + this.margin.top + this.margin.bottom)
      .attr('width', '100%')
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('transform', 'translate('
        + this.margin.left + ',' + this.margin.top + ')');


    // declares a tree layout and assigns the size
    this.treemap = d3.tree().size([this.height, this.width]);

    // Assigns parent, children, height, depth
    this.root = d3.hierarchy(this.appConfiguration.nodeConfiguration, (d: any) => d.children);
    this.root.x0 = this.height / 2;
    this.root.y0 = 0;

    this.update(this.root);

    console.log(this.svg);
  }

  openDialog(action: string): void {
    if (this.isMenuOpen) {
      d3.select('#my_custom_menu')
        .style('display', 'none');
      this.isMenuOpen = false;
    }
    this.selectedNode.action = action;
    const modalData = this.selectedNode as DialogData;
    modalData.action = action;

    const dialogRef = this.dialog.open(NewDialogComponent, {
      width: '400px',
      data: modalData
    });

    dialogRef.afterClosed().subscribe((newNode: NodeConfiguration) => {
      console.log('The dialog was closed', newNode);

      if (!this.selectedNode) {
        alert('Please select parent node!');
      }

      if (!newNode) { return; }

      if (action === 'create') {
        const node = {
          name: newNode.name,
          type: newNode.type,
          route: newNode.route,
          parentModule: this.selectedNode.data.name,
          modulePath: this.getModulePath(this.selectedNode, newNode.name),

        };

        const newD3Node: any = d3.hierarchy(node);
        newD3Node.depth = this.selectedNode.depth + 1;
        newD3Node.height = this.selectedNode.height + 1;
        newD3Node.parent = this.selectedNode;
        newD3Node.id = ++this.i;
        if (!this.selectedNode.children) {
          this.selectedNode.children = [];
          this.selectedNode.data.children = [];
        }

        this.selectedNode.children.push(newD3Node);
        this.selectedNode.data.children.push(newD3Node.data);

        this.update(this.selectedNode);
      } else {
        // TODO: Need to loop through all children of selected node and update their modulePath, route and name
        // console.log('updating existing node...');
        // this.updateModulePath(newNode);
        // debugger;
        // this.svg.select('#text-' + this.selectedNode.id).text(this.getNodeText(newNode));
        // this.updateNodeName();
      }

    });
  }

  updateModulePath(newNode: NodeConfiguration) {
    const parts = newNode.modulePath.split('/');
    parts[parts.length - 1] = newNode.name;
    newNode.modulePath = parts.join('/');


  }

  update(source: any) {
    // console.log(this.appConfiguration.nodeConfiguration);

    // Assigns the x and y position for the nodes
    const treeData = this.treemap(this.root);

    // Compute the new tree layout.
    const nodes = treeData.descendants();
    const links = treeData.descendants().slice(1);

    // Normalize for fixed-depth.
    nodes.forEach((d: any) => { d.y = d.depth * 180; });

    // ****************** Nodes section ***************************

    // Update the nodes...
    const node = this.svg.selectAll('g.node')
      .data(nodes, (d: any) => d.id || (d.id = ++this.i));

    // Enter any new modes at the parent's previous position.
    const nodeEnter = node.enter().append('g')
      .attr('class', 'node')
      .attr('transform', (d: any) => {
        return 'translate(' + source.y0 + ',' + source.x0 + ')';
      })
      .on('click', (e: any) => { this.click(e); });

    // Add Circle for the nodes
    nodeEnter.append('circle')
      .attr('class', 'node')
      .attr('r', 1e-6)
      .style('fill', (d: any) => {
        // return d.data.type == NodeType.module ? "black" : "white";
        // return d._children ? "lightsteelblue" : "#fff";
      })
      .on('click', (selectedNode) => {
        this.isMenuOpen = true;
        this.selectedNode = selectedNode;
        d3.select('#my_custom_menu')
          .style('position', 'absolute')
          .style('left', d3.event.clientX + 10 + 'px')
          .style('top', d3.event.clientY + 10 + 'px')
          .style('display', 'block');
        d3.event.stopPropagation();
      });


    // Add labels for the nodes
    nodeEnter.append('text')
      .attr('dy', '.35em')
      .attr('x', (d: any) => {
        return d.children || d._children ? -13 : 13;
      })
      .attr('text-anchor', (d: any) => {
        return d.children || d._children ? 'end' : 'start';
      })
      .attr('id', (d: any) => 'text-' + d.id)
      .text((d: any) => {
        return this.getNodeText(d.data);
      });

    // UPDATE
    const nodeUpdate = nodeEnter.merge(node);

    // Transition to the proper position for the node
    nodeUpdate.transition()
      .duration(this.duration)
      .attr('transform', (d: any) => {
        return 'translate(' + d.y + ',' + d.x + ')';
      });

    // Update the node attributes and style
    nodeUpdate.select('circle.node')
      .attr('r', 10)
      .style('fill', (d: any) => {
        // return d.data.type == NodeType.module ? "black" : "white";

        return d._children ? 'lightsteelblue' : '#fff';
      })
      .attr('cursor', 'pointer');

    // Remove any exiting nodes
    const nodeExit = node.exit().transition()
      .duration(this.duration)
      .attr('transform', (d: any) => {
        return 'translate(' + source.y + ',' + source.x + ')';
      })
      .remove();

    // On exit reduce the node circles size to 0
    nodeExit.select('circle')
      .attr('r', 1e-6);

    // On exit reduce the opacity of text labels
    nodeExit.select('text')
      .style('fill-opacity', 1e-6);

    // ****************** links section ***************************

    // Update the links...
    const link = this.svg.selectAll('path.link')
      .data(links, (d: any) => d.id);

    // Enter any new links at the parent's previous position.
    const linkEnter = link.enter().insert('path', 'g')
      .attr('class', 'link')
      .attr('d', (d: any) => {
        const o = { x: source.x0, y: source.y0 };
        return this.diagonal(o, o);
      });

    // UPDATE
    const linkUpdate = linkEnter.merge(link);

    // Transition back to the parent element position
    linkUpdate.transition()
      .duration(this.duration)
      .attr('d', (d: any) => this.diagonal(d, d.parent));

    // Remove any exiting links
    const linkExit = link.exit().transition()
      .duration(this.duration)
      .attr('d', (d: any) => {
        const o = { x: source.x, y: source.y };
        return this.diagonal(o, o);
      })
      .remove();

    // Store the old positions for transition.
    nodes.forEach((d: any) => {
      d.x0 = d.x;
      d.y0 = d.y;
    });
  }

  getNodeText(node: any) {
    return node.name + ' (' + node.type + ')';
  }

  diagonal(s: any, d: any) {
    const path = `M ${s.y} ${s.x}
        C ${(s.y + d.y) / 2} ${s.x},
        ${(s.y + d.y) / 2} ${d.x},
        ${d.y} ${d.x}`;

    return path;
  }

  click(d: any) {
    this.selectedNode = d;
    this.selectedNodeName = this.selectedNode.data.name;

    this.svg.selectAll('circle')
      .style('fill', '#fff');

    this.svg.selectAll('circle')
      .filter((node: any) => node.id === d.id)
      .style('fill', (node: any) => {
        return node.id === d.id ? 'black' : '#fff';
      });

  }

  addNode() {
    if (!this.selectedNode) {
      alert('Please select parent node!');
    }
    const newNode = { name: this.newNodeName, type: NodeType.moduleWithRoute };
    const newD3Node: any = d3.hierarchy(newNode);
    newD3Node.depth = this.selectedNode.depth + 1;
    newD3Node.height = this.selectedNode.height + 1;
    newD3Node.parent = this.selectedNode;
    newD3Node.id = ++this.i;
    if (!this.selectedNode.children) {
      this.selectedNode.children = [];
      this.selectedNode.data.children = [];
    }

    this.selectedNode.children.push(newD3Node);
    this.selectedNode.data.children.push(newD3Node.data);

    this.update(this.selectedNode);

  }

  removeNode() {
    const children = [];
    this.selectedNode.parent.children.forEach(child => {
      if (child.id !== this.selectedNode.id) {
        children.push(child);
      }
    });

    const dataChildren = [];
    this.selectedNode.parent.data.children.forEach(child => {
      if (child.id !== this.selectedNode.id) {
        dataChildren.push(child);
      }
    });

    this.selectedNode.parent.children = children;
    this.selectedNode.parent.data.children = children;

    if (this.selectedNode.parent.children.length === 0) {
      delete this.selectedNode.parent.children;
    }
    this.update(this.selectedNode.parent);

    if (this.isMenuOpen) {
      d3.select('#my_custom_menu')
        .style('display', 'none');
      this.isMenuOpen = false;
    }
  }

  updateNodeName() {
    this.selectedNode.data.name = this.selectedNodeName;
    // console.log(this.selectedNode);
    this.svg.select('#text-' + this.selectedNode.id).text(this.selectedNodeName);

    this.update(this.selectedNode);
  }

  addNodeWithComponent() {
    if (!this.selectedNode) {
      alert('Please select parent node!');
    }
    const newNode = {
      name: this.newNodeName,
      type: NodeType.moduleWithRoute,
      // modulePath: this.selectedNode.data.name == 'app' ? '' : this.selectedNode.data.name,
      modulePath: this.getModulePath(this.selectedNode, this.newNodeName),
      route: this.newNodeName,
      parentModule: this.selectedNode.data.name
    };
    const newD3Node: any = d3.hierarchy(newNode);
    newD3Node.depth = this.selectedNode.depth + 1;
    newD3Node.height = this.selectedNode.height + 1;
    newD3Node.parent = this.selectedNode;
    newD3Node.id = ++this.i;
    if (!this.selectedNode.children) {
      this.selectedNode.children = [];
      this.selectedNode.data.children = [];
    }

    this.selectedNode.children.push(newD3Node);
    this.selectedNode.data.children.push(newD3Node.data);

    this.update(this.selectedNode);

  }

  getModulePath(node: any, newNodeName: string) {
    // this.selectedNode.data.name == 'app' ? '' : this.selectedNode.data.name
    console.log(node);

    const modulePath: Array<string> = [];
    modulePath.push(newNodeName);

    if (node.parent && node.parent.data.name === 'app') {
      modulePath.push(node.data.name);
    } else {
      while (node.parent && node.data.name !== 'app') {
        modulePath.push(node.data.name);
        node = node.parent;
      }
    }
    if (modulePath.length === 1) {
      return newNodeName;
    }
    const fullModulePath = modulePath.reverse().join('/');
    console.log(fullModulePath);

    return fullModulePath;
  }

  generateApp() {
    this.showSpinner = true;
    this.dataService.generateApp(this.appConfiguration).subscribe((response: any) => {
      console.log(response);
      sdk.openProject(response);
      this.showSpinner = false;
    }, (err) => {
      this.showSpinner = false;
      alert(JSON.stringify(err));
      console.error(err);
    });

  }

  downloadApp() {
    this.showSpinner = true;
    this.dataService.downloadApp(this.appConfiguration).subscribe((response: any) => {
      saveAs(response, this.appConfiguration.name + '.zip');
      console.log(response);
      this.showSpinner = false;
    }, (err) => {
      this.showSpinner = false;
      alert(JSON.stringify(err));
      console.error(err);
    });

  }
}
Example #25
Source File: vg-player.component.ts    From ngx-videogular with MIT License 4 votes vote down vote up
@Component({
  selector: 'vg-player',
  encapsulation: ViewEncapsulation.None,
  template: `<ng-content></ng-content>`,
  styles: [
    `
      vg-player {
        font-family: 'videogular';
        position: relative;
        display: flex;
        width: 100%;
        height: 100%;
        overflow: hidden;
        background-color: black;
      }
      vg-player.fullscreen {
        position: fixed;
        left: 0;
        top: 0;
      }
      vg-player.native-fullscreen.controls-hidden {
        cursor: none;
      }
    `,
  ],
  providers: [VgApiService, VgFullscreenApiService, VgControlsHiddenService],
})
export class VgPlayerComponent implements AfterContentInit, OnDestroy {
  elem: HTMLElement;

  @HostBinding('class.fullscreen') isFullscreen = false;
  @HostBinding('class.native-fullscreen') isNativeFullscreen = false;
  @HostBinding('class.controls-hidden') areControlsHidden = false;
  @HostBinding('style.z-index') zIndex: string;

  @Output() onPlayerReady: EventEmitter<VgApiService> = new EventEmitter<VgApiService>();
  @Output() onMediaReady: EventEmitter<any> = new EventEmitter();

  @ContentChildren(VgMediaDirective) medias: QueryList<VgMediaDirective>;

  subscriptions: Subscription[] = [];

  constructor(
    ref: ElementRef,
    public api: VgApiService,
    public fsAPI: VgFullscreenApiService,
    private controlsHidden: VgControlsHiddenService
  ) {
    this.elem = ref.nativeElement;

    this.api.registerElement(this.elem);
  }

  ngAfterContentInit() {
    this.medias.toArray().forEach((media) => {
      this.api.registerMedia(media);
    });

    this.fsAPI.init(this.elem, this.medias);

    this.subscriptions.push(
      this.fsAPI.onChangeFullscreen.subscribe(
        this.onChangeFullscreen.bind(this)
      )
    );
    this.subscriptions.push(
      this.controlsHidden.isHidden.subscribe(this.onHideControls.bind(this))
    );

    this.api.onPlayerReady(this.fsAPI);
    this.onPlayerReady.emit(this.api);
  }

  onChangeFullscreen(fsState: boolean) {
    if (!this.fsAPI.nativeFullscreen) {
      this.isFullscreen = fsState;
      this.zIndex = fsState ? VgUtilsService.getZIndex().toString() : 'auto';
    } else {
      this.isNativeFullscreen = fsState;
    }
  }

  onHideControls(hidden: boolean) {
    this.areControlsHidden = hidden;
  }

  ngOnDestroy() {
    this.subscriptions.forEach((s) => s.unsubscribe());
  }
}
Example #26
Source File: toc-container.directive.ts    From alauda-ui with MIT License 4 votes vote down vote up
@Directive({
  selector: '[auiTocContainer]',
  exportAs: 'auiTocContainer',
})
export class TocContainerDirective implements AfterContentInit, OnDestroy {
  @Output()
  activedChange = new EventEmitter<string>();

  private _contents: TocContentDirective[] = [];
  private readonly _scrollTop$ = new Subject<number>();
  private readonly _scrollTo$ = new Subject<string>();
  private readonly _onDestroy$ = new Subject<void>();
  private readonly _subs: Subscription[] = [];
  private _nativeElement: HTMLElement;

  get scrollTop(): number {
    return this._nativeElement.scrollTop || 0;
  }

  set scrollTop(value: number) {
    this._nativeElement.scrollTop = value;
  }

  get isScrollEnd() {
    return (
      this._nativeElement.scrollHeight - this._nativeElement.scrollTop ===
      this._nativeElement.clientHeight
    );
  }

  constructor(
    elementRef: ElementRef<HTMLElement>,
    private readonly cdr: ChangeDetectorRef,
  ) {
    this._nativeElement = elementRef.nativeElement;
  }

  getOffsetTop(element: HTMLElement): number {
    if (element.parentElement === this._nativeElement) {
      return element.offsetTop;
    }
    return element.offsetTop + this.getOffsetTop(element.parentElement);
  }

  private getMinContent(scrollTop: number) {
    return (minContent: TocContentDirective, content: TocContentDirective) => {
      if (
        Math.abs(scrollTop - this.getOffsetTop(content.nativeElement)) <
        Math.abs(scrollTop - this.getOffsetTop(minContent.nativeElement))
      ) {
        minContent = content;
      }
      return minContent;
    };
  }

  private getMaxContent(
    maxContent: TocContentDirective,
    content: TocContentDirective,
  ) {
    if (
      this.getOffsetTop(content.nativeElement) >
      this.getOffsetTop(maxContent.nativeElement)
    ) {
      maxContent = content;
    }
    return maxContent;
  }

  @HostListener('scroll')
  onScroll() {
    this._scrollTop$.next(this.scrollTop);
  }

  ngAfterContentInit() {
    const actived$ = this._scrollTop$
      .pipe(
        startWith(this.scrollTop),
        debounceTime(200),
        map(scrollTop =>
          this._contents.reduce(
            this.isScrollEnd
              ? this.getMaxContent.bind(this)
              : this.getMinContent(scrollTop),
          ),
        ),
        map(actived => actived.auiTocContent),
      )
      .pipe(
        tap(actived => {
          this._contents.forEach(content => {
            content.active = actived === content.auiTocContent;
          });
          this.cdr.detectChanges();
        }),
      );

    const scrollTween$ = this._scrollTo$.pipe(
      switchMap(name => {
        const target = this._contents.find(
          content => content.auiTocContent === name,
        );

        if (!target) {
          return EMPTY;
        }
        const destination = this.getOffsetTop(target.nativeElement);

        const start = performance.now();
        const source = this.scrollTop;
        const duration = 500;

        return of(0).pipe(
          observeOn(animationFrameScheduler),
          repeat(),
          map(() => (performance.now() - start) / duration),
          takeWhile(t => t < 1),
          endWith(1),
          map(t => t * t * t),
          map(t => source * (1 - t) + destination * t),
        );
      }),
      takeUntil(this._onDestroy$),
    );

    this._subs.push(
      actived$.subscribe(this.activedChange),
      scrollTween$.subscribe(tweenValue => {
        this.scrollTop = tweenValue;
      }),
    );
  }

  ngOnDestroy() {
    this._subs.forEach(sub => sub.unsubscribe());
    this._onDestroy$.next();
  }

  scrollTo(content: string | string[]) {
    if (Array.isArray(content)) {
      this._scrollTo$.next(content[0]);
    } else {
      this._scrollTo$.next(content);
    }
  }

  registerContent(tocContent: TocContentDirective) {
    this._contents = [...this._contents, tocContent];
  }

  deregisterContent(tocContent: TocContentDirective) {
    this._contents = this._contents.filter(
      content => content.auiTocContent !== tocContent.auiTocContent,
    );
  }
}
Example #27
Source File: accordion-item.component.ts    From canopy with Apache License 2.0 4 votes vote down vote up
@Component({
  selector: 'lg-accordion-item',
  templateUrl: './accordion-item.component.html',
  styleUrls: [ './accordion-item.component.scss' ],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  // Do not provide LG_ACCORDION to nested accordion components
  providers: [ { provide: LG_ACCORDION, useValue: undefined } ],
})
export class LgAccordionItemComponent implements AfterContentInit, OnChanges, OnDestroy {
  private _toggleSubscription: Subscription;
  _id = `${nextUniqueId++}`;
  _toggleId = `lg-accordion-panel-heading-${this._id}`;
  _panelId = `lg-accordion-panel-${this._id}`;
  _showContent = false;
  _contentTemplate: TemplateRef<any>;

  @Input() isActive = false;
  @Output() opened = new EventEmitter<void>();
  @Output() closed = new EventEmitter<void>();

  @ViewChild(LgAccordionItemContentDirective, { static: true })
  defaultContent: LgAccordionItemContentDirective;
  @ContentChild(LgAccordionItemContentDirective)
  lazyContent: LgAccordionItemContentDirective;
  @ContentChild(LgAccordionPanelHeadingComponent)
  accordionPanelHeading: LgAccordionPanelHeadingComponent;

  constructor(
    @Optional() @SkipSelf() @Inject(LG_ACCORDION) public accordion: LgAccordionComponent,
    private selectionDispatcher: UniqueSelectionDispatcher,
    private cdr: ChangeDetectorRef,
  ) {
    this._removeSingleItemSelectionListener = selectionDispatcher.listen(
      (id: string, accordionId: string) => {
        if (
          accordion &&
          !accordion.multi &&
          accordion.id === accordionId &&
          this._id !== id
        ) {
          this.isActive = false;
          this.accordionPanelHeading.isActive = false;

          this.closed.emit();
          this.cdr.markForCheck();
        }
      },
    );
  }

  ngAfterContentInit() {
    this.accordionPanelHeading.isActive = this.isActive;
    this._showContent = this.isActive || !this.lazyContent;
    this._contentTemplate = (this.lazyContent || this.defaultContent)._template;

    if (this.isActive) {
      this.toggleActiveState(this.isActive);
    }

    this._toggleSubscription = this.accordionPanelHeading.toggleActive.subscribe(
      isActive => {
        this.isActive = isActive;

        this.toggleActiveState(isActive);
        this.cdr.markForCheck();
      },
    );
  }

  ngOnChanges(changes: SimpleChanges) {
    if (this.accordionPanelHeading && changes.isActive) {
      const isActive = changes.isActive.currentValue || false;

      this.accordionPanelHeading.isActive = isActive;
      this.toggleActiveState(isActive);
    }
  }

  ngOnDestroy() {
    this._toggleSubscription?.unsubscribe();
    this._removeSingleItemSelectionListener();
  }

  private toggleActiveState(isActive: boolean) {
    if (isActive) {
      if (!this._showContent) {
        this._showContent = true;
      }

      this.opened.emit();

      this.selectionDispatcher.notify(
        this._id,
        this.accordion
          ? this.accordion.id
          : this._panelId,
      );
    } else {
      this.closed.emit();
    }
  }

  private readonly _removeSingleItemSelectionListener: () => void = () => {};
}
Example #28
Source File: chat.component.ts    From qd-messages-ts with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * Conversational UI collection - a set of components for chat-like UI construction.
 *
 * Main features:
 * - different message types support (text, image, file, file group, map, etc)
 * - drag & drop for images and files with preview
 * - different UI styles
 * - custom action buttons (coming soon)
 *
 * Here's a complete example build in a bot-like app. Type `help` to be able to receive different message types.
 * Enjoy the conversation and the beautiful UI.
 * @stacked-example(Showcase, chat/chat-showcase.component)
 *
 * Basic chat configuration and usage:
 * ```ts
 * <nb-chat title="Nebular Conversational UI">
 *       <nb-chat-message *ngFor="let msg of messages"
 *                        [type]="msg.type"
 *                        [message]="msg.text"
 *                        [reply]="msg.reply"
 *                        [sender]="msg.user.name"
 *                        [date]="msg.date"
 *                        [files]="msg.files"
 *                        [quote]="msg.quote"
 *                        [latitude]="msg.latitude"
 *                        [longitude]="msg.longitude"
 *                        [avatar]="msg.user.avatar">
 *   </nb-chat-message>
 *
 *   <nb-chat-form (send)="sendMessage($event)" [dropFiles]="true">
 *   </nb-chat-form>
 * </nb-chat>
 * ```
 * ### Installation
 *
 * Import `NbChatModule` to your feature module.
 * ```ts
 * @NgModule({
 *   imports: [
 *     // ...
 *     NbChatModule,
 *   ],
 * })
 * export class PageModule { }
 * ```
 *
 * If you need to provide an API key for a `map` message type (which is required by Google Maps)
 * you may use `NbChatModule.forRoot({ ... })` call if this is a global app configuration
 * or `NbChatModule.forChild({ ... })` for a feature module configuration:
 *
 * ```ts
 * @NgModule({
 *   imports: [
 *     // ...
 *     NbChatModule.forRoot({ messageGoogleMapKey: 'MAP_KEY' }),
 *   ],
 * })
 * export class AppModule { }
 * ```
 *
 * ### Usage
 *
 * There are three main components:
 * ```ts
 * <nb-chat>
 * </nb-chat> // chat container
 *
 * <nb-chat-form>
 * </nb-chat-form> // chat form with drag&drop files feature
 *
 * <nb-chat-message>
 * </nb-chat-message> // chat message, available multiple types
 * ```
 *
 * Two users conversation showcase:
 * @stacked-example(Conversation, chat/chat-conversation-showcase.component)
 *
 * Chat UI is also available in different colors by specifying a `[status]` input:
 *
 * @stacked-example(Colored Chat, chat/chat-colors.component)
 *
 * Also it is possible to configure sizes through `[size]` input:
 *
 * @stacked-example(Chat Sizes, chat/chat-sizes.component)
 *
 * @styles
 *
 * chat-background-color:
 * chat-border:
 * chat-border-radius:
 * chat-shadow:
 * chat-padding:
 * chart-scrollbar-color:
 * chart-scrollbar-background-color:
 * chart-scrollbar-width:
 * chat-text-color:
 * chat-text-font-family:
 * chat-text-font-size:
 * chat-text-font-weight:
 * chat-text-line-height:
 * chat-header-text-color:
 * chat-header-text-font-family:
 * chat-header-text-font-size:
 * chat-header-text-font-weight:
 * chat-header-text-line-height:
 * chat-tiny-height:
 * chat-small-height:
 * chat-medium-height:
 * chat-large-height:
 * chat-giant-height:
 * chat-primary-background-color:
 * chat-primary-text-color:
 * chat-success-background-color:
 * chat-success-text-color:
 * chat-info-background-color:
 * chat-info-text-color:
 * chat-warning-background-color:
 * chat-warning-text-color:
 * chat-danger-background-color:
 * chat-danger-text-color:
 * chat-divider-color:
 * chat-divider-style:
 * chat-divider-width:
 */
@Component({
  selector: 'nb-chat',
  styleUrls: ['./chat.component.scss'],
  template: `
    <div class="header">{{ title }}</div>
    <div class="scrollable" #scrollable>
      <div class="messages">
        <ng-content select="nb-chat-message"></ng-content>
        <p class="no-messages" *ngIf="!messages?.length">No messages yet.</p>
      </div>
    </div>
    <div class="form">
      <ng-content select="nb-chat-form"></ng-content>
    </div>
  `,
})
export class NbChatComponent implements OnChanges, AfterContentInit, AfterViewInit {

  @Input() title: string;

  /**
   * Chat size, available sizes:
   * `tiny`, `small`, `medium`, `large`, `giant`
   */
  @Input() size: NbComponentSize;

  /**
   * Chat status color (adds specific styles):
   * `primary`, `success`, `info`, `warning`, `danger`
   */
  @Input() status: NbComponentStatus;

  /**
   * Scroll chat to the bottom of the list when a new message arrives
   */
  @Input()
  get scrollBottom(): boolean {
    return this._scrollBottom
  }
  set scrollBottom(value: boolean) {
    this._scrollBottom = convertToBoolProperty(value);
  }
  protected _scrollBottom: boolean = true;

  @ViewChild('scrollable', { static: false }) scrollable: ElementRef;
  @ContentChildren(NbChatMessageComponent) messages: QueryList<NbChatMessageComponent>;
  @ContentChild(NbChatFormComponent, { static: false }) chatForm: NbChatFormComponent;

  ngOnChanges(changes: SimpleChanges) {
    if ('status' in changes) {
      this.updateFormStatus();
    }
  }

  ngAfterContentInit() {
    this.updateFormStatus();
  }

  ngAfterViewInit() {
    this.messages.changes
      .subscribe((messages) => {
        this.messages = messages;
        this.updateView();
      });

    this.updateView();
  }

  updateView() {
    if (this.scrollBottom) {
      this.scrollListBottom();
    }
  }

  scrollListBottom() {
    this.scrollable.nativeElement.scrollTop = this.scrollable.nativeElement.scrollHeight;
  }

  protected updateFormStatus(): void {
    if (this.chatForm) {
      this.chatForm.setStatus(this.status);
    }
  }

  @HostBinding('class.size-tiny')
  get tiny(): boolean {
    return this.size === 'tiny';
  }

  @HostBinding('class.size-small')
  get small(): boolean {
    return this.size === 'small';
  }

  @HostBinding('class.size-medium')
  get medium(): boolean {
    return this.size === 'medium';
  }

  @HostBinding('class.size-large')
  get large(): boolean {
    return this.size === 'large';
  }

  @HostBinding('class.size-giant')
  get giant(): boolean {
    return this.size === 'giant';
  }

  @HostBinding('class.status-primary')
  get primary(): boolean {
    return this.status === 'primary';
  }

  @HostBinding('class.status-success')
  get success(): boolean {
    return this.status === 'success';
  }

  @HostBinding('class.status-info')
  get info(): boolean {
    return this.status === 'info';
  }

  @HostBinding('class.status-warning')
  get warning(): boolean {
    return this.status === 'warning';
  }

  @HostBinding('class.status-danger')
  get danger(): boolean {
    return this.status === 'danger';
  }
}
Example #29
Source File: anchor.directive.ts    From alauda-ui with MIT License 4 votes vote down vote up
@Directive({
  selector: '[auiAnchor]',
})
export class AnchorDirective implements AfterContentInit, OnDestroy {
  @Input()
  auiAnchor: HTMLElement | '';

  @Input()
  adaptPosition = true;

  @Input()
  padding = 20;

  @Input()
  minTop: number;

  @Input()
  injectId = window === window.top;

  @ContentChildren(AnchorLabelDirective, { descendants: true })
  anchorLabels: QueryList<AnchorLabelDirective>;

  get containerEl() {
    return this.elRef.nativeElement;
  }

  get scrollableEl() {
    const el = this.containerEl;
    return (
      this.auiAnchor ||
      this.cdkScrollable?.getElementRef().nativeElement ||
      (el.scrollHeight > el.offsetHeight ? el : window)
    );
  }

  anchorPortal: ComponentPortal<AnchorComponent>;

  destroy$$ = new Subject<void>();

  constructor(
    private readonly cfr: ComponentFactoryResolver,
    private readonly appRef: ApplicationRef,
    private readonly injector: Injector,
    public readonly elRef: ElementRef<HTMLElement>,
    @Optional() private readonly cdkScrollable: CdkScrollable,
  ) {}

  ngAfterContentInit() {
    const containerEl = this.containerEl;
    this.anchorPortal = new ComponentPortal(AnchorComponent);
    const portalOutlet = new DomPortalOutlet(
      document.body,
      this.cfr,
      this.appRef,
      this.injector,
    );
    const anchorComponentRef = this.anchorPortal.attach(portalOutlet);
    const anchorEl = anchorComponentRef.injector.get(ElementRef)
      .nativeElement as HTMLElement;

    requestAnimationFrame(() =>
      this.adaptAnchorPosition(containerEl, anchorEl),
    );

    this.anchorLabels.changes
      .pipe(startWith(this.anchorLabels), takeUntil(this.destroy$$))
      .subscribe((anchorLabels: QueryList<AnchorLabelDirective>) => {
        Object.assign(anchorComponentRef.instance, {
          items: anchorLabels.toArray(),
        });
      });
  }

  ngOnDestroy() {
    this.destroy$$.next();
    this.destroy$$.complete();
    this.anchorPortal.detach();
  }

  adaptAnchorPosition(containerEl: HTMLElement, anchorEl: HTMLElement) {
    const pageContentEl = containerEl.closest('.aui-page__content');
    const anchorContentEl = anchorEl.querySelector('.aui-anchor');

    merge(observeResizeOn(containerEl), fromEvent(window, 'scroll'))
      .pipe(startWith(null as void), takeUntil(this.destroy$$))
      .subscribe(() => {
        const containerRect = containerEl.getBoundingClientRect();
        Object.assign(anchorEl.style, {
          display: !containerRect.width || !containerRect.height ? 'none' : '',
          left:
            containerRect.right -
            anchorContentEl.getBoundingClientRect().width +
            'px',
          top:
            Math.max(
              containerRect.top,
              (this.minTop ??
                (pageContentEl &&
                  +getComputedStyle(pageContentEl).paddingTop.slice(0, -2))) ||
                0,
            ) + 'px',
        });
      });

    if (this.adaptPosition) {
      observeResizeOn(anchorContentEl)
        .pipe(takeUntil(this.destroy$$))
        .subscribe(el => {
          const width = el.getBoundingClientRect().width;
          const padding = width + this.padding;
          containerEl.style.paddingRight = padding + 'px';
        });
    }
  }
}