mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-11-04 19:27:50 +08:00
Compare commits
3 Commits
d41117cecc
...
b9ce947b9b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9ce947b9b | ||
|
|
da2c6c1ebb | ||
|
|
c85c072376 |
@@ -9,7 +9,6 @@ build-autocraft:
|
|||||||
|
|
||||||
build-accesscontrol:
|
build-accesscontrol:
|
||||||
pnpm tstl -p ./tsconfig.accesscontrol.json
|
pnpm tstl -p ./tsconfig.accesscontrol.json
|
||||||
cp ./src/accesscontrol/access.config.json ./build/
|
|
||||||
|
|
||||||
build-test:
|
build-test:
|
||||||
pnpm tstl -p ./tsconfig.test.json
|
pnpm tstl -p ./tsconfig.test.json
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"detectRange": 256,
|
|
||||||
"detectInterval": 1,
|
|
||||||
"watchInterval": 10,
|
|
||||||
"noticeTimes": 2,
|
|
||||||
"isWarn": false,
|
|
||||||
"adminGroupConfig": {
|
|
||||||
"groupName": "Admin",
|
|
||||||
"groupUsers": ["Selcon"],
|
|
||||||
"isAllowed": true,
|
|
||||||
"isNotice": true
|
|
||||||
},
|
|
||||||
"usersGroups": [
|
|
||||||
{
|
|
||||||
"groupName": "user",
|
|
||||||
"groupUsers": [],
|
|
||||||
"isAllowed": true,
|
|
||||||
"isNotice": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupName": "VIP",
|
|
||||||
"groupUsers": [],
|
|
||||||
"isAllowed": true,
|
|
||||||
"isNotice": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupName": "enemies",
|
|
||||||
"groupUsers": [],
|
|
||||||
"isAllowed": false,
|
|
||||||
"isNotice": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"welcomeToastConfig": {
|
|
||||||
"title": {
|
|
||||||
"text": "Welcome",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
"msg": {
|
|
||||||
"text": "Hello User %playerName%",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
"prefix": "Taohuayuan",
|
|
||||||
"brackets": "[]",
|
|
||||||
"bracketColor": ""
|
|
||||||
},
|
|
||||||
"noticeToastConfig": {
|
|
||||||
"title": {
|
|
||||||
"text": "Welcome",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
"msg": {
|
|
||||||
"text": "Hello User %playerName%",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
"prefix": "Taohuayuan",
|
|
||||||
"brackets": "[]",
|
|
||||||
"bracketColor": ""
|
|
||||||
},
|
|
||||||
"warnToastConfig": {
|
|
||||||
"title": {
|
|
||||||
"text": "Attention!!!",
|
|
||||||
"color": "red"
|
|
||||||
},
|
|
||||||
"msg": {
|
|
||||||
"text": "%playerName% you are not allowed to be here",
|
|
||||||
"color": "red"
|
|
||||||
},
|
|
||||||
"prefix": "Taohuayuan",
|
|
||||||
"brackets": "[]",
|
|
||||||
"bracketColor": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -75,12 +75,12 @@ const defaultConfig: AccessConfig = {
|
|||||||
},
|
},
|
||||||
noticeToastConfig: {
|
noticeToastConfig: {
|
||||||
title: {
|
title: {
|
||||||
text: "Welcome",
|
text: "Notice",
|
||||||
color: "green",
|
color: "red",
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
text: "Hello User %playerName%",
|
text: "Unfamiliar player %playerName% appeared at Position %playerPosX%, %playerPosY%, %playerPosZ%",
|
||||||
color: "green",
|
color: "red",
|
||||||
},
|
},
|
||||||
prefix: "Taohuayuan",
|
prefix: "Taohuayuan",
|
||||||
brackets: "[]",
|
brackets: "[]",
|
||||||
@@ -105,12 +105,16 @@ function loadConfig(filepath: string): AccessConfig {
|
|||||||
const [fp] = io.open(filepath, "r");
|
const [fp] = io.open(filepath, "r");
|
||||||
if (fp == undefined) {
|
if (fp == undefined) {
|
||||||
print("Failed to open config file " + filepath);
|
print("Failed to open config file " + filepath);
|
||||||
|
print("Use default config");
|
||||||
|
saveConfig(defaultConfig, filepath);
|
||||||
return defaultConfig;
|
return defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configJson = fp.read("*a");
|
const configJson = fp.read("*a");
|
||||||
if (configJson == undefined) {
|
if (configJson == undefined) {
|
||||||
print("Failed to read config file");
|
print("Failed to read config file");
|
||||||
|
print("Use default config");
|
||||||
|
saveConfig(defaultConfig, filepath);
|
||||||
return defaultConfig;
|
return defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ const DEBUG = false;
|
|||||||
const args = [...$vararg];
|
const args = [...$vararg];
|
||||||
|
|
||||||
// Init Log
|
// Init Log
|
||||||
const log = new CCLog("accesscontrol.log", true, DAY);
|
const logger = new CCLog("accesscontrol.log", true, DAY);
|
||||||
|
|
||||||
// Load Config
|
// Load Config
|
||||||
const configFilepath = `${shell.dir()}/access.config.json`;
|
const configFilepath = `${shell.dir()}/access.config.json`;
|
||||||
const config = loadConfig(configFilepath);
|
const config = loadConfig(configFilepath);
|
||||||
log.info("Load config successfully!");
|
logger.info("Load config successfully!");
|
||||||
if (DEBUG) log.debug(textutils.serialise(config, { allow_repetitions: true }));
|
if (DEBUG)
|
||||||
|
logger.debug(textutils.serialise(config, { allow_repetitions: true }));
|
||||||
const groupNames = config.usersGroups.map((value) => value.groupName);
|
const groupNames = config.usersGroups.map((value) => value.groupName);
|
||||||
let noticeTargetPlayers: string[];
|
let noticeTargetPlayers: string[];
|
||||||
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
|
const playerDetector = peripheralManager.findByNameRequired("playerDetector");
|
||||||
@@ -23,26 +24,56 @@ const chatBox = peripheralManager.findByNameRequired("chatBox");
|
|||||||
let inRangePlayers: string[] = [];
|
let inRangePlayers: string[] = [];
|
||||||
let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
|
let watchPlayersInfo: { name: string; hasNoticeTimes: number }[] = [];
|
||||||
|
|
||||||
|
interface ParseParams {
|
||||||
|
name?: string;
|
||||||
|
group?: string;
|
||||||
|
info?: PlayerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
function safeParseTextComponent(
|
function safeParseTextComponent(
|
||||||
component: MinecraftTextComponent,
|
component: MinecraftTextComponent,
|
||||||
playerName: string,
|
params?: ParseParams,
|
||||||
groupName?: string,
|
|
||||||
): string {
|
): string {
|
||||||
if (component.text == undefined) {
|
if (component.text == undefined) {
|
||||||
component.text = "Wrong text, please contanct with admin";
|
component.text = "Wrong text, please contanct with admin";
|
||||||
} else if (component.text.includes("%")) {
|
} else if (component.text.includes("%")) {
|
||||||
component.text = component.text.replace("%playerName%", playerName);
|
component.text = component.text.replace(
|
||||||
if (groupName != undefined)
|
"%playerName%",
|
||||||
component.text = component.text.replace("%groupName%", groupName);
|
params?.name ?? "UnknowPlayer",
|
||||||
|
);
|
||||||
|
component.text = component.text.replace(
|
||||||
|
"%groupName%",
|
||||||
|
params?.group ?? "UnknowGroup",
|
||||||
|
);
|
||||||
|
component.text = component.text.replace(
|
||||||
|
"%playerPosX%",
|
||||||
|
params?.info?.x.toString() ?? "UnknowPosX",
|
||||||
|
);
|
||||||
|
component.text = component.text.replace(
|
||||||
|
"%playerPosY%",
|
||||||
|
params?.info?.y.toString() ?? "UnknowPosY",
|
||||||
|
);
|
||||||
|
component.text = component.text.replace(
|
||||||
|
"%playerPosZ%",
|
||||||
|
params?.info?.z.toString() ?? "UnknowPosZ",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return textutils.serialiseJSON(component);
|
return textutils.serialiseJSON(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendToast(toastConfig: ToastConfig, targetPlayer: string) {
|
function sendToast(
|
||||||
|
toastConfig: ToastConfig,
|
||||||
|
targetPlayer: string,
|
||||||
|
params: ParseParams,
|
||||||
|
) {
|
||||||
return chatBox.sendFormattedToastToPlayer(
|
return chatBox.sendFormattedToastToPlayer(
|
||||||
textutils.serialiseJSON(toastConfig.msg ?? config.welcomeToastConfig.msg),
|
safeParseTextComponent(
|
||||||
textutils.serialiseJSON(
|
toastConfig.msg ?? config.welcomeToastConfig.msg,
|
||||||
|
params,
|
||||||
|
),
|
||||||
|
safeParseTextComponent(
|
||||||
toastConfig.title ?? config.welcomeToastConfig.title,
|
toastConfig.title ?? config.welcomeToastConfig.title,
|
||||||
|
params,
|
||||||
),
|
),
|
||||||
targetPlayer,
|
targetPlayer,
|
||||||
toastConfig.prefix ?? config.welcomeToastConfig.prefix,
|
toastConfig.prefix ?? config.welcomeToastConfig.prefix,
|
||||||
@@ -62,29 +93,22 @@ function sendNotice(player: string, playerInfo?: PlayerInfo) {
|
|||||||
.flat(),
|
.flat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const toastConfig: ToastConfig = {
|
|
||||||
title: {
|
|
||||||
text: "Notice",
|
|
||||||
color: "red",
|
|
||||||
},
|
|
||||||
msg: {
|
|
||||||
text: `Unfamiliar Player ${player} appeared at\n Position ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
|
||||||
color: "red",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
for (const targetPlayer of noticeTargetPlayers) {
|
for (const targetPlayer of noticeTargetPlayers) {
|
||||||
if (!onlinePlayers.includes(targetPlayer)) continue;
|
if (!onlinePlayers.includes(targetPlayer)) continue;
|
||||||
sendToast(toastConfig, targetPlayer);
|
sendToast(config.noticeToastConfig, targetPlayer, {
|
||||||
|
name: player,
|
||||||
|
info: playerInfo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWarn(player: string) {
|
function sendWarn(player: string) {
|
||||||
const warnMsg = `Not Allowed Player ${player} Break in Home `;
|
const warnMsg = `Not Allowed Player ${player} Break in Home `;
|
||||||
log.warn(warnMsg);
|
logger.warn(warnMsg);
|
||||||
|
|
||||||
sendToast(config.warnToastConfig, player);
|
sendToast(config.warnToastConfig, player, { name: player });
|
||||||
chatBox.sendFormattedMessageToPlayer(
|
chatBox.sendFormattedMessageToPlayer(
|
||||||
safeParseTextComponent(config.warnToastConfig.msg, player),
|
safeParseTextComponent(config.warnToastConfig.msg, { name: player }),
|
||||||
player,
|
player,
|
||||||
"AccessControl",
|
"AccessControl",
|
||||||
"[]",
|
"[]",
|
||||||
@@ -110,7 +134,7 @@ function watchLoop() {
|
|||||||
if (config.isWarn) sendWarn(player.name);
|
if (config.isWarn) sendWarn(player.name);
|
||||||
|
|
||||||
// Record
|
// Record
|
||||||
log.warn(
|
logger.warn(
|
||||||
`${player.name} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
`${player.name} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -118,6 +142,7 @@ function watchLoop() {
|
|||||||
watchPlayersInfo = watchPlayersInfo.filter(
|
watchPlayersInfo = watchPlayersInfo.filter(
|
||||||
(value) => value.name != player.name,
|
(value) => value.name != player.name,
|
||||||
);
|
);
|
||||||
|
logger.info(`${player.name} has left the range`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,14 +155,14 @@ function mainLoop() {
|
|||||||
const players = playerDetector.getPlayersInRange(config.detectRange);
|
const players = playerDetector.getPlayersInRange(config.detectRange);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
const playersList = "[ " + players.join(",") + " ]";
|
const playersList = "[ " + players.join(",") + " ]";
|
||||||
log.debug(`Detected ${players.length} players: ${playersList}`);
|
logger.debug(`Detected ${players.length} players: ${playersList}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const player of players) {
|
for (const player of players) {
|
||||||
if (inRangePlayers.includes(player)) continue;
|
if (inRangePlayers.includes(player)) continue;
|
||||||
|
|
||||||
if (config.adminGroupConfig.groupUsers.includes(player)) {
|
if (config.adminGroupConfig.groupUsers.includes(player)) {
|
||||||
log.info(`Admin ${player} appear`);
|
logger.info(`Admin ${player} appear`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +179,7 @@ function mainLoop() {
|
|||||||
if (!userGroupConfig.groupUsers.includes(player)) continue;
|
if (!userGroupConfig.groupUsers.includes(player)) continue;
|
||||||
|
|
||||||
groupConfig = userGroupConfig;
|
groupConfig = userGroupConfig;
|
||||||
log.info(
|
logger.info(
|
||||||
`${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
`${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -162,7 +187,7 @@ function mainLoop() {
|
|||||||
}
|
}
|
||||||
if (groupConfig.isAllowed) continue;
|
if (groupConfig.isAllowed) continue;
|
||||||
|
|
||||||
log.warn(
|
logger.warn(
|
||||||
`${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
`${groupConfig.groupName} ${player} appear at ${playerInfo?.x}, ${playerInfo?.y}, ${playerInfo?.z}`,
|
||||||
);
|
);
|
||||||
if (config.isWarn) sendWarn(player);
|
if (config.isWarn) sendWarn(player);
|
||||||
@@ -178,26 +203,29 @@ function keyboardLoop() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const [eventType, key] = os.pullEvent("key");
|
const [eventType, key] = os.pullEvent("key");
|
||||||
if (eventType === "key" && key === keys.c) {
|
if (eventType === "key" && key === keys.c) {
|
||||||
log.info("Launching Access Control TUI...");
|
logger.info("Launching Access Control TUI...");
|
||||||
try {
|
try {
|
||||||
|
logger.setInTerminal(false);
|
||||||
launchAccessControlTUI();
|
launchAccessControlTUI();
|
||||||
log.info("TUI closed, resuming normal operation");
|
logger.info("TUI closed, resuming normal operation");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`TUI error: ${textutils.serialise(error as object)}`);
|
logger.error(`TUI error: ${textutils.serialise(error as object)}`);
|
||||||
|
} finally {
|
||||||
|
logger.setInTerminal(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main(args: string[]) {
|
function main(args: string[]) {
|
||||||
log.info("Starting access control system, get args: " + args.join(", "));
|
logger.info("Starting access control system, get args: " + args.join(", "));
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
if (args[0] == "start") {
|
if (args[0] == "start") {
|
||||||
// 创建CLI处理器
|
// 创建CLI处理器
|
||||||
const cli = createAccessControlCLI(
|
const cli = createAccessControlCLI(
|
||||||
config,
|
config,
|
||||||
configFilepath,
|
configFilepath,
|
||||||
log,
|
logger,
|
||||||
chatBox,
|
chatBox,
|
||||||
groupNames,
|
groupNames,
|
||||||
);
|
);
|
||||||
@@ -221,12 +249,12 @@ function main(args: string[]) {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (args[0] == "config") {
|
} else if (args[0] == "config") {
|
||||||
log.info("Launching Access Control TUI...");
|
logger.info("Launching Access Control TUI...");
|
||||||
log.setInTerminal(false);
|
logger.setInTerminal(false);
|
||||||
try {
|
try {
|
||||||
launchAccessControlTUI();
|
launchAccessControlTUI();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`TUI error: ${textutils.serialise(error as object)}`);
|
logger.error(`TUI error: ${textutils.serialise(error as object)}`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -240,7 +268,7 @@ function main(args: string[]) {
|
|||||||
try {
|
try {
|
||||||
main(args);
|
main(args);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
log.error(textutils.serialise(error as object));
|
logger.error(textutils.serialise(error as object));
|
||||||
} finally {
|
} finally {
|
||||||
log.close();
|
logger.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,6 +243,18 @@ const AccessControlTUI = () => {
|
|||||||
/**
|
/**
|
||||||
* Basic Configuration Tab
|
* Basic Configuration Tab
|
||||||
*/
|
*/
|
||||||
|
const [getDetectInterval, setDetectInterval] = createSignal(
|
||||||
|
config().detectInterval.toString(),
|
||||||
|
);
|
||||||
|
const [getWatchInterval, setWatchInterval] = createSignal(
|
||||||
|
config().watchInterval.toString(),
|
||||||
|
);
|
||||||
|
const [getNoticeTimes, setNoticeTimes] = createSignal(
|
||||||
|
config().noticeTimes.toString(),
|
||||||
|
);
|
||||||
|
const [getDetectRange, setDetectRange] = createSignal(
|
||||||
|
config().detectRange.toString(),
|
||||||
|
);
|
||||||
const BasicTab = () => {
|
const BasicTab = () => {
|
||||||
return div(
|
return div(
|
||||||
{ class: "flex flex-col" },
|
{ class: "flex flex-col" },
|
||||||
@@ -251,10 +263,12 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Detect Interval (ms):"),
|
label({}, "Detect Interval (ms):"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => config().detectInterval?.toString() ?? "",
|
value: () => getDetectInterval(),
|
||||||
onInput: (value) => {
|
onInput: (value) => setDetectInterval(value),
|
||||||
const num = validateNumber(value);
|
onFocusChanged: () => {
|
||||||
|
const num = validateNumber(getDetectInterval());
|
||||||
if (num !== null) setConfig("detectInterval", num);
|
if (num !== null) setConfig("detectInterval", num);
|
||||||
|
else setDetectInterval(config().detectInterval.toString());
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -263,10 +277,12 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Watch Interval (ms):"),
|
label({}, "Watch Interval (ms):"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => config().watchInterval?.toString() ?? "",
|
value: () => getWatchInterval(),
|
||||||
onInput: (value) => {
|
onInput: (value) => setWatchInterval(value),
|
||||||
const num = validateNumber(value);
|
onFocusChanged: () => {
|
||||||
|
const num = validateNumber(getWatchInterval());
|
||||||
if (num !== null) setConfig("watchInterval", num);
|
if (num !== null) setConfig("watchInterval", num);
|
||||||
|
else setWatchInterval(config().watchInterval.toString());
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -275,10 +291,12 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Notice Times:"),
|
label({}, "Notice Times:"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => config().noticeTimes?.toString() ?? "",
|
value: () => getNoticeTimes(),
|
||||||
onInput: (value) => {
|
onInput: (value) => setNoticeTimes(value),
|
||||||
const num = validateNumber(value);
|
onFocusChanged: () => {
|
||||||
|
const num = validateNumber(getNoticeTimes());
|
||||||
if (num !== null) setConfig("noticeTimes", num);
|
if (num !== null) setConfig("noticeTimes", num);
|
||||||
|
else setNoticeTimes(config().noticeTimes.toString());
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -287,10 +305,12 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Detect Range:"),
|
label({}, "Detect Range:"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => config().detectRange?.toString() ?? "",
|
value: () => getDetectRange(),
|
||||||
onInput: (value) => {
|
onInput: (value) => setDetectRange(value),
|
||||||
const num = validateNumber(value);
|
onFocusChanged: () => {
|
||||||
|
const num = validateNumber(getDetectRange());
|
||||||
if (num !== null) setConfig("detectRange", num);
|
if (num !== null) setConfig("detectRange", num);
|
||||||
|
else setDetectRange(config().detectRange.toString());
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -436,6 +456,13 @@ const AccessControlTUI = () => {
|
|||||||
) => {
|
) => {
|
||||||
return () => {
|
return () => {
|
||||||
const toastConfig = config()[toastType];
|
const toastConfig = config()[toastType];
|
||||||
|
const [getTempToastConfig, setTempToastConfig] = createSignal({
|
||||||
|
title: textutils.serialiseJSON(toastConfig.title),
|
||||||
|
msg: textutils.serialiseJSON(toastConfig.msg),
|
||||||
|
prefix: toastConfig.prefix ?? "",
|
||||||
|
brackets: toastConfig.brackets ?? "",
|
||||||
|
bracketColor: toastConfig.bracketColor ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
return div(
|
return div(
|
||||||
{ class: "flex flex-col w-full" },
|
{ class: "flex flex-col w-full" },
|
||||||
@@ -443,20 +470,34 @@ const AccessControlTUI = () => {
|
|||||||
input({
|
input({
|
||||||
class: "w-full",
|
class: "w-full",
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => textutils.serialiseJSON(toastConfig?.title) ?? "",
|
value: () => getTempToastConfig().title,
|
||||||
onInput: (value) => {
|
onInput: (value) =>
|
||||||
|
setTempToastConfig({
|
||||||
|
...getTempToastConfig(),
|
||||||
|
title: value,
|
||||||
|
}),
|
||||||
|
onFocusChanged: () => {
|
||||||
|
const currentToastConfig = config()[toastType];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = textutils.unserialiseJSON(value);
|
const parsed = textutils.unserialiseJSON(
|
||||||
if (parsed != undefined && typeof parsed === "object") {
|
getTempToastConfig().title,
|
||||||
const currentConfig = config();
|
) as MinecraftTextComponent;
|
||||||
const currentToast = currentConfig[toastType];
|
if (
|
||||||
|
typeof parsed === "object" &&
|
||||||
|
parsed.text !== undefined &&
|
||||||
|
parsed.color !== undefined
|
||||||
|
) {
|
||||||
setConfig(toastType, {
|
setConfig(toastType, {
|
||||||
...currentToast,
|
...currentToastConfig,
|
||||||
title: parsed as MinecraftTextComponent,
|
title: parsed,
|
||||||
});
|
});
|
||||||
}
|
} else throw new Error("Invalid JSON");
|
||||||
} catch {
|
} catch {
|
||||||
// Invalid JSON, ignore
|
setTempToastConfig({
|
||||||
|
...getTempToastConfig(),
|
||||||
|
title: textutils.serialiseJSON(currentToastConfig.title),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -465,19 +506,31 @@ const AccessControlTUI = () => {
|
|||||||
input({
|
input({
|
||||||
class: "w-full",
|
class: "w-full",
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => textutils.serialiseJSON(toastConfig?.msg) ?? "",
|
value: () => getTempToastConfig().msg,
|
||||||
onInput: (value) => {
|
onInput: (value) =>
|
||||||
|
setTempToastConfig({ ...getTempToastConfig(), msg: value }),
|
||||||
|
onFocusChanged: () => {
|
||||||
|
const currentToastConfig = config()[toastType];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = textutils.unserialiseJSON(value);
|
const parsed = textutils.unserialiseJSON(
|
||||||
if (parsed != undefined && typeof parsed === "object") {
|
getTempToastConfig().msg,
|
||||||
const currentConfig = config();
|
) as MinecraftTextComponent;
|
||||||
const currentToast = currentConfig[toastType];
|
if (
|
||||||
|
typeof parsed === "object" &&
|
||||||
|
parsed.text !== undefined &&
|
||||||
|
parsed.color !== undefined
|
||||||
|
) {
|
||||||
setConfig(toastType, {
|
setConfig(toastType, {
|
||||||
...currentToast,
|
...currentToastConfig,
|
||||||
msg: parsed as MinecraftTextComponent,
|
msg: parsed,
|
||||||
});
|
});
|
||||||
}
|
} else throw new Error("Invalid JSON");
|
||||||
} catch {
|
} catch {
|
||||||
|
setTempToastConfig({
|
||||||
|
...getTempToastConfig(),
|
||||||
|
msg: textutils.serialiseJSON(currentToastConfig.msg),
|
||||||
|
});
|
||||||
// Invalid JSON, ignore
|
// Invalid JSON, ignore
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -488,11 +541,15 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Prefix:"),
|
label({}, "Prefix:"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => toastConfig?.prefix ?? "",
|
value: () => getTempToastConfig().prefix,
|
||||||
onInput: (value) => {
|
onInput: (value) =>
|
||||||
const currentConfig = config();
|
setTempToastConfig({ ...getTempToastConfig(), prefix: value }),
|
||||||
const currentToast = currentConfig[toastType];
|
onFocusChanged: () => {
|
||||||
setConfig(toastType, { ...currentToast, prefix: value });
|
const currentToastConfig = config()[toastType];
|
||||||
|
setConfig(toastType, {
|
||||||
|
...currentToastConfig,
|
||||||
|
prefix: getTempToastConfig().prefix,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -502,11 +559,15 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Brackets:"),
|
label({}, "Brackets:"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => toastConfig?.brackets ?? "",
|
value: () => getTempToastConfig().brackets,
|
||||||
onInput: (value) => {
|
onInput: (value) =>
|
||||||
const currentConfig = config();
|
setTempToastConfig({ ...getTempToastConfig(), brackets: value }),
|
||||||
const currentToast = currentConfig[toastType];
|
onFocusChanged: () => {
|
||||||
setConfig(toastType, { ...currentToast, brackets: value });
|
const currentToastConfig = config()[toastType];
|
||||||
|
setConfig(toastType, {
|
||||||
|
...currentToastConfig,
|
||||||
|
brackets: getTempToastConfig().brackets,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -516,11 +577,18 @@ const AccessControlTUI = () => {
|
|||||||
label({}, "Bracket Color:"),
|
label({}, "Bracket Color:"),
|
||||||
input({
|
input({
|
||||||
type: "text",
|
type: "text",
|
||||||
value: () => toastConfig?.bracketColor ?? "",
|
value: () => getTempToastConfig().bracketColor,
|
||||||
onInput: (value) => {
|
onInput: (value) =>
|
||||||
const currentConfig = config();
|
setTempToastConfig({
|
||||||
const currentToast = currentConfig[toastType];
|
...getTempToastConfig(),
|
||||||
setConfig(toastType, { ...currentToast, bracketColor: value });
|
bracketColor: value,
|
||||||
|
}),
|
||||||
|
onFocusChanged: () => {
|
||||||
|
const currentToastConfig = config()[toastType];
|
||||||
|
setConfig(toastType, {
|
||||||
|
...currentToastConfig,
|
||||||
|
bracketColor: getTempToastConfig().bracketColor,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
* Represents a node in the UI tree
|
* Represents a node in the UI tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Accessor } from "./reactivity";
|
import { ButtonProps, DivProps, InputProps, LabelProps } from "./components";
|
||||||
|
import { Accessor, Setter } from "./reactivity";
|
||||||
|
import { ScrollContainerProps } from "./scrollContainer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout properties for flexbox layout
|
* Layout properties for flexbox layout
|
||||||
@@ -34,7 +36,7 @@ export interface StyleProps {
|
|||||||
/**
|
/**
|
||||||
* Scroll properties for scroll containers
|
* Scroll properties for scroll containers
|
||||||
*/
|
*/
|
||||||
export interface ScrollProps {
|
export interface ScrollProps extends BaseProps {
|
||||||
/** Current horizontal scroll position */
|
/** Current horizontal scroll position */
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
/** Current vertical scroll position */
|
/** Current vertical scroll position */
|
||||||
@@ -69,6 +71,9 @@ export interface ComputedLayout {
|
|||||||
export interface BaseProps {
|
export interface BaseProps {
|
||||||
/** CSS-like class names for layout (e.g., "flex flex-col") */
|
/** CSS-like class names for layout (e.g., "flex flex-col") */
|
||||||
class?: string;
|
class?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
onFocusChanged?: Setter<boolean> | ((value: boolean) => void);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,6 +95,14 @@ export type UIObjectType =
|
|||||||
| "fragment"
|
| "fragment"
|
||||||
| "scroll-container";
|
| "scroll-container";
|
||||||
|
|
||||||
|
export type UIObjectProps =
|
||||||
|
| DivProps
|
||||||
|
| LabelProps
|
||||||
|
| InputProps
|
||||||
|
| ButtonProps
|
||||||
|
| ScrollProps
|
||||||
|
| ScrollContainerProps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UIObject represents a node in the UI tree
|
* UIObject represents a node in the UI tree
|
||||||
* It can be a component, text, or a control flow element
|
* It can be a component, text, or a control flow element
|
||||||
@@ -99,7 +112,7 @@ export class UIObject {
|
|||||||
type: UIObjectType;
|
type: UIObjectType;
|
||||||
|
|
||||||
/** Props passed to the component */
|
/** Props passed to the component */
|
||||||
props: Record<string, unknown>;
|
props: UIObjectProps;
|
||||||
|
|
||||||
/** Children UI objects */
|
/** Children UI objects */
|
||||||
children: UIObject[];
|
children: UIObject[];
|
||||||
@@ -136,7 +149,7 @@ export class UIObject {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
type: UIObjectType,
|
type: UIObjectType,
|
||||||
props: Record<string, unknown> = {},
|
props: UIObjectProps = {},
|
||||||
children: UIObject[] = [],
|
children: UIObject[] = [],
|
||||||
) {
|
) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -155,7 +168,7 @@ export class UIObject {
|
|||||||
this.extractHandlers();
|
this.extractHandlers();
|
||||||
|
|
||||||
// Initialize cursor position for text inputs
|
// Initialize cursor position for text inputs
|
||||||
if (type === "input" && props.type !== "checkbox") {
|
if (type === "input" && (props as InputProps).type !== "checkbox") {
|
||||||
this.cursorPos = 0;
|
this.cursorPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +181,9 @@ export class UIObject {
|
|||||||
maxScrollY: 0,
|
maxScrollY: 0,
|
||||||
contentWidth: 0,
|
contentWidth: 0,
|
||||||
contentHeight: 0,
|
contentHeight: 0,
|
||||||
showScrollbar: props.showScrollbar !== false,
|
showScrollbar: (props as ScrollProps).showScrollbar !== false,
|
||||||
viewportWidth: (props.width as number) ?? 10,
|
viewportWidth: props.width ?? 10,
|
||||||
viewportHeight: (props.height as number) ?? 10,
|
viewportHeight: props.height ?? 10,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +221,7 @@ export class UIObject {
|
|||||||
* Parse CSS-like class string into layout and style properties
|
* Parse CSS-like class string into layout and style properties
|
||||||
*/
|
*/
|
||||||
private parseClassNames(): void {
|
private parseClassNames(): void {
|
||||||
const className = this.props.class as string | undefined;
|
const className = this.props.class;
|
||||||
if (className === undefined) return;
|
if (className === undefined) return;
|
||||||
|
|
||||||
const classes = className.split(" ").filter((c) => c.length > 0);
|
const classes = className.split(" ").filter((c) => c.length > 0);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { calculateLayout } from "./layout";
|
|||||||
import { render as renderTree, clearScreen } from "./renderer";
|
import { render as renderTree, clearScreen } from "./renderer";
|
||||||
import { CCLog, HOUR } from "../ccLog";
|
import { CCLog, HOUR } from "../ccLog";
|
||||||
import { setLogger } from "./context";
|
import { setLogger } from "./context";
|
||||||
|
import { InputProps } from "./components";
|
||||||
|
import { Setter } from "./reactivity";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main application class
|
* Main application class
|
||||||
@@ -145,7 +147,7 @@ export class Application {
|
|||||||
if (
|
if (
|
||||||
this.focusedNode !== undefined &&
|
this.focusedNode !== undefined &&
|
||||||
this.focusedNode.type === "input" &&
|
this.focusedNode.type === "input" &&
|
||||||
this.focusedNode.props.type !== "checkbox"
|
(this.focusedNode.props as InputProps).type !== "checkbox"
|
||||||
) {
|
) {
|
||||||
this.needsRender = true;
|
this.needsRender = true;
|
||||||
}
|
}
|
||||||
@@ -213,11 +215,13 @@ export class Application {
|
|||||||
this.needsRender = true;
|
this.needsRender = true;
|
||||||
}
|
}
|
||||||
} else if (this.focusedNode.type === "input") {
|
} else if (this.focusedNode.type === "input") {
|
||||||
const type = this.focusedNode.props.type as string | undefined;
|
const type = (this.focusedNode.props as InputProps).type as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
if (type === "checkbox") {
|
if (type === "checkbox") {
|
||||||
// Toggle checkbox
|
// Toggle checkbox
|
||||||
const onChangeProp = this.focusedNode.props.onChange;
|
const onChangeProp = (this.focusedNode.props as InputProps).onChange;
|
||||||
const checkedProp = this.focusedNode.props.checked;
|
const checkedProp = (this.focusedNode.props as InputProps).checked;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof onChangeProp === "function" &&
|
typeof onChangeProp === "function" &&
|
||||||
@@ -234,7 +238,9 @@ export class Application {
|
|||||||
this.focusedNode.type === "input"
|
this.focusedNode.type === "input"
|
||||||
) {
|
) {
|
||||||
// Handle text input key events
|
// Handle text input key events
|
||||||
const type = this.focusedNode.props.type as string | undefined;
|
const type = (this.focusedNode.props as InputProps).type as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
if (type !== "checkbox") {
|
if (type !== "checkbox") {
|
||||||
this.handleTextInputKey(key);
|
this.handleTextInputKey(key);
|
||||||
}
|
}
|
||||||
@@ -247,8 +253,8 @@ export class Application {
|
|||||||
private handleTextInputKey(key: number): void {
|
private handleTextInputKey(key: number): void {
|
||||||
if (this.focusedNode === undefined) return;
|
if (this.focusedNode === undefined) return;
|
||||||
|
|
||||||
const valueProp = this.focusedNode.props.value;
|
const valueProp = (this.focusedNode.props as InputProps).value;
|
||||||
const onInputProp = this.focusedNode.props.onInput;
|
const onInputProp = (this.focusedNode.props as InputProps).onInput;
|
||||||
|
|
||||||
if (typeof valueProp !== "function" || typeof onInputProp !== "function") {
|
if (typeof valueProp !== "function" || typeof onInputProp !== "function") {
|
||||||
return;
|
return;
|
||||||
@@ -292,11 +298,11 @@ export class Application {
|
|||||||
*/
|
*/
|
||||||
private handleCharEvent(char: string): void {
|
private handleCharEvent(char: string): void {
|
||||||
if (this.focusedNode !== undefined && this.focusedNode.type === "input") {
|
if (this.focusedNode !== undefined && this.focusedNode.type === "input") {
|
||||||
const type = this.focusedNode.props.type as string | undefined;
|
const type = (this.focusedNode.props as InputProps).type;
|
||||||
if (type !== "checkbox") {
|
if (type !== "checkbox") {
|
||||||
// Insert character at cursor position
|
// Insert character at cursor position
|
||||||
const onInputProp = this.focusedNode.props.onInput;
|
const onInputProp = (this.focusedNode.props as InputProps).onInput;
|
||||||
const valueProp = this.focusedNode.props.value;
|
const valueProp = (this.focusedNode.props as InputProps).value;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof onInputProp === "function" &&
|
typeof onInputProp === "function" &&
|
||||||
@@ -331,11 +337,26 @@ export class Application {
|
|||||||
string.format("handleMouseClick: Found node of type %s.", clicked.type),
|
string.format("handleMouseClick: Found node of type %s.", clicked.type),
|
||||||
);
|
);
|
||||||
// Set focus
|
// Set focus
|
||||||
|
if (
|
||||||
|
this.focusedNode !== undefined &&
|
||||||
|
typeof this.focusedNode.props.onFocusChanged === "function"
|
||||||
|
) {
|
||||||
|
const onFocusChanged = this.focusedNode.props
|
||||||
|
.onFocusChanged as Setter<boolean>;
|
||||||
|
onFocusChanged(false);
|
||||||
|
}
|
||||||
this.focusedNode = clicked;
|
this.focusedNode = clicked;
|
||||||
|
if (typeof clicked.props.onFocusChanged === "function") {
|
||||||
|
const onFocusChanged = clicked.props.onFocusChanged as Setter<boolean>;
|
||||||
|
onFocusChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize cursor position for text inputs on focus
|
// Initialize cursor position for text inputs on focus
|
||||||
if (clicked.type === "input" && clicked.props.type !== "checkbox") {
|
if (
|
||||||
const valueProp = clicked.props.value;
|
clicked.type === "input" &&
|
||||||
|
(clicked.props as InputProps).type !== "checkbox"
|
||||||
|
) {
|
||||||
|
const valueProp = (clicked.props as InputProps).value;
|
||||||
if (typeof valueProp === "function") {
|
if (typeof valueProp === "function") {
|
||||||
const currentValue = (valueProp as () => string)();
|
const currentValue = (valueProp as () => string)();
|
||||||
clicked.cursorPos = currentValue.length;
|
clicked.cursorPos = currentValue.length;
|
||||||
@@ -354,10 +375,10 @@ export class Application {
|
|||||||
this.needsRender = true;
|
this.needsRender = true;
|
||||||
}
|
}
|
||||||
} else if (clicked.type === "input") {
|
} else if (clicked.type === "input") {
|
||||||
const type = clicked.props.type as string | undefined;
|
const type = (clicked.props as InputProps).type as string | undefined;
|
||||||
if (type === "checkbox") {
|
if (type === "checkbox") {
|
||||||
const onChangeProp = clicked.props.onChange;
|
const onChangeProp = (clicked.props as InputProps).onChange;
|
||||||
const checkedProp = clicked.props.checked;
|
const checkedProp = (clicked.props as InputProps).checked;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof onChangeProp === "function" &&
|
typeof onChangeProp === "function" &&
|
||||||
@@ -424,6 +445,14 @@ export class Application {
|
|||||||
|
|
||||||
const interactive = this.collectInteractive(this.root);
|
const interactive = this.collectInteractive(this.root);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.focusedNode !== undefined &&
|
||||||
|
typeof this.focusedNode.props.onFocusChanged === "function"
|
||||||
|
) {
|
||||||
|
const onFocusChanged = this.focusedNode.props
|
||||||
|
.onFocusChanged as Setter<boolean>;
|
||||||
|
onFocusChanged(false);
|
||||||
|
}
|
||||||
if (interactive.length === 0) {
|
if (interactive.length === 0) {
|
||||||
this.focusedNode = undefined;
|
this.focusedNode = undefined;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { concatSentence } from "../common";
|
|||||||
/**
|
/**
|
||||||
* Props for div component
|
* Props for div component
|
||||||
*/
|
*/
|
||||||
export type DivProps = BaseProps & Record<string, unknown>;
|
export type DivProps = BaseProps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for label component
|
* Props for label component
|
||||||
@@ -20,7 +20,7 @@ export type DivProps = BaseProps & Record<string, unknown>;
|
|||||||
export type LabelProps = BaseProps & {
|
export type LabelProps = BaseProps & {
|
||||||
/** Whether to automatically wrap long text. Defaults to false. */
|
/** Whether to automatically wrap long text. Defaults to false. */
|
||||||
wordWrap?: boolean;
|
wordWrap?: boolean;
|
||||||
} & Record<string, unknown>;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for button component
|
* Props for button component
|
||||||
@@ -28,7 +28,7 @@ export type LabelProps = BaseProps & {
|
|||||||
export type ButtonProps = BaseProps & {
|
export type ButtonProps = BaseProps & {
|
||||||
/** Click handler */
|
/** Click handler */
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
} & Record<string, unknown>;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for input component
|
* Props for input component
|
||||||
@@ -46,7 +46,7 @@ export type InputProps = BaseProps & {
|
|||||||
onChange?: Setter<boolean> | ((checked: boolean) => void);
|
onChange?: Setter<boolean> | ((checked: boolean) => void);
|
||||||
/** Placeholder text */
|
/** Placeholder text */
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
} & Record<string, unknown>;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for form component
|
* Props for form component
|
||||||
@@ -54,7 +54,7 @@ export type InputProps = BaseProps & {
|
|||||||
export type FormProps = BaseProps & {
|
export type FormProps = BaseProps & {
|
||||||
/** Submit handler */
|
/** Submit handler */
|
||||||
onSubmit?: () => void;
|
onSubmit?: () => void;
|
||||||
} & Record<string, unknown>;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic container component for layout
|
* Generic container component for layout
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* Calculates positions and sizes for UI elements based on flexbox rules
|
* Calculates positions and sizes for UI elements based on flexbox rules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { InputProps } from "./components";
|
||||||
import { UIObject } from "./UIObject";
|
import { UIObject } from "./UIObject";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,7 +110,7 @@ function measureNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "input": {
|
case "input": {
|
||||||
const type = node.props.type as string | undefined;
|
const type = (node.props as InputProps).type as string | undefined;
|
||||||
if (type === "checkbox") {
|
if (type === "checkbox") {
|
||||||
const naturalWidth = 3; // [X] or [ ]
|
const naturalWidth = 3; // [X] or [ ]
|
||||||
const naturalHeight = 1;
|
const naturalHeight = 1;
|
||||||
@@ -119,7 +120,7 @@ function measureNode(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Text input - use a default width or from props
|
// Text input - use a default width or from props
|
||||||
const defaultWidth = (node.props.width as number | undefined) ?? 20;
|
const defaultWidth = node.props.width ?? 20;
|
||||||
const naturalHeight = 1;
|
const naturalHeight = 1;
|
||||||
return {
|
return {
|
||||||
width: measuredWidth ?? defaultWidth,
|
width: measuredWidth ?? defaultWidth,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { UIObject } from "./UIObject";
|
import { UIObject } from "./UIObject";
|
||||||
import { Accessor } from "./reactivity";
|
import { InputProps } from "./components";
|
||||||
import { isScrollContainer } from "./scrollContainer";
|
import { isScrollContainer } from "./scrollContainer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,14 +189,14 @@ function drawNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "input": {
|
case "input": {
|
||||||
const type = node.props.type as string | undefined;
|
const type = (node.props as InputProps).type as string | undefined;
|
||||||
|
|
||||||
if (type === "checkbox") {
|
if (type === "checkbox") {
|
||||||
// Draw checkbox
|
// Draw checkbox
|
||||||
let isChecked = false;
|
let isChecked = false;
|
||||||
const checkedProp = node.props.checked;
|
const checkedProp = (node.props as InputProps).checked;
|
||||||
if (typeof checkedProp === "function") {
|
if (typeof checkedProp === "function") {
|
||||||
isChecked = (checkedProp as Accessor<boolean>)();
|
isChecked = checkedProp();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focused) {
|
if (focused) {
|
||||||
@@ -212,12 +212,11 @@ function drawNode(
|
|||||||
} else {
|
} else {
|
||||||
// Draw text input
|
// Draw text input
|
||||||
let displayText = "";
|
let displayText = "";
|
||||||
const valueProp = node.props.value;
|
const valueProp = (node.props as InputProps).value;
|
||||||
if (typeof valueProp === "function") {
|
if (typeof valueProp === "function") {
|
||||||
displayText = (valueProp as Accessor<string>)();
|
displayText = valueProp();
|
||||||
}
|
}
|
||||||
|
const placeholder = (node.props as InputProps).placeholder;
|
||||||
const placeholder = node.props.placeholder as string | undefined;
|
|
||||||
const cursorPos = node.cursorPos ?? 0;
|
const cursorPos = node.cursorPos ?? 0;
|
||||||
let currentTextColor = textColor;
|
let currentTextColor = textColor;
|
||||||
let showPlaceholder = false;
|
let showPlaceholder = false;
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ const Counter = () => {
|
|||||||
div(
|
div(
|
||||||
{ class: "flex flex-row" },
|
{ class: "flex flex-row" },
|
||||||
button({ onClick: () => setCount(count() - 1), class: "text-red" }, "-"),
|
button({ onClick: () => setCount(count() - 1), class: "text-red" }, "-"),
|
||||||
button({ onClick: () => setCount(count() + 1), class: "text-green" }, "+"),
|
button(
|
||||||
|
{ onClick: () => setCount(count() + 1), class: "text-green" },
|
||||||
|
"+",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -81,10 +84,10 @@ const TodosApp = () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
label(
|
label(
|
||||||
{
|
{
|
||||||
class: todo.completed ? "ml-1 text-gray" : "ml-1 text-white"
|
class: todo.completed ? "ml-1 text-gray" : "ml-1 text-white",
|
||||||
},
|
},
|
||||||
() => todo.title
|
() => todo.title,
|
||||||
),
|
),
|
||||||
button(
|
button(
|
||||||
{
|
{
|
||||||
@@ -308,7 +311,7 @@ const App = () => {
|
|||||||
{
|
{
|
||||||
when: () => tabIndex() === 0,
|
when: () => tabIndex() === 0,
|
||||||
fallback: Show(
|
fallback: Show(
|
||||||
{
|
{
|
||||||
when: () => tabIndex() === 1,
|
when: () => tabIndex() === 1,
|
||||||
fallback: Show(
|
fallback: Show(
|
||||||
{
|
{
|
||||||
@@ -318,13 +321,13 @@ const App = () => {
|
|||||||
when: () => tabIndex() === 3,
|
when: () => tabIndex() === 3,
|
||||||
fallback: MultiScrollExample(),
|
fallback: MultiScrollExample(),
|
||||||
},
|
},
|
||||||
StaticScrollExample()
|
StaticScrollExample(),
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
SimpleScrollExample()
|
SimpleScrollExample(),
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
TodosApp()
|
TodosApp(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Counter(),
|
Counter(),
|
||||||
@@ -347,4 +350,4 @@ try {
|
|||||||
print("Error running application:");
|
print("Error running application:");
|
||||||
printError(e);
|
printError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user