/** * Copyright Aquera Inc 2023 * * This source code is licensed under the BSD-3-Clause license found in the * LICENSE file in the root directory of this source tree. */ import { html, CSSResultArray, TemplateResult } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { styles } from './nile-app-shell-panel.css'; import { renderGlyph } from '../internal/glyph'; import { AppShellController } from '../nile-app-shell/shell-controller'; import '../nile-button/nile-button'; import NileElement from '../internal/nile-element'; import type { NileAppShellPanelMode } from '../nile-app-shell/nile-app-shell'; /** * @summary The shell's right region — the AI assistant panel; takes over the panel chrome while the shell keeps the geometry and open state. * * @status stable * @since 2.0 * * @tag nile-app-shell-panel * * @dependency nile-app-shell * @dependency nile-glyph * * @slot - The panel body. Scrolls. * @slot header - Replaces the default heading (mark plus label); the back control renders before it. * @slot actions - Controls added before the close button; size and clear their `base` part from page CSS to match the header's bare glyphs. * @slot footer - A composer, docked below the scroll region. * * @event nile-close - Emitted when the close button is pressed, after the shell has been told to close. * @event nile-back - Emitted when the back control is pressed; the page swaps its own content back. * @event nile-panel-back - Emitted right after `nile-back` for the same press, matching the shell's built-in panel chrome, so a page listening on the shell keeps working when it slots this component. Listen for one of the two, not both, or one press is handled twice. * * @csspart header - The header bar. * @csspart back - The back control, shown by `back`. * @csspart title - The default heading text. * @csspart actions - The header action group. * @csspart close - The close button. * @csspart body - The scrolling body. * @csspart footer - The footer. * * @cssproperty [--nile-app-shell-utility-size=32px] - Box the header's back and close controls sit in. * * @method goBack() - Emit `nile-back` then `nile-panel-back`, as the back control does. */ @customElement('nile-app-shell-panel') export class NileAppShellPanel extends NileElement { public static get styles(): CSSResultArray { return [styles]; } private shellLink = new AppShellController(this); /** The heading, and the accessible name of the region. */ @property({ type: String }) label: string = 'Ask Aquera'; /** Drops the header bar entirely. */ @property({ type: Boolean, reflect: true, attribute: 'no-header' }) noHeader: boolean = false; /** Shows a back control at the start of the header for a sub-view; the panel keeps no view stack and only emits `nile-back`. */ @property({ type: Boolean, reflect: true }) back: boolean = false; /** Accessible name of the back control. */ @property({ type: String }) backLabel: string = 'Back'; /** Drops the close button — for a panel the page closes its own way. */ @property({ type: Boolean, reflect: true, attribute: 'no-close' }) noClose: boolean = false; /** Removes the body's padding, for a full-bleed transcript. */ @property({ type: Boolean, reflect: true, attribute: 'no-padding' }) noPadding: boolean = false; /** Id of the shell to follow. Defaults to the nearest ancestor. */ @property({ type: String }) shell?: string; /** Mirrors the shell's panel mode. Set by the shell; treat it as read-only and style against it (`nile-app-shell-panel[mode='overlay']`). */ @property({ type: String, reflect: true }) mode: NileAppShellPanelMode = 'docked'; @state() private _hasFooter: boolean = false; public connectedCallback(): void { super.connectedCallback(); // Claim the shell's right region unless the author placed it elsewhere. if (!this.hasAttribute('slot')) this.setAttribute('slot', 'panel'); if (!this.hasAttribute('role')) this.setAttribute('role', 'complementary'); } protected willUpdate(): void { this.shellLink.shellId = this.shell; const shell = this.shellLink.shell; if (shell) this.mode = shell.panelMode; } protected updated(): void { this.toggleAttribute('has-footer', this._hasFooter); this.setAttribute('aria-label', this.label); } /** Announce that the back control was pressed, as the page owns which view it goes back to: `nile-back`, then `nile-panel-back` for pages written against the shell's built-in panel chrome. */ public goBack(): void { this.emit('nile-back'); this.emit('nile-panel-back'); } /** Close the panel through the shell. */ public close(): void { this.shellLink.shell?.closePanel(); this.emit('nile-close'); } private handleFooterSlotChange(event: Event): void { this._hasFooter = (event.target as HTMLSlotElement) .assignedNodes({ flatten: true }) .some( node => node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? '').trim() !== '' ); } public render(): TemplateResult { return html`
${this.back ? html` this.goBack()} > ${renderGlyph('ng-arrow-left', 20, 'currentColor')} ` : ''} ${renderGlyph('ng-sparkles', 20, 'currentColor')}

${this.label}

this.close()} > ${renderGlyph('ng-x-close', 20, 'currentColor')}
`; } } export default NileAppShellPanel; declare global { interface HTMLElementTagNameMap { 'nile-app-shell-panel': NileAppShellPanel; } }