add word wrap for label

This commit is contained in:
2025-10-14 12:41:32 +08:00
parent 0ccafa2e2e
commit 7f2a51c5aa
5 changed files with 43 additions and 23 deletions

View File

@@ -538,9 +538,9 @@ const AccessControlTUI = () => {
return Show(
{ when: () => errorState().show },
div(
{ class: "flex flex-col bg-red " },
{ class: "flex flex-col" },
label(
{ class: "w-25 text-white", wordWrap: true },
{ class: "w-50 text-white bg-red", wordWrap: true },
() => errorState().message,
),
button(

View File

@@ -4,16 +4,10 @@
*/
import { UIObject, BaseProps, createTextNode } from "./UIObject";
import {
Accessor,
createEffect,
createMemo,
createSignal,
Setter,
Signal,
} from "./reactivity";
import { Accessor, createMemo, Setter, Signal } from "./reactivity";
import { For } from "./controlFlow";
import { logger } from "./context";
import { context } from "./context";
import { concatSentence } from "../common";
/**
* Props for div component
@@ -114,6 +108,7 @@ export function div(
* @returns An array of words and whitespace.
*/
function splitByWhitespace(text: string): string[] {
if (!text) return [];
const parts: string[] = [];
let currentWord = "";
let currentWhitespace = "";
@@ -148,13 +143,16 @@ export function label(
props: LabelProps,
text: string | Accessor<string>,
): UIObject {
context.logger?.debug(`label : ${textutils.serialiseJSON(props)}`);
context.logger?.debug(
`label text: ${typeof text == "string" ? text : text()}`,
);
if (props.wordWrap === true) {
logger?.debug(`label : ${textutils.serialiseJSON(props)}`);
const p = { ...props };
delete p.wordWrap;
const containerProps: DivProps = {
...p,
class: `${p.class ?? ""} flex flex-row flex-wrap`,
class: `${p.class ?? ""} flex flex-col`,
};
if (typeof text === "string") {
@@ -166,16 +164,18 @@ export function label(
return node;
} else {
// Handle reactive strings (Accessor<string>)
const words = createMemo(() => splitByWhitespace(text()));
const sentences = createMemo(() => {
const words = splitByWhitespace(text());
const ret = concatSentence(words, 40);
context.logger?.debug(`label words changed : [ ${ret.join(",")} ]`);
return ret;
});
const forNode = For(
{ class: `${p.class ?? ""} flex flex-row flex-wrap`, each: words },
(word) => createTextNode(word),
const forNode = For({ class: `flex flex-col`, each: sentences }, (word) =>
label({ class: p.class }, word),
);
const node = new UIObject("div", containerProps, [forNode]);
forNode.parent = node;
return node;
return forNode;
}
}

View File

@@ -7,15 +7,17 @@
import type { CCLog } from "../ccLog";
/**
* The global logger instance for the TUI application.
* The global context object for the TUI application.
* This will be set by the Application instance on creation.
*/
export let logger: CCLog | undefined;
export const context: { logger: CCLog | undefined } = {
logger: undefined,
};
/**
* Sets the global logger instance.
* @param l The logger instance.
*/
export function setLogger(l: CCLog): void {
logger = l;
context.logger = l;
}

0
src/lib/ccTUI/utils.ts Normal file
View File

View File

@@ -4,3 +4,21 @@ export function parseBoolean(obj: string): boolean | undefined {
else if (str === "false") return false;
else return undefined;
}
export function concatSentence(words: string[], length: number): string[] {
let i = 0,
j = 1;
const ret: string[] = [];
while (i < words.length) {
let sentence = words[i];
while (j < words.length && sentence.length + words[j].length < length) {
sentence += words[j];
j++;
}
ret.push(sentence);
i = j;
j++;
}
return ret;
}