add buttons to restore, clone, move a section
This commit is contained in:
parent
0a3ffb0bc8
commit
0c58783a6c
|
@ -205,6 +205,14 @@
|
|||
"configOnChangeTooltip": {
|
||||
"message": "Autosave and apply changes automatically"
|
||||
},
|
||||
"genericAdd": {
|
||||
"message": "Add",
|
||||
"description": "Used in various places for an action that adds something"
|
||||
},
|
||||
"genericClone": {
|
||||
"message": "Clone",
|
||||
"description": "Used in various places for an action that clones something"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Error",
|
||||
"description": "Used in various places to indicate some error occurred."
|
||||
|
@ -840,6 +848,10 @@
|
|||
"message": "Remove section",
|
||||
"description": "Label for the button to remove a section"
|
||||
},
|
||||
"sectionRestore": {
|
||||
"message": "Restore removed section",
|
||||
"description": "Label for the button to restore a removed section"
|
||||
},
|
||||
"shortcuts": {
|
||||
"message": "Shortcuts",
|
||||
"description": "Go to shortcut configuration"
|
||||
|
|
14
edit.html
14
edit.html
|
@ -121,7 +121,7 @@
|
|||
</template>
|
||||
|
||||
<template data-id="section">
|
||||
<div>
|
||||
<div class="section">
|
||||
<label i18n-text="sectionCode" class="code-label"></label>
|
||||
<br>
|
||||
<div class="applies-to">
|
||||
|
@ -134,13 +134,23 @@
|
|||
</div>
|
||||
<div class="edit-actions">
|
||||
<button class="remove-section" i18n-text="sectionRemove"></button>
|
||||
<button class="add-section" i18n-text="sectionAdd"></button>
|
||||
<button class="add-section" i18n-long-text="sectionAdd" i18n-short-text="genericAdd"></button>
|
||||
<button class="clone-section" i18n-text="genericClone"></button>
|
||||
<button class="move-section-up"></button>
|
||||
<button class="move-section-down"></button>
|
||||
<button class="beautify-section" i18n-text="styleBeautify"></button>
|
||||
<button class="test-regexp" i18n-text="styleRegexpTestButton"></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- not using DIV to make our CSS work for #sections > div:only-of-type .remove-section -->
|
||||
<template data-id="deletedSection">
|
||||
<p class="deleted-section">
|
||||
<button class="restore-section" i18n-text="sectionRestore"></button>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template data-id="searchReplaceDialog">
|
||||
<div id="search-replace-dialog">
|
||||
<div data-type="main">
|
||||
|
|
|
@ -264,16 +264,22 @@ input:invalid {
|
|||
margin-top: 4em;
|
||||
}
|
||||
/************ content ***********/
|
||||
#sections > div {
|
||||
#sections > * {
|
||||
margin: 0.7rem;
|
||||
padding: 1rem 1rem .3rem;
|
||||
}
|
||||
#sections > div:first-of-type {
|
||||
#sections > *:first-child {
|
||||
padding: 0 1rem .3rem;
|
||||
}
|
||||
#sections > div:not(:first-of-type) {
|
||||
#sections > *:not(:first-child) {
|
||||
border-top: 2px solid hsl(0, 0%, 80%);
|
||||
}
|
||||
.add-section:after {
|
||||
content: attr(short-text);
|
||||
}
|
||||
#sections > div:only-of-type .add-section:after {
|
||||
content: attr(long-text);
|
||||
}
|
||||
#sections > div:only-of-type .remove-section {
|
||||
display: none;
|
||||
}
|
||||
|
@ -291,17 +297,35 @@ input:invalid {
|
|||
#sections {
|
||||
counter-reset: codebox;
|
||||
}
|
||||
#sections > div > label {
|
||||
#sections > .section > label {
|
||||
animation: 2s highlight;
|
||||
animation-play-state: paused;
|
||||
animation-direction: reverse;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
#sections > div > label::after {
|
||||
#sections > .section > label::after {
|
||||
counter-increment: codebox;
|
||||
content: counter(codebox);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
.section:only-of-type .move-section-up,
|
||||
.section:only-of-type .move-section-down {
|
||||
display: none;
|
||||
}
|
||||
.move-section-up:after {
|
||||
content: "";
|
||||
display: block;
|
||||
border-style: solid;
|
||||
border-width: 0 .3em .5em .3em;
|
||||
border-color: transparent transparent currentColor transparent;
|
||||
}
|
||||
.move-section-down:after {
|
||||
content: "";
|
||||
display: block;
|
||||
border-style: solid;
|
||||
border-width: .5em .3em 0 .3em;
|
||||
border-color: currentColor transparent transparent transparent;
|
||||
}
|
||||
/* code */
|
||||
.code {
|
||||
height: 10rem;
|
||||
|
@ -729,7 +753,7 @@ html:not(.usercss) .usercss-only,
|
|||
}
|
||||
|
||||
#sections .single-editor,
|
||||
#sections > div.single-editor:first-of-type {
|
||||
#sections > .single-editor:first-child {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
|
@ -908,11 +932,11 @@ html:not(.usercss) .usercss-only,
|
|||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
#sections > div {
|
||||
#sections > * {
|
||||
margin: 0 .5rem .5rem;
|
||||
padding: .5rem 0 0;
|
||||
}
|
||||
#sections > div:first-of-type {
|
||||
#sections > *:first-child {
|
||||
margin: .5rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ function isCleanGlobal() {
|
|||
|
||||
function setCleanGlobal() {
|
||||
setCleanItem($('#sections'), true);
|
||||
$$('#header, #sections > div').forEach(setCleanSection);
|
||||
$$('#header, #sections > .section').forEach(setCleanSection);
|
||||
// forget the dirty applies-to ids from a deleted section after the style was saved
|
||||
dirty = {};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* global CodeMirror messageBox */
|
||||
/* global editors makeSectionVisible showCodeMirrorPopup showHelp */
|
||||
/* global loadScript require CSSLint stylelint */
|
||||
/* global clipString */
|
||||
'use strict';
|
||||
|
||||
onDOMready().then(loadLinterAssets);
|
||||
|
@ -288,10 +289,6 @@ function updateLintReportInternal(scope, {postponeNewIssues} = {}) {
|
|||
result.fixedSome |= lintState.reportDisplayed && oldMarkers.size;
|
||||
return result;
|
||||
}
|
||||
|
||||
function clipString(str, limit) {
|
||||
return str.length <= limit ? str : str.substr(0, limit) + '...';
|
||||
}
|
||||
}
|
||||
|
||||
function renderLintReport(someBlockChanged) {
|
||||
|
|
|
@ -5,6 +5,7 @@ global onChange indicateCodeChange initHooks setCleanGlobal
|
|||
global fromMozillaFormat maximizeCodeHeight toggleContextMenuDelete
|
||||
global setCleanItem updateTitle updateLintReportIfEnabled renderLintReport
|
||||
global showAppliesToHelp beautify regExpTester setGlobalProgress setCleanSection
|
||||
global clipString
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -14,7 +15,7 @@ function initWithSectionStyle(style, codeIsUpdated) {
|
|||
$('#url').href = style.url || '';
|
||||
if (codeIsUpdated !== false) {
|
||||
editors.length = 0;
|
||||
getSections().forEach(div => div.remove());
|
||||
$('#sections').textContent = '';
|
||||
addSections(style.sections.length ? style.sections : [{code: ''}]);
|
||||
initHooks();
|
||||
}
|
||||
|
@ -76,9 +77,12 @@ function addSections(sections, onAdded = () => {}) {
|
|||
|
||||
function addSection(event, section) {
|
||||
const div = template.section.cloneNode(true);
|
||||
$('.applies-to-help', div).addEventListener('click', showAppliesToHelp, false);
|
||||
$('.remove-section', div).addEventListener('click', removeSection, false);
|
||||
$('.add-section', div).addEventListener('click', addSection, false);
|
||||
$('.applies-to-help', div).addEventListener('click', showAppliesToHelp);
|
||||
$('.remove-section', div).addEventListener('click', removeSection);
|
||||
$('.add-section', div).addEventListener('click', addSection);
|
||||
$('.clone-section', div).addEventListener('click', cloneSection);
|
||||
$('.move-section-up', div).addEventListener('click', moveSection);
|
||||
$('.move-section-down', div).addEventListener('click', moveSection);
|
||||
$('.beautify-section', div).addEventListener('click', beautify);
|
||||
|
||||
const code = (section || {}).code || '';
|
||||
|
@ -134,8 +138,11 @@ function addSection(event, section) {
|
|||
const sections = $('#sections');
|
||||
let cm;
|
||||
if (event) {
|
||||
const clickedSection = getSectionForChild(event.target);
|
||||
sections.insertBefore(div, clickedSection.nextElementSibling);
|
||||
let clickedSection = event && getSectionForChild(event.target, {includeDeleted: true});
|
||||
clickedSection.insertAdjacentElement('afterend', div);
|
||||
while (clickedSection && !clickedSection.matches('.section')) {
|
||||
clickedSection = clickedSection.previousElementSibling;
|
||||
}
|
||||
const newIndex = getSections().indexOf(clickedSection) + 1;
|
||||
cm = setupCodeMirror(div, code, newIndex);
|
||||
makeSectionVisible(cm);
|
||||
|
@ -192,7 +199,31 @@ function addAppliesTo(list, type, value) {
|
|||
if (toFocus) toFocus.focus();
|
||||
}
|
||||
|
||||
function setupCodeMirror(sectionDiv, code, index) {
|
||||
function cloneSection(event) {
|
||||
const section = getSectionForChild(event.target);
|
||||
addSection(event, getSectionsHashes([section]).pop());
|
||||
setCleanItem($('#sections'), false);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
function moveSection(event) {
|
||||
const section = getSectionForChild(event.target);
|
||||
const dir = event.target.closest('.move-section-up') ? -1 : 1;
|
||||
const cm = section.CodeMirror;
|
||||
const index = editors.indexOf(cm);
|
||||
const newIndex = (index + dir + editors.length) % editors.length;
|
||||
const currentNextEl = section.nextElementSibling;
|
||||
const newSection = editors[newIndex].getSection();
|
||||
newSection.insertAdjacentElement('afterend', section);
|
||||
section.parentNode.insertBefore(newSection, currentNextEl || null);
|
||||
cm.focus();
|
||||
editors[index] = editors[newIndex];
|
||||
editors[newIndex] = cm;
|
||||
setCleanItem($('#sections'), false);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
function setupCodeMirror(sectionDiv, code, index = editors.length) {
|
||||
const cm = CodeMirror(wrapper => {
|
||||
$('.code-label', sectionDiv).insertAdjacentElement('afterend', wrapper);
|
||||
}, {
|
||||
|
@ -269,7 +300,7 @@ function setupCodeMirror(sectionDiv, code, index) {
|
|||
});
|
||||
};
|
||||
|
||||
editors.splice(index || editors.length, 0, cm);
|
||||
editors.splice(index, 0, cm);
|
||||
return cm;
|
||||
}
|
||||
|
||||
|
@ -385,17 +416,17 @@ function toggleSectionHeight(cm) {
|
|||
}
|
||||
}
|
||||
|
||||
function getSectionForChild(e) {
|
||||
return e.closest('#sections > div');
|
||||
function getSectionForChild(el, {includeDeleted} = {}) {
|
||||
return el.closest(`#sections > ${includeDeleted ? '*' : '.section'}`);
|
||||
}
|
||||
|
||||
function getSections() {
|
||||
return $$('#sections > div');
|
||||
return $$('#sections > .section');
|
||||
}
|
||||
|
||||
function getSectionsHashes() {
|
||||
function getSectionsHashes(elements = getSections()) {
|
||||
const sections = [];
|
||||
for (const div of getSections()) {
|
||||
for (const div of elements) {
|
||||
const meta = {urls: [], urlPrefixes: [], domains: [], regexps: []};
|
||||
for (const li of $('.applies-to-list', div).childNodes) {
|
||||
if (li.className === template.appliesToEverything.className) {
|
||||
|
@ -430,6 +461,29 @@ function removeAppliesTo(event) {
|
|||
function removeSection(event) {
|
||||
const section = getSectionForChild(event.target);
|
||||
const cm = section.CodeMirror;
|
||||
if (event instanceof Event && (!cm.isClean() || !cm.isBlank())) {
|
||||
const stub = template.deletedSection.cloneNode(true);
|
||||
const MAX_LINES = 10;
|
||||
const lines = [];
|
||||
cm.doc.iter(0, MAX_LINES + 1, ({text}) => lines.push(text) && false);
|
||||
stub.title = t('sectionCode') + '\n' +
|
||||
'-'.repeat(20) + '\n' +
|
||||
lines.slice(0, MAX_LINES).map(s => clipString(s, 100)).join('\n') +
|
||||
(lines.length > MAX_LINES ? '\n...' : '');
|
||||
$('.restore-section', stub).onclick = () => {
|
||||
let el = stub;
|
||||
while (el && !el.matches('.section')) {
|
||||
el = el.previousElementSibling;
|
||||
}
|
||||
const index = el ? editors.indexOf(el) + 1 : 0;
|
||||
editors.splice(index, 0, cm);
|
||||
stub.parentNode.replaceChild(section, stub);
|
||||
setCleanItem(section, false);
|
||||
updateTitle();
|
||||
cm.focus();
|
||||
};
|
||||
section.insertAdjacentElement('afterend', stub);
|
||||
}
|
||||
setCleanItem($('#sections'), false);
|
||||
removeAreaAndSetDirty(section);
|
||||
editors.splice(editors.indexOf(cm), 1);
|
||||
|
|
|
@ -116,3 +116,8 @@ function sectionsToMozFormat(style) {
|
|||
section.code;
|
||||
}).join('\n\n');
|
||||
}
|
||||
|
||||
|
||||
function clipString(str, limit = 100) {
|
||||
return str.length <= limit ? str : str.substr(0, limit) + '...';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user