import { property, query } from "lit/decorators.js"; import { LitElementWw } from "@webwriter/lit"; import { CSSResult, html, LitElement, TemplateResult, } from "lit"; import HelpCircleIcon from "@tabler/icons/outline/help-circle.svg"; import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"; import SlCheckbox from "@shoelace-style/shoelace/dist/components/checkbox/checkbox.component.js"; import SlOption from "@shoelace-style/shoelace/dist/components/option/option.component.js"; import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js"; import SlTooltip from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.component.js"; import SlTree from "@shoelace-style/shoelace/dist/components/tree/tree.component.js"; import SlTreeItem from "@shoelace-style/shoelace/dist/components/tree-item/tree-item.component.js"; import { styles } from "./options.styles"; import { msg } from "../../locales"; import { OptionsChangeEvent, StageType } from "../../types"; import { BlockTypes, SelectedBlocks } from "../../lib/blockly"; enum TreeItemState { UNSELECTED, INDETERMINATE, SELECTED, } /** * The options component. */ export class Options extends LitElementWw { /** * Whether the widget are readonly. */ @property({ type: Number }) public accessor readonly: 0 | 1; /** * The selected stage type. */ @property({ type: String }) public accessor stageType: StageType; /** * The selected blocks. */ @property({ type: Array }) public accessor selectedBlocks: SelectedBlocks; /** * The available blocks. */ @property({ type: Array }) public accessor availableBlocks: BlockTypes[]; /** * @inheritDoc */ public static get scopedElements(): Record { return { "sl-checkbox": SlCheckbox, "sl-select": SlSelect, "sl-option": SlOption, "sl-tree": SlTree, "sl-tree-item": SlTreeItem, "sl-tooltip": SlTooltip, "sl-icon": SlIcon, }; } /** * @inheritDoc */ public static get styles(): CSSResult[] { return [ styles, ]; } /** * @inheritDoc */ public connectedCallback(): void { super.connectedCallback(); } /** * Determines in which state a tree item folder should be. * @param category The category of the tree item. * @param blocks The blocks under the category. * @param selectedBlocksSet The set of selected blocks. * @returns True if the tree item should be indeterminate, false otherwise. * @private */ private getTreeItemFolderState(category: string, blocks: string[], selectedBlocksSet: Set): TreeItemState { const totalBlocks = blocks.length; const selectedBlocks = blocks.filter((name) => selectedBlocksSet.has(`${category}:${name}` as BlockTypes)).length; if (selectedBlocks === 0) { return TreeItemState.UNSELECTED; } if (selectedBlocks === totalBlocks) { return TreeItemState.SELECTED; } return TreeItemState.INDETERMINATE; } /** * @inheritDoc */ public render(): TemplateResult { const selectedBlocksSet = new Set(this.selectedBlocks); const availableBlocksMap = new Map(); this.availableBlocks.forEach((block: BlockTypes) => { const [category, name] = block.split(":") as [string, string]; if (!availableBlocksMap.has(category)) { availableBlocksMap.set(category, []); } if (name) { availableBlocksMap.get(category)!.push(name); } }); return html`
${msg("OPTIONS.READONLY")}
${msg("OPTIONS.STAGE")} ${Object.values(StageType).map((type) => html` ${msg(`OPTIONS.STAGE_TYPES.${type.toUpperCase() as Uppercase}`)} `)}
${msg("OPTIONS.AVAILABLE_BLOCKS")} all ${Array.from(availableBlocksMap.entries()).map(([category, blocks]) => { if (blocks.length === 0) { return html` ${category} `; } const folderState = this.getTreeItemFolderState(category, blocks, selectedBlocksSet); return html` ${category} ${blocks.map((name) => html` ${name} `)} `; })}
`; } /** * Handles the readonly change event. * @param event The change event. * @private */ private handleReadonlyChange(event): void { const changeEvent = new OptionsChangeEvent({ readonly: event.target.checked ? 1 : 0, }); this.dispatchEvent(changeEvent); } /** * Handles the stage type change event. * @param event The change event. * @private */ private handleStageTypeChange(event): void { const changeEvent = new OptionsChangeEvent({ stageType: event.target.value, }); this.dispatchEvent(changeEvent); } /** * Handles the selected blocks change event. * @param event The change event. * @private */ private handleSelectedBlocksChange(event: CustomEvent<{ selection: SlTreeItem[] }>): void { const selectedBlocks = event.detail.selection .filter((item) => item.getAttribute("data-block-key")) .map((item) => item.getAttribute("data-block-key") as BlockTypes); const changeEvent = new OptionsChangeEvent({ selectedBlocks, }); this.dispatchEvent(changeEvent); } }