From b80969135b3a63c1ab2da1b94bfd06c75bc33817 Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Thu, 26 Feb 2026 19:32:09 +0200 Subject: [PATCH 1/2] Update changelog for interact 2.0.2 and remove old plans --- .cursor/plans/cleanupNamedEffects.plan.md | 404 ------------------ .../css_style_generator_7af0135d.plan.md | 156 ------- .../update_interact_docs_26c8d522.plan.md | 152 ------- ...ate_interact_system_rules_aa9a2ffa.plan.md | 128 ------ CHANGELOG.md | 8 + packages/motion/src/Sequence.ts | 75 ++++ 6 files changed, 83 insertions(+), 840 deletions(-) delete mode 100644 .cursor/plans/cleanupNamedEffects.plan.md delete mode 100644 .cursor/plans/css_style_generator_7af0135d.plan.md delete mode 100644 .cursor/plans/update_interact_docs_26c8d522.plan.md delete mode 100644 .cursor/plans/update_interact_system_rules_aa9a2ffa.plan.md create mode 100644 packages/motion/src/Sequence.ts diff --git a/.cursor/plans/cleanupNamedEffects.plan.md b/.cursor/plans/cleanupNamedEffects.plan.md deleted file mode 100644 index e7159290..00000000 --- a/.cursor/plans/cleanupNamedEffects.plan.md +++ /dev/null @@ -1,404 +0,0 @@ -# Named Effects Cleanup Plan - -## Overview - -**Phase 1**: Remove/consolidate redundant presets (GlitchIn, CircleIn, PunchIn removed; ExpandIn consolidated into GrowIn). RevealIn remains as a separate preset. - -**Phase 2**: Remove direction-fixing logic from 5 presets that use `getAdjustedDirection()` to compensate for element rotation. Remove "from out of screen" option. - -**Phase 3**: Simplify opacity handling by removing redundant final keyframes where possible. Provide an API to set style properties such as element opacity and rotation. - -**Phase 4**: Standardize parameter types and coordinate systems across all presets. Unify direction options. Allow all units for distance with sensible defaults. - -**Phase 5**: Remove `power` parameter (`'soft' | 'medium' | 'hard'`) - it's editor wrapper logic that maps to actual numeric values. LLMs can suggest appropriate values directly. - -**Phase 6**: Allow customization of previously hardcoded values like `perspective`, `depth`, `angle`, etc. and values based on DOM measurements. These will be reviewed on a case-by-case basis. - ---- - -## Phase 1: Remove Redundant Presets - -### Presets to Remove - -### 1. GlitchIn (Wrapper) - -**Why Remove**: GlitchIn is just a thin wrapper around [`GlideIn.ts`](packages/motion-presets/src/library/entrance/GlideIn.ts) that provides no additional capabilities. - -**What it does**: - -- Defaults `direction` to 270° (from left, slides right) -- If user provides a direction, arbitrarily subtracts 90° from it -- Then calls GlideIn with the adjusted direction -- No unique visual effect or animation logic - -**Changes to GlideIn**: - -- Update default `direction` from `0` to `270` to match the more common "from left" behavior that GlitchIn provided -- Remove `startFromOffScreen` option - this requires DOM measurements (left, top) and adds complexity - -**Migration**: Use `GlideIn` directly with any `direction` angle (e.g., `direction: 270` for from left, or any value 0-360°). - -### 2. ExpandIn (Consolidate into GrowIn) - -**Why Remove**: ExpandIn and GrowIn achieve the same visual effect with minor parameter tweaks. The only notable difference is fade-in timing, which doesn't justify maintaining two separate presets. - -**Comparison**: - -**ExpandIn:** - -- Scales from a transform origin point (edge/corner) -- Direction: Discrete positions (top, right, bottom-left, center, etc.) -- Complex transform origin manipulation with width/height calculations -- Uses element measurement (prepare function) -- Transform: `translateX(calc(width * x)) translateY(calc(height * y)) scale() translateX(calc(width * -x)) ...` - -**GrowIn:** - -- Translates from a direction + scales -- Direction: Angle-based (0-360 degrees, any direction) -- Simpler implementation with distance + direction -- Transform: `translate(x, y) rotate() scale()` - -**Analysis**: After value tweaks, both effects behave almost identically except for fade-in timing. GrowIn provides more flexibility with 360° direction support and simpler configuration. ExpandIn's discrete position + complex transform origin approach can be replicated with GrowIn using appropriate angle and distance values. - -**Migration**: Use `GrowIn` with appropriate `direction` (angle) and `distance` parameters to achieve the same expand-from-edge effect. - -### 3. CircleIn (Legacy) - -**Why Remove**: Legacy preset, no longer needed for consumers. - -### 4. PunchIn (Legacy) - -**Why Remove**: Legacy preset, no longer needed for consumers. - -### RevealIn - NOT Being Removed - -**Decision**: RevealIn will remain as a separate preset and will NOT be consolidated into ShapeIn. They serve different purposes: - -- RevealIn: directional reveals (left, right, top, bottom) -- ShapeIn: geometric shape reveals (diamond, circle, rectangle, ellipse, window) - -### ShapeIn Cleanup - -Since RevealIn is not being consolidated into ShapeIn, if ShapeIn currently accepts a `direction` parameter, it should be removed from: - -- Type definitions -- API/parameters -- Implementation code - -### Files to Modify (Phase 1) - -### Core Files to Delete - -- [`packages/motion-presets/src/library/entrance/GlitchIn.ts`](packages/motion-presets/src/library/entrance/GlitchIn.ts) -- [`packages/motion-presets/src/library/entrance/ExpandIn.ts`](packages/motion-presets/src/library/entrance/ExpandIn.ts) -- [`packages/motion-presets/src/library/entrance/CircleIn.ts`](packages/motion-presets/src/library/entrance/CircleIn.ts) -- [`packages/motion-presets/src/library/entrance/PunchIn.ts`](packages/motion-presets/src/library/entrance/PunchIn.ts) - -### Test Files to Delete - -- `packages/motion-presets/src/library/entrance/test/GlitchIn.spec.ts` -- `packages/motion-presets/src/library/entrance/test/ExpandIn.spec.ts` -- `packages/motion-presets/src/library/entrance/test/CircleIn.spec.ts` -- `packages/motion-presets/src/library/entrance/test/PunchIn.spec.ts` - -### Update Exports - -- [`packages/motion-presets/src/library/entrance/index.ts`](packages/motion-presets/src/library/entrance/index.ts) - Remove GlitchIn, ExpandIn, CircleIn, and PunchIn exports - -### Update Type Definitions - -Check [`packages/motion-presets/src/types.ts`](packages/motion-presets/src/types.ts) for any GlitchIn, ExpandIn, CircleIn, and PunchIn type definitions and remove them. - ---- - -## Phase 2: Remove Direction-Fixing Logic - -### Overview - -Many presets have complex `prepare()` functions that read `--comp-rotate-z` from computed styles and use `getAdjustedDirection()` to compensate for element rotation. This adds complexity and couples the presets to the element's rotation state. - -**Goal**: Simplify presets by removing this rotation-aware direction adjustment. Effects should use the direction parameter as specified, without trying to compensate for element rotation. - -### Additional: Remove "From Out of Screen" Option - -Remove the `startFromOffScreen` option from presets that support it. This option requires DOM measurements (left, top) and adds unnecessary complexity. - -### Presets with Direction-Fixing Logic - -These 5 presets use `getAdjustedDirection()` in their prepare functions: - -1. **FlipIn** - Adjusts rotateX/Y based on element rotation -2. **FoldIn** - Adjusts origin and rotation based on element rotation -3. **SlideIn** - Adjusts clip-path direction based on element rotation -4. **TiltIn** - Adjusts clip-path direction based on element rotation -5. **WinkIn** - Adjusts direction based on element rotation - -### Changes Required - -For each preset: - -1. **Remove rotation measurement** from `prepare()` function -2. **Remove `getAdjustedDirection()` calls** -3. **Use direction parameter directly** without adjustment -4. **Simplify or remove `prepare()` function** if it only did direction fixing -5. **Keep `prepare()` if it measures element dimensions** (width, height, left, top) - -### Benefits - -- **Simpler code**: Remove rotation measurement and adjustment logic -- **Predictable behavior**: Direction works as specified -- **Better performance**: No DOM measurements for rotation -- **Easier to understand**: Less coupling between element state and effect - ---- - -## Phase 3: Simplify Opacity and Rotation Handling - -### Overview - -Many presets include explicit final keyframes setting `opacity: 'var(--comp-opacity, 1)'`. The `--comp-opacity` variable was originally required because the element's computed opacity was 0 in order to keep the element hidden on load. - -Additionally, `--comp-rotate-z` is used for scroll effects - we need to enable users to set rotation. - -### Goals - -1. **Remove redundant final keyframes**: When a final keyframe only specifies opacity, it can be removed to let the animation use the element's natural CSS opacity -2. **Provide an API for style properties**: Create an API that allows users to set element opacity and rotation values that presets can reference -3. **Evaluate each preset**: Determine which presets can omit the final opacity keyframe(s) entirely - -### Strategy - -**For Opacity**: - -When the final keyframe only sets opacity and no other properties, omit it entirely. The Web Animations API will automatically use the element's computed opacity from CSS. - -**For Style Properties API**: - -Provide a mechanism for users to specify target values for: - -- Element opacity (final opacity after animation) -- Element rotation (target rotation state) - -This allows presets to animate to user-specified values rather than relying on CSS custom properties with fallbacks. - ---- - -## Phase 4: Standardize Parameter Types and Coordinate Systems - -### Overview - -Standardize parameter naming and coordinate systems across all presets for consistency and predictability. - -### 1. Unify Direction Options - -Standardize how direction is specified across all presets for consistency. - -Presets using numeric degrees should use `angle` instead of `direction`: - -| Preset | Current | Change To | - -| :---- | :---- | :---- | - -| GrowIn | `direction = 0` | `angle = 0` | - -| GlideIn | `direction = 270` | `angle = 270` | - -**Files to update**: - -- `packages/motion-presets/src/library/entrance/GrowIn.ts` -- `packages/motion-presets/src/library/entrance/GlideIn.ts` -- Corresponding type definitions in `types.ts` - -### 2. Standardize Coordinate System (0° = Right) - -**Current system** (incorrect): - -- `0°` = from top (north) -- `90°` = from right (east) - -**Standard system** (correct): - -- `0°` = from right (east) -- `90°` = from top (north) - -**Change required**: - -```ts -// Current (wrong) -const x = Math.sin(angleInRad) * distance.value; -const y = Math.cos(angleInRad) * distance.value * -1; - -// Standard (correct) -const x = Math.cos(angleInRad) * distance.value; -const y = Math.sin(angleInRad) * distance.value * -1; -``` - -**Files to update**: - -- `packages/motion-presets/src/library/entrance/GrowIn.ts` -- `packages/motion-presets/src/library/entrance/GlideIn.ts` -- `packages/motion-presets/src/library/scroll/MoveScroll.ts` (verify) - -### 3. Allow All Units for Distance - -Distance parameters should accept all CSS units while maintaining a sensible default. - -**Current**: Distance may be limited to specific units or numeric values - -**Change**: Accept any valid CSS unit (px, em, rem, %, vw, vh, etc.) - -**Default**: Maintain a sensible default value (e.g., `100px`) - -### 4. Rename `direction` for Horizontal/Vertical Presets - -Presets using `'horizontal' | 'vertical'` should use a different parameter name to avoid confusion with cardinal directions. - -**Options** (need to choose): - -- `axis: 'horizontal' | 'vertical'` -- `orientation: 'horizontal' | 'vertical'` - -**Presets to update**: - -- `packages/motion-presets/src/library/entrance/WinkIn.ts` -- `packages/motion-presets/src/library/scroll/FlipScroll.ts` -- `packages/motion-presets/src/library/scroll/ArcScroll.ts` -- `packages/motion-presets/src/library/ongoing/Flip.ts` -- `packages/motion-presets/src/library/ongoing/Breathe.ts` -- Corresponding type definitions in `types.ts` - -### Summary of `direction` Parameter Usage (After Changes) - -| Meaning | Parameter | Values | Presets | - -| :---- | :---- | :---- | :---- | - -| Numeric angle | `angle` | `0-360` (degrees) | GrowIn, GlideIn, MoveScroll, mouse presets | - -| Cardinal | `direction` | `'top' \| 'right' \| 'bottom' \| 'left'` | FlipIn, FoldIn, SlideIn, FloatIn, BounceIn, etc. | - -| Rotation | `direction` | `'clockwise' \| 'counter-clockwise'` | SpinIn, SpinScroll, Spin | - -| Axis | `axis` or `orientation` | `'horizontal' \| 'vertical'` | WinkIn, FlipScroll, ArcScroll, Flip, Breathe | - ---- - -## Phase 5: Remove `power` Parameter - -### Overview - -The `power` parameter (`'soft' | 'medium' | 'hard'`) is a simplified abstraction designed for editor UIs (like Wix Editor). It maps these three intensity levels to actual numeric parameter values, which differ per preset. - -**Why Remove**: This adds wrapper logic and indirection without real benefit. Users calling these presets directly (or through an LLM) can use the actual parameters with precise values. - -### Current Behavior - -Each preset has a `POWER_MAP` (or similar) that translates power levels: - -| Preset | Parameter | soft | medium | hard | - -| :---- | :---- | :---- | :---- | :---- | - -| **FlipIn** | `initialRotate` | 45° | 90° | 270° | - -| **FoldIn** | `initialRotate` | 45° | 90° | 120° | - -| **GrowIn** | `initialScale` | 0.8 | 0.6 | 0 | - -| **BounceIn** | `distanceFactor` | 0.5 | 0.75 | 1 | - -| **ArcIn** | easing | quintInOut | backOut | backInOut | - -| **FlipScroll** | `rotate` | 45° | 90° | 120° | - -| **TiltScroll** | travel distance | 0.25× | 0.5× | 1× multiplier | - -| **Spin3dScroll** | rotation + travel | varies | varies | varies | - -| **Mouse presets** | angle/scale/easing | varies | varies | varies | - -### Migration Strategy - -1. **Remove power parameter** from all preset type definitions -2. **Remove POWER_MAP constants** from preset implementations -3. **Use direct parameters** - users specify exact values like `initialRotate: 45` instead of `power: 'soft'` -4. **Document conversions** - provide a reference table so LLMs can suggest appropriate values when users ask for "soft" or "hard" effects - ---- - -## Phase 6: Expose Hardcoded Values as Parameters - -### Overview - -Many presets measure element dimensions or use hardcoded values that could instead be customizable parameters. This phase exposes these values as optional parameters with sensible defaults. If those params are not passed, we fallback to hardcoded defaults or measurements like before. - -**Note**: These will be reviewed on a case-by-case basis. - -### Category 1: DOM Measurements That Could Be Parameters - -These presets measure element dimensions and derive values that could instead be customizable: - -| Preset | What's Measured | Hardcoded Calculation | Suggested Parameter | - -| :---- | :---- | :---- | :---- | - -| ArcIn | `width`, `height` | `z = (height or width) / 2` | `depth` (default: 300px) | - -| CurveIn | `width` | `translateZ = width * 3` | `depth` (default: 900px) | - -| TiltIn | `height` | `translateZ = height / 2` | `depth` (default: 200px) | - -| TurnScroll | `left` | Viewport-relative translation | Use CSS fallback | - -| SkewPanScroll | `left` | Viewport-relative translation | Use CSS fallback | - -### Category 2: Hardcoded Values That Could Be Parameters - -| Preset | Hardcoded Value | What It Controls | Suggested Parameter | - -| :---- | :---- | :---- | :---- | - -| **ArcIn** | `ROTATION_ANGLE = 80` | Arc rotation angle | `angle = 80` | - -| **ArcIn** | `perspective(800px)` | 3D perspective | `perspective = 800` | - -| **ArcScroll** | `translateZ(-300px)` | Arc depth | `depth = 300` | - -| **ArcScroll** | `ROTATION = 68` | Arc rotation | `angle = 68` | - -| **ArcScroll** | `perspective(500px)` | 3D perspective | `perspective = 500` | - -| **TiltIn** | `rotateX(-90deg)` | Tilt angle | `tiltAngle = 90` | - -| **TiltIn** | `ROTATION_MAP = { left: 30, right: -30 }` | Z rotation | `rotateZ = 30` | - -| **TiltIn** | `perspective(800px)` | 3D perspective | `perspective = 800` | - -| **FoldIn** | `perspective(800px)` | 3D perspective | `perspective = 800` | - -| **FlipIn** | `perspective(800px)` | 3D perspective | `perspective = 800` | - -| **FlipScroll** | `perspective(800px)` | 3D perspective | `perspective = 800` | - -| **TiltScroll** | `perspective(400px)` | 3D perspective | `perspective = 400` | - -| **TiltScroll** | `[ROTATION_X, ROTATION_Y, ROTATION_Z] = [10, 25, 25] `| Rotation angles | `rotationX`, `rotationY`, `rotationZ` | - -| **TiltScroll** | `MAX_Y_TRAVEL = 40` | Max vertical travel | `maxTravelY = 40` | - -| **Spin3dScroll** | `perspective(1000px)` | 3D perspective | `perspective = 1000` | - -| **Spin3dScroll** | `MAX_Y_TRAVEL = 40` | Max vertical travel | `maxTravelY = 40` | - -| **FloatIn** | `distance: 120` | Float distance | `distance = 120` | - -| **TurnIn** | `angle: -50 / 50` | Rotation angle | `angle = 50` | - -| **CurveIn** | `perspective(200px)` | 3D perspective | `perspective = 200` | - -| **BounceIn** | `perspective(800px)` (center only) | 3D perspective | `perspective = 800` | - -| **TurnScroll** | `ELEMENT_ROTATION = 45` | Element rotation | `rotation = 45` | - -| **Breathe** | `FACTORS_SEQUENCE` | Decay pattern | Could allow custom decay factors | diff --git a/.cursor/plans/css_style_generator_7af0135d.plan.md b/.cursor/plans/css_style_generator_7af0135d.plan.md deleted file mode 100644 index a0e78832..00000000 --- a/.cursor/plans/css_style_generator_7af0135d.plan.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -name: CSS Style Generator -overview: 'Modify the `generate` function in `css.ts` to dynamically create CSS rules based on the interaction config, targeting only elements from `viewEnter` triggers with `type: once` where target equals source.' -todos: - - id: imports - content: Add necessary imports (getSelector, getSelectorCondition, ViewEnterParams type) - status: pending - - id: filter-logic - content: Implement filtering logic for viewEnter/once triggers where target equals source (matching key, selector, listContainer, selectorCondition) - status: pending - - id: selector-builder - content: Build dynamic CSS selectors using key, selector, listContainer, selectorCondition, useFirstChild - status: pending - - id: css-rules - content: Generate CSS rules for each matching interaction/effect pair - status: pending - - id: test - content: Test with various config scenarios (lists, conditions, selectors) - status: pending -isProject: false ---- - -# Dynamic CSS Generation for ViewEnter Once Effects - -## Current State - -The current implementation in `[packages/interact/src/core/css.ts](packages/interact/src/core/css.ts)` outputs a static CSS rule that hides all elements with `data-interact-initial="true"`: - -```typescript -export function generate(_config: InteractConfig, useFirstChild: boolean = false): string { - const css: string[] = [ - `@media (prefers-reduced-motion: no-preference) { - [data-interact-initial="true"]${useFirstChild ? ' > :first-child' : ''}:not([data-interact-enter]) { - visibility: hidden; - ... - } -}`, - ]; - return css.join('\n'); -} -``` - -The `_config` parameter is currently unused. - -## Proposed Changes - -### 1. Iterate Through Interactions and Filter by Criteria - -Loop through `config.interactions` and identify effects that match: - -- Trigger type: `viewEnter` -- Trigger params type: `once` (or undefined, since `once` is the default) -- Target element equals source element - ALL of the following must be true: - - Effect has no `key` property, OR effect's `key` matches interaction's `key` (key IS inherited when missing) - - Effect's `selector` exactly equals interaction's `selector` (both missing, or both have same value) - NOT inherited - - Effect's `listContainer` exactly equals interaction's `listContainer` (both missing, or both have same value) - NOT inherited - - Effect's resolved `selectorCondition` exactly equals interaction's resolved `selectorCondition` (both missing, or both have same value) - -### 2. Build Dynamic Selectors Using Existing Logic - -For each matching interaction/effect pair, build a CSS selector using the `getSelector` utility function from `[packages/interact/src/core/Interact.ts](packages/interact/src/core/Interact.ts)`. Consider: - -- `**key**`: Used to build the base selector `[data-interact-key="${key}"]` -- `**selector**`: Direct selector within the element -- `**listContainer**` and `listItemSelector`: For list-based selections -- `**useFirstChild**`: Adds `> :first-child` combinator -- `**selectorCondition**`: Apply condition predicates using `applySelectorCondition` pattern from `[packages/interact/src/utils.ts](packages/interact/src/utils.ts)` - -### 3. Generate CSS Rules - -For each matching selector, generate a CSS rule in the format: - -```css -@media (prefers-reduced-motion: no-preference) { - [data-interact-key="${key}"]${childSelector}:not([data-interact-enter]) { - visibility: hidden; - transform: none; - translate: none; - scale: none; - rotate: none; - } -} -``` - -### 4. Handle Conditions - -Resolve `selectorCondition` from `conditions` array using `getSelectorCondition` from utils and apply it to the selector using the `&` replacement pattern. - -## Implementation Details - -### Key Function Signature - -```typescript -export function generate(config: InteractConfig, useFirstChild: boolean = false): string; -``` - -### Helper Imports Needed - -Import from existing utilities: - -- `getSelector` from `./Interact` -- `getSelectorCondition` from `../utils` - -### Matching Logic - -```typescript -// For each interaction -config.interactions.forEach((interaction) => { - // Check if trigger is viewEnter - if (interaction.trigger !== 'viewEnter') return; - - // Check if type is 'once' (default if not specified) - const params = interaction.params as ViewEnterParams | undefined; - if (params?.type && params.type !== 'once') return; - - // Resolve interaction's selectorCondition from conditions - const interactionSelectorCondition = getSelectorCondition( - interaction.conditions, - config.conditions || {}, - ); - - // For each effect - interaction.effects.forEach((effect) => { - const effectData = config.effects[effect.effectId] || effect; - - // Check if target equals source - ALL criteria must EXACTLY match: - - // 1. Key: inherited when missing, so check if missing OR matches - if (effectData.key && effectData.key !== interaction.key) return; - - // 2. Selector: NOT inherited, must be exactly equal (both undefined or same value) - if (effectData.selector !== interaction.selector) return; - - // 3. ListContainer: NOT inherited, must be exactly equal (both undefined or same value) - if (effectData.listContainer !== interaction.listContainer) return; - - // 4. SelectorCondition: must be exactly equal (both undefined or same value) - const effectSelectorCondition = getSelectorCondition( - effectData.conditions, - config.conditions || {}, - ); - if (effectSelectorCondition !== interactionSelectorCondition) return; - - // Build selector and add CSS rule - }); -}); -``` - -### Selector Building - -Combine: - -1. Base: `[data-interact-key="${interaction.key}"]` -2. Child selector from `getSelector(effectData, { asCombinator: true, useFirstChild })` -3. Selector condition if present -4. State filter: `:not([data-interact-enter])` diff --git a/.cursor/plans/update_interact_docs_26c8d522.plan.md b/.cursor/plans/update_interact_docs_26c8d522.plan.md deleted file mode 100644 index 62318b89..00000000 --- a/.cursor/plans/update_interact_docs_26c8d522.plan.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -name: Update Interact docs -overview: Update the Interact documentation to match the current library code, fixing factual errors in API signatures, type definitions, and examples, while trimming for conciseness per user request. -todos: - - id: fix-interact-class - content: 'Fix `api/interact-class.md`: correct `setup()` signature (scrollOptionsGetter, pointerOptionsGetter instead of forceReducedMotion/viewProgress/pointerMove), update class overview block and examples' - status: completed - - id: fix-functions - content: 'Fix `api/functions.md`: correct `generate()` signature (add useFirstChild param), fix CSS output to show actual key-based selectors, fix availability claim (all 3 entry points)' - status: completed - - id: fix-types - content: "Fix `api/types.md`: add 'state' to ViewEnterType, add useSafeViewEnter to ViewEnterParams, add axis to PointerMoveParams, add 'selector' to Condition.type, add activate/interest to InteractionParamsTypes, add useFirstChild to IInteractionController, fix customEffect signature, remove fabricated InteractConfigBuilder, remove internal types" - status: completed - - id: fix-controller - content: 'Fix `api/interaction-controller.md`: add useFirstChild to interface, fix disconnect() signature, remove internal _childListChangeHandler, trim verbose sections' - status: completed - - id: fix-triggers-guide - content: "Fix `guides/understanding-triggers.md`: add 'state' to viewEnter behaviors, add axis param to pointerMove, fix section numbering, add brief pageVisible note" - status: completed - - id: fix-react-integration - content: 'Fix `integration/react.md`: add `initial` prop to Interaction component props table, fix accordion example config' - status: completed - - id: fix-readme - content: 'Fix root `README.md`: update entry points table, remove all broken links to non-existent doc files, simplify TOC to only existing pages' - status: completed -isProject: false ---- - -# Update Interact Docs to Match Current Library State - -## Summary of Discrepancies Found - -After comparing all 25 doc files against the source code in `src/core`, `src/handlers`, `src/types.ts`, `src/react`, `src/web`, and `src/dom`, here are the issues grouped by severity. - ---- - -## 1. Fix Factual Errors in API Reference - -### [api/interact-class.md](packages/interact/docs/api/interact-class.md) -- `Interact.setup()` wrong signature - -The documented signature is: - -```typescript -static setup(options: { forceReducedMotion?: boolean, viewEnter?: object, viewProgress?: object, pointerMove?: object, allowA11yTriggers?: boolean }): void -``` - -The actual code at `src/core/Interact.ts:173-200` is: - -```typescript -static setup(options: { - scrollOptionsGetter?: () => Partial; - pointerOptionsGetter?: () => Partial; - viewEnter?: Partial; - allowA11yTriggers?: boolean; -}): void -``` - -Changes needed: - -- Remove `forceReducedMotion` from `setup()` (it's a direct static property: `Interact.forceReducedMotion = true`) -- Replace `viewProgress` with `scrollOptionsGetter` (a callback, not an object) -- Replace `pointerMove` with `pointerOptionsGetter` (a callback, not an object) -- Update the class overview block and examples - -### [api/functions.md](packages/interact/docs/api/functions.md) -- `generate()` errors - -Multiple issues: - -- **Wrong signature**: Docs show `generate(config: InteractConfig): string` but code has `generate(config: InteractConfig, useFirstChild?: boolean): string` -- **Wrong CSS output**: Docs show CSS targeting `[data-interact-initial='true']` but the actual code generates key-specific selectors like `[data-interact-key="hero"]:not([data-interact-enter])` -- **Wrong availability claim**: Docs say "generate is only available from the main `@wix/interact` entry point" but it is exported from all three entry points (`@wix/interact`, `@wix/interact/react`, `@wix/interact/web`) - -### [api/interaction-controller.md](packages/interact/docs/api/interaction-controller.md) -- Missing interface members - -- `useFirstChild: boolean` property is missing from the interface block -- `disconnect()` signature is wrong -- missing `options?: { removeFromCache?: boolean }` parameter - ---- - -## 2. Fix Type Definitions ([api/types.md](packages/interact/docs/api/types.md)) - -All of these are in `api/types.md`, comparing against `src/types.ts`: - -- `**ViewEnterType**`: Missing `'state'` -- should be `'once' | 'repeat' | 'alternate' | 'state'` -- `**ViewEnterParams**`: Missing `useSafeViewEnter?: boolean` -- `**PointerMoveParams**`: Missing `axis?: PointerMoveAxis` (where `PointerMoveAxis = 'x' | 'y'`) -- `**Condition.type**`: Missing `'selector'` -- should be `'media' | 'container' | 'selector'` -- `**InteractionParamsTypes**`: Missing `activate` and `interest` entries -- `**IInteractionController**`: Missing `useFirstChild: boolean` property -- `**customEffect` signature\*\*: Docs show `(element: HTMLElement) => Animation` but code has `(element: Element, progress: any) => void` -- `**HandlerObject.handler**`: Docs show `handler?: () => void` but code has `handler?: (isIntersecting?: boolean) => void` -- **Remove fabricated `InteractConfigBuilder**`: This class does not exist anywhere in the source code - ---- - -## 3. Fix Guides - -### [guides/understanding-triggers.md](packages/interact/docs/guides/understanding-triggers.md) - -- **ViewEnter section**: Only lists `once`, `repeat`, `alternate` -- missing `state` behavior type -- **PointerMove section**: Missing `axis` parameter documentation -- `**pageVisible` trigger\*\*: Listed in overview table but has no dedicated section (add a brief note or remove from table) -- Section numbering jumps from 6 to 8 (missing 7 for PointerMove) - ---- - -## 4. Fix Integration Docs - -### [integration/react.md](packages/interact/docs/integration/react.md) - -- `**Interaction` component props table\*\*: Missing the `initial` prop (`initial?: boolean` sets `data-interact-initial="true"`) -- The accordion config example uses a non-existent `transitionEffect` structure (should use `transition`/`transitionProperties`) - ---- - -## 5. Fix Root README and Remove Broken Links - -### [README.md](packages/interact/docs/README.md) - -- **Entry points table**: `@wix/interact/web` should also show `add`, `remove` in Key Exports -- **Remove or mark broken links** to non-existent files: - - `guides/performance.md` - - `examples/scroll-animations.md`, `examples/advanced-patterns.md`, `examples/real-world.md` - - `integration/vanilla-js.md`, `integration/other-frameworks.md`, `integration/migration.md`, `integration/testing.md`, `integration/debugging.md` - - `advanced/architecture.md`, `advanced/custom-triggers.md`, `advanced/browser-support.md`, `advanced/performance-optimization.md`, `advanced/contributing.md` -- Simplify the TOC to only link to files that exist - ---- - -## 6. Conciseness Pass (per user request) - -Across all docs: - -- **Remove internal types** from `api/types.md`: `HandlerObject`, `HandlerObjectMap`, `InteractCache`, `CreateTransitionCSSParams`, `InteractionHandlerModule`, `TriggerHandlerMap` -- these are internal implementation details -- **Remove `\_childListChangeHandler**`from`api/interaction-controller.md`-- it's an internal method (prefixed with`\_`) -- **Trim verbose examples** in `api/interaction-controller.md` (e.g. the programmatic state management / dynamic list management sections can be shortened) -- **Trim `api/types.md**` significantly by removing internal types and overly detailed examples for rarely-used types -- **Simplify "See Also" sections** across all API docs -- many link to the same set of files repeatedly - ---- - -## File-by-File Summary - -| File | Changes | -| ---------------------------------- | --------------------------------------------------------------------------- | -| `README.md` | Fix entry points table, remove broken links, trim TOC | -| `api/interact-class.md` | Fix `setup()` signature and examples | -| `api/functions.md` | Fix `generate()` signature, CSS output, and availability | -| `api/interaction-controller.md` | Add `useFirstChild`, fix `disconnect()`, trim internals | -| `api/types.md` | Fix 8+ type discrepancies, remove fabricated builder, remove internal types | -| `guides/understanding-triggers.md` | Add `state` to viewEnter, add `axis` to pointerMove, fix numbering | -| `integration/react.md` | Add `initial` prop, fix accordion example | diff --git a/.cursor/plans/update_interact_system_rules_aa9a2ffa.plan.md b/.cursor/plans/update_interact_system_rules_aa9a2ffa.plan.md deleted file mode 100644 index 297bd469..00000000 --- a/.cursor/plans/update_interact_system_rules_aa9a2ffa.plan.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -name: Update interact system rules -overview: Update the 8 rule files in `packages/interact/rules/` to fix bugs, add missing features, and align with the current codebase (types, handlers, core logic, and docs). -todos: - - id: fix-full-lean - content: 'Update full-lean.md: add selector condition type, activate/interest triggers, state to ViewEnterParams, axis to PointerMoveParams' - status: completed - - id: fix-click-md - content: 'Fix click.md: change source/target to key in Rule 2, fix TransitionEffect params in Rule 4' - status: completed - - id: fix-viewprogress-md - content: "Fix viewprogress.md: fix missing comma in Rule 3, fix 'enter' to 'entry' in Rule 4, fix customEffect 3-param signatures" - status: completed - - id: fix-viewenter-md - content: 'Fix viewenter.md: fix broken FOUC section, fix import path, rename SELECTOR variables to KEY' - status: completed - - id: fix-integration-md - content: 'Update integration.md: add activate, interest, pageVisible to triggers table' - status: completed - - id: fix-scroll-list-md - content: 'Fix scroll-list.md: fix customEffect 3-param signature, standardize variable naming' - status: completed - - id: fix-pointermove-md - content: 'Review pointermove.md: fix any customEffect 3-param signatures if present' - status: completed -isProject: false ---- - -# Update Interact System Rules - -The rules in `packages/interact/rules/` have several discrepancies with the current code in `types.ts`, `handlers/`, `core/`, and the docs. Changes fall into three categories: **bug fixes**, **missing features**, and **consistency improvements**. - ---- - -## 1. Fix Bugs and Syntax Errors - -### [full-lean.md](packages/interact/rules/full-lean.md) - -- **Line 118**: Condition `type` only lists `'media' | 'container'` -- add `'selector'`. The `selector` type is used in code (`getSelectorCondition` in [utils.ts](packages/interact/src/utils.ts)) to produce a CSS selector predicate (with `&` replacement) that guards when an effect/interaction applies: - -```typescript -type: 'media' | 'container' | 'selector'; -``` - -Add documentation: - -- `'selector'`: The predicate is a CSS selector pattern. If it contains `&`, the `&` is replaced with the base element selector; otherwise the predicate is appended to the base selector. Used for conditional styling based on element state (e.g., `:hover`, `.active`). - -### [click.md](packages/interact/rules/click.md) - -- **Rule 2 (lines 122-141)**: Pattern uses `source` and `target` property names -- must be changed to `key`: - -```typescript -// WRONG (current): -source: '[SOURCE_IDENTIFIER]', -effects: [{ target: '[TARGET_IDENTIFIER]', ... }] - -// CORRECT: -key: '[SOURCE_IDENTIFIER]', -effects: [{ key: '[TARGET_IDENTIFIER]', ... }] -``` - -- **Rule 4 (line 313)**: TransitionEffect patterns use `params: { type: 'alternate' }` but TransitionEffects should use `StateParams` with `method`. Change to `params: { method: 'toggle' }` for the toggle behavior, or remove `params` (toggle is the default). Per the handler code in [click.ts](packages/interact/src/handlers/click.ts), `TransitionEffect` goes through `createTransitionHandler` which reads `StateParams.method`, not `PointerTriggerParams.type`. - -### [viewprogress.md](packages/interact/rules/viewprogress.md) - -- **Rule 3 example (~line 230)**: Missing comma after `name: 'fade-out'`: - -```typescript -// WRONG: -name: 'fade-out' -keyframes: [{ ... - -// CORRECT: -name: 'fade-out', -keyframes: [{ ... -``` - -- **Rule 4 example (~line 333)**: Uses range name `'enter'` instead of `'entry'`. Per [types.ts](packages/interact/src/types.ts) `RangeOffset` names are `'entry' | 'exit' | 'contain' | 'cover' | 'entry-crossing' | 'exit-crossing'`: - -```typescript -// WRONG: -rangeStart: { name: 'enter', ... } -// CORRECT: -rangeStart: { name: 'entry', ... } -``` - -### [viewenter.md](packages/interact/rules/viewenter.md) - -- **FOUC section (lines ~887-941)**: Contains broken/duplicated markdown with nested code blocks. The section has an incomplete `InteractConfig` declaration followed by a duplicate `generate` usage block. Clean up to a single well-formed code block. -- **FOUC import path (line ~895)**: Uses `import { generate } from '@wix/interact'` -- should be `'@wix/interact/web'` per the code and [full-lean.md](packages/interact/rules/full-lean.md). - -### Multiple files: `customEffect` signature - -- **viewprogress.md** (Rules 7, 8, 9): Patterns show `(element, progress, params)` with 3 parameters -- **scroll-list.md** (Rule 7): Pattern shows `(element, progress, params)` with 3 parameters - -Per [types.ts](packages/interact/src/types.ts) line 90, the actual signature is `(element: Element, progress: any) => void` (2 parameters). Remove the third `params` parameter from all patterns. - ---- - -## 2. Add Missing Triggers and Parameters - -### [full-lean.md](packages/interact/rules/full-lean.md) - -- **Trigger list (line ~136)**: Add `'activate'` and `'interest'` triggers. Per [handlers/index.ts](packages/interact/src/handlers/index.ts): - - `activate`: Same as `click` but with `allowA11yTriggers: true` (adds keyboard Space/Enter listeners, sets `tabIndex=0`) - - `interest`: Same as `hover` but with `allowA11yTriggers: true` (adds focusin/focusout listeners) -- **Params section**: Add activate/interest params (same as click/hover respectively) -- **ViewEnterParams type (line ~156)**: Add `'state'` to the type list: `type?: 'once' | 'repeat' | 'alternate' | 'state'`. The `state` type is documented in [viewenter.md](packages/interact/rules/viewenter.md) Rule 4 and supported in [viewEnter.ts](packages/interact/src/handlers/viewEnter.ts). -- Add `'state'` description: plays on entry, pauses on exit (for looping/continuous animations). -- **PointerMoveParams (line ~170)**: Add `axis?: 'x' | 'y'` parameter. Already documented in [pointermove.md](packages/interact/rules/pointermove.md) Rules 10-11 but missing from the central reference. Note: `axis` selects which pointer coordinate maps to linear 0-1 progress for `keyframeEffect`; defaults to `'y'`. - -### [integration.md](packages/interact/rules/integration.md) - -- **Triggers table (lines 178-186)**: Add missing triggers: - - `activate` -- Accessible click (click + keyboard Space/Enter) - - `interest` -- Accessible hover (hover + focus) - ---- - -## 3. Consistency Improvements - -### Variable naming standardization across rule files - -- **[viewenter.md](packages/interact/rules/viewenter.md)**: Uses `[SOURCE_SELECTOR]`, `[TARGET_SELECTOR]`, `[OBSERVER_SELECTOR]` -- these are element keys (`data-interact-key` values), not CSS selectors. Rename to `[SOURCE_KEY]`, `[TARGET_KEY]`, `[OBSERVER_KEY]` for consistency with [pointermove.md](packages/interact/rules/pointermove.md) which correctly uses `[SOURCE_KEY]`. -- **[viewprogress.md](packages/interact/rules/viewprogress.md)**: Same issue -- rename `[SOURCE_SELECTOR]`/`[TARGET_SELECTOR]` to `[SOURCE_KEY]`/`[TARGET_KEY]`. -- **[scroll-list.md](packages/interact/rules/scroll-list.md)**: Uses a mix (`[CONTAINER_SELECTOR]` vs `[ITEM_KEY]`) -- standardize to `KEY` suffix for interact key values, reserving `SELECTOR` for actual CSS selector fields like `selector` or `listItemSelector`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 35c50640..b92a7294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## @wix/interact +### [2.0.2] - 2026-02-26 + +#### Fixed + +- Move to wix org on GH (#126) + +## @wix/interact + ### [2.0.1] - 2026-02-16 #### Fixed diff --git a/packages/motion/src/Sequence.ts b/packages/motion/src/Sequence.ts new file mode 100644 index 00000000..488db1c7 --- /dev/null +++ b/packages/motion/src/Sequence.ts @@ -0,0 +1,75 @@ +import { AnimationGroup } from './AnimationGroup'; +import type { AnimationOptions, SequenceOptions } from './types'; +import { calculateOffsets, resolveEasingFunction } from './utils'; +import { getAnimation } from './motion'; + +export class Sequence extends AnimationGroup { + animationGroups: AnimationGroup[]; + sequenceDelay: number; + offset: number; + offsetEasing: (t: number) => number; + private _calculatedOffsets: number[]; + + constructor(animationGroups: AnimationGroup[], options?: SequenceOptions) { + const allAnimations = animationGroups.flatMap((group) => group.animations); + super(allAnimations, options); + + this.animationGroups = animationGroups; + this.sequenceDelay = options?.delay ?? 0; + this.offset = options?.offset ?? 0; + this.offsetEasing = resolveEasingFunction(options?.offsetEasing); + this._calculatedOffsets = calculateOffsets( + animationGroups.length, + this.offset, + this.offsetEasing, + ); + this._applyDelays(); + } + + private _applyDelays(): void { + const minOffset = Math.min(...this._calculatedOffsets); + const maxOffset = Math.max(...this._calculatedOffsets); + const totalSpan = maxOffset - minOffset; + + this.animationGroups.forEach((group, index) => { + // Normalize offset to be non-negative + const normalizedOffset = + minOffset < 0 ? this._calculatedOffsets[index] - minOffset : this._calculatedOffsets[index]; + const groupDelay = this.sequenceDelay + normalizedOffset; + const endDelay = totalSpan - normalizedOffset; + group.addGroupDelay(groupDelay, endDelay); + }); + } + + getOffsetAt(index: number): number { + return this._calculatedOffsets[index] ?? 0; + } + + getOffsets(): number[] { + return [...this._calculatedOffsets]; + } + + recalculateOffsets(): void { + this._calculatedOffsets = calculateOffsets( + this.animationGroups.length, + this.offset, + this.offsetEasing, + ); + } + + static build( + configs: Array<{ target: HTMLElement | string | null; animationOptions: AnimationOptions }>, + sequenceOptions?: SequenceOptions, + reducedMotion: boolean = false, + ): Sequence | null { + const groups = configs + .map((cfg) => getAnimation(cfg.target, cfg.animationOptions, undefined, reducedMotion)) + .filter((a): a is AnimationGroup => a instanceof AnimationGroup); + return groups.length ? new Sequence(groups, sequenceOptions) : null; + } + + // Note: play(), pause(), reverse(), cancel(), setPlaybackRate(), onFinish(), + // finished, and playState are inherited from AnimationGroup. + // Since we pass all flattened animations to super(), the parent's + // implementations work correctly on the same Animation objects. +} From df79f099fae03d318dd51e0af7df0923a539027b Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Thu, 26 Feb 2026 19:41:32 +0200 Subject: [PATCH 2/2] Delete packages/motion/src/Sequence.ts Remove file from another PR --- packages/motion/src/Sequence.ts | 75 --------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 packages/motion/src/Sequence.ts diff --git a/packages/motion/src/Sequence.ts b/packages/motion/src/Sequence.ts deleted file mode 100644 index 488db1c7..00000000 --- a/packages/motion/src/Sequence.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AnimationGroup } from './AnimationGroup'; -import type { AnimationOptions, SequenceOptions } from './types'; -import { calculateOffsets, resolveEasingFunction } from './utils'; -import { getAnimation } from './motion'; - -export class Sequence extends AnimationGroup { - animationGroups: AnimationGroup[]; - sequenceDelay: number; - offset: number; - offsetEasing: (t: number) => number; - private _calculatedOffsets: number[]; - - constructor(animationGroups: AnimationGroup[], options?: SequenceOptions) { - const allAnimations = animationGroups.flatMap((group) => group.animations); - super(allAnimations, options); - - this.animationGroups = animationGroups; - this.sequenceDelay = options?.delay ?? 0; - this.offset = options?.offset ?? 0; - this.offsetEasing = resolveEasingFunction(options?.offsetEasing); - this._calculatedOffsets = calculateOffsets( - animationGroups.length, - this.offset, - this.offsetEasing, - ); - this._applyDelays(); - } - - private _applyDelays(): void { - const minOffset = Math.min(...this._calculatedOffsets); - const maxOffset = Math.max(...this._calculatedOffsets); - const totalSpan = maxOffset - minOffset; - - this.animationGroups.forEach((group, index) => { - // Normalize offset to be non-negative - const normalizedOffset = - minOffset < 0 ? this._calculatedOffsets[index] - minOffset : this._calculatedOffsets[index]; - const groupDelay = this.sequenceDelay + normalizedOffset; - const endDelay = totalSpan - normalizedOffset; - group.addGroupDelay(groupDelay, endDelay); - }); - } - - getOffsetAt(index: number): number { - return this._calculatedOffsets[index] ?? 0; - } - - getOffsets(): number[] { - return [...this._calculatedOffsets]; - } - - recalculateOffsets(): void { - this._calculatedOffsets = calculateOffsets( - this.animationGroups.length, - this.offset, - this.offsetEasing, - ); - } - - static build( - configs: Array<{ target: HTMLElement | string | null; animationOptions: AnimationOptions }>, - sequenceOptions?: SequenceOptions, - reducedMotion: boolean = false, - ): Sequence | null { - const groups = configs - .map((cfg) => getAnimation(cfg.target, cfg.animationOptions, undefined, reducedMotion)) - .filter((a): a is AnimationGroup => a instanceof AnimationGroup); - return groups.length ? new Sequence(groups, sequenceOptions) : null; - } - - // Note: play(), pause(), reverse(), cancel(), setPlaybackRate(), onFinish(), - // finished, and playState are inherited from AnimationGroup. - // Since we pass all flattened animations to super(), the parent's - // implementations work correctly on the same Animation objects. -}