diff --git a/src/uix/eidos/components/date-picker/date-picker-month-view.svelte b/src/uix/eidos/components/date-picker/date-picker-month-view.svelte index 94f3d3400..2f4949616 100644 --- a/src/uix/eidos/components/date-picker/date-picker-month-view.svelte +++ b/src/uix/eidos/components/date-picker/date-picker-month-view.svelte @@ -6,11 +6,13 @@ * `size` from the visual context and forwards it. */ import * as DatePicker from '$soma/components/date-picker'; + import type { PassthroughProps } from '$soma/types'; import type { MonthGridSize } from '../month-grid'; import { getDatePickerVisualContext } from './context.svelte'; - import type { HTMLAttributes } from 'svelte/elements'; - type Props = HTMLAttributes; + // See `date-picker-year-view.svelte` for the rationale on + // `PassthroughProps` — same pattern, same boundary. + type Props = PassthroughProps; const props: Props = $props(); diff --git a/src/uix/eidos/components/date-picker/date-picker-year-view.svelte b/src/uix/eidos/components/date-picker/date-picker-year-view.svelte index 08a3b1fe6..c5c5f38ef 100644 --- a/src/uix/eidos/components/date-picker/date-picker-year-view.svelte +++ b/src/uix/eidos/components/date-picker/date-picker-year-view.svelte @@ -6,11 +6,15 @@ * `size` from the visual context and forwards it. */ import * as DatePicker from '$soma/components/date-picker'; + import type { PassthroughProps } from '$soma/types'; import type { YearGridSize } from '../year-grid'; import { getDatePickerVisualContext } from './context.svelte'; - import type { HTMLAttributes } from 'svelte/elements'; - type Props = HTMLAttributes; + // `PassthroughProps` drops the HTML keys that Soma's + // YearGrid narrows (`dir`, `value`, `placeholder`). See + // `soma/types/html.ts` — pattern adopted to avoid the "union too + // complex" / type-incompat errors at the Eidos→Soma boundary. + type Props = PassthroughProps; const props: Props = $props(); diff --git a/src/uix/soma/layers/floating/shell.ts b/src/uix/soma/layers/floating/shell.ts index 59e1c73c9..8b5595968 100644 --- a/src/uix/soma/layers/floating/shell.ts +++ b/src/uix/soma/layers/floating/shell.ts @@ -87,11 +87,16 @@ export function buildFloatingShellWrapperProps( pointerEvents: 'auto' | 'none' = 'auto' ): FloatingContent['wrapperProps'] & { style: Record } { const baseStyle = floating.wrapperProps.style; + // The spread + conditional object widens the inferred type beyond + // what the floating wrapperProps contract narrows (e.g. `transform` + // goes from required-string to optional). At runtime the keys are + // always present — we cast at the boundary so consumers downstream + // keep the strict shape. return { ...floating.wrapperProps, style: { ...(typeof baseStyle === 'object' && baseStyle !== null ? baseStyle : {}), 'pointer-events': pointerEvents } - }; + } as FloatingContent['wrapperProps'] & { style: Record }; } diff --git a/src/uix/soma/types/html.ts b/src/uix/soma/types/html.ts index c169af6a6..021e2fa89 100644 --- a/src/uix/soma/types/html.ts +++ b/src/uix/soma/types/html.ts @@ -15,6 +15,38 @@ import type { // Primitive attribute types — omit style, id, children (handled by WithChild/mergeProps) type OmitManaged = Omit; +/** + * Canonical "safe" HTML attribute subset for Eidos→Soma passthrough. + * + * Use this in **Eidos wrapper components** that type their props as + * `HTMLAttributes` and forward via `{...props}` into a Soma + * Provider. The naive `HTMLAttributes` clashes with Soma's + * narrowing of certain attributes (`dir`, `value`, `placeholder`) — + * the intersection produces "Expression produces a union type that is + * too complex to represent" errors or property incompatibility on the + * Eidos→Soma boundary. + * + * `PassthroughProps` drops every key Soma is allowed to narrow. + * Anything the wrapper still wants to expose at the Eidos surface + * (typically `class`, `role`, `data-*`, `aria-*` — all included) flows + * through cleanly. If a wrapper needs to accept a narrowed key (e.g. + * `dir: 'ltr' | 'rtl'`) it extends explicitly: + * + * ```ts + * type Props = PassthroughProps & { + * dir?: 'ltr' | 'rtl'; + * }; + * ``` + * + * Pattern adopted in the 2026-05-25 audit-codex cleanup after the + * picker-view drift (`date-picker-year-view` / `month-view`) surfaced + * the same shape repeatedly across Eidos wrappers. + */ +export type PassthroughProps = Omit< + OmitManaged>, + 'dir' | 'value' | 'placeholder' +>; + export type PrimitiveDivAttributes = OmitManaged>; export type PrimitiveButtonAttributes = OmitManaged; export type PrimitiveInputAttributes = OmitManaged; diff --git a/src/uix/soma/types/index.ts b/src/uix/soma/types/index.ts index 6023f9f49..69e7650ba 100644 --- a/src/uix/soma/types/index.ts +++ b/src/uix/soma/types/index.ts @@ -43,5 +43,6 @@ export type { PrimitiveTRAttributes, PrimitiveTHAttributes, PrimitiveTDAttributes, - PrimitiveCaptionAttributes + PrimitiveCaptionAttributes, + PassthroughProps } from './html';