show style settings in a dialog
This commit is contained in:
parent
440395db9f
commit
669fb443a9
97
edit.html
97
edit.html
|
@ -17,7 +17,6 @@
|
|||
<script src="content/apply.js"></script>
|
||||
|
||||
<script src="js/sections-util.js"></script>
|
||||
<script src="js/event-emitter.js"></script>
|
||||
<script src="edit/codemirror-themes.js"></script> <!-- must precede base.js -->
|
||||
<script src="edit/base.js"></script>
|
||||
|
||||
|
@ -62,7 +61,6 @@
|
|||
<script src="edit/sections-editor-section.js"></script>
|
||||
<script src="edit/sections-editor.js"></script>
|
||||
<script src="edit/usw-integration.js"></script>
|
||||
<script src="edit/settings.js"></script>
|
||||
<script src="edit/edit.js"></script>
|
||||
|
||||
<template data-id="appliesTo">
|
||||
|
@ -232,6 +230,47 @@
|
|||
</table>
|
||||
</template>
|
||||
|
||||
<template data-id="styleSettings">
|
||||
<fieldset class="style-settings" disabled>
|
||||
<!-- <label class="style-origin">
|
||||
<span class="form-label" i18n-text="styleOriginLabel"></span>
|
||||
<input id="styleOrigin" type="text">
|
||||
</label> -->
|
||||
<label class="form-group style-update-url">
|
||||
<span class="form-label" i18n-text="styleUpdateUrlLabel"></span>
|
||||
<input type="text">
|
||||
</label>
|
||||
<div class="form-group style-prefer-scheme radio-group">
|
||||
<!-- FIXME: should we use a different message from install page? -->
|
||||
<span class="form-label" i18n-text="installPreferSchemeLabel"></span>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="none">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeNone"></span>
|
||||
</label>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="dark">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeDark"></span>
|
||||
</label>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="light">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeLight"></span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="form-group style-include">
|
||||
<span class="form-label" i18n-text="styleIncludeLabel"></span>
|
||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||
</label>
|
||||
<label class="form-group style-exclude">
|
||||
<span class="form-label" i18n-text="styleExcludeLabel"></span>
|
||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||
</label>
|
||||
<!-- <label class="style-always-important">
|
||||
<input type="checkbox">
|
||||
<span class="form-label" i18n-text="styleAlwaysImportantLabel"></span>
|
||||
</label> -->
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
||||
<link href="vendor/codemirror/addon/dialog/dialog.css" rel="stylesheet">
|
||||
<link href="vendor/codemirror/addon/fold/foldgutter.css" rel="stylesheet">
|
||||
|
@ -241,8 +280,6 @@
|
|||
<link href="js/color/color-picker.css" rel="stylesheet">
|
||||
<link href="edit/codemirror-default.css" rel="stylesheet">
|
||||
<link href="edit/edit.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="edit/tab.css">
|
||||
<link rel="stylesheet" href="edit/settings.css">
|
||||
</head>
|
||||
|
||||
<body id="stylus-edit">
|
||||
|
@ -278,7 +315,8 @@
|
|||
<div>
|
||||
<button id="save-button" i18n-text="styleSaveLabel" data-hotkey-tooltip="save" disabled></button>
|
||||
<button id="beautify" i18n-text="styleBeautify"></button>
|
||||
<a href="manage.html" tabindex="-1"><button id="cancel-button" i18n-text="styleCancelEditLabel"></button></a>
|
||||
<button id="style-cfg-btn" i18n-text="editorSettingLabel"></button>
|
||||
<button id="cancel-button" i18n-title="styleCancelEditLabel">↩</button>
|
||||
</div>
|
||||
<div id="mozilla-format-buttons" class="sectioned-only">
|
||||
<button id="from-mozilla" i18n-text="importLabel"></button>
|
||||
|
@ -437,53 +475,7 @@
|
|||
target="_blank"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main tab-container">
|
||||
<div class="tab-bar">
|
||||
<div class="tab-bar-item active" i18n-text="editorCodeLabel"></div>
|
||||
<div class="tab-bar-item" i18n-text="editorSettingLabel"></div>
|
||||
</div>
|
||||
<div class="tab-panel">
|
||||
<section id="sections" class="active"></section>
|
||||
<fieldset class="style-settings" disabled>
|
||||
<!-- <label class="style-origin">
|
||||
<span class="form-label" i18n-text="styleOriginLabel"></span>
|
||||
<input id="styleOrigin" type="text">
|
||||
</label> -->
|
||||
<label class="form-group style-update-url">
|
||||
<span class="form-label" i18n-text="styleUpdateUrlLabel"></span>
|
||||
<input type="text">
|
||||
</label>
|
||||
<div class="form-group style-prefer-scheme radio-group">
|
||||
<!-- FIXME: should we use a different message from install page? -->
|
||||
<span class="form-label" i18n-text="installPreferSchemeLabel"></span>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="none">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeNone"></span>
|
||||
</label>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="dark">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeDark"></span>
|
||||
</label>
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="preferScheme" value="light">
|
||||
<span class="radio-label" i18n-text="installPreferSchemeLight"></span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="form-group style-include">
|
||||
<span class="form-label" i18n-text="styleIncludeLabel"></span>
|
||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||
</label>
|
||||
<label class="form-group style-exclude">
|
||||
<span class="form-label" i18n-text="styleExcludeLabel"></span>
|
||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||
</label>
|
||||
<!-- <label class="style-always-important">
|
||||
<input type="checkbox">
|
||||
<span class="form-label" i18n-text="styleAlwaysImportantLabel"></span>
|
||||
</label> -->
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<section id="sections"></section>
|
||||
<div id="help-popup">
|
||||
<div class="title"></div><svg id="sections-help" class="svg-icon dismiss"><use xlink:href="#svg-icon-close"/></svg>
|
||||
<div class="contents"></div>
|
||||
|
@ -528,6 +520,5 @@
|
|||
</symbol>
|
||||
|
||||
</svg>
|
||||
<script src="edit/tab.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
46
edit/base.js
46
edit/base.js
|
@ -11,19 +11,20 @@
|
|||
debounce
|
||||
getOwnTab
|
||||
sessionStore
|
||||
tryCatch
|
||||
tryJSONparse
|
||||
tryURL
|
||||
*/// toolbox.js
|
||||
/* global EventEmitter */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @type Editor
|
||||
* @namespace Editor
|
||||
*/
|
||||
const editor = Object.assign(EventEmitter(), {
|
||||
const editor = {
|
||||
style: null,
|
||||
dirty: DirtyReporter(),
|
||||
events: {},
|
||||
isUsercss: false,
|
||||
isWindowed: false,
|
||||
lazyKeymaps: {
|
||||
|
@ -36,7 +37,21 @@ const editor = Object.assign(EventEmitter(), {
|
|||
previewDelay: 200, // Chrome devtools uses 200
|
||||
scrollInfo: null,
|
||||
|
||||
onStyleUpdated() {
|
||||
cancel: () => location.assign('/manage.html'),
|
||||
|
||||
emit(name, ...args) {
|
||||
for (const fn of editor.events[name] || []) {
|
||||
tryCatch(fn, ...args);
|
||||
}
|
||||
},
|
||||
|
||||
on(name, fn) {
|
||||
(editor.events[name] || (
|
||||
editor.events[name] = new Set()
|
||||
)).add(fn);
|
||||
},
|
||||
|
||||
updateClass() {
|
||||
document.documentElement.classList.toggle('is-new-style', !editor.style.id);
|
||||
},
|
||||
|
||||
|
@ -48,6 +63,19 @@ const editor = Object.assign(EventEmitter(), {
|
|||
customName || name || t('styleMissingName')
|
||||
} - Stylus`; // the suffix enables external utilities to process our windows e.g. pin on top
|
||||
},
|
||||
};
|
||||
|
||||
editor.on('styleUpdated', (newStyle, reason) => {
|
||||
if (reason === 'config') {
|
||||
delete newStyle.sourceCode;
|
||||
delete newStyle.sections;
|
||||
delete newStyle.name;
|
||||
delete newStyle.enabled;
|
||||
Object.assign(editor.style, newStyle);
|
||||
editor.updateLivePreview();
|
||||
} else if (reason !== 'new') {
|
||||
editor.replaceStyle(newStyle);
|
||||
}
|
||||
});
|
||||
|
||||
//#region pre-init
|
||||
|
@ -90,7 +118,7 @@ const baseInit = (() => {
|
|||
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
||||
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
||||
editor.style = style;
|
||||
editor.onStyleUpdated();
|
||||
editor.updateClass();
|
||||
editor.updateTitle(false);
|
||||
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
||||
sessionStore.justEditedStyleId = style.id || '';
|
||||
|
@ -292,16 +320,10 @@ baseInit.ready.then(() => {
|
|||
}
|
||||
}
|
||||
|
||||
getOwnTab().then(async tab => {
|
||||
getOwnTab().then(tab => {
|
||||
ownTabId = tab.id;
|
||||
// use browser history back when 'back to manage' is clicked
|
||||
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
||||
await baseInit.domReady;
|
||||
$('#cancel-button').onclick = event => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
history.back();
|
||||
};
|
||||
editor.cancel = () => history.back();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -105,11 +105,8 @@ label {
|
|||
#header h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.main {
|
||||
padding-left: 280px;
|
||||
height: 100%;
|
||||
}
|
||||
#sections {
|
||||
padding-left: 280px;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -284,10 +281,6 @@ input:invalid {
|
|||
margin: 0 .2rem .5rem 0;
|
||||
}
|
||||
|
||||
#actions #cancel-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#options:not([open]) + #lint h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -768,11 +761,6 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
|||
max-height: calc(100vh - 8rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
#help-popup .settings {
|
||||
min-width: 500px;
|
||||
min-height: 200px;
|
||||
max-width: 48vw;
|
||||
}
|
||||
#help-popup .dismiss {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
|
@ -1180,16 +1168,13 @@ body.linter-disabled .hidden-unless-compact {
|
|||
#lint:not([open]) + #footer {
|
||||
margin: .25em 0 -1em .25em;
|
||||
}
|
||||
.main {
|
||||
#sections {
|
||||
height: unset !important;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
.tab-bar {
|
||||
margin-top: var(--fixed-height);
|
||||
}
|
||||
#sections > :not(.single-editor) {
|
||||
margin: 0 .5rem;
|
||||
padding: .5rem 0;
|
||||
|
|
14
edit/edit.js
14
edit/edit.js
|
@ -1,5 +1,5 @@
|
|||
/* global $ $create messageBoxProxy waitForSheet */// dom.js
|
||||
/* global msg API */// msg.js
|
||||
/* global API msg */// msg.js
|
||||
/* global CodeMirror */
|
||||
/* global SectionsEditor */
|
||||
/* global SourceEditor */
|
||||
|
@ -11,7 +11,6 @@
|
|||
/* global linterMan */
|
||||
/* global prefs */
|
||||
/* global t */// localization.js
|
||||
/* global StyleSettings */// settings.js
|
||||
'use strict';
|
||||
|
||||
//#region init
|
||||
|
@ -19,7 +18,6 @@
|
|||
baseInit.ready.then(async () => {
|
||||
await waitForSheet();
|
||||
(editor.isUsercss ? SourceEditor : SectionsEditor)();
|
||||
StyleSettings(editor);
|
||||
await editor.ready;
|
||||
editor.ready = true;
|
||||
editor.dirty.onChange(editor.updateDirty);
|
||||
|
@ -32,6 +30,7 @@ baseInit.ready.then(async () => {
|
|||
// enabling after init to prevent flash of validation failure on an empty name
|
||||
$('#name').required = !editor.isUsercss;
|
||||
$('#save-button').onclick = editor.save;
|
||||
$('#cancel-button').onclick = editor.cancel;
|
||||
|
||||
const elSec = $('#sections-list');
|
||||
// editor.toc.expanded pref isn't saved in compact-layout so prefs.subscribe won't work
|
||||
|
@ -48,6 +47,11 @@ baseInit.ready.then(async () => {
|
|||
require(['/edit/linter-dialogs'], () => linterMan.showLintConfig());
|
||||
$('#lint-help').onclick = () =>
|
||||
require(['/edit/linter-dialogs'], () => linterMan.showLintHelp());
|
||||
$('#style-cfg-btn').onclick = () => require([
|
||||
'/edit/settings.css',
|
||||
'/edit/settings', /* global StyleSettings */
|
||||
], () => StyleSettings());
|
||||
|
||||
require([
|
||||
'/edit/autocomplete',
|
||||
'/edit/global-search',
|
||||
|
@ -75,7 +79,7 @@ msg.onExtension(request => {
|
|||
} else {
|
||||
API.styles.get(request.style.id)
|
||||
.then(style => {
|
||||
editor.emit('styleChange', style, request.reason);
|
||||
editor.emit('styleUpdated', style, request.reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +172,7 @@ window.on('beforeunload', e => {
|
|||
}
|
||||
},
|
||||
|
||||
toggleStyle(enabled = style.enabled) {
|
||||
toggleStyle(enabled = !style.enabled) {
|
||||
$('#enabled').checked = enabled;
|
||||
editor.updateEnabledness(enabled);
|
||||
},
|
||||
|
|
|
@ -89,7 +89,7 @@ function SectionsEditor() {
|
|||
// FIXME: avoid recreating all editors?
|
||||
await initSections(newStyle.sections, {replace: true});
|
||||
Object.assign(style, newStyle);
|
||||
editor.onStyleUpdated();
|
||||
editor.updateClass();
|
||||
updateHeader();
|
||||
// Go from new style URL to edit style URL
|
||||
if (style.id && !/[&?]id=/.test(location.search)) {
|
||||
|
@ -109,7 +109,7 @@ function SectionsEditor() {
|
|||
newStyle = await API.styles.editSave(newStyle);
|
||||
destroyRemovedSections();
|
||||
if (!style.id) {
|
||||
editor.emit('styleChange', newStyle, 'new');
|
||||
editor.emit('styleUpdated', newStyle, 'new');
|
||||
}
|
||||
sessionStore.justEditedStyleId = newStyle.id;
|
||||
editor.replaceStyle(newStyle, false);
|
||||
|
@ -137,18 +137,6 @@ function SectionsEditor() {
|
|||
updateHeader();
|
||||
updateLivePreview();
|
||||
});
|
||||
editor.on('styleChange', (newStyle, reason) => {
|
||||
if (reason === 'new') return; // nothing is new for us
|
||||
if (reason === 'config') {
|
||||
delete newStyle.sections;
|
||||
delete newStyle.name;
|
||||
delete newStyle.enabled;
|
||||
Object.assign(style, newStyle);
|
||||
updateLivePreview();
|
||||
return;
|
||||
}
|
||||
editor.replaceStyle(newStyle);
|
||||
});
|
||||
|
||||
/** @param {EditorSection} section */
|
||||
function fitToContent(section) {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
.compact-layout #help-popup[data-type="styleSettings"] {
|
||||
width: 90%;
|
||||
}
|
||||
.style-settings {
|
||||
padding: 0.7rem 1.7rem;
|
||||
border: 0;
|
||||
|
@ -41,6 +44,10 @@
|
|||
}
|
||||
.style-settings textarea {
|
||||
resize: vertical;
|
||||
min-width: 33vw;
|
||||
min-height: 2.5em;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.style-settings textarea:not(:placeholder-shown) {
|
||||
min-width: 50vw;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
/* global $ $$ */// dom.js
|
||||
/* global $ $$ $create moveFocus */// dom.js
|
||||
/* global API */// msg.js
|
||||
/* global editor */
|
||||
/* global helpPopup */// util.js
|
||||
/* global t */// localization.js
|
||||
/* exported StyleSettings */
|
||||
'use strict';
|
||||
|
||||
function StyleSettings(editor) {
|
||||
function StyleSettings() {
|
||||
let {style} = editor;
|
||||
|
||||
const ui = t.template.styleSettings.cloneNode(true);
|
||||
const inputs = [
|
||||
createInput('.style-update-url input', () => style.updateUrl || '',
|
||||
e => API.styles.config(style.id, 'updateUrl', e.target.value)),
|
||||
|
@ -16,10 +19,16 @@ function StyleSettings(editor) {
|
|||
['.style-exclude', 'exclusions'],
|
||||
].map(createArea),
|
||||
];
|
||||
|
||||
update(style);
|
||||
|
||||
editor.on('styleChange', update);
|
||||
editor.on('styleUpdated', update);
|
||||
helpPopup.show(t('editorSettingLabel'), $create([
|
||||
ui,
|
||||
$create('.buttons', [
|
||||
$create('button', {onclick: helpPopup.close}, t('confirmClose')),
|
||||
]),
|
||||
]));
|
||||
$('#help-popup').dataset.type = 'styleSettings';
|
||||
moveFocus(ui, 0);
|
||||
|
||||
function textToList(text) {
|
||||
const list = text.split(/\s*\r?\n\s*/g);
|
||||
|
@ -30,13 +39,12 @@ function StyleSettings(editor) {
|
|||
if (!newStyle.id) return;
|
||||
if (reason === 'editSave') return;
|
||||
style = newStyle;
|
||||
$('.style-settings').disabled = false;
|
||||
inputs.forEach(i => i.update());
|
||||
}
|
||||
|
||||
function createArea([parentSel, type]) {
|
||||
const sel = parentSel + ' textarea';
|
||||
const el = $(sel);
|
||||
const el = $(sel, ui);
|
||||
el.on('input', () => {
|
||||
const val = el.value;
|
||||
el.rows = val.match(/^/gm).length + !val.endsWith('\n');
|
||||
|
@ -53,7 +61,7 @@ function StyleSettings(editor) {
|
|||
}
|
||||
|
||||
function createRadio(selector, getter, setter) {
|
||||
const els = $$(selector);
|
||||
const els = $$(selector, ui);
|
||||
for (const el of els) {
|
||||
el.addEventListener('change', e => {
|
||||
if (el.checked) {
|
||||
|
@ -73,7 +81,7 @@ function StyleSettings(editor) {
|
|||
}
|
||||
|
||||
function createInput(selector, getter, setter) {
|
||||
const el = $(selector);
|
||||
const el = $(selector, ui);
|
||||
el.addEventListener('change', setter);
|
||||
return {
|
||||
update() {
|
||||
|
|
|
@ -71,7 +71,7 @@ function SourceEditor() {
|
|||
} else {
|
||||
res = await API.usercss.editSave({customName, enabled, id, sourceCode});
|
||||
if (!id) {
|
||||
editor.emit('styleChange', res.style, 'new');
|
||||
editor.emit('styleUpdated', res.style, 'new');
|
||||
}
|
||||
// Awaiting inside `try` so that exceptions go to our `catch`
|
||||
await replaceStyle(res.style);
|
||||
|
@ -125,17 +125,6 @@ function SourceEditor() {
|
|||
updateMeta();
|
||||
updateLivePreview();
|
||||
});
|
||||
editor.on('styleChange', (newStyle, reason) => {
|
||||
if (reason === 'new') return;
|
||||
if (reason === 'config') {
|
||||
delete newStyle.sourceCode;
|
||||
delete newStyle.name;
|
||||
Object.assign(style, newStyle);
|
||||
updateLivePreview();
|
||||
return;
|
||||
}
|
||||
replaceStyle(newStyle);
|
||||
});
|
||||
|
||||
async function preprocess(style) {
|
||||
const res = await API.usercss.build({
|
||||
|
@ -265,7 +254,7 @@ function SourceEditor() {
|
|||
}
|
||||
sessionStore.justEditedStyleId = newStyle.id;
|
||||
Object.assign(style, newStyle);
|
||||
editor.onStyleUpdated();
|
||||
editor.updateClass();
|
||||
updateMeta();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/* exported EventEmitter */
|
||||
'use strict';
|
||||
|
||||
function EventEmitter() {
|
||||
const listeners = new Map();
|
||||
return {
|
||||
on(ev, cb, opt) {
|
||||
if (!listeners.has(ev)) {
|
||||
listeners.set(ev, new Map());
|
||||
}
|
||||
listeners.get(ev).set(cb, opt);
|
||||
if (opt && opt.runNow) {
|
||||
cb();
|
||||
}
|
||||
},
|
||||
off(ev, cb) {
|
||||
const cbs = listeners.get(ev);
|
||||
if (cbs) {
|
||||
cbs.delete(cb);
|
||||
}
|
||||
},
|
||||
emit(ev, ...args) {
|
||||
const cbs = listeners.get(ev);
|
||||
if (!cbs) return;
|
||||
for (const [cb, opt] of cbs.entries()) {
|
||||
try {
|
||||
cb(...args);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
if (opt && opt.once) {
|
||||
cbs.delete(cb);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user