import { PropertyValueMap, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import {
biArrowLeftRight,
biClipboard,
biCodeSlash,
biFullscreen,
biFullscreenExit,
biList,
biQuestionLg,
} from '../styles/icons';
import '@shoelace-style/shoelace/dist/themes/light.css';
import SlButton from '@shoelace-style/shoelace/dist/components/button/button.component.js';
import SlBadge from '@shoelace-style/shoelace/dist/components/badge/badge.component.js';
import SlTooltip from '@shoelace-style/shoelace/dist/components/tooltip/tooltip.component.js';
import SlPopup from '@shoelace-style/shoelace/dist/components/popup/popup.component.js';
import SlTab from '@shoelace-style/shoelace/dist/components/tab/tab.component.js';
import SlTabGroup from '@shoelace-style/shoelace/dist/components/tab-group/tab-group.component.js';
import SlTabPanel from '@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.component.js';
import { AutomatonComponent } from '../index';
import { topMenuStyles } from '../styles/topMenu';
import { transformations } from '../utils/transformations';
import { LitElementWw } from '@webwriter/lit';
import { Graph } from '../graph';
import { stripNode, stripTransition } from '../utils/updates';
import RandExp from 'randexp';
import { SimulationStatus } from 'automata';
import { NFA } from 'automata/nfa';
import { PDA } from 'automata/pda';
import { localized, msg } from '@lit/localize';
import { Logger } from '@u/logger';
@customElement('webwriter-automaton-topmenu')
@localized()
export class TopMenu extends LitElementWw {
@property({ type: Object, attribute: false })
private accessor _component!: AutomatonComponent;
public set component(component: AutomatonComponent) {
this._component = component;
}
public get component(): AutomatonComponent {
return this._component;
}
@state()
private accessor _fullscreen: boolean = false;
@state()
private accessor _helpOverlay: boolean = false;
@state()
private accessor _collapsed: boolean = false;
@state()
private accessor _menuOpen: boolean = false;
private _resizeObserver?: ResizeObserver;
@property({ type: Object, attribute: false })
private accessor _graph!: Graph;
public set graph(graph: Graph) {
this._graph = graph;
}
@property({ type: Object, attribute: false })
private accessor _setHelpOverlay!: (visible: boolean) => (void);
public set setHelpOverlay(f: (visible: boolean) => (void)) {
this._setHelpOverlay = f;
}
private _setCollapsed?: (collapsed: boolean) => void;
public set setCollapsed(f: (collapsed: boolean) => void) {
this._setCollapsed = f;
}
public static get styles() {
return topMenuStyles;
}
public static get scopedElements() {
return {
'sl-button': SlButton,
'sl-badge': SlBadge,
'sl-tooltip': SlTooltip,
'sl-popup': SlPopup,
'sl-tab': SlTab,
'sl-tab-group': SlTabGroup,
'sl-tab-panel': SlTabPanel,
};
}
connectedCallback(): void {
super.connectedCallback();
const host = (this.getRootNode() as ShadowRoot)?.host ?? this.parentElement;
if (host) {
this._resizeObserver = new ResizeObserver((entries) => {
const width = entries[0]?.contentRect.width ?? Infinity;
const collapsed = width < 500;
this._collapsed = collapsed;
this._setCollapsed?.(collapsed);
if (!collapsed) this._menuOpen = false;
});
this._resizeObserver.observe(host);
}
}
disconnectedCallback(): void {
super.disconnectedCallback();
this._resizeObserver?.disconnect();
}
protected firstUpdated(_changedProperties: PropertyValueMap | Map): void {
// Important for when the user exits fullscreen mode by pressing ESC or F11
document.addEventListener('fullscreenchange', () => {
this._fullscreen = !!document.fullscreenElement;
});
}
render() {
if (this._collapsed) {
return html`
`;
}
return html``;
}
private renderButtonGroups(collapsed = false) {
const formalDefinition = this._component?.automaton?.getFormalDefinition();
const tooltipPlacement = collapsed ? 'top' : 'left';
const popupPlacement = collapsed ? 'left-end' : 'bottom-end';
return html`
`;
}
private getTransitionsTable() {
const transitions = this._component.automaton.transitions
.get()
.filter((t) => t.from !== Graph.initialGhostNode.id);
const alphabet = this._component.automaton.getFormalDefinition().alphabet.split(', ').sort();
const nodes = this._component.automaton.nodes.get().filter((n) => n.id !== Graph.initialGhostNode.id);
const table = html``;
return table;
}
private switchAutomatonType(type: string): void {
Logger.log(
'Switching from',
this._component.automaton.type.toUpperCase(),
'to',
type.toUpperCase()
);
switch (this._component.automaton.type) {
case 'dfa':
if (type === 'nfa') this._component.automaton = transformations.DFAtoNFA(this._component.automaton);
if (type === 'pda') this._component.automaton = transformations.DFAtoPDA(this._component.automaton);
break;
case 'nfa':
if (type === 'dfa') this._component.automaton = transformations.NFAtoDFA(this._component.automaton as NFA);
if (type === 'pda') this._component.automaton = transformations.NFAtoPDA(this._component.automaton as NFA);
break;
case 'pda':
if (type === 'dfa') this._component.automaton = transformations.PDAtoDFA(this._component.automaton as PDA);
if (type === 'nfa') this._component.automaton = transformations.PDAtoNFA(this._component.automaton as PDA);
break;
}
this._graph.network.setData({
nodes: this._component.automaton.nodes,
edges: this._component.automaton.transitions,
});
this._component.nodes = this._component.automaton.nodes
.get()
.filter((n) => n.id !== Graph.initialGhostNode.id)
.map(stripNode);
this._component.nodes = [...this._component.nodes];
this._component.transitions = this._component.automaton.transitions
.get()
.filter((t) => t.from !== Graph.initialGhostNode.id)
.map(stripTransition);
this._component.transitions = [...this._component.transitions];
this.requestUpdate();
this._component.requestUpdate();
}
}