diff --git a/src/uix/PENDIENTES.md b/src/uix/PENDIENTES.md index b6f3241c2..cb320948e 100644 --- a/src/uix/PENDIENTES.md +++ b/src/uix/PENDIENTES.md @@ -168,3 +168,35 @@ Footer, ToolbarItem, etc.). Demos: las parts se envuelven en `{#if showX}` con state local del demo para dar el control UI, pero las parts en sí no leen ese state. + +### N-8 · Reutilizar componentes ya definidos en componentes complejos + +Cuando un componente complejo (picker, combobox, color-picker, etc.) +necesita un sub-control que ya existe como componente independiente +(Slider, Popover, Calendar, Field, etc.), **debe componerlo en vez de +reimplementarlo**. Sólo se justifica un fork si reusarlo causaría una +pérdida de características concreta y demostrable. + +Beneficios: + +1. **Una sola fuente de eventos sema**. El sub-control emite sus + eventos canónicos (`slider:handle-pick`, `slider:commit-set`, + `popover:close-dismiss`, etc.) que el composite hereda + automáticamente — sin reimplementar la dimensión perceptiva. +2. **Una sola implementación de pointer/keyboard/ARIA**. Bugs + corregidos en el componente raíz se propagan a todos los composites + que lo usan. Sin duplicación de drag, focus trap, etc. +3. **Tokens del recipe unificados**. `--slider-track-size`, + `--popover-content-padding`, etc. se setean en un único recipe; + todos los composites consumen los mismos. +4. **Superficie pública estable**. La API del composite (parts + + eventos) se reduce a su lógica diferenciadora; el resto cae del + sub-control. + +Caso aplicado 2026-05-21: ColorPicker.ChannelSlider deja de tener su +propio `ColorPickerChannelSliderProvider` con pointer handlers +duplicados; ahora compone `SliderProvider` por debajo. Channel-aware +behavior queda en una sola función bridge (`setChannel` + gradient +inline en `--cp-channel-gradient`). Resultado: −200 líneas en soma, +eventos `slider:handle-pick/drag/commit-set` propagados, tokens +unificados con time-picker / standalone Slider. diff --git a/src/uix/eidos/components/color-picker/color-picker-channel-slider-thumb.svelte b/src/uix/eidos/components/color-picker/color-picker-channel-slider-thumb.svelte deleted file mode 100644 index f824ca345..000000000 --- a/src/uix/eidos/components/color-picker/color-picker-channel-slider-thumb.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -{@render children?.()} diff --git a/src/uix/eidos/components/color-picker/color-picker-channel-slider-track.svelte b/src/uix/eidos/components/color-picker/color-picker-channel-slider-track.svelte deleted file mode 100644 index 54480690a..000000000 --- a/src/uix/eidos/components/color-picker/color-picker-channel-slider-track.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -{@render children?.()} diff --git a/src/uix/eidos/components/color-picker/color-picker-channel-slider.svelte b/src/uix/eidos/components/color-picker/color-picker-channel-slider.svelte index b6eff2aab..5a0eb4cb9 100644 --- a/src/uix/eidos/components/color-picker/color-picker-channel-slider.svelte +++ b/src/uix/eidos/components/color-picker/color-picker-channel-slider.svelte @@ -1,5 +1,6 @@ - -{#if child} - {@render child({ props: mergedProps })} -{:else} -
{@render children?.()}
-{/if} diff --git a/src/uix/soma/components/color-picker/components/color-picker-channel-slider-track.svelte b/src/uix/soma/components/color-picker/components/color-picker-channel-slider-track.svelte deleted file mode 100644 index 5f1c33d15..000000000 --- a/src/uix/soma/components/color-picker/components/color-picker-channel-slider-track.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - -{#if child} - {@render child({ props: mergedProps })} -{:else} -
{@render children?.()}
-{/if} diff --git a/src/uix/soma/components/color-picker/components/color-picker-channel-slider.svelte b/src/uix/soma/components/color-picker/components/color-picker-channel-slider.svelte index 1abe53f64..77cf67b37 100644 --- a/src/uix/soma/components/color-picker/components/color-picker-channel-slider.svelte +++ b/src/uix/soma/components/color-picker/components/color-picker-channel-slider.svelte @@ -1,11 +1,14 @@ {#if child} - {@render child({ props: mergedProps, ...snippetProps })} + {@render child({ props: mergedProps, channel })} {:else}
- {@render children?.(snippetProps)} + {@render children?.({ channel })}
{/if} diff --git a/src/uix/soma/components/color-picker/exports.ts b/src/uix/soma/components/color-picker/exports.ts index 20b949fa4..46d45a503 100644 --- a/src/uix/soma/components/color-picker/exports.ts +++ b/src/uix/soma/components/color-picker/exports.ts @@ -16,10 +16,17 @@ export { default as AreaBackground } from './components/color-picker-area-backgr export { default as AreaThumb } from './components/color-picker-area-thumb.svelte'; export { default as TransparencyGrid } from './components/color-picker-transparency-grid.svelte'; -// Stage 3 — Channel sliders (1D slider per channel: hue / saturation / alpha / red / …) +// Stage 3 — Channel sliders. +// The ChannelSlider composes the generic SliderProvider underneath (see +// `components/color-picker-channel-slider.svelte`). The Track / Thumb roles +// are filled by the slider's `Range` / `Thumb` parts directly — re-exported +// here so the public API stays `ColorPicker.ChannelSliderTrack` / +// `ColorPicker.ChannelSliderThumb` for consumers that compose them inline. export { default as ChannelSlider } from './components/color-picker-channel-slider.svelte'; -export { default as ChannelSliderTrack } from './components/color-picker-channel-slider-track.svelte'; -export { default as ChannelSliderThumb } from './components/color-picker-channel-slider-thumb.svelte'; +export { + Range as ChannelSliderTrack, + Thumb as ChannelSliderThumb +} from '../slider/exports'; // Stage 5 — Swatches (radiogroup of preset colors) export { default as SwatchGroup } from './components/color-picker-swatch-group.svelte'; @@ -57,8 +64,6 @@ export type { ColorPickerAreaSnippetProps as AreaSnippetProps, ColorPickerTransparencyGridProps as TransparencyGridProps, ColorPickerChannelSliderProps as ChannelSliderProps, - ColorPickerChannelSliderTrackProps as ChannelSliderTrackProps, - ColorPickerChannelSliderThumbProps as ChannelSliderThumbProps, ColorPickerChannelSliderSnippetProps as ChannelSliderSnippetProps, ColorPickerSwatchGroupProps as SwatchGroupProps, ColorPickerSwatchTriggerProps as SwatchTriggerProps, @@ -74,3 +79,9 @@ export type { ColorPickerFormatSelectProps as FormatSelectProps, ColorChannel } from './types'; + +// Re-export slider props for the inner thumb/track tags. +export type { + SliderRangeProps as ChannelSliderTrackProps, + SliderThumbProps as ChannelSliderThumbProps +} from '../slider/types'; diff --git a/src/uix/soma/components/color-picker/types.ts b/src/uix/soma/components/color-picker/types.ts index 07efd40fc..cccf5630f 100644 --- a/src/uix/soma/components/color-picker/types.ts +++ b/src/uix/soma/components/color-picker/types.ts @@ -37,14 +37,17 @@ export type ColorPickerAreaSnippetProps = { thumbStyle: { left: string; top: string }; }; -/** Snippet props exposed by `ColorPicker.ChannelSlider`. */ +/** + * Snippet props exposed by `ColorPicker.ChannelSlider`. + * + * As of 2026-05-21 the channel slider composes the generic `SliderProvider` + * internally; the gradient + thumb position are wired automatically through + * the slider's own DOM ([data-slider]::before for the track, the gradient + * lives on the wrapper as `--cp-channel-gradient`). Consumers only need + * the `channel` discriminator to label / branch on. + */ export type ColorPickerChannelSliderSnippetProps = { - /** Current normalized value (0..1) along the slider. */ - progress: number; - /** CSS thumb position (left for horizontal, top for vertical). */ - thumbStyle: { left: string } | { top: string }; - /** Gradient string representing the channel's full range under the current color. */ - backgroundStyle: string; + channel: ColorChannel; }; // ── Root provider ────────────────────────────────────────────────────────── @@ -203,7 +206,15 @@ export type ColorPickerAreaThumbProps = WithChild<{ id?: string }> & // ── ChannelSlider ────────────────────────────────────────────────────────── -/** Props for `ColorPicker.ChannelSlider` — 1D slider bound to a single channel. */ +/** + * Props for `ColorPicker.ChannelSlider` — 1D slider bound to a single channel. + * + * Internally composes the generic `SliderProvider`. Track / Thumb are + * re-exported from `Slider` as `ChannelSliderTrack` / `ChannelSliderThumb` + * — they carry the canonical `[data-slider-range]` / `[data-slider-thumb]` + * markers and emit the slider's sema events (`handle-pick`, `handle-drag`, + * `commit-set`). + */ export type ColorPickerChannelSliderProps = WithChild< { id?: string; @@ -212,16 +223,10 @@ export type ColorPickerChannelSliderProps = WithChild< /** Orientation. @default 'horizontal' */ orientation?: Orientation; }, - ColorPickerChannelSliderSnippetProps + { channel: ColorChannel } > & Without; -export type ColorPickerChannelSliderTrackProps = WithChild<{ id?: string }> & - Without; - -export type ColorPickerChannelSliderThumbProps = WithChild<{ id?: string }> & - Without; - // ── TransparencyGrid ─────────────────────────────────────────────────────── /** Props for `ColorPicker.TransparencyGrid` — decorative checkerboard under alpha. */