fix(carousel/range-calendar/time-range-field): apply canonical i18n + vertical layout patterns

## Vertical carousel layout

Recipe was making the indicator group stretch full width (380px) because
the root stayed in `flex-direction: column` and the indicator-group
inherited `align-items: stretch`. Reshaped:

- `[data-carousel][data-orientation='vertical']` now flips the root to
  `flex-direction: row` so viewport and indicator-group sit side-by-side.
- Viewport takes `flex: 1 1 auto` of the inline axis with explicit
  100% block-size.
- Indicator-group becomes `flex: 0 0 auto; inline-size: max-content;
  align-self: center` — narrow column of dots on the right edge.

Active vertical-stretched indicator already had `block-size: 2.25×`
treatment from the prior commit, so it now appears as a vertical pill
inside the dot column.

## i18n controls: follow canonical pattern

Mirrored the date-range-picker / calendar demos verbatim — those are
the canon and the new demos drifted from them. Three corrections:

1. **Locale**: `<select>` with `en-US / es-ES / fr-FR` (the docs
   canon — chip rows and made-up locale lists like ar/he/ja were both
   inventions). Carousel exposes locale too; it's forwarded to soma
   for downstream lang-aware behavior (announcer text, future label
   formatting hooks).
2. **Direction**: chip row with `ltr / rtl` (no `auto`). Label is
   "direction" not "dir".
3. **State shape**: plain `let dir = $state<Direction>('ltr')`, not
   the synthetic `dirCtl + derived` pattern I had invented.

Applied across all three demos so they read identically to the canon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
dev 5 months ago
parent 23ad9351ea
commit a6baae0cf1

@ -55,13 +55,28 @@
* level needs `100%` (or the token at the root) so the basis-100% along
* the column main axis resolves to viewport block-size, not content.
*/
/*
* Vertical layout flips the root flex axis to row, putting the viewport
* on the left (taking the available inline space) and the indicator
* group on the right as a narrow column of dots. This matches the
* common pattern in Mantine / ark-ui vertical carousels and stops the
* indicator-group from claiming the full inline width as a stretch
* child of a column flex container.
*
* The viewport / item-group / item each take 100% along the block axis
* so slide basis resolves to the bounded block-size.
*/
[data-carousel][data-orientation='vertical'] {
flex-direction: row;
align-items: stretch;
block-size: var(--_carousel-vertical-block-size);
}
[data-carousel][data-orientation='vertical'] [data-carousel-viewport] {
flex: 1 1 auto;
block-size: 100%;
min-block-size: 0;
min-inline-size: 0;
}
[data-carousel][data-orientation='vertical'] [data-carousel-item-group] {
@ -75,6 +90,13 @@
block-size: 100%;
}
/* Indicator group becomes a narrow auto-sized column on the right. */
[data-carousel][data-orientation='vertical'] [data-carousel-indicator-group] {
flex: 0 0 auto;
inline-size: max-content;
align-self: center;
}
[data-carousel][data-block] {
inline-size: 100%;
}

@ -43,16 +43,14 @@
let disabled = $state(false);
// i18n — `dir` controls the keyboard arrow semantics (RTL swaps
// next/prev) AND flips the translation sign so horizontal advance
// follows reading direction. `locale` is forwarded for label
// formatting hooks (consumers using lang-aware aria-labels).
type DirCtl = 'auto' | 'ltr' | 'rtl';
let dirCtl = $state<DirCtl>('auto');
// `dir` controls keyboard arrow semantics (RTL swaps next/prev) and
// flips the translation sign so horizontal advance follows reading
// direction. `locale` is forwarded for downstream lang-aware behavior
// (announcer text, future label formatting).
type Direction = 'ltr' | 'rtl';
let dir = $state<Direction>('ltr');
let locale = $state('en-US');
const dir = $derived(dirCtl === 'auto' ? undefined : dirCtl);
let size = $state<CarouselSize>('md');
let variant = $state<CarouselVariant>('surface');
let color = $state<CarouselColor>('primary');
@ -137,7 +135,6 @@
autoplay && stopOnLastSnap && ' stopOnLastSnap',
disabled && ' disabled',
dir && ` dir="${dir}"`,
locale !== 'en-US' && ` locale="${locale}"`,
'>',
' <Carousel.Viewport>',
' <Carousel.ItemGroup>',
@ -460,24 +457,21 @@
<div data-uix-subsection-head>
<span data-uix-layer-badge="soma">soma</span> i18n
<span style="margin-inline-start: auto; color: var(--color-content-secondary); font-size: var(--font-size-xs);">
<code>dir</code> flips arrow-key + drag semantics for horizontal RTL; <code>locale</code> is forwarded for downstream label formatting.
</span>
</div>
<div data-uix-controls>
<label data-uix-control>
<span data-uix-control-label>dir <span data-uix-control-hint>{dir ?? 'inherits app pref'}</span></span>
<span data-uix-chips role="radiogroup">
{#each ['auto', 'ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dirCtl === d} onclick={() => (dirCtl = d as DirCtl)}>{d}</button>
{/each}
</span>
<span data-uix-control-label>locale</span>
<select bind:value={locale}>
<option value="en-US">en-US</option>
<option value="es-ES">es-ES</option>
<option value="fr-FR">fr-FR</option>
</select>
</label>
<label data-uix-control>
<span data-uix-control-label>locale</span>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['en-US', 'es-ES', 'ar-EG', 'he-IL', 'ja-JP'] as l (l)}
<button data-uix-chip data-active={locale === l} onclick={() => (locale = l)}>{l}</button>
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as Direction)}>{value}</button>
{/each}
</span>
</label>

@ -55,12 +55,8 @@
let readonly = $state(false);
let weekStartsOn = $state<WeekStartControl>('1');
let locale = $state('en-US');
// `dir` reflows the calendar header + weekday strip + day grid. `auto`
// inherits the app preference set in the topbar; explicit `ltr`/`rtl`
// overrides it per-instance.
type DirCtl = 'auto' | 'ltr' | 'rtl';
let dirCtl = $state<DirCtl>('auto');
const dir = $derived(dirCtl === 'auto' ? undefined : dirCtl);
type Direction = 'ltr' | 'rtl';
let dir = $state<Direction>('ltr');
let size = $state<RangeCalendarSize>('md');
let variant = $state<RangeCalendarVariant>('surface');
let color = $state<RangeCalendarColor>('primary');
@ -464,18 +460,18 @@
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>dir <span data-uix-control-hint>{dir ?? 'inherits app pref'}</span></span>
<span data-uix-chips role="radiogroup">
{#each ['auto', 'ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dirCtl === d} onclick={() => (dirCtl = d as DirCtl)}>{d}</button>
{/each}
</span>
<span data-uix-control-label>locale</span>
<select bind:value={locale}>
<option value="en-US">en-US</option>
<option value="es-ES">es-ES</option>
<option value="fr-FR">fr-FR</option>
</select>
</label>
<label data-uix-control>
<span data-uix-control-label>locale</span>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['en-US', 'es-ES', 'ar-EG', 'he-IL', 'ja-JP'] as l (l)}
<button data-uix-chip data-active={locale === l} onclick={() => (locale = l)}>{l}</button>
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as Direction)}>{value}</button>
{/each}
</span>
</label>

@ -264,17 +264,17 @@
</label>
<label data-uix-control>
<span data-uix-control-label>locale</span>
<span data-uix-chips role="radiogroup">
{#each ['en-US', 'en-GB', 'es-ES', 'de-DE', 'fr-FR', 'ja-JP'] as l (l)}
<button data-uix-chip data-active={locale === l} onclick={() => (locale = l)}>{l}</button>
{/each}
</span>
<select bind:value={locale}>
<option value="en-US">en-US</option>
<option value="es-ES">es-ES</option>
<option value="fr-FR">fr-FR</option>
</select>
</label>
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}>{d}</button>
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as Direction)}>{value}</button>
{/each}
</span>
</label>

Loading…
Cancel
Save

Powered by TurnKey Linux.