1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 | // Shared pieces for percent-meter chips (volume, brightness): the
// collapsible percentage value, the peek-on-change reveal, and the
// segmented icon fill. Widgets compose these in their own create() —
// see volume.js and brightness.js.
export const METER_SETTINGS = [
{ key: "showPercent", label: "Show percent", type: "boolean", default: false },
{
key: "peekOnChange",
label: "Show percent briefly on change",
type: "boolean",
default: true,
},
];
// The collapsible "100%" value: fixed 4ch when shown, zero-width when
// the chip has the "compact" class.
export function meterValueCss(widgetClass) {
return `
.${widgetClass} .value {
width: 4ch; /* "100%" */
text-align: left;
overflow: hidden;
transition: width 200ms ease, opacity 200ms ease, margin-left 200ms ease;
}
/* Collapsed: zero width plus a negative margin to cancel the chip gap */
.${widgetClass}.compact .value {
width: 0;
opacity: 0;
margin-left: -5px;
}
`;
}
// Temporarily reveal the percentage after a change, then re-collapse.
// cancel() in the widget's destroy so the timer doesn't outlive the chip.
export function makePeek(el, ms = 5000) {
let timer = null;
return {
show() {
el.classList.remove("compact");
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
el.classList.add("compact");
timer = null;
}, ms);
},
cancel() {
if (timer) clearTimeout(timer);
timer = null;
},
};
}
const clamp01 = (x) => Math.min(1, Math.max(0, x));
// Proportional icon fill: each element owns an equal slice of 0–100 and
// fades from minOpacity to full as the value sweeps through its slice.
export function segmentFill(els, percent, minOpacity) {
const slice = 100 / els.length;
els.forEach((el, i) => {
const t = clamp01((percent - i * slice) / slice);
el.style.opacity = (minOpacity + (1 - minOpacity) * t).toFixed(3);
});
}
|