مربع نص متعدد الأسطر
مربع نص أصلي يُعرض على الخادم. التمدد التلقائي والعدّاد الحي هما الجزآن الوحيدان اللذان يُحمَّلان في المتصفح.
split - الحجم لم يُقَس بعد
مربع نص متعدد الأسطر
split - الحجم لم يُقَس بعدمربع نص أصلي يُعرض على الخادم. التمدد التلقائي والعدّاد الحي هما الجزآن الوحيدان اللذان يُحمَّلان في المتصفح.
جرّبه
size
invalid
fullWidth
rows
maxRows
autosize
showCount
الشيفرة
textarea.tsx
import { Textarea } from 'topclassui/textarea'
<div className="grid max-w-sm gap-1">
<label htmlFor="textarea-playground" className="text-sm font-medium text-body">About you</label>
<Textarea id="textarea-playground" name="bio" placeholder="Write a short paragraph" />
</div>وحده هو HTML من الخادم؛ autosize أو showCount هو ما يحمّل نصف العميل الصغير.
كل الحالات، بدون تمرير الفأرة
6 حالاتأو استورده من الحزمة
topclassui/textarea
import { Textarea } from 'topclassui/textarea'ثبّت مرة واحدة:npm i topclassui@beta zod
أمثلة
maxLengthقيمته 5000 افتراضيًا. مرّر رقمك، أوmaxLength={null}لإلغاء الحد؛ عنده يوقف المتصفح الكتابة واللصق.- مع
autosizeتتبع الأسطر النص منrowsحتىmaxRows، ثم يظهر التمرير.maxRowsوحده لا يمدّد شيئًا. - يظهر العدّاد بصيغة 42 / 500 تحت الحقل: دائمًا مع
showCount، ومعautosizeوحده ابتداءً من تسعة أعشار الحد. عند الحد يصبح غامقًا، وهذا ليس خطأ. - مع
autosizeأوshowCountيعود العرض إلى صندوق يحيط بالمربع: استخدمfullWidth، أو حدّد عرض الحاوية. - بلا أي سكربت يُحفظ ما كُتب ويُرسل، وإعادة ضبط النموذج أصلية.
basic
A short paragraph is enough
basic.tsx
import { Textarea } from 'topclassui/textarea'
/** A label points at the id, and aria-describedby ties the hint on. No script is needed for any of it. */
export default function Demo() {
return (
<div className="grid max-w-sm gap-1">
<label htmlFor="textarea-basic-bio" className="text-sm font-medium text-body">About you</label>
<Textarea
id="textarea-basic-bio"
name="bio"
fullWidth
placeholder="Write a short paragraph"
aria-describedby="textarea-basic-hint"
/>
<span id="textarea-basic-hint" className="text-xs text-muted">A short paragraph is enough</span>
</div>
)
}
autosize
autosize.tsx
import { Textarea } from 'topclassui/textarea'
const LONG = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'].join('\n')
/** One empty field that grows from three rows to six as lines come, and one already past six that scrolls. */
export default function Demo() {
return (
<div className="grid max-w-sm gap-3">
<Textarea fullWidth autosize placeholder="Grows as you write" aria-label="Grows" />
<Textarea fullWidth autosize defaultValue={LONG} aria-label="Past six rows" />
</div>
)
}
show-count
عدد الأحرف: 12 من 60
عدد الأحرف: 12 من 12
show-count.tsx
import { Textarea } from 'topclassui/textarea'
/** The count against the cap: always with showCount, from nine tenths of it with autosize alone, never with no cap. */
export default function Demo() {
return (
<div className="grid max-w-sm gap-3">
<Textarea fullWidth showCount maxLength={60} defaultValue="Twelve chars" aria-label="Always counted" />
<Textarea fullWidth autosize maxLength={60} aria-label="Counted near the cap" />
<Textarea fullWidth showCount maxLength={12} defaultValue="Twelve chars" aria-label="At the cap" />
<Textarea fullWidth showCount maxLength={null} aria-label="No cap" />
</div>
)
}
sizes
sizes.tsx
import { Textarea } from 'topclassui/textarea'
/** The five steps of the field scale: the padding and the type step change, the three rows stay. */
export default function Demo() {
return (
<div className="grid justify-items-start gap-3">
<Textarea size="xs" placeholder="Extra small" aria-label="Extra small" />
<Textarea size="sm" placeholder="Small" aria-label="Small" />
<Textarea placeholder="Medium" aria-label="Medium" />
<Textarea size="lg" placeholder="Large" aria-label="Large" />
<Textarea size="xl" placeholder="Extra large" aria-label="Extra large" />
</div>
)
}
form
form.tsx
import { Button } from 'topclassui/button'
import { Textarea } from 'topclassui/textarea'
/** A plain form: the textarea sends what is typed before any script runs, and a reset needs none either. */
export default function Demo() {
return (
<form className="grid max-w-sm justify-items-start gap-3">
<label htmlFor="textarea-form-message" className="text-sm font-medium text-body">Message</label>
<Textarea id="textarea-form-message" name="message" fullWidth defaultValue="Hello" required />
<label htmlFor="textarea-form-notes" className="text-sm font-medium text-body">Notes</label>
<Textarea id="textarea-form-notes" name="notes" fullWidth autosize showCount />
<div className="flex flex-wrap gap-2">
<Button type="submit">Send</Button>
<Button type="reset" variant="outline">Clear</Button>
</div>
</form>
)
}
row
عدد الأحرف: 0 من 5,000
عدد الأحرف: 0 من 5,000
row.tsx
import { Textarea } from 'topclassui/textarea'
/** Two in a flex row: each keeps an Input's width while there is room, and both give way on a phone. */
export default function Demo() {
return (
<div className="grid gap-4">
<div data-row="plain" className="flex gap-2">
<Textarea name="pros" placeholder="What went well" aria-label="What went well" />
<Textarea name="cons" placeholder="What did not" aria-label="What did not" />
</div>
<div data-row="held" className="flex gap-2">
<Textarea name="pros-held" autosize showCount placeholder="What went well" aria-label="What went well, counted" />
<Textarea name="cons-held" autosize showCount placeholder="What did not" aria-label="What did not, counted" />
</div>
</div>
)
}
long-content
long-content.tsx
import { Textarea } from 'topclassui/textarea'
const WORD = 'Supercalifragilistic'.repeat(15)
const LINES = Array.from({ length: 60 }, (_, line) => `Line ${String(line + 1)}`).join('\n')
/** Hostile text: one word of 300 characters wraps inside the box, and sixty lines scroll inside it. */
export default function Demo() {
return (
<div className="grid max-w-sm gap-3">
<Textarea fullWidth defaultValue={WORD} aria-label="One long word" />
<Textarea defaultValue={WORD} aria-label="One long word, own width" />
<Textarea fullWidth autosize defaultValue={LINES} aria-label="Sixty lines" />
</div>
)
}
skeleton
skeleton.tsx
import { Textarea, TextareaSkeleton } from 'topclassui/textarea'
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const
/** Each field above the skeleton that holds its place, in a wrapper each: the same box. */
export default function Demo() {
return (
<div className="grid gap-4">
{SIZES.map((size) => (
<div key={size} data-pair={size} className="grid gap-2">
<div><Textarea size={size} placeholder="Message" aria-label={`Message, ${size}`} /></div>
<div><TextareaSkeleton size={size} /></div>
</div>
))}
<div data-pair="one-row" className="grid gap-2">
<div><Textarea size="xs" rows={1} placeholder="One row" aria-label="One row" /></div>
<div><TextareaSkeleton size="xs" rows={1} /></div>
</div>
<div data-pair="full" className="grid gap-2">
<div><Textarea fullWidth rows={5} placeholder="Fills its container" aria-label="Full width" /></div>
<div><TextareaSkeleton fullWidth rows={5} /></div>
</div>
</div>
)
}
بالعربية، من اليمين إلى اليسار
عدد الأحرف: ٢٤ من ٥٠٠
rtl.tsx
import { TopClassProvider } from 'topclassui/provider'
import { Textarea } from 'topclassui/textarea'
/** Arabic, inside TopClassProvider as the root layout would mount it, with Arabic-Indic digits: the
* counter sits at the inline end, on the left, and its two numbers keep their order. */
export default function Demo() {
return (
<TopClassProvider direction="rtl" locale="ar" numberingSystem="arab">
<div dir="rtl" lang="ar" className="grid max-w-sm gap-3">
<label htmlFor="textarea-rtl-bio" className="text-sm font-medium text-body">نبذة عنك</label>
<Textarea id="textarea-rtl-bio" name="bio" fullWidth showCount maxLength={500} defaultValue="مرحبا بكم في TopClass UI" />
<Textarea name="note" fullWidth dir="auto" placeholder="اكتب ملاحظتك" aria-label="ملاحظة" />
</div>
</TopClassProvider>
)
}
الخصائص
لوحة المفاتيح وإمكانية الوصول
- الاسم مطلوب: تسمية
<label htmlFor>تشير إلىid، أوaria-label، أوaria-labelledby. - العدّاد هو وصف الحقل، فيقرؤه قارئ الشاشة بعد الاسم. وهو ليس منطقة إعلان حيّة.
invalidيضبط aria-invalid، ويتحوّل الإطار إلى لون الخطر ويزداد سمكًا.
من اليمين إلى اليسار
يبدأ النص من اليمين، ويقع العدّاد في نهاية السطر، أي على اليسار. اضبط الاتجاه على auto ليتبع كل نص حرفه الأول.
الخادم والمتصفح
يُعرض على الخادم، والجزء التفاعلي مكوّن متصفح لا يُرسل إلا عند استخدامه.
الملفات: index.tsx, client.tsx.
على الشارة: split - الحجم لم يُقَس بعد.