u2ui 1.5.3 → 1.5.7
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.
- package/el/ico/ico.js +5 -5
- package/el/rte/README.md +53 -13
- package/el/rte/manifest.json +19 -4
- package/el/rte/rte.css +31 -6
- package/el/rte/rte.js +174 -44
- package/el/rte0/README.md +37 -0
- package/el/rte0/manifest.json +33 -0
- package/el/rte0/rte0.css +9 -0
- package/el/rte0/rte0.js +56 -0
- package/el/system/styler.js +2 -0
- package/el/tooltip/tooltip.css +2 -2
- package/js/Placer/Placer.js +2 -1
- package/js/rte/AGENTS.md +142 -0
- package/js/rte/PLAN.md +487 -0
- package/js/rte/README.md +319 -25
- package/js/rte/blocks.js +7 -0
- package/js/rte/breaks.js +6 -0
- package/js/rte/classes.js +7 -0
- package/js/rte/docs/usage.md +845 -0
- package/js/rte/images.js +7 -0
- package/js/rte/link.js +8 -0
- package/js/rte/playground/README.md +95 -0
- package/js/rte/playground/hosts.html +553 -0
- package/js/rte/playground/index.html +835 -0
- package/js/rte/rte.js +56 -5
- package/js/rte/run.out +17 -0
- package/js/rte/source.js +12 -0
- package/js/rte/src/browser/README.md +40 -0
- package/js/rte/src/browser/interactive.js +17 -0
- package/js/rte/src/browser/range-rect.js +82 -0
- package/js/rte/src/client/README.md +385 -0
- package/js/rte/src/client/blocks.js +38 -0
- package/js/rte/src/client/breaks.js +212 -0
- package/js/rte/src/client/classes.js +48 -0
- package/js/rte/src/client/editor.js +472 -0
- package/js/rte/src/client/external-input.js +47 -0
- package/js/rte/src/client/history.js +13 -0
- package/js/rte/src/client/images.js +285 -0
- package/js/rte/src/client/link.js +567 -0
- package/js/rte/src/client/marks.js +25 -0
- package/js/rte/src/client/source.js +182 -0
- package/js/rte/src/client/structure.js +27 -0
- package/js/rte/src/client/tables.js +165 -0
- package/js/rte/src/client/unstyle.js +32 -0
- package/js/rte/src/command/README.md +520 -0
- package/js/rte/src/command/block-boundary.js +43 -0
- package/js/rte/src/command/block-style.js +120 -0
- package/js/rte/src/command/commands.js +159 -0
- package/js/rte/src/command/delete.js +119 -0
- package/js/rte/src/command/edit.js +78 -0
- package/js/rte/src/command/element.js +45 -0
- package/js/rte/src/command/enter.js +175 -0
- package/js/rte/src/command/fragment.js +76 -0
- package/js/rte/src/command/insert.js +52 -0
- package/js/rte/src/command/list.js +284 -0
- package/js/rte/src/command/mark.js +583 -0
- package/js/rte/src/command/pending-marks.js +216 -0
- package/js/rte/src/command/table.js +207 -0
- package/js/rte/src/command/unstyle.js +248 -0
- package/js/rte/src/config/README.md +96 -0
- package/js/rte/src/config/config.js +134 -0
- package/js/rte/src/core/README.md +63 -0
- package/js/rte/src/core/core.js +247 -0
- package/js/rte/src/history/README.md +83 -0
- package/js/rte/src/history/history.js +283 -0
- package/js/rte/src/input/README.md +173 -0
- package/js/rte/src/input/external-input.js +130 -0
- package/js/rte/src/input/input-pipeline.js +459 -0
- package/js/rte/src/mark/README.md +219 -0
- package/js/rte/src/mark/dom-adapter.js +100 -0
- package/js/rte/src/mark/mark.js +133 -0
- package/js/rte/src/mark/standard.js +76 -0
- package/js/rte/src/model/README.md +71 -0
- package/js/rte/src/model/content-model.js +213 -0
- package/js/rte/src/model/html/README.md +51 -0
- package/js/rte/src/model/html/html-model.js +90 -0
- package/js/rte/src/normalize/execute/README.md +33 -0
- package/js/rte/src/normalize/execute/repair-executor.js +129 -0
- package/js/rte/src/normalize/normalizer/README.md +48 -0
- package/js/rte/src/normalize/normalizer/normalizer.js +141 -0
- package/js/rte/src/normalize/repair/README.md +70 -0
- package/js/rte/src/normalize/repair/repair-planner.js +208 -0
- package/js/rte/src/sanitize/README.md +108 -0
- package/js/rte/src/sanitize/native.js +43 -0
- package/js/rte/src/sanitize/policy.js +206 -0
- package/js/rte/src/selection/README.md +39 -0
- package/js/rte/src/selection/map/README.md +53 -0
- package/js/rte/src/selection/map/point-map.js +307 -0
- package/js/rte/src/selection/ownership/README.md +32 -0
- package/js/rte/src/selection/ownership/ownership.js +42 -0
- package/js/rte/src/selection/point/README.md +34 -0
- package/js/rte/src/selection/point/point.js +83 -0
- package/js/rte/src/selection/range/README.md +38 -0
- package/js/rte/src/selection/range/edit-range.js +223 -0
- package/js/rte/src/selection/snapshot.js +64 -0
- package/js/rte/src/source/README.md +64 -0
- package/js/rte/src/source/source.js +193 -0
- package/js/rte/src/surface/README.md +36 -0
- package/js/rte/src/surface/content.js +16 -0
- package/js/rte/src/surface/surface.js +87 -0
- package/js/rte/src/transaction/README.md +35 -0
- package/js/rte/src/transaction/transaction.js +56 -0
- package/js/rte/src/ui/README.md +271 -0
- package/js/rte/src/ui/caret.js +17 -0
- package/js/rte/src/ui/chrome.js +214 -0
- package/js/rte/src/ui/handles.js +179 -0
- package/js/rte/src/ui/highlight.js +86 -0
- package/js/rte/src/ui/place.js +25 -0
- package/js/rte/src/ui/toolbar.js +258 -0
- package/js/rte/src/unstyle/README.md +103 -0
- package/js/rte/src/unstyle/unstyle.js +140 -0
- package/js/rte/tables.js +7 -0
- package/js/rte/unstyle.js +8 -0
- package/js/rte0/README.md +37 -0
- package/js/rte0/rte.js +7 -0
- package/js/{rte → rte0}/src/behavior.js +0 -1
- package/js/{rte → rte0}/src/events.js +0 -1
- package/js/{rte → rte0}/src/range.js +0 -2
- package/js/{rte → rte0}/src/ui.items.js +0 -2
- package/js/{rte → rte0}/src/ui.js +0 -1
- package/package.json +1 -1
- package/u2/Element.js +2 -2
- package/u2/plan-scoped-refactoring.md +133 -303
- package/u2/projects.json +18 -2
- package/u2/tools/update.repos.json.js +1 -1
- package/el/qrcode/package.json +0 -22
- /package/js/{rte → rte0}/old/NodeCleaner.js +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.items.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/crossbrowser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.svg +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.woff +0 -0
- /package/js/{rte → rte0}/old/Rte/helpers.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/htmlparser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/index.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/main.css +0 -0
- /package/js/{rte → rte0}/old/old.rte.js +0 -0
- /package/js/{rte → rte0}/old/range-selection.old.js +0 -0
- /package/js/{rte → rte0}/src/fixes.js +0 -0
- /package/js/{rte → rte0}/src/rte.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/ai.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/code.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/undo.js +0 -0
- /package/js/{rte → rte0}/src/util/htmlparser.js +0 -0
- /package/js/{rte → rte0}/src/util/indentHtml.js +0 -0
package/el/ico/ico.js
CHANGED
|
@@ -61,14 +61,14 @@ export default class U2Ico extends HTMLElement {
|
|
|
61
61
|
const name = this.getAttribute('icon');
|
|
62
62
|
const path = dirTemplateToUrl(dir, name);
|
|
63
63
|
|
|
64
|
-
requestAnimationFrame(()=>{
|
|
65
|
-
this.setAttribute('state','loading')
|
|
66
|
-
});
|
|
67
|
-
|
|
68
64
|
const pathStr = path.toString();
|
|
69
65
|
if (this._lastLoading === pathStr) return; // prevent multiple requests
|
|
70
66
|
this._lastLoading = pathStr;
|
|
71
67
|
|
|
68
|
+
requestAnimationFrame(()=>{
|
|
69
|
+
this.setAttribute('state','loading')
|
|
70
|
+
});
|
|
71
|
+
|
|
72
72
|
loadSvgString(path).then(svg=>{
|
|
73
73
|
// if (path.origin !== location.origin) {} todo: sanitize svg
|
|
74
74
|
|
|
@@ -85,7 +85,7 @@ export default class U2Ico extends HTMLElement {
|
|
|
85
85
|
const height = parseFloat(svgEl.getAttribute('height')) ?? 24;
|
|
86
86
|
svgEl.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
|
|
87
87
|
}
|
|
88
|
-
['xmlns','xmlns:xlink','version','class'
|
|
88
|
+
['xmlns','xmlns:xlink','version','class'].forEach(attr=>svgEl.removeAttribute(attr));
|
|
89
89
|
svgEl.ariaHidden = true;
|
|
90
90
|
this.setAttribute('state','loaded');
|
|
91
91
|
this.dispatchEvent(new CustomEvent('u2-ico-loaded', {bubbles: true, composed: true}));
|
package/el/rte/README.md
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
# <u2-rte> - element
|
|
2
|
-
|
|
2
|
+
Progressively enhances one native textarea with the RTE rich-text editor
|
|
3
|
+
|
|
4
|
+
The textarea remains the form control, fallback, and canonical serialized value.
|
|
3
5
|
|
|
4
6
|
## Usage
|
|
5
7
|
|
|
6
8
|
```html
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
```css
|
|
15
|
-
u2-rte {
|
|
16
|
-
--u2-rte:true;
|
|
17
|
-
--u2-rte-toolbar:Bold Italic;
|
|
18
|
-
}
|
|
9
|
+
<form>
|
|
10
|
+
<u2-rte style="--u2-rte-toolbar:bold italic">
|
|
11
|
+
<textarea name=content><p>Some <strong>HTML</strong></p></textarea>
|
|
12
|
+
</u2-rte>
|
|
13
|
+
</form>
|
|
19
14
|
```
|
|
20
15
|
|
|
21
16
|
## Install
|
|
@@ -30,6 +25,51 @@ u2-rte {
|
|
|
30
25
|
[test.html](http://gcdn.li/u2ui/u2@main/el/rte/tests/test.html)
|
|
31
26
|
[minimal.html](http://gcdn.li/u2ui/u2@main/el/rte/tests/minimal.html)
|
|
32
27
|
|
|
28
|
+
## Behavior
|
|
29
|
+
|
|
30
|
+
HTML is the default language. Markdown loads its converters only when used:
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<u2-rte language="markdown">
|
|
34
|
+
<textarea name="content">Some **Markdown**</textarea>
|
|
35
|
+
</u2-rte>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Exactly one direct `textarea` is required. Free child content is deliberately
|
|
39
|
+
not supported. Put form attributes such as `name`, `required`, `disabled`,
|
|
40
|
+
`readonly`, `placeholder`, and `form` on the textarea.
|
|
41
|
+
|
|
42
|
+
The `value` property reads and writes the serialized textarea value. Editor
|
|
43
|
+
changes update it before a bubbling `input` event is emitted from the textarea.
|
|
44
|
+
A bubbling `change` event follows when focus leaves an editor changed since its
|
|
45
|
+
last change event. Form reset, disabled, readonly, placeholder, direction,
|
|
46
|
+
language, and spellchecking state are mirrored back to the editor.
|
|
47
|
+
|
|
48
|
+
## Languages
|
|
49
|
+
|
|
50
|
+
- `html` (default) stores sanitized HTML.
|
|
51
|
+
- `markdown` parses with Marked and serializes with Turndown. The dependencies
|
|
52
|
+
are loaded lazily from jsDelivr.
|
|
53
|
+
|
|
54
|
+
Other formats need a proven round-trip between their source text and the HTML
|
|
55
|
+
DOM edited by RTE. They should not be added as aliases or partial parsers.
|
|
56
|
+
|
|
57
|
+
## TODO
|
|
58
|
+
|
|
59
|
+
- Add `el/rte` to U2's generated project catalog when the module is ready for
|
|
60
|
+
automatic loading. It currently requires the explicit module import shown
|
|
61
|
+
above because that catalog lives outside this module.
|
|
62
|
+
- RTE needs a shared root-aware convention-client entry point before this
|
|
63
|
+
adapter can support editors placed inside an application ShadowRoot. The
|
|
64
|
+
current public convenience client owns the document root only.
|
|
65
|
+
- RTE should report whether a committed change already has a corresponding
|
|
66
|
+
native `input` event. The adapter currently uses the transaction trigger to
|
|
67
|
+
avoid emitting duplicate input events.
|
|
68
|
+
- Complete label, constraint-validation, selection, and focus tests for the
|
|
69
|
+
visually hidden textarea across current Chromium, Firefox, and WebKit.
|
|
70
|
+
- Allow applications to provide language codecs without turning the initial
|
|
71
|
+
two built-in formats into a general plugin system prematurely.
|
|
72
|
+
|
|
33
73
|
## About
|
|
34
74
|
|
|
35
75
|
- MIT License, Copyright (c) 2022 <u2> (like all repositories in this organization) <br>
|
package/el/rte/manifest.json
CHANGED
|
@@ -6,11 +6,26 @@
|
|
|
6
6
|
"path": "rte.js",
|
|
7
7
|
"declarations": [{
|
|
8
8
|
"kind": "class",
|
|
9
|
-
"description": "
|
|
9
|
+
"description": "Progressively enhances a textarea with the RTE2 rich-text editor",
|
|
10
10
|
"name": "U2Rte",
|
|
11
|
-
"members": [
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
"members": [{
|
|
12
|
+
"kind": "field",
|
|
13
|
+
"name": "value",
|
|
14
|
+
"description": "The serialized textarea value"
|
|
15
|
+
}],
|
|
16
|
+
"events": [{
|
|
17
|
+
"name": "input",
|
|
18
|
+
"description": "Fired from the textarea after the editor value changes"
|
|
19
|
+
},{
|
|
20
|
+
"name": "change",
|
|
21
|
+
"description": "Fired from the textarea when focus leaves a changed editor"
|
|
22
|
+
}],
|
|
23
|
+
"attributes": [{
|
|
24
|
+
"name": "language",
|
|
25
|
+
"description": "Serialization language",
|
|
26
|
+
"u2-type": "string",
|
|
27
|
+
"u2Examples": ["html", "markdown"]
|
|
28
|
+
}],
|
|
14
29
|
"cssProperties": [{
|
|
15
30
|
"name": "--u2-rte-toolbar",
|
|
16
31
|
"description": "Space separated list of toolbar commands",
|
package/el/rte/rte.css
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
u2-rte {
|
|
2
|
-
display:block;
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
display: block;
|
|
3
|
+
position: relative;
|
|
4
|
+
border: 1px solid var(--color-line, color-mix(in srgb, currentColor, transparent 75%));
|
|
5
|
+
|
|
6
|
+
& > textarea {
|
|
7
|
+
display: block;
|
|
8
|
+
inline-size: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&[data-ready] > textarea {
|
|
12
|
+
position: absolute;
|
|
13
|
+
inline-size: 1px;
|
|
14
|
+
block-size: 1px;
|
|
15
|
+
margin: 0 !important;
|
|
16
|
+
padding: 0;
|
|
17
|
+
border: 0;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
clip-path: inset(50%);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
& > [data-u2-rte-editor] {
|
|
23
|
+
min-block-size: 6em;
|
|
24
|
+
padding: .5em;
|
|
25
|
+
outline: 0;
|
|
26
|
+
--u2-rte: true;
|
|
5
27
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
28
|
+
&:empty::before {
|
|
29
|
+
content: attr(data-placeholder);
|
|
30
|
+
color: color-mix(in srgb, currentColor, transparent 45%);
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
9
34
|
}
|
package/el/rte/rte.js
CHANGED
|
@@ -1,56 +1,186 @@
|
|
|
1
|
+
import {Source, editor} from '../../js/rte/rte.js';
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
const codecs = {
|
|
4
|
+
html: () => ({
|
|
5
|
+
parse: value => value,
|
|
6
|
+
serialize: value => value,
|
|
7
|
+
}),
|
|
8
|
+
markdown: async () => {
|
|
9
|
+
const [{parse}, {default: TurndownService}] = await Promise.all([
|
|
10
|
+
// deno-lint-ignore no-import-prefix
|
|
11
|
+
import('https://cdn.jsdelivr.net/npm/marked@16.2.1/+esm'),
|
|
12
|
+
// deno-lint-ignore no-import-prefix
|
|
13
|
+
import('https://cdn.jsdelivr.net/npm/turndown@7.2.0/+esm'),
|
|
14
|
+
]);
|
|
15
|
+
const turndown = new TurndownService({headingStyle: 'atx'});
|
|
16
|
+
return {
|
|
17
|
+
parse: value => parse(value),
|
|
18
|
+
serialize: value => turndown.turndown(value),
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
3
22
|
|
|
4
23
|
export default class U2Rte extends HTMLElement {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
#codec;
|
|
25
|
+
#source;
|
|
26
|
+
#observer;
|
|
27
|
+
#form;
|
|
28
|
+
#tabindex;
|
|
29
|
+
#dirty = false;
|
|
30
|
+
#writing = false;
|
|
31
|
+
#version = 0;
|
|
32
|
+
|
|
33
|
+
connectedCallback() {
|
|
34
|
+
const textarea = this.querySelector(':scope > textarea:only-child');
|
|
35
|
+
if (!textarea || this.children.length !== 1) {
|
|
36
|
+
console.error('u2-rte requires exactly one direct textarea');
|
|
37
|
+
return;
|
|
17
38
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
//this.setAttribute('contenteditable', true);
|
|
22
|
-
this.editor = this.shadowRoot.getElementById('editor');
|
|
23
|
-
|
|
24
|
-
this.editor.addEventListener('input', (event)=>{
|
|
25
|
-
const html = this.editor.innerHTML;
|
|
26
|
-
const value = html;
|
|
27
|
-
if (this.textarea) {
|
|
28
|
-
this.textarea.value = value;
|
|
29
|
-
} else {
|
|
30
|
-
this.innerHTML = value;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
39
|
+
this.textarea = textarea;
|
|
40
|
+
this.#init(++this.#version);
|
|
41
|
+
}
|
|
33
42
|
|
|
43
|
+
disconnectedCallback() {
|
|
44
|
+
this.#version++;
|
|
45
|
+
this.#dispose();
|
|
34
46
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
|
|
48
|
+
get language() { return this.getAttribute('language') || 'html'; }
|
|
49
|
+
get value() { return this.textarea?.value ?? ''; }
|
|
50
|
+
set value(value) {
|
|
51
|
+
if (!this.textarea) return;
|
|
52
|
+
this.textarea.value = String(value);
|
|
53
|
+
if (this.#source) this.#write(this.textarea.value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async #init(version) {
|
|
57
|
+
const load = codecs[this.language];
|
|
58
|
+
if (!load) {
|
|
59
|
+
console.error(`u2-rte: Unsupported language: ${this.language}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const codec = await load();
|
|
64
|
+
if (!this.isConnected || version !== this.#version) return;
|
|
65
|
+
this.#codec = codec;
|
|
66
|
+
this.editor = document.createElement('div');
|
|
67
|
+
this.editor.contentEditable = 'true';
|
|
68
|
+
this.editor.dataset.u2RteEditor = '';
|
|
69
|
+
this.editor.role = 'textbox';
|
|
70
|
+
this.editor.ariaMultiLine = 'true';
|
|
71
|
+
this.append(this.editor);
|
|
72
|
+
this.surface = editor.core.add(this.editor);
|
|
73
|
+
this.#source = new Source(this.surface);
|
|
74
|
+
this.#write(this.textarea.value);
|
|
75
|
+
this.editor.addEventListener('input', this.#input);
|
|
76
|
+
this.editor.addEventListener('focusout', this.#focusout);
|
|
77
|
+
this.editor.addEventListener('u2-rte-change', this.#change);
|
|
78
|
+
this.textarea.addEventListener('focus', this.#focus);
|
|
79
|
+
this.textarea.addEventListener('invalid', this.#focus);
|
|
80
|
+
this.#form = this.textarea.form;
|
|
81
|
+
this.#form?.addEventListener('reset', this.#reset);
|
|
82
|
+
this.#tabindex = this.textarea.getAttribute('tabindex');
|
|
83
|
+
this.textarea.tabIndex = -1;
|
|
84
|
+
this.#observer = new MutationObserver(this.#mutate);
|
|
85
|
+
this.#observer.observe(this.editor, {subtree: true, childList: true, characterData: true, attributes: true});
|
|
86
|
+
this.#observer.observe(this.textarea, {attributes: true});
|
|
87
|
+
this.#state();
|
|
88
|
+
this.dataset.ready = '';
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.#dispose();
|
|
91
|
+
console.error('u2-rte:', error);
|
|
38
92
|
}
|
|
39
|
-
let sourceValue = this.textarea ? this.textarea.value : this.innerHTML;
|
|
40
|
-
this.editor.innerHTML = sourceValue;
|
|
41
|
-
|
|
42
|
-
this._internals = this.attachInternals();
|
|
43
|
-
this._internals.role = 'textbox';
|
|
44
93
|
}
|
|
45
|
-
}
|
|
46
94
|
|
|
47
|
-
|
|
95
|
+
#write(value) {
|
|
96
|
+
this.#writing = true;
|
|
97
|
+
try {
|
|
98
|
+
this.#source.write(this.#codec.parse(value));
|
|
99
|
+
} finally {
|
|
100
|
+
this.#writing = false;
|
|
101
|
+
}
|
|
102
|
+
this.#sync();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#sync() {
|
|
106
|
+
const html = this.#source.read().html;
|
|
107
|
+
this.textarea.value = this.#codec.serialize(html);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#emitInput(event) {
|
|
111
|
+
this.#sync();
|
|
112
|
+
this.#dirty = true;
|
|
113
|
+
this.textarea.dispatchEvent(new InputEvent('input', {
|
|
114
|
+
bubbles: true,
|
|
115
|
+
composed: true,
|
|
116
|
+
data: event?.data ?? null,
|
|
117
|
+
inputType: event?.inputType || '',
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#input = event => {
|
|
122
|
+
event.stopPropagation();
|
|
123
|
+
this.#emitInput(event);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
#change = event => {
|
|
127
|
+
if (this.#writing) return;
|
|
128
|
+
this.#sync();
|
|
129
|
+
if (event.detail.transaction.options.trigger !== 'input') this.#emitInput();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
#focusout = () => queueMicrotask(() => {
|
|
133
|
+
if (!this.#dirty || this.editor.matches(':focus-within')) return;
|
|
134
|
+
this.#dirty = false;
|
|
135
|
+
this.textarea.dispatchEvent(new Event('change', {bubbles: true}));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
#focus = event => {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
this.editor.focus();
|
|
141
|
+
};
|
|
48
142
|
|
|
143
|
+
#reset = () => queueMicrotask(() => this.#write(this.textarea.value));
|
|
49
144
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
145
|
+
#mutate = records => {
|
|
146
|
+
if (records.some(record => record.target === this.textarea)) this.#state();
|
|
147
|
+
if (records.some(record => record.target !== this.textarea)) this.#sync();
|
|
148
|
+
};
|
|
54
149
|
|
|
55
|
-
|
|
56
|
-
|
|
150
|
+
#state() {
|
|
151
|
+
const inactive = this.textarea.disabled || this.textarea.readOnly;
|
|
152
|
+
this.editor.contentEditable = inactive ? 'false' : 'true';
|
|
153
|
+
this.editor.ariaDisabled = this.textarea.disabled ? 'true' : null;
|
|
154
|
+
this.editor.ariaReadOnly = this.textarea.readOnly ? 'true' : null;
|
|
155
|
+
this.editor.dataset.placeholder = this.textarea.placeholder;
|
|
156
|
+
for (const name of ['dir', 'lang', 'spellcheck']) {
|
|
157
|
+
const value = this.textarea.getAttribute(name);
|
|
158
|
+
if (value === null) this.editor.removeAttribute(name);
|
|
159
|
+
else this.editor.setAttribute(name, value);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#dispose() {
|
|
164
|
+
this.#observer?.disconnect();
|
|
165
|
+
this.#form?.removeEventListener('reset', this.#reset);
|
|
166
|
+
this.textarea?.removeEventListener('focus', this.#focus);
|
|
167
|
+
this.textarea?.removeEventListener('invalid', this.#focus);
|
|
168
|
+
this.editor?.removeEventListener('input', this.#input);
|
|
169
|
+
this.editor?.removeEventListener('focusout', this.#focusout);
|
|
170
|
+
this.editor?.removeEventListener('u2-rte-change', this.#change);
|
|
171
|
+
this.surface?.dispose();
|
|
172
|
+
this.editor?.remove();
|
|
173
|
+
if (this.#tabindex === null) this.textarea?.removeAttribute('tabindex');
|
|
174
|
+
else if (this.#tabindex !== undefined) this.textarea?.setAttribute('tabindex', this.#tabindex);
|
|
175
|
+
this.removeAttribute('data-ready');
|
|
176
|
+
this.#observer = null;
|
|
177
|
+
this.#form = null;
|
|
178
|
+
this.#source = null;
|
|
179
|
+
this.#tabindex = undefined;
|
|
180
|
+
this.#dirty = false;
|
|
181
|
+
this.surface = null;
|
|
182
|
+
this.editor = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
customElements.get('u2-rte') || customElements.define('u2-rte', U2Rte);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# <u2-rte0> - element
|
|
2
|
+
"Rich" Rich Text Editor (alpha)
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<u2-rte0 style="--u2-rte-toolbar:Bold Italic">
|
|
8
|
+
<strong>RTE</strong>
|
|
9
|
+
<br>
|
|
10
|
+
<i>(Rich Text Editor)</i>
|
|
11
|
+
</u2-rte0>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```css
|
|
15
|
+
u2-rte0 {
|
|
16
|
+
--u2-rte:true;
|
|
17
|
+
--u2-rte-toolbar:Bold Italic;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<link href="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/rte0/rte0.css" rel=stylesheet>
|
|
25
|
+
<script src="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/rte0/rte0.js" type=module async></script>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Demos
|
|
29
|
+
|
|
30
|
+
[test.html](http://gcdn.li/u2ui/u2@main/el/rte0/tests/test.html)
|
|
31
|
+
[minimal.html](http://gcdn.li/u2ui/u2@main/el/rte0/tests/minimal.html)
|
|
32
|
+
|
|
33
|
+
## About
|
|
34
|
+
|
|
35
|
+
- MIT License, Copyright (c) 2022 <u2> (like all repositories in this organization) <br>
|
|
36
|
+
- Suggestions, ideas, finding bugs and making pull requests make us very happy. ♥
|
|
37
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"readme": "README.md",
|
|
4
|
+
"modules": [{
|
|
5
|
+
"kind": "javascript-module",
|
|
6
|
+
"path": "rte0.js",
|
|
7
|
+
"declarations": [{
|
|
8
|
+
"kind": "class",
|
|
9
|
+
"description": "\"Rich\" Rich Text Editor (alpha)",
|
|
10
|
+
"name": "U2Rte0",
|
|
11
|
+
"members": [],
|
|
12
|
+
"events": [],
|
|
13
|
+
"attributes": [],
|
|
14
|
+
"cssProperties": [{
|
|
15
|
+
"name": "--u2-rte-toolbar",
|
|
16
|
+
"description": "Space separated list of toolbar commands",
|
|
17
|
+
"syntax": "*"
|
|
18
|
+
},{
|
|
19
|
+
"name": "--u2-rte",
|
|
20
|
+
"description": "Marks regions as rich-text editable",
|
|
21
|
+
"syntax": "*"
|
|
22
|
+
}],
|
|
23
|
+
"superclass": { "name": "HTMLElement" },
|
|
24
|
+
"tagName": "u2-rte0",
|
|
25
|
+
"customElement": true
|
|
26
|
+
}],
|
|
27
|
+
"exports": [{
|
|
28
|
+
"kind": "custom-element-definition",
|
|
29
|
+
"name": "u2-rte0",
|
|
30
|
+
"declaration": { "name": "U2Rte0", "module": "rte0.js" }
|
|
31
|
+
}]
|
|
32
|
+
}]
|
|
33
|
+
}
|
package/el/rte0/rte0.css
ADDED
package/el/rte0/rte0.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
import '../../js/rte0/rte.js';
|
|
3
|
+
|
|
4
|
+
export default class U2Rte0 extends HTMLElement {
|
|
5
|
+
constructor() {
|
|
6
|
+
super();
|
|
7
|
+
let shadowRoot = this.attachShadow({mode:'open'});
|
|
8
|
+
shadowRoot.innerHTML =
|
|
9
|
+
`<style>
|
|
10
|
+
:host {
|
|
11
|
+
display:block;
|
|
12
|
+
--u2-rte:true;
|
|
13
|
+
}
|
|
14
|
+
#editor {
|
|
15
|
+
--u2-rte:true;
|
|
16
|
+
padding:1px; /* to avoid margin collapse */
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
<div id=editor contenteditable></div>
|
|
20
|
+
`;
|
|
21
|
+
//this.setAttribute('contenteditable', true);
|
|
22
|
+
this.editor = this.shadowRoot.getElementById('editor');
|
|
23
|
+
|
|
24
|
+
this.editor.addEventListener('input', (event)=>{
|
|
25
|
+
const html = this.editor.innerHTML;
|
|
26
|
+
const value = html;
|
|
27
|
+
if (this.textarea) {
|
|
28
|
+
this.textarea.value = value;
|
|
29
|
+
} else {
|
|
30
|
+
this.innerHTML = value;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
if (this.children.length === 1 && this.children[0].tagName === 'TEXTAREA') {
|
|
37
|
+
this.textarea = this.children[0];
|
|
38
|
+
}
|
|
39
|
+
let sourceValue = this.textarea ? this.textarea.value : this.innerHTML;
|
|
40
|
+
this.editor.innerHTML = sourceValue;
|
|
41
|
+
|
|
42
|
+
this._internals = this.attachInternals();
|
|
43
|
+
this._internals.role = 'textbox';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
customElements.get('u2-rte0') || customElements.define('u2-rte0', U2Rte0);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// import Showdown from 'https://cdn.skypack.dev/showdown';
|
|
51
|
+
// const converter = new Showdown.Converter();
|
|
52
|
+
// converter.setOption('tables', true);
|
|
53
|
+
// this.editor.innerHTML = converter.makeHtml(sourceValue);
|
|
54
|
+
|
|
55
|
+
// import TurndownService from 'https://cdn.skypack.dev/turndown@7.1.1';
|
|
56
|
+
// value = new TurndownService().turndown(html);
|
package/el/system/styler.js
CHANGED
|
@@ -597,6 +597,7 @@ function parseCssLength(str) {
|
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
function unitToPx(value, unit, el){
|
|
600
|
+
if (!unit) return value;
|
|
600
601
|
if (unit === 'px') return value;
|
|
601
602
|
if (unit === 'cm') return value * 96 / 2.54;
|
|
602
603
|
if (unit === 'mm') return value * 96 / 25.4;
|
|
@@ -627,6 +628,7 @@ function unitToPx(value, unit, el){
|
|
|
627
628
|
}
|
|
628
629
|
|
|
629
630
|
function pxToUnit(px, unit, el){
|
|
631
|
+
if (!unit) return px;
|
|
630
632
|
if (unit === 'px') return px;
|
|
631
633
|
if (unit === 'cm') return px * 2.54 / 96;
|
|
632
634
|
if (unit === 'mm') return px * 25.4 / 96;
|
package/el/tooltip/tooltip.css
CHANGED
|
@@ -3,7 +3,7 @@ u2-tooltip {
|
|
|
3
3
|
/* relevant */
|
|
4
4
|
opacity: 0;
|
|
5
5
|
visibility:hidden;
|
|
6
|
-
position: absolute;
|
|
6
|
+
position: absolute; /* keep its document position while scrolling */
|
|
7
7
|
left: -9999px;
|
|
8
8
|
top: -9999px;
|
|
9
9
|
z-index: 9999;
|
|
@@ -97,4 +97,4 @@ u2-tooltip {
|
|
|
97
97
|
top: var(--halfMinus);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
/* */
|
|
100
|
+
/* */
|
package/js/Placer/Placer.js
CHANGED
|
@@ -52,7 +52,8 @@ export class Placer {
|
|
|
52
52
|
|
|
53
53
|
let position = getComputedStyle(this.el).getPropertyValue('position');
|
|
54
54
|
if (position !== 'fixed') {
|
|
55
|
-
|
|
55
|
+
// Popovers use the top-layer containing block regardless of their DOM ancestry.
|
|
56
|
+
let root = this.el.popover ? null : offsetParent(this.el);
|
|
56
57
|
if (root) {
|
|
57
58
|
// if (!root) root = document.documentElement; // make no sense
|
|
58
59
|
viewport = root.getBoundingClientRect();
|