diff --git a/mod.ts b/mod.ts index 0ceeb18..80ea6a1 100644 --- a/mod.ts +++ b/mod.ts @@ -1,2 +1,2 @@ -export { isDarkMode } from './src/accessibility.js' -export { xbar, separator } from './src/index.js' \ No newline at end of file +export { isDarkMode } from './src/accessibility.ts' +export { xbar, separator } from './src/index.ts' \ No newline at end of file diff --git a/src/accessibility.js b/src/accessibility.js deleted file mode 100644 index 69d4b12..0000000 --- a/src/accessibility.js +++ /dev/null @@ -1,78 +0,0 @@ -import { - pipe, - pipeCompatibleFilter, - pipeCompatibleMap, - pipeCompatibleReduce, - pipeCompatibleSplit, -} from './utility.js'; -// array access layer -const lastIndex = (arr) => arr.length - 1; -const lastItem = (arr) => arr[lastIndex(arr)]; - -// array element transform layer -const appendLastLine = (lines, word) => [ - ...lines.slice(0, lastIndex(lines)), - lastItem(lines).concat(' ', word), -]; -const newLine = (lines, newLine) => lines.concat(newLine); - -// conditional logic layer -const isUnderLimit = (line, word, lineLength) => - line.length + word.length + 1 <= lineLength || line === ''; //if line is empty always return true - -// array reducer -const reduceLines = (lineLength) => - (lines, word) => { - return isUnderLimit(lastItem(lines), word, lineLength) - ? appendLastLine(lines, word) - : newLine(lines, word); - }; - -// word wrap transformations -const spiltToWords = pipeCompatibleSplit(' '); -const removeExtraSpaces = pipeCompatibleFilter((word) => word !== ''); - -const reduceToLines = (maxLineLength) => - pipeCompatibleReduce(reduceLines(maxLineLength))(['']); - -const passPropertiesToLInes = (propertiesToPass) => - pipeCompatibleMap((line) => ({ - text: line.trim(), - ...propertiesToPass, - })); - -// apply transformations in order with pipe -const applyWordWrap = ({ text, wordWrap, ...menuItemProperties }) => - pipe( - spiltToWords, - removeExtraSpaces, - reduceToLines(wordWrap), - passPropertiesToLInes(menuItemProperties), - )(text); - -// entry layer conditional logic -const hasWordWrap = (item) => !!item.wordWrap; -const hasText = (item) => !!item.text; -const shouldApplyWordWrap = (item) => hasWordWrap(item) && hasText(item); - -// deno-lint-ignore no-unused-vars -const ignoreWordWrap = ({ wordWrap, ...item }) => item; - -// entry layer -export const wordWrap = (menuItem) => - shouldApplyWordWrap(menuItem) - ? applyWordWrap(menuItem) - : [ignoreWordWrap(menuItem)]; - -/** - * returns true if XBARDarkMode is enabled. - * allways returns false if 'env' permission has not been previously granted. - * @async - * @returns {Promise} - */ -export const isDarkMode = async () => { - const permissions = await Deno.permissions.query({ name: 'env' }); - return permissions.state === 'granted' - ? Deno.env.get('XBARDarkMode') === 'true' - : false; -}; diff --git a/src/accessibility.ts b/src/accessibility.ts new file mode 100644 index 0000000..862cfd7 --- /dev/null +++ b/src/accessibility.ts @@ -0,0 +1,89 @@ +import { + pipe, + pipeCompatibleFilter, + pipeCompatibleMap, + pipeCompatibleReduce, + pipeCompatibleSplit, +} from './utility.ts'; + +import { Menu, MenuItem } from './types.d.ts'; +// array access layer +const lastIndex = (arr: string[]) => arr.length - 1; +const lastItem = (arr: string[]) => arr[lastIndex(arr)]; + +// array element transform layer +const appendLastLine = (lines: string[], word: string) => [ + ...lines.slice(0, lastIndex(lines)), + lastItem(lines).concat(' ', word), +]; +const newLine = (lines: string[], newLine: string) => lines.concat(newLine); + +// conditional logic layer +const isUnderLimit = (line: string, word: string, lineLength: number) => + line.length + word.length + 1 <= lineLength || line === ''; //if line is empty always return true + +// array reducer +const reduceLines = (lineLength: number) => + (lines: string[], word: string) => { + return isUnderLimit(lastItem(lines), word, lineLength) + ? appendLastLine(lines, word) + : newLine(lines, word); + }; + +// word wrap transformations +const spiltToWords = pipeCompatibleSplit(' '); +const removeExtraSpaces = pipeCompatibleFilter((word: string) => word !== ''); + +const reduceToLines = (maxLineLength: number) => + pipeCompatibleReduce(reduceLines(maxLineLength))(['']); + +const passPropertiesToLInes = (propertiesToPass: Menu) => + pipeCompatibleMap((line: string) => ({ + text: line.trim(), + ...propertiesToPass, + })); + +const castToType = (menuItem: MenuItem): { text: string; wordWrap: number } => ( + { + text: `${menuItem.text}`, + wordWrap: parseInt(`${menuItem.wordWrap}`), + ...menuItem, + } +); + +// apply transformations in order with pipe +const applyWordWrap = ( + { text, wordWrap, ...menuItemProperties }: { text: string; wordWrap: number }, +): MenuItem[] => + pipe( + spiltToWords, + removeExtraSpaces, + reduceToLines(wordWrap), + passPropertiesToLInes(menuItemProperties), + )(text); + +// entry layer conditional logic +const hasWordWrap = (item: MenuItem) => !!item.wordWrap; +const hasText = (item: MenuItem) => !!item.text; +const shouldApplyWordWrap = (item: MenuItem) => + hasWordWrap(item) && hasText(item); + +// deno-lint-ignore no-unused-vars +const ignoreWordWrap = ({ wordWrap, ...item }: MenuItem) => item; + +// entry layer +export const wordWrap = (menuItem: MenuItem) => + shouldApplyWordWrap(menuItem) + ? applyWordWrap(castToType(menuItem)) + : [ignoreWordWrap(menuItem)]; + +/** + * returns true if XBARDarkMode is enabled. + * allways returns false if 'env' permission has not been previously granted. + */ +export const isDarkMode = async (): Promise => { + const permissions = await Deno.permissions.query({ name: 'env' }); + return permissions.state === 'granted' + ? Deno.env.get('XBARDarkMode') === 'true' + : false; +}; diff --git a/src/format.js b/src/format.js deleted file mode 100644 index d6447a9..0000000 --- a/src/format.js +++ /dev/null @@ -1,32 +0,0 @@ -import { pipe, pipeCompatibleReduce } from './utility.js'; - -// line Object access layer -const getText = (lineObj) => lineObj.text ?? ''; -const getSubMenuLevel = (lineObj) => lineObj.submenuLevel; -// deno-lint-ignore no-unused-vars -const ignoreSubMenuLevel = ({ submenuLevel, ...lineObj }) => lineObj; -// deno-lint-ignore no-unused-vars -const ignoreText = ({ text, ...lineObj }) => lineObj; - -// line formatting layer -const getSubMenuFormatting = (lineObj) => '--'.repeat(getSubMenuLevel(lineObj)); -const getFormattedText = (lineObj) => `${getText(lineObj)}`.trim(); -const formatOutputOptions = pipeCompatibleReduce((output, [option, value]) => - output.concat(` | ${option}=${value}`) -)(''); - -const getFormattedOutputOptions = pipe( - ignoreText, - ignoreSubMenuLevel, - Object.entries, - formatOutputOptions, -); - -// entry layer -export const formatLine = (lineObj) => { - const formattedLine = getSubMenuFormatting(lineObj) + - getFormattedText(lineObj) + - getFormattedOutputOptions(lineObj); - console.log(formattedLine); - return formattedLine; -}; diff --git a/src/format.ts b/src/format.ts new file mode 100644 index 0000000..e436310 --- /dev/null +++ b/src/format.ts @@ -0,0 +1,37 @@ +import { pipe, pipeCompatibleReduce } from './utility.ts'; +import { Menu } from './types.d.ts'; + +// line Object access layer +const getText = (lineObj: Menu) => lineObj.text ?? ''; +const getSubMenuLevel = (lineObj: Menu) => parseInt(`${lineObj.submenuLevel}`); + +// deno-lint-ignore no-unused-vars +const ignoreSubMenuLevel = ({ submenuLevel, ...lineObj }: Menu) => lineObj; +// deno-lint-ignore no-unused-vars +const ignoreText = ({ text, ...lineObj }: Menu) => lineObj; + +// line formatting layer +const getSubMenuFormatting = (lineObj: Menu) => + '--'.repeat(getSubMenuLevel(lineObj)); +const getFormattedText = (lineObj: Menu) => `${getText(lineObj)}`.trim(); + +const formatOutputOptions = pipeCompatibleReduce(( + output: string, + [option, value]: string, +) => output.concat(` | ${option}=${value}`))(''); + +const getFormattedOutputOptions = pipe( + ignoreText, + ignoreSubMenuLevel, + Object.entries, + formatOutputOptions, +); + +// entry layer +export const formatLine = (lineObj: Menu) => { + const formattedLine = getSubMenuFormatting(lineObj) + + getFormattedText(lineObj) + + getFormattedOutputOptions(lineObj); + console.log(formattedLine); + return formattedLine; +}; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index bc53ab1..0000000 --- a/src/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import { pipe, pipeCompatibleMap } from './utility.js'; -import { wordWrap } from './accessibility.js'; -import { formatLine } from './format.js'; - -// item layer -const isSeparator = (item) => item === separator; - -const hasSubmenu = (item) => !!item.submenu; - -const getSubmenu = (item) => - item.submenu.map((e) => ({ - ...e, - submenuLevel: item.submenuLevel ? item.submenuLevel + 1 : 1, - })); - -// deno-lint-ignore no-unused-vars -const ignoreSubmenu = ({ submenu, ...item }) => item; - -const formatItem = pipe(wordWrap, pipeCompatibleMap(formatLine)); - -// array layer -const addItem = (arr, item) => [ - ...arr, - ...formatItem(item), -]; - -const addItemWithSubmenu = (arr, item) => [ - ...addItem(arr, ignoreSubmenu(item)), - ...pipe(getSubmenu, xbar)(item), -]; - -const includeSeparator = (arr) => [ - ...arr, - formatLine(separator), -]; - -//entry layer -/** - * Prints output to xbar and returns printed output as an array of strings - * @param layout - array of xbar menu items - * @returns {string[]} array of strings that were printed as menu items - */ -export const xbar = (layout) => - layout.reduce((menuItems, item) => { - switch (true) { - case (isSeparator(item)): - return includeSeparator(menuItems); - case (hasSubmenu(item)): - return addItemWithSubmenu(menuItems, item); - default: - return addItem(menuItems, item); - } - }, []); - -/** used for declaring a separator*/ -export const separator = { - text: '---', -}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..249c117 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,57 @@ +import { pipe, pipeCompatibleMap } from './utility.ts'; +import { wordWrap } from './accessibility.ts'; +import { formatLine } from './format.ts'; +import { Menu, MenuItem, MenuItemWithSubMenu } from './types.d.ts'; + +// item layer +//const isSeparator = (item:MenuItem) => item === separator; + +const hasSubmenu = (item: MenuItem) => !!item.submenu; + +const getSubmenu = (item: MenuItemWithSubMenu) => + item.submenu.map((e) => ({ + ...e, + submenuLevel: item.submenuLevel ? item.submenuLevel + 1 : 1, + })); + +// deno-lint-ignore no-unused-vars +const ignoreSubmenu = ({ submenu, ...item }: MenuItem): Menu => item; + +const formatItem = pipe(wordWrap, pipeCompatibleMap(formatLine)); + +// array layer +const addItem = (arr: string[], item: Menu) => [ + ...arr, + ...formatItem(item), +]; + +const addItemWithSubmenu = (arr: string[], item: MenuItem) => [ + ...addItem(arr, ignoreSubmenu(item)), + ...pipe(getSubmenu, xbar)(item), +]; + +//const includeSeparator = (arr:MenuItem[]) => [ +// ...arr, +// formatLine(separator), +//]; + +//entry layer +/** + * Prints output to xbar and returns printed output as an array of strings + */ +export const xbar = (layout: MenuItem[]): string[] => + layout.reduce((menuItems: string[], item) => { + switch (true) { + // case (isSeparator(item)): + // return includeSeparator(menuItems); + case (hasSubmenu(item)): + return addItemWithSubmenu(menuItems, item); + default: + return addItem(menuItems, item); + } + }, []); + +/** used for declaring a separator*/ +export const separator = { + text: '---', +}; diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..a542fe0 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,49 @@ +interface Menu { + text?: string; + color?: string; + wordWrap?: number; + size?: number; + submenuLevel?: number; + href?: string; + font?: string; + shell?: string; + terminal?: boolean; + refresh?: boolean; + dropdown?: boolean; + length?: number; + trim?: boolean; + alternate?: boolean; + templateImage?: string; + image?: string; + emojize?: boolean; + ansi?: boolean; + key?: string; + param1?: string; + param2?: string; + param3?: string; + param4?: string; + param5?: string; + param6?: string; + param7?: string; + param8?: string; + param9?: string; + param10?: string; +} + +export interface MenuItem extends Menu { + submenu?: Menu[]; +} + +export interface MenuItemWithSubMenu extends Menu { + submenu: Menu[]; +} + +export interface MenuItemWithWordWrap extends Menu { + text: string; + wordWrap: number; +} + +export type separator = { + text: '---'; +}; +export type Layout = Array; diff --git a/src/utility.js b/src/utility.js deleted file mode 100644 index f925d92..0000000 --- a/src/utility.js +++ /dev/null @@ -1,14 +0,0 @@ -export const pipe = (...functions) => - (x) => - functions.reduce( - (acc, fn) => fn(acc), - x, - ); - -export const pipeCompatibleMap = (callback) => (target) => target.map(callback); -export const pipeCompatibleReduce = (callback) => - (initValue) => (target) => target.reduce(callback, initValue); -export const pipeCompatibleSplit = (separator) => - (target) => target.split(separator); -export const pipeCompatibleFilter = (callback) => - (target) => target.filter(callback); diff --git a/src/utility.ts b/src/utility.ts new file mode 100644 index 0000000..14671cc --- /dev/null +++ b/src/utility.ts @@ -0,0 +1,27 @@ +// deno-lint-ignore ban-types +export const pipe = (...functions: Function[]) => + // deno-lint-ignore no-explicit-any + (x: any) => + functions.reduce( + (acc, fn) => fn(acc), + x, + ); + +export const pipeCompatibleSplit = (separator: string) => + (target: string) => target.split(separator); + +export const pipeCompatibleMap = (callback: (x: T1) => T2) => + (target: Array) => target.map(callback); + +export const pipeCompatibleFilter = (callback: (x: T) => boolean) => + (target: Array) => target.filter(callback); + +export const pipeCompatibleReduce = ( + callback: ( + previousValue: T2, + currentValue: T1, + currentIndex: number, + array: T1[], + ) => T2, +) => + (initValue: T2) => (target: Array) => target.reduce(callback, initValue); diff --git a/test/accessibility.test.js b/test/accessibility.test.js index d1facb3..98401ce 100644 --- a/test/accessibility.test.js +++ b/test/accessibility.test.js @@ -1,5 +1,5 @@ import { assertEquals } from 'https://deno.land/std@0.128.0/testing/asserts.ts'; -import { wordWrap } from '../src/accessibility.js'; +import { wordWrap } from '../src/accessibility.ts'; Deno.test('wordWrap splits a string into an array of lines', () => { const input = { diff --git a/test/format.test.js b/test/format.test.js index ab620f1..aa2aeff 100644 --- a/test/format.test.js +++ b/test/format.test.js @@ -2,7 +2,7 @@ import { assert, assertEquals, } from 'https://deno.land/std@0.128.0/testing/asserts.ts'; -import { formatLine } from '../src/format.js'; +import { formatLine } from '../src/format.ts'; Deno.test('formatLine returns a string', () => { const input = { text: diff --git a/test/index.test.js b/test/index.test.js index 71e6c72..f5fd9bc 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -2,7 +2,7 @@ import { assert, assertEquals, } from 'https://deno.land/std@0.128.0/testing/asserts.ts'; -import { separator, xbar } from '../src/index.js'; +import { separator, xbar } from '../src/index.ts'; Deno.test('xbar returns separator as ---', () => { const input = [separator];