Skip to content

Commit 592047a

Browse files
committed
stabilize source editor on reload
* position * sticky header * color swatches
1 parent 31b2623 commit 592047a

9 files changed

+51
-34
lines changed

src/edit/codemirror-factory.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ const cms = new Set();
1717
const cmDefaults = CodeMirror.defaults;
1818
const cmFactory = {
1919

20-
/** @return {CodeMirror.Editor} */
20+
/**
21+
* @param {HTMLElement | ((host: HTMLElement) => void)} place
22+
* @param {CodeMirror.EditorConfiguration} [options]
23+
* @return {CodeMirror.Editor}
24+
*/
2125
create(place, options) {
2226
const cm = CodeMirror(place, options);
2327
cm.display.lineDiv.on('mousewheel', plusMinusOnWheel.bind(cm), true);

src/edit/compact-header.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import {$create, mqCompact} from '@/js/dom';
22
import {important} from '@/js/dom-util';
3+
import {template} from '@/js/localization';
34
import * as prefs from '@/js/prefs';
45
import editor from './editor';
56

7+
const h = template.body.$('#header');
8+
export const toggleSticky = val => h.classList.toggle('sticky', val);
9+
export let sticky;
10+
611
export default function CompactHeader() {
712
// Set up mini-header on scroll
813
const {isUsercss} = editor;
@@ -40,9 +45,8 @@ export default function CompactHeader() {
4045

4146
/** @param {IntersectionObserverEntry[]} entries */
4247
function onScrolled(entries) {
43-
const h = $id('header');
44-
const sticky = !entries.pop().intersectionRatio;
48+
sticky = !entries.pop().intersectionRatio;
4549
if (!isUsercss) scroller.style.paddingTop = sticky ? h.offsetHeight + 'px' : '';
46-
h.classList.toggle('sticky', sticky);
50+
toggleSticky(sticky);
4751
}
4852
}

src/edit/editor.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {$create} from '@/js/dom';
22
import * as prefs from '@/js/prefs';
33
import {clipString, debounce, deepEqual, mapObj, sessionStore, t} from '@/js/util';
4+
import {sticky} from './compact-header';
45
import DirtyReporter from './dirty-reporter';
56

67
const dirty = DirtyReporter();
@@ -28,6 +29,7 @@ const editor = self.editor = {
2829
},
2930
regexps,
3031
saving: false,
32+
/** @type {EditorScrollInfoContainer} */
3133
scrollInfo: {},
3234
get style() {
3335
return style;
@@ -44,21 +46,25 @@ const editor = self.editor = {
4446
cm.setSelections(...si.sel, {scroll: false});
4547
Object.assign(cm.display.scroller, si.scroll); // for source editor
4648
Object.assign(cm.doc, si.scroll); // for sectioned editor
49+
return si;
4750
}
4851
},
4952

5053
cancel: () => location.assign('/manage.html'),
5154

5255
makeScrollInfo() {
53-
return {
56+
return /** @namespace EditorScrollInfoContainer */ {
57+
sticky,
5458
scrollY: window.scrollY,
59+
/** @type {EditorScrollInfo[]} */
5560
cms: editor.getEditors().map(cm => /** @namespace EditorScrollInfo */({
5661
bookmarks: (cm.state.sublimeBookmarks || []).map(b => b.find()),
5762
focus: cm.hasFocus(),
5863
height: cm.display.wrapper.style.height.replace('100vh', ''),
5964
parentHeight: cm.display.wrapper.parentElement.offsetHeight,
6065
scroll: mapObj(cm.doc, null, ['scrollLeft', 'scrollTop']),
6166
sel: [cm.doc.sel.ranges, cm.doc.sel.primIndex],
67+
viewTo: cm.display.viewTo,
6268
})),
6369
};
6470
},

src/edit/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import '@/js/dom-init';
22
import {tBody} from '@/js/localization';
33
import * as prefs from '@/js/prefs';
44
import {CodeMirror} from '@/cm';
5-
import CompactHeader from './compact-header';
5+
import CompactHeader, {toggleSticky} from './compact-header';
66
import editor from './editor';
77
import EditorHeader from './editor-header';
88
import * as linterMan from './linter';
@@ -23,6 +23,7 @@ tBody();
2323

2424
(async () => {
2525
if (loading) await loading;
26+
if (editor.scrollInfo.sticky) toggleSticky(true);
2627
EditorHeader();
2728
USWIntegration();
2829
// TODO: load respective js on demand?

src/edit/moz-section-finder.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default function MozSectionFinder(cm) {
2323
let updTo;
2424
let scheduled;
2525

26+
/** @namespace MozSectionFinder */
2627
const finder = {
2728
IGNORE_ORIGIN: KEY,
2829
EQ_SKIP_KEYS: [

src/edit/moz-section-widget.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,12 @@ export default function MozSectionWidget(cm, finder = MozSectionFinder(cm)) {
229229
const isDelayed = added.isDelayed && (cm.startOperation(), true);
230230
const toDelay = [];
231231
const t0 = performance.now();
232-
let {viewFrom, viewTo} = cm.display;
232+
let viewTo = editor.viewTo || cm.display.viewTo;
233233
for (const sec of added) {
234234
const i = removed.findIndex(isReusableWidget, sec);
235235
const old = removed[i];
236-
if (isDelayed || old || sec.end.line >= viewFrom && sec.start.line < viewTo) {
236+
if (isDelayed || old
237+
|| sec.start.line < viewTo /* must add preceding ones to calc scrollTop*/) {
237238
renderWidget(sec, old);
238239
viewTo -= (sec.funcs.length || 1) * 1.25;
239240
if (old) removed[i] = null;

src/edit/sections-editor-section.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export default class EditorSection {
3737
at[prefs.get('editor.targetsFirst') ? 'after' : 'before'](wrapper);
3838
}, {
3939
value: sectionData.code,
40+
finishInit(_) {
41+
editor.applyScrollInfo(_, si);
42+
},
4043
});
4144
this.elLabelText = elLabel.lastChild;
4245
cm.el = el;
@@ -66,7 +69,6 @@ export default class EditorSection {
6669
if (cssName && arr) for (const v of arr) this.addTarget(cssName, v);
6770
}
6871
if (!this.targets.length) this.addTarget();
69-
editor.applyScrollInfo(cm, si);
7072
initBeautifyButton(el.$('.beautify-section'), [cm]);
7173
prefs.subscribe('editor.toc.expanded', this.updateTocPrefToggled.bind(this), true);
7274
new ResizeGrip(cm); // eslint-disable-line no-use-before-define

src/edit/source-editor.js

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,33 @@ export default function SourceEditor() {
2727
`.replace(/^\s+/gm, '');
2828
let savedGeneration;
2929
let prevMode = NaN;
30+
/** @type {MozSectionFinder} */
31+
let sectionFinder;
32+
let sectionWidget;
3033

3134
$$remove('.sectioned-only');
3235
$id('header').on('wheel', headerOnScroll);
3336
$id('sections').textContent = '';
3437
$id('sections').appendChild($create('.single-editor'));
3538
$id('save-button').on('split-btn', saveTemplate);
3639

37-
const cm = cmFactory.create($('.single-editor'));
40+
const cm = cmFactory.create($('.single-editor'), {
41+
value: style.id ? style.sourceCode : setupNewStyle(editor.template),
42+
finishInit(me) {
43+
const si = editor.applyScrollInfo(me) || {};
44+
editor.viewTo = si.viewTo;
45+
sectionFinder = MozSectionFinder(me);
46+
sectionWidget = MozSectionWidget(me, sectionFinder);
47+
prefs.subscribe('editor.linter', updateLinterSwitch, true);
48+
prefs.subscribe('editor.appliesToLineWidget',
49+
(k, val) => sectionWidget.toggle(val), true);
50+
prefs.subscribe('editor.toc.expanded',
51+
(k, val) => sectionFinder.onOff(editor.updateToc, val), true);
52+
Object.assign(me.curOp, si.scroll);
53+
editor.viewTo = 0;
54+
},
55+
});
3856
const cmpPos = CodeMirror.cmpPos;
39-
const sectionFinder = MozSectionFinder(cm);
40-
const sectionWidget = MozSectionWidget(cm, sectionFinder);
4157
const metaCompiler = createMetaCompiler(meta => {
4258
const {vars} = style[UCD] || {};
4359
if (vars) {
@@ -53,7 +69,6 @@ export default function SourceEditor() {
5369
style.url = meta.homepageURL || style.installationUrl;
5470
updateMeta();
5571
});
56-
if (!style.id) setupNewStyle(editor.template);
5772
updateMeta();
5873

5974
/** @namespace Editor */
@@ -110,17 +125,6 @@ export default function SourceEditor() {
110125
scrollToEditor: () => {},
111126
});
112127

113-
prefs.subscribe('editor.linter', updateLinterSwitch, true);
114-
prefs.subscribe('editor.appliesToLineWidget',
115-
(k, val) => sectionWidget.toggle(val), true);
116-
prefs.subscribe('editor.toc.expanded',
117-
(k, val) => sectionFinder.onOff(editor.updateToc, val), true);
118-
119-
if (style.id) {
120-
cm.setValue(style.sourceCode);
121-
cm.clearHistory();
122-
cm.markClean();
123-
}
124128
savedGeneration = cm.changeGeneration();
125129
cm.on('changes', (_, changes) => {
126130
dirty.modify('sourceGeneration', savedGeneration, cm.changeGeneration());
@@ -139,7 +143,6 @@ export default function SourceEditor() {
139143
if (!$isTextInput(document.activeElement)) {
140144
cm.focus();
141145
}
142-
editor.applyScrollInfo(cm); // WARNING! Place it after all cm.XXX calls that change scroll pos
143146

144147
/** Shows the console.log output from the background worker stored in `log` property */
145148
function showLog(log) {
@@ -183,19 +186,14 @@ export default function SourceEditor() {
183186
if (Object.keys(sec0).length === 1) { // the only key is 'code'
184187
sec0.domains = ['example.com'];
185188
}
186-
style.sourceCode = (tpl || DEFAULT_TEMPLATE)
189+
return (style.sourceCode = (tpl || DEFAULT_TEMPLATE)
187190
.replace(/(@name)(?:([\t\x20]+).*|\n)/, (_, k, space) => `${k}${space || ' '}${style.name}`)
188191
.replace(/\s*@-moz-document[^{]*{([^}]*)}\s*$/g, // stripping dummy sections
189192
(s, body) => body.trim() === comment ? '\n\n' : s)
190193
.trim() +
191194
'\n\n' +
192-
styleToCss(style);
193-
cm.startOperation();
194-
cm.setValue(style.sourceCode);
195-
cm.clearHistory();
196-
cm.markClean();
197-
cm.endOperation();
198-
dirty.clear('sourceGeneration');
195+
styleToCss(style)
196+
);
199197
}
200198

201199
function updateMeta() {

src/js/color/color-view.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const COLORVIEW_SWATCH_PROP = `--${COLORVIEW_SWATCH_CLASS}`;
1111
const CLOSE_POPUP_EVENT = 'close-colorpicker-popup';
1212

1313
const {RX_COLOR, testAt} = colorConverter;
14-
const RX_UNSUPPORTED = (s => s && new RegExp(s))([
14+
const RX_UNSUPPORTED = !__.MV3 && (s => s && new RegExp(s))([
1515
!CSS.supports('color', '#abcd') && /#(.{4}){1,2}$/,
1616
!CSS.supports('color', 'hwb(1 0% 0%)') && /^hwb\(/,
1717
!CSS.supports('color', 'rgb(1e2,0,0)') && /\de/,

0 commit comments

Comments
 (0)