coveo-search-ui
Version:
Coveo JavaScript Search Framework
169 lines (149 loc) • 6.44 kB
text/typescript
import { Component } from '../Base/Component';
import { ComponentOptions } from '../Base/ComponentOptions';
import { IComponentBindings } from '../Base/ComponentBindings';
import { Initialization } from '../Base/Initialization';
import { CardOverlayEvents } from '../../events/CardOverlayEvents';
import { $$ } from '../../utils/Dom';
import { Assert } from '../../misc/Assert';
import { KeyboardUtils, KEYBOARD } from '../../utils/KeyboardUtils';
import { exportGlobally } from '../../GlobalExports';
import 'styling/_CardOverlay';
import { SVGIcons } from '../../utils/SVGIcons';
import { SVGDom } from '../../utils/SVGDom';
import { Utils } from '../../utils/Utils';
import { l } from '../../strings/Strings';
export interface ICardOverlayOptions {
title: string;
icon?: string;
}
/**
* The CardOverlay component displays a button that the user can click to toggle the visibility of an overlay on top of
* an {@link IQueryResult}. While this component typically populates a {@link CardActionBar} component, it is actually
* possible to place a CardOverlay component anywhere in any result.
*
* The primary purpose of the CardOverlay component is to display additional information about a result in a format that
* fits well within a card result layout (see [Result Layouts](https://docs.coveo.com/en/360/)).
*
* When initialized, this component creates a `<div class="coveo-card-overlay">` element as the last child of its parent
* IQueryResult, and displays a button which toggles the visibility of the overlay.
*/
export class CardOverlay extends Component {
static ID = 'CardOverlay';
static doExport = () => {
exportGlobally({
CardOverlay: CardOverlay
});
};
private parentCard: HTMLElement;
private overlay: HTMLElement;
/**
* @componentOptions
*/
static options: ICardOverlayOptions = {
/**
* Specifies the string to use for the overlay title and for the button text.
*
* Setting a value for this option is required for this component to work.
*/
title: ComponentOptions.buildLocalizedStringOption({
required: true,
localizedString: () => l('NoTitle')
}),
/**
* Specifies the icon to use for the overlay icon and for the button icon.
*
* The name of the icon to use should be specified in dashed case. ie: facet-expand
*/
icon: ComponentOptions.buildIconOption()
};
/**
* Creates a new CardOverlay component.
* @param element The HTMLElement on which to instantiate the component.
* @param options The options for the CardOverlay component.
* @param bindings The bindings that the component requires to function normally. If not set, these will be
* automatically resolved (with a slower execution time).
*/
constructor(public element: HTMLElement, public options?: ICardOverlayOptions, bindings?: IComponentBindings) {
super(element, CardOverlay.ID, bindings);
this.options = ComponentOptions.initComponentOptions(element, CardOverlay, options);
this.parentCard = $$(this.element).closest('.CoveoResult');
Assert.exists(this.parentCard);
$$(this.parentCard).addClass('coveo-with-card-overlay');
this.createOverlay();
this.createButton(this.element);
this.closeOverlay();
}
/**
* Toggles the CardOverlay visibility.
*
* @param swtch Specifying a value for this parameter forces the component visibility to take the corresponding value
* (`true` for visible; `false` for hidden).
*/
public toggleOverlay(swtch?: boolean) {
if (swtch !== undefined) {
swtch ? this.openOverlay() : this.closeOverlay();
} else {
if ($$(this.overlay).hasClass('coveo-opened')) {
this.closeOverlay();
} else {
this.openOverlay();
}
}
}
/**
* Opens the CardOverlay.
*
* Also triggers the {@link CardOverlayEvents.openCardOverlay} event.
*/
public openOverlay() {
$$(this.overlay).removeClass('coveo-hidden-for-tab-nav');
$$(this.overlay).addClass('coveo-opened');
$$(this.parentCard).addClass('coveo-card-overlay-opened');
this.bind.trigger(this.element, CardOverlayEvents.openCardOverlay);
}
/**
* Closes the CardOverlay.
*
* Also triggers the {@link CardOverlayEvents.closeCardOverlay} event.
*/
public closeOverlay() {
$$(this.overlay).addClass('coveo-hidden-for-tab-nav');
$$(this.overlay).removeClass('coveo-opened');
$$(this.parentCard).removeClass('coveo-card-overlay-opened');
this.bind.trigger(this.element, CardOverlayEvents.closeCardOverlay);
}
private createOverlay() {
this.overlay = $$('div', { className: 'coveo-card-overlay' }).el;
// Create header
let overlayHeader = $$('div', { className: 'coveo-card-overlay-header' }).el;
this.createButton(overlayHeader);
this.overlay.appendChild(overlayHeader);
// Create body
let overlayBody = $$('div', { className: 'coveo-card-overlay-body' }).el;
// Transfer all of element's children to the overlay
while (this.element.childNodes.length > 0) {
overlayBody.appendChild(this.element.firstChild);
}
this.overlay.appendChild(overlayBody);
// Create footer
const icon = $$('span', { className: 'coveo-icon coveo-open-card-overlay' }, SVGIcons.icons.arrowDown);
SVGDom.addClassToSVGInContainer(icon.el, 'coveo-open-card-overlay-svg');
let overlayFooter = $$('div', { className: 'coveo-card-overlay-footer', tabindex: '0' }, icon.el);
overlayFooter.on('click', () => this.toggleOverlay(false));
this.bind.on(overlayFooter.el, 'keyup', KeyboardUtils.keypressAction(KEYBOARD.ENTER, () => this.toggleOverlay(false)));
this.overlay.appendChild(overlayFooter.el);
this.parentCard.appendChild(this.overlay);
}
private createButton(element: HTMLElement) {
if (this.options.icon) {
const icon = $$('span', { className: 'coveo-icon ' }, SVGIcons.icons[this.options.icon]).el;
element.appendChild(icon);
SVGDom.addClassToSVGInContainer(element, `coveo-${Utils.toDashCase(this.options.icon)}-svg`);
}
element.appendChild($$('span', { className: 'coveo-label' }, this.options.title).el);
element.setAttribute('tabindex', '0');
$$(element).on('click', () => this.toggleOverlay());
this.bind.on(element, 'keyup', KeyboardUtils.keypressAction(KEYBOARD.ENTER, () => this.toggleOverlay()));
}
}
Initialization.registerAutoCreateComponent(CardOverlay);