Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/entity-selector/src/item/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/entity-selector/src/item/item-node.js |
import { ajax as Ajax, Cache, Dom, Runtime, Tag, Type, Browser, Event } from 'main.core'; import { OrderedArray } from 'main.core.collections'; import { Loader } from 'main.loader'; import ItemNodeComparator from './item-node-comparator'; import Highlighter from '../search/highlighter'; import ItemBadge from './item-badge'; import MatchField from '../search/match-field'; import TextNode from '../common/text-node'; import Animation from '../common/animation'; import Item from './item'; import encodeUrl from '../common/encode-url'; import type Tab from '../dialog/tabs/tab'; import type Dialog from '../dialog/dialog'; import type { ItemOptions } from './item-options'; import type { ItemNodeOptions } from './item-node-options'; import type { ItemBadgeOptions } from './item-badge-options'; import type { TextNodeOptions } from '../common/text-node-options'; import type { CaptionOptions } from './caption-options'; import type { BadgesOptions } from './badges-options'; import type { AvatarOptions } from './avatar-options'; export class RenderMode { static PARTIAL = 'partial'; static OVERRIDE = 'override'; } export default class ItemNode { item: Item = null; tab: Tab = null; cache = new Cache.MemoryCache(); parentNode: ItemNode = null; children: OrderedArray<ItemNode> = null; childItems: WeakMap<Item, ItemNode> = new WeakMap(); // for the fast access loaded: boolean = false; dynamic: boolean = false; dynamicPromise: ?Promise = null; loader: Loader = null; open: boolean = false; autoOpen: boolean = false; focused: boolean = false; renderMode: RenderMode = RenderMode.PARTIAL; title: ?TextNode = null; subtitle: ?TextNode = null; supertitle: ?TextNode = null; caption: ?TextNode = null; captionOptions: CaptionOptions = {}; avatar: ?string = null; avatarOptions: ?AvatarOptions = null; link: ?string = null; linkTitle: ?TextNode = null; textColor: ?string = null; badges: ItemBadgeOptions[] = null; badgesOptions: BadgesOptions = {}; hidden: boolean = false; highlights: MatchField[] = []; rendered: false; renderWithDebounce = Runtime.debounce(this.render, 50, this); constructor(item: Item, nodeOptions: ItemNodeOptions) { const options: ItemNodeOptions = Type.isPlainObject(nodeOptions) ? nodeOptions : {}; if (Type.isObject(item)) { this.item = item; } let comparator = null; if (Type.isFunction(options.itemOrder)) { comparator = options.itemOrder; } else if (Type.isPlainObject(options.itemOrder)) { comparator = ItemNodeComparator.makeMultipleComparator(options.itemOrder); } this.children = new OrderedArray(comparator); this.renderMode = options.renderMode === RenderMode.OVERRIDE ? RenderMode.OVERRIDE : RenderMode.PARTIAL; if (this.renderMode === RenderMode.OVERRIDE) { this.setTitle(''); this.setSubtitle(''); this.setSupertitle(''); this.setCaption(''); this.setLinkTitle(''); this.avatar = ''; this.avatarOptions = { bgSize: null, bgColor: null, bgImage: null, border: null, borderRadius: null, }; this.textColor = ''; this.link = ''; this.badges = []; this.captionOptions = { fitContent: null, maxWidth: null, justifyContent: null, }; this.badgesOptions = { fitContent: null, maxWidth: null, justifyContent: null, }; } this.setTitle(options.title); this.setSubtitle(options.subtitle); this.setSupertitle(options.supertitle); this.setCaption(options.caption); this.setCaptionOptions(options.captionOptions); this.setAvatar(options.avatar); this.setAvatarOptions(options.avatarOptions); this.setTextColor(options.textColor); this.setLink(options.link); this.setLinkTitle(options.linkTitle); this.setBadges(options.badges); this.setBadgesOptions(options.badgesOptions); this.setDynamic(options.dynamic); this.setOpen(options.open); } getItem(): Item { return this.item; } isRoot(): boolean { return this.getParentNode() === null; } getDialog(): Dialog { return this.getTab().getDialog(); } setTab(tab: Tab): void { this.tab = tab; } getTab(): Tab { return this.tab; } getParentNode(): ?ItemNode { return this.parentNode; } setParentNode(parentNode: ItemNode): void { this.parentNode = parentNode; } getNextSibling(): ?ItemNode { if (!this.getParentNode()) { return null; } const siblings = this.getParentNode().getChildren(); const index = siblings.getIndex(this); return siblings.getByIndex(index + 1); } getPreviousSibling(): ?ItemNode { if (!this.getParentNode()) { return null; } const siblings = this.getParentNode().getChildren(); const index = siblings.getIndex(this); return siblings.getByIndex(index - 1); } addChildren(children: ItemOptions[]): void { if (!Type.isArray(children)) { return; } children.forEach((childOptions: ItemOptions) => { delete childOptions.tabs; const childItem = this.getDialog().addItem(childOptions); const childNode = this.addItem(childItem, childOptions.nodeOptions); childNode.addChildren(childOptions.children); }); } addChild(child: ItemNode): ItemNode { if (!(child instanceof ItemNode)) { throw new Error('EntitySelector.ItemNode: an item must be an instance of EntitySelector.ItemNode.'); } if (this.isChildOf(child) || child === this) { throw new Error('EntitySelector.ItemNode: a child item cannot be a parent of current item.'); } if (this.getChildren().has(child) || this.childItems.has(child.getItem())) { return null; } this.getChildren().add(child); this.childItems.set(child.getItem(), child); child.setTab(this.getTab()); child.setParentNode(this); if (this.isRendered()) { this.renderWithDebounce(); } return child; } getDepthLevel(): number { return this.isRoot() ? 0 : this.getParentNode().getDepthLevel() + 1; } addItem(item: Item, nodeOptions: ItemNodeOptions): ItemNode { let itemNode = this.childItems.get(item); if (!itemNode) { itemNode = item.createNode(nodeOptions); this.addChild(itemNode); } return itemNode; } addItems(items: Item[] | Array<[Item, ItemNodeOptions]>): void { if (Type.isArray(items)) { this.disableRender(); items.forEach((item: Item | [Item, ItemNodeOptions]) => { if (Type.isArray(item) && item.length === 2) { this.addItem(item[0], item[1]); } else if (item instanceof Item) { this.addItem(item); } }); this.enableRender(); if (this.isRendered()) { this.renderWithDebounce(); } } } hasItem(item: Item): boolean { return this.childItems.has(item); } removeChild(child: ItemNode): boolean { if (!this.getChildren().has(child)) { return false; } child.removeChildren(); if (child.isFocused()) { child.unfocus(); } child.setParentNode(null); child.getItem().removeNode(child); this.getChildren().delete(child); this.childItems.delete(child.getItem()); if (this.isRendered()) { Dom.remove(child.getOuterContainer()); } return true; } removeChildren(): void { if (!this.hasChildren()) { return; } this.getChildren().forEach((node: ItemNode) => { node.removeChildren(); if (node.isFocused()) { node.unfocus(); } node.setParentNode(null); node.getItem().removeNode(node); }); this.getChildren().clear(); this.childItems = new WeakMap(); if (this.isRendered()) { if (Browser.isIE()) { Dom.clean(this.getChildrenContainer()); } else { this.getChildrenContainer().textContent = ''; } } } hasChild(child: ItemNode): boolean { return this.getChildren().has(child); } isChildOf(parent: ItemNode): boolean { let parentNode = this.getParentNode(); while (parentNode !== null) { if (parentNode === parent) { return true; } parentNode = parentNode.getParentNode(); } return false; } getFirstChild(): ?ItemNode { return this.children.getFirst(); } getLastChild(): ?ItemNode { return this.children.getLast(); } getChildren(): OrderedArray<ItemNode> { return this.children; } hasChildren(): boolean { return this.children.count() > 0; } loadChildren(): Promise { if (!this.isDynamic()) { throw new Error('EntitySelector.ItemNode.loadChildren: an item node is not dynamic.'); } if (this.dynamicPromise) { return this.dynamicPromise; } this.dynamicPromise = Ajax.runAction('ui.entityselector.getChildren', { json: { parentItem: this.getItem().getAjaxJson(), dialog: this.getDialog().getAjaxJson() }, getParameters: { context: this.getDialog().getContext() } }); this.dynamicPromise.then((response) => { if (response && response.data && Type.isPlainObject(response.data.dialog)) { this.addChildren(response.data.dialog.items); this.render(); } this.loaded = true; }); this.dynamicPromise.catch((error) => { this.loaded = false; this.dynamicPromise = null; console.error(error); }); return this.dynamicPromise; } setOpen(open: boolean): void { if (Type.isBoolean(open)) { if (open && this.isDynamic() && !this.isLoaded()) { this.setAutoOpen(true); } else { this.open = open; } } } isOpen(): boolean { return this.open; } isAutoOpen(): boolean { return this.autoOpen && this.isDynamic() && !this.isLoaded(); } setAutoOpen(autoOpen: boolean): void { if (Type.isBoolean(autoOpen)) { this.autoOpen = autoOpen; } } setDynamic(dynamic: boolean): void { if (Type.isBoolean(dynamic)) { this.dynamic = dynamic; } } isDynamic(): boolean { return this.dynamic; } isLoaded(): boolean { return this.loaded; } getLoader(): Loader { if (this.loader === null) { this.loader = new Loader({ target: this.getIndicatorContainer(), size: 30 }); } return this.loader; } showLoader(): void { void this.getLoader().show(); Dom.addClass(this.getIndicatorContainer(), 'ui-selector-item-indicator-hidden'); } hideLoader(): void { void this.getLoader().hide(); Dom.removeClass(this.getIndicatorContainer(), 'ui-selector-item-indicator-hidden'); } destroyLoader(): void { this.getLoader().destroy(); this.loader = null; Dom.removeClass(this.getIndicatorContainer(), 'ui-selector-item-indicator-hidden'); } expand(): void { if (this.isOpen() || (!this.hasChildren() && !this.isDynamic())) { return; } if (this.isDynamic() && !this.isLoaded()) { this.loadChildren().then(() => { this.destroyLoader(); this.expand(); }); this.showLoader(); return; } Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-open'); Dom.style(this.getChildrenContainer(), 'height', '0px'); Dom.style(this.getChildrenContainer(), 'opacity', 0); requestAnimationFrame(() => { requestAnimationFrame(() => { Dom.style(this.getChildrenContainer(), 'height', `${this.getChildrenContainer().scrollHeight}px`); Dom.style(this.getChildrenContainer(), 'opacity', 1); Animation.handleTransitionEnd(this.getChildrenContainer(), 'height').then(() => { Dom.style(this.getChildrenContainer(), 'height', null); Dom.style(this.getChildrenContainer(), 'opacity', null); Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-open'); this.setOpen(true); }); }); }); } collapse(): void { if (!this.isOpen()) { return; } Dom.style(this.getChildrenContainer(), 'height', `${this.getChildrenContainer().offsetHeight}px`); requestAnimationFrame(() => { requestAnimationFrame(() => { Dom.style(this.getChildrenContainer(), 'height', '0px'); Dom.style(this.getChildrenContainer(), 'opacity', 0); Animation.handleTransitionEnd(this.getChildrenContainer(), 'height').then(() => { Dom.style(this.getChildrenContainer(), 'height', null); Dom.style(this.getChildrenContainer(), 'opacity', null); Dom.removeClass(this.getOuterContainer(), 'ui-selector-item-box-open'); this.setOpen(false); }); }); }); } render(appendChildren = false): void { if (this.isRoot()) { this.renderRoot(appendChildren); return; } const titleNode = this.getTitleNode(); if (titleNode) { titleNode.renderTo(this.getTitleContainer()); } else { this.getTitleContainer().textContent = ''; } const supertitleNode = this.getSupertitleNode(); if (supertitleNode) { supertitleNode.renderTo(this.getSupertitleContainer()); } else { this.getSupertitleContainer().textContent = ''; } const subtitleNode = this.getSubtitleNode(); if (subtitleNode) { subtitleNode.renderTo(this.getSubtitleContainer()); } else { this.getSubtitleContainer().textContent = ''; } const captionNode = this.getCaptionNode(); if (captionNode) { captionNode.renderTo(this.getCaptionContainer()); } else { this.getCaptionContainer().textContent = ''; } const captionFitContent = this.getCaptionOption('fitContent'); if (Type.isBoolean(captionFitContent)) { Dom.style(this.getCaptionContainer(), 'flex-shrink', captionFitContent ? 0 : null); } const captionJustifyContent = this.getCaptionOption('justifyContent'); if (Type.isStringFilled(captionJustifyContent) || captionJustifyContent === null) { Dom.style( this.getCaptionContainer(), { flexGrow: captionJustifyContent ? '1' : null, textAlign: captionJustifyContent || null, }, ); } const captionMaxWidth = this.getCaptionOption('maxWidth'); if (Type.isString(captionMaxWidth) || Type.isNumber(captionMaxWidth)) { Dom.style( this.getCaptionContainer(), 'max-width', Type.isNumber(captionMaxWidth) ? `${captionMaxWidth}px` : captionMaxWidth ); } if (Type.isStringFilled(this.getTextColor())) { this.getTitleContainer().style.color = this.getTextColor(); } else { this.getTitleContainer().style.removeProperty('color'); } const avatar = this.getAvatar(); if (Type.isStringFilled(avatar)) { this.getAvatarContainer().style.backgroundImage = `url('${encodeUrl(avatar)}')`; } else { const bgImage = this.getAvatarOption('bgImage'); if (Type.isStringFilled(bgImage)) { this.getAvatarContainer().style.backgroundImage = bgImage; } else { this.getAvatarContainer().style.removeProperty('background-image'); } } const bgColor = this.getAvatarOption('bgColor'); if (Type.isStringFilled(bgColor)) { this.getAvatarContainer().style.backgroundColor = bgColor; } else { this.getAvatarContainer().style.removeProperty('background-color'); } const bgSize = this.getAvatarOption('bgSize'); if (Type.isStringFilled(bgSize)) { this.getAvatarContainer().style.backgroundSize = bgSize; } else { this.getAvatarContainer().style.removeProperty('background-size'); } const border = this.getAvatarOption('border'); if (Type.isStringFilled(border)) { this.getAvatarContainer().style.border = border; } else { this.getAvatarContainer().style.removeProperty('border'); } const borderRadius = this.getAvatarOption('borderRadius'); if (Type.isStringFilled(borderRadius)) { this.getAvatarContainer().style.borderRadius = borderRadius; } else { this.getAvatarContainer().style.removeProperty('border-radius'); } Dom.clean(this.getBadgeContainer()); this.getBadges().forEach((badge: ItemBadge) => { badge.renderTo(this.getBadgeContainer()); }); const badgesFitContent = this.getBadgesOption('fitContent'); if (Type.isBoolean(badgesFitContent)) { Dom.style(this.getBadgeContainer(), 'flex-shrink', badgesFitContent ? 0 : null); } const badgesJustifyContent = this.getBadgesOption('justifyContent'); if (Type.isStringFilled(badgesJustifyContent) || badgesJustifyContent === null) { Dom.style( this.getBadgeContainer(), { flexGrow: badgesJustifyContent ? '1' : null, justifyContent: badgesJustifyContent || null, }, ); } const badgesMaxWidth = this.getBadgesOption('maxWidth'); if (Type.isString(badgesMaxWidth) || Type.isNumber(badgesMaxWidth)) { Dom.style( this.getBadgeContainer(), 'max-width', Type.isNumber(badgesMaxWidth) ? `${badgesMaxWidth}px` : badgesMaxWidth ); } const linkTitleNode = this.getLinkTitleNode(); if (linkTitleNode) { linkTitleNode.renderTo(this.getLinkTextContainer()); } else { this.getLinkTextContainer().textContent = ''; } if (this.hasChildren() || this.isDynamic()) { Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-has-children'); if (this.getDepthLevel() >= this.getTab().getItemMaxDepth()) { Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-max-depth'); } } else if (this.getOuterContainer().classList.contains('ui-selector-item-box-has-children')) { Dom.removeClass( this.getOuterContainer(), ['ui-selector-item-box-has-children', 'ui-selector-item-box-max-depth'] ); } if (this.hasChildren()) { const hasVisibleChild = this.getChildren().getAll().some((child: ItemNode) => { return child.isHidden() !== true; }); if (!hasVisibleChild) { this.#setHidden(true); } } this.toggleVisibility(); this.highlight(); this.renderChildren(appendChildren); if (this.isAutoOpen()) { this.setAutoOpen(false); requestAnimationFrame(() => { requestAnimationFrame(() => { this.expand(); }); }); } this.rendered = true; } /** * @private */ renderRoot(appendChildren = false): void { this.renderChildren(appendChildren); this.rendered = true; const stub = this.getTab().getStub(); if (stub && stub.isAutoShow() && (this.getDialog().isLoaded() || !this.getDialog().hasDynamicLoad())) { if (this.hasChildren()) { stub.hide(); } else { stub.show(); } } } /** * @private */ renderChildren(appendChildren = false): void { if (!appendChildren) { if (Browser.isIE()) { Dom.clean(this.getChildrenContainer()); } else { this.getChildrenContainer().textContent = ''; } } if (this.hasChildren()) { let previousSibling: ItemNode = null; this.getChildren().forEach((child: ItemNode) => { child.render(appendChildren); const container = child.getOuterContainer(); if (!appendChildren) { Dom.append(container, this.getChildrenContainer()); } if (!container.parentNode) { if (previousSibling !== null) { Dom.insertAfter(container, previousSibling.getOuterContainer()); } else { Dom.append(container, this.getChildrenContainer()); } } previousSibling = child; }); } } isRendered(): boolean { return this.rendered && this.getDialog() && this.getDialog().isRendered(); } enableRender(): void { this.rendered = true; } disableRender(): void { this.rendered = false; } getRenderMode(): RenderMode { return this.renderMode; } isHidden(): boolean { return this.hidden === true || this.getItem().isHidden() === true; } setHidden(flag: boolean): void { if (!Type.isBoolean(flag) || this.isRoot()) { return; } this.#setHidden(flag); if (this.isRendered()) { this.toggleVisibility(); let parentNode = this.getParentNode(); const isHidden = this.isHidden(); while (parentNode.isRoot() === false) { if (isHidden) { const hasVisibleChild = parentNode.getChildren().getAll().some((child: ItemNode) => { return child.isHidden() !== true; }); if (!hasVisibleChild) { parentNode.#setHidden(true); } parentNode.toggleVisibility(); } else { parentNode.#setHidden(false); parentNode.toggleVisibility(); if (parentNode.isHidden()) { break; } } parentNode = parentNode.getParentNode(); } } } #setHidden(flag: boolean): void { if (Type.isBoolean(flag) && !this.isRoot()) { this.hidden = flag; } } toggleVisibility(): boolean { if (this.isHidden()) { Dom.addClass(this.getOuterContainer(), '--hidden'); } else if (this.getOuterContainer().classList.contains('--hidden')) { Dom.removeClass(this.getOuterContainer(), '--hidden'); } } getTitle(): string { const titleNode = this.getTitleNode(); return titleNode !== null ? titleNode.getText() : null; } getTitleNode(): ?TextNode { return this.title !== null ? this.title: this.getItem().getTitleNode(); } setTitle(title: string | TextNodeOptions): void { if (Type.isString(title) || Type.isPlainObject(title)) { this.title = new TextNode(title); } else if (title === null) { this.title = null; } } getSubtitle(): ?string { const subtitleNode = this.getSubtitleNode(); return subtitleNode !== null ? subtitleNode.getText() : null; } getSubtitleNode(): ?TextNode { return this.subtitle !== null ? this.subtitle: this.getItem().getSubtitleNode(); } setSubtitle(subtitle: string | TextNodeOptions): void { if (Type.isString(subtitle) || Type.isPlainObject(subtitle)) { this.subtitle = new TextNode(subtitle); } else if (subtitle === null) { this.subtitle = null; } } getSupertitle(): ?string { const supertitleNode = this.getSupertitleNode(); return supertitleNode !== null ? supertitleNode.getText() : null; } getSupertitleNode(): ?TextNode { return this.supertitle !== null ? this.supertitle: this.getItem().getSupertitleNode(); } setSupertitle(supertitle: string | TextNodeOptions): void { if (Type.isString(supertitle) || Type.isPlainObject(supertitle)) { this.supertitle = new TextNode(supertitle); } else if (supertitle === null) { this.supertitle = null; } } getCaption(): ?string { const caption = this.getCaptionNode(); return caption !== null ? caption.getText() : null; } getCaptionNode(): ?TextNode { return this.caption !== null ? this.caption: this.getItem().getCaptionNode(); } setCaption(caption: string | TextNodeOptions): void { if (Type.isString(caption) || Type.isPlainObject(caption)) { this.caption = new TextNode(caption); } else if (caption === null) { this.caption = null; } } getCaptionOption(option: string): string | boolean | number | null { if (!Type.isUndefined(this.captionOptions[option])) { return this.captionOptions[option]; } return this.getItem().getCaptionOption(option); } setCaptionOption(option: string, value: string | boolean | number | null): void { if (Type.isStringFilled(option) && !Type.isUndefined(value)) { this.captionOptions[option] = value; } } setCaptionOptions(options: {[key: string]: any } | null): void { if (Type.isPlainObject(options)) { Object.keys(options).forEach((option: string) => { this.setCaptionOption(option, options[option]); }); } } getAvatar(): ?string { return this.avatar !== null ? this.avatar : this.getItem().getAvatar(); } setAvatar(avatar: ?string): void { if (Type.isString(avatar) || avatar === null) { this.avatar = avatar; } } getAvatarOption(option: $Keys<AvatarOptions>): string | boolean | number | null { return ( this.avatarOptions === null || Type.isUndefined(this.avatarOptions[option]) ? this.getItem().getAvatarOption(option) : this.avatarOptions[option] ); } setAvatarOption(option: $Keys<AvatarOptions>, value: string | boolean | number | null): void { if (Type.isStringFilled(option) && !Type.isUndefined(value)) { if (this.avatarOptions === null) { this.avatarOptions = {}; } this.avatarOptions[option] = value; } } setAvatarOptions(avatarOptions: AvatarOptions): void { if (Type.isPlainObject(avatarOptions)) { Object.keys(avatarOptions).forEach((option: string) => { this.setAvatarOption(option, avatarOptions[option]); }); } } getTextColor(): ?string { return this.textColor !== null ? this.textColor : this.getItem().getTextColor(); } setTextColor(textColor: ?string): void { if (Type.isString(textColor) || textColor === null) { this.textColor = textColor; } } getLink(): ?string { return this.link !== null ? this.getItem().replaceMacros(this.link) : this.getItem().getLink(); } setLink(link: string): void { if (Type.isString(link) || link === null) { this.link = link; } } getLinkTitle(): ?string { const linkTitle = this.getLinkTitleNode(); return linkTitle !== null ? linkTitle.getText() : null; } getLinkTitleNode(): ?TextNode { return this.linkTitle !== null ? this.linkTitle: this.getItem().getLinkTitleNode(); } setLinkTitle(title: string | TextNodeOptions): void { if (Type.isString(title) || Type.isPlainObject(title)) { this.linkTitle = new TextNode(title); } else if (title === null) { this.linkTitle = null; } } getBadges(): ItemBadge[] { return this.badges !== null ? this.badges : this.getItem().getBadges(); } setBadges(badges: ?ItemBadgeOptions[]): void { if (Type.isArray(badges)) { this.badges = []; badges.forEach(badge => { this.badges.push(new ItemBadge(badge)); }); } else if (badges === null) { this.badges = null; } } getBadgesOption(option: string): string | boolean | number | null { if (!Type.isUndefined(this.badgesOptions[option])) { return this.badgesOptions[option]; } return this.getItem().getBadgesOption(option); } setBadgesOption(option: string, value: string | boolean | number | null): void { if (Type.isStringFilled(option) && !Type.isUndefined(value)) { this.badgesOptions[option] = value; } } setBadgesOptions(options: {[key: string]: any } | null): void { if (Type.isPlainObject(options)) { Object.keys(options).forEach((option: string) => { this.setBadgesOption(option, options[option]); }); } } getOuterContainer(): HTMLElement { return this.cache.remember('outer-container', () => { let className = ''; if (this.hasChildren() || this.isDynamic()) { className += ' ui-selector-item-box-has-children'; if (this.getDepthLevel() >= this.getTab().getItemMaxDepth()) { className += ' ui-selector-item-box-max-depth'; } } else if (this.getItem().isSelected()) { className += ' ui-selector-item-box-selected'; } if (this.isOpen()) { className += ' ui-selector-item-box-open'; } const div = document.createElement('div'); div.className = `ui-selector-item-box${className}`; div.appendChild(this.getContainer()); div.appendChild(this.getChildrenContainer()); return div; }); } getChildrenContainer(): HTMLElement { if (this.isRoot() && this.getTab()) { return this.getTab().getItemsContainer(); } return this.cache.remember('children-container', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-children'; return div; }); } getContainer(): HTMLElement { return this.cache.remember('container', () => { const div = document.createElement('div'); div.className = 'ui-selector-item'; Event.bind(div, 'click', this.handleClick.bind(this)) Event.bind(div, 'mouseenter', this.handleMouseEnter.bind(this)) Event.bind(div, 'mouseleave', this.handleMouseLeave.bind(this)) div.appendChild(this.getAvatarContainer()); div.appendChild(this.getTitlesContainer()); div.appendChild(this.getIndicatorContainer()); if (Type.isStringFilled(this.getLink())) { div.appendChild(this.getLinkContainer()); } return div; }); } getAvatarContainer(): HTMLElement { return this.cache.remember('avatar', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-avatar'; return div; }); } getTitlesContainer(): HTMLElement { return this.cache.remember('titles', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-titles'; div.appendChild(this.getSupertitleContainer()); div.appendChild(this.getTitleBoxContainer()); div.appendChild(this.getSubtitleContainer()); return div; }); } getTitleBoxContainer(): HTMLElement { return this.cache.remember('title-box', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-title-box'; div.appendChild(this.getTitleContainer()); div.appendChild(this.getBadgeContainer()); div.appendChild(this.getCaptionContainer()); return div; }); } getTitleContainer(): HTMLElement { return this.cache.remember('title', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-title'; return div; }); } getSubtitleContainer(): HTMLElement { return this.cache.remember('subtitle', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-subtitle'; return div; }); } getSupertitleContainer(): HTMLElement { return this.cache.remember('supertitle', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-supertitle'; return div; }); } getCaptionContainer(): HTMLElement { return this.cache.remember('caption', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-caption'; return div; }); } getIndicatorContainer(): HTMLElement { return this.cache.remember('indicator', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-indicator'; return div; }); } getBadgeContainer(): HTMLElement { return this.cache.remember('badge', () => { const div = document.createElement('div'); div.className = 'ui-selector-item-badges'; return div; }); } getLinkContainer(): HTMLElement { return this.cache.remember('link', () => { const anchor: HTMLAnchorElement = document.createElement('a'); anchor.className = 'ui-selector-item-link'; anchor.href = this.getLink(); anchor.target = '_blank'; anchor.title = ''; Event.bind(anchor, 'click', this.handleLinkClick.bind(this)); anchor.appendChild(this.getLinkTextContainer()); return anchor; }); } getLinkTextContainer(): HTMLElement { return this.cache.remember('link-text', () => { const span = document.createElement('span'); span.className = 'ui-selector-item-link-text'; return span; }); } showLink(): void { if (Type.isStringFilled(this.getLink())) { Dom.addClass(this.getLinkContainer(), 'ui-selector-item-link--show'); requestAnimationFrame(() => { requestAnimationFrame(() => { Dom.addClass(this.getLinkContainer(), 'ui-selector-item-link--animate'); }); }); } } hideLink(): void { if (Type.isStringFilled(this.getLink())) { Dom.removeClass( this.getLinkContainer(), ['ui-selector-item-link--show', 'ui-selector-item-link--animate'] ); } } setHighlights(highlights: MatchField[]): void { this.highlights = highlights; } getHighlights(): MatchField[] { return this.highlights; } highlight(): void { this.getHighlights().forEach(matchField => { const field = matchField.getField(); const fieldName = field.getName(); if (field.isCustom()) { const text = this.getItem().getCustomData().get(fieldName); this.getSubtitleContainer().innerHTML = Highlighter.mark(text, matchField.getMatches()); } else if (field.getName() === 'title') { this.getTitleContainer().innerHTML = Highlighter.mark(this.getItem().getTitleNode(), matchField.getMatches()) ; } else if (field.getName() === 'subtitle') { this.getSubtitleContainer().innerHTML = Highlighter.mark(this.getItem().getSubtitleNode(), matchField.getMatches()) ; } else if (field.getName() === 'supertitle') { this.getSupertitleContainer().innerHTML = Highlighter.mark(this.getItem().getSupertitleNode(), matchField.getMatches()) ; } else if (field.getName() === 'caption') { this.getCaptionContainer().innerHTML = ( Highlighter.mark(this.getItem().getCaptionNode(), matchField.getMatches()) ); } }); } select(): void { if (this.hasChildren() || this.isDynamic()) { return; } Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-selected'); } deselect(): void { if (this.hasChildren() || this.isDynamic()) { return; } Dom.removeClass(this.getOuterContainer(), 'ui-selector-item-box-selected'); } focus(): void { if (this.isFocused()) { return; } this.focused = true; Dom.addClass(this.getOuterContainer(), 'ui-selector-item-box-focused'); this.getDialog().emit('ItemNode:onFocus', { node: this }); } unfocus(): void { if (!this.isFocused()) { return; } this.focused = false; Dom.removeClass(this.getOuterContainer(), 'ui-selector-item-box-focused'); this.getDialog().emit('ItemNode:onUnfocus', { node: this }); } isFocused(): boolean { return this.focused; } click(): void { if (this.hasChildren() || this.isDynamic()) { if (this.isOpen()) { this.collapse(); } else { this.expand(); } } else { if (this.getItem().isSelected()) { if (this.getItem().isDeselectable()) { this.getItem().deselect(); } if (this.getDialog().shouldHideOnDeselect()) { this.getDialog().hide(); } } else { this.getItem().select(); if (this.getDialog().shouldClearSearchOnSelect()) { this.getDialog().clearSearch(); } if (this.getDialog().shouldHideOnSelect()) { this.getDialog().hide(); } } } this.getDialog().focusSearch(); } scrollIntoView(): void { const tabContainer = this.getTab().getContainer(); const nodeContainer = this.getContainer(); const tabRect = Dom.getPosition(tabContainer); const nodeRect = Dom.getPosition(nodeContainer); const margin = 9; // 'ui-selector-items' padding - 'ui-selector-item' margin = 10 - 1 if (nodeRect.top < tabRect.top) // scroll up { tabContainer.scrollTop -= (tabRect.top - nodeRect.top + margin); } else if (nodeRect.bottom > tabRect.bottom) // scroll down { tabContainer.scrollTop += nodeRect.bottom - tabRect.bottom + margin; } } #makeEllipsisTitle(): void { if (this.constructor.#isEllipsisActive(this.getTitleContainer())) { this.getContainer().setAttribute( 'title', this.constructor.#sanitizeTitle(this.getTitleContainer().textContent) ); } else { Dom.attr(this.getContainer(), 'title', null); } const containers = [ this.getSupertitleContainer(), this.getSubtitleContainer(), this.getCaptionContainer(), ...this.getBadges().map((badge: ItemBadge) => badge.getContainer(this.getBadgeContainer())) ]; containers.forEach(container => { if (this.constructor.#isEllipsisActive(container)) { container.setAttribute('title', this.constructor.#sanitizeTitle(container.textContent)); } else { Dom.attr(container, 'title', null); } }); } static #isEllipsisActive(element: HTMLElement): boolean { return element.offsetWidth < element.scrollWidth; } static #sanitizeTitle(text: string) { return text.replace(/[\t ]+/gm, ' ').replace(/\n+/gm, '\n').trim(); } handleClick(): void { this.click(); } handleLinkClick(event: MouseEvent): void { this.getDialog().emit('ItemNode:onLinkClick', { node: this, event }); event.stopPropagation(); } handleMouseEnter(): void { this.focus(); this.showLink(); this.#makeEllipsisTitle(); } handleMouseLeave(): void { this.unfocus(); this.hideLink(); } }