directionOf function

TextDirection? directionOf(
  1. String text
)

Wraps every grouped-digit run in text with LRI…PDI so it keeps its own left-to-right order when laid out inside an RTL paragraph, instead of the bidi algorithm reordering the groups themselves — the +20 2 2411 86108610 2411 2 20+ defect this exists to fix.

A no-op when direction is not RTL (returns text itself, unchanged) — the reversal only happens inside an RTL paragraph, so there is nothing to isolate against under LTR. Also a no-op on text this has already processed, guarded by text containing an LRI at all.

ponytail: that guard is whole-string, not per-match — a value that concatenated an already-isolated fragment with a fresh, un-isolated run would have the fresh run skipped too. A per-match version (skip a match immediately preceded by LRI) was tried and reverted: \+? being optional lets the regex re-anchor one character later — right after a previously-wrapped run's own leading + — where the lookbehind no longer sees an LRI, producing a genuine double-wrap (a second LRI/PDI pair nested just inside the first one, around everything after the +), a worse bug than the one it was meant to fix. The correct narrow fix (walk the string, skip existing LRI…PDI spans verbatim, isolate only the text between them) is real extra code for a shape that doesn't occur at any of this package's five call sites (entry_widgets, semantic_badge, card_fields, rich_entry_tile, dashboard_screen) — each isolates one field's fresh raw string exactly once, never a concatenation of previously-isolated and fresh text. Revisit if a caller ever does that concatenation. The direction a run of text would resolve to on its own, or null when it has no strong character at all — digits, punctuation and spaces are all bidi-neutral or weak, so a phone number or a price has none.

A composed paragraph uses this instead of isolateBidi: a paragraph built from several mark leaves cannot isolate each leaf without changing how the whole thing lays out, but it can be told its own direction, which is what the bidi algorithm needed all along. rich_entry_tile passes the result to Text.rich.

This is the standard "first-strong" rule the Unicode algorithm uses to give a paragraph its own base direction, hand-rolled because this package takes no intl dependency. The RTL ranges are the scripts a Filament panel actually ships translations for — Hebrew, Arabic and its supplement and presentation forms, Syriac, Thaana, N'Ko — and anything else with a case mapping is treated as strong LTR.

Implementation

/// The direction a run of text would resolve to on its own, or null when it
/// has no strong character at all — digits, punctuation and spaces are all
/// bidi-neutral or weak, so a phone number or a price has none.
///
/// A composed paragraph uses this instead of [isolateBidi]: a paragraph built
/// from several mark leaves cannot isolate each leaf without changing how the
/// whole thing lays out, but it *can* be told its own direction, which is what
/// the bidi algorithm needed all along. `rich_entry_tile` passes the result
/// to `Text.rich`.
///
/// This is the standard "first-strong" rule the Unicode algorithm uses to
/// give a paragraph its own base direction, hand-rolled because this package
/// takes no `intl` dependency. The RTL ranges are the scripts a Filament
/// panel actually ships translations for — Hebrew, Arabic and its supplement
/// and presentation forms, Syriac, Thaana, N'Ko — and anything else with a
/// case mapping is treated as strong LTR.
TextDirection? directionOf(String text) {
  final ltr = _firstStrongIsLtr(text);
  if (ltr == null) return null;
  return ltr ? TextDirection.ltr : TextDirection.rtl;
}