Toast & SvgSource
Two small globals: toast shows a short message over the app for about two seconds, and
SvgSource turns SVG markup into an image source. A toast is a banner inside the running app,
not an OS notification: a notification in the system tray is a remote push
(Push), and local or scheduled notifications do not exist
(Not available).
- show a short "Saved" or "Copied" message after an action (
toast) - tell the user a request failed without opening a dialog
- show SVG markup as an image or an icon (
SvgSource) - recolor an SVG icon, or tint it with a theme color
- show an SVG file of the project as an image (
asset('./logo.svg'))
Show a short message: toast
toast(message: string): voidtoast shows a short message ("Saved", "Copied", "No connection") near the bottom of the screen
for about 2 seconds, then hides it. It is fire-and-forget: nothing to await, no tap handler, no
way to close it early. It is drawn inside the app while the app is open, never in the system's
notification tray; for that, see Push.
const code = 'LC-4821'
UIScreen(
UIColumn(
UIText(`Invite code: ${code}`),
UIButton(UIText('Copy')).onClick(() => {
clipboard.write(code)
toast('Copied')
}),
).style({ p: 16, gap: 12 }),
).open()keep it to a few words: the desktop host draws the message on one line, unwrapped. Hosts differ on a second toast while one is showing (queued on iOS and Android, stacked on the web, replaced on desktop), so show one at a time.
a toast is not an element of the UI tree, so a test flow (lecodes render /
lecodes test) cannot expect its text on any host, and headless runs do not draw it.
console.log the outcome the test should check.
Report a failed request: toast in a catch
A toast tells the user that something did not work without stopping them with a dialog. Check the HTTP status (an error status still resolves) and catch the rejection of a request that never reached the server:
const status = UIText('No news yet')
const reload = async () => {
try {
const res = await fetch('https://api.example.com/news')
if (res.status !== 200) { toast(`Could not load the news (${res.status})`); return }
status.text = `${res.json<unknown[]>().length} stories`
} catch (e) {
toast('No connection')
}
}
UIScreen(
UIColumn(status, UIButton(UIText('Reload')).onClick(reload)).style({ p: 16, gap: 12 }),
).open()For an error the user must act on, use a dialog (UIModal, widget.md).
SVG markup as an image: SvgSource
SvgSource(svg: string): SvgSource // { readonly svg: string, tintColor: ColorInput | null }SvgSource wraps a string of SVG XML so it can be shown as an image: the source of a UIImage
or a bgImage style (UI content). SvgSource is also the type: what
assetIcon("lucide:bell") and asset('./logo.svg') return too, so a component that takes an
icon is typed (icon: SvgSource). For an icon from a pack, assetIcon needs no hand-written
SVG (Icons).
const star = SvgSource(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +
'<path fill="#f5a623" d="M12 2l3 7h7l-5.5 4.5 2 7.5-6.5-4.5-6.5 4.5 2-7.5L2 9h7z"/></svg>',
)
UIScreen(
UIRow(
UIImage(star).style({ width: 24, height: 24 }),
UIText('Favorites'),
).style({ p: 16, gap: 8, alignItems: 'center' }),
).open()Change the color of an SVG icon: tintColor
Set tintColor on an SvgSource to change the color of an SVG icon: the whole SVG is drawn in
that one color, every fill and stroke, which suits a one-color icon, not multicolored artwork. It
takes any color form or a theme variable (var(--…), an accessor of theme()), which follows
later theme() calls; null (the default) keeps the SVG's own colors. The tintColor style of
a UIImage does the same for one image and wins over the source's tint.
The tint is read when the image takes the source: set tintColor before UIImage(icon), and to
change it later, set it and assign the source again (img.src = icon).
const colors = theme({ liked: '#e0245e', idle: '#9aa0a6' })
const heart = SvgSource('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +
'<path d="M12 21 3.5 12.5a5 5 0 0 1 8.5-6 5 5 0 0 1 8.5 6z"/></svg>')
heart.tintColor = colors.idle // set before the image takes the source
let liked = false
const icon = UIImage(heart).style({ width: 28, height: 28 }).named('heart')
UIScreen(
UIButton(icon).named('like').onClick(() => {
liked = !liked
heart.tintColor = liked ? colors.liked : colors.idle
icon.src = heart // the tint is read on assign: assign the source again
}),
).open()as a bgImage, an SvgSource keeps its own colors: the value's tintColor is
applied to a UIImage only.
An SVG file of the project: asset('./logo.svg')
asset('./logo.svg') reads the file at build time and gives the same SvgSource value, ready
for UIImage or bgImage. import logo from './logo.svg' bundles to the same value, but the
project's types do not declare .svg modules and tsc rejects it: use asset().
const logo = asset('./logo.svg') // an SvgSource, made from the file at build time
UIScreen(
UIColumn(
UIImage(logo).style({ width: 120, height: 40, tintColor: '#ffffff' }),
).style({ p: 24, bgColor: '#15a34a', alignItems: 'center' }),
).open()every asset('./logo.svg') in the app is the SAME object. Setting its tintColor
changes it for every image that takes it afterwards; tint one image with the tintColor style
(as above), or tint a copy: { ...logo, tintColor: '#ffffff' }.
See also
- UI content:
UIImage,assetIconand the full set of image sources - Styling:
bgImageand the other background layers - Push: notifications that reach the phone when the app is closed
- Networking:
FetchResponse/Fileas the other image sources