UNPKG

@infinite-canvas-tutorial/webcomponents

Version:
256 lines (251 loc) 9.5 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.PenbarCrop = void 0; const lit_1 = require("lit"); const decorators_js_1 = require("lit/decorators.js"); const context_1 = require("@lit/context"); const context_2 = require("../context"); const localize_1 = require("@lit/localize"); let PenbarCrop = class PenbarCrop extends lit_1.LitElement { constructor() { super(...arguments); this.cropRatio = 2; this.previousLayersCropping = []; this.originalClipWidth = 0; this.originalClipHeight = 0; /** Geometric center of the child node in clip coordinates when entering crop mode, scale around this point when ratio changes */ this.originalCenterX = 0; this.originalCenterY = 0; } get clipNode() { const { layersCropping } = this.appState; const [croppingNodeId] = layersCropping; const node = this.api.getNodeById(croppingNodeId); return node; } get clipChildNode() { const node = this.clipNode; if (node) { return this.api.getNodeByEntity(this.api.getChildren(node)[0]); } return null; } handleApply() { this.api.applyCrop(); } handleCancel() { this.api.cancelCrop(); } handleClipAspectChanged(event) { const value = event.target.value; if (value === 'original') { const { x: px, y: py } = this.clipNode; const { x: cx, y: cy, width: cw, height: ch } = this.clipChildNode; // Make clip the same size of the original image this.api.updateNode(this.clipNode, { x: px + cx, y: py + cy, width: cw, height: ch, }); this.api.updateNode(this.clipChildNode, { x: 0, y: 0, }); } else if (value === 'square') { // 1:1: Use the smaller dimension of the child element as the side length, crop from center const { x: px, y: py } = this.clipNode; const { x: cx, y: cy, width: cw, height: ch } = this.clipChildNode; const side = Math.min(cw, ch); const offsetX = (cw - side) / 2; const offsetY = (ch - side) / 2; this.api.updateNode(this.clipNode, { x: px + cx + offsetX, y: py + cy + offsetY, width: side, height: side, }); this.api.updateNode(this.clipChildNode, { x: -offsetX, y: -offsetY, }); } else if (value.includes(':')) { const [w, h] = value.split(':').map(Number); const ratio = w / h; const { x: px, y: py } = this.clipNode; const { x: cx, y: cy, width: cw, height: ch } = this.clipChildNode; // Calculate the largest visible rectangle within the child element according to the ratio, centered const clipW = Math.min(cw, ratio * ch); const clipH = clipW / ratio; const offsetX = (cw - clipW) / 2; const offsetY = (ch - clipH) / 2; this.api.updateNode(this.clipNode, { x: px + cx + offsetX, y: py + cy + offsetY, width: clipW, height: clipH, }); this.api.updateNode(this.clipChildNode, { x: -offsetX, y: -offsetY, }); } } handleCropRatioChanged(e) { const value = e.target.value; this.cropRatio = parseFloat(value); if (this.clipChildNode) { const child = this.clipChildNode; const width = this.originalClipWidth * this.cropRatio; const height = this.originalClipHeight * this.cropRatio; // Scale around the geometric center recorded when entering crop mode const x = this.originalCenterX - width / 2; const y = this.originalCenterY - height / 2; this.api.updateNode(child, { x, y, width, height, }); this.api.record(); } } shouldUpdate(changedProperties) { const newLayersCropping = this.appState.layersCropping; if (newLayersCropping.length !== this.previousLayersCropping.length) { this.previousLayersCropping = newLayersCropping; if (this.clipChildNode) { const child = this.clipChildNode; this.cropRatio = child.width / this.clipNode.width; this.originalClipWidth = this.clipNode.width; this.originalClipHeight = this.clipNode.height; // Record the geometric center when entering crop mode (in clip coordinates), scale around this center when ratio changes this.originalCenterX = child.x + child.width / 2; this.originalCenterY = child.y + child.height / 2; } return true; } return super.shouldUpdate(changedProperties); } render() { const { layersCropping } = this.appState; if (layersCropping.length === 1) { return (0, lit_1.html) ` <div class="wrapper"> ${(0, localize_1.msg)((0, localize_1.str) `Crop`)} <sp-divider size="s" style="align-self: stretch; height: auto;" vertical ></sp-divider> <sp-slider size="s" quiet value=${this.cropRatio} min=${1} max=${4} step=${0.2} labelVisibility="none" @input=${this.handleCropRatioChanged} ></sp-slider> <sp-divider size="s" style="align-self: stretch; height: auto;" vertical ></sp-divider> <sp-action-menu size="s" label=${(0, localize_1.msg)((0, localize_1.str) `Clip Aspect`)} @change=${this.handleClipAspectChanged}> <sp-icon-crop slot="icon"></sp-icon-crop> <sp-menu-item value="original"> ${(0, localize_1.msg)((0, localize_1.str) `Original`)} </sp-menu-item> <sp-menu-item value="square"> ${(0, localize_1.msg)((0, localize_1.str) `Square`)} </sp-menu-item> <sp-menu-item> ${(0, localize_1.msg)((0, localize_1.str) `Landscape`)} <sp-menu slot="submenu" @change=${this.handleClipAspectChanged}> <sp-menu-item value="16:9" >16:9</sp-menu-item> <sp-menu-item value="4:3" >4:3</sp-menu-item> <sp-menu-item value="3:2" >3:2</sp-menu-item> </sp-menu> </sp-menu-item> <sp-menu-item> ${(0, localize_1.msg)((0, localize_1.str) `Portrait`)} <sp-menu slot="submenu" @change=${this.handleClipAspectChanged}> <sp-menu-item value="9:16" >9:16</sp-menu-item> <sp-menu-item value="3:4" >3:4</sp-menu-item> <sp-menu-item value="2:3" >2:3</sp-menu-item> </sp-menu> </sp-menu-item> </sp-action-menu> <div class="buttons"> <sp-action-button quiet size="s" @click=${this.handleCancel}> <sp-icon-cancel slot="icon"></sp-icon-cancel> </sp-action-button> <sp-action-button quiet size="s" @click=${this.handleApply}> <sp-icon-checkmark slot="icon"></sp-icon-checkmark> </sp-action-button> </div> </div> `; } } }; exports.PenbarCrop = PenbarCrop; PenbarCrop.styles = (0, lit_1.css) ` .wrapper { transform: translateX(-50%); position: absolute; left: -50%; bottom: 8px; display: flex; gap: var(--spectrum-global-dimension-size-100); align-items: center; background: var(--spectrum-gray-100); border-radius: var(--spectrum-corner-radius-200); padding: var(--spectrum-global-dimension-size-100); padding-top: 0; padding-bottom: 0; margin: 4px; filter: drop-shadow( var(--spectrum-drop-shadow-color) 0px var(--spectrum-drop-shadow-y) var(--spectrum-drop-shadow-blur) ); } .buttons { display: flex; } `; __decorate([ (0, context_1.consume)({ context: context_2.appStateContext, subscribe: true }) ], PenbarCrop.prototype, "appState", void 0); __decorate([ (0, context_1.consume)({ context: context_2.apiContext, subscribe: true }) ], PenbarCrop.prototype, "api", void 0); __decorate([ (0, decorators_js_1.state)() ], PenbarCrop.prototype, "cropRatio", void 0); exports.PenbarCrop = PenbarCrop = __decorate([ (0, decorators_js_1.customElement)('ic-spectrum-penbar-crop') ], PenbarCrop); //# sourceMappingURL=penbar-crop.js.map