|
|
|
|
import { type Active, type ActiveProps } from '$libs/reactive';
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
import { watch } from 'runed';
|
|
|
|
|
import type { ActiveDom } from '$adom';
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
import type { EngineMotion } from '$motion';
|
|
|
|
|
import type { PresenceGroup, PresenceMember, PresencePhase, PresenceRole } from './presence-group';
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
|
|
|
|
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export interface PresenceOptions extends ActiveProps<{ open: boolean; ref: HTMLElement | null }> {
|
|
|
|
|
dom: ActiveDom;
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
/** Called after open/close animation completes. Receives the current open state. */
|
|
|
|
|
onComplete?: (open: boolean) => void;
|
|
|
|
|
/** When false, skips animation waiting — unmounts immediately on close. @default true */
|
|
|
|
|
enabled?: boolean;
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
/**
|
|
|
|
|
* The motion runtime (`uix.motion` / `soma.motion`). When present, the cycle
|
|
|
|
|
* runs `motion.run(node, phase)` and awaits its `finished` ALONGSIDE
|
|
|
|
|
* `getAnimations()` — so a JS preset (a `spring`, invisible to
|
|
|
|
|
* `getAnimations()`) still gates the unmount. CSS presets settle immediately
|
|
|
|
|
* (their generated CSS is awaited via `getAnimations()`). The component passes
|
|
|
|
|
* `this.soma.motion`; the engine reads the node's `data-animation-style`.
|
|
|
|
|
*/
|
|
|
|
|
motion?: EngineMotion;
|
|
|
|
|
/**
|
|
|
|
|
* Presence-coordination group (RFC: eidos/MOTION_SERVICE_RFC.md §7). When
|
|
|
|
|
* present, this surface CEDES its enter timing to the group: it mounts and
|
|
|
|
|
* waits to be released instead of self-driving (the group sequences releases
|
|
|
|
|
* per the morfo's `animation.children.{enter,exit}` and aggregates `finished`).
|
|
|
|
|
* Absent (the common case) ⇒ this `Presence` is an island, exactly as before.
|
|
|
|
|
*/
|
|
|
|
|
group?: PresenceGroup;
|
|
|
|
|
/**
|
|
|
|
|
* Role within the group. The parent/coordinating surface is `'owner'`; nested
|
|
|
|
|
* surfaces are `'child'` (the default). Only the owner triggers the group's
|
|
|
|
|
* coordinated enter; children mount and wait to be released.
|
|
|
|
|
*/
|
|
|
|
|
groupRole?: PresenceRole;
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type TransitionStatus = 'starting' | 'ending' | undefined;
|
|
|
|
|
|
|
|
|
|
// ── Presence ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Animation-aware presence manager.
|
|
|
|
|
*
|
|
|
|
|
* Lifecycle:
|
|
|
|
|
*
|
|
|
|
|
* OPENING:
|
|
|
|
|
* open=true → shouldRender=true + transitionStatus='starting'
|
|
|
|
|
* → next rAF: transitionStatus=undefined (removes data-starting-style, triggers CSS transition)
|
|
|
|
|
* → getAnimations().finished → onComplete(true)
|
|
|
|
|
*
|
|
|
|
|
* CLOSING:
|
|
|
|
|
* open=false → transitionStatus='ending' (element stays in DOM!)
|
|
|
|
|
* → getAnimations().finished
|
|
|
|
|
* → shouldRender=false + transitionStatus=undefined → onComplete(false)
|
|
|
|
|
*/
|
|
|
|
|
export class Presence implements PresenceMember {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
readonly opts: PresenceOptions;
|
|
|
|
|
|
|
|
|
|
shouldRender = $state(false);
|
|
|
|
|
transitionStatus = $state<TransitionStatus>(undefined);
|
|
|
|
|
|
|
|
|
|
private runId = 0;
|
|
|
|
|
private frameIds = new Set<number>();
|
|
|
|
|
/** Run id captured when a grouped surface mounts and waits to be released (RFC §7). */
|
|
|
|
|
private pendingRunId = 0;
|
|
|
|
|
/** Deregister callback from the coordination group, if any. */
|
|
|
|
|
private deregister: (() => void) | undefined;
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
|
|
|
|
|
constructor(opts: PresenceOptions) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.shouldRender = opts.open.current;
|
|
|
|
|
|
|
|
|
|
// RFC §7.1: cede to a coordination group if one is in scope. Register now
|
|
|
|
|
// (synchronously, in the provider's init) and deregister on teardown so the
|
|
|
|
|
// group's member set tracks mount/unmount. No group ⇒ island (below).
|
|
|
|
|
if (opts.group) {
|
|
|
|
|
this.deregister = opts.group.register(this);
|
|
|
|
|
$effect(() => () => this.deregister?.());
|
|
|
|
|
}
|
|
|
|
|
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
watch(
|
|
|
|
|
() => opts.open.current,
|
|
|
|
|
(isOpen) => {
|
|
|
|
|
if (isOpen) {
|
|
|
|
|
this.handleOpen();
|
|
|
|
|
} else {
|
|
|
|
|
this.handleClose();
|
|
|
|
|
}
|
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
}
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Whether the element should be in the DOM. Use in template: `{#if presence.isPresent}` */
|
|
|
|
|
readonly isPresent = $derived.by(() => this.shouldRender);
|
|
|
|
|
|
|
|
|
|
/** Data attrs to spread onto the element for CSS transition support. */
|
|
|
|
|
readonly transitionAttrs = $derived.by(() => {
|
|
|
|
|
if (this.transitionStatus === 'starting') return { 'data-starting-style': '' } as const;
|
|
|
|
|
if (this.transitionStatus === 'ending') return { 'data-ending-style': '' } as const;
|
|
|
|
|
return {} as const;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Open ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
private handleOpen() {
|
|
|
|
|
// Grouped surface: cede the release timing to the group (RFC §7). The
|
|
|
|
|
// island path below is unchanged.
|
|
|
|
|
if (this.opts.group) {
|
|
|
|
|
this.handleOpenGrouped();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
this.cleanup();
|
|
|
|
|
this.shouldRender = true;
|
|
|
|
|
this.transitionStatus = 'starting';
|
|
|
|
|
|
|
|
|
|
const runId = ++this.runId;
|
|
|
|
|
|
|
|
|
|
// Remove data-starting-style on next frame to trigger CSS transition
|
|
|
|
|
this.requestFrame(() => {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
if (runId !== this.runId) return;
|
|
|
|
|
this.transitionStatus = undefined;
|
|
|
|
|
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
// Start any JS-driven motion, then wait for CSS + JS to complete.
|
|
|
|
|
const extra = this.startMotion('enter');
|
|
|
|
|
this.waitForAnimations(runId, extra, () => {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
this.opts.onComplete?.(true);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Close ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
private handleClose() {
|
|
|
|
|
// Grouped surface: cede the exit to the group, which coordinates the tree's
|
|
|
|
|
// exit and unmounts after (DOM retention §8.1). The island path is unchanged.
|
|
|
|
|
if (this.opts.group) {
|
|
|
|
|
this.handleCloseGrouped();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
this.cleanup();
|
|
|
|
|
|
|
|
|
|
const enabled = this.opts.enabled ?? true;
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
this.shouldRender = false;
|
|
|
|
|
this.transitionStatus = undefined;
|
|
|
|
|
this.opts.onComplete?.(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.transitionStatus = 'ending';
|
|
|
|
|
|
|
|
|
|
const runId = ++this.runId;
|
|
|
|
|
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
// Start any JS-driven exit motion, then wait for CSS + JS, then unmount.
|
|
|
|
|
const extra = this.startMotion('exit');
|
|
|
|
|
this.waitForAnimations(runId, extra, () => {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
if (runId !== this.runId) return;
|
|
|
|
|
this.shouldRender = false;
|
|
|
|
|
this.transitionStatus = undefined;
|
|
|
|
|
this.opts.onComplete?.(false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Grouped lifecycle (RFC §7) — only active when `opts.group` is set ──────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Grouped ENTER: mount immediately, then WAIT to be released by the group.
|
|
|
|
|
* Only the owner triggers the group's coordinated enter; children mount and
|
|
|
|
|
* stay in `data-starting-style` until the group calls `release('enter')`.
|
|
|
|
|
*/
|
|
|
|
|
private handleOpenGrouped() {
|
|
|
|
|
this.cleanup();
|
|
|
|
|
this.shouldRender = true;
|
|
|
|
|
this.transitionStatus = 'starting';
|
|
|
|
|
this.pendingRunId = ++this.runId;
|
|
|
|
|
|
|
|
|
|
if (this.role === 'owner') this.opts.group?.requestEnter();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Grouped EXIT: cede to the group (RFC §8.1). Mark `ending` and WAIT — the
|
|
|
|
|
* group runs the tree's exit in `when` order and calls `unmount()` here only
|
|
|
|
|
* after the whole exit settles (DOM retention). Only the owner triggers it.
|
|
|
|
|
*/
|
|
|
|
|
private handleCloseGrouped() {
|
|
|
|
|
this.cleanup();
|
|
|
|
|
const enabled = this.opts.enabled ?? true;
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
this.unmount();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Stay in the OPEN visual state (no `data-ending-style` yet) until the group
|
|
|
|
|
// RELEASES this surface. That is what makes `when: 'after'` exit the children
|
|
|
|
|
// before the owner — and the owner stay visibly intact while its children
|
|
|
|
|
// leave — instead of everyone's exit animation firing at once. `release`
|
|
|
|
|
// stamps `ending` when our turn comes (RFC §8.1).
|
|
|
|
|
this.pendingRunId = ++this.runId;
|
|
|
|
|
|
|
|
|
|
if (this.role === 'owner') this.opts.group?.requestExit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** This surface's role within its group (`PresenceMember`). */
|
|
|
|
|
get role(): PresenceRole {
|
|
|
|
|
return this.opts.groupRole ?? 'child';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Release this surface's motion for the phase — the group calls this in
|
|
|
|
|
* coordinated order (`PresenceMember`). Removes `data-starting-style` on enter,
|
|
|
|
|
* starts the JS motion, and resolves when this surface's own animation finishes
|
|
|
|
|
* (CSS via `getAnimations()` + any JS `finished`). The visual stagger is CSS
|
|
|
|
|
* (eidos); soma only awaits.
|
|
|
|
|
*/
|
|
|
|
|
release(phase: PresencePhase): Promise<void> {
|
|
|
|
|
const runId = this.pendingRunId;
|
|
|
|
|
return new Promise<void>((resolve) => {
|
|
|
|
|
this.requestFrame(() => {
|
|
|
|
|
if (runId !== this.runId) {
|
|
|
|
|
resolve();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Enter: drop `starting` → the CSS transition plays. Exit: stamp
|
|
|
|
|
// `ending` NOW (not at close) so the surface holds its open state until
|
|
|
|
|
// the group reaches it, giving `when: 'after'` its sequencing (§8.1).
|
|
|
|
|
if (phase === 'enter') this.transitionStatus = undefined;
|
|
|
|
|
else this.transitionStatus = 'ending';
|
|
|
|
|
const extra = this.startMotion(phase);
|
|
|
|
|
this.waitForAnimations(runId, extra, () => {
|
|
|
|
|
if (phase === 'enter') this.opts.onComplete?.(true);
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tear down after the group's coordinated exit settles (`PresenceMember`, RFC
|
|
|
|
|
* §8.1). Mirrors the island unmount: drop the surface and notify. The group
|
|
|
|
|
* calls this once the whole tree's exit has aggregated — never before, so the
|
|
|
|
|
* owner's DOM (and any retained subtree) survives the children's exit.
|
|
|
|
|
*/
|
|
|
|
|
unmount(): void {
|
|
|
|
|
this.shouldRender = false;
|
|
|
|
|
this.transitionStatus = undefined;
|
|
|
|
|
this.opts.onComplete?.(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cancel in-flight motion (interruption — RFC §8.3, M4). `cleanup()` bumps the
|
|
|
|
|
* runId so the in-flight async tail (frames, `waitForAnimations`) becomes a
|
|
|
|
|
* no-op, and `motion.cancel(node)` stops any JS-driven run so a reversal does
|
|
|
|
|
* not stack a second animation on the node (CSS presets are untracked → no-op;
|
|
|
|
|
* the reversed CSS transition continues from its current value). The group calls
|
|
|
|
|
* this on every member when a reversal supersedes the in-flight phase.
|
|
|
|
|
*/
|
|
|
|
|
cancel(): void {
|
|
|
|
|
this.cleanup();
|
|
|
|
|
const node = this.opts.ref.current;
|
|
|
|
|
if (node) this.opts.motion?.cancel(node);
|
|
|
|
|
}
|
|
|
|
|
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
// ── Animation waiting ────────────────────────────────────────────────────
|
|
|
|
|
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
/** Start the JS-driven motion for this phase, if a `motion` engine is wired. */
|
|
|
|
|
private startMotion(phase: 'enter' | 'exit'): Promise<void> | undefined {
|
|
|
|
|
const node = this.opts.ref.current;
|
|
|
|
|
if (!node) return undefined;
|
|
|
|
|
return this.opts.motion?.run(node, phase)?.finished;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private waitForAnimations(
|
|
|
|
|
runId: number,
|
|
|
|
|
extra: Promise<void> | undefined,
|
|
|
|
|
onDone: () => void
|
|
|
|
|
) {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
// getAnimations() needs a frame to see the active animations
|
|
|
|
|
this.requestFrame(() => {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
if (runId !== this.runId) return;
|
|
|
|
|
|
|
|
|
|
const node = this.opts.ref.current;
|
|
|
|
|
if (!node) {
|
|
|
|
|
onDone();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
// CSS animations (via getAnimations) + any JS-driven `finished`. A spring
|
|
|
|
|
// is invisible to getAnimations(), so its `finished` joins the wait here.
|
|
|
|
|
const finishers: Promise<unknown>[] = node.getAnimations().map((a) => a.finished);
|
|
|
|
|
if (extra) finishers.push(extra);
|
|
|
|
|
if (finishers.length === 0) {
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
onDone();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
feat(motion): motion engine as arts/motion service + F6 tokens + F7 typegen
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
Promise.all(finishers)
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
.then(() => {
|
|
|
|
|
if (runId !== this.runId) return;
|
|
|
|
|
onDone();
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {
|
|
|
|
|
// An awaited animation was CANCELLED (its `.finished` rejects).
|
|
|
|
|
// This is not only rapid toggling (stale runId → no-op below): a
|
|
|
|
|
// visual exit animation can be interrupted WITHOUT a state change —
|
|
|
|
|
// e.g. sema unstamps the `data-event-*` that drives the close
|
|
|
|
|
// `dismiss-fade` when its hold ends BEFORE the animation's own
|
|
|
|
|
// duration, cancelling it. A cancelled exit MUST still finalize the
|
|
|
|
|
// lifecycle; otherwise a closed overlay stays mounted forever and
|
|
|
|
|
// its FocusScope traps focus (no block can be focused to type).
|
|
|
|
|
// Stale runs are filtered by the runId guard, exactly as in `then`.
|
|
|
|
|
if (runId !== this.runId) return;
|
|
|
|
|
onDone();
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Cleanup ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
private requestFrame(callback: () => void) {
|
|
|
|
|
const frame = this.opts.dom.requestFrame(() => {
|
|
|
|
|
this.frameIds.delete(frame);
|
|
|
|
|
callback();
|
|
|
|
|
}, this.opts.ref.current);
|
|
|
|
|
this.frameIds.add(frame);
|
|
|
|
|
}
|
|
|
|
|
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
private cleanup() {
|
|
|
|
|
this.runId++;
|
|
|
|
|
for (const frame of this.frameIds) {
|
|
|
|
|
this.opts.dom.cancelFrame(frame, this.opts.ref.current);
|
|
|
|
|
}
|
|
|
|
|
this.frameIds.clear();
|
Refactor soma: consolidate layers, add Soma class, Dialog component, animation system
Layers:
- Consolidate 28 files/10 dirs → 13 files/2 dirs
- Separate behavior layers (classes) from internal components
- Normalize naming: Dismissal, ScrollLock, TextSelection, FocusScope
- Adapt floating layer from terra to soma (imports, CSS vars --soma--)
- Presence rewritten: data-starting/ending-style, forceMount, onComplete, getAnimations API
Architecture:
- Replace config/ with Soma class (root instance with services)
- Add SomaFormat namespace (date, number, currency, unit formatters)
- Add generic resolve() for prop → config → fallback chain
- Provider supports optional ref (ProviderOpts vs WithRefOpts)
- Hierarchical component barrel: import { Dialog } from '$soma/components'
- Descriptive IDs: soma-dialog-trigger-c12
Dialog component (7 parts):
- Provider, Trigger, Content, Overlay, Title, Description, Close
- Content integrates all behavior layers as classes (no template nesting)
- modal prop controls interactOutsideBehavior automatically
- Nested dialog support: depth, data-nested, --soma-dialog-depth CSS vars
- Animation support: forceMount, data-starting/ending-style, onOpenChangeComplete
- Translator integration for aria-labels
- JSDoc documentation on all props
Bug fixes:
- Presence mount without ref (chicken-and-egg)
- ScrollLock unlock path restores body style
- Nested dialog infinite loop (untrack mutation)
- pointer-events: none blocking dialog interaction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
}
|
|
|
|
|
}
|