From 7b001d5b7ec17b234a845fb15774f1398bd4c27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Mon, 22 Jul 2024 09:09:35 +0200 Subject: [PATCH] svg --- eslint.config.mjs | 9 +++ examples/generic-editor/CanvasEditor.js | 73 ------------------- examples/generic-editor/index.js | 1 - examples/generic_editor.ts | 10 +-- .../canvas_editor/clipboard_handler.js | 6 +- lib/canvas_editor/create_editor.js | 55 ++++++++++++++ .../canvas_editor/cursors.js | 57 +-------------- .../canvas_editor/draw_context.js | 6 +- lib/canvas_editor/editor_area.js | 57 +++++++++++++++ .../canvas_editor/editor_dialog.js | 12 +-- .../canvas_editor/editor_image.js | 4 +- .../canvas_editor}/events.js | 9 ++- lib/canvas_editor/index.js | 35 ++++++++- .../canvas_editor/toolbar.js | 6 +- .../canvas_editor}/utils.js | 15 +++- types.d.ts | 29 +++++--- 16 files changed, 222 insertions(+), 162 deletions(-) delete mode 100644 examples/generic-editor/CanvasEditor.js delete mode 100644 examples/generic-editor/index.js rename examples/generic-editor/CanvasClipboardHandler.js => lib/canvas_editor/clipboard_handler.js (77%) create mode 100644 lib/canvas_editor/create_editor.js rename examples/generic-editor/CanvasEditorArea.js => lib/canvas_editor/cursors.js (90%) rename examples/generic-editor/CanvasDrawContext.js => lib/canvas_editor/draw_context.js (97%) create mode 100644 lib/canvas_editor/editor_area.js rename examples/generic-editor/CanvasEditorDialog.js => lib/canvas_editor/editor_dialog.js (96%) rename examples/generic-editor/CanvasEditorImage.js => lib/canvas_editor/editor_image.js (92%) rename {examples/generic-editor => lib/canvas_editor}/events.js (93%) rename examples/generic-editor/CanvasToolbar.js => lib/canvas_editor/toolbar.js (86%) rename {examples/generic-editor => lib/canvas_editor}/utils.js (85%) diff --git a/eslint.config.mjs b/eslint.config.mjs index 8ba41258..36fb0852 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -23,6 +23,15 @@ export default [ sourceType: 'commonjs', }, }, + { + files: ['lib/**'], + languageOptions: { + sourceType: 'commonjs', + globals: { + ...globals.browser, + }, + }, + }, { files: ['__tests__/**'], languageOptions: { diff --git a/examples/generic-editor/CanvasEditor.js b/examples/generic-editor/CanvasEditor.js deleted file mode 100644 index a3813008..00000000 --- a/examples/generic-editor/CanvasEditor.js +++ /dev/null @@ -1,73 +0,0 @@ -import { CanvasEditorArea } from './CanvasEditorArea.js'; -import { CanvasToolbar } from './CanvasToolbar.js'; -import { addMouseListeners, addKeyboardListeners } from './events.js'; - -const { EditorArea } = OCL; - -export class CanvasEditor { - /** - * - * @param {HTMLElement} rootElement - */ - constructor(rootElement, options = {}) { - const { onChange } = options; - - const childElement = document.createElement('div'); - childElement.setAttribute( - 'style', - 'width: 100%; height: 100%; display: flex; flex-direction: row; align-items: start; background-color: white;', - ); - rootElement.appendChild(childElement); - - const toolbarCanvas = document.createElement('canvas'); - childElement.appendChild(toolbarCanvas); - - const editorContainer = document.createElement('div'); - editorContainer.setAttribute('style', 'width: 100%; height: 100%;'); - childElement.appendChild(editorContainer); - - const editorCanvas = document.createElement('canvas'); - editorCanvas.tabIndex = '0'; - editorCanvas.style = 'outline: none'; - editorContainer.appendChild(editorCanvas); - - const containerSize = editorContainer.getBoundingClientRect(); - editorCanvas.width = containerSize.width; - editorCanvas.height = containerSize.height; - - const editorArea = new EditorArea( - new CanvasEditorArea(editorCanvas, onChange), - ); - this.editorArea = editorArea; - - editorArea.draw(); - - this.toolbar = new CanvasToolbar(toolbarCanvas, editorArea); - - const resizeObserver = new ResizeObserver(([entry]) => { - editorCanvas.width = entry.contentRect.width; - editorCanvas.height = entry.contentRect.height; - editorArea.repaint(); - }); - resizeObserver.observe(editorContainer); - - addMouseListeners(editorCanvas, editorArea); - addKeyboardListeners(editorCanvas, editorArea); - } - - getMolecule() { - return this.editorArea.getMolecule(); - } - - setMolecule(molecule) { - this.editorArea.setMolecule(molecule); - } - - getReaction() { - return this.editorArea.getReaction(); - } - - setReaction(reaction) { - this.editorArea.setReaction(reaction); - } -} diff --git a/examples/generic-editor/index.js b/examples/generic-editor/index.js deleted file mode 100644 index a37f85b2..00000000 --- a/examples/generic-editor/index.js +++ /dev/null @@ -1 +0,0 @@ -export { CanvasEditor } from './CanvasEditor.js'; diff --git a/examples/generic_editor.ts b/examples/generic_editor.ts index 6e3025f9..e46a51b0 100644 --- a/examples/generic_editor.ts +++ b/examples/generic_editor.ts @@ -2,11 +2,11 @@ import OCL from '../distesm/full.pretty'; const editorArea = document.getElementById('editor-area') as HTMLElement; -const editor = new OCL.CanvasEditor(editorArea, { - width: 500, - height: 500, -}); +const editor = new OCL.CanvasEditor(editorArea); const molecule = OCL.Molecule.fromSmiles('COCO'); - editor.setMolecule(molecule); + +editor.setOnChangeListener((event) => { + console.log('change', event); +}); diff --git a/examples/generic-editor/CanvasClipboardHandler.js b/lib/canvas_editor/clipboard_handler.js similarity index 77% rename from examples/generic-editor/CanvasClipboardHandler.js rename to lib/canvas_editor/clipboard_handler.js index ad8ec6f6..35364f5c 100644 --- a/examples/generic-editor/CanvasClipboardHandler.js +++ b/lib/canvas_editor/clipboard_handler.js @@ -1,4 +1,6 @@ -export class CanvasClipboardHandler { +'use strict'; + +class ClipboardHandler { copyMolecule(molecule) { const data = molecule.getIDCodeAndCoordinates(); navigator.clipboard.writeText(`${data.idCode} ${data.coordinates}`); @@ -9,3 +11,5 @@ export class CanvasClipboardHandler { return null; } } + +module.exports = ClipboardHandler; diff --git a/lib/canvas_editor/create_editor.js b/lib/canvas_editor/create_editor.js new file mode 100644 index 00000000..f0680bb7 --- /dev/null +++ b/lib/canvas_editor/create_editor.js @@ -0,0 +1,55 @@ +const EditorArea = require('editor_area'); + +const { + CanvasToolbar, +} = require('../../examples/generic-editor/CanvasToolbar'); +const { + addMouseListeners, + addKeyboardListeners, +} = require('../../examples/generic-editor/events'); + +function createEditor(rootElement, EditorArea, EditorToolbar) { + const childElement = document.createElement('div'); + childElement.setAttribute( + 'style', + 'width: 100%; height: 100%; display: flex; flex-direction: row; align-items: start; background-color: white;', + ); + rootElement.append(childElement); + + const toolbarCanvas = document.createElement('canvas'); + childElement.append(toolbarCanvas); + + const editorContainer = document.createElement('div'); + editorContainer.setAttribute('style', 'width: 100%; height: 100%;'); + childElement.append(editorContainer); + + const editorCanvas = document.createElement('canvas'); + editorCanvas.tabIndex = '0'; + editorCanvas.style = 'outline: none'; + editorContainer.append(editorCanvas); + + const containerSize = editorContainer.getBoundingClientRect(); + editorCanvas.width = containerSize.width; + editorCanvas.height = containerSize.height; + + const editorArea = new EditorArea( + new CanvasEditorArea(editorCanvas, onChange), + ); + this.editorArea = editorArea; + + editorArea.draw(); + + this.toolbar = new CanvasToolbar(toolbarCanvas, editorArea); + + const resizeObserver = new ResizeObserver(([entry]) => { + editorCanvas.width = entry.contentRect.width; + editorCanvas.height = entry.contentRect.height; + editorArea.repaint(); + }); + resizeObserver.observe(editorContainer); + + addMouseListeners(editorCanvas, editorArea); + addKeyboardListeners(editorCanvas, editorArea); +} + +module.exports = createEditor; diff --git a/examples/generic-editor/CanvasEditorArea.js b/lib/canvas_editor/cursors.js similarity index 90% rename from examples/generic-editor/CanvasEditorArea.js rename to lib/canvas_editor/cursors.js index e6cb28db..f6bca74d 100644 --- a/examples/generic-editor/CanvasEditorArea.js +++ b/lib/canvas_editor/cursors.js @@ -1,57 +1,4 @@ -import { CanvasClipboardHandler } from './CanvasClipboardHandler.js'; -import { CanvasDrawContext } from './CanvasDrawContext.js'; -import { CanvasEditorDialog } from './CanvasEditorDialog.js'; -import { CanvasEditorImage } from './CanvasEditorImage.js'; -import { decodeBase64 } from './utils.js'; - -const { EditorArea } = OCL; - -export class CanvasEditorArea { - constructor(canvasElement, onChange) { - this.canvasElement = canvasElement; - this.changeListener = onChange; - } - // JSEditorArea methods - getBackgroundRGB() { - return 0xffffff; - } - getCanvasWidth() { - return this.canvasElement.width; - } - getCanvasHeight() { - return this.canvasElement.height; - } - getDrawContext() { - return new CanvasDrawContext(this.canvasElement.getContext('2d')); - } - onChange(what, isUserEvent) { - this.changeListener?.({ what, isUserEvent }); - } - getClipboardHandler() { - return new CanvasClipboardHandler(); - } - // JSUIHelper methods - grabFocus() { - this.canvasElement.focus(); - } - setCursor(cursor) { - this.canvasElement.style.cursor = getCursor(cursor); - } - showHelpDialog(url, title) { - console.log({ url, title }); - alert('Help dialog is not implemented yet'); - } - createImageFromBase64(width, height, base64) { - base64 = base64.replaceAll('%', 'A'.repeat(20)); - const decoded = decodeBase64(base64); - const typedArray = new Uint8ClampedArray(decoded); - const imageData = new ImageData(typedArray, width, height); - return new CanvasEditorImage(imageData); - } - createDialog(title) { - return new CanvasEditorDialog(title); - } -} +'use strict'; const customCursors = { [EditorArea.cChainCursor]: @@ -95,3 +42,5 @@ function getCursor(cursor) { throw new Error(`Unknown cursor: ${cursor}`); } } + +module.exports = getCursor; diff --git a/examples/generic-editor/CanvasDrawContext.js b/lib/canvas_editor/draw_context.js similarity index 97% rename from examples/generic-editor/CanvasDrawContext.js rename to lib/canvas_editor/draw_context.js index 7d3fa143..96ab6e81 100644 --- a/examples/generic-editor/CanvasDrawContext.js +++ b/lib/canvas_editor/draw_context.js @@ -1,6 +1,6 @@ -import { toHex } from './utils.js'; +const { toHex } = require('./utils'); -export class CanvasDrawContext { +class DrawContext { /** * * @param {CanvasRenderingContext2D} ctx @@ -140,3 +140,5 @@ export class CanvasDrawContext { this.ctx.drawImage(fullScaleCanvas, sx, sy, sw, sh, dx, dy, dw, dh); } } + +module.exports = DrawContext; diff --git a/lib/canvas_editor/editor_area.js b/lib/canvas_editor/editor_area.js new file mode 100644 index 00000000..0e02ad62 --- /dev/null +++ b/lib/canvas_editor/editor_area.js @@ -0,0 +1,57 @@ +'use strict'; + +const ClipboardHandler = require('./clipboard_handler'); +const getCursor = require('./cursors'); +const DrawContext = require('./draw_context'); +const EditorDialog = require('./editor_dialog'); +const EditorImage = require('./editor_image'); +const { decodeBase64 } = require('./utils'); + +class EditorArea { + constructor(canvasElement, onChange) { + this.canvasElement = canvasElement; + this.changeListener = onChange; + } + // JSEditorArea methods + getBackgroundRGB() { + return 0xffffff; + } + getCanvasWidth() { + return this.canvasElement.width; + } + getCanvasHeight() { + return this.canvasElement.height; + } + getDrawContext() { + return new DrawContext(this.canvasElement.getContext('2d')); + } + onChange(what, isUserEvent) { + this.changeListener?.({ what, isUserEvent }); + } + getClipboardHandler() { + return new ClipboardHandler(); + } + // JSUIHelper methods + grabFocus() { + this.canvasElement.focus(); + } + setCursor(cursor) { + this.canvasElement.style.cursor = getCursor(cursor); + } + showHelpDialog(url, title) { + console.log({ url, title }); + alert('Help dialog is not implemented yet'); + } + createImageFromBase64(width, height, base64) { + base64 = base64.replaceAll('%', 'A'.repeat(20)); + const decoded = decodeBase64(base64); + const typedArray = new Uint8ClampedArray(decoded); + const imageData = new ImageData(typedArray, width, height); + return new EditorImage(imageData); + } + createDialog(title) { + return new EditorDialog(title); + } +} + +module.exports = EditorArea; diff --git a/examples/generic-editor/CanvasEditorDialog.js b/lib/canvas_editor/editor_dialog.js similarity index 96% rename from examples/generic-editor/CanvasEditorDialog.js rename to lib/canvas_editor/editor_dialog.js index 21a8423f..6cddb531 100644 --- a/examples/generic-editor/CanvasEditorDialog.js +++ b/lib/canvas_editor/editor_dialog.js @@ -1,4 +1,4 @@ -export class CanvasEditorDialog { +class EditorDialog { /** * * @param {string} title @@ -60,8 +60,8 @@ export class CanvasEditorDialog { div.style.gridColumn = `${x + 1} / ${x2 + 2}`; div.style.gridRow = `${y + 1} / ${y2 + 2}`; } - div.appendChild(component.getElement()); - grid.appendChild(div); + div.append(component.getElement()); + grid.append(div); } const buttons = document.createElement('div'); buttons.style.display = 'flex'; @@ -72,13 +72,13 @@ export class CanvasEditorDialog { okButton.addEventListener('click', () => { this.consumer.fireOk(); }); - buttons.appendChild(okButton); + buttons.append(okButton); const cancelButton = document.createElement('button'); cancelButton.textContent = 'Cancel'; cancelButton.addEventListener('click', () => { this.consumer.fireCancel(); }); - buttons.appendChild(cancelButton); + buttons.append(cancelButton); dialog.append(buttons); dialog.showModal(); dialog.addEventListener('cancel', () => { @@ -232,3 +232,5 @@ function generateLayout(layout) { }) .join(' '); } + +module.exports = EditorDialog; diff --git a/examples/generic-editor/CanvasEditorImage.js b/lib/canvas_editor/editor_image.js similarity index 92% rename from examples/generic-editor/CanvasEditorImage.js rename to lib/canvas_editor/editor_image.js index 75ee45ee..10863d89 100644 --- a/examples/generic-editor/CanvasEditorImage.js +++ b/lib/canvas_editor/editor_image.js @@ -1,4 +1,4 @@ -export class CanvasEditorImage { +class EditorImage { /** * * @param {ImageData} imageData @@ -31,3 +31,5 @@ export class CanvasEditorImage { this.dataView.setInt32((y * this.imageData.width + x) * 4, rgb, false); } } + +module.exports = EditorImage; diff --git a/examples/generic-editor/events.js b/lib/canvas_editor/events.js similarity index 93% rename from examples/generic-editor/events.js rename to lib/canvas_editor/events.js index 3f0c96db..a0ed7b76 100644 --- a/examples/generic-editor/events.js +++ b/lib/canvas_editor/events.js @@ -1,6 +1,6 @@ const { EditorArea } = OCL; -export function addMouseListeners(canvasElement, drawArea) { +function addMouseListeners(canvasElement, drawArea) { let isMouseDown = false; function fireMouseEvent(what, ev, clickCount = 0) { @@ -47,7 +47,7 @@ export function addMouseListeners(canvasElement, drawArea) { }); } -export function addKeyboardListeners(canvasElement, editorArea) { +function addKeyboardListeners(canvasElement, editorArea) { const isMac = typeof navigator !== 'undefined' && navigator.platform === 'MacIntel'; @@ -102,3 +102,8 @@ function getKeyFromEvent(ev) { } } } + +module.exports = { + addMouseListeners, + addKeyboardListeners, +}; diff --git a/lib/canvas_editor/index.js b/lib/canvas_editor/index.js index d1737a16..414e4e10 100644 --- a/lib/canvas_editor/index.js +++ b/lib/canvas_editor/index.js @@ -2,9 +2,40 @@ function createCanvasEditor(EditorArea, EditorToolbar) { return class CanvasEditor { + #editorArea; + #onChange; + + constructor(rootElement) { + this.#editorArea = createEditorArea( + rootElement, + EditorArea, + EditorToolbar, + ); + this.#onChange = null; + } + setMolecule(molecule) { - console.log('WITH WATCH'); - this.molecule = molecule; + this.#editorArea.setMolecule(molecule); + } + + getMolecule() { + return this.#editorArea.getMolecule(); + } + + setReaction(reaction) { + this.#editorArea.setReaction(reaction); + } + + getReaction() { + this.#editorArea.getReaction(); + } + + setOnChangeListener(onChange) { + this.#onChange = onChange; + } + + removeOnChangeListner() { + this.#onChange = null; } }; } diff --git a/examples/generic-editor/CanvasToolbar.js b/lib/canvas_editor/toolbar.js similarity index 86% rename from examples/generic-editor/CanvasToolbar.js rename to lib/canvas_editor/toolbar.js index aa4e4d8e..530c2d35 100644 --- a/examples/generic-editor/CanvasToolbar.js +++ b/lib/canvas_editor/toolbar.js @@ -1,9 +1,9 @@ -import { CanvasDrawContext } from './CanvasDrawContext.js'; +const DrawContext = require('./draw_context'); import { addMouseListeners } from './events.js'; const { EditorToolbar } = OCL; -export class CanvasToolbar { +class Toolbar { constructor(canvasElement, editorArea) { this.toolbar = new EditorToolbar(editorArea, { setDimensions(width, height) { @@ -25,3 +25,5 @@ export class CanvasToolbar { addMouseListeners(canvasElement, this.toolbar); } } + +module.exports = Toolbar; diff --git a/examples/generic-editor/utils.js b/lib/canvas_editor/utils.js similarity index 85% rename from examples/generic-editor/utils.js rename to lib/canvas_editor/utils.js index fd937be2..849c315e 100644 --- a/examples/generic-editor/utils.js +++ b/lib/canvas_editor/utils.js @@ -1,3 +1,5 @@ +'use strict'; + // https://github.com/niklasvh/base64-arraybuffer/blob/master/LICENSE const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; @@ -7,7 +9,7 @@ for (let i = 0; i < chars.length; i++) { lookup[chars.charCodeAt(i)] = i; } -export function decodeBase64(base64) { +function decodeBase64(base64) { let bufferLength = base64.length * 0.75; let len = base64.length; let i; @@ -17,9 +19,9 @@ export function decodeBase64(base64) { let encoded3; let encoded4; - if (base64[base64.length - 1] === '=') { + if (base64.at(-1) === '=') { bufferLength--; - if (base64[base64.length - 2] === '=') { + if (base64.at(-2) === '=') { bufferLength--; } } @@ -41,6 +43,11 @@ export function decodeBase64(base64) { return arraybuffer; } -export function toHex(v) { +function toHex(v) { return v.toString(16).padStart(2, '0'); } + +module.exports = { + decodeBase64, + toHex, +}; diff --git a/types.d.ts b/types.d.ts index 2bc7b268..167d9657 100644 --- a/types.d.ts +++ b/types.d.ts @@ -3680,19 +3680,28 @@ export declare namespace SVGRenderer { ): string; } -export interface CanvasEditorOptions { - /** - * The width of the canvas. - */ - width: number; - /** - * The height of the canvas. - */ - height: number; +export type OnChangeEventType = + | 'molecule' + | 'selection' + | 'highlight-atom' + | 'highlight-bond'; + +export interface OnChangeEvent { + type: OnChangeEventType; + isUserEvent: boolean; } +export type OnChangeListenerCallback = (event: OnChangeEvent) => void; + export declare class CanvasEditor { - constructor(element: HTMLElement, options?: CanvasEditorOptions); + constructor(element: HTMLElement); setMolecule(molecule: Molecule): void; + getMolecule(): Molecule; + + setReaction(reaction: Reaction): void; + getReaction(): Reaction; + + setOnChangeListener(callback: OnChangeListenerCallback): void; + removeOnChangeListener(): void; }