Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic editor #219

Merged
merged 69 commits into from
Jul 25, 2024
Merged
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
bac7287
feat: add JS API to create a generic editor
targos Feb 9, 2024
4e5f760
chore: add Prettier scripts
targos Feb 10, 2024
054c457
Merge remote-tracking branch 'origin/main' into generic-editor
targos Jul 19, 2024
487fb01
chore: update ESLint config and lint all js code
targos Jul 19, 2024
b8857d5
chore: setup examples deployment with vite
targos Jul 19, 2024
e23ca05
chore: add get-java script
targos Jul 19, 2024
b697160
chore: fix jdk PATH syntax
targos Jul 19, 2024
2a5f763
chore: use Node.js 22 for build
targos Jul 19, 2024
d6a206b
wip: start working on CanvasEditor extension
targos Jul 19, 2024
7d44b49
feat: move poc code from examples to lib and install CanvasEditor in …
targos Jul 22, 2024
416a4ee
chore: remove unused imports
targos Jul 22, 2024
a470dce
chore: migrate poc page to ts
targos Jul 22, 2024
2fb5012
fix: grabFocus from java preventScroll now
tpoisseau Jul 23, 2024
cf77094
fix(Dialog): button ok on the right, cancel on the left
tpoisseau Jul 23, 2024
e0ed340
docs: add jsdoc to all CanvasEditor methods
targos Jul 23, 2024
7728439
chore: use correct hashbang in get-* scripts
targos Jul 23, 2024
579a66e
refactor: extract UI helper to separate class and use 16px cursor
targos Jul 23, 2024
57451bf
chore: add idea config for java modules
tpoisseau Jul 23, 2024
00a07e7
fix: repaint editor area after Dialog fireOk
tpoisseau Jul 23, 2024
e1c5007
fix: properly bind clickCount from dom mouse events to java fireMouse…
tpoisseau Jul 23, 2024
ec567e3
fix: use 24px size for cursors
targos Jul 23, 2024
9d60d9f
fix: implement drawRectangle
targos Jul 23, 2024
5951f11
feat: add readonly mode
tpoisseau Jul 23, 2024
9c0eb44
Merge remote-tracking branch 'origin/generic-editor' into generic-editor
tpoisseau Jul 23, 2024
581bba4
refactor: move readOnly to an options object
targos Jul 23, 2024
03ccf21
feat: add destroy method for cleanup
targos Jul 24, 2024
aecd411
feat: add support for touch screens
targos Jul 24, 2024
1f1da4e
refactor: restructure demo code
targos Jul 24, 2024
61f17d5
chore: add TailwindCSS for demo styles
targos Jul 24, 2024
c2a515c
feat: add option to set initial mode
targos Jul 24, 2024
acb6d45
docs: add reset button to demo page
targos Jul 24, 2024
df193bc
docs: retitle demo page
targos Jul 24, 2024
f63e816
feat: add clearAll method
targos Jul 24, 2024
e3e3bb9
fix: set highlight color to blue
tpoisseau Jul 24, 2024
34da498
Merge remote-tracking branch 'origin/generic-editor' into generic-editor
tpoisseau Jul 24, 2024
f178b1e
refactor: move seeds data
tpoisseau Jul 24, 2024
198f11f
fix: do not make first draw to soon
tpoisseau Jul 24, 2024
999f091
fix: draw toolbar before editor area
targos Jul 24, 2024
b939181
fix: select current tool before redraw
targos Jul 24, 2024
f6025c3
fix: jsDialog
targos Jul 24, 2024
9d5ebee
docs: update demo examples
targos Jul 24, 2024
c4ca78b
feat: prepare custom element
tpoisseau Jul 24, 2024
b6dfe35
Merge remote-tracking branch 'origin/generic-editor' into generic-editor
tpoisseau Jul 24, 2024
a915040
refactor: use animation frame to repaint editor area
targos Jul 25, 2024
f5422f0
refactor: init of canvas editor
targos Jul 25, 2024
8b8cb07
feat: add `getMode` method
targos Jul 25, 2024
2a79004
docs: empty all results on reset
targos Jul 25, 2024
467259a
docs: remove unused variable
targos Jul 25, 2024
f7eda57
chore: update exposed name
targos Jul 25, 2024
1e542ac
chore: continue implem of custom element
tpoisseau Jul 25, 2024
a3c6b70
Merge remote-tracking branch 'origin/generic-editor' into generic-editor
tpoisseau Jul 25, 2024
5a787be
fix: allow backspace for delete actions
targos Jul 25, 2024
55d9760
fix: add more checks for not destroyed
targos Jul 25, 2024
e67a254
fix: only create toolbar canvas if needed
targos Jul 25, 2024
ba0e4fb
docs: add readonly option to init and display molfile v2
targos Jul 25, 2024
dc6cb18
feat: add initialFragment option and trigger update on dialog submit
targos Jul 25, 2024
420361b
chore: update submodule
targos Jul 25, 2024
898d829
fix: rxn v3 bug
targos Jul 25, 2024
c2c9393
test: update snapshot
targos Jul 25, 2024
8e1d4b9
chore: continue implem of custom element
tpoisseau Jul 25, 2024
0b67d9c
chore: cast dom attributes values
tpoisseau Jul 25, 2024
d486cdc
fix: chromium derived do not support custom element extending builtin…
tpoisseau Jul 25, 2024
0bab565
fix: init and document styling
tpoisseau Jul 25, 2024
220bfde
fix: center dialog and isolate it with shadow dom
targos Jul 25, 2024
282d6a2
fix: remove shadow root from custom element
targos Jul 25, 2024
959e16d
refactor: do not init for nothing
targos Jul 25, 2024
d0de3da
fix: run mutatorHandler if this.#editor is defined
tpoisseau Jul 25, 2024
70bacec
chore: run Prettier
targos Jul 25, 2024
a4bcddb
docs: keep default height
targos Jul 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: init and document styling
tpoisseau committed Jul 25, 2024

Verified

This commit was signed with the committer’s verified signature.
tpoisseau tpoisseau
commit 0bab565361a6999b63da4cfcc408b98860166878
54 changes: 46 additions & 8 deletions examples/web_component/demo.html
Original file line number Diff line number Diff line change
@@ -5,21 +5,59 @@
<title>Generic editor - Demo CanvasEditorElement (WebComponent)</title>
<link rel="stylesheet" href="../base.css" />
<script type="module" src="demo.ts"></script>

<style>
openchemlib-editor:defined {
height: 50vh;
width: 50vw;
}
</style>
</head>
<body>
<h1>Generic editor - Demo CanvasEditorElement (WebComponent)</h1>

<openchemlib-editor>
<p>Empty editor</p>
</openchemlib-editor>
<p>Empty editor</p>
<openchemlib-editor></openchemlib-editor>

<p>Molecule <code>ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@</code></p>
<openchemlib-editor
idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"
></openchemlib-editor>

<p>
Molecule Fragment
<code>
ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUhCyqHiCHy@leBhMEh]B\sa^kp
</code>
</p>
<openchemlib-editor
idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUhCyqHiCHy@leBhMEh]B\sa^kp"
fragment
></openchemlib-editor>

<p>Reaction <code>gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@</code></p>
<openchemlib-editor
idcode="gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@"
mode="reaction"
></openchemlib-editor>

<p>
Reaction Fragment
<code>gJX@@eKU@P gGQHDHaImfhB!defH@DAIfUVjj`B</code>
</p>
<openchemlib-editor
idcode="gJX@@eKU@P gGQHDHaImfhB!defH@DAIfUVjj`B"
mode="reaction"
fragment
></openchemlib-editor>

<p>
Molecule readonly
<code>ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@</code>
</p>
<openchemlib-editor
readonly
>
<p>
Readonly editor with idcode ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@
</p>
</openchemlib-editor>
idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"
></openchemlib-editor>
</body>
</html>
70 changes: 25 additions & 45 deletions lib/canvas_editor/init/canvas_editor_element.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
'use strict';

function initCanvasEditorElement(CanvasEditor, Molecule, ReactionEncoder) {
/**
* `<openchemlib-editor>` support 4 observable attributes :
*
* - idcode: init molecule with `Molecule.fromIDCode(idcode)` or reaction with `ReactionEncoder.decode(this.idcode)`.
* fallback to an empty string if not defined.
* - fragment: enable or disable fragment on Molecule / Reaction.
* fallback to false if not defined.
* - mode: can be either `'molecule'` or `'reaction'`. `CanvasEditorElement.MODE` map these values for enum style.
* fallback to `CanvasEditorElement.MODE.MOLECULE` if not defined.
* - readonly: if enable, it do not add toolbar and don't handle user interactions.
* fallback to false.
*
* @example
*
* Empty editor:
* ```html
* <openchemlib-editor></openchemlib-editor>
* ```
*/
class CanvasEditorElement extends HTMLElement {
/** @type {{MOLECULE: 'molecule', REACTION: 'reaction'}} */
static MODE = Object.freeze(
@@ -94,7 +75,7 @@ function initCanvasEditorElement(CanvasEditor, Molecule, ReactionEncoder) {
});
this.#editor.setOnChangeListener(this.#handleChange);

this.#initIdCode();
requestIdleCallback(() => this.#initIdCode());
}

#initIdCode() {
@@ -152,8 +133,6 @@ function initCanvasEditorElement(CanvasEditor, Molecule, ReactionEncoder) {
this.attachShadow({ mode: 'open' });
this.shadowRoot.adoptedStyleSheets = [new CSSStyleSheet()];

this.shadowRoot.append(...this.childNodes);

this.#initEditor();
}

@@ -174,30 +153,31 @@ function initCanvasEditorElement(CanvasEditor, Molecule, ReactionEncoder) {
* Attribute ${name} has changed from ${oldValue} to ${newValue}
*/
attributeChangedCallback(name, oldValue, newValue) {
if (!this.shadowRoot) return;

switch (name) {
case 'idcode': {
this.idcode = String(newValue);
return void this.#initIdCode();
}
case 'fragment': {
this.fragment = Boolean(newValue);
const molecule = this.#editor.getMolecule();
molecule.setFragment(this.fragment);
return void this.#editor.setMolecule(molecule);
}
case 'mode': {
this.mode = String(newValue);
return void this.#resetEditor();
const mutatorHandler = (() => {
switch (name) {
case 'idcode': {
this.idcode = String(newValue);
return () => this.#initIdCode();
}
case 'fragment': {
this.fragment = newValue !== null;
return () => this.#initIdCode();
}
case 'mode': {
this.mode = String(newValue);
return () => this.#resetEditor();
}
case 'readonly': {
this.readonly = newValue !== null;
return () => this.#resetEditor();
}
default:
throw new Error('unsupported attribute change');
}
case 'readonly': {
this.readonly = Boolean(newValue);
return void this.#resetEditor();
}
default:
throw new Error('unsupported attribute change');
}
})();

if (!this.shadowRoot) return;
mutatorHandler();
}
}

29 changes: 21 additions & 8 deletions lib/canvas_editor/init/index.js
Original file line number Diff line number Diff line change
@@ -33,14 +33,27 @@ function init(OCL) {

customElements.define('openchemlib-editor', CanvasEditorElement);

const css = new CSSStyleSheet();
css.replaceSync(`
openchemlib-editor:defined {
display: block;
height: 400px;
}
`);
document.adoptedStyleSheets.push(css);
// mutate the first stylesheet available or construct a new one, added to adoptedStyleSheets
// It's default styling for openchemlib-editor element,
// should be considered as a user-agent stylesheet (low-priority)
const css =
document.styleSheets[0] ??
(() => {
const css = new CSSStyleSheet();
document.adoptedStyleSheets.unshift(css);
return css;
})();
css.insertRule(
`
/* dynamicaly added from openchemlib registerCustomElement with low priority */
openchemlib-editor:defined {
display: block;
height: 400px;
width: 600px;
}
`,
0,
);

return CanvasEditorElement;
}
76 changes: 59 additions & 17 deletions types.d.ts
Original file line number Diff line number Diff line change
@@ -3480,7 +3480,7 @@
}

export interface IForceFieldMMFF94Options {
// TODO

Check warning on line 3483 in types.d.ts

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO'
}

export interface IForceFieldMinimiseOptions {
@@ -3810,43 +3810,85 @@
*
* Usage:
*
* In Javascript:
* ```js
* import {registerCustomElement} from 'openchemlib/minimal';
* import {registerCustomElement, Molecule, ReactionEncoder} from 'openchemlib/minimal';
*
* // register CanvasEditorElement with `openchemlib-editor` tag name
* const CanvasEditorElement = registerCustomElement();
*
* // CanvasEditorElementConstructor.MODE return enums of possible modes
* const firstEditor = document.querySelector('openchemlib-editor');
* console.assert(firstEditor instanceof CanvasEditorElement);
*
* ```
* firstEditor.setMolecule(Molecule.fromIDCode('ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@'));
* const molecule = firstEditor.getMolecule();
*
* @example
* firstEditor.setReaction(ReactionEncoder.decode('gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@'));
* const reaction = firstEditor.getReaction();
*
* firstEditor.clearAll();
* ```
*
* In HTML
* ```html
* Molecule
* <openchemlib-editor idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"></openchemlib-editor>
* <p>Empty editor</p>
* <openchemlib-editor></openchemlib-editor>
*
* <p>Molecule <code>ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@</code></p>
* <openchemlib-editor
* idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"
* ></openchemlib-editor>
*
* <p>
* Molecule Fragment
* <code>
* ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUhCyqHiCHy@leBhMEh]B\sa^kp
* </code>
* </p>
* <openchemlib-editor
* idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUhCyqHiCHy@leBhMEh]B\sa^kp"
* fragment
* ></openchemlib-editor>
*
* Molecule Fragment
* <p>Reaction <code>gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@</code></p>
* <openchemlib-editor
* idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUhCyqHiCHy@leBhMEh]B\sa^kp"
* fragment
* idcode="gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@"
* mode="reaction"
* ></openchemlib-editor>
*
* Reaction
* <p>
* Reaction Fragment
* <code>gJX@@eKU@P gGQHDHaImfhB!defH@DAIfUVjj`B</code>
* </p>
* <openchemlib-editor
* idcode="gJX@@eKU@@ gGQHDHaImfh@!defH@DAIfUVjj`@"
* mode="reaction"
* idcode="gJX@@eKU@P gGQHDHaImfhB!defH@DAIfUVjj`B"
* mode="reaction"
* fragment
* ></openchemlib-editor>
*
* Reaction Fragment
* <p>
* Molecule readonly
* <code>ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@</code>
* </p>
* <openchemlib-editor
* idcode="gJX@@eKU@P gGQHDHaImfhB!defH@DAIfUVjj`B"
* mode="reaction"
* fragment
* readonly
* idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"
* ></openchemlib-editor>
* ```
*
* Styling:
* `registerCustomElement()` insertRule to define height to 400px and width to 600px on `openchemlib-editor` element.
* This element need to be contained by fixed width and height, or it will grow indefinitely.
* So for responsive layout, ensure your container has max-width and max-height
* before override the inserted rules to 100% instead fixed units.
*
* Molecule readonly
* <openchemlib-editor readonly idcode="ffc`P@H`QxNQQJJIJIZJHiSkQSejB`jFjhhaEqFUh@"></openchemlib-editor>
* ```css
* // need to be into a fixed size container
* openchemlib-editor:defined {
* width: 100%;
* height: 100%
* }
* ```
*/
declare interface CanvasEditorElement extends HTMLElement {

Unchanged files with check annotations Beta

const parser = new SmilesParser();
const coords1 = parser.parseMolecule(smiles).getIDCoordinates();
const coords2 = parser.parseMolecule(smiles).getIDCoordinates();
// TODO: Find a SMILES that goes through the random branch of coordinate invention.

Check warning on line 83 in __tests__/SmilesParser.js

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: Find a SMILES that goes through...'
// expect(coords1).not.toBe(coords2);
expect(coords1).toBe(coords2);
parser.setRandomSeed(1);
}
pasteMolecule() {
// TODO: find a way to implement this in a synchronous way.

Check warning on line 10 in lib/canvas_editor/clipboard_handler.js

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: find a way to implement this in a...'
return null;
}
}
function fireMouseEvent(what, ev, clickCount = 0) {
if (ev.button > 0) {
// TODO: remove this to implement popup menu.

Check warning on line 14 in lib/canvas_editor/events.js

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: remove this to implement popup...'
return;
}
drawArea.fireMouseEvent(
}
showHelpDialog(/* url, title */) {
// TODO: implement help dialog?

Check warning on line 28 in lib/canvas_editor/ui_helper.js

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: implement help dialog?'
// console.log({ url, title });
}
}
function changeGenericEditorArea(code) {
// TODO: find replacements

Check warning on line 333 in scripts/openchemlib/classes.js

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: find replacements'
code = code.replaceAll(
methodRegExp('showWarningMessage'),
'private void showWarningMessage(String msg) {}',