You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/soma/layers/floating/shell.ts

103 lines
4.1 KiB

refactor(floating): extract createFloatingShellRoot + buildFloatingShellWrapperProps helpers (audit Round 3 §3 #1) 7 popover-based soma providers (popover, dropdown-menu, context-menu, combobox, select, tooltip, link-preview) shared two byte-identical blocks that an investigation report (delegated Plan agent) confirmed as real duplication after looking carefully past the audit's headline "600 lines across 15 providers" — the actual scope was ~230 lines across 7 providers (the audit overcounted by ~2x, similar to the BaseSegmentProvider case). Two narrow helpers added in `src/uix/soma/layers/floating/shell.ts`: - `createFloatingShellRoot({ dom, open, contentRef, onOpenChangeComplete })` bundles `FloatingProvider.create({ dom })` + the `contentPresence` constructor (which were verbatim across all 7 providers, byte for byte). Returns `{ floatingProvider, contentPresence }` the consumer assigns to `this.*` fields. - `buildFloatingShellWrapperProps(floating, pointerEvents = 'auto')` returns the canonical wrapperProps shape (spread floating's wrapperProps + `style.pointer-events` forced to a value). Identical in 6 of 7; tooltip parameterises `pointerEvents` to flip on `hoverableDisabled`. Explicitly NOT abstracted: FocusScope, Dismissal, ScrollLock, TextSelection — these look similar but diverge per provider (modal- derived `trap`, custom `isValidEvent` closures, different close callbacks — see Round 3 audit analysis). Trying to hide them would recreate the BaseSegmentProvider trap of flag bloat. Notable variations preserved: - popover keeps its `overlayPresence` (popover-only chrome) outside the shell helper — only `contentPresence` is shared. - context-menu's SubContent sub-provider also uses the same wrapper helper (its FloatingProvider.create stays inline because it reads `this.provider.soma.dom` not `this.soma.dom`). - dropdown-menu has both a Content and SubContent wrapperProps — both routed through the helper. Test result: 2394/2399 passing — no regressions in the 27 tests across the 7 affected providers. The 5 fails remain Words + cookie infra (cookie is flaky; sometimes 6, sometimes 5). Closes the last item of Kim audit Round 3 §3. Net code reduction in the 7 consumer files is ~85 lines; helper file is 96 lines with docblocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
* Floating "shell" helpers — small abstractions over the byte-identical
* boilerplate that 7 popover-based soma providers (popover, dropdown-menu,
* context-menu, combobox, select, tooltip, link-preview) used to inline:
*
* 1. **Root-side** triad: every root provider calls `FloatingProvider.create({ dom })`
* then constructs a `contentPresence` with the open state + content ref +
* onOpenChangeComplete callback. The 5-line block was byte-identical
* across the 7 providers.
*
* 2. **Content-side** `wrapperProps`: every Content provider exposes a
* `$derived` field that re-emits the floating layer's wrapperProps
* with `pointer-events: 'auto'` forced onto the style object. The
* 7-line block was identical in 6 of 7 (tooltip parameterises the
* pointer-events value).
*
* The helpers do NOT abstract FocusScope / Dismissal / ScrollLock /
* TextSelection — those layers genuinely diverge per provider
* (different `trap` resolution, different `isValidEvent` closures,
* different close routing — see Round 3 audit analysis).
*
* Saves ~150 lines net across the 7 consumers without introducing
* configuration flags.
*/
import type { ActiveDom } from '$adom';
import type { Active, State } from '$libs/reactive';
import type { OnChangeFn } from '../../types';
import { FloatingProvider, type FloatingContent } from './floating.svelte';
import { Presence } from '../presence.svelte';
export interface FloatingShellRootOpts {
/** DOM service — every floating root piggybacks on the soma ActiveDom. */
dom: ActiveDom;
/** The provider's `open` state — drives the content presence transitions. */
open: Active<boolean>;
/** Ref to the Content element. Presence listens to its mount/unmount. */
contentRef: State<HTMLElement | null>;
/**
* Called when the open/close presence transition completes (animation
* end). Optional — when omitted, presence still runs but the callback
* is a no-op.
*/
onOpenChangeComplete?: Active<OnChangeFn<boolean> | undefined>;
}
export interface FloatingShellRoot {
floatingProvider: FloatingProvider;
contentPresence: Presence;
}
/**
* Initialise the shared `FloatingProvider + contentPresence` triad. The
* resulting handles are stored on the consumer's class (`this.floatingProvider`,
* `this.contentPresence`) — the helper just removes the boilerplate.
*/
export function createFloatingShellRoot(opts: FloatingShellRootOpts): FloatingShellRoot {
const floatingProvider = FloatingProvider.create({ dom: opts.dom });
const contentPresence = new Presence({
dom: opts.dom,
open: opts.open,
ref: opts.contentRef,
onComplete: opts.onOpenChangeComplete
? (open) => opts.onOpenChangeComplete!.current?.(open)
refactor(motion): retire parallel orchestration motor (Plan A) + verified model + Fase 1 prototype Remove the parallel motion-coordination service per the redesign: animation is a channel of the EVENT's firma (sema owns it across all channels), not a parallel axis. Code is gone; the RFC stays as historical record with a retirement banner. - morfo: drop MorfoPart.animation + its schema/compile/types/exports - arts/motion: drop CoordinatedPreset / MotionConfig.coordinated - eidos: drop BUILTIN_COORDINATED_PRESETS / renderCoordinatedPresetRules / the animation:none neutralization + registry block; regen generated/base.css - soma: Presence reduced to a single-surface island; delete presence-group / dom-cascade / coordination; dropdown-menu loses the cascade wiring - delete Reveal / Rail (morfo + soma + demos) Docs: MOTION_SERVICE_RFC gains the retirement banner + §D.8 (retirada) + §D.9 (verified model: morfo->soma->sema->eidos pipeline + the two hard rules — soma never writes a visual --var; data-event-* is a single-target stamp, not a bus) + §D.10 (Fase 1 prototype). Fix stale "5 canales" claim in GUIA §11; eidos-motion + dropdown README aligned. Fase 1 prototype (web/routes/temas/animations/panel-cascade): validates the model end to end — panel->cards cascade (enter/exit), dynamic removal with Svelte out: retention, nested cascade — all via :nth-child + custom-property inheritance, with zero JS visual writes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
: undefined
refactor(floating): extract createFloatingShellRoot + buildFloatingShellWrapperProps helpers (audit Round 3 §3 #1) 7 popover-based soma providers (popover, dropdown-menu, context-menu, combobox, select, tooltip, link-preview) shared two byte-identical blocks that an investigation report (delegated Plan agent) confirmed as real duplication after looking carefully past the audit's headline "600 lines across 15 providers" — the actual scope was ~230 lines across 7 providers (the audit overcounted by ~2x, similar to the BaseSegmentProvider case). Two narrow helpers added in `src/uix/soma/layers/floating/shell.ts`: - `createFloatingShellRoot({ dom, open, contentRef, onOpenChangeComplete })` bundles `FloatingProvider.create({ dom })` + the `contentPresence` constructor (which were verbatim across all 7 providers, byte for byte). Returns `{ floatingProvider, contentPresence }` the consumer assigns to `this.*` fields. - `buildFloatingShellWrapperProps(floating, pointerEvents = 'auto')` returns the canonical wrapperProps shape (spread floating's wrapperProps + `style.pointer-events` forced to a value). Identical in 6 of 7; tooltip parameterises `pointerEvents` to flip on `hoverableDisabled`. Explicitly NOT abstracted: FocusScope, Dismissal, ScrollLock, TextSelection — these look similar but diverge per provider (modal- derived `trap`, custom `isValidEvent` closures, different close callbacks — see Round 3 audit analysis). Trying to hide them would recreate the BaseSegmentProvider trap of flag bloat. Notable variations preserved: - popover keeps its `overlayPresence` (popover-only chrome) outside the shell helper — only `contentPresence` is shared. - context-menu's SubContent sub-provider also uses the same wrapper helper (its FloatingProvider.create stays inline because it reads `this.provider.soma.dom` not `this.soma.dom`). - dropdown-menu has both a Content and SubContent wrapperProps — both routed through the helper. Test result: 2394/2399 passing — no regressions in the 27 tests across the 7 affected providers. The 5 fails remain Words + cookie infra (cookie is flaky; sometimes 6, sometimes 5). Closes the last item of Kim audit Round 3 §3. Net code reduction in the 7 consumer files is ~85 lines; helper file is 96 lines with docblocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
});
return { floatingProvider, contentPresence };
}
/**
* Build the wrapper props the Content provider exposes on `data-*-content`'s
* wrapper. Merges the floating layer's own wrapperProps (positioning + role
* + transition attrs) with `pointer-events` forced ON so portaled content
* captures clicks.
*
* Tooltip overrides `pointerEvents` to toggle on `hoverableDisabled`; the
* rest pass `'auto'` (default).
*
* Returns the spread + an explicit `style: Record<string, unknown>` shape
* so Content components can pass it to `styleToString` directly without TS
* narrowing tripping on the upstream `string | Record<...>` union.
*/
export function buildFloatingShellWrapperProps(
floating: FloatingContent,
pointerEvents: 'auto' | 'none' = 'auto'
): FloatingContent['wrapperProps'] & { style: Record<string, unknown> } {
const baseStyle = floating.wrapperProps.style;
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>
5 months ago
// 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.
refactor(floating): extract createFloatingShellRoot + buildFloatingShellWrapperProps helpers (audit Round 3 §3 #1) 7 popover-based soma providers (popover, dropdown-menu, context-menu, combobox, select, tooltip, link-preview) shared two byte-identical blocks that an investigation report (delegated Plan agent) confirmed as real duplication after looking carefully past the audit's headline "600 lines across 15 providers" — the actual scope was ~230 lines across 7 providers (the audit overcounted by ~2x, similar to the BaseSegmentProvider case). Two narrow helpers added in `src/uix/soma/layers/floating/shell.ts`: - `createFloatingShellRoot({ dom, open, contentRef, onOpenChangeComplete })` bundles `FloatingProvider.create({ dom })` + the `contentPresence` constructor (which were verbatim across all 7 providers, byte for byte). Returns `{ floatingProvider, contentPresence }` the consumer assigns to `this.*` fields. - `buildFloatingShellWrapperProps(floating, pointerEvents = 'auto')` returns the canonical wrapperProps shape (spread floating's wrapperProps + `style.pointer-events` forced to a value). Identical in 6 of 7; tooltip parameterises `pointerEvents` to flip on `hoverableDisabled`. Explicitly NOT abstracted: FocusScope, Dismissal, ScrollLock, TextSelection — these look similar but diverge per provider (modal- derived `trap`, custom `isValidEvent` closures, different close callbacks — see Round 3 audit analysis). Trying to hide them would recreate the BaseSegmentProvider trap of flag bloat. Notable variations preserved: - popover keeps its `overlayPresence` (popover-only chrome) outside the shell helper — only `contentPresence` is shared. - context-menu's SubContent sub-provider also uses the same wrapper helper (its FloatingProvider.create stays inline because it reads `this.provider.soma.dom` not `this.soma.dom`). - dropdown-menu has both a Content and SubContent wrapperProps — both routed through the helper. Test result: 2394/2399 passing — no regressions in the 27 tests across the 7 affected providers. The 5 fails remain Words + cookie infra (cookie is flaky; sometimes 6, sometimes 5). Closes the last item of Kim audit Round 3 §3. Net code reduction in the 7 consumer files is ~85 lines; helper file is 96 lines with docblocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return {
...floating.wrapperProps,
style: {
...(typeof baseStyle === 'object' && baseStyle !== null ? baseStyle : {}),
'pointer-events': pointerEvents
}
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>
5 months ago
} as FloatingContent['wrapperProps'] & { style: Record<string, unknown> };
refactor(floating): extract createFloatingShellRoot + buildFloatingShellWrapperProps helpers (audit Round 3 §3 #1) 7 popover-based soma providers (popover, dropdown-menu, context-menu, combobox, select, tooltip, link-preview) shared two byte-identical blocks that an investigation report (delegated Plan agent) confirmed as real duplication after looking carefully past the audit's headline "600 lines across 15 providers" — the actual scope was ~230 lines across 7 providers (the audit overcounted by ~2x, similar to the BaseSegmentProvider case). Two narrow helpers added in `src/uix/soma/layers/floating/shell.ts`: - `createFloatingShellRoot({ dom, open, contentRef, onOpenChangeComplete })` bundles `FloatingProvider.create({ dom })` + the `contentPresence` constructor (which were verbatim across all 7 providers, byte for byte). Returns `{ floatingProvider, contentPresence }` the consumer assigns to `this.*` fields. - `buildFloatingShellWrapperProps(floating, pointerEvents = 'auto')` returns the canonical wrapperProps shape (spread floating's wrapperProps + `style.pointer-events` forced to a value). Identical in 6 of 7; tooltip parameterises `pointerEvents` to flip on `hoverableDisabled`. Explicitly NOT abstracted: FocusScope, Dismissal, ScrollLock, TextSelection — these look similar but diverge per provider (modal- derived `trap`, custom `isValidEvent` closures, different close callbacks — see Round 3 audit analysis). Trying to hide them would recreate the BaseSegmentProvider trap of flag bloat. Notable variations preserved: - popover keeps its `overlayPresence` (popover-only chrome) outside the shell helper — only `contentPresence` is shared. - context-menu's SubContent sub-provider also uses the same wrapper helper (its FloatingProvider.create stays inline because it reads `this.provider.soma.dom` not `this.soma.dom`). - dropdown-menu has both a Content and SubContent wrapperProps — both routed through the helper. Test result: 2394/2399 passing — no regressions in the 27 tests across the 7 affected providers. The 5 fails remain Words + cookie infra (cookie is flaky; sometimes 6, sometimes 5). Closes the last item of Kim audit Round 3 §3. Net code reduction in the 7 consumer files is ~85 lines; helper file is 96 lines with docblocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}

Powered by TurnKey Linux.