lexxy 0.9.9.beta → 0.9.9.beta.preview1
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.
- checksums.yaml +4 -4
- data/app/assets/javascript/lexxy.js +59 -10
- data/app/assets/javascript/lexxy.js.br +0 -0
- data/app/assets/javascript/lexxy.js.gz +0 -0
- data/app/assets/javascript/lexxy.js.map +1 -1
- data/app/assets/javascript/lexxy.min.js +1 -1
- data/app/assets/javascript/lexxy.min.js.br +0 -0
- data/app/assets/javascript/lexxy.min.js.gz +0 -0
- data/app/assets/stylesheets/lexxy-editor.css +7 -0
- data/lib/lexxy/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be0c22bb945fd56429ced73c583c28bcd0e6caf5ae885d666587ff329e1785cc
|
|
4
|
+
data.tar.gz: ab43a2841e18c6a0b44b788197f6f22f7653584f43214216ba10f875fda46e87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e719a3e55ed0758b455c9ec191aa669b7f467eb7a55479f34d5462cd743d3391e62d93e74f2177074671a1bee82201db96737bd282b9bb675cb29bf99badb8b8
|
|
7
|
+
data.tar.gz: '04495b4335b01e4992b475977566dd7b3065991e3e8584b717306dcd0701754a1242faf0a92261fb6ee2735f18f6e1058e92cb5947feec44ef7675f6d842508a'
|
|
@@ -7847,6 +7847,35 @@ function isAttachmentSpacerTextNode(node, previousNode, index, childCount) {
|
|
|
7847
7847
|
&& previousNode instanceof CustomActionTextAttachmentNode
|
|
7848
7848
|
}
|
|
7849
7849
|
|
|
7850
|
+
// Shared, strictly-contained element used to attach ephemeral nodes when we
|
|
7851
|
+
// need to read computed styles (e.g. canonicalizing style values, resolving
|
|
7852
|
+
// CSS custom properties). The container is created once and attached to
|
|
7853
|
+
// `document.body` once; subsequent child mutations happen *inside* the
|
|
7854
|
+
// contained subtree so they do not invalidate style on the rest of the page.
|
|
7855
|
+
//
|
|
7856
|
+
// Without this, `document.body.appendChild(...)` / `element.remove()` calls
|
|
7857
|
+
// forced the browser to re-evaluate every ancestor-dependent selector (`:has()`,
|
|
7858
|
+
// descendant combinators, universal sibling rules) across the document on each
|
|
7859
|
+
// invocation — a 13,000+ element style recalc per call on a typical Basecamp
|
|
7860
|
+
// page.
|
|
7861
|
+
|
|
7862
|
+
let resolverRoot = null;
|
|
7863
|
+
|
|
7864
|
+
function styleResolverRoot() {
|
|
7865
|
+
if (resolverRoot && resolverRoot.isConnected) return resolverRoot
|
|
7866
|
+
|
|
7867
|
+
resolverRoot = document.createElement("div");
|
|
7868
|
+
resolverRoot.setAttribute("aria-hidden", "true");
|
|
7869
|
+
resolverRoot.setAttribute("data-lexxy-style-resolver", "");
|
|
7870
|
+
// `contain: strict` (size, layout, paint, style) isolates everything.
|
|
7871
|
+
// The root itself paints nothing (visibility hidden), has zero
|
|
7872
|
+
// geometric impact (position fixed, intrinsic size via contain), and
|
|
7873
|
+
// never leaks style invalidation to its ancestors.
|
|
7874
|
+
resolverRoot.style.cssText = "contain: strict; position: fixed; top: 0; left: 0; visibility: hidden; pointer-events: none; width: 0; height: 0;";
|
|
7875
|
+
document.body.appendChild(resolverRoot);
|
|
7876
|
+
return resolverRoot
|
|
7877
|
+
}
|
|
7878
|
+
|
|
7850
7879
|
function isSelectionHighlighted(selection) {
|
|
7851
7880
|
if (!wr(selection)) return false
|
|
7852
7881
|
|
|
@@ -7927,10 +7956,11 @@ class StyleCanonicalizer {
|
|
|
7927
7956
|
}
|
|
7928
7957
|
}
|
|
7929
7958
|
|
|
7930
|
-
// Separates DOM writes from layout reads to avoid forced reflows
|
|
7931
|
-
// elements
|
|
7932
|
-
//
|
|
7933
|
-
//
|
|
7959
|
+
// Separates DOM writes from layout reads to avoid forced reflows, and attaches
|
|
7960
|
+
// resolver elements to a strictly-contained root (outside the normal document
|
|
7961
|
+
// flow) so neither the attach nor the detach invalidate styles on the rest of
|
|
7962
|
+
// the page. Without containment, appending to `document.body` triggered a
|
|
7963
|
+
// page-wide style recalc on every canonicalization pass.
|
|
7934
7964
|
function computeStyleValues(property, values) {
|
|
7935
7965
|
const fragment = document.createDocumentFragment();
|
|
7936
7966
|
|
|
@@ -7940,7 +7970,7 @@ function computeStyleValues(property, values) {
|
|
|
7940
7970
|
return element
|
|
7941
7971
|
});
|
|
7942
7972
|
|
|
7943
|
-
|
|
7973
|
+
styleResolverRoot().appendChild(fragment);
|
|
7944
7974
|
|
|
7945
7975
|
const computed = elements.map(element =>
|
|
7946
7976
|
window.getComputedStyle(element).getPropertyValue(property)
|
|
@@ -13099,6 +13129,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
13099
13129
|
static observedAttributes = [ "connected", "required" ]
|
|
13100
13130
|
|
|
13101
13131
|
#initialValue = ""
|
|
13132
|
+
#initialValueLoaded = false
|
|
13102
13133
|
#validationTextArea = document.createElement("textarea")
|
|
13103
13134
|
#editorInitializedRafId = null
|
|
13104
13135
|
#listeners = new ListenerBin()
|
|
@@ -13276,9 +13307,19 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
13276
13307
|
}
|
|
13277
13308
|
|
|
13278
13309
|
focus() {
|
|
13310
|
+
// `editor.focus()` commits a reconciler update to position the cursor.
|
|
13311
|
+
// Skip if the contenteditable already owns focus — the update would be a
|
|
13312
|
+
// no-op but still triggers a full style/layout pass on pages with large
|
|
13313
|
+
// DOMs.
|
|
13314
|
+
if (this.#isContentFocused) return
|
|
13315
|
+
|
|
13279
13316
|
this.editor.focus(() => this.#onFocus());
|
|
13280
13317
|
}
|
|
13281
13318
|
|
|
13319
|
+
get #isContentFocused() {
|
|
13320
|
+
return !!this.editorContentElement && this.editorContentElement.contains(document.activeElement)
|
|
13321
|
+
}
|
|
13322
|
+
|
|
13282
13323
|
get value() {
|
|
13283
13324
|
if (!this.cachedValue) {
|
|
13284
13325
|
this.editor?.getEditorState().read(() => {
|
|
@@ -13290,6 +13331,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
13290
13331
|
}
|
|
13291
13332
|
|
|
13292
13333
|
set value(html) {
|
|
13334
|
+
const wasEmpty = !this.#initialValueLoaded;
|
|
13335
|
+
|
|
13293
13336
|
this.editor.update(() => {
|
|
13294
13337
|
gs(Vn);
|
|
13295
13338
|
const root = zo();
|
|
@@ -13299,11 +13342,17 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
13299
13342
|
|
|
13300
13343
|
this.#toggleEmptyStatus();
|
|
13301
13344
|
|
|
13302
|
-
// The first time you set the value
|
|
13303
|
-
// in an inconsistent state until
|
|
13304
|
-
// fails because no root node detected.
|
|
13305
|
-
|
|
13345
|
+
// The first time you set the value on an empty editor, Lexical can be
|
|
13346
|
+
// left in an inconsistent state until the next update (adding attachments
|
|
13347
|
+
// fails because no root node is detected). A no-op update works around
|
|
13348
|
+
// it. Only fire on the first load — subsequent set value calls don't hit
|
|
13349
|
+
// the inconsistent state and the extra reconciler cycle is pure overhead.
|
|
13350
|
+
if (wasEmpty) {
|
|
13351
|
+
requestAnimationFrame(() => this.editor?.update(() => { }));
|
|
13352
|
+
}
|
|
13306
13353
|
});
|
|
13354
|
+
|
|
13355
|
+
this.#initialValueLoaded = true;
|
|
13307
13356
|
}
|
|
13308
13357
|
|
|
13309
13358
|
#parseHtmlIntoLexicalNodes(html) {
|
|
@@ -13737,7 +13786,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
13737
13786
|
return { element, name: cssValue }
|
|
13738
13787
|
});
|
|
13739
13788
|
|
|
13740
|
-
|
|
13789
|
+
styleResolverRoot().appendChild(container);
|
|
13741
13790
|
|
|
13742
13791
|
const resolved = resolvers.map(({ element, name }) => ({
|
|
13743
13792
|
name,
|
|
Binary file
|
|
Binary file
|