feat: add <br /> tag support and update examples

- Introduced a new <br /> tag for line breaks in JSX, enhancing readability and usability.
- Updated multiple example components to utilize the <br /> tag instead of newline characters for better formatting.
- Added a new BrDemo component to showcase the <br /> tag functionality.
This commit is contained in:
Kyle Fang
2025-07-01 09:39:16 +08:00
parent cd808b8d4f
commit 19ebc996d6
9 changed files with 129 additions and 55 deletions

44
src/br-demo.tsx Normal file
View File

@@ -0,0 +1,44 @@
/// <reference path="./jsx.d.ts" />
import React from 'react';
import { createContainer } from './reconciler';
// Demo showcasing <br /> tag support
const BrDemo: React.FC = () => {
return (
<>
<b>Line Break Demo</b>
<br />
<br />
This demonstrates the new <code>&lt;br /&gt;</code> tag support.
<br />
<br />
<i>Benefits:</i>
<br />
Cleaner JSX syntax
<br />
More intuitive for React developers
<br />
No more {'\\n'} strings
<br />
<br />
<b>Example Usage:</b>
<br />
<pre>{`<b>Title</b>
<br />
<i>Subtitle</i>
<br />
<br />
Content here...`}</pre>
</>
);
};
// Create container and render
const { render, container } = createContainer();
render(<BrDemo />);
// Log the output
setTimeout(() => {
console.log('Line break demo output:');
console.log(JSON.stringify(container.root, null, 2));
}, 0);

View File

@@ -9,9 +9,11 @@ const CounterApp = () => {
return (
<>
<b>🔢 Counter Bot</b>
{'\n\n'}
<br />
<br />
<i>Current count: {count}</i>
{'\n\n'}
<br />
<br />
<row>
<button onClick={() => setCount(c => c - 1)}> Decrease</button>
<button onClick={() => setCount(c => c + 1)}> Increase</button>
@@ -30,18 +32,19 @@ const TodoApp = () => {
return (
<>
<b>📝 Todo List</b>
{'\n\n'}
<br />
<br />
{todos.length === 0 ? (
<i>No todos yet!</i>
) : (
todos.map((todo, i) => (
<React.Fragment key={i}>
{showCompleted ? <s>{todo}</s> : todo}
{'\n'}
<br />
</React.Fragment>
))
)}
{'\n'}
<br />
<row>
<button onClick={() => setTodos([...todos, `Task ${todos.length + 1}`])}>
Add Task
@@ -69,27 +72,29 @@ const HelpApp = () => {
return (
<>
<b>Text Formatting Examples</b>
{'\n\n'}
<br />
<br />
<b>Bold text</b>
{'\n'}
<br />
<i>Italic text</i>
{'\n'}
<br />
<u>Underlined text</u>
{'\n'}
<br />
<s>Strikethrough</s>
{'\n'}
<br />
<tg-spoiler>Hidden spoiler</tg-spoiler>
{'\n'}
<br />
<code>inline code</code>
{'\n\n'}
<br />
<br />
<pre>
{`function example() {
return "Code block";
}`}
</pre>
{'\n'}
<br />
<blockquote>This is a quote</blockquote>
{'\n'}
<br />
<row>
<button onClick={() => setSection('main')}> Back</button>
</row>
@@ -101,7 +106,8 @@ const HelpApp = () => {
return (
<>
<b>Bot Features</b>
{'\n\n'}
<br />
<br />
<blockquote expandable>
This bot demonstrates a React-based Telegram bot using a custom reconciler.
@@ -112,7 +118,7 @@ const HelpApp = () => {
Custom emoji support
Dynamic content updates
</blockquote>
{'\n'}
<br />
Links: <a href="https://github.com">GitHub</a> | <a href="tg://user?id=123456">Contact</a>
{'\n\n'}
<row>
@@ -125,17 +131,20 @@ const HelpApp = () => {
return (
<>
<b>🤖 React Telegram Bot</b>
{'\n\n'}
<br />
<br />
Welcome! This bot is powered by React
{'\n\n'}
<br />
<br />
Available commands:
{'\n'}
<br />
/counter - Interactive counter
{'\n'}
<br />
/todo - Todo list manager
{'\n'}
<br />
/help - This help message
{'\n\n'}
<br />
<br />
<row>
<button onClick={() => setSection('formatting')}>📝 Formatting</button>
<button onClick={() => setSection('features')}> Features</button>

View File

@@ -14,6 +14,7 @@ const App = () => {
return (
<>
<b>Welcome to Telegram React!</b>
<br />
<i>Current count: {count}</i>
<row>
<button onClick={() => setCount(p => p - 1)}> Decrease</button>

View File

@@ -22,38 +22,38 @@ const AutoDeleteDemo: React.FC = () => {
return (
<>
<b>Input Auto-Delete Demo</b>
{'\n\n'}
<br /><br />
Current mode: <b>{mode === 'normal' ? '📝 Normal Mode' : '🤫 Secret Mode'}</b>
{'\n\n'}
<br /><br />
{mode === 'normal' ? (
<>
<i>Send any message - it will stay in the chat.</i>
{'\n'}
<br />
<input onSubmit={handleNormalInput} />
</>
) : (
<>
<i>Send a secret message - it will be deleted automatically!</i>
{'\n'}
<br />
<input onSubmit={handleSecretInput} autoDelete />
</>
)}
{'\n\n'}
<br />
<br />
{messages.length > 0 && (
<>
<b>Received Messages:</b>
{'\n'}
<br />
{messages.map((msg, idx) => (
<React.Fragment key={idx}>
{msg}
{'\n'}
<br />
</React.Fragment>
))}
{'\n'}
<br />
</>
)}

View File

@@ -51,16 +51,19 @@ const QuizBot: React.FC = () => {
return (
<>
<b>🎉 Quiz Complete!</b>
{'\n\n'}
<br />
<br />
Your final score: <b>{score}/{questions.length}</b>
{'\n\n'}
<br />
<br />
{score === questions.length ?
"Perfect score! Well done! 🌟" :
score >= questions.length / 2 ?
"Good job! 👍" :
"Better luck next time! 📚"
}
{'\n\n'}
<br />
<br />
<row>
<button onClick={restart}>Play Again</button>
</row>
@@ -73,33 +76,36 @@ const QuizBot: React.FC = () => {
return (
<>
<b>Quiz Bot 🤖</b>
{'\n'}
<br />
Question {currentQuestion + 1} of {questions.length}
{'\n'}
<br />
Score: {score}/{currentQuestion}
{'\n\n'}
<br />
<br />
<b>{currentQ?.question}</b>
{'\n\n'}
<br />
<br />
{lastAnswer !== null && (
<>
Your answer: <code>{lastAnswer}</code>
{'\n'}
<br />
{lastAnswer.toLowerCase().trim() === currentQ?.answer ?
"✅ Correct!" :
`❌ Wrong! The answer was: ${currentQ?.answer}`
}
{'\n\n'}
<br />
<br />
{currentQuestion < questions.length - 1 && "Next question coming up..."}
{'\n'}
<br />
</>
)}
{waitingForAnswer && !lastAnswer && (
<>
<i>Reply to this message with your answer!</i>
{'\n'}
<br />
</>
)}

View File

@@ -19,28 +19,31 @@ const InputExample: React.FC = () => {
return (
<>
<b>Input Example</b>
{'\n\n'}
<br />
<br />
{messages.length === 0 ? (
<>
<i>Reply to this message to send text!</i>
{'\n\n'}
<br />
<br />
The bot will echo whatever you type.
</>
) : (
<>
<b>Message History:</b>
{'\n'}
<br />
{messages.map((msg, idx) => (
<React.Fragment key={idx}>
{msg}
{'\n'}
<br />
</React.Fragment>
))}
</>
)}
{'\n\n'}
<br />
<br />
{/* Input handler - will process any reply to this message */}
{waitingForInput && <input onSubmit={handleInput} autoDelete />}

3
src/jsx.d.ts vendored
View File

@@ -60,6 +60,9 @@ declare module 'react' {
onSubmit?: (text: string) => void;
autoDelete?: boolean;
};
// Line break
br: {};
}
}
}

View File

@@ -114,6 +114,8 @@ const hostConfig: ReactReconciler.HostConfig<
return { type: 'row', children: [] };
case 'input':
return { type: 'input', onSubmit: props.onSubmit, autoDelete: props.autoDelete };
case 'br':
return { type: 'text', content: '\n' };
default:
return { type: 'formatted', format: 'bold', children: [] };
}

View File

@@ -11,9 +11,10 @@ const TypedApp: React.FC = () => {
return (
<>
<b>TypeScript Example</b>
{'\n'}
<br />
<i>All custom elements are properly typed!</i>
{'\n\n'}
<br />
<br />
{/* Text formatting */}
<u>Underlined text</u>
@@ -21,7 +22,8 @@ const TypedApp: React.FC = () => {
<s>Strikethrough</s>
{' '}
<code>inline code</code>
{'\n\n'}
<br />
<br />
{/* Spoiler */}
{showSpoiler ? (
@@ -29,17 +31,20 @@ const TypedApp: React.FC = () => {
) : (
<span className="tg-spoiler">Hidden content</span>
)}
{'\n\n'}
<br />
<br />
{/* Links */}
<a href="https://telegram.org">Telegram Website</a>
{' | '}
<a href="tg://user?id=123456">User mention</a>
{'\n\n'}
<br />
<br />
{/* Emoji */}
<tg-emoji emojiId="5368324170671202286">👍</tg-emoji>
{'\n\n'}
<br />
<br />
{/* Code block */}
<pre>
@@ -48,14 +53,15 @@ const TypedApp: React.FC = () => {
console.log(message);`}
</code>
</pre>
{'\n'}
<br />
{/* Blockquote */}
<blockquote expandable>
This is an expandable quote.
It can contain multiple lines.
</blockquote>
{'\n\n'}
<br />
<br />
{/* Interactive buttons */}
<row>