UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

287 lines (286 loc) 14.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Erase_1 = __importDefault(require("../../commands/Erase")); const uniteCommands_1 = __importDefault(require("../../commands/uniteCommands")); const BackgroundComponent_1 = __importStar(require("../../components/BackgroundComponent")); const EditorImage_1 = require("../../image/EditorImage"); const math_1 = require("@js-draw/math"); const types_1 = require("../../types"); const constants_1 = require("../constants"); const makeColorInput_1 = __importDefault(require("./components/makeColorInput")); const BaseWidget_1 = __importDefault(require("./BaseWidget")); class DocumentPropertiesWidget extends BaseWidget_1.default { constructor(editor, localizationTable) { super(editor, 'document-properties-widget', localizationTable); this.updateDropdownContent = () => { }; this.dropdownUpdateQueued = false; // Make it possible to open the dropdown, even if this widget isn't selected. this.container.classList.add('dropdownShowable'); this.editor.notifier.on(types_1.EditorEventType.UndoRedoStackUpdated, () => { this.queueDropdownUpdate(); }); this.editor.image.notifier.on(EditorImage_1.EditorImageEventType.ExportViewportChanged, () => { this.queueDropdownUpdate(); }); } getTitle() { return this.localizationTable.documentProperties; } createIcon() { return this.editor.icons.makeConfigureDocumentIcon(); } handleClick() { this.setDropdownVisible(!this.isDropdownVisible()); this.queueDropdownUpdate(); } queueDropdownUpdate() { if (!this.dropdownUpdateQueued) { requestAnimationFrame(() => this.updateDropdown()); this.dropdownUpdateQueued = true; } } updateDropdown() { this.dropdownUpdateQueued = false; if (this.isDropdownVisible()) { this.updateDropdownContent(); } } setBackgroundColor(color) { this.editor.dispatch(this.editor.setBackgroundColor(color)); } getBackgroundColor() { return this.editor.estimateBackgroundColor(); } removeBackgroundComponents() { const previousBackgrounds = []; for (const component of this.editor.image.getBackgroundComponents()) { if (component instanceof BackgroundComponent_1.default) { previousBackgrounds.push(component); } } return new Erase_1.default(previousBackgrounds); } /** Replace existing background components with a background of the given type. */ setBackgroundType(backgroundType) { const prevBackgroundColor = this.editor.estimateBackgroundColor(); const newBackground = new BackgroundComponent_1.default(backgroundType, prevBackgroundColor); const addBackgroundCommand = this.editor.image.addComponent(newBackground); return (0, uniteCommands_1.default)([this.removeBackgroundComponents(), addBackgroundCommand]); } /** Returns the type of the topmost background component */ getBackgroundType() { const backgroundComponents = this.editor.image.getBackgroundComponents(); for (let i = backgroundComponents.length - 1; i >= 0; i--) { const component = backgroundComponents[i]; if (component instanceof BackgroundComponent_1.default) { return component.getBackgroundType(); } } return BackgroundComponent_1.BackgroundType.None; } updateImportExportRectSize(size) { const filterDimension = (dim) => { if (dim !== undefined && (!isFinite(dim) || dim <= 0)) { dim = 100; } return dim; }; const width = filterDimension(size.width); const height = filterDimension(size.height); const currentRect = this.editor.getImportExportRect(); const newRect = new math_1.Rect2(currentRect.x, currentRect.y, width ?? currentRect.w, height ?? currentRect.h); this.editor.dispatch(this.editor.image.setImportExportRect(newRect)); this.editor.queueRerender(); } getHelpText() { return this.localizationTable.pageDropdown__baseHelpText; } fillDropdown(dropdown, helpDisplay) { const container = document.createElement('div'); container.classList.add(`${constants_1.toolbarCSSPrefix}spacedList`, `${constants_1.toolbarCSSPrefix}nonbutton-controls-main-list`, `${constants_1.toolbarCSSPrefix}document-properties-widget`); // Background color input const makeBackgroundColorInput = () => { const backgroundColorRow = document.createElement('div'); const backgroundColorLabel = document.createElement('label'); backgroundColorLabel.innerText = this.localizationTable.backgroundColor; const { input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue, registerWithHelpTextDisplay: registerHelpForInputs, } = (0, makeColorInput_1.default)(this.editor, (color) => { if (!color.eq(this.getBackgroundColor())) { this.setBackgroundColor(color); } }); colorInput.id = `${constants_1.toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`; backgroundColorLabel.htmlFor = colorInput.id; backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer); const registerWithHelp = (helpDisplay) => { if (!helpDisplay) { return; } helpDisplay?.registerTextHelpForElement(backgroundColorRow, this.localizationTable.pageDropdown__backgroundColorHelpText); registerHelpForInputs(helpDisplay); }; return { setBgColorInputValue, backgroundColorRow, registerWithHelp }; }; const { backgroundColorRow, setBgColorInputValue, registerWithHelp: registerBackgroundRowWithHelp, } = makeBackgroundColorInput(); const makeCheckboxRow = (labelText, onChange) => { const rowContainer = document.createElement('div'); const labelElement = document.createElement('label'); const checkboxElement = document.createElement('input'); checkboxElement.id = `${constants_1.toolbarCSSPrefix}docPropertiesCheckbox-${DocumentPropertiesWidget.idCounter++}`; labelElement.htmlFor = checkboxElement.id; checkboxElement.type = 'checkbox'; labelElement.innerText = labelText; checkboxElement.oninput = () => { onChange(checkboxElement.checked); }; rowContainer.replaceChildren(labelElement, checkboxElement); return { container: rowContainer, checkbox: checkboxElement }; }; // Background style selector const { container: useGridRow, checkbox: useGridCheckbox } = makeCheckboxRow(this.localizationTable.useGridOption, (checked) => { const prevBackgroundType = this.getBackgroundType(); const wasGrid = prevBackgroundType === BackgroundComponent_1.BackgroundType.Grid; if (wasGrid === checked) { // Already the requested background type. return; } let newBackgroundType = BackgroundComponent_1.BackgroundType.SolidColor; if (checked) { newBackgroundType = BackgroundComponent_1.BackgroundType.Grid; } this.editor.dispatch(this.setBackgroundType(newBackgroundType)); }); // Adds a width/height input const addDimensionRow = (labelContent, onChange) => { const row = document.createElement('div'); const label = document.createElement('label'); const input = document.createElement('input'); label.innerText = labelContent; input.type = 'number'; input.min = '0'; input.id = `${constants_1.toolbarCSSPrefix}docPropertiesDimensionRow-${DocumentPropertiesWidget.idCounter++}`; label.htmlFor = input.id; input.style.flexGrow = '2'; input.style.width = '25px'; input.oninput = () => { onChange(parseFloat(input.value)); }; row.classList.add('js-draw-size-input-row'); row.replaceChildren(label, input); return { setValue: (value) => { // Slightly improve the case where the user tries to change the // first digit of a dimension like 600. // // As changing the value also gives the image zero size (which is unsupported, // .setValue is called immediately). We work around this by trying to select // the added/changed digits. // // See https://github.com/personalizedrefrigerator/js-draw/issues/58. if (document.activeElement === input && input.value.match(/^0*$/)) { // We need to switch to type="text" and back to type="number" because // number inputs don't support selection. // // See https://stackoverflow.com/q/22381837 const originalValue = input.value; input.type = 'text'; input.value = value.toString(); // Select the added digits const lengthToSelect = Math.max(1, input.value.length - originalValue.length); input.setSelectionRange(0, lengthToSelect); input.type = 'number'; } else { input.value = value.toString(); } }, setIsAutomaticSize: (automatic) => { input.disabled = automatic; const automaticSizeClass = 'size-input-row--automatic-size'; if (automatic) { row.classList.add(automaticSizeClass); } else { row.classList.remove(automaticSizeClass); } }, element: row, }; }; const imageWidthRow = addDimensionRow(this.localizationTable.imageWidthOption, (value) => { this.updateImportExportRectSize({ width: value }); }); const imageHeightRow = addDimensionRow(this.localizationTable.imageHeightOption, (value) => { this.updateImportExportRectSize({ height: value }); }); // The autoresize checkbox const { container: auroresizeRow, checkbox: autoresizeCheckbox } = makeCheckboxRow(this.localizationTable.enableAutoresizeOption, (checked) => { const image = this.editor.image; this.editor.dispatch(image.setAutoresizeEnabled(checked)); }); // The "About..." button const aboutButton = document.createElement('button'); aboutButton.classList.add('about-button'); aboutButton.innerText = this.localizationTable.about; aboutButton.onclick = () => { this.editor.showAboutDialog(); }; // Add help text registerBackgroundRowWithHelp(helpDisplay); helpDisplay?.registerTextHelpForElement(useGridRow, this.localizationTable.pageDropdown__gridCheckboxHelpText); helpDisplay?.registerTextHelpForElement(auroresizeRow, this.localizationTable.pageDropdown__autoresizeCheckboxHelpText); helpDisplay?.registerTextHelpForElement(aboutButton, this.localizationTable.pageDropdown__aboutButtonHelpText); this.updateDropdownContent = () => { setBgColorInputValue(this.getBackgroundColor()); const autoresize = this.editor.image.getAutoresizeEnabled(); const importExportRect = this.editor.getImportExportRect(); imageWidthRow.setValue(importExportRect.width); imageHeightRow.setValue(importExportRect.height); autoresizeCheckbox.checked = autoresize; imageWidthRow.setIsAutomaticSize(autoresize); imageHeightRow.setIsAutomaticSize(autoresize); useGridCheckbox.checked = this.getBackgroundType() === BackgroundComponent_1.BackgroundType.Grid; }; this.updateDropdownContent(); container.replaceChildren(backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element, auroresizeRow, aboutButton); dropdown.replaceChildren(container); return true; } } DocumentPropertiesWidget.idCounter = 0; exports.default = DocumentPropertiesWidget;