Reported: the popover sliders showed a track with no visible thumb,
and there was no indication of the value scale.
- Each TimePicker.{Hour,Minute,Second}Slider eidos wrapper now renders
<Slider.Range /> + <Slider.Thumb /> by default when no children are
passed. Consumers can still override with custom children.
- Adds a labelled ruler row next to each slider:
- HourSlider: scale ABOVE the track. 24h shows 0/3/6/9/12/15/18/21,
12h shows 1/3/6/9/12. Numbers zero-padded for 24h.
- MinuteSlider / SecondSlider: scale BELOW the track at 0/15/30/45.
- Tick positions are computed proportionally and offset inward by
half the thumb width so the endpoint labels align with the actual
track endpoints (not the bounding box).
- Reinforces thumb visibility inside the clock popover: ring shadow
against the popover surface + accented border tied to the picker's
intent color. Range fill matches the accent border so the filled
portion of the track is unambiguous.
All checks clean: 67/67 audit PASS, svelte-check 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
parent
391de6e04c
commit
e7b2e461bc
@ -1,10 +1,43 @@
|
||||
<script lang="ts">
|
||||
import * as TimePicker from '$soma/components/time-picker';
|
||||
import { TimePickerProvider } from '$soma/components/time-picker';
|
||||
import * as Slider from '$soma/components/slider';
|
||||
import type { TimePickerSliderProps } from './types';
|
||||
|
||||
let { children, ...rest }: TimePickerSliderProps = $props();
|
||||
|
||||
const provider = TimePickerProvider.require();
|
||||
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
|
||||
const tickValues = $derived.by(() => {
|
||||
if (cycle === 12) return [1, 3, 6, 9, 12];
|
||||
return [0, 3, 6, 9, 12, 15, 18, 21];
|
||||
});
|
||||
const min = $derived(provider.hourMin);
|
||||
const max = $derived(provider.hourMax);
|
||||
|
||||
function percent(v: number): string {
|
||||
const t = (v - min) / (max - min || 1);
|
||||
return `${Math.max(0, Math.min(1, t)) * 100}%`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<TimePicker.HourSlider {...rest}>
|
||||
{@render children?.()}
|
||||
</TimePicker.HourSlider>
|
||||
<div class="time-picker-slider time-picker-hour-slider">
|
||||
<div class="time-picker-scale" aria-hidden="true">
|
||||
{#each tickValues as v}
|
||||
<span class="time-picker-scale-tick" style:left={percent(v)}>
|
||||
<span class="time-picker-scale-mark"></span>
|
||||
<span class="time-picker-scale-label">{cycle === 12 ? v : String(v).padStart(2, '0')}</span>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
<TimePicker.HourSlider {...rest}>
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
{:else}
|
||||
<Slider.Range />
|
||||
<Slider.Thumb />
|
||||
{/if}
|
||||
</TimePicker.HourSlider>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,36 @@
|
||||
<script lang="ts">
|
||||
import * as TimePicker from '$soma/components/time-picker';
|
||||
import * as Slider from '$soma/components/slider';
|
||||
import type { TimePickerSliderProps } from './types';
|
||||
|
||||
let { children, ...rest }: TimePickerSliderProps = $props();
|
||||
|
||||
// Tick every 15 minutes by default: 0, 15, 30, 45 (range 0..59).
|
||||
const tickValues = [0, 15, 30, 45];
|
||||
const min = 0;
|
||||
const max = 59;
|
||||
|
||||
function percent(v: number): string {
|
||||
const t = (v - min) / (max - min);
|
||||
return `${Math.max(0, Math.min(1, t)) * 100}%`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<TimePicker.MinuteSlider {...rest}>
|
||||
{@render children?.()}
|
||||
</TimePicker.MinuteSlider>
|
||||
<div class="time-picker-slider time-picker-minute-slider">
|
||||
<TimePicker.MinuteSlider {...rest}>
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
{:else}
|
||||
<Slider.Range />
|
||||
<Slider.Thumb />
|
||||
{/if}
|
||||
</TimePicker.MinuteSlider>
|
||||
<div class="time-picker-scale time-picker-scale-below" aria-hidden="true">
|
||||
{#each tickValues as v}
|
||||
<span class="time-picker-scale-tick" style:left={percent(v)}>
|
||||
<span class="time-picker-scale-mark"></span>
|
||||
<span class="time-picker-scale-label">{String(v).padStart(2, '0')}</span>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,35 @@
|
||||
<script lang="ts">
|
||||
import * as TimePicker from '$soma/components/time-picker';
|
||||
import * as Slider from '$soma/components/slider';
|
||||
import type { TimePickerSliderProps } from './types';
|
||||
|
||||
let { children, ...rest }: TimePickerSliderProps = $props();
|
||||
|
||||
const tickValues = [0, 15, 30, 45];
|
||||
const min = 0;
|
||||
const max = 59;
|
||||
|
||||
function percent(v: number): string {
|
||||
const t = (v - min) / (max - min);
|
||||
return `${Math.max(0, Math.min(1, t)) * 100}%`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<TimePicker.SecondSlider {...rest}>
|
||||
{@render children?.()}
|
||||
</TimePicker.SecondSlider>
|
||||
<div class="time-picker-slider time-picker-second-slider">
|
||||
<TimePicker.SecondSlider {...rest}>
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
{:else}
|
||||
<Slider.Range />
|
||||
<Slider.Thumb />
|
||||
{/if}
|
||||
</TimePicker.SecondSlider>
|
||||
<div class="time-picker-scale time-picker-scale-below" aria-hidden="true">
|
||||
{#each tickValues as v}
|
||||
<span class="time-picker-scale-tick" style:left={percent(v)}>
|
||||
<span class="time-picker-scale-mark"></span>
|
||||
<span class="time-picker-scale-label">{String(v).padStart(2, '0')}</span>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in new issue