@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
332 lines (331 loc) • 15.4 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ChangeDetectorRef, Component, ElementRef, Host, HostBinding, Input, Renderer2 } from '@angular/core';
import { LocalizationService } from '@progress/kendo-angular-l10n';
import { DraggableDirective, Keys, isObjectPresent, parseAttributes, removeHTMLAttributes, setHTMLAttributes } from '@progress/kendo-angular-common';
import { SplitterService } from './splitter.service';
import { Subscription, of } from 'rxjs';
import { delay, filter, map, switchMap, takeUntil, tap } from 'rxjs/operators';
import { shouldToggleOrResize } from './util';
import { caretAltDownIcon, caretAltLeftIcon, caretAltRightIcon, caretAltUpIcon } from '@progress/kendo-svg-icons';
import { IconWrapperComponent } from '@progress/kendo-angular-icons';
import { NgIf } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-common";
import * as i2 from "@progress/kendo-angular-l10n";
import * as i3 from "./splitter.service";
const stopPropagation = ({ originalEvent: event }) => {
event.stopPropagation();
event.preventDefault();
};
const preventOnDblClick = release => mouseDown => of(mouseDown).pipe(delay(150), takeUntil(release));
const classFromObject = classes => Object.keys(classes).filter(c => classes[c]).join(' ');
const createMoveStream = (draggable) => mouseDown => draggable.kendoDrag
.pipe(takeUntil(draggable.kendoRelease), map(({ pageX, pageY }) => ({
originalX: mouseDown.pageX,
originalY: mouseDown.pageY,
pageX,
pageY
})));
/**
* @hidden
*/
export class SplitterBarComponent {
draggable;
localization;
splitterService;
element;
renderer;
cdr;
ariaRole = 'separator';
ariaLabel = 'Splitter pane';
focused = false;
get hostOrientation() {
return this.orientation === 'horizontal' ? 'vertical' : 'horizontal';
}
get tabIndex() {
return this.splitterService.isStatic(this.index) ? -1 : 0;
}
get hostClasses() {
const isHorizontal = this.orientation === 'horizontal';
const isDraggable = this.splitterService.isDraggable(this.index);
const isStatic = this.splitterService.isStatic(this.index);
return classFromObject({
'k-focus': this.focused,
'k-splitbar': true,
'k-splitbar-horizontal': isHorizontal,
'k-splitbar-vertical': !isHorizontal,
'k-splitbar-draggable-horizontal': isHorizontal && isDraggable,
'k-splitbar-draggable-vertical': !isHorizontal && isDraggable,
'k-splitbar-static-horizontal': isHorizontal && isStatic,
'k-splitbar-static-vertical': !isHorizontal && isStatic,
'k-touch-action-none': isDraggable
});
}
get order() {
return 2 * this.index + 1;
}
orientation = 'horizontal';
index = 0;
set htmlAttributes(attributes) {
if (isObjectPresent(this.parsedAttributes)) {
removeHTMLAttributes(this.parsedAttributes, this.renderer, this.element.nativeElement);
}
this._htmlAttributes = attributes;
this.parsedAttributes = this.htmlAttributes ?
parseAttributes(this.htmlAttributes, this.defaultAttributes) :
this.htmlAttributes;
this.setHtmlAttributes();
}
get htmlAttributes() {
return this._htmlAttributes;
}
subscriptions = new Subscription();
_htmlAttributes;
parsedAttributes = {};
get defaultAttributes() {
return {
'aria-orientation': this.hostOrientation,
role: this.ariaRole
};
}
get mutableAttributes() {
return { 'tabindex': this.tabIndex };
}
constructor(draggable, localization, splitterService, element, renderer, cdr) {
this.draggable = draggable;
this.localization = localization;
this.splitterService = splitterService;
this.element = element;
this.renderer = renderer;
this.cdr = cdr;
}
ngOnInit() {
let state;
const listener = this.draggable.kendoPress.pipe(tap(stopPropagation), filter(() => this.splitterService.isDraggable(this.index)), tap(() => state = this.splitterService.dragState(this.index)), tap(() => this.splitterService.toggleContentOverlay(this.index, true)), switchMap(preventOnDblClick(this.draggable.kendoRelease)), switchMap(createMoveStream(this.draggable))).subscribe(({ pageX, pageY, originalX, originalY }) => {
let delta;
if (this.orientation === 'vertical') {
delta = pageY - originalY;
}
else if (this.direction === 'rtl') {
delta = originalX - pageX;
}
else {
delta = pageX - originalX;
}
this.splitterService.setSize(state, delta);
});
this.subscriptions.add(listener);
this.subscriptions.add(this.draggable.kendoRelease.subscribe(() => this.splitterService.toggleContentOverlay(this.index, false)));
const element = this.element.nativeElement;
this.subscriptions.add(this.renderer.listen(element, 'keydown', event => this.onKeyDown(event)));
this.subscriptions.add(this.renderer.listen(element, 'focusin', () => this.focused = true));
this.subscriptions.add(this.renderer.listen(element, 'focusout', () => this.focused = false));
this.subscriptions.add(this.renderer.listen(element, 'dblclick', () => this.togglePane()));
}
ngOnDestroy() {
if (this.subscriptions) {
this.subscriptions.unsubscribe();
}
}
togglePrevious() {
this.splitterService.tryToggle(this.index);
}
toggleNext() {
this.splitterService.tryToggle(this.index + 1);
}
get direction() {
return this.localization.rtl ? 'rtl' : 'ltr';
}
shouldShowIcon(iconName) {
const paneIndex = iconName === 'prev' ? this.index : this.index + 1;
const relatedPaneIndex = iconName === 'prev' ? this.index + 1 : this.index;
const pane = this.splitterService.pane(paneIndex);
const relatedPane = this.splitterService.pane(relatedPaneIndex);
const isCollapsible = pane?.collapsible;
return isCollapsible && !relatedPane?.isHidden;
}
previousArrowClass() {
const pane = this.splitterService.pane(this.index);
const isCollapsible = pane?.collapsible;
const isCollapsed = pane?.collapsed;
const isHorizontal = this.orientation === 'horizontal';
const isRTL = this.direction === 'rtl';
return classFromObject({
'caret-alt-left': isCollapsible && isHorizontal && ((!isCollapsed && !isRTL) || (isCollapsed && isRTL)),
'caret-alt-right': isCollapsible && isHorizontal && ((isCollapsed && !isRTL) || (!isCollapsed && isRTL)),
'caret-alt-up': isCollapsible && !isHorizontal && !isCollapsed,
'caret-alt-down': isCollapsible && !isHorizontal && isCollapsed
});
}
previousSVGArrowClass() {
const pane = this.splitterService.pane(this.index);
const isCollapsible = pane?.collapsible;
const isCollapsed = pane?.collapsed;
const isHorizontal = this.orientation === 'horizontal';
const isRTL = this.direction === 'rtl';
if (isCollapsible && isHorizontal && ((!isCollapsed && !isRTL) || (isCollapsed && isRTL))) {
return caretAltLeftIcon;
}
if (isCollapsible && isHorizontal && ((isCollapsed && !isRTL) || (!isCollapsed && isRTL))) {
return caretAltRightIcon;
}
if (isCollapsible && !isHorizontal && !isCollapsed) {
return caretAltUpIcon;
}
if (isCollapsible && !isHorizontal && isCollapsed) {
return caretAltDownIcon;
}
}
nextArrowClass() {
const pane = this.splitterService.pane(this.index + 1);
const isCollapsible = pane?.collapsible;
const isCollapsed = pane?.collapsed;
const isHorizontal = this.orientation === 'horizontal';
const isRTL = this.direction === 'rtl';
return classFromObject({
'caret-alt-right': isCollapsible && isHorizontal && ((!isCollapsed && !isRTL) || (isCollapsed && isRTL)),
'caret-alt-left': isCollapsible && isHorizontal && ((isCollapsed && !isRTL) || (!isCollapsed && isRTL)),
'caret-alt-down': isCollapsible && !isHorizontal && !isCollapsed,
'caret-alt-up': isCollapsible && !isHorizontal && isCollapsed
});
}
nextSVGArrowClass() {
const pane = this.splitterService.pane(this.index + 1);
const isCollapsible = pane?.collapsible;
const isCollapsed = pane?.collapsed;
const isHorizontal = this.orientation === 'horizontal';
const isRTL = this.direction === 'rtl';
if (isCollapsible && isHorizontal && ((!isCollapsed && !isRTL) || (isCollapsed && isRTL))) {
return caretAltRightIcon;
}
if (isCollapsible && isHorizontal && ((isCollapsed && !isRTL) || (!isCollapsed && isRTL))) {
return caretAltLeftIcon;
}
if (isCollapsible && !isHorizontal && !isCollapsed) {
return caretAltDownIcon;
}
if (isCollapsible && !isHorizontal && isCollapsed) {
return caretAltUpIcon;
}
}
togglePane() {
if (this.expandLast) {
this.toggleNext();
}
else {
this.tryToggleNearest();
}
this.cdr.markForCheck();
}
get expandLast() {
const panes = this.splitterService.panes;
return panes.length === 2 && panes[1].collapsed;
}
onKeyDown(event) {
const keyCode = event.keyCode;
const shouldToggle = event.ctrlKey || event.metaKey;
if (keyCode === Keys.Enter) {
event.preventDefault();
this.togglePane();
}
else if (shouldToggleOrResize(keyCode, this.orientation)) {
event.preventDefault();
if (shouldToggle) {
this.splitterService.togglePane(keyCode, this.index);
}
else {
this.splitterService.resizePane(keyCode, this.index);
}
}
}
tryToggleNearest() {
const prev = this.index;
const next = this.index + 1;
if (!this.splitterService.tryToggle(prev)) {
this.splitterService.tryToggle(next);
}
}
setHtmlAttributes() {
const attributesToRender = { ...this.mutableAttributes, ...this.parsedAttributes };
setHTMLAttributes(attributesToRender, this.renderer, this.element.nativeElement);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterBarComponent, deps: [{ token: i1.DraggableDirective, host: true }, { token: i2.LocalizationService }, { token: i3.SplitterService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SplitterBarComponent, isStandalone: true, selector: "kendo-splitter-bar", inputs: { orientation: "orientation", index: "index", htmlAttributes: "htmlAttributes" }, host: { properties: { "attr.role": "this.ariaRole", "attr.aria-label": "this.ariaLabel", "class.k-focus": "this.focused", "attr.aria-orientation": "this.hostOrientation", "attr.tabindex": "this.tabIndex", "class": "this.hostClasses", "style.-ms-flex-order": "this.order", "style.order": "this.order" } }, ngImport: i0, template: `
<div *ngIf="shouldShowIcon('prev')" class="k-collapse-prev" (click)="togglePrevious()">
<kendo-icon-wrapper
size="xsmall"
[name]="previousArrowClass()"
[svgIcon]="previousSVGArrowClass()"
></kendo-icon-wrapper>
</div>
<div class="k-resize-handle"></div>
<div *ngIf="shouldShowIcon('next')" class="k-collapse-next" (click)="toggleNext()">
<kendo-icon-wrapper
size="xsmall"
[name]="nextArrowClass()"
[svgIcon]="nextSVGArrowClass()"
></kendo-icon-wrapper>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitterBarComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-splitter-bar',
template: `
<div *ngIf="shouldShowIcon('prev')" class="k-collapse-prev" (click)="togglePrevious()">
<kendo-icon-wrapper
size="xsmall"
[name]="previousArrowClass()"
[svgIcon]="previousSVGArrowClass()"
></kendo-icon-wrapper>
</div>
<div class="k-resize-handle"></div>
<div *ngIf="shouldShowIcon('next')" class="k-collapse-next" (click)="toggleNext()">
<kendo-icon-wrapper
size="xsmall"
[name]="nextArrowClass()"
[svgIcon]="nextSVGArrowClass()"
></kendo-icon-wrapper>
</div>
`,
standalone: true,
imports: [NgIf, IconWrapperComponent]
}]
}], ctorParameters: function () { return [{ type: i1.DraggableDirective, decorators: [{
type: Host
}] }, { type: i2.LocalizationService }, { type: i3.SplitterService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { ariaRole: [{
type: HostBinding,
args: ['attr.role']
}], ariaLabel: [{
type: HostBinding,
args: ['attr.aria-label']
}], focused: [{
type: HostBinding,
args: ['class.k-focus']
}], hostOrientation: [{
type: HostBinding,
args: ['attr.aria-orientation']
}], tabIndex: [{
type: HostBinding,
args: ['attr.tabindex']
}], hostClasses: [{
type: HostBinding,
args: ['class']
}], order: [{
type: HostBinding,
args: ['style.-ms-flex-order']
}, {
type: HostBinding,
args: ['style.order']
}], orientation: [{
type: Input
}], index: [{
type: Input
}], htmlAttributes: [{
type: Input
}] } });