diff --git a/edit.html b/edit.html
index 12614c99..b87396fe 100644
--- a/edit.html
+++ b/edit.html
@@ -91,7 +91,6 @@
-
@@ -305,7 +304,7 @@
diff --git a/edit/codemirror-default.css b/edit/codemirror-default.css
index 6adbf962..0db190f4 100644
--- a/edit/codemirror-default.css
+++ b/edit/codemirror-default.css
@@ -15,8 +15,7 @@
-webkit-animation: highlight 3s cubic-bezier(.18, .02, 0, .94);
}
.CodeMirror-focused {
- outline: -webkit-focus-ring-color auto 5px;
- outline-offset: -2px;
+ outline: #7dadd9 auto 1px; /* not using the ring-color hack as it became ugly in new Chrome */
}
.CodeMirror-bookmark {
background: linear-gradient(to right, currentColor, transparent);
@@ -24,13 +23,6 @@
width: 2em;
opacity: .5;
}
-@supports (-moz-appearance:none) {
- /* restrict to FF */
- .CodeMirror-focused {
- outline: #7dadd9 auto 1px;
- outline-offset: -1px;
- }
-}
.CodeMirror-search-field {
width: 10em;
}
diff --git a/edit/edit.css b/edit/edit.css
index 3635fa61..8db215ba 100644
--- a/edit/edit.css
+++ b/edit/edit.css
@@ -63,6 +63,7 @@ label {
#sections {
padding-left: 280px;
min-height: 0;
+ height: 100%;
}
#sections h2 {
margin-top: 1rem;
@@ -278,12 +279,6 @@ input:invalid {
.section-editor .section:not(:first-child) {
border-top: 2px solid hsl(0, 0%, 80%);
}
-.section-editor:not(.section-editor-ready) .section {
- opacity: 0 !important;
-}
-.section-editor:not(.section-editor-ready) .CodeMirror {
- height: 0;
-}
.add-section:after {
content: attr(short-text);
}
@@ -817,13 +812,8 @@ body.linter-disabled .hidden-unless-compact {
color: #888;
}
-/* FIXME: remove the ID selector */
-#sections .single-editor {
+.single-editor {
height: 100%;
- margin: 0;
- padding: 0;
- display: flex;
- box-sizing: border-box;
}
.single-editor .CodeMirror {
@@ -843,7 +833,6 @@ body.linter-disabled .hidden-unless-compact {
}
.usercss.firefox #sections,
-.usercss.firefox .single-editor,
.usercss.firefox .CodeMirror {
height: 100%;
}
@@ -996,7 +985,7 @@ body.linter-disabled .hidden-unless-compact {
flex-direction: column;
flex: 1;
}
- #sections > * {
+ #sections > :not(.single-editor) {
margin: 0 .5rem;
padding: .5rem 0;
}
diff --git a/edit/edit.js b/edit/edit.js
index 7d3b49d4..b902a1e3 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -541,7 +541,7 @@ function detectLayout() {
body.classList.add('fixed-header');
}
}, 250);
- window.addEventListener('scroll', fixedHeader);
+ window.addEventListener('scroll', fixedHeader, {passive: true});
}
} else {
body.classList.remove('compact-layout');
diff --git a/edit/refresh-on-view.js b/edit/refresh-on-view.js
deleted file mode 100644
index ffbc0589..00000000
--- a/edit/refresh-on-view.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/* global CodeMirror */
-/*
-Initialization of the multi-sections editor is slow if there are many editors
-e.g. https://github.com/openstyles/stylus/issues/178. So we only refresh the
-editor when they were scroll into view.
-*/
-'use strict';
-
-CodeMirror.defineExtension('refreshOnView', function () {
- const cm = this;
- if (typeof IntersectionObserver === 'undefined') {
- // uh
- cm.isRefreshed = true;
- cm.refresh();
- return;
- }
- const wrapper = cm.display.wrapper;
- const observer = new IntersectionObserver(entries => {
- for (const entry of entries) {
- if (entry.isIntersecting) {
- // wrapper.style.visibility = 'visible';
- cm.isRefreshed = true;
- cm.refresh();
- observer.disconnect();
- }
- }
- });
- observer.observe(wrapper);
-});
diff --git a/edit/sections-editor-section.js b/edit/sections-editor-section.js
index 8885cfc6..2735ac2c 100644
--- a/edit/sections-editor-section.js
+++ b/edit/sections-editor-section.js
@@ -296,19 +296,14 @@ function createSection({
function insertApplyAfter(init, base) {
const apply = createApply(init);
- if (base) {
- const index = appliesTo.indexOf(base);
- appliesTo.splice(index + 1, 0, apply);
- appliesToContainer.insertBefore(apply.el, base.el.nextSibling);
- } else {
- appliesTo.push(apply);
- appliesToContainer.appendChild(apply.el);
- }
+ appliesTo.splice(base ? appliesTo.indexOf(base) + 1 : appliesTo.length, 0, apply);
+ appliesToContainer.insertBefore(apply.el, base ? base.el.nextSibling : null);
dirty.add(apply, apply);
if (appliesTo.length > 1 && appliesTo[0].all) {
removeApply(appliesTo[0]);
}
emitSectionChange();
+ return apply;
}
function removeApply(apply) {
@@ -380,7 +375,8 @@ function createSection({
}
$('.add-applies-to', el).addEventListener('click', e => {
e.preventDefault();
- insertApplyAfter({type, value: ''}, apply);
+ const newApply = insertApplyAfter({type, value: ''}, apply);
+ $('input', newApply.el).focus();
});
return apply;
diff --git a/edit/sections-editor.js b/edit/sections-editor.js
index bcf95148..8a04e06c 100644
--- a/edit/sections-editor.js
+++ b/edit/sections-editor.js
@@ -29,6 +29,9 @@ function createSectionsEditor({style, onTitleChanged}) {
updateLivePreview();
});
+ updateHeader();
+ rerouteHotkeys(true);
+
$('#to-mozilla').addEventListener('click', showMozillaFormat);
$('#to-mozilla-help').addEventListener('click', showToMozillaHelp);
$('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
@@ -47,17 +50,22 @@ function createSectionsEditor({style, onTitleChanged}) {
.forEach(e => e.addEventListener('mousedown', toggleContextMenuDelete));
}
- let sectionOrder = '';
- const initializing = new Promise(resolve => initSection({
- sections: style.sections.slice(),
- done:() => {
- dirty.clear();
- rerouteHotkeys(true);
- resolve();
- updateHeader();
- sections.forEach(fitToContent);
+ const xo = window.IntersectionObserver && new IntersectionObserver(entries => {
+ for (const {isIntersecting, target} of entries) {
+ if (isIntersecting) {
+ target.CodeMirror.refresh();
+ xo.unobserve(target);
+ }
}
- }));
+ }, {rootMargin: '100%'});
+ const refreshOnView = (cm, force) =>
+ force || !xo ?
+ cm.refresh() :
+ xo.observe(cm.display.wrapper);
+
+ let sectionOrder = '';
+ let headerOffset; // in compact mode the header is at the top so it reduces the available height
+ const initializing = initSections(style.sections.slice());
const livePreview = createLivePreview();
livePreview.show(Boolean(style.id));
@@ -83,28 +91,26 @@ function createSectionsEditor({style, onTitleChanged}) {
};
function fitToContent(section) {
- if (section.cm.isRefreshed) {
+ const {cm, cm: {display: {wrapper, sizer}}} = section;
+ if (cm.display.renderedView) {
resize();
} else {
- section.cm.on('update', resize);
+ cm.on('update', resize);
}
function resize() {
- let contentHeight = section.el.querySelector('.CodeMirror-sizer').offsetHeight;
- if (contentHeight < section.cm.defaultTextHeight()) {
+ let contentHeight = sizer.offsetHeight;
+ if (contentHeight < cm.defaultTextHeight()) {
return;
}
- contentHeight += 9; // border & resize grip
- section.cm.off('update', resize);
- const cmHeight = section.cm.getWrapperElement().offsetHeight;
- const maxHeight = cmHeight + window.innerHeight - section.el.offsetHeight;
- section.cm.setSize(null, Math.min(contentHeight, maxHeight));
- if (sections.every(s => s.cm.isRefreshed)) {
- fitToAvailableSpace();
+ if (headerOffset == null) {
+ headerOffset = wrapper.getBoundingClientRect().top;
}
- setTimeout(() => {
- container.classList.add('section-editor-ready');
- }, 50);
+ contentHeight += 9; // border & resize grip
+ cm.off('update', resize);
+ const cmHeight = wrapper.offsetHeight;
+ const maxHeight = (window.innerHeight - headerOffset) - (section.el.offsetHeight - cmHeight);
+ cm.setSize(null, Math.min(contentHeight, maxHeight));
}
}
@@ -367,7 +373,7 @@ function createSectionsEditor({style, onTitleChanged}) {
if (replaceOldStyle) {
return replaceSections(sections);
}
- return new Promise(resolve => initSection({sections, done: resolve, focusOn: false}));
+ return initSections(sections, {focusOn: false});
})
.then(() => {
$('.dismiss').dispatchEvent(new Event('click'));
@@ -472,36 +478,33 @@ function createSectionsEditor({style, onTitleChanged}) {
livePreview.update(getModel());
}
- function initSection({
- sections: originalSections,
+ function initSections(originalSections, {
total = originalSections.length,
focusOn = 0,
- done
- }) {
- container.classList.add('hidden');
- chunk();
-
- function chunk() {
- if (!originalSections.length) {
- setGlobalProgress();
- if (focusOn !== false) {
- setTimeout(() => sections[focusOn].cm.focus());
- }
- container.classList.remove('hidden');
- for (const section of sections) {
- section.cm.refreshOnView();
- }
- if (done) {
- done();
- }
- return;
- }
+ } = {}) {
+ let done;
+ return new Promise(resolve => {
+ done = resolve;
+ chunk(true);
+ });
+ function chunk(forceRefresh) {
const t0 = performance.now();
while (originalSections.length && performance.now() - t0 < 100) {
- insertSectionAfter(originalSections.shift());
+ insertSectionAfter(originalSections.shift(), undefined, forceRefresh);
+ dirty.clear();
+ if (focusOn !== false && sections[focusOn]) {
+ sections[focusOn].cm.focus();
+ focusOn = false;
+ }
}
setGlobalProgress(total - originalSections.length, total);
- setTimeout(chunk);
+ if (!originalSections.length) {
+ setGlobalProgress();
+ fitToAvailableSpace();
+ done();
+ } else {
+ setTimeout(chunk);
+ }
}
}
@@ -540,7 +543,7 @@ function createSectionsEditor({style, onTitleChanged}) {
updateLivePreview();
}
- function insertSectionAfter(init, base) {
+ function insertSectionAfter(init, base, forceRefresh) {
if (!init) {
init = {code: '', urlPrefixes: ['http://example.com']};
}
@@ -557,15 +560,18 @@ function createSectionsEditor({style, onTitleChanged}) {
prevEditor,
nextEditor
});
- if (base) {
- const index = sections.indexOf(base);
- sections.splice(index + 1, 0, section);
- container.insertBefore(section.el, base.el.nextSibling);
- } else {
- sections.push(section);
- container.appendChild(section.el);
+ const {cm} = section;
+ sections.splice(base ? sections.indexOf(base) + 1 : sections.length, 0, section);
+ container.insertBefore(section.el, base ? base.el.nextSibling : null);
+ refreshOnView(cm, forceRefresh);
+ if (!base || init.code) {
+ // Fit a) during startup or b) when the clone button is clicked on a section with some code
+ fitToContent(section);
+ }
+ if (base) {
+ cm.focus();
+ setTimeout(scrollToEditor, 0, cm);
}
- section.render();
updateSectionOrder();
section.onChange(updateLivePreview);
updateLivePreview();
@@ -599,7 +605,7 @@ function createSectionsEditor({style, onTitleChanged}) {
}
sections.length = 0;
container.textContent = '';
- return new Promise(resolve => initSection({sections: originalSections, done: resolve}));
+ return initSections(originalSections);
}
function replaceStyle(newStyle, codeIsUpdated) {