TypingIndicator
"Ana is typing…" with pulsing dots, naming one, two or more people, and nothing at all when nobody is.
Demo
Ana is typing…
Ana Silva, Ben Okafor and Lee Chen are typing…
Support
Sam is looking into it…
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/typing_indicator.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/typing_indicator.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/typing_indicator.qwe
component TypingIndicatorDemoLive() { state who = ["Ana Silva"] let all = ["Ana Silva", "Ben Okafor", "Lee Chen", "Mia Rossi"] <div.demo-stack> <div id="typing-indicator-demo-live"><TypingIndicator names={who} first_names={true}/></div> <div.demo-row> {for p in all => <label key={p} style="display: inline-flex; gap: 6px; align-items: center; font-size: 13px"> <input type="checkbox" checked={contains(who, p)} value={p} @change={(e) => { who = e.checked ? all | filter((x) => x == p || contains(who, x)) : who | filter((x) => x != p) }}/> {p} </label>} </div> </div>}view TypingIndicatorDemo() = <div.demo-stack> <TypingIndicatorDemoLive/> <div id="typing-indicator-demo-plain"><TypingIndicator names={["Ana Silva", "Ben Okafor", "Lee Chen"]}/></div> <div.demo-note id="typing-indicator-demo-inline">Support <TypingIndicator names={["Sam"]} dots={false} text="is looking into it…"/></div> </div>Source
ui/typing_indicator.qwe
// TypingIndicator: "Ana is typing…" with three dots that pulse, for the bottom of a// chat. With nobody typing it shows nothing and takes no room, so it can stay in the// page and be given the names as they come.//// <TypingIndicator names={["Ana Silva"]}/> Ana Silva is typing…// <TypingIndicator names={typing} first_names={true}/> Ana and Ben are typing…// <TypingIndicator names={["Ana", "Ben", "Lee", "Mia"]}/> Ana, Ben and 2 others are typing…//// names: who is typing (an empty list shows nothing). first_names: true shows each// person's first word only. dots: false drops the dots (the text alone, inline).// text: the words after the names ("is typing…" / "are typing…" by default; give one// to say something else, "is recording…"). It is a status for screen readers, so// they hear it change.import ui/theme.qwe use ui_clslet typing_indicator_css = css`.ui-typing-indicator { display: flex; align-items: center; gap: 8px; min-height: 26px; color: var(--ui-muted); font: 12px/1.3 var(--ui-font); }.ui-typing-indicator:empty { min-height: 0; }.ui-typing-indicator-dots { display: inline-flex; gap: 3px; padding: 7px 10px; border-radius: 12px; background: var(--ui-subtle); }.ui-typing-indicator-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ui-muted); animation: ui-typing-indicator-dot 1.2s infinite ease-in-out; }.ui-typing-indicator-dots i:nth-child(2) { animation-delay: .15s; }.ui-typing-indicator-dots i:nth-child(3) { animation-delay: .3s; }.ui-typing-indicator-inline { display: inline-flex; min-height: 0; }@keyframes ui-typing-indicator-dot { 0%, 60%, 100% { opacity: .35; transform: none; } 30% { opacity: 1; transform: translateY(-2px); } }@media (prefers-reduced-motion: reduce) { .ui-typing-indicator-dots i { animation: none; opacity: .7; } }`// "Ana", "Ana and Ben", "Ana, Ben and Lee", "Ana, Ben and 2 others".fn typing_indicator_who(names: list) -> str { let n = len(names) if n == 0 { return "" } if n == 1 { return names[0] } if n == 2 { return `${names[0]} and ${names[1]}` } if n == 3 { return `${names[0]}, ${names[1]} and ${names[2]}` } `${names[0]}, ${names[1]} and ${n - 2} others`}view TypingIndicator(names: list = [], first_names: bool = false, dots: bool = true, text: str? = null, class: str? = null) { style global typing_indicator_css let shown = first_names ? names | map((nm) => split(trim(nm), " ")[0]) : names let words = text ?? (len(names) == 1 ? "is typing…" : "are typing…") <div class={ui_cls(["ui-typing-indicator", !dots && "ui-typing-indicator-inline", class])} role="status" aria-live="polite"> {len(names) > 0 && [ dots && <span.ui-typing-indicator-dots aria-hidden="true"><i></i><i></i><i></i></span>, <span>{typing_indicator_who(shown)} {words}</span>, ]} </div>}Related
ChatInputA chat message box that grows as you type, sends on Enter and breaks the line on Shift+Enter; a plain form in a page.6 views · 6 downloadsMessageBubbleOne chat message, yours on the right or theirs on the left, with the time and sent, delivered, read or failed ticks.6 views · 8 downloadsChatWidgetA chat button in the corner that opens a small chat panel, with replies from your server and a count for unseen ones.4 views · 7 downloadsChatThreadA conversation: grouped messages with avatars, days, a typing indicator, and a box where Enter sends.4 views · 4 downloadsCommentOne comment: avatar, name, role, time, text, Like and Reply, with its replies nested inside it.6 views · 8 downloadsCommentThreadNested comments with Like and Reply, a reply box that opens under the comment answered, and branches that fold.5 views · 5 downloads
Thread
Thread 0 posts
On TypingIndicator: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.