node-radio-mini
Version:
A terminal based radio streaming solution made entirely in Node.js
176 lines (136 loc) • 4.76 kB
JavaScript
const { screen } = require('./screens-config');
const Ut = require('../utils');
const __ = {};
__.render = screen.render.bind(screen);
function KeyListenerFactory({ box, actionFn, bgPlain, bgFocus }) {
const getHeight = () => box.height - 1;
const getChildrenLength = () => box.children.length - 1;
const doChildrenOverlflow = () => getHeight() < getChildrenLength();
const getNavigationLimit = () => Math.min(box.children.length - 1, getHeight() - 1);
const setActiveChildColor = (color) => {
const activeChild = box.children[focusIndexer.get()];
if (activeChild) {
activeChild.style.bg = color;
}
};
const focusIndexer = (function makeIndexer() {
let index = 1;
return {
get: () => index,
incr: () => index < getNavigationLimit() && ++index,
decr: () => index > 1 && --index
};
})();
const scrollRememberer = (function makeRememberer() {
let borderHitAndShouldScroll = false;
return {
shouldScroll: (direction) => borderHitAndShouldScroll === direction,
startScroll: (direction) => { borderHitAndShouldScroll = direction; },
stopScroll: () => { borderHitAndShouldScroll = false; }
};
})();
function navigator(key) {
if (box.children.length === 1) {
return;
}
const unfocusedIndex = focusIndexer.get();
const unfocusedChild = box.children[unfocusedIndex];
if (key === 'k') {
focusIndexer.decr();
}
else if (key === 'l') {
focusIndexer.incr();
}
const focusedIndex = focusIndexer.get();
const focusedChild = box.children[focusedIndex];
unfocusedChild.style.bg = bgPlain;
focusedChild.style.bg = bgFocus;
__.render();
}
function action({ fromTop } = {}) {
const index = fromTop ? 1 : focusIndexer.get();
const child = box.children[index];
const content = child && child.content;
if (!content) {
return {};
}
actionFn({
content,
index: focusIndexer.get(),
cb: focusIndexer.decr
});
__.render();
return { content, index };
}
function preFocus() {
setActiveChildColor(bgFocus);
}
function postFocus() {
setActiveChildColor(bgPlain);
}
function changeOrder(key) {
if (box.children.length === 1) {
return;
}
const index1 = focusIndexer.get();
const child1 = box.children[index1];
if (key === 'a') {
focusIndexer.decr();
}
else if (key === 'z') {
focusIndexer.incr();
}
const index2 = focusIndexer.get();
const child2 = box.children[index2];
child1.style.bg = bgPlain;
child2.style.bg = bgFocus;
[child1.content, child2.content] = [
`${Ut.firstWord(child1.content)} ${Ut.noFirstWord(child2.content)}`,
`${Ut.firstWord(child2.content)} ${Ut.noFirstWord(child1.content)}`,
];
__.render();
return { index1, index2 };
}
function circleList(key) {
if (box.children.length === 1) {
return;
}
if (key === 'k' && scrollRememberer.shouldScroll('up')) {
const temp = box.children[box.children.length - 1].content;
box.children.reduceRight((lowerChild, upperChild) => {
lowerChild.content = upperChild.content;
return upperChild;
});
box.children[1].content = temp;
}
else if (key === 'l' && scrollRememberer.shouldScroll('down')) {
const temp = box.children[1].content;
box.children.reduce((upperChild, lowerChild, index) => {
if (index > 1) {
upperChild.content = lowerChild.content;
}
return lowerChild;
});
box.children[box.children.length - 1].content = temp;
}
else if (
(focusIndexer.get() === 1 && doChildrenOverlflow()) ||
focusIndexer.get() === getHeight() - 1
) {
scrollRememberer.startScroll(key === 'k' ? 'up' : 'down');
}
else {
scrollRememberer.stopScroll();
}
__.render();
}
return Object.freeze({
navigator,
action,
preFocus,
postFocus,
...(box.children[0].content === 'Queue' && { changeOrder }),
...(box.children[0].content === 'Playlist' && { circleList })
});
}
module.exports = KeyListenerFactory;