canvico-editor 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +31 -8
  3. package/dist/CanvicoEditor.d.ts +45 -24
  4. package/dist/CanvicoEditor.d.ts.map +1 -1
  5. package/dist/CanvicoEditor.js +262 -87
  6. package/dist/CanvicoEditor.js.map +1 -1
  7. package/dist/modules/BaseModule.d.ts +5 -4
  8. package/dist/modules/BaseModule.d.ts.map +1 -1
  9. package/dist/modules/BaseModule.js +7 -6
  10. package/dist/modules/BaseModule.js.map +1 -1
  11. package/dist/modules/CropModule.d.ts +32 -31
  12. package/dist/modules/CropModule.d.ts.map +1 -1
  13. package/dist/modules/CropModule.js +147 -106
  14. package/dist/modules/CropModule.js.map +1 -1
  15. package/dist/modules/ResizeModule.d.ts +12 -26
  16. package/dist/modules/ResizeModule.d.ts.map +1 -1
  17. package/dist/modules/ResizeModule.js +27 -35
  18. package/dist/modules/ResizeModule.js.map +1 -1
  19. package/dist/modules/TransformModule.d.ts +38 -0
  20. package/dist/modules/TransformModule.d.ts.map +1 -0
  21. package/dist/modules/TransformModule.js +55 -0
  22. package/dist/modules/TransformModule.js.map +1 -0
  23. package/dist/state/CanvasState.d.ts +34 -0
  24. package/dist/state/CanvasState.d.ts.map +1 -0
  25. package/dist/state/CanvasState.js +67 -0
  26. package/dist/state/CanvasState.js.map +1 -0
  27. package/dist/state/EditorReducer.d.ts +60 -0
  28. package/dist/state/EditorReducer.d.ts.map +1 -0
  29. package/dist/state/EditorReducer.js +127 -0
  30. package/dist/state/EditorReducer.js.map +1 -0
  31. package/dist/types.d.ts +33 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/utils/dom-manager.d.ts +12 -1
  34. package/dist/utils/dom-manager.d.ts.map +1 -1
  35. package/dist/utils/dom-manager.js +22 -14
  36. package/dist/utils/dom-manager.js.map +1 -1
  37. package/dist/utils/error-handler.d.ts +18 -7
  38. package/dist/utils/error-handler.d.ts.map +1 -1
  39. package/dist/utils/error-handler.js +34 -7
  40. package/dist/utils/error-handler.js.map +1 -1
  41. package/dist/utils/validation.d.ts +0 -7
  42. package/dist/utils/validation.d.ts.map +1 -1
  43. package/dist/utils/validation.js +33 -29
  44. package/dist/utils/validation.js.map +1 -1
  45. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"CanvicoEditor.js","sources":["../src/CanvicoEditor.ts"],"sourcesContent":["import type { CanvicoEditorConfig, IModule } from \"./types.js\";\r\nimport { ErrorHandler } from \"./utils/error-handler.js\";\r\nimport { DOMManager } from \"./utils/dom-manager.js\";\r\nimport { validateFile, createCanvasContextError, createImageLoadError, createImageSaveError, createFeatureNotSupportedError } from \"./utils/validation.js\";\r\n\r\n// Modules\r\nimport { ResizeModule } from \"./modules/ResizeModule.js\";\r\nimport { CropModule } from \"./modules/CropModule.js\";\r\n\r\nenum ModuleName {\r\n RESIZE = \"resize\",\r\n CROP = \"crop\",\r\n}\r\n\r\nexport class CanvicoEditor {\r\n /** Manages all DOM element interactions and selections. */\r\n private dom: DOMManager;\r\n\r\n private canvas: HTMLCanvasElement;\r\n private ctx: CanvasRenderingContext2D;\r\n\r\n /** The original, unmodified image loaded by the user. */\r\n private initialImage?: HTMLImageElement;\r\n /** The image currently being displayed and edited, including all modifications. */\r\n private currentImage?: HTMLImageElement;\r\n\r\n /** A map holding all registered and initialized modules. */\r\n private modules: Map<string, IModule> = new Map();\r\n\r\n /** Handles and logs errors that occur within the editor. */\r\n private errorHandler: ErrorHandler;\r\n\r\n /** The configuration options passed to the editor upon instantiation. */\r\n private config: CanvicoEditorConfig;\r\n\r\n private DEFAULT_MODULE: ModuleName | null = null;\r\n private activeModuleName: ModuleName | null = null;\r\n\r\n /**\r\n * Creates an instance of CanvasImageEditor.\r\n * @param config - The configuration object for the editor.\r\n * @throws {Error} If a required feature like FileReader is not supported by the browser.\r\n */\r\n constructor(config: CanvicoEditorConfig) {\r\n // Assign properties first, so they are available in case of an early error\r\n this.config = config;\r\n this.errorHandler = new ErrorHandler();\r\n\r\n if (!window.FileReader) {\r\n throw createFeatureNotSupportedError(\"FileReader API is not supported by this browser.\");\r\n }\r\n\r\n // All initializations\r\n this.dom = new DOMManager(config, this.errorHandler); // Initialize and validate DOM elements\r\n [this.canvas, this.ctx] = this._initializeCanvas(); // Create canvas and get context 2d\r\n this._registerModules();\r\n this._bindGlobalEvents();\r\n }\r\n\r\n /**\r\n * Creates the canvas element and its 2D rendering context.\r\n * @returns A tuple containing the canvas and its context.\r\n * @throws {Error} If the 2D context cannot be created.\r\n */\r\n private _initializeCanvas(): [HTMLCanvasElement, CanvasRenderingContext2D] {\r\n const canvas = document.createElement(\"canvas\");\r\n const ctx = canvas.getContext(\"2d\");\r\n\r\n if (!ctx) {\r\n throw createCanvasContextError();\r\n }\r\n this.dom.elements.container.appendChild(canvas);\r\n\r\n return [canvas, ctx];\r\n }\r\n\r\n // --- Public API & User Actions ---\r\n\r\n /**\r\n * Cleans up all resources, event listeners, and modules to safely remove the editor instance.\r\n */\r\n public destroy(): void {\r\n // Deactivate any active module\r\n this._setActiveModule(null);\r\n\r\n // A simple way to remove all listeners is to replace the nodes.\r\n this.dom.elements.imageFileInput.replaceWith(this.dom.elements.imageFileInput.cloneNode(true));\r\n this.dom.elements.clearCanvasButton.replaceWith(this.dom.elements.clearCanvasButton.cloneNode(true));\r\n this.dom.elements.saveButton.replaceWith(this.dom.elements.saveButton.cloneNode(true));\r\n\r\n this.modules.forEach((module) => module.destroy());\r\n this.modules.clear();\r\n }\r\n\r\n /**\r\n * Resets the current image to its original state, discarding all changes.\r\n */\r\n private _resetImage(): void {\r\n if (this.initialImage) {\r\n this.currentImage = this.initialImage;\r\n this._resetCanvasView(this.initialImage);\r\n this._setActiveModule(this.DEFAULT_MODULE);\r\n }\r\n }\r\n\r\n /**\r\n * Clears the canvas and resets the entire editor state, including loaded images and module states.\r\n */\r\n private _cleanAll(): void {\r\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\r\n this.canvas.width = 0;\r\n this.canvas.height = 0;\r\n\r\n this.initialImage = undefined;\r\n this.currentImage = undefined;\r\n\r\n // Reset input fields\r\n if (this.dom.resizeElements) {\r\n this.dom.resizeElements.widthInput.value = \"\";\r\n this.dom.resizeElements.heightInput.value = \"\";\r\n if (this.dom.resizeElements.lockAspectRatio) this.dom.resizeElements.lockAspectRatio.checked = false;\r\n }\r\n\r\n this.dom.elements.imageFileInput.value = \"\";\r\n\r\n this._setActiveModule(this.DEFAULT_MODULE);\r\n }\r\n\r\n /**\r\n * Triggers a download of the current canvas content as a PNG image.\r\n */\r\n private _saveImage(): void {\r\n if (!this.currentImage) {\r\n this.errorHandler.handle(createImageSaveError());\r\n return;\r\n }\r\n const link = document.createElement(\"a\");\r\n const originalFile = this.dom.elements.imageFileInput.files?.[0];\r\n const baseName = originalFile ? originalFile.name.replace(/\\.[^/.]+$/, \"\") : \"image\";\r\n link.download = `${baseName}-edited.png`;\r\n link.href = this.canvas.toDataURL(\"image/png\");\r\n link.click();\r\n }\r\n\r\n // --- Initialization Methods ---\r\n\r\n /**\r\n * Binds event listeners to the main control elements like file input, save, and reset buttons.\r\n */\r\n private _bindGlobalEvents(): void {\r\n this.dom.elements.imageFileInput.addEventListener(\"change\", (e: Event) => this._loadImage(e));\r\n\r\n // Bind module buttons\r\n if (this.dom.cropElements) {\r\n this.dom.cropElements.activateButton.addEventListener(\"click\", () => this.currentImage && this._setActiveModule(ModuleName.CROP));\r\n }\r\n\r\n this.dom.elements.resetEditsButton.addEventListener(\"click\", () => this._resetImage());\r\n this.dom.elements.clearCanvasButton.addEventListener(\"click\", () => this._cleanAll());\r\n this.dom.elements.saveButton.addEventListener(\"click\", () => this._saveImage());\r\n }\r\n\r\n // --- Core Drawing & State Logic ---\r\n\r\n /**\r\n * Initializes and registers all available modules based on the provided options.\r\n */\r\n private _registerModules(): void {\r\n // Register ResizeModule only if its essential inputs are provided\r\n if (this.dom.resizeElements) {\r\n const resizeModule = new ResizeModule(this.dom.resizeElements, {\r\n container: this.dom.elements.container,\r\n canvas: this.canvas,\r\n ctx: this.ctx,\r\n\r\n getCurrentImage: () => this.currentImage,\r\n });\r\n this.modules.set(ModuleName.RESIZE, resizeModule);\r\n this.DEFAULT_MODULE = ModuleName.RESIZE;\r\n }\r\n\r\n // Register CropModule only if its essential button is provided\r\n if (this.dom.cropElements && this.config.modules?.crop) {\r\n const cropModule = new CropModule(this.dom.cropElements, {\r\n canvas: this.canvas,\r\n ctx: this.ctx,\r\n frameColor: this.config.modules.crop.frameColor,\r\n outsideOverlayColor: this.config.modules.crop.outsideOverlayColor,\r\n requestRedraw: () => this._redraw(),\r\n getCurrentImage: () => this.currentImage,\r\n onCropApplied: (newImageDataUrl: string) => this._handleCropApplied(newImageDataUrl),\r\n });\r\n\r\n this.modules.set(ModuleName.CROP, cropModule);\r\n }\r\n\r\n // Initialize all registered modules\r\n this.modules.forEach((module) => module.init());\r\n console.log(this.modules);\r\n\r\n // Set the Resize module as active by default\r\n this._setActiveModule(this.DEFAULT_MODULE);\r\n }\r\n\r\n /**\r\n * Resets the canvas to display the given image, scaled to fit the container.\r\n * @param {HTMLImageElement} image - The image to display.\r\n * @internal\r\n */\r\n private _resetCanvasView(image: HTMLImageElement): void {\r\n this.currentImage = image;\r\n\r\n const containerWidth = this.dom.elements.container.clientWidth;\r\n const containerHeight = this.dom.elements.container.clientHeight;\r\n const scale = Math.min(containerWidth / image.width, containerHeight / image.height);\r\n\r\n const drawW = image.width * scale;\r\n const drawH = image.height * scale;\r\n\r\n this.canvas.width = drawW;\r\n this.canvas.height = drawH;\r\n\r\n this._redraw();\r\n\r\n if (this.dom.resizeElements) {\r\n this.dom.resizeElements.widthInput.value = Math.round(drawW).toString();\r\n this.dom.resizeElements.heightInput.value = Math.round(drawH).toString();\r\n }\r\n }\r\n\r\n /**\r\n * Sets which module is currently active, deactivating all others.\r\n * If the same module is activated again (and it's not the default), it toggles it off,\r\n * returning to the default module.\r\n * @param {ModuleName | null} moduleName - The name of the module to activate, or null to deactivate all.\r\n * @internal\r\n */\r\n private _setActiveModule(moduleName: ModuleName | null): void {\r\n let newActiveModuleName = moduleName;\r\n\r\n // If we try to activate a module that is already active (and it's not the default module),\r\n // we switch back to the default module. This creates a 'toggle' effect.\r\n if (this.activeModuleName === moduleName && moduleName !== this.DEFAULT_MODULE) {\r\n newActiveModuleName = this.DEFAULT_MODULE;\r\n }\r\n\r\n // If the state doesn't change, do nothing.\r\n if (this.activeModuleName === newActiveModuleName) {\r\n return;\r\n }\r\n\r\n this.activeModuleName = newActiveModuleName;\r\n\r\n this.modules.forEach((module, name) => {\r\n if (name === this.activeModuleName) {\r\n module.activate();\r\n } else {\r\n module.deactivate();\r\n }\r\n });\r\n this._redraw();\r\n }\r\n\r\n // --- Event Handlers & Callbacks ---\r\n\r\n /**\r\n * Handles the file input change event to load, validate, and display an image.\r\n * @param {Event} event - The file input change event.\r\n * @internal\r\n */\r\n private _loadImage(event: Event): void {\r\n // If no file input element is available, do nothing\r\n try {\r\n const target = event.target as HTMLInputElement;\r\n const file = target.files?.[0];\r\n if (!file) {\r\n return;\r\n }\r\n\r\n validateFile(file, this.config.maxFileSizeMB || 5);\r\n\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n const img = new Image();\r\n img.onload = () => {\r\n try {\r\n this.currentImage = img;\r\n this.initialImage = img;\r\n\r\n this._resetCanvasView(img);\r\n } catch (error) {\r\n this.errorHandler.handle(error as Error);\r\n }\r\n };\r\n img.onerror = () => {\r\n this.errorHandler.handle(createImageLoadError(\"Error reading image file.\"));\r\n };\r\n img.src = e.target!.result as string;\r\n };\r\n reader.onerror = () => {\r\n this.errorHandler.handle(createImageLoadError(\"Error reading file with FileReader.\"));\r\n };\r\n reader.readAsDataURL(file);\r\n } catch (error) {\r\n this.errorHandler.handle(error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * Callback for the CropModule after a crop is applied. Updates the current image with the cropped version.\r\n * @param {string} newImageDataUrl - The data URL of the newly cropped image.\r\n * @internal\r\n */\r\n private _handleCropApplied(newImageDataUrl: string): void {\r\n const newImg = new Image();\r\n newImg.onload = () => {\r\n this.currentImage = newImg;\r\n\r\n this._setActiveModule(this.DEFAULT_MODULE);\r\n\r\n this._resetCanvasView(newImg);\r\n };\r\n newImg.src = newImageDataUrl;\r\n }\r\n\r\n // --- Module Management ---\r\n\r\n /**\r\n * Clears the canvas, redraws the base image, and then draws the overlay for the currently active module.\r\n * @internal\r\n */\r\n private _redraw(): void {\r\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\r\n\r\n if (!this.currentImage) {\r\n return;\r\n }\r\n this.ctx.drawImage(this.currentImage, 0, 0, this.currentImage.width, this.currentImage.height, 0, 0, this.canvas.width, this.canvas.height);\r\n\r\n if (this.activeModuleName) {\r\n const activeModule = this.modules.get(this.activeModuleName);\r\n // The 'drawOverlay' method is optional, so we check for its existence before calling.\r\n activeModule?.drawOverlay?.();\r\n }\r\n }\r\n}\r\n"],"names":["CanvicoEditor","config","ErrorHandler","createFeatureNotSupportedError","DOMManager","canvas","ctx","createCanvasContextError","module","createImageSaveError","link","originalFile","baseName","resizeModule","ResizeModule","cropModule","CropModule","newImageDataUrl","image","containerWidth","containerHeight","scale","drawW","drawH","moduleName","newActiveModuleName","name","event","file","validateFile","reader","e","img","error","createImageLoadError","newImg"],"mappings":"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"}
1
+ {"version":3,"file":"CanvicoEditor.js","sources":["../src/CanvicoEditor.ts"],"sourcesContent":["import type { CanvicoEditorConfig, IModule } from \"./types.js\";\r\nimport { ErrorHandler } from \"./utils/error-handler.js\";\r\nimport { DOMManager } from \"./utils/dom-manager.js\";\r\nimport { validateFile, createCanvasContextError, createImageLoadError, createImageSaveError, createFeatureNotSupportedError } from \"./utils/validation.js\";\r\nimport { CanvasState, type CanvasStateChange, type CropRect } from \"./state/CanvasState.js\";\r\n\r\n// Modules\r\nimport { ResizeModule } from \"./modules/ResizeModule.js\";\r\nimport { CropModule } from \"./modules/CropModule.js\";\r\nimport { TransformModule } from \"./modules/TransformModule.js\";\r\n\r\nenum ModuleName {\r\n RESIZE = \"resize\",\r\n CROP = \"crop\",\r\n TRANSFORM = \"transform\",\r\n}\r\n\r\nexport class CanvicoEditor {\r\n /** Manages all DOM element interactions and selections. */\r\n private readonly dom: DOMManager;\r\n\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly ctx: CanvasRenderingContext2D;\r\n\r\n /** Central shared document state. */\r\n private readonly state: CanvasState = new CanvasState();\r\n\r\n /** A map holding all registered and initialized modules. */\r\n private readonly modules: Map<string, IModule> = new Map();\r\n private resizeModule?: ResizeModule;\r\n private cropModule?: CropModule;\r\n private transformModule?: TransformModule;\r\n\r\n /** Handles and logs errors that occur within the editor. */\r\n private readonly errorHandler: ErrorHandler;\r\n\r\n /** The configuration options passed to the editor upon instantiation. */\r\n private readonly config: CanvicoEditorConfig;\r\n\r\n private readonly DEFAULT_MODULE: ModuleName | null = null;\r\n private activeModuleName: ModuleName | null = null;\r\n private renderScheduled = false;\r\n private cleanupCallbacks: Array<() => void> = [];\r\n private isDestroyed = false;\r\n private asyncOperationVersion = 0;\r\n\r\n /**\r\n * Creates an instance of CanvasImageEditor.\r\n * @param config - The configuration object for the editor.\r\n * @throws {Error} If a required feature like FileReader is not supported by the browser.\r\n */\r\n constructor(config: CanvicoEditorConfig) {\r\n this.config = config;\r\n this.errorHandler = new ErrorHandler({\r\n onError: config.onError,\r\n logToConsole: config.logErrorsToConsole,\r\n });\r\n\r\n if (!globalThis.FileReader) {\r\n const error = createFeatureNotSupportedError(\"FileReader API is not supported by this browser.\");\r\n this.errorHandler.handle(error, { source: \"validation\", operation: \"constructor:file-reader-check\" });\r\n throw error;\r\n }\r\n\r\n this.dom = new DOMManager(config, this.errorHandler);\r\n [this.canvas, this.ctx] = this._initializeCanvas();\r\n this._registerModules();\r\n this._bindGlobalEvents();\r\n\r\n const unsubscribe = this.state.subscribe((change) => this._onStateChange(change));\r\n this.cleanupCallbacks.push(unsubscribe);\r\n }\r\n\r\n /**\r\n * Creates the canvas element and its 2D rendering context.\r\n * @returns A tuple containing the canvas and its context.\r\n * @throws {Error} If the 2D context cannot be created.\r\n */\r\n private _initializeCanvas(): [HTMLCanvasElement, CanvasRenderingContext2D] {\r\n const canvas = document.createElement(\"canvas\");\r\n const ctx = canvas.getContext(\"2d\");\r\n\r\n if (!ctx) {\r\n throw createCanvasContextError();\r\n }\r\n this.dom.elements.container.appendChild(canvas);\r\n\r\n return [canvas, ctx];\r\n }\r\n\r\n // --- Public API & User Actions ---\r\n\r\n /**\r\n * Cleans up all resources, event listeners, and modules to safely remove the editor instance.\r\n */\r\n public destroy(): void {\r\n if (this.isDestroyed) {\r\n return;\r\n }\r\n this.isDestroyed = true;\r\n this._cancelPendingAsyncOperations();\r\n\r\n this._setActiveModule(null, false, true);\r\n\r\n this.cleanupCallbacks.forEach((cleanup) => cleanup());\r\n this.cleanupCallbacks = [];\r\n\r\n this.modules.forEach((module) => module.destroy());\r\n this.modules.clear();\r\n }\r\n\r\n /**\r\n * Resets the current image to its original state, discarding all changes.\r\n */\r\n private _resetImage(): void {\r\n if (!this.state.getInitial()) {\r\n return;\r\n }\r\n this.state.resetToInitial();\r\n this._setActiveModule(this.DEFAULT_MODULE, false, true);\r\n }\r\n\r\n /**\r\n * Clears the canvas and resets the entire editor state, including loaded images and module states.\r\n */\r\n private _cleanAll(): void {\r\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\r\n this.canvas.width = 0;\r\n this.canvas.height = 0;\r\n\r\n this.state.clear();\r\n\r\n if (this.dom.resizeElements) {\r\n this.dom.resizeElements.widthInput.value = \"\";\r\n this.dom.resizeElements.heightInput.value = \"\";\r\n if (this.dom.resizeElements.lockAspectRatio) {\r\n this.dom.resizeElements.lockAspectRatio.checked = false;\r\n }\r\n }\r\n\r\n this.dom.elements.imageFileInput.value = \"\";\r\n this._setActiveModule(null, false, true);\r\n }\r\n\r\n /**\r\n * Triggers a download of the current canvas content as a PNG image.\r\n */\r\n private _saveImage(): void {\r\n if (!this.state.getCurrent()) {\r\n this.errorHandler.handle(createImageSaveError(), { source: \"editor\", operation: \"save-image:no-image\" });\r\n return;\r\n }\r\n\r\n const exportDataUrl = this._exportCurrentViewDataUrl();\r\n if (!exportDataUrl) {\r\n this.errorHandler.handle(createImageSaveError(), { source: \"editor\", operation: \"save-image:export-failed\" });\r\n return;\r\n }\r\n\r\n const link = document.createElement(\"a\");\r\n const originalFile = this.dom.elements.imageFileInput.files?.[0];\r\n const baseName = originalFile ? originalFile.name.replace(/\\.[^/.]+$/, \"\") : \"image\";\r\n link.download = `${baseName}-edited.png`;\r\n link.href = exportDataUrl;\r\n link.click();\r\n }\r\n\r\n // --- Initialization Methods ---\r\n\r\n /**\r\n * Binds event listeners to the main control elements like file input, save, and reset buttons.\r\n */\r\n private _bindGlobalEvents(): void {\r\n this._addManagedListener(this.dom.elements.imageFileInput, \"change\", (e: Event) => this._loadImage(e));\r\n this._addManagedListener(this.dom.elements.resetEditsButton, \"click\", () => this._resetImage());\r\n this._addManagedListener(this.dom.elements.clearCanvasButton, \"click\", () => this._cleanAll());\r\n this._addManagedListener(this.dom.elements.saveButton, \"click\", () => this._saveImage());\r\n }\r\n\r\n // --- Core Drawing & State Logic ---\r\n\r\n /**\r\n * Initializes and registers all available modules based on the provided options.\r\n */\r\n private _registerModules(): void {\r\n if (this.dom.resizeElements) {\r\n const resizeModule = new ResizeModule(this.dom.resizeElements, {\r\n state: this.state,\r\n errorHandler: this.errorHandler,\r\n });\r\n this.modules.set(ModuleName.RESIZE, resizeModule);\r\n this.resizeModule = resizeModule;\r\n }\r\n\r\n if (this.dom.cropElements && this.config.modules?.crop) {\r\n const cropModule = new CropModule(this.dom.cropElements, {\r\n canvas: this.canvas,\r\n ctx: this.ctx,\r\n frameColor: this.config.modules.crop.frameColor,\r\n outsideOverlayColor: this.config.modules.crop.outsideOverlayColor,\r\n state: this.state,\r\n onCropApplied: () => this._handleCropApplied(),\r\n });\r\n\r\n this.modules.set(ModuleName.CROP, cropModule);\r\n this.cropModule = cropModule;\r\n\r\n this._addManagedListener(this.dom.cropElements.activateButton, \"click\", () => this._enterCropMode());\r\n }\r\n\r\n if (this.dom.transformElements) {\r\n const transformModule = new TransformModule(this.dom.transformElements, {\r\n state: this.state,\r\n });\r\n this.modules.set(ModuleName.TRANSFORM, transformModule);\r\n this.transformModule = transformModule;\r\n }\r\n\r\n this.modules.forEach((module) => module.init());\r\n this._setActiveModule(this.DEFAULT_MODULE, false, true);\r\n }\r\n\r\n private _addManagedListener(target: EventTarget, type: string, handler: EventListenerOrEventListenerObject): void {\r\n target.addEventListener(type, handler);\r\n this.cleanupCallbacks.push(() => target.removeEventListener(type, handler));\r\n }\r\n\r\n private _getOutputDimensions(img: HTMLImageElement): { width: number; height: number } {\r\n const resizeState = this.state.getResizeState();\r\n const width = resizeState.width > 0 ? resizeState.width : img.width;\r\n const height = resizeState.height > 0 ? resizeState.height : img.height;\r\n\r\n return {\r\n width: Math.max(1, Math.round(width)),\r\n height: Math.max(1, Math.round(height)),\r\n };\r\n }\r\n\r\n private _getPreviewDimensions(outputWidth: number, outputHeight: number): { width: number; height: number } {\r\n const containerWidth = Math.max(this.dom.elements.container.clientWidth, 1);\r\n const containerHeight = Math.max(this.dom.elements.container.clientHeight, 1);\r\n const scale = Math.min(containerWidth / outputWidth, containerHeight / outputHeight, 1);\r\n\r\n return {\r\n width: Math.max(1, Math.round(outputWidth * scale)),\r\n height: Math.max(1, Math.round(outputHeight * scale)),\r\n };\r\n }\r\n\r\n /**\r\n * Resets the canvas to display the given image, scaled to fit the container.\r\n * This should ONLY be called when loading a new image or explicitly resetting the view.\r\n */\r\n private _resetCanvasView(image: HTMLImageElement): void {\r\n const { width: outputWidth, height: outputHeight } = this._getOutputDimensions(image);\r\n const { width: previewWidth, height: previewHeight } = this._getPreviewDimensions(outputWidth, outputHeight);\r\n\r\n this.canvas.width = previewWidth;\r\n this.canvas.height = previewHeight;\r\n\r\n if (this.dom.resizeElements) {\r\n this.dom.resizeElements.widthInput.value = outputWidth.toString();\r\n this.dom.resizeElements.heightInput.value = outputHeight.toString();\r\n }\r\n\r\n this._requestRender();\r\n }\r\n\r\n /**\r\n * Sets which module is currently active.\r\n * Crop mode disables interactions with the remaining modules until exited or applied.\r\n */\r\n private _setActiveModule(moduleName: ModuleName | null, shouldToggle: boolean = true, force: boolean = false): void {\r\n let newActiveModuleName = moduleName;\r\n\r\n if (shouldToggle && this.activeModuleName === moduleName && moduleName !== this.DEFAULT_MODULE) {\r\n newActiveModuleName = this.DEFAULT_MODULE;\r\n }\r\n\r\n if (!force && this.activeModuleName === newActiveModuleName) {\r\n return;\r\n }\r\n\r\n this.activeModuleName = newActiveModuleName;\r\n const hasImage = Boolean(this.state.getCurrent());\r\n\r\n if (this.activeModuleName === ModuleName.CROP && hasImage) {\r\n this.state.setMode(\"crop\");\r\n this.resizeModule?.deactivate();\r\n this.transformModule?.deactivate();\r\n this.cropModule?.activate();\r\n this._toggleNonCropInteractions(false);\r\n } else {\r\n this.state.setMode(\"edit\");\r\n this.cropModule?.deactivate();\r\n this._toggleNonCropInteractions(true);\r\n\r\n if (hasImage) {\r\n this.resizeModule?.activate();\r\n this.transformModule?.activate();\r\n } else {\r\n this.resizeModule?.deactivate();\r\n this.transformModule?.deactivate();\r\n }\r\n }\r\n\r\n this._requestRender();\r\n }\r\n\r\n /**\r\n * Disables or enables UI elements for Resize and Transform modules.\r\n */\r\n private _toggleNonCropInteractions(enable: boolean): void {\r\n const disabled = !enable;\r\n\r\n if (this.dom.resizeElements) {\r\n this.dom.resizeElements.widthInput.disabled = disabled;\r\n this.dom.resizeElements.heightInput.disabled = disabled;\r\n if (this.dom.resizeElements.lockAspectRatio) {\r\n this.dom.resizeElements.lockAspectRatio.disabled = disabled;\r\n }\r\n }\r\n\r\n if (this.dom.transformElements) {\r\n const t = this.dom.transformElements;\r\n if (t.rotateInput) t.rotateInput.disabled = disabled;\r\n if (t.flipHorizontalButton) t.flipHorizontalButton.disabled = disabled;\r\n if (t.flipVerticalButton) t.flipVerticalButton.disabled = disabled;\r\n }\r\n\r\n this.dom.elements.clearCanvasButton.disabled = disabled;\r\n this.dom.elements.resetEditsButton.disabled = disabled;\r\n }\r\n\r\n // --- Event Handlers & Callbacks ---\r\n\r\n /**\r\n * Handles the file input change event to load, validate, and display an image.\r\n */\r\n private _loadImage(event: Event): void {\r\n if (this.isDestroyed) {\r\n return;\r\n }\r\n\r\n try {\r\n const target = event.target as HTMLInputElement;\r\n const file = target.files?.[0];\r\n if (!file) {\r\n return;\r\n }\r\n\r\n validateFile(file, this.config.maxFileSizeMB || 5);\r\n\r\n const operationVersion = this._beginAsyncOperation();\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n return;\r\n }\r\n\r\n const result = e.target?.result;\r\n if (typeof result !== \"string\") {\r\n this.errorHandler.handle(createImageLoadError(\"Unexpected image format from FileReader.\"), {\r\n source: \"editor\",\r\n operation: \"load-image:reader-result\",\r\n });\r\n return;\r\n }\r\n\r\n const img = new Image();\r\n img.onload = () => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n return;\r\n }\r\n\r\n try {\r\n this.state.setInitial(img);\r\n this._setActiveModule(this.DEFAULT_MODULE, false, true);\r\n } catch (error) {\r\n this.errorHandler.handle(error, { source: \"state\", operation: \"load-image:set-initial\" });\r\n }\r\n };\r\n img.onerror = () => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n return;\r\n }\r\n this.errorHandler.handle(createImageLoadError(\"Error reading image file.\"), { source: \"editor\", operation: \"load-image:image-read\" });\r\n };\r\n img.src = result;\r\n };\r\n reader.onerror = () => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n return;\r\n }\r\n this.errorHandler.handle(createImageLoadError(\"Error reading file with FileReader.\"), { source: \"editor\", operation: \"load-image:file-reader\" });\r\n };\r\n reader.readAsDataURL(file);\r\n } catch (error) {\r\n this.errorHandler.handle(error, { source: \"validation\", operation: \"load-image:validate-file\" });\r\n }\r\n }\r\n\r\n /**\r\n * Callback for the CropModule after a crop is applied.\r\n */\r\n private _handleCropApplied(): void {\r\n const dataUrl = this._bakeCrop();\r\n if (dataUrl) {\r\n void this._applyCropAndExit(dataUrl);\r\n }\r\n }\r\n\r\n private async _applyCropAndExit(newImageDataUrl: string): Promise<void> {\r\n try {\r\n await this._applyImageDataUrl(newImageDataUrl);\r\n } catch (error) {\r\n if (!this._isAbortError(error)) {\r\n this.errorHandler.handle(error, { source: \"editor\", operation: \"apply-crop:apply-image\" });\r\n }\r\n return;\r\n }\r\n\r\n if (this.isDestroyed) {\r\n return;\r\n }\r\n\r\n this._setActiveModule(this.DEFAULT_MODULE, false, true);\r\n const croppedImage = this.state.getCurrent();\r\n if (croppedImage) {\r\n const { lockAspectRatio } = this.state.getResizeState();\r\n this.state.setResizeState({\r\n width: croppedImage.width,\r\n height: croppedImage.height,\r\n lockAspectRatio,\r\n });\r\n }\r\n this.state.setTransformState({ rotate: 0, flipH: false, flipV: false });\r\n this.transformModule?.activate();\r\n }\r\n\r\n private _applyImageDataUrl(dataUrl: string): Promise<void> {\r\n const operationVersion = this._beginAsyncOperation();\r\n\r\n return new Promise((resolve, reject) => {\r\n const newImg = new Image();\r\n newImg.onload = () => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n reject(this._createAbortError(\"Image apply operation was canceled.\"));\r\n return;\r\n }\r\n this.state.setCurrent(newImg);\r\n resolve();\r\n };\r\n newImg.onerror = () => {\r\n if (!this._isAsyncOperationActive(operationVersion)) {\r\n reject(this._createAbortError(\"Image apply operation was canceled.\"));\r\n return;\r\n }\r\n reject(createImageLoadError(\"Error applying image data to canvas.\"));\r\n };\r\n newImg.src = dataUrl;\r\n });\r\n }\r\n\r\n private _onStateChange(change: CanvasStateChange): void {\r\n if (change === \"clear\") {\r\n this.transformModule?.syncControlsWithState();\r\n this._requestRender();\r\n return;\r\n }\r\n\r\n const img = this.state.getCurrent();\r\n if (!img) {\r\n this.transformModule?.syncControlsWithState();\r\n this._requestRender();\r\n return;\r\n }\r\n\r\n if (change === \"image\" || change === \"resize\") {\r\n if (change === \"image\") {\r\n this.transformModule?.syncControlsWithState();\r\n }\r\n this._resetCanvasView(img);\r\n return;\r\n }\r\n\r\n this._requestRender();\r\n }\r\n\r\n private _enterCropMode(): void {\r\n if (!this.state.getCurrent()) {\r\n return;\r\n }\r\n this._setActiveModule(ModuleName.CROP);\r\n }\r\n\r\n // --- Module Management ---\r\n\r\n private _requestRender(): void {\r\n if (this.isDestroyed) {\r\n return;\r\n }\r\n\r\n if (this.renderScheduled) {\r\n return;\r\n }\r\n\r\n this.renderScheduled = true;\r\n globalThis.requestAnimationFrame(() => {\r\n this.renderScheduled = false;\r\n if (this.isDestroyed) {\r\n return;\r\n }\r\n this._redraw();\r\n });\r\n }\r\n\r\n /**\r\n * Clears the canvas, redraws the base image, and then draws the overlay for the currently active module.\r\n */\r\n private _redraw(): void {\r\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\r\n\r\n const img = this.state.getCurrent();\r\n if (!img) {\r\n return;\r\n }\r\n\r\n this._drawImageLayer(this.ctx, img, this.canvas.width, this.canvas.height);\r\n\r\n if (this.activeModuleName === ModuleName.CROP) {\r\n this.cropModule?.drawOverlay?.();\r\n }\r\n }\r\n\r\n private _drawImageLayer(targetCtx: CanvasRenderingContext2D, img: HTMLImageElement, targetWidth: number, targetHeight: number): void {\r\n const transformState = this.state.getTransformState();\r\n const cx = targetWidth / 2;\r\n const cy = targetHeight / 2;\r\n\r\n targetCtx.save();\r\n targetCtx.translate(cx, cy);\r\n targetCtx.rotate((transformState.rotate * Math.PI) / 180);\r\n targetCtx.scale(transformState.flipH ? -1 : 1, transformState.flipV ? -1 : 1);\r\n targetCtx.drawImage(img, -cx, -cy, targetWidth, targetHeight);\r\n targetCtx.restore();\r\n }\r\n\r\n private _exportCurrentViewDataUrl(): string | null {\r\n const img = this.state.getCurrent();\r\n if (!img) return null;\r\n const { width: outputWidth, height: outputHeight } = this._getOutputDimensions(img);\r\n\r\n const offscreen = document.createElement(\"canvas\");\r\n offscreen.width = outputWidth;\r\n offscreen.height = outputHeight;\r\n const offscreenCtx = offscreen.getContext(\"2d\");\r\n if (!offscreenCtx) return null;\r\n\r\n this._drawImageLayer(offscreenCtx, img, offscreen.width, offscreen.height);\r\n return offscreen.toDataURL(\"image/png\");\r\n }\r\n\r\n /**\r\n * Creates a new image based on the current crop state.\r\n * Crop rectangle is defined in preview space and remapped to output space.\r\n */\r\n private _bakeCrop(): string | null {\r\n const img = this.state.getCurrent();\r\n const cropState = this.state.getCropState();\r\n if (!img || !cropState.active) return null;\r\n\r\n const rect = this._normalizeRect(cropState.rect);\r\n const previewWidth = this.canvas.width;\r\n const previewHeight = this.canvas.height;\r\n if (previewWidth <= 0 || previewHeight <= 0) return null;\r\n\r\n const { width: outputWidth, height: outputHeight } = this._getOutputDimensions(img);\r\n const scaleX = outputWidth / previewWidth;\r\n const scaleY = outputHeight / previewHeight;\r\n\r\n const sx = Math.max(0, Math.floor(rect.x * scaleX));\r\n const sy = Math.max(0, Math.floor(rect.y * scaleY));\r\n const maxW = outputWidth - sx;\r\n const maxH = outputHeight - sy;\r\n const sw = Math.min(Math.floor(rect.w * scaleX), Math.floor(maxW));\r\n const sh = Math.min(Math.floor(rect.h * scaleY), Math.floor(maxH));\r\n\r\n if (sw <= 0 || sh <= 0) return null;\r\n\r\n const transformedCanvas = document.createElement(\"canvas\");\r\n transformedCanvas.width = outputWidth;\r\n transformedCanvas.height = outputHeight;\r\n const transformedCtx = transformedCanvas.getContext(\"2d\");\r\n if (!transformedCtx) return null;\r\n this._drawImageLayer(transformedCtx, img, transformedCanvas.width, transformedCanvas.height);\r\n\r\n const offscreen = document.createElement(\"canvas\");\r\n offscreen.width = sw;\r\n offscreen.height = sh;\r\n const offscreenCtx = offscreen.getContext(\"2d\");\r\n if (!offscreenCtx) return null;\r\n\r\n offscreenCtx.drawImage(transformedCanvas, sx, sy, sw, sh, 0, 0, sw, sh);\r\n return offscreen.toDataURL(\"image/png\");\r\n }\r\n\r\n private _normalizeRect(rect: CropRect): CropRect {\r\n return {\r\n x: rect.w >= 0 ? rect.x : rect.x + rect.w,\r\n y: rect.h >= 0 ? rect.y : rect.y + rect.h,\r\n w: Math.abs(rect.w),\r\n h: Math.abs(rect.h),\r\n };\r\n }\r\n\r\n private _beginAsyncOperation(): number {\r\n this.asyncOperationVersion += 1;\r\n return this.asyncOperationVersion;\r\n }\r\n\r\n private _cancelPendingAsyncOperations(): void {\r\n this.asyncOperationVersion += 1;\r\n }\r\n\r\n private _isAsyncOperationActive(operationVersion: number): boolean {\r\n return !this.isDestroyed && operationVersion === this.asyncOperationVersion;\r\n }\r\n\r\n private _createAbortError(message: string): Error {\r\n const error = new Error(message);\r\n error.name = \"AbortError\";\r\n return error;\r\n }\r\n\r\n private _isAbortError(error: unknown): boolean {\r\n return error instanceof Error && error.name === \"AbortError\";\r\n }\r\n}\r\n"],"names":["CanvicoEditor","CanvasState","config","ErrorHandler","error","createFeatureNotSupportedError","DOMManager","unsubscribe","change","canvas","ctx","createCanvasContextError","cleanup","module","createImageSaveError","exportDataUrl","link","originalFile","baseName","resizeModule","ResizeModule","cropModule","CropModule","transformModule","TransformModule","target","type","handler","img","resizeState","width","height","outputWidth","outputHeight","containerWidth","containerHeight","scale","image","previewWidth","previewHeight","moduleName","shouldToggle","force","newActiveModuleName","hasImage","enable","disabled","t","event","file","validateFile","operationVersion","reader","e","result","createImageLoadError","dataUrl","newImageDataUrl","croppedImage","lockAspectRatio","resolve","reject","newImg","targetCtx","targetWidth","targetHeight","transformState","cx","cy","offscreen","offscreenCtx","cropState","rect","scaleX","scaleY","sx","sy","maxW","maxH","sw","sh","transformedCanvas","transformedCtx","message"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { IModule, ModuleEventHandler } from '../types.js';
1
+ import { IModule } from '../types.js';
2
2
  /**
3
3
  * Base class for all editor modules. It provides a centralized way to manage
4
4
  * event listeners, ensuring they are properly cleaned up when a module is destroyed.
@@ -12,9 +12,10 @@ export declare abstract class BaseModule implements IModule {
12
12
  * allowing for easy removal in the `destroy` method.
13
13
  */
14
14
  protected eventHandlers: Array<{
15
- element: HTMLElement;
15
+ element: EventTarget;
16
16
  type: string;
17
- handler: ModuleEventHandler;
17
+ handler: EventListenerOrEventListenerObject;
18
+ options?: boolean | AddEventListenerOptions;
18
19
  }>;
19
20
  /**
20
21
  * @param name - The unique name for the module.
@@ -51,6 +52,6 @@ export declare abstract class BaseModule implements IModule {
51
52
  * @param type The event name (e.g., "click", "mousedown", "input").
52
53
  * @param handler The event handler function.
53
54
  */
54
- protected addEventListener(el: HTMLElement, type: string, handler: ModuleEventHandler): void;
55
+ protected addEventListener<TEvent extends Event>(el: EventTarget, type: string, handler: (event: TEvent) => void, options?: boolean | AddEventListenerOptions): void;
55
56
  }
56
57
  //# sourceMappingURL=BaseModule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseModule.d.ts","sourceRoot":"","sources":["../../src/modules/BaseModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE/D;;;GAGG;AACH,8BAAsB,UAAW,YAAW,OAAO;IAC/C,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B;;;;OAIG;IACH,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC;QAC3B,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,kBAAkB,CAAC;KAC/B,CAAC,CAAM;IAER;;OAEG;gBACS,IAAI,EAAE,MAAM;IAIxB;;;OAGG;aACa,IAAI,IAAI,IAAI;IAE5B;;;OAGG;aACa,QAAQ,IAAI,IAAI;IAEhC;;;OAGG;aACa,UAAU,IAAI,IAAI;IAElC;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;OAGG;IACI,OAAO,IAAI,IAAI;IAOtB;;;;;OAKG;IACH,SAAS,CAAC,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,IAAI;CAI/F"}
1
+ {"version":3,"file":"BaseModule.d.ts","sourceRoot":"","sources":["../../src/modules/BaseModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,8BAAsB,UAAW,YAAW,OAAO;IAC/C,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B;;;;OAIG;IACH,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC;QAC3B,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,kCAAkC,CAAC;QAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAAC;KAC/C,CAAC,CAAM;IAER;;OAEG;gBACS,IAAI,EAAE,MAAM;IAIxB;;;OAGG;aACa,IAAI,IAAI,IAAI;IAE5B;;;OAGG;aACa,QAAQ,IAAI,IAAI;IAEhC;;;OAGG;aACa,UAAU,IAAI,IAAI;IAElC;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;OAGG;IACI,OAAO,IAAI,IAAI;IAOtB;;;;;OAKG;IACH,SAAS,CAAC,gBAAgB,CAAC,MAAM,SAAS,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI;CAKvK"}
@@ -1,4 +1,4 @@
1
- class r {
1
+ class d {
2
2
  /** The unique name of the module (e.g., 'crop', 'resize'). */
3
3
  name;
4
4
  /**
@@ -25,8 +25,8 @@ class r {
25
25
  * This method iterates over all registered event listeners and removes them.
26
26
  */
27
27
  destroy() {
28
- this.eventHandlers.forEach(({ element: e, type: t, handler: n }) => {
29
- e.removeEventListener(t, n);
28
+ this.eventHandlers.forEach(({ element: e, type: n, handler: s, options: t }) => {
29
+ e.removeEventListener(n, s, t);
30
30
  }), this.eventHandlers = [];
31
31
  }
32
32
  /**
@@ -35,11 +35,12 @@ class r {
35
35
  * @param type The event name (e.g., "click", "mousedown", "input").
36
36
  * @param handler The event handler function.
37
37
  */
38
- addEventListener(e, t, n) {
39
- e.addEventListener(t, n), this.eventHandlers.push({ element: e, type: t, handler: n });
38
+ addEventListener(e, n, s, t) {
39
+ const r = s;
40
+ e.addEventListener(n, r, t), this.eventHandlers.push({ element: e, type: n, handler: r, options: t });
40
41
  }
41
42
  }
42
43
  export {
43
- r as BaseModule
44
+ d as BaseModule
44
45
  };
45
46
  //# sourceMappingURL=BaseModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseModule.js","sources":["../../src/modules/BaseModule.ts"],"sourcesContent":["import type { IModule, ModuleEventHandler } from \"../types.js\";\r\n\r\n/**\r\n * Base class for all editor modules. It provides a centralized way to manage\r\n * event listeners, ensuring they are properly cleaned up when a module is destroyed.\r\n */\r\nexport abstract class BaseModule implements IModule {\r\n /** The unique name of the module (e.g., 'crop', 'resize'). */\r\n private readonly name: string;\r\n /**\r\n * Stores all registered event listeners for this module.\r\n * Each entry contains the element, event type, and the handler function,\r\n * allowing for easy removal in the `destroy` method.\r\n */\r\n protected eventHandlers: Array<{\r\n element: HTMLElement;\r\n type: string;\r\n handler: ModuleEventHandler;\r\n }> = [];\r\n\r\n /**\r\n * @param name - The unique name for the module.\r\n */\r\n constructor(name: string) {\r\n this.name = name;\r\n }\r\n\r\n /**\r\n * Initializes the module. Each module must implement this method\r\n * to register its necessary event listeners using the `addEventListener` helper.\r\n */\r\n public abstract init(): void;\r\n\r\n /**\r\n * Activates the module, enabling its specific functionality.\r\n * Each module must implement this method.\r\n */\r\n public abstract activate(): void;\r\n\r\n /**\r\n * Deactivates the module, disabling its functionality and cleaning up its active state.\r\n * Each module must implement this method.\r\n */\r\n public abstract deactivate(): void;\r\n\r\n /**\r\n * Returns the unique name of the module.\r\n * @returns The name of the module.\r\n */\r\n public getName(): string {\r\n return this.name;\r\n }\r\n\r\n /**\r\n * Cleans up all resources used by the module.\r\n * This method iterates over all registered event listeners and removes them.\r\n */\r\n public destroy(): void {\r\n this.eventHandlers.forEach(({ element, type, handler }) => {\r\n element.removeEventListener(type, handler);\r\n });\r\n this.eventHandlers = [];\r\n }\r\n\r\n /**\r\n * A helper method to attach an event listener to an element and track it for later removal.\r\n * @param el The element to attach the listener to (e.g., HTMLElement, HTMLInputElement).\r\n * @param type The event name (e.g., \"click\", \"mousedown\", \"input\").\r\n * @param handler The event handler function.\r\n */\r\n protected addEventListener(el: HTMLElement, type: string, handler: ModuleEventHandler): void {\r\n el.addEventListener(type, handler);\r\n this.eventHandlers.push({ element: el, type, handler });\r\n }\r\n}\r\n"],"names":["BaseModule","name","element","type","handler","el"],"mappings":"AAMO,MAAeA,EAA8B;AAAA;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMP,gBAIL,CAAA;AAAA;AAAA;AAAA;AAAA,EAKL,YAAYC,GAAc;AACtB,SAAK,OAAOA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBO,UAAkB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACnB,SAAK,cAAc,QAAQ,CAAC,EAAE,SAAAC,GAAS,MAAAC,GAAM,SAAAC,QAAc;AACvD,MAAAF,EAAQ,oBAAoBC,GAAMC,CAAO;AAAA,IAC7C,CAAC,GACD,KAAK,gBAAgB,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,iBAAiBC,GAAiBF,GAAcC,GAAmC;AACzF,IAAAC,EAAG,iBAAiBF,GAAMC,CAAO,GACjC,KAAK,cAAc,KAAK,EAAE,SAASC,GAAI,MAAAF,GAAM,SAAAC,GAAS;AAAA,EAC1D;AACJ;"}
1
+ {"version":3,"file":"BaseModule.js","sources":["../../src/modules/BaseModule.ts"],"sourcesContent":["import type { IModule } from \"../types.js\";\r\n\r\n/**\r\n * Base class for all editor modules. It provides a centralized way to manage\r\n * event listeners, ensuring they are properly cleaned up when a module is destroyed.\r\n */\r\nexport abstract class BaseModule implements IModule {\r\n /** The unique name of the module (e.g., 'crop', 'resize'). */\r\n private readonly name: string;\r\n /**\r\n * Stores all registered event listeners for this module.\r\n * Each entry contains the element, event type, and the handler function,\r\n * allowing for easy removal in the `destroy` method.\r\n */\r\n protected eventHandlers: Array<{\r\n element: EventTarget;\r\n type: string;\r\n handler: EventListenerOrEventListenerObject;\r\n options?: boolean | AddEventListenerOptions;\r\n }> = [];\r\n\r\n /**\r\n * @param name - The unique name for the module.\r\n */\r\n constructor(name: string) {\r\n this.name = name;\r\n }\r\n\r\n /**\r\n * Initializes the module. Each module must implement this method\r\n * to register its necessary event listeners using the `addEventListener` helper.\r\n */\r\n public abstract init(): void;\r\n\r\n /**\r\n * Activates the module, enabling its specific functionality.\r\n * Each module must implement this method.\r\n */\r\n public abstract activate(): void;\r\n\r\n /**\r\n * Deactivates the module, disabling its functionality and cleaning up its active state.\r\n * Each module must implement this method.\r\n */\r\n public abstract deactivate(): void;\r\n\r\n /**\r\n * Returns the unique name of the module.\r\n * @returns The name of the module.\r\n */\r\n public getName(): string {\r\n return this.name;\r\n }\r\n\r\n /**\r\n * Cleans up all resources used by the module.\r\n * This method iterates over all registered event listeners and removes them.\r\n */\r\n public destroy(): void {\r\n this.eventHandlers.forEach(({ element, type, handler, options }) => {\r\n element.removeEventListener(type, handler, options);\r\n });\r\n this.eventHandlers = [];\r\n }\r\n\r\n /**\r\n * A helper method to attach an event listener to an element and track it for later removal.\r\n * @param el The element to attach the listener to (e.g., HTMLElement, HTMLInputElement).\r\n * @param type The event name (e.g., \"click\", \"mousedown\", \"input\").\r\n * @param handler The event handler function.\r\n */\r\n protected addEventListener<TEvent extends Event>(el: EventTarget, type: string, handler: (event: TEvent) => void, options?: boolean | AddEventListenerOptions): void {\r\n const listener = handler as EventListener;\r\n el.addEventListener(type, listener, options);\r\n this.eventHandlers.push({ element: el, type, handler: listener, options });\r\n }\r\n}\r\n"],"names":["BaseModule","name","element","type","handler","options","el","listener"],"mappings":"AAMO,MAAeA,EAA8B;AAAA;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMP,gBAKL,CAAA;AAAA;AAAA;AAAA;AAAA,EAKL,YAAYC,GAAc;AACtB,SAAK,OAAOA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBO,UAAkB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACnB,SAAK,cAAc,QAAQ,CAAC,EAAE,SAAAC,GAAS,MAAAC,GAAM,SAAAC,GAAS,SAAAC,QAAc;AAChE,MAAAH,EAAQ,oBAAoBC,GAAMC,GAASC,CAAO;AAAA,IACtD,CAAC,GACD,KAAK,gBAAgB,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,iBAAuCC,GAAiBH,GAAcC,GAAkCC,GAAmD;AACjK,UAAME,IAAWH;AACjB,IAAAE,EAAG,iBAAiBH,GAAMI,GAAUF,CAAO,GAC3C,KAAK,cAAc,KAAK,EAAE,SAASC,GAAI,MAAAH,GAAM,SAASI,GAAU,SAAAF,GAAS;AAAA,EAC7E;AACJ;"}
@@ -1,4 +1,5 @@
1
1
  import { CropDOMElements } from '../utils/dom-manager.js';
2
+ import { CanvasState } from '../state/CanvasState.js';
2
3
  import { BaseModule } from './BaseModule.js';
3
4
  /**
4
5
  * Configuration options for the CropModule.
@@ -6,9 +7,8 @@ import { BaseModule } from './BaseModule.js';
6
7
  export interface CropModuleOptions {
7
8
  canvas: HTMLCanvasElement;
8
9
  ctx: CanvasRenderingContext2D;
9
- getCurrentImage: () => HTMLImageElement | undefined;
10
- requestRedraw?: () => void;
11
- onCropApplied: (newImageDataUrl: string) => void;
10
+ state: CanvasState;
11
+ onCropApplied: () => void;
12
12
  frameColor?: string;
13
13
  outsideOverlayColor?: string;
14
14
  }
@@ -18,26 +18,24 @@ export interface CropModuleOptions {
18
18
  */
19
19
  export declare class CropModule extends BaseModule {
20
20
  /** Core Dependencies */
21
- private canvas;
22
- private ctx;
23
- private applyButton;
24
- private getCurrentImage;
25
- private requestRedraw?;
26
- private onCropAppliedCallback;
27
- private frameColor;
28
- private outsideOverlayColor;
21
+ private readonly canvas;
22
+ private readonly ctx;
23
+ private readonly applyButton;
24
+ private readonly state;
25
+ private readonly onCropAppliedCallback;
26
+ private readonly frameColor;
27
+ private readonly outsideOverlayColor;
29
28
  private readonly HANDLE_SIZE;
30
29
  private readonly MIN_CROP_SIZE;
31
- private cropMode;
32
- private cropRect;
30
+ private isLocalActive;
33
31
  private dragging;
34
32
  private dragOffsetX;
35
33
  private dragOffsetY;
36
- /** The handle currently being dragged, or "rect" for the whole rectangle. */
37
34
  private activeHandle;
38
35
  private shiftPressed;
39
36
  private lastMouseX;
40
37
  private lastMouseY;
38
+ private previousTouchAction;
41
39
  /**
42
40
  * Creates an instance of the CropModule.
43
41
  * @param elements - The DOM elements used by the module.
@@ -60,47 +58,44 @@ export declare class CropModule extends BaseModule {
60
58
  * Deactivates the crop module.
61
59
  */
62
60
  deactivate(): void;
63
- /**
64
- * Checks if the crop mode is currently active.
65
- * @returns True if crop mode is active, false otherwise.
66
- */
67
- isCropModeActive(): boolean;
68
61
  /**
69
62
  * Enables crop mode.
70
- * This initializes the crop rectangle to a default size and adds all necessary event listeners for interaction.
71
- * Enables crop mode, initializing the crop rectangle and adding event listeners.
63
+ * This initializes the crop rectangle to a default size.
72
64
  */
73
- enableCropMode(): void;
65
+ private _enableCropMode;
74
66
  /**
75
67
  * Disables crop mode.
76
- * This resets the module's state and removes all event listeners related to cropping.
77
- * Disables crop mode, resetting state and removing event listeners.
68
+ * This resets the module's state.
78
69
  */
79
- disableCropMode(): void;
70
+ private _disableCropMode;
80
71
  /**
81
72
  * Handles the keydown event, specifically for the Shift key to toggle aspect ratio lock.
82
73
  * @internal
83
74
  */
84
- onKeyDown: (e: KeyboardEvent) => void;
75
+ private readonly _onKeyDown;
85
76
  /**
86
77
  * Handles the keyup event, specifically for the Shift key to toggle aspect ratio lock.
87
78
  * @param e - The keyboard event.
88
79
  */
89
- private _onKeyUp;
80
+ private readonly _onKeyUp;
90
81
  /**
91
82
  * Handles the mousedown event to initiate dragging or resizing of the crop rectangle.
92
83
  * @param event - The mouse event.
93
84
  */
94
- private _onMouseDown;
85
+ private readonly _onMouseDown;
95
86
  /**
96
87
  * Handles the mousemove event to update the crop rectangle during drag/resize and to update the cursor style.
97
88
  * @param event - The mouse event.
98
89
  */
99
- private _onMouseMove;
90
+ private readonly _onMouseMove;
100
91
  /**
101
92
  * Handles the mouseup event to finalize the drag/resize operation and normalize the crop rectangle.
102
93
  */
103
- private _onMouseUp;
94
+ private readonly _onMouseUp;
95
+ private readonly _onTouchStart;
96
+ private readonly _onTouchMove;
97
+ private readonly _onTouchEnd;
98
+ private readonly _onTouchCancel;
104
99
  /**
105
100
  * Handles the dragging logic for resizing and moving the crop rectangle.
106
101
  * @param mx - The current mouse X position.
@@ -115,7 +110,7 @@ export declare class CropModule extends BaseModule {
115
110
  /**
116
111
  * Applies the crop and provides the new image data URL to the callback.
117
112
  */
118
- private _applyCrop;
113
+ private readonly _applyCrop;
119
114
  /**
120
115
  * Draws the crop overlay, including the semi-transparent mask, border, and handles.
121
116
  */
@@ -143,5 +138,11 @@ export declare class CropModule extends BaseModule {
143
138
  * @param handle - The handle currently under the cursor.
144
139
  */
145
140
  private _updateCursor;
141
+ private _getCropRect;
142
+ private _setCropRect;
143
+ private _getTouchPoint;
144
+ private _moveRect;
145
+ private _resizeRect;
146
+ private _asSquareRect;
146
147
  }
147
148
  //# sourceMappingURL=CropModule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CropModule.d.ts","sourceRoot":"","sources":["../../src/modules/CropModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,wBAAwB,CAAC;IAC9B,eAAe,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAC;IACpD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAwBD;;;GAGG;AAEH,qBAAa,UAAW,SAAQ,UAAU;IAGtC,wBAAwB;IACxB,OAAO,CAAC,MAAM,CAAoB;IAElC,OAAO,CAAC,GAAG,CAA2B;IAEtC,OAAO,CAAC,WAAW,CAAc;IAEjC,OAAO,CAAC,eAAe,CAAqC;IAE5D,OAAO,CAAC,aAAa,CAAC,CAAa;IAEnC,OAAO,CAAC,qBAAqB,CAAoC;IAGjE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,mBAAmB,CAAS;IAEpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IAItD,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,QAAQ,CAAoC;IAEpD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAK;IACxB,6EAA6E;IAC7E,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAAK;IAIvB;;;;OAIG;gBACS,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB;IAcjE;;OAEG;IACI,IAAI,IAAI,IAAI;IAInB;;OAEG;IACI,OAAO,IAAI,IAAI;IAOtB;;OAEG;IACI,QAAQ,IAAI,IAAI;IAIvB;;OAEG;IACI,UAAU,IAAI,IAAI;IAIzB;;;OAGG;IACI,gBAAgB,IAAI,OAAO;IAIlC;;;;OAIG;IACI,cAAc,IAAI,IAAI;IAuB7B;;;;OAIG;IACI,eAAe,IAAI,IAAI;IAsB9B;;;OAGG;IACI,SAAS,GAAI,GAAG,aAAa,KAAG,IAAI,CAQzC;IAEF;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAQd;IAEF;;;OAGG;IACH,OAAO,CAAC,YAAY,CAgBlB;IAEF;;;OAGG;IACH,OAAO,CAAC,YAAY,CAclB;IAEF;;OAEG;IACH,OAAO,CAAC,UAAU,CAUhB;IAIF;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAiDnB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA0B1B;;OAEG;IACH,OAAO,CAAC,UAAU,CA4ChB;IAIF;;OAEG;IACI,WAAW,IAAI,IAAI;IA+B1B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;OAGG;IACH,OAAO,CAAC,aAAa;CASxB"}
1
+ {"version":3,"file":"CropModule.d.ts","sourceRoot":"","sources":["../../src/modules/CropModule.ts"],"names":[],"mappings":"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"}