From 09e0ad25e3f39993c0db10c333ceb1229ff75d57 Mon Sep 17 00:00:00 2001 From: Elia Lazzari Date: Fri, 22 Sep 2023 15:25:06 +0200 Subject: [PATCH] [Feature]: addColumn (instead of double or quad) Fixes #74 Added fitHeight layout option --- README.md | 2 + docs/Box.md | 12 +++- docs/Button.md | 34 +++++----- docs/ButtonPopup.md | 15 ++++- docs/ConfirmPopup.md | 11 +++- docs/Control.md | 32 ++++++++- docs/CustomPopup.md | 7 +- docs/DoubleLayout.md | 1 + docs/FileSelectorPopup.md | 9 ++- docs/LayoutManager.md | 1 + docs/OptionPopup.md | 9 ++- docs/PageBuilder.md | 6 +- docs/ProgressBar.md | 74 ++++++++++++++++++++- docs/QuadLayout.md | 1 + docs/Screen.md | 5 +- docs/SingleLayout.md | 1 + docs/Utils.md | 6 +- examples/layout.mjs | 90 ++++++++++++++++++++++++++ package.json | 2 +- src/components/layout/DoubleLayout.ts | 54 ++++++++++++---- src/components/layout/LayoutManager.ts | 5 ++ src/components/layout/QuadLayout.ts | 9 ++- src/components/layout/SingleLayout.ts | 26 ++++++-- 23 files changed, 360 insertions(+), 52 deletions(-) create mode 100644 examples/layout.mjs diff --git a/README.md b/README.md index c410746..6e3a7eb 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ const layoutOptions = { direction: 'vertical', // Set to 'horizontal' to enable horizontal layout (only for "double" layout) boxColor: 'yellow', // The color of the box boxStyle: 'bold', // The style of the box (bold) + fitHeight: true, // Set to false to disable the auto height fit [default: false] (since v3.2.0 - **NEW!**) } ``` @@ -383,6 +384,7 @@ In the "layoutOptions" provided to the ConsoleManager, we can set the layout: - direction: Set to 'horizontal' to enable horizontal layout (only for "double" layout) - boxColor: The color of the box (default: 'yellow') - boxStyle: The style of the box (default: 'bold') + - fitHeight: Set to false to disable the auto height fit [default: false] (since v3.2.0 - **NEW!**) To draw multiple pages, we need to use the setPage or setPages methods: diff --git a/docs/Box.md b/docs/Box.md index 037397e..643897f 100644 --- a/docs/Box.md +++ b/docs/Box.md @@ -65,4 +65,14 @@ | config | [BoxConfig](#BoxConfig) |

The configuration of the box.

| **Example** -```ts const box = new Box({ id: "box", x: 0, y: 0, width: 10, height: 5, style: { boxed: true, color: "red", label: "Box" } }) box.setContent(new InPageWidgetBuilder(5).addText("Hello World!")) ``` +```ts +const box = new Box({ + id: "box", + x: 0, + y: 0, + width: 10, + height: 5, + style: { boxed: true, color: "red", label: "Box" } +}) +box.setContent(new InPageWidgetBuilder(5).addText("Hello World!")) +``` diff --git a/docs/Button.md b/docs/Button.md index 49bce8d..cc14011 100644 --- a/docs/Button.md +++ b/docs/Button.md @@ -98,19 +98,21 @@ | config | [ButtonConfig](#ButtonConfig) |

The configuration object

| **Example** -```js new Button({ - id: "btnRun", - text: "Run me!", - x: 21, - y: 18, - style: { - color: "magentaBright", - bold: true, - italic: true, - borderColor: "green" - }, - onRelease: () => { - GUI.log("Button clicked!") - }, - draggable: true, - }) ``` +```js +new Button({ + id: "btnRun", + text: "Run me!", + x: 21, + y: 18, + style: { + color: "magentaBright", + bold: true, + italic: true, + borderColor: "green" + }, + onRelease: () => { + GUI.log("Button clicked!") + }, + draggable: true, + }) +``` diff --git a/docs/ButtonPopup.md b/docs/ButtonPopup.md index 211227f..b309c71 100644 --- a/docs/ButtonPopup.md +++ b/docs/ButtonPopup.md @@ -78,7 +78,20 @@ | config | [ButtonPopupConfig](#ButtonPopupConfig) |

The configuration of the popup.

| **Example** -```ts const popup = new ButtonPopup({ id: "popup1", title: "Choose the option", buttons: ["YES", "NO", "?"], }) popup.show() // show the popup popup.on("confirm", () => { console.log("User confirmed") }) popup.on("cancel", () => { console.log("User canceled") }) ``` +```ts +const popup = new ButtonPopup({ + id: "popup1", + title: "Choose the option", + buttons: ["YES", "NO", "?"], +}) +popup.show() // show the popup +popup.on("confirm", () => { + console.log("User confirmed") +}) +popup.on("cancel", () => { + console.log("User canceled") +}) +``` ### buttonPopup.keyListener(_str, key) diff --git a/docs/ConfirmPopup.md b/docs/ConfirmPopup.md index 053ef96..8697c1d 100644 --- a/docs/ConfirmPopup.md +++ b/docs/ConfirmPopup.md @@ -49,4 +49,13 @@ | config | [ConfirmPopupConfig](#ConfirmPopupConfig) |

The configuration of the popup.

| **Example** -```ts const popup = new ConfirmPopup({ id: "popup1", title: "Are you shure", }) popup.show() // show the popup popup.on("confirm", (answer) => { console.log(console.log(answer)) }) ``` +```ts +const popup = new ConfirmPopup({ + id: "popup1", + title: "Are you shure", +}) +popup.show() // show the popup +popup.on("confirm", (answer) => { + console.log(console.log(answer)) +}) +``` diff --git a/docs/Control.md b/docs/Control.md index b867d1e..ca7473b 100644 --- a/docs/Control.md +++ b/docs/Control.md @@ -74,7 +74,33 @@ absolute position on the screen. It's a base class for all the controls (widgets | config | [ControlConfig](#ControlConfig) |

The configuration object for the control.

| **Example** -```ts const widget1 = new InPageWidgetBuilder() widget1.addRow({ text: "┌────────┐", color: "yellow", style: "bold" }) widget1.addRow({ text: "│ START! │", color: "yellow", style: "bold" }) widget1.addRow({ text: "└────────┘", color: "yellow", style: "bold" }) const button1 = new Control({ id: "btn1", visible: false, attributes: { x: 30, y: 18, width: 10, height: 3 }, children: widget1 }) button1.on("relativeMouse", (event) => { // The relative mouse event is triggered with the mouse position relative to the widget //console.log(`Mouse event: x: ${event.data.x}, y: ${event.data.y}`) if (event.name === "MOUSE_LEFT_BUTTON_RELEASED") { GUI.log("Button 1 clicked!") if (valueEmitter) { clearInterval(valueEmitter) valueEmitter = null } else { valueEmitter = setInterval(frame, period) } } }) button1.show() ``` +```ts +const widget1 = new InPageWidgetBuilder() +widget1.addRow({ text: "┌────────┐", color: "yellow", style: "bold" }) +widget1.addRow({ text: "│ START! │", color: "yellow", style: "bold" }) +widget1.addRow({ text: "└────────┘", color: "yellow", style: "bold" }) + +const button1 = new Control({ + id: "btn1", + visible: false, + attributes: { x: 30, y: 18, width: 10, height: 3 }, + children: widget1 +}) +button1.on("relativeMouse", (event) => { + // The relative mouse event is triggered with the mouse position relative to the widget + //console.log(`Mouse event: x: ${event.data.x}, y: ${event.data.y}`) + if (event.name === "MOUSE_LEFT_BUTTON_RELEASED") { + GUI.log("Button 1 clicked!") + if (valueEmitter) { + clearInterval(valueEmitter) + valueEmitter = null + } else { + valueEmitter = setInterval(frame, period) + } + } +}) +button1.show() +``` ### control.delete() @@ -102,7 +128,9 @@ Inside this function are defined all the keys that can be pressed and the action **Kind**: instance method of [Control](#Control) **Returns**: InPageWidgetBuilder -

The content of the Control.

**Example** -```ts const content = control.getContent() ``` +```ts +const content = control.getContent() +``` ### control.focus() ⇒ [Control](#Control) diff --git a/docs/CustomPopup.md b/docs/CustomPopup.md index 8d4fb3e..c7a86e2 100644 --- a/docs/CustomPopup.md +++ b/docs/CustomPopup.md @@ -83,7 +83,12 @@ | config | [PopupConfig](#PopupConfig) |

The configuration of the popup.

| **Example** -```ts const popup = new CustomPopup({ id: "popup1", title: "See that values", content: new PageBuilder().addText("Hello world!"), }).show() +```ts +const popup = new CustomPopup({ + id: "popup1", + title: "See that values", + content: new PageBuilder().addText("Hello world!"), +}).show() ### customPopup.keyListener(str, key) diff --git a/docs/DoubleLayout.md b/docs/DoubleLayout.md index 69d5dd7..3f86985 100644 --- a/docs/DoubleLayout.md +++ b/docs/DoubleLayout.md @@ -42,6 +42,7 @@ | [page1Title] | string |

The title of the first page.

| | [page2Title] | string |

The title of the second page.

| | [pageRatio] | Array.<number> |

The ratio of the pages. (in horizontal direction)

| +| [fitHeight] | boolean |

If the height of the pages should be the same.

| diff --git a/docs/FileSelectorPopup.md b/docs/FileSelectorPopup.md index 7cc4856..a344dc4 100644 --- a/docs/FileSelectorPopup.md +++ b/docs/FileSelectorPopup.md @@ -86,7 +86,14 @@ The user can select a file or a directory and the popup will be closed.

| config | [FileSelectorPopupConfig](#FileSelectorPopupConfig) |

The configuration for the FileSelectorPopup class.

| **Example** -```ts const popup = new FileSelectorPopup({ id: "popup1", title: "Choose the file", basePath: "./examples" }).show().on("confirm", (selected) => { console.log(selected) }) // show the popup and wait for the user to confirm +```ts +const popup = new FileSelectorPopup({ + id: "popup1", + title: "Choose the file", + basePath: "./examples" +}).show().on("confirm", (selected) => { + console.log(selected) +}) // show the popup and wait for the user to confirm ### fileSelectorPopup.listDir(dir) ⇒ Promise.<Array.<object>> diff --git a/docs/LayoutManager.md b/docs/LayoutManager.md index a1c6ef8..f74926a 100644 --- a/docs/LayoutManager.md +++ b/docs/LayoutManager.md @@ -40,6 +40,7 @@ | [direction] | "horizontal" \| "vertical" |

The direction of the layout.

| | [pageTitles] | Array.<string> |

The title of the first page.

| | [pageRatio] | Array.<number> |

The ratio of the pages. (in horizontal direction)

| +| [fitHeight] | boolean |

If the height of the pages should be the same.

| diff --git a/docs/OptionPopup.md b/docs/OptionPopup.md index 52bf40c..ca52d7b 100644 --- a/docs/OptionPopup.md +++ b/docs/OptionPopup.md @@ -84,7 +84,14 @@ | visible | boolean |

If the popup is visible. Default is false (make it appears using show()).

| **Example** -```ts const popup = new OptionPopup({ id:"popup1", title: "Choose the option", options, selected }).show().on("confirm", (option) => { console.log(option) }) // show the popup and wait for the user to confirm ``` +```ts +const popup = new OptionPopup({ + id:"popup1", + title: "Choose the option", + options, + selected +}).show().on("confirm", (option) => { console.log(option) }) // show the popup and wait for the user to confirm +``` ### optionPopup.keyListener(str, key) diff --git a/docs/PageBuilder.md b/docs/PageBuilder.md index b1a5273..2e16306 100644 --- a/docs/PageBuilder.md +++ b/docs/PageBuilder.md @@ -44,7 +44,8 @@ It's a sort of collection of styled rows.

**Example** ```js -page.addRow({ text: 'Hello World', color: 'white' }) page.addRow({ text: 'Hello World', color: 'white' }, { text: 'Hello World', color: 'white' }) +page.addRow({ text: 'Hello World', color: 'white' }) +page.addRow({ text: 'Hello World', color: 'white' }, { text: 'Hello World', color: 'white' }) ``` @@ -59,7 +60,8 @@ page.addRow({ text: 'Hello World', color: 'white' }) page.addRow({ text: 'Hello **Example** ```js -page.addEmptyRow() page.addEmptyRow(2) +page.addEmptyRow() +page.addEmptyRow(2) ``` diff --git a/docs/ProgressBar.md b/docs/ProgressBar.md index 09c820a..8a1b84c 100644 --- a/docs/ProgressBar.md +++ b/docs/ProgressBar.md @@ -104,7 +104,79 @@ | config | [ProgressConfig](#ProgressConfig) |

The configuration object for the progress bar

| **Example** -```js const pStyle = { boxed: true, showTitle: true, showValue: true, showPercentage: true, showMinMax: false, } const p = new Progress({ id: "prog1", x: 10, y: 2, style: pStyle, theme: "htop", length: 25, label: "Mem" }) const incr = setInterval(() => { const value = p.getValue() + 0.25 p.setValue(value) if (value >= p.getMax()) { clearInterval(incr) } }, 100) const p1Style = { background: "bgBlack", borderColor: "yellow", color: "green", boxed: true, showTitle: true, showValue: true, showPercentage: true, showMinMax: true, } const p1 = new Progress({ id: "prog1", x: 10, y: 4, style: pStyle, theme: "precision", length: 25, label: "Precision" }) const incr1 = setInterval(() => { const value = p1.getValue() + 0.25 p1.setValue(value) if (value >= p1.getMax()) { clearInterval(incr1) } }, 100) const p2Style = { background: "bgBlack", borderColor: "yellow", color: "magenta", boxed: true, showTitle: true, showValue: true, showPercentage: true, showMinMax: true, } const p2 = new Progress({ id: "prog3", x: 10, y: 6, style: pStyle, theme: "precision", length: 25, label: "Interactive", direction: "vertical", interactive: true, }) p2.on("valueChanged", (value) => { console.log(`Value changed: ${value}`) }) ``` +```js + const pStyle = { + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: false, + } + const p = new Progress({ + id: "prog1", + x: 10, y: 2, + style: pStyle, + theme: "htop", + length: 25, + label: "Mem" + }) + const incr = setInterval(() => { + const value = p.getValue() + 0.25 + p.setValue(value) + if (value >= p.getMax()) { + clearInterval(incr) + } + }, 100) + + const p1Style = { + background: "bgBlack", + borderColor: "yellow", + color: "green", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + } + const p1 = new Progress({ + id: "prog1", + x: 10, y: 4, + style: pStyle, + theme: "precision", + length: 25, + label: "Precision" + }) + const incr1 = setInterval(() => { + const value = p1.getValue() + 0.25 + p1.setValue(value) + if (value >= p1.getMax()) { + clearInterval(incr1) + } + }, 100) + const p2Style = { + background: "bgBlack", + borderColor: "yellow", + color: "magenta", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + } + const p2 = new Progress({ + id: "prog3", + x: 10, y: 6, + style: pStyle, + theme: "precision", + length: 25, + label: "Interactive", + direction: "vertical", + interactive: true, + }) + p2.on("valueChanged", (value) => { + console.log(`Value changed: ${value}`) + }) +``` ## drawingChars : Object diff --git a/docs/QuadLayout.md b/docs/QuadLayout.md index 84bd459..4419b3f 100644 --- a/docs/QuadLayout.md +++ b/docs/QuadLayout.md @@ -47,6 +47,7 @@ | [page3Title] | string |

The title of the third page.

| | [page4Title] | string |

The title of the fourth page.

| | [pageRatio] | Array.<number> |

The ratio of the pages.

| +| [fitHeight] | boolean |

If the height of the pages should be the same.

| diff --git a/docs/Screen.md b/docs/Screen.md index 292aafd..818ed8d 100644 --- a/docs/Screen.md +++ b/docs/Screen.md @@ -82,7 +82,7 @@ const screen = new Screen(process.stdout) **Example** ```js -screen.write({ text: 'Hello World', color: 'white' }) +screen.write({ text: 'Hello World', color: 'white' }) screen.write({ text: 'Hello World', color: 'white' }, { text: 'Hello World', color: 'white' }) ``` @@ -170,7 +170,8 @@ screen.replaceAt('Hello Luca', 6, 'Elia') // returns 'Hello Elia' **Example** ```js -screen.mergeStyles([{ color: 'red', bg: 'black', italic: false, bold: false, index: [0, 5] }, { color: 'white', bg: 'black', italic: false, bold: false, index: [6, 10] }], [{ color: 'magenta', bg: 'black', italic: false, bold: false, index: [0, 30] }], 5, 15) returns [{ color: 'magenta', bg: 'black', italic: false, bold: false, index: [0, 4] }, { color: 'red', bg: 'black', italic: false, bold: false, index: [5, 10] }, { color: 'white', bg: 'black', italic: false, bold: false, index: [11, 15] }, { color: 'magenta', bg: 'black', italic: false, bold: false, index: [16, 30] }] +screen.mergeStyles([{ color: 'red', bg: 'black', italic: false, bold: false, index: [0, 5] }, { color: 'white', bg: 'black', italic: false, bold: false, index: [6, 10] }], [{ color: 'magenta', bg: 'black', italic: false, bold: false, index: [0, 30] }], 5, 15) +returns [{ color: 'magenta', bg: 'black', italic: false, bold: false, index: [0, 4] }, { color: 'red', bg: 'black', italic: false, bold: false, index: [5, 10] }, { color: 'white', bg: 'black', italic: false, bold: false, index: [11, 15] }, { color: 'magenta', bg: 'black', italic: false, bold: false, index: [16, 30] }] ``` diff --git a/docs/SingleLayout.md b/docs/SingleLayout.md index 9b36ea8..4386fb9 100644 --- a/docs/SingleLayout.md +++ b/docs/SingleLayout.md @@ -36,6 +36,7 @@ | [boxColor] | ForegroundColorName \| HEX \| RGB \| "" |

The color of the box taken from the chalk library.

| | [boxStyle] | "bold" |

If the border of the box should be bold.

| | [pageTitle] | string |

The title of the first page.

| +| [fitHeight] | boolean |

If the height of the layout should be the same as the height of the screen.

| diff --git a/docs/Utils.md b/docs/Utils.md index 4beec22..a4a5c4b 100644 --- a/docs/Utils.md +++ b/docs/Utils.md @@ -154,7 +154,8 @@ CM.truncate("Hello world", 5, true) // "Hello..." **Example** ```js -const simplifiedStyledElement = styledToSimplifiedStyled({ text: "Hello world", style: { color: "red", backgroundColor: "blue", bold: true, italic: true } }) // returns { text: "Hello world", color: "red", backgroundColor: "blue", bold: true, italic: true } +const simplifiedStyledElement = styledToSimplifiedStyled({ text: "Hello world", style: { color: "red", backgroundColor: "blue", bold: true, italic: true } }) +// returns { text: "Hello world", color: "red", backgroundColor: "blue", bold: true, italic: true } ``` @@ -170,7 +171,8 @@ const simplifiedStyledElement = styledToSimplifiedStyled({ text: "Hello world", **Example** ```js -const styledElement = simplifiedStyledToStyled({ text: "Hello world", color: "red", bold: true }) // returns { text: "Hello world", style: { color: "red", bold: true } } +const styledElement = simplifiedStyledToStyled({ text: "Hello world", color: "red", bold: true }) +// returns { text: "Hello world", style: { color: "red", bold: true } } ``` diff --git a/examples/layout.mjs b/examples/layout.mjs new file mode 100644 index 0000000..d66b3f5 --- /dev/null +++ b/examples/layout.mjs @@ -0,0 +1,90 @@ +import { ConsoleManager,ConfirmPopup, PageBuilder } from "../dist/esm/ConsoleGui.mjs" + +const opt = { + title: "Layout Test", + layoutOptions: { + type: "quad", + boxed: true, // Set to true to enable boxed layout + showTitle: true, // Set to false to hide title + changeFocusKey: "ctrl+l", // Change layout with ctrl+l to switch to the logs page + boxColor: "yellow", + boxStyle: "bold", + fitHeight: true, + }, + logLocation: 1, + enableMouse: true +} + +const GUI = new ConsoleManager(opt) + +GUI.on("exit", () => { + closeApp() +}) + +GUI.on("keypressed", (key) => { + switch (key.name) { + case "q": + new ConfirmPopup({ + id: "popupQuit", title: "Are you sure you want to quit?" + }).show().on("confirm", () => closeApp()) + break + default: + break + } +}) + +const closeApp = () => { + console.clear() + process.exit() +} + +GUI.refresh() + +const loremIpsumPage = new PageBuilder() + +loremIpsumPage.addRow({ + text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + color: "red", + bg: "blue", +}) + +loremIpsumPage.addRow({ + text: "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + color: "blue", + bg: "red", +}) + +loremIpsumPage.addRow({ + text: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", + color: "green", + bg: "yellow", +}) + +loremIpsumPage.addRow({ + text: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.", + color: "yellow", + bg: "green", +}) + +loremIpsumPage.addRow({ + text: "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + color: "cyan", + bg: "magenta", +}) + +const loremIpsumOverflowedPage = new PageBuilder() + +// add some random text to the page +for (let i = 0; i < 100; i++) { + loremIpsumOverflowedPage.addRow({ + text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + color: "red", + bg: "blue", + }) +} + +GUI.setPage(loremIpsumPage, 0, "Lorem Ipsum") +GUI.setPage(loremIpsumOverflowedPage, 3, "Lorem Ipsum Overflowed") +GUI.setPage(new PageBuilder(), 2, "Page 3") + +console.log("Done") \ No newline at end of file diff --git a/package.json b/package.json index 2cdb606..5a90362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "console-gui-tools", - "version": "3.1.1", + "version": "3.2.0", "description": "A simple library to draw option menu, text popup or other widgets and layout on a Node.js console.", "main": "dist/esm/ConsoleGui.mjs", "types": "dist/types/ConsoleGui.d.ts", diff --git a/src/components/layout/DoubleLayout.ts b/src/components/layout/DoubleLayout.ts index 477e194..2dc2767 100644 --- a/src/components/layout/DoubleLayout.ts +++ b/src/components/layout/DoubleLayout.ts @@ -21,6 +21,7 @@ import { * @prop {string} [page1Title] - The title of the first page. * @prop {string} [page2Title] - The title of the second page. * @prop {number[]} [pageRatio] - The ratio of the pages. (in horizontal direction) + * @prop {boolean} [fitHeight] - If the height of the pages should be the same. * * @export * @interface DoubleLayoutOptions @@ -36,6 +37,7 @@ export interface DoubleLayoutOptions { page1Title?: string; page2Title?: string; pageRatio?: [number, number]; + fitHeight?: boolean; } /** @@ -408,6 +410,26 @@ export class DoubleLayout { truncate(this.page1Title, this.realWidth[0] - 4, false), truncate(this.page2Title, this.realWidth[1] - 4, false), ] + + const pageHeights = [ + this.page1.getViewedPageHeight(), + this.page2.getViewedPageHeight(), + ] + + if (this.options.fitHeight) { + const decorationHeight = this.options.boxed || this.options.showTitle ? 2 : 0 + const halfHeight = (this.CM.Screen.height - decorationHeight) / 2 - 1 + if (Math.max(...pageHeights) > halfHeight) { + // Change pageHeights to fit the screen + const ratio = halfHeight / Math.max(...pageHeights) + pageHeights[0] = Math.round(pageHeights[0] * ratio) + pageHeights[1] = Math.round(pageHeights[1] * ratio) + } else { + pageHeights[0] = halfHeight + pageHeights[1] = halfHeight + } + } + if (this.options.boxed) { // Draw pages with borders if (this.options.showTitle) { @@ -435,9 +457,9 @@ export class DoubleLayout { }, }) } - this.page1.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line, undefined, 0) - }) + for (let i = 0; i < pageHeights[0]; i++) { + this.drawLine(this.page1.getContent()[i] || [{ text: "", style: { color: "" } }], undefined, 0) + } if (this.options.showTitle) { this.CM.Screen.write({ text: `${boxChars["normal"].left}${boxChars["normal"].horizontal}${ @@ -457,9 +479,9 @@ export class DoubleLayout { style: { color: this.options.boxColor, bold: this.boxBold }, }) } - this.page2.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line, undefined, 1) - }) + for (let i = 0; i < pageHeights[1]; i++) { + this.drawLine(this.page2.getContent()[i] || [{ text: "", style: { color: "" } }], undefined, 1) + } this.CM.Screen.write({ text: `${boxChars["normal"].bottomLeft}${boxChars[ "normal" @@ -482,9 +504,9 @@ export class DoubleLayout { }, }) } - this.page1.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line, undefined, 0) - }) + for (let i = 0; i < pageHeights[0]; i++) { + this.drawLine(this.page1.getContent()[i] || [{ text: "", style: { color: "" } }], undefined, 0) + } if (this.options.showTitle) { this.CM.Screen.write({ text: `${trimmedTitle[1]}`, @@ -494,9 +516,9 @@ export class DoubleLayout { }, }) } - this.page2.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line, undefined, 1) - }) + for (let i = 0; i < pageHeights[1]; i++) { + this.drawLine(this.page2.getContent()[i] || [{ text: "", style: { color: "" } }], undefined, 1) + } } } else { // Draw horizontally @@ -508,10 +530,16 @@ export class DoubleLayout { truncate(this.page1Title, this.realWidth[0] - 4, false), truncate(this.page2Title, this.realWidth[1] - 3, false), ] - const maxPageHeight = Math.max( + let maxPageHeight = Math.max( this.page1.getViewedPageHeight(), this.page2.getViewedPageHeight() ) + + if (this.options.fitHeight) { + const decorationHeight = this.options.boxed || this.options.showTitle ? 2 : 0 + maxPageHeight = this.CM.Screen.height - decorationHeight + } + const p1 = this.page1.getContent() const p2 = this.page2.getContent() if (this.options.boxed) { diff --git a/src/components/layout/LayoutManager.ts b/src/components/layout/LayoutManager.ts index b5ab356..db944b9 100644 --- a/src/components/layout/LayoutManager.ts +++ b/src/components/layout/LayoutManager.ts @@ -18,6 +18,7 @@ import SingleLayout, { SingleLayoutOptions } from "./SingleLayout.js" * @prop {"horizontal" | "vertical"} [direction] - The direction of the layout. * @prop {string[]} [pageTitles] - The title of the first page. * @prop {number[]} [pageRatio] - The ratio of the pages. (in horizontal direction) + * @prop {boolean} [fitHeight] - If the height of the pages should be the same. * * @export * @interface LayoutOptions @@ -33,6 +34,7 @@ export interface LayoutOptions { direction?: "horizontal" | "vertical"; pageTitles?: string[]; pageRatio?: [number, number] | [[number, number], [number, number]]; + fitHeight?: boolean; } /** @@ -79,6 +81,7 @@ export class LayoutManager { boxColor: this.options.boxColor, boxStyle: this.options.boxStyle, pageTitle: this.pageTitles ? this.pageTitles[0] : "", + fitHeight: this.options.fitHeight, } as SingleLayoutOptions this.layout = new SingleLayout(this.pages[0], this.optionsRelative) break @@ -93,6 +96,7 @@ export class LayoutManager { page1Title: this.pageTitles ? this.pageTitles[0] : "", page2Title: this.pageTitles ? this.pageTitles[1] : "", pageRatio: this.options.pageRatio, + fitHeight: this.options.fitHeight, } as DoubleLayoutOptions this.layout = new DoubleLayout(this.pages[0], this.pages[1], this.optionsRelative as DoubleLayoutOptions) break @@ -112,6 +116,7 @@ export class LayoutManager { page3Title: this.pageTitles ? this.pageTitles[2] : "", page4Title: this.pageTitles ? this.pageTitles[3] : "", pageRatio: this.options.pageRatio, + fitHeight: this.options.fitHeight, } as QuadLayoutOptions this.layout = new QuadLayout(this.pages[0], this.pages[1], this.pages[2], this.pages[3], this.optionsRelative as QuadLayoutOptions) break diff --git a/src/components/layout/QuadLayout.ts b/src/components/layout/QuadLayout.ts index 291031f..4df7d38 100644 --- a/src/components/layout/QuadLayout.ts +++ b/src/components/layout/QuadLayout.ts @@ -15,6 +15,7 @@ import { boxChars, HEX, RGB, StyledElement, truncate } from "../Utils.js" * @prop {string} [page3Title] - The title of the third page. * @prop {string} [page4Title] - The title of the fourth page. * @prop {number[]} [pageRatio] - The ratio of the pages. + * @prop {boolean} [fitHeight] - If the height of the pages should be the same. * * @export * @interface DoubleLayoutOptions @@ -31,6 +32,7 @@ export interface QuadLayoutOptions { page3Title?: string; page4Title?: string; pageRatio?: [[number, number], [number, number]]; + fitHeight?: boolean; } /** @@ -358,13 +360,18 @@ export class QuadLayout { truncate(this.page4Title, this.realWidth[1][1] - 3, false) ] ] - const maxPageHeight = Math.max( + let maxPageHeight = Math.max( this.page1.getViewedPageHeight(), this.page2.getViewedPageHeight(), this.page3.getViewedPageHeight(), this.page4.getViewedPageHeight() ) + if (this.options.fitHeight) { + const decorationHeight = this.options.boxed || this.options.showTitle ? 4 : 0 + maxPageHeight = (this.CM.Screen.height - decorationHeight) / 2 + } + const p = [ this.page1.getContent(), this.page2.getContent(), diff --git a/src/components/layout/SingleLayout.ts b/src/components/layout/SingleLayout.ts index ada4830..56130f3 100644 --- a/src/components/layout/SingleLayout.ts +++ b/src/components/layout/SingleLayout.ts @@ -10,6 +10,7 @@ import { boxChars, HEX, RGB, StyledElement, truncate } from "../Utils.js" * @prop {ForegroundColorName | HEX | RGB | ""} [boxColor] - The color of the box taken from the chalk library. * @prop {"bold"} [boxStyle] - If the border of the box should be bold. * @prop {string} [pageTitle] - The title of the first page. + * @prop {boolean} [fitHeight] - If the height of the layout should be the same as the height of the screen. * * @export * @interface SingleLayoutOptions @@ -21,6 +22,7 @@ export interface SingleLayoutOptions { boxColor?: ForegroundColorName | HEX | RGB | ""; // add color list from chalk boxStyle?: "bold"; pageTitle?: string; + fitHeight?: boolean; } /** @@ -134,17 +136,29 @@ export class SingleLayout { } else { this.CM.Screen.write({ text: `${boxChars["normal"].topLeft}${boxChars["normal"].horizontal}${boxChars["normal"].horizontal.repeat(this.CM.Screen.width - 3)}${boxChars["normal"].topRight}`, style: { color: this.options.boxColor, bold: this.boxBold } }) } - this.page.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line) - }) + if (this.options.fitHeight) { + for (let i = 0; i < this.CM.Screen.height - 2; i++) { + this.drawLine(this.page.getContent()[i] || [{ text: "", style: { color: "" } }]) + } + } else { + this.page.getContent().forEach((line: StyledElement[]) => { + this.drawLine(line) + }) + } this.CM.Screen.write({ text: `${boxChars["normal"].bottomLeft}${boxChars["normal"].horizontal.repeat(this.CM.Screen.width - 2)}${boxChars["normal"].bottomRight}`, style: { color: this.options.boxColor, bold: this.boxBold } }) } else { // Draw pages without borders if (this.options.showTitle) { this.CM.Screen.write({ text: `${trimmedTitle}`, style: { color: this.options.boxColor, bold: this.boxBold } }) } - this.page.getContent().forEach((line: StyledElement[]) => { - this.drawLine(line) - }) + if (this.options.fitHeight) { + for (let i = 0; i < this.CM.Screen.height - 2; i++) { + this.drawLine(this.page.getContent()[i] || [{ text: "", style: { color: "" } }]) + } + } else { + this.page.getContent().forEach((line: StyledElement[]) => { + this.drawLine(line) + }) + } } } }