/**
* 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.label}