diff --git a/src/uix/eidos/components/time-picker/context.svelte.ts b/src/uix/eidos/components/time-picker/context.svelte.ts index 85f37d270..0ae22fcbd 100644 --- a/src/uix/eidos/components/time-picker/context.svelte.ts +++ b/src/uix/eidos/components/time-picker/context.svelte.ts @@ -7,6 +7,13 @@ export interface TimePickerVisualContext { getSize(): TimePickerSize; getVariant(): TimePickerVariant; getColor(): TimePickerColor; + /** + * Whether to render the labelled ruler scale next to each slider. + * `true` by default; pass `ruler={false}` on `` to hide + * the rulers entirely, or pass `ruler={false}` on an individual + * slider eidos wrapper to opt that one out. + */ + getRuler(): boolean; } export function setTimePickerVisualContext(ctx: TimePickerVisualContext): TimePickerVisualContext { diff --git a/src/uix/eidos/components/time-picker/time-picker-hour-slider.svelte b/src/uix/eidos/components/time-picker/time-picker-hour-slider.svelte index d60c2024f..577c74576 100644 --- a/src/uix/eidos/components/time-picker/time-picker-hour-slider.svelte +++ b/src/uix/eidos/components/time-picker/time-picker-hour-slider.svelte @@ -2,20 +2,32 @@ import * as TimePicker from '$soma/components/time-picker'; import { TimePickerProvider } from '$soma/components/time-picker'; import * as Slider from '$soma/components/slider'; + import { getTimePickerVisualContext } from './context.svelte'; import type { TimePickerSliderProps } from './types'; - let { children, ...rest }: TimePickerSliderProps = $props(); + type Props = TimePickerSliderProps & { + /** Show the labelled ruler above the track. Falls back to the + * picker-level `ruler` flag (default `true`). */ + ruler?: boolean; + }; + + let { children, ruler, ...rest }: Props = $props(); const provider = TimePickerProvider.require(); + const visual = getTimePickerVisualContext(); const cycle = $derived(provider.resolvedHourCycle); - // Cap-inclusive range. 24h → 0..23 (step 3 ticks): 0,3,6,9,12,15,18,21,23 - // 12h → 1..12 (step 1, every 3): 1,3,6,9,12 + + // Endpoint-inclusive scale. 24h: every 3h plus the upper cap (23) so the + // scale shows the full available range, not just up to 21. 12h: 1, 3, 6, + // 9, 12 — already endpoint-inclusive. const tickValues = $derived.by(() => { if (cycle === 12) return [1, 3, 6, 9, 12]; - return [0, 3, 6, 9, 12, 15, 18, 21]; + return [0, 3, 6, 9, 12, 15, 18, 21, 23]; }); + const min = $derived(provider.hourMin); const max = $derived(provider.hourMax); + const showRuler = $derived(ruler ?? visual?.getRuler() ?? true); function percent(v: number): string { const t = (v - min) / (max - min || 1); @@ -23,15 +35,19 @@ } -
- +
+ {#if showRuler} + + {/if} {#if children} {@render children?.()} diff --git a/src/uix/eidos/components/time-picker/time-picker-minute-slider.svelte b/src/uix/eidos/components/time-picker/time-picker-minute-slider.svelte index a91a0e39f..f8d8e90fb 100644 --- a/src/uix/eidos/components/time-picker/time-picker-minute-slider.svelte +++ b/src/uix/eidos/components/time-picker/time-picker-minute-slider.svelte @@ -1,22 +1,33 @@ -
+
{#if children} {@render children?.()} @@ -25,12 +36,14 @@ {/if} - + {#if showRuler} + + {/if}
diff --git a/src/uix/eidos/components/time-picker/time-picker-second-slider.svelte b/src/uix/eidos/components/time-picker/time-picker-second-slider.svelte index cd4bf7e6e..e1c6ec798 100644 --- a/src/uix/eidos/components/time-picker/time-picker-second-slider.svelte +++ b/src/uix/eidos/components/time-picker/time-picker-second-slider.svelte @@ -1,21 +1,31 @@ -
+
{#if children} {@render children?.()} @@ -24,12 +34,14 @@ {/if} - + {#if showRuler} + + {/if}
diff --git a/src/uix/eidos/components/time-picker/time-picker.svelte b/src/uix/eidos/components/time-picker/time-picker.svelte index 864c8d9fb..fc1ff7ffa 100644 --- a/src/uix/eidos/components/time-picker/time-picker.svelte +++ b/src/uix/eidos/components/time-picker/time-picker.svelte @@ -8,6 +8,7 @@ size = 'md', variant = 'surface', color = 'primary', + ruler = true, value = $bindable(), placeholder = $bindable(), open = $bindable(false), @@ -21,7 +22,8 @@ setTimePickerVisualContext({ getSize: () => resolvedSize, getVariant: () => variant, - getColor: () => color + getColor: () => color, + getRuler: () => ruler }); diff --git a/src/uix/eidos/components/time-picker/types.ts b/src/uix/eidos/components/time-picker/types.ts index 9b9b7e944..a6cb97660 100644 --- a/src/uix/eidos/components/time-picker/types.ts +++ b/src/uix/eidos/components/time-picker/types.ts @@ -33,6 +33,11 @@ export type TimePickerProps = ProviderProps & { variant?: TimePickerVariant /** Intent palette used for focus and selected affordances. @default 'primary' */ color?: TimePickerColor + /** + * Show the labelled ruler scale next to each slider in the clock + * popover. @default true + */ + ruler?: boolean } export type TimePickerTriggerProps = SomaTimePickerTriggerProps & { diff --git a/src/uix/morfo/components/time-picker.ts b/src/uix/morfo/components/time-picker.ts index e65a476b3..60d424c46 100644 --- a/src/uix/morfo/components/time-picker.ts +++ b/src/uix/morfo/components/time-picker.ts @@ -230,6 +230,58 @@ export const timePickerMorfo = { { key: 'Shift+Tab', action: 'focus-prev' } ] }, + { + // Hour / Minute / Second sliders — composite parts. The outer + // wrapper element carries `data-time-picker-{X}-slider`; the + // inner slider DOM is declared by the slider morfo (data-slider, + // data-slider-thumb, data-slider-range). + name: 'HourSlider', + kebab: 'hour-slider', + archetype: 'group', + kind: 'public', + defaultElement: 'div', + optional: true, + data: [{ attr: 'data-disabled', severity: 'optional' }], + aria: [ + { + attr: 'aria-label', + value: v.translationRef('#?components.time-picker.hour|Hour'), + severity: 'optional' + } + ] + }, + { + name: 'MinuteSlider', + kebab: 'minute-slider', + archetype: 'group', + kind: 'public', + defaultElement: 'div', + optional: true, + data: [{ attr: 'data-disabled', severity: 'optional' }], + aria: [ + { + attr: 'aria-label', + value: v.translationRef('#?components.time-picker.minute|Minute'), + severity: 'optional' + } + ] + }, + { + name: 'SecondSlider', + kebab: 'second-slider', + archetype: 'group', + kind: 'public', + defaultElement: 'div', + optional: true, + data: [{ attr: 'data-disabled', severity: 'optional' }], + aria: [ + { + attr: 'aria-label', + value: v.translationRef('#?components.time-picker.second|Second'), + severity: 'optional' + } + ] + }, { name: 'DayPeriodToggle', kebab: 'day-period-toggle', diff --git a/web/routes/uix/components/time-picker/+page.svelte b/web/routes/uix/components/time-picker/+page.svelte index b0ef0fa20..70063487d 100644 --- a/web/routes/uix/components/time-picker/+page.svelte +++ b/web/routes/uix/components/time-picker/+page.svelte @@ -38,6 +38,7 @@ let disabled = $state(false); let readonly = $state(false); let lockSeconds = $state(false); + let ruler = $state(true); let granularity = $state('minute'); let hourCycle = $state(24); let bounds = $state(false); @@ -239,6 +240,7 @@ {granularity} {hourCycle} {locale} + {ruler} {size} {variant} {color} @@ -421,6 +423,13 @@ seconds readonly +
Footer composition