TokenField
A field of text with tokens in it, such as mentions, tags or commands, which the user types and the field turns into tokens.
Ask @ada about #launch
Type a @name or a #tag, then a space.Tokens: @ada, #launch.
<script>
import { $bind, $memo, $signal } from 'brumate/signals';
import Label from '@brumate/ui/Label.brm';
import Text from '@brumate/ui/Text.brm';
import Token from '@brumate/ui/Token.brm';
import TokenField from '@brumate/ui/TokenField.brm';
import TokenInput from '@brumate/ui/TokenInput.brm';
import type { TokenSegment } from '@brumate/ui/token-field.ts';
let message = $signal<TokenSegment[]>([
{ type: 'text', text: 'Ask ' },
{ type: 'token', text: '@ada' },
{ type: 'text', text: ' about ' },
{ type: 'token', text: '#launch' },
{ type: 'text', text: ' ' },
]);
const tokens = $memo(message.filter((segment) => segment.type === 'token').map((one) => one.text));
</script>
<TokenField class="ui-field" pattern="[#@]\S+" placeholder="Write a message" value=$bind(message)>
<Label class="ui-label">Message</Label>
<TokenInput class="ui-input ui-token-input" token=chip />
<Text slot="description" class="ui-description">Type a @name or a #tag, then a space.</Text>
</TokenField>
<p>Tokens: {tokens.join(', ') || 'none'}.</p>
#template chip(segment: TokenSegment) {
<Token class=(segment.text.startsWith('@') ? 'ui-token ui-mention' : 'ui-token')>{segment.text}</Token>
}
<TokenField pattern="[#@]\S+" placeholder="Write a message" value=$bind(message)>
<Label>Message</Label>
<TokenInput token=chip />
<Text slot="description">Type a @name or a #tag, then a space.</Text>
</TokenField>
#template chip(segment: TokenSegment) {
<Token>{segment.text}</Token>
}
- A
TokenFieldholds aLabel, aTokenInputand aTextwithslot="description". The input is named by the label and described by the text. valueis an array of segments, each{ type: 'text' | 'token', text }, which the page state holds.onChangehears of changes. React Aria'sTokenFieldValueis a class, which the page state cannot hold, so the value is the array of its segments.patternis a regular expression, as a string. Text that matches it becomes a token once a space follows it, so a word that the user is still typing stays text.tokenis a#templatethat renders aTokenfor each token. Without it, a token is a Token with its text.- With
name, a form sends the text of the value, with each token as its text. placeholder,allowsNewlines,isDisabledandisReadOnly.roleistextbox, orsearchboxfor a field that searches. The box has no popup, so it is never acombobox.
Editing
- The TokenInput is an element with
contenteditable, which the browser edits. It reads its text back into the value, and writes itself again when tokens change, with the caret where it was. - A token is one piece. The caret goes past it, and Backspace removes it whole.
- Enter adds a new line only with
allowsNewlines, and a paste adds its text without its markup. - The TokenInput shows
data-focused,data-focus-visible,data-disabled,data-readonlyanddata-empty, with the placeholder indata-placeholder, for CSS. A Token showsdata-selectedwhile the selection covers it, anddata-disabled.