UNPKG

@infinite-canvas-tutorial/webcomponents

Version:
297 lines (290 loc) 12.3 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LayersPanel = void 0; const lit_1 = require("lit"); const context_1 = require("@lit/context"); const map_js_1 = require("lit/directives/map.js"); const when_js_1 = require("lit/directives/when.js"); const decorators_js_1 = require("lit/decorators.js"); const ecs_1 = require("@infinite-canvas-tutorial/ecs"); const context_2 = require("../context"); const event_1 = require("../event"); const localize_1 = require("@lit/localize"); let LayersPanel = class LayersPanel extends lit_1.LitElement { connectedCallback() { super.connectedCallback(); this.api.element.addEventListener(event_1.Event.SELECTED_NODES_CHANGED, (e) => { const { selected } = e.detail; // Scroll to the selected layer if (selected.length > 0) { const scrollToId = this.generateLayersPanelItemId(selected[0]); const scrollToElement = this.shadowRoot.querySelector(`#${scrollToId}`); const container = this.shadowRoot.querySelector('.container'); if (scrollToElement && container) { // 计算元素相对于容器的位置 const elementTop = scrollToElement.offsetTop; const elementHeight = scrollToElement.offsetHeight; const containerHeight = container.clientHeight; // 如果元素不在视口内,则滚动到合适位置 const currentScrollTop = container.scrollTop; const elementBottom = elementTop + elementHeight; const visibleTop = currentScrollTop; const visibleBottom = currentScrollTop + containerHeight; if (elementTop < visibleTop || elementBottom > visibleBottom) { // 计算目标滚动位置,让元素在视口中央 const targetScrollTop = elementTop - (containerHeight - elementHeight) / 2; container.scrollTo({ top: Math.max(0, targetScrollTop), behavior: 'smooth', }); } } } }); } generateLayersPanelItemId(node) { return `layers-panel-item-${node.id}`; } handleClose() { this.api.setAppState({ taskbarSelected: this.appState.taskbarSelected.filter((task) => task !== ecs_1.Task.SHOW_LAYERS_PANEL), }); } handleDelete() { const { layersSelected } = this.api.getAppState(); this.api.deleteNodesById(layersSelected); // Try to select the next layer const nextLayer = this.nodes.find((node) => !layersSelected.includes(node.id)); if (nextLayer) { this.api.selectNodes([nextLayer]); } this.api.record(); } handleAdd() { // TODO: add new layer // this.api.addNodes(this.nodes); } handleSelect(e, id) { const { layersSelected } = this.api.getAppState(); if (layersSelected.length === 1 && layersSelected.includes(id)) { return; } const node = this.api.getNodeById(id); if (!node.locked) { this.api.selectNodes([node], e.shiftKey); this.api.record(); } } handleBringToFront() { const { layersSelected } = this.api.getAppState(); const node = this.api.getNodeById(layersSelected[0]); this.api.bringToFront(node); this.api.record(); } handleBringForward() { const { layersSelected } = this.api.getAppState(); const node = this.api.getNodeById(layersSelected[0]); this.api.bringForward(node); this.api.record(); } handleSendBackward() { const { layersSelected } = this.api.getAppState(); const node = this.api.getNodeById(layersSelected[0]); this.api.sendBackward(node); this.api.record(); } handleSendToBack() { const { layersSelected } = this.api.getAppState(); const node = this.api.getNodeById(layersSelected[0]); this.api.sendToBack(node); this.api.record(); } render() { const { layersSelected, taskbarSelected } = this.appState; const sortedNodes = this.nodes .filter((node) => node.parentId === undefined) .map((node) => { return this.api.getEntity(node); }) .sort(ecs_1.sortByFractionalIndex) .map((entity) => { return this.api.getNodeByEntity(entity); }); const isSelectedEmpty = layersSelected.length === 0; let bringForwardDisabled = false; let sendBackwardDisabled = false; if (layersSelected.length === 1) { const node = this.api.getNodeById(layersSelected[0]); if (node) { const children = this.api .getSiblings(node) .filter((child) => !child.has(ecs_1.UI)); const maxZIndex = Math.max(...children.map((child) => child.read(ecs_1.ZIndex).value)); const minZIndex = Math.min(...children.map((child) => child.read(ecs_1.ZIndex).value)); if (node.zIndex === maxZIndex) { bringForwardDisabled = true; } if (node.zIndex === minZIndex) { sendBackwardDisabled = true; } } } return taskbarSelected.includes(ecs_1.Task.SHOW_LAYERS_PANEL) ? (0, lit_1.html) `<section> <h4> ${(0, localize_1.msg)((0, localize_1.str) `Layers`)} <sp-action-button quiet size="s" @click=${this.handleClose}> <sp-icon-close slot="icon"></sp-icon-close> </sp-action-button> </h4> <sp-action-group class="actions"> <sp-action-button quiet size="s" disabled @click=${this.handleAdd}> <sp-tooltip self-managed placement="bottom"> ${(0, localize_1.msg)((0, localize_1.str) `Add new layer`)} </sp-tooltip> <sp-icon-add slot="icon"></sp-icon-add> </sp-action-button> <sp-action-menu label=${(0, localize_1.msg)((0, localize_1.str) `Arrange layers`)} quiet size="s" .disabled=${layersSelected.length === 0} > <sp-icon-show-all-layers slot="icon"></sp-icon-show-all-layers> <sp-menu-group> <span slot="header">${(0, localize_1.msg)((0, localize_1.str) `Arrange layers`)}</span> <sp-menu-item ?disabled=${isSelectedEmpty || bringForwardDisabled} @click=${this.handleBringToFront} > <sp-icon-layers-bring-to-front slot="icon" ></sp-icon-layers-bring-to-front> ${(0, localize_1.msg)((0, localize_1.str) `Bring to front`)} </sp-menu-item> <sp-menu-item ?disabled=${isSelectedEmpty || bringForwardDisabled} @click=${this.handleBringForward} > <sp-icon-layers-forward slot="icon"></sp-icon-layers-forward> ${(0, localize_1.msg)((0, localize_1.str) `Bring forward`)} </sp-menu-item> <sp-menu-item ?disabled=${isSelectedEmpty || sendBackwardDisabled} @click=${this.handleSendBackward} > <sp-icon-layers-backward slot="icon" ></sp-icon-layers-backward> ${(0, localize_1.msg)((0, localize_1.str) `Send backward`)} </sp-menu-item> <sp-menu-item ?disabled=${isSelectedEmpty || sendBackwardDisabled} @click=${this.handleSendToBack} > <sp-icon-layers-send-to-back slot="icon" ></sp-icon-layers-send-to-back> ${(0, localize_1.msg)((0, localize_1.str) `Send to back`)} </sp-menu-item> </sp-menu-group> </sp-action-menu> <sp-action-button quiet size="s" @click=${this.handleDelete} .disabled=${layersSelected.length === 0} > <sp-tooltip self-managed placement="bottom"> ${(0, localize_1.msg)((0, localize_1.str) `Delete`)} </sp-tooltip> <sp-icon-delete slot="icon"></sp-icon-delete> </sp-action-button> </sp-action-group> <div class="container"> ${(0, map_js_1.map)(sortedNodes, (node) => { return this.renderParentNode(node); })} </div> </section>` : null; } renderParentNode(node, depth = 0) { const children = this.api.getChildren(node); const sortedNodes = children.sort(ecs_1.sortByFractionalIndex).map((entity) => { return this.api.getNodeByEntity(entity); }); const { layersSelected, layersHighlighted, layersExpanded } = this.appState; const hasChildren = sortedNodes.length > 0; const isExpanded = layersExpanded.includes(node.id); return (0, lit_1.html) `<ic-spectrum-layers-panel-item id=${this.generateLayersPanelItemId(node)} .node=${node} .depth=${depth} .hasChildren=${hasChildren} draggable @click=${(e) => this.handleSelect(e, node.id)} ?selected=${layersSelected.includes(node.id)} ?highlighted=${layersHighlighted.includes(node.id)} ></ic-spectrum-layers-panel-item> ${(0, when_js_1.when)(hasChildren && isExpanded, () => (0, lit_1.html) ` ${(0, map_js_1.map)(sortedNodes, (node) => { // TODO: virtual scroll for better performance return this.renderParentNode(node, depth + 1); })} `)}`; } }; exports.LayersPanel = LayersPanel; LayersPanel.styles = (0, lit_1.css) ` section { display: flex; flex-direction: column; background: var(--spectrum-gray-100); border-radius: var(--spectrum-corner-radius-200); margin: 4px; filter: drop-shadow( var(--spectrum-drop-shadow-color) 0px var(--spectrum-drop-shadow-y) var(--spectrum-drop-shadow-blur) ); } h4 { padding: var(--spectrum-global-dimension-size-100); display: flex; align-items: center; justify-content: space-between; margin: 0; color: canvastext; } .actions { padding: 4px; padding-top: 0; } .container { height: 300px; overflow: hidden; overflow-y: auto; scroll-behavior: smooth; scroll-padding: 8px; } `; __decorate([ (0, context_1.consume)({ context: context_2.appStateContext, subscribe: true }) ], LayersPanel.prototype, "appState", void 0); __decorate([ (0, context_1.consume)({ context: context_2.nodesContext, subscribe: true }) ], LayersPanel.prototype, "nodes", void 0); __decorate([ (0, context_1.consume)({ context: context_2.apiContext, subscribe: true }) ], LayersPanel.prototype, "api", void 0); exports.LayersPanel = LayersPanel = __decorate([ (0, decorators_js_1.customElement)('ic-spectrum-layers-panel'), (0, localize_1.localized)() ], LayersPanel); //# sourceMappingURL=layers-panel.js.map