T02 typography: text styles, keys, formatters, fit, links, math, motion text
Headings to overlines, Code, Kbd and KbdCombo on gpui keystrokes, Highlight, MiddleEllipsis and EllipsisTooltip, links, eight formatted values on jiff with a typographic minus, CopyableText, SelectableText, Emoji, a TeX subset, rolling AnimatedNumber, Typewriter, tonal GradientText, ShimmerText. Gallery probes now hug their targets. Codex gpt-6-astra (max): PASS in round 2.
This commit is contained in:
30 files changed
+3125
-58
No files matched your search
@@ -1,6 +1,7 @@
|
||||
mod buttons;
|
||||
mod primitives;
|
||||
mod theme;
|
||||
mod typography;
|
||||
|
||||
use gpui::{AnyElement, App, Window};
|
||||
|
||||
@@ -16,7 +17,12 @@ pub struct Page {
|
||||
pub script: &'static [Step],
|
||||
}
|
||||
|
||||
pub const ALL: &[Page] = &[primitives::PAGE, buttons::PAGE, theme::PAGE];
|
||||
pub const ALL: &[Page] = &[
|
||||
primitives::PAGE,
|
||||
typography::PAGE,
|
||||
buttons::PAGE,
|
||||
theme::PAGE,
|
||||
];
|
||||
|
||||
pub fn find(slug: &str) -> Option<usize> {
|
||||
ALL.iter().position(|page| page.slug == slug)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
mod motion;
|
||||
mod text;
|
||||
mod values;
|
||||
|
||||
use gpui::{AnyElement, App, IntoElement, ParentElement, Window, div};
|
||||
|
||||
use super::Page;
|
||||
use crate::script::Step;
|
||||
|
||||
pub const PAGE: Page = Page {
|
||||
number: 2,
|
||||
slug: "typography",
|
||||
title: "Typography",
|
||||
summary: "Inter for words, tabular figures for numbers, one ink for everything.",
|
||||
render,
|
||||
script: SCRIPT,
|
||||
};
|
||||
|
||||
const SCRIPT: &[Step] = &[
|
||||
Step::Rest,
|
||||
Step::Hover("fit-tooltip"),
|
||||
Step::Wait(700),
|
||||
Step::Shot("ellipsis-tooltip"),
|
||||
Step::Rest,
|
||||
Step::ClickEnd("copy-token"),
|
||||
Step::Shot("copied"),
|
||||
Step::Click("select-text"),
|
||||
Step::Key("cmd-a"),
|
||||
Step::Shot("select-all"),
|
||||
Step::Rest,
|
||||
];
|
||||
|
||||
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
div()
|
||||
.child(text::headings(cx))
|
||||
.child(text::title(cx))
|
||||
.child(text::paragraph(cx))
|
||||
.child(text::label(cx))
|
||||
.child(text::caption(cx))
|
||||
.child(text::overline(cx))
|
||||
.child(text::inline_code(cx))
|
||||
.child(text::kbd(cx))
|
||||
.child(text::kbd_combo(cx))
|
||||
.child(text::blockquote(cx))
|
||||
.child(text::highlight(cx))
|
||||
.child(text::truncate(cx))
|
||||
.child(text::line_clamp(cx))
|
||||
.child(text::middle(cx))
|
||||
.child(text::ellipsis_tooltip(cx))
|
||||
.child(text::link(cx))
|
||||
.child(text::external_link(cx))
|
||||
.child(values::relative_time(cx))
|
||||
.child(values::date_time(cx))
|
||||
.child(values::numbers(cx))
|
||||
.child(values::currency(cx))
|
||||
.child(values::percent(cx))
|
||||
.child(values::file_size(cx))
|
||||
.child(values::duration(cx))
|
||||
.child(values::plural(cx))
|
||||
.child(values::copyable(cx))
|
||||
.child(values::selectable(cx))
|
||||
.child(values::emoji(cx))
|
||||
.child(values::latex(cx))
|
||||
.child(motion::animated_number(window, cx))
|
||||
.child(motion::typewriter(window, cx))
|
||||
.child(motion::gradient(cx))
|
||||
.child(motion::shimmer(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::{Button, ButtonVariant},
|
||||
theme::{ActiveTheme, TextSize},
|
||||
typography::{AnimatedNumber, GradientText, ShimmerText, Typewriter},
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, Styled, Task, Window, div};
|
||||
|
||||
use crate::ui::{row, section, specimen, specimens};
|
||||
|
||||
const STEP: Duration = Duration::from_millis(1600);
|
||||
const VALUES: [f64; 5] = [1_284.0, 1_917.0, 12_406.0, 9_998.0, 10_031.0];
|
||||
|
||||
struct Ticker {
|
||||
index: usize,
|
||||
_task: Task<()>,
|
||||
}
|
||||
|
||||
pub fn animated_number(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let ticker = window.use_keyed_state("type-ticker", cx, |window, cx| Ticker {
|
||||
index: 0,
|
||||
_task: cx.spawn_in(window, async move |ticker, cx| {
|
||||
loop {
|
||||
cx.background_executor().timer(STEP).await;
|
||||
let advanced = ticker.update(cx, |ticker: &mut Ticker, cx| {
|
||||
ticker.index = (ticker.index + 1) % VALUES.len();
|
||||
cx.notify();
|
||||
});
|
||||
if advanced.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
let value = VALUES[ticker.read(cx).index];
|
||||
section(
|
||||
"AnimatedNumber",
|
||||
"Digits roll by place; or the value counts up.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.gap_12()
|
||||
.child(specimen("roll", AnimatedNumber::new("roll", value), cx))
|
||||
.child(specimen(
|
||||
"count up",
|
||||
AnimatedNumber::new("count", value * 3.7)
|
||||
.decimals(1)
|
||||
.count_up(),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn typewriter(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let take = window.use_keyed_state("type-take", cx, |_, _| 0usize);
|
||||
let lines = [
|
||||
"Writing, one grapheme at a time.",
|
||||
"中文与 emoji ✨ 也按字形逐个出现。",
|
||||
];
|
||||
let line = lines[*take.read(cx) % lines.len()];
|
||||
section(
|
||||
"Typewriter",
|
||||
"Reveals by grapheme, so emoji and CJK stay whole.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
row()
|
||||
.gap_6()
|
||||
.child(div().min_w_80().child(Typewriter::new("typewriter", line)))
|
||||
.child(
|
||||
Button::new("type-again", "Next line")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.on_click(move |_, _, cx| {
|
||||
take.update(cx, |take, cx| {
|
||||
*take += 1;
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gradient(cx: &App) -> impl IntoElement + use<> {
|
||||
let colors = &cx.theme().colors;
|
||||
let (ink, focus) = (colors.fg, colors.focus);
|
||||
let display = cx.theme().text_size(TextSize::Display);
|
||||
section("GradientText", "A tonal ramp, glyph by glyph. No neon.", cx).child(
|
||||
specimens()
|
||||
.gap_10()
|
||||
.child(specimen(
|
||||
"ink to muted",
|
||||
div()
|
||||
.text_size(display)
|
||||
.child(GradientText::new("Quiet type")),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"ink to focus",
|
||||
div()
|
||||
.text_size(display)
|
||||
.child(GradientText::new("Quiet type").colors(ink, focus)),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn shimmer(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"ShimmerText",
|
||||
"A slow band of light while something loads.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(cx.theme().text_size(TextSize::Lg))
|
||||
.child(ShimmerText::new("shimmer", "Thinking through the request…")),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
use ely_gpui_component::{
|
||||
theme::{ActiveTheme, Radius},
|
||||
typography::{
|
||||
Blockquote, Caption, Code, EllipsisTooltip, ExternalLink, Heading, Highlight, Kbd,
|
||||
KbdCombo, Label, Link, MiddleEllipsis, Overline, Paragraph, Subtitle, Title,
|
||||
},
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, Styled, div, px};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
ui::{code, row, section, specimen, specimens},
|
||||
};
|
||||
|
||||
const PROSE: &str = "Good interfaces are quiet. They leave room around each idea, \
|
||||
use one voice for text, and save color for the moments that need it. The rest is spacing.";
|
||||
|
||||
pub fn headings(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"Heading (H1–H6)",
|
||||
"Six steps, one weight. Size carries the order.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(Heading::h1("Quiet interfaces"))
|
||||
.child(Heading::h2("Quiet interfaces"))
|
||||
.child(Heading::h3("Quiet interfaces"))
|
||||
.child(Heading::h4("Quiet interfaces"))
|
||||
.child(Heading::h5("Quiet interfaces"))
|
||||
.child(Heading::h6("Quiet interfaces")),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn title(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Title / Subtitle", "A pair for page and panel headers.", cx).child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(Title::new("Quarterly review"))
|
||||
.child(Subtitle::new("Numbers, notes and the next three moves.")),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn paragraph(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Paragraph", "Reading text at 14 with loose leading.", cx)
|
||||
.child(Paragraph::new(PROSE).max_w(px(560.0)))
|
||||
}
|
||||
|
||||
pub fn label(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Label", "Names a control or a value.", cx).child(
|
||||
row()
|
||||
.child(Label::new("Display name"))
|
||||
.child(Label::new("Workspace")),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn caption(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Caption", "The quiet line beneath.", cx)
|
||||
.child(Caption::new("Updated a moment ago · visible to your team"))
|
||||
}
|
||||
|
||||
pub fn overline(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Overline", "An eyebrow in small capitals.", cx).child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(Overline::new("Release notes"))
|
||||
.child(Title::new("Version 0.1")),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn inline_code(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Code", "Monospace on a quiet fill.", cx).child(
|
||||
row()
|
||||
.child("Run")
|
||||
.child(Code::new("cargo run --example gallery"))
|
||||
.child("to open this window."),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn kbd(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"Kbd",
|
||||
"gpui key syntax. secondary means ⌘ on macOS and Ctrl elsewhere.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen("secondary-s", Kbd::new("secondary-s"), cx))
|
||||
.child(specimen("shift-tab", Kbd::new("shift-tab"), cx))
|
||||
.child(specimen("escape", Kbd::new("escape"), cx))
|
||||
.child(specimen("enter", Kbd::new("enter"), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn kbd_combo(cx: &App) -> impl IntoElement + use<> {
|
||||
section("KbdCombo", "Sequences read left to right.", cx).child(
|
||||
specimens()
|
||||
.child(specimen(
|
||||
"secondary-shift-p",
|
||||
KbdCombo::new("secondary-shift-p"),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"secondary-k secondary-s",
|
||||
KbdCombo::new("secondary-k secondary-s"),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn blockquote(cx: &App) -> impl IntoElement + use<> {
|
||||
section("Blockquote", "A rule and a softer ink.", cx).child(div().max_w(px(520.0)).child(
|
||||
Blockquote::new().child(
|
||||
"Simplicity is not the absence of clutter. It is the right thing, in the right place.",
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn highlight(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"Highlight / Mark",
|
||||
"Every match of a query, case-insensitive.",
|
||||
cx,
|
||||
)
|
||||
.child(Highlight::matching(
|
||||
"Quiet interfaces age well. Loud ones AGE fast.",
|
||||
"age",
|
||||
))
|
||||
.child(code("Highlight::matching(text, \"age\")", cx))
|
||||
}
|
||||
|
||||
fn narrow(cx: &App) -> gpui::Div {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.w(px(240.0))
|
||||
.px_3()
|
||||
.py_2()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
.border_1()
|
||||
.border_color(theme.colors.border)
|
||||
}
|
||||
|
||||
pub fn truncate(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"Truncate",
|
||||
"gpui's .truncate(): one line, an ellipsis at the end.",
|
||||
cx,
|
||||
)
|
||||
.child(narrow(cx).child(div().truncate().child(PROSE)))
|
||||
}
|
||||
|
||||
pub fn line_clamp(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"LineClamp",
|
||||
"gpui's .line_clamp(n): n lines, then a hard cut.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
narrow(cx)
|
||||
.w(px(360.0))
|
||||
.child(div().line_clamp(2).child(PROSE)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn middle(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"MiddleEllipsis",
|
||||
"Cuts the middle so both ends stay legible.",
|
||||
cx,
|
||||
)
|
||||
.child(narrow(cx).w(px(280.0)).child(MiddleEllipsis::new(
|
||||
"~/Documents/GitHub/Ely-GPUI-Components/src/typography/fit.rs",
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn ellipsis_tooltip(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"EllipsisTooltip",
|
||||
"Hover shows the whole line, only when it was cut.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"fit-tooltip",
|
||||
narrow(cx).child(EllipsisTooltip::new(
|
||||
"fit-tooltip-text",
|
||||
"Design review · Tuesday, with the full product team and guests",
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn link(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"Link",
|
||||
"Blue marks where a click goes. Underline on hover.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
row()
|
||||
.child("Read the")
|
||||
.child(
|
||||
Link::new("guide", "style guide").on_click(|_, _, _| log::info!("gallery: link")),
|
||||
)
|
||||
.child("before you start."),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn external_link(cx: &App) -> impl IntoElement + use<> {
|
||||
section("ExternalLink", "Leaves the app. The arrow says so.", cx).child(ExternalLink::new(
|
||||
"gpui",
|
||||
"gpui.rs",
|
||||
"https://www.gpui.rs",
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
use ely_gpui_component::{
|
||||
theme::TextSize,
|
||||
typography::{
|
||||
CopyableText, CurrencyText, DateTimeText, DurationStyle, DurationText, Emoji, FileSizeText,
|
||||
Latex, NumberText, PercentText, PluralText, RelativeTime, SelectableText,
|
||||
},
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, Styled, div, px};
|
||||
use jiff::{SignedDuration, Timestamp};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
ui::{code, section, specimen, specimens},
|
||||
};
|
||||
|
||||
/// Fixed moment, so shots repeat: 24 Sep 2026, 09:41 UTC.
|
||||
fn moment() -> Timestamp {
|
||||
Timestamp::from_second(1_790_242_860).expect("valid timestamp")
|
||||
}
|
||||
|
||||
pub fn relative_time(cx: &App) -> impl IntoElement + use<> {
|
||||
let ago = |seconds: i64| {
|
||||
Timestamp::now()
|
||||
.checked_sub(SignedDuration::from_secs(seconds))
|
||||
.expect("in range")
|
||||
};
|
||||
section(
|
||||
"RelativeTime",
|
||||
"Refreshes every thirty seconds while on screen.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen("10 s", RelativeTime::new("rel-a", ago(10)), cx))
|
||||
.child(specimen("3 min", RelativeTime::new("rel-b", ago(180)), cx))
|
||||
.child(specimen("2 h", RelativeTime::new("rel-c", ago(7_200)), cx))
|
||||
.child(specimen(
|
||||
"3 d",
|
||||
RelativeTime::new("rel-d", ago(259_200)),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn date_time(cx: &App) -> impl IntoElement + use<> {
|
||||
section("DateTimeText", "strftime patterns in the system zone.", cx).child(
|
||||
specimens()
|
||||
.child(specimen("default", DateTimeText::new(moment()), cx))
|
||||
.child(specimen(
|
||||
"%Y-%m-%d",
|
||||
DateTimeText::new(moment()).pattern("%Y-%m-%d"),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"%A %H:%M",
|
||||
DateTimeText::new(moment()).pattern("%A %H:%M"),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn numbers(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"NumberText",
|
||||
"Grouped, fixed precision, tabular figures.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen(
|
||||
"2 decimals",
|
||||
NumberText::new(1_234_567.891).decimals(2),
|
||||
cx,
|
||||
))
|
||||
.child(specimen("integer", NumberText::new(42_000.0), cx))
|
||||
.child(specimen(
|
||||
"negative",
|
||||
NumberText::new(-1_234.5).decimals(1),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn currency(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"CurrencyText",
|
||||
"ISO 4217 codes. Yen and won have no minor units.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen("USD", CurrencyText::new(1_234.5, "USD"), cx))
|
||||
.child(specimen("EUR", CurrencyText::new(-42.0, "EUR"), cx))
|
||||
.child(specimen("JPY", CurrencyText::new(1_500.4, "JPY"), cx))
|
||||
.child(specimen("CHF", CurrencyText::new(9.99, "CHF"), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn percent(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"PercentText",
|
||||
"Signed values carry direction in word and color.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen("plain", PercentText::new(0.1234).decimals(2), cx))
|
||||
.child(specimen("gain", PercentText::new(0.052).signed(), cx))
|
||||
.child(specimen("loss", PercentText::new(-0.031).signed(), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn file_size(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"FileSizeText",
|
||||
"Decimal units by default, binary on request.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen("1536 B", FileSizeText::new(1_536), cx))
|
||||
.child(specimen("3.2 GB", FileSizeText::new(3_200_000_000), cx))
|
||||
.child(specimen("binary", FileSizeText::new(3 << 30).binary(), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn duration(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"DurationText",
|
||||
"A clock for media, a compact span for logs.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.child(specimen(
|
||||
"clock",
|
||||
DurationText::new(5_025, DurationStyle::Clock),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"clock",
|
||||
DurationText::new(1_425, DurationStyle::Clock),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"compact",
|
||||
DurationText::new(245, DurationStyle::Compact),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn plural(cx: &App) -> impl IntoElement + use<> {
|
||||
section("PluralText", "The noun follows the count.", cx).child(
|
||||
specimens()
|
||||
.child(specimen("1", PluralText::new(1, "file", "files"), cx))
|
||||
.child(specimen(
|
||||
"12345",
|
||||
PluralText::new(12_345, "file", "files"),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn copyable(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"CopyableText",
|
||||
"One click copies. A check confirms, then leaves.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"copy-token",
|
||||
CopyableText::new("token", "ely_live_4f9a2c81d7").mono(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn selectable(cx: &App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"SelectableText",
|
||||
"Drag to select. ⌘C copies, ⌘A takes it all.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"select-text",
|
||||
div().max_w(px(520.0)).child(SelectableText::new(
|
||||
"selectable",
|
||||
"Selection works on read-only text too: logs, messages, receipts.",
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn emoji(cx: &App) -> impl IntoElement + use<> {
|
||||
let glyph = |code: &str| Emoji::shortcode(code).expect("known shortcode");
|
||||
section("Emoji", "Shortcodes resolve to the system color font.", cx).child(
|
||||
specimens()
|
||||
.child(specimen("sparkles", glyph("sparkles"), cx))
|
||||
.child(specimen("herb", glyph("herb"), cx))
|
||||
.child(specimen("coffee", glyph("coffee"), cx))
|
||||
.child(specimen("xl", glyph("sparkles").size(TextSize::Xxl), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn latex(cx: &App) -> impl IntoElement + use<> {
|
||||
let math = |source: &str| Latex::parse(source).expect("gallery formula parses");
|
||||
section(
|
||||
"Latex / MathInline",
|
||||
"A TeX subset: scripts, fractions, roots, Greek.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
specimens()
|
||||
.gap_10()
|
||||
.child(specimen("euler", math(r"e^{i\pi} + 1 = 0"), cx))
|
||||
.child(specimen(
|
||||
"sum",
|
||||
math(r"\sum_{k=1}^{n} k = \frac{n(n+1)}{2}"),
|
||||
cx,
|
||||
))
|
||||
.child(specimen(
|
||||
"root",
|
||||
math(r"x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}"),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
.child(code("Latex::parse(r\"e^{i\\pi} + 1 = 0\")?", cx))
|
||||
}
|
||||
Reference in new issue
Block a user