@obliczeniowo/elementary
Version:
Library made in Angular version 20
246 lines (239 loc) • 13.4 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, HostBinding, HostListener, Output, Input, Component, ViewChild, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import { DomPortalOutlet, DomPortal } from '@angular/cdk/portal';
import { Subject } from 'rxjs';
import * as i2$1 from '@obliczeniowo/elementary/resize-window';
import * as i2 from '@obliczeniowo/elementary/icons';
import { IconsModule } from '@obliczeniowo/elementary/icons';
import * as i3 from '@obliczeniowo/elementary/text-pipes';
import { TextPipesModule } from '@obliczeniowo/elementary/text-pipes';
import { OverlayModule } from '@angular/cdk/overlay';
class OblMenuTeleportService {
appRef;
injector;
portalOutlet = new Map();
constructor(appRef, injector) {
this.appRef = appRef;
this.injector = injector;
}
registerPortalOutlet(outletElement) {
const id = Math.random();
this.portalOutlet.set(id, new DomPortalOutlet(outletElement, this.appRef, this.injector));
return id;
}
unregisterPortalOutlet(id) {
const portal = this.portalOutlet.get(id);
portal?.dispose();
this.portalOutlet.delete(id);
}
teleport(id, portal) {
if (!this.portalOutlet.get(id)?.hasAttached()) {
this.portalOutlet.get(id)?.attach(portal);
}
}
finishTeleportation(id) {
this.portalOutlet.get(id)?.detach();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMenuTeleportService, deps: [{ token: i0.ApplicationRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMenuTeleportService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMenuTeleportService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.Injector }] });
class MenuItemComponent {
item;
translations = {};
noIcon = false;
clicked = new EventEmitter();
onClick() {
if (!this.item.disabled) {
this.clicked.emit(this.item);
}
}
get disabled() {
return this.item.disabled;
}
ngOnChanges(changes) {
if (changes.item) {
this.noIcon = !this.item.icon;
}
}
stringIcon() {
return typeof this.item.icon === 'string';
}
iconTemplate() {
return this.item.icon;
}
iconString() {
return this.item.icon;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: MenuItemComponent, isStandalone: false, selector: "obl-menu-item", inputs: { item: "item", translations: "translations" }, outputs: { clicked: "clicked" }, host: { listeners: { "click": "onClick()" }, properties: { "class.no-icon": "this.noIcon", "class.disabled": "this.disabled" } }, usesOnChanges: true, ngImport: i0, template: "@if (item.icon) {\n <ng-container *ngTemplateOutlet=\"stringIcon() ? iconsRef : iconTemplate(); context: { $implicit: item }\"></ng-container>\n}\n\n<div>\n {{ item.text | oblTranslation : translations }}\n</div>\n\n<ng-template #iconsRef>\n <obl-icon [name]=\"iconString()\"></obl-icon>\n</ng-template>\n\n", styles: [":host{display:flex;padding:5px;background-color:var(--menu-background, #d8d8d8);color:var(--default-text-color);cursor:pointer}:host:hover{background-color:var(--menu-hover, #ecebeb)}:host.no-icon div{padding-left:24px}:host.disabled{color:var(--default-disabled-color)}:host.disabled obl-icon{--obl-default-icon-color: var(--default-disabled-color)}obl-icon{margin-right:5px}\n"], dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.IconComponent, selector: "obl-icon", inputs: ["name", "width"] }, { kind: "pipe", type: i3.TranslationPipe, name: "oblTranslation" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuItemComponent, decorators: [{
type: Component,
args: [{ selector: 'obl-menu-item', standalone: false, template: "@if (item.icon) {\n <ng-container *ngTemplateOutlet=\"stringIcon() ? iconsRef : iconTemplate(); context: { $implicit: item }\"></ng-container>\n}\n\n<div>\n {{ item.text | oblTranslation : translations }}\n</div>\n\n<ng-template #iconsRef>\n <obl-icon [name]=\"iconString()\"></obl-icon>\n</ng-template>\n\n", styles: [":host{display:flex;padding:5px;background-color:var(--menu-background, #d8d8d8);color:var(--default-text-color);cursor:pointer}:host:hover{background-color:var(--menu-hover, #ecebeb)}:host.no-icon div{padding-left:24px}:host.disabled{color:var(--default-disabled-color)}:host.disabled obl-icon{--obl-default-icon-color: var(--default-disabled-color)}obl-icon{margin-right:5px}\n"] }]
}], propDecorators: { item: [{
type: Input,
args: [{ required: true }]
}], translations: [{
type: Input
}], noIcon: [{
type: HostBinding,
args: ['class.no-icon']
}], clicked: [{
type: Output
}], onClick: [{
type: HostListener,
args: ['click']
}], disabled: [{
type: HostBinding,
args: ['class.disabled']
}] } });
class MenuComponent {
menuTeleportService;
resize;
elementRef;
_opened = false;
/**
* Open/close menu. When set to false emits extra command 'on-close' only by using command Subject
*/
set opened(opened) {
this._opened = opened;
if (!this.opened) {
this.command.next('on-close');
}
}
get opened() {
return this._opened;
}
// target element (by default document.body)
target = document.body;
// items menu to display
items = [];
/**
* translations for item.text from {@link items}
*/
translations = {};
/**
* emit command when active element was clicked
*/
clicked = new EventEmitter();
command = new Subject();
parent;
position = { x: 0, y: 0 };
size = { width: 0, height: 0 };
id;
set content(elemRef) {
this.menuTeleportService.teleport(this.id, new DomPortal(elemRef));
}
checkParent(target) {
if (!this.target) {
return false;
}
do {
if (target && this.target === target) {
return false;
}
target = target?.parentElement;
} while (target);
return true;
}
targetClicked = (event) => {
if (this.checkParent(event.target)) {
if (!this.opened) {
return;
}
}
this.opened = !!this.items.length && !this.opened;
this.position.x = event.pageX;
this.position.y = event.pageY;
if (this.size.width + this.position.x > this.resize.size.width - 15) {
this.position.x = this.resize.size.width - this.size.width - 15;
}
event.preventDefault();
};
constructor(menuTeleportService, resize, elementRef) {
this.menuTeleportService = menuTeleportService;
this.resize = resize;
this.elementRef = elementRef;
this.parent = document.createElement('div');
document.body.appendChild(this.parent);
this.parent.classList.add('obl-menu');
this.id = this.menuTeleportService.registerPortalOutlet(this.parent);
this.target.addEventListener('contextmenu', this.targetClicked);
}
ngOnDestroy() {
this.menuTeleportService.finishTeleportation(this.id);
this.menuTeleportService.unregisterPortalOutlet(this.id);
}
ngAfterViewChecked() {
const dom = document.querySelector('#obl-menu .menu-content')?.getBoundingClientRect();
this.size = { width: dom?.width || 0, height: dom?.height || 0 };
}
itemClicked(item) {
this.clicked.emit(item.command);
this.command.next(item.command);
this.opened = false;
this.target.removeEventListener('click', this.targetClicked);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuComponent, deps: [{ token: OblMenuTeleportService }, { token: i2$1.OblResizeWindowService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: MenuComponent, isStandalone: false, selector: "obl-menu", inputs: { opened: "opened", target: "target", items: "items", translations: "translations" }, outputs: { clicked: "clicked" }, viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: "<div #content>\n @if (opened) {\n <div class=\"background\" (click)=\"opened = false\"></div>\n <div\n class=\"menu-content\"\n [style.transform]=\"'translate(' + position.x + 'px,' + position.y + 'px)'\"\n >\n @for(item of items; track item.id) {\n <obl-menu-item\n [item]=\"item\"\n [translations]=\"translations\"\n (clicked)=\"itemClicked($event)\"\n >\n </obl-menu-item>\n }\n </div>\n }\n</div>\n", styles: ["::ng-deep .obl-menu{position:absolute;inset:0;pointer-events:none}.background{position:fixed;inset:0;background-color:#0000;pointer-events:all}:host{max-width:200}.menu-content{width:fit-content;max-width:300px;pointer-events:all}\n"], dependencies: [{ kind: "component", type: MenuItemComponent, selector: "obl-menu-item", inputs: ["item", "translations"], outputs: ["clicked"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuComponent, decorators: [{
type: Component,
args: [{ selector: 'obl-menu', standalone: false, template: "<div #content>\n @if (opened) {\n <div class=\"background\" (click)=\"opened = false\"></div>\n <div\n class=\"menu-content\"\n [style.transform]=\"'translate(' + position.x + 'px,' + position.y + 'px)'\"\n >\n @for(item of items; track item.id) {\n <obl-menu-item\n [item]=\"item\"\n [translations]=\"translations\"\n (clicked)=\"itemClicked($event)\"\n >\n </obl-menu-item>\n }\n </div>\n }\n</div>\n", styles: ["::ng-deep .obl-menu{position:absolute;inset:0;pointer-events:none}.background{position:fixed;inset:0;background-color:#0000;pointer-events:all}:host{max-width:200}.menu-content{width:fit-content;max-width:300px;pointer-events:all}\n"] }]
}], ctorParameters: () => [{ type: OblMenuTeleportService }, { type: i2$1.OblResizeWindowService }, { type: i0.ElementRef }], propDecorators: { opened: [{
type: Input
}], target: [{
type: Input
}], items: [{
type: Input
}], translations: [{
type: Input
}], clicked: [{
type: Output
}], content: [{
type: ViewChild,
args: ['content']
}] } });
class MenuModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: MenuModule, declarations: [MenuComponent,
MenuItemComponent], imports: [CommonModule,
OverlayModule,
IconsModule,
TextPipesModule], exports: [MenuComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuModule, imports: [CommonModule,
OverlayModule,
IconsModule,
TextPipesModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: MenuModule, decorators: [{
type: NgModule,
args: [{
declarations: [
MenuComponent,
MenuItemComponent
],
imports: [
CommonModule,
OverlayModule,
IconsModule,
TextPipesModule
],
exports: [
MenuComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { MenuComponent, MenuModule };
//# sourceMappingURL=obliczeniowo-elementary-menu.mjs.map