stylus/manage/sorter.js

203 lines
5.7 KiB
JavaScript
Raw Normal View History

2022-02-19 14:12:15 +00:00
/* global $ $create messageBoxProxy */// dom.js
/* global installed */// manage.js
/* global prefs */
/* global t */// localization.js
2017-12-23 00:11:46 +00:00
'use strict';
const sorter = (() => {
const sorterType = {
alpha: (a, b) => a < b ? -1 : a === b ? 0 : 1,
number: (a, b) => (a || 0) - (b || 0),
};
const tagData = {
title: {
text: t('genericTitle'),
2022-09-03 16:36:59 +00:00
parse: v => v.styleNameLowerCase,
sorter: sorterType.alpha,
2017-12-23 00:11:46 +00:00
},
usercss: {
text: 'Usercss',
2022-09-03 16:36:59 +00:00
parse: v => v.styleMeta.usercssData ? 0 : 1,
sorter: sorterType.number,
2017-12-23 00:11:46 +00:00
},
disabled: {
text: '', // added as either "enabled" or "disabled" by the addOptions function
2022-09-03 16:36:59 +00:00
parse: v => v.styleMeta.enabled ? 1 : 0,
sorter: sorterType.number,
2017-12-23 00:11:46 +00:00
},
dateInstalled: {
text: t('dateInstalled'),
2022-09-03 16:36:59 +00:00
parse: v => v.styleMeta.installDate,
sorter: sorterType.number,
2017-12-23 00:11:46 +00:00
},
dateUpdated: {
text: t('dateUpdated'),
2022-09-03 16:36:59 +00:00
parse: ({styleMeta: s}) => s.updateDate || s.installDate,
sorter: sorterType.number,
},
size: {
text: t('genericSize'),
parse: v => v.styleSize,
sorter: sorterType.number,
},
2017-12-23 00:11:46 +00:00
};
// Adding (assumed) most commonly used ('title,asc' should always be first)
// whitespace before & after the comma is ignored
const selectOptions = [
'{groupAsc}',
'title,asc',
'dateInstalled,desc, title,asc',
'dateInstalled,asc, title,asc',
'dateUpdated,desc, title,asc',
'dateUpdated,asc, title,asc',
'usercss,asc, title,asc',
'usercss,desc, title,asc',
'disabled,asc, title,asc',
'disabled,desc, title,asc',
'disabled,desc, usercss,asc, title,asc',
2022-09-03 16:36:59 +00:00
'size,desc, title,asc',
2017-12-23 00:11:46 +00:00
'{groupDesc}',
'title,desc',
'usercss,asc, title,desc',
'usercss,desc, title,desc',
'disabled,desc, title,desc',
'disabled,desc, usercss,asc, title,desc',
2017-12-23 00:11:46 +00:00
];
const splitRegex = /\s*,\s*/;
2022-02-16 19:33:55 +00:00
const ID = 'manage.newUI.sort';
2022-02-24 03:55:49 +00:00
const getPref = () => prefs.get(ID) || prefs.defaults[ID];
2017-12-23 00:11:46 +00:00
let columns = 1;
2022-02-19 14:12:15 +00:00
function init() {
2022-02-16 19:33:55 +00:00
prefs.subscribe(ID, sorter.update);
$('#sorter-help').onclick = showHelp;
addOptions();
updateColumnCount();
2022-02-19 14:12:15 +00:00
}
2017-12-23 00:11:46 +00:00
function addOptions() {
let container;
2022-02-19 14:12:15 +00:00
const select = $('#' + ID);
2017-12-23 00:11:46 +00:00
const renderBin = document.createDocumentFragment();
const option = $create('option');
const optgroup = $create('optgroup');
const meta = {
desc: ' \u21E9',
enabled: t('genericEnabledLabel'),
disabled: t('genericDisabledLabel'),
dateNew: ` (${t('sortDateNewestFirst')})`,
dateOld: ` (${t('sortDateOldestFirst')})`,
groupAsc: t('sortLabelTitleAsc'),
groupDesc: t('sortLabelTitleDesc'),
2017-12-23 00:11:46 +00:00
};
selectOptions.forEach(sort => {
if (/{\w+}/.test(sort)) {
2017-12-23 00:11:46 +00:00
if (container) {
renderBin.appendChild(container);
}
container = optgroup.cloneNode();
container.label = meta[sort.substring(1, sort.length - 1)];
return;
}
let lastTag = '';
const opt = option.cloneNode();
opt.textContent = sort.split(splitRegex).reduce((acc, val) => {
if (tagData[val]) {
lastTag = val;
return acc + (acc !== '' ? ' + ' : '') + tagData[val].text;
}
if (lastTag.indexOf('date') > -1) return acc + meta[val === 'desc' ? 'dateNew' : 'dateOld'];
if (lastTag === 'disabled') return acc + meta[val === 'desc' ? 'enabled' : 'disabled'];
return acc + (meta[val] || '');
}, '');
opt.value = sort;
container.appendChild(opt);
});
renderBin.appendChild(container);
select.appendChild(renderBin);
2022-02-16 19:33:55 +00:00
select.value = getPref();
2017-12-23 00:11:46 +00:00
}
return {
2017-12-23 00:11:46 +00:00
2022-02-19 14:12:15 +00:00
init,
2022-09-03 16:36:59 +00:00
sort(styles) {
2022-02-16 19:33:55 +00:00
const sortBy = getPref().split(splitRegex);
const len = sortBy.length;
return styles.sort((a, b) => {
let types, direction;
let result = 0;
let index = 0;
// multi-sort
while (result === 0 && index < len) {
types = tagData[sortBy[index++]];
direction = sortBy[index++] === 'asc' ? 1 : -1;
result = types.sorter(types.parse(a), types.parse(b)) * direction;
}
return result;
});
},
2017-12-23 00:11:46 +00:00
update() {
if (!installed) return;
const current = [...installed.children];
2022-09-03 16:36:59 +00:00
const sorted = sorter.sort([...current]);
if (current.some((el, i) => el !== sorted[i])) {
installed.append(...sorted);
2017-12-23 00:11:46 +00:00
}
sorter.updateStripes();
},
updateStripes({onlyWhenColumnsChanged} = {}) {
if (onlyWhenColumnsChanged && !updateColumnCount()) return;
let index = 0;
let isOdd = false;
const flipRows = columns % 2 === 0;
for (const {classList} of installed.children) {
if (classList.contains('hidden')) continue;
classList.toggle('odd', isOdd);
classList.toggle('even', !isOdd);
if (flipRows && ++index >= columns) {
index = 0;
} else {
isOdd = !isOdd;
}
}
},
};
function updateColumnCount() {
let newValue = 1;
for (let el = $.root.lastElementChild;
el.localName === 'style';
el = el.previousElementSibling) {
if (el.textContent.includes('--columns:')) {
newValue = Math.max(1, getComputedStyle($.root).getPropertyValue('--columns') | 0);
break;
}
}
if (columns !== newValue) {
columns = newValue;
return true;
}
2017-12-23 00:11:46 +00:00
}
async function showHelp(event) {
2017-12-23 00:11:46 +00:00
event.preventDefault();
messageBoxProxy.show({
className: 'help-text center-dialog',
2017-12-23 00:11:46 +00:00
title: t('sortStylesHelpTitle'),
contents:
$create('div',
t('sortStylesHelp').split('\n').map(line =>
$create('p', line))),
buttons: [t('confirmOK')],
});
}
})();