fix(soma/eidos): close last 5 check errors — PassthroughProps helper + floating/shell cast

`npm run check`: 5 errors → 0 errors.

floating/shell.ts (Phase 1 — local fix):
- `buildFloatingShellWrapperProps` returned a type where TS couldn't
  prove `transform` stayed required after the conditional-object
  spread. Runtime preserves the key; we cast at the return boundary so
  consumers downstream keep the strict shape.

soma/types/html.ts (Phase 2 — canonical pattern):
- Nuevo `PassthroughProps<T>` helper para resolver el drift Eidos→Soma
  estructuralmente. Es `Omit<HTMLAttributes<T>, 'style' | 'id' |
  'children' | 'dir' | 'value' | 'placeholder'>`. Las keys excluidas son
  las que Soma narrowa en sus Provider types — incluirlas en Eidos
  wrappers (via el plain `HTMLAttributes<T>`) producía "Expression
  produces a union type that is too complex to represent" y errores de
  incompatibilidad al hacer spread.
- Exportado por `soma/types/index.ts`.

eidos picker views (Phase 3 — adopción):
- `date-picker-year-view.svelte` y `date-picker-month-view.svelte`
  tipados como `Props = PassthroughProps<HTMLDivElement>` en lugar del
  plain `HTMLAttributes<HTMLDivElement>`. Conserva data-*, aria-*,
  class, role, tabindex etc. — solo dropea las keys conflictivas.

Pattern reusable: cualquier futuro Eidos wrapper que envuelva un Soma
Provider via `{...props}` debe usar `PassthroughProps<T>` en vez de
`HTMLAttributes<T>`. Documentado en el JSDoc del helper con ejemplo.

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

@ -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<HTMLDivElement>;
// See `date-picker-year-view.svelte` for the rationale on
// `PassthroughProps` — same pattern, same boundary.
type Props = PassthroughProps<HTMLDivElement>;
const props: Props = $props();

@ -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<HTMLDivElement>;
// `PassthroughProps<HTMLDivElement>` 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<HTMLDivElement>;
const props: Props = $props();

@ -87,11 +87,16 @@ export function buildFloatingShellWrapperProps(
pointerEvents: 'auto' | 'none' = 'auto'
): FloatingContent['wrapperProps'] & { style: Record<string, unknown> } {
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<string, unknown> };
}

@ -15,6 +15,38 @@ import type {
// Primitive attribute types — omit style, id, children (handled by WithChild/mergeProps)
type OmitManaged<T> = Omit<T, 'style' | 'id' | 'children'>;
/**
* Canonical "safe" HTML attribute subset for Eidos→Soma passthrough.
*
* Use this in **Eidos wrapper components** that type their props as
* `HTMLAttributes<T>` and forward via `{...props}` into a Soma
* Provider. The naive `HTMLAttributes<T>` 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<T>` 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<HTMLDivElement> & {
* 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<T extends HTMLElement = HTMLElement> = Omit<
OmitManaged<HTMLAttributes<T>>,
'dir' | 'value' | 'placeholder'
>;
export type PrimitiveDivAttributes = OmitManaged<HTMLAttributes<HTMLDivElement>>;
export type PrimitiveButtonAttributes = OmitManaged<HTMLButtonAttributes>;
export type PrimitiveInputAttributes = OmitManaged<HTMLInputAttributes>;

@ -43,5 +43,6 @@ export type {
PrimitiveTRAttributes,
PrimitiveTHAttributes,
PrimitiveTDAttributes,
PrimitiveCaptionAttributes
PrimitiveCaptionAttributes,
PassthroughProps
} from './html';

Loading…
Cancel
Save

Powered by TurnKey Linux.