diff --git a/tonalmine/javascripts/theme.js b/tonalmine/javascripts/theme.js index 0ca85d3..65e67ef 100644 --- a/tonalmine/javascripts/theme.js +++ b/tonalmine/javascripts/theme.js @@ -1,5 +1,5 @@ /* - * tonalmine: per-user appearance (mode, contrast, color palette). + * tonalmine: per-user appearance (mode, contrast, size, color palette). * * Redmine loads this file in , so the saved choice is applied before the page renders * (no flash of the default colors). The picker is added to the profile menu and the mobile @@ -18,9 +18,10 @@ ]; var MODES = [['auto', 'Auto'], ['light', 'Light'], ['dark', 'Dark']]; var CONTRASTS = [['standard', 'Standard'], ['medium', 'Medium'], ['high', 'High']]; + var SIZES = [['compact', 'Compact'], ['normal', 'Normal'], ['large', 'Large']]; var KEY = 'tonalmine'; var CUSTOM_SEL = ':root[data-palette="custom"]'; - var DEFAULT_KEYS = ['mode', 'contrast', 'palette', 'seed', 'css']; + var DEFAULT_KEYS = ['mode', 'contrast', 'size', 'palette', 'seed', 'css']; var root = document.documentElement; function load() { @@ -34,6 +35,8 @@ else root.removeAttribute('data-mode'); if (prefs.contrast === 'medium' || prefs.contrast === 'high') root.setAttribute('data-contrast', prefs.contrast); else root.removeAttribute('data-contrast'); + if (prefs.size === 'compact' || prefs.size === 'large') root.setAttribute('data-size', prefs.size); + else root.removeAttribute('data-size'); // The custom palette's rules are generated once (see withScheme) and kept with the prefs. if (prefs.palette === 'custom' && prefs.css) { var style = document.getElementById('nm-custom-palette'); @@ -73,7 +76,7 @@ // palette, seed and css form one part: a custom palette brings its own generated CSS. function merge(base, top) { var src = top.palette ? top : base; - return { mode: top.mode || base.mode, contrast: top.contrast || base.contrast, + return { mode: top.mode || base.mode, contrast: top.contrast || base.contrast, size: top.size || base.size, palette: src.palette, seed: src.seed, css: src.css }; } @@ -130,6 +133,7 @@ function pickerHtml(prefs, isDefault) { var mode = prefs.mode || 'auto'; var contrast = prefs.contrast || 'standard'; + var size = prefs.size || 'normal'; var palette = prefs.palette || PALETTES[0][0]; return '
' + (isDefault ? '' : '
Appearance
') + @@ -140,6 +144,10 @@ '
' + CONTRASTS.map(function (c) { return button('data-nm-contrast', c[0], c[1], c[0] === contrast); }).join('') + '
' + + '
Size
' + + '
' + + SIZES.map(function (s) { return button('data-nm-size', s[0], s[1], s[0] === size); }).join('') + + '
' + '
' + PALETTES.map(function (p) { return button('data-nm-palette', p[0], '', p[0] === palette, @@ -162,6 +170,9 @@ picker.querySelectorAll('[data-nm-contrast]').forEach(function (b) { b.setAttribute('aria-pressed', String(b.getAttribute('data-nm-contrast') === (prefs.contrast || 'standard'))); }); + picker.querySelectorAll('[data-nm-size]').forEach(function (b) { + b.setAttribute('aria-pressed', String(b.getAttribute('data-nm-size') === (prefs.size || 'normal'))); + }); picker.querySelectorAll('[data-nm-palette]').forEach(function (b) { b.setAttribute('aria-pressed', String(b.getAttribute('data-nm-palette') === (prefs.palette || PALETTES[0][0]))); }); @@ -190,13 +201,14 @@ } document.addEventListener('click', function (event) { - var target = event.target.closest('[data-nm-mode], [data-nm-contrast], [data-nm-palette], [data-nm-reset]'); + var target = event.target.closest('[data-nm-mode], [data-nm-contrast], [data-nm-size], [data-nm-palette], [data-nm-reset]'); if (!target) return; var isDefault = !!target.closest('[data-nm-default]'); var prefs = isDefault ? draft : own; if (target.hasAttribute('data-nm-reset')) own = {}; else if (target.hasAttribute('data-nm-mode')) prefs.mode = target.getAttribute('data-nm-mode'); else if (target.hasAttribute('data-nm-contrast')) prefs.contrast = target.getAttribute('data-nm-contrast'); + else if (target.hasAttribute('data-nm-size')) prefs.size = target.getAttribute('data-nm-size'); // The custom swatch opens the color dialog; it only selects the palette once a color was picked. else if (target.getAttribute('data-nm-palette') !== 'custom' || prefs.css) prefs.palette = target.getAttribute('data-nm-palette'); changed(isDefault, true); @@ -225,26 +237,26 @@ // select. The hidden inputs are saved by the settings form as Setting.plugin_redmine_tonalmine. function defaultPicker(select) { draft = merge({}, defaults); - // One labelled form row per group (mode, contrast, color), each in its own default picker + // One labelled form row per group (mode, contrast, size, color), each in its own default picker // wrapper so sync() and the click handlers treat it like the whole picker. var picker = document.createElement('div'); picker.innerHTML = pickerHtml(draft, true); var groups = picker.querySelectorAll('[role="group"]'); - var rows = ['Default appearance', 'Contrast', 'Color'].map(function (label, i) { + var rows = ['Default appearance', 'Contrast', 'Size', 'Color'].map(function (label, i) { var row = document.createElement('p'); row.innerHTML = '
'; row.lastChild.appendChild(groups[i]); return row; }); - rows[2].insertAdjacentHTML('beforeend', DEFAULT_KEYS.map(function (key) { + rows[3].insertAdjacentHTML('beforeend', DEFAULT_KEYS.map(function (key) { return ''; }).join('')); - select.closest('p').after(rows[0], rows[1], rows[2]); + select.closest('p').after(rows[0], rows[1], rows[2], rows[3]); // Only shown, and only posted, while Tonalmine is selected; otherwise the saved default stays. function toggle() { var on = select.value === 'tonalmine'; rows.forEach(function (row) { row.style.display = on ? '' : 'none'; }); - rows[2].querySelectorAll('[data-nm-default-input]').forEach(function (input) { input.disabled = !on; }); + rows[3].querySelectorAll('[data-nm-default-input]').forEach(function (input) { input.disabled = !on; }); } select.addEventListener('change', toggle); toggle();