From 673e66856a0d5e1c7a727415f7f178cfd10f97fb Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Sun, 25 Jan 2026 18:45:17 +0200 Subject: [PATCH 01/29] Add spec and plan for Sequence and clean up old plans --- ...ce_feature_implementation_84c01c97.plan.md | 206 ++++++++ packages/interact/dev/DOCS_PLAN.md | 463 ------------------ packages/interact/dev/LIST_PLAN.md | 212 -------- packages/interact/dev/RULES_PLAN.md | 288 ----------- packages/interact/dev/VIEW_ENTER_SPEC.md | 28 -- packages/interact/dev/sequences-spec.md | 166 +++++++ 6 files changed, 372 insertions(+), 991 deletions(-) create mode 100644 .cursor/plans/sequence_feature_implementation_84c01c97.plan.md delete mode 100644 packages/interact/dev/DOCS_PLAN.md delete mode 100644 packages/interact/dev/LIST_PLAN.md delete mode 100644 packages/interact/dev/RULES_PLAN.md delete mode 100644 packages/interact/dev/VIEW_ENTER_SPEC.md create mode 100644 packages/interact/dev/sequences-spec.md diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md new file mode 100644 index 00000000..de7c5cff --- /dev/null +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -0,0 +1,206 @@ +--- +name: Sequence Feature Implementation +overview: Implement a new Sequence feature in @wix/motion that manages a list of AnimationGroup instances with staggered delays, and integrate it into @eix/interact for declarative configuration. +todos: + - id: motion-sequence-class + content: Create Sequence class in @wix/motion that extends AnimationGroup and manages AnimationGroup instances + status: pending + - id: motion-types + content: Add SequenceOptions type to @wix/motion/types.ts and export from index.ts + status: pending + - id: interact-types + content: Update InteractConfig, Interaction, and InteractCache types to include sequences + status: pending + - id: interact-parse-config + content: Update parseConfig in Interact.ts to process sequence declarations and sequence effects + status: pending + - id: interact-add-sequence + content: Update add.ts to create Sequence instances and apply calculated delay offsets + status: pending + - id: handler-integration + content: Update trigger handlers to work with Sequence instances for coordinated playback + status: pending +isProject: false +--- + +# Sequence Feature Implementation + +This plan implements the Sequence feature as specified in [sequences-spec.md](packages/interact/dev/sequences-spec.md). The feature enables managing multiple Effects as a coordinated timeline with staggered delays. + +## Architecture Overview + +```mermaid +classDiagram + class AnimationGroup { + +animations: Animation[] + +options: AnimationGroupOptions + +ready: Promise + +play() + +pause() + +reverse() + +cancel() + +onFinish() + } + + class Sequence { + +animationGroups: AnimationGroup[] + +delay: number + +offset: number + +offsetEasing: function + +play() + +pause() + +reverse() + +cancel() + +onFinish() + -calculateOffsets() + } + + Sequence --|> AnimationGroup : extends + Sequence "1" --> "*" AnimationGroup : manages +``` + +## Part 1: @wix/motion Package Changes + +### 1.1 Create Sequence Class + +Create new file `packages/motion/src/Sequence.ts`: + +- Extend `AnimationGroup` to inherit the playback control API +- Store `animationGroups: AnimationGroup[]` instead of `animations: Animation[]` +- Add properties: `delay`, `offset`, `offsetEasing` +- Implement `calculateOffsets()` method using the formula from spec: +```typescript +const last = indices.at(-1); +indices.map(n => easing(n / last) * last * offset | 0); +``` + +- Override playback methods (`play`, `pause`, `reverse`, `cancel`) to delegate to child `AnimationGroup` instances +- Apply calculated delay offsets to each effect's animation timing + +### 1.2 Add Sequence Types + +Update `packages/motion/src/types.ts`: + +```typescript +export type SequenceOptions = { + delay?: number; // default 0 + offset?: number; // default 100 + offsetEasing?: string | ((p: number) => number); +}; +``` + +### 1.3 Export Sequence + +Update `packages/motion/src/index.ts` to export: + +- `Sequence` class +- `SequenceOptions` type + +## Part 2: @eix/interact Package Changes + +### 2.1 Update Types + +Update `packages/interact/src/types.ts`: + +```typescript +// New Sequence type +export type Sequence = { + sequenceId?: string; // for referencing reusable sequences + delay?: number; // default 0 + offset?: number; // default 100 + offsetEasing?: string | ((p: number) => number); + effects: (Effect | EffectRef)[]; +}; + +// Update InteractConfig +export type InteractConfig = { + effects: Record; + sequences?: Record; // NEW: reusable sequences + conditions?: Record; + interactions: Interaction[]; +}; + +// Update Interaction +export type Interaction = InteractionTrigger & { + effects: ((Effect | EffectRef) & { interactionId?: string })[]; + sequences?: Sequence[]; // NEW: inline sequences +}; +``` + +### 2.2 Update InteractCache + +Add sequences to the cache structure in `packages/interact/src/types.ts`: + +```typescript +export type InteractCache = { + effects: { [effectId: string]: Effect }; + sequences: { [sequenceId: string]: Sequence }; // NEW + conditions: { [conditionId: string]: Condition }; + interactions: { /* existing structure */ }; +}; +``` + +### 2.3 Update parseConfig Function + +Modify `packages/interact/src/core/Interact.ts`: + +1. Parse `config.sequences` into cache (similar to `config.effects`) +2. Process `interaction.sequences` array: + + - Resolve `sequenceId` references from `config.sequences` + - Process each effect within the sequence + - Generate unique IDs for sequence effects + +3. Track sequence membership for effects (needed for delay calculation) + +### 2.4 Update Effect Processing in add.ts + +Modify `packages/interact/src/core/add.ts`: + +1. When adding interactions, check if effects belong to a sequence +2. Create `Sequence` instance from `@wix/motion` for grouped effects +3. Apply calculated delay offsets based on effect index in sequence +4. Handle sequence removal (when conditions change or elements removed) + +### 2.5 Handler Integration + +Update relevant trigger handlers (e.g., `viewEnter.ts`, `click.ts`) to: + +- Accept `Sequence` instances in addition to individual `AnimationGroup` +- Properly manage sequence lifecycle (play, pause, cancel) + +## Part 3: Offset Calculation Implementation + +The offset calculation follows this algorithm: + +```typescript +function calculateOffsets( + count: number, + offset: number, + easingFn: (t: number) => number +): number[] { + if (count <= 1) return [0]; + + const last = count - 1; + return Array.from({ length: count }, (_, i) => + (easingFn(i / last) * last * offset) | 0 + ); +} +``` + +The calculated offsets are added to each effect's existing `delay` property. + +## Key Implementation Notes + +1. **Initial Scope**: Only `keyframeEffect` and `namedEffect` types (not `customEffect`) +2. **Skip `align` Property**: Per spec, do not implement the `align` property yet +3. **Effect Removal**: When an effect is removed (e.g., condition no longer matches), recalculate delays for remaining effects +4. **Sequence Removal**: Optimize to avoid recalculating when entire sequence is removed +5. **No Element Target**: `Sequence` has no `key` property - targeting is per-effect + +## Testing Strategy + +1. Unit tests for `Sequence` class offset calculations +2. Unit tests for easing function integration +3. Integration tests for sequence parsing in Interact +4. E2E tests for staggered animations with various easing functions \ No newline at end of file diff --git a/packages/interact/dev/DOCS_PLAN.md b/packages/interact/dev/DOCS_PLAN.md deleted file mode 100644 index ae3b5eb2..00000000 --- a/packages/interact/dev/DOCS_PLAN.md +++ /dev/null @@ -1,463 +0,0 @@ -## Context Summary - -**Key Files, Functions, Types & Structures Involved:** - -1. **Main API Files:** - - `src/index.ts` - Public exports: `Interact` class, `add()`, `remove()` functions, and all types - - `src/interact.ts` - Core `Interact` class with static methods and instance management - - `src/types.ts` - Comprehensive type definitions (238 lines) including triggers, effects, configurations - -2. **Core Components:** - - `src/InteractElement.ts` - Custom element implementation with state management - - `src/utils.ts` - Utility functions for ID generation, CSS transition creation, media queries - - `src/handlers/` - Directory with 7 trigger handlers: `click`, `hover`, `viewEnter`, `viewProgress`, `pointerMove`, `animationEnd`, and `utilities` - -3. **Key Types & Interfaces:** - - `InteractConfig` - Main configuration object structure - - `Interaction` - Defines source, trigger, effects relationship - - `Effect` types - `TimeEffect`, `ScrubEffect`, `TransitionEffect` - - `TriggerType` - Union of supported triggers: 'hover', 'click', 'viewEnter', 'pageVisible', 'animationEnd', 'viewProgress', 'pointerMove' - -**Current Data Flow & Observed Patterns:** - -1. **Configuration-driven Architecture:** Users provide `InteractConfig` with effects, conditions, and interactions -2. **Custom Element Integration:** Uses `` with `data-interact-key` attributes -3. **Event Management:** Trigger handlers register/remove DOM event listeners -4. **State Management:** Effects applied via CSS custom states or data attributes -5. **Dependency on @wix/motion:** Integrates with Wix's motion library for animations -6. **Dynamic List Management:** Built-in mutation observers for tracking list item additions/removals -7. **Element Selection Priority:** Sophisticated selector system with `listContainer`, `selector`, and fallback to first child -8. **Effect Cascading:** Condition-based effect layering similar to CSS cascade - -**Reference Implementations/Utilities Found:** - -- `packages/interact-playground/` - Comprehensive examples showing real usage patterns -- Integration with `@wix/motion` for animation effects -- Test suite (`__tests__/interact.spec.ts`) with 1194 lines of test cases showing API usage -- Minimal existing documentation (current README is 9 lines with basic Yoshi links) - -**Potential Challenges, Risks & Considerations:** - -1. **Complex API Surface:** The package exports extensive types and configuration options -2. **Browser Compatibility:** Uses modern APIs like `ElementInternals` with legacy fallbacks -3. **Integration Complexity:** Requires understanding of custom elements, CSS states, and motion library -4. **Configuration Structure:** Multi-level nested configuration can be overwhelming for new users -5. **Missing Documentation:** Currently minimal documentation for a feature-rich package - ---- - -## Phase 2: Documentation Plan for @wix/interact Package - -Based on my comprehensive analysis, I'll create a detailed plan for writing comprehensive documentation for the `@interact` package. - -### **Stage 1: Foundation Documentation Structure** - -**What:** Create the core documentation framework and main README -**Where:** - -- Update `packages/interact/README.md` -- Create `packages/interact/docs/` directory structure - **Why:** Establish clear information architecture and entry points for different user types - -**Deliverables:** - -- Enhanced main README with quick start, installation, and overview -- Documentation directory structure following monorepo patterns -- Table of contents and navigation structure - -### **Stage 2: API Reference Documentation** - -**What:** Comprehensive API documentation for all public interfaces -**Where:** `packages/interact/docs/api/` directory -**Why:** Developers need detailed reference documentation for all classes, methods, and types - -**Deliverables:** - -- `Interact` class documentation (static methods, instance methods, properties) - - Document `Interact.forceReducedMotion` static property -- Function documentation (`add()`, `remove()`) - - Add `addListItems(root, key, listContainer, elements)` documentation - - Add `removeListItems(elements)` documentation -- Complete type definitions with examples -- Configuration object schemas and validation rules -- Custom element API (``) - - Document `watchChildList(listContainer)` method -- Element selection priority documentation - - `listContainer` + `selector` combinations - - Resolution order and fallback behavior - -### **Stage 3: Concept Guides** - -**What:** Educational content explaining core concepts and architecture -**Where:** `packages/interact/docs/guides/` directory -**Why:** Users need to understand the mental model and design philosophy - -**Deliverables:** - -- "Getting Started" tutorial with first interaction ✅ (complete) -- "Understanding Triggers" - all 7 trigger types with examples (expand/complete) -- "Effects and Animations" - integration with @wix/motion ✅ (complete) -- "Configuration Structure" - nested config organization ✅ (complete) -- "Custom Elements" - how interact-element works ✅ (complete) -- "State Management" - CSS states vs data attributes (complete/expand) -- "Conditions and Media Queries" - responsive interactions ✅ (complete) -- **NEW: "Lists and Dynamic Content"** - Working with dynamic lists (HIGH PRIORITY) - - When to use `listContainer` vs multiple elements - - How mutation observers track list changes automatically - - Staggered animations for list items - - Performance considerations for large lists - - Examples: animated galleries, dynamic to-do lists, filtered product grids - - Add/remove item animations - - Infinite scroll integration patterns - -### **Stage 4: Usage Examples and Patterns** - -**What:** Practical examples and common patterns -**Where:** `packages/interact/docs/examples/` directory -**Why:** Developers learn best from working examples and common use cases - -**Deliverables:** - -- Prefer using examples of Effects using `keyframeEffect` over `namedEffect` -- Basic examples for each trigger type (extracted from playground) -- Advanced patterns (chaining animations, conditional effects) -- Integration examples (with React, vanilla JS) -- Performance optimization patterns -- Common pitfalls and troubleshooting - -**NEW: Expanded Example Categories:** - -- **List Patterns** (HIGH PRIORITY): - - Staggered entrance animations for list items (10+ variations) - - Add/remove item animations with smooth transitions - - Sorting and filtering with animated transitions - - Infinite scroll integration patterns - - Grid-to-list layout transitions - - Drag-and-drop with visual feedback -- **Selector Pattern Examples:** - - Complex component targeting strategies - - `listContainer` + `selector` combinations with real use cases - - Dynamic selector strategies for SPAs - - Nested component interactions - - Cross-element targeting patterns - -- **Effect Cascading Examples:** - - Responsive effect variations using effect-level conditions - - Mobile-first progressive enhancement patterns - - Graceful degradation with accessibility - - Theme-aware animations (light/dark mode) - - Performance-based effect selection - -- **Code Quality Standards:** - - All examples include TypeScript types - - Both vanilla JS and React versions - - Before/after HTML structure shown - - Common gotchas documented as comments - - Copy-paste ready with zero modifications needed - -### **Stage 5: Migration and Integration Guides** - -**What:** Guides for adopting and integrating the package -**Where:** `packages/interact/docs/integration/` directory -**Why:** Teams need guidance on how to adopt this into existing projects - -**Deliverables:** - -- Integration with different frameworks (React, vanilla JS, Vue, Angular, Svelte) -- Migration from other animation libraries (GSAP, Framer Motion, CSS-only) - - Side-by-side comparison examples - - Feature mapping tables - - Migration checklists -- Best practices for performance -- Testing strategies for interactions -- Debugging and development tools - -**NEW: Server-Side Rendering (SSR) Guide** (HIGH PRIORITY): - -- Hydration strategies and timing -- When to initialize Interact (useEffect, componentDidMount, etc.) -- Avoiding hydration mismatches -- Next.js integration patterns - - App Router (React Server Components) - - Pages Router - - Client component boundaries -- Remix integration -- SvelteKit integration -- Framework-specific list handling with SSR -- Progressive enhancement patterns for SSR - -### **Stage 6: Advanced Topics** - -**What:** Deep-dive technical documentation -**Where:** `packages/interact/docs/advanced/` directory -**Why:** Power users and contributors need detailed technical information - -**Deliverables:** - -- Architecture overview and design decisions -- Custom trigger development guide -- Browser compatibility and polyfills -- Performance optimization techniques -- Contributing guidelines - -### **Implementation Priority Levels** - -Given that significant documentation already exists, prioritize work as follows: - -**🔴 High Priority (Complete First):** - -1. Lists and Dynamic Content guide (new, critical missing feature) -2. List management API documentation (`addListItems`, `removeListItems`, `watchChildList`) -3. SSR integration patterns (common pain point for modern frameworks) -4. Element selection priority flowchart/decision tree -5. List pattern examples (10+ variations) -6. Complete "Understanding Triggers" guide (expand existing) -7. Expand "State Management" guide with list-specific patterns - -**🟡 Medium Priority:** 8. Selector pattern examples (complex targeting strategies) 9. Effect cascading examples with conditions 10. Migration guides from GSAP/Framer Motion 11. Framework-specific integration examples (Vue, Angular) 12. Performance optimization patterns for lists 13. Troubleshooting sections for each major feature - -**🟢 Lower Priority:** 14. Advanced topics (custom trigger development) 15. Browser compatibility deep-dives 16. Contributing guidelines 17. Experimental features documentation - -### **Check-in Points:** - -1. **After High Priority Items:** Review with team and early adopters -2. **After Stage 3:** Validate concept explanations with subject matter experts -3. **After Stage 4:** Test examples with actual users/developers (must be copy-paste ready) -4. **After Stage 5:** Validate SSR patterns with framework experts -5. **After Stage 6:** Final review for completeness and accuracy - -### **Success Criteria:** - -**User Experience Metrics:** - -- ✅ New developers can get first interaction working in under 10 minutes -- ✅ Developer can implement a staggered list animation in under 5 minutes -- ✅ Zero ambiguity in element selection priority behavior (flowchart/decision tree) -- ✅ All example code runs without modification (copy-paste ready) -- ✅ SSR/hydration works first time for Next.js, Remix, SvelteKit - -**Documentation Coverage:** - -- ✅ All public APIs have comprehensive documentation with examples -- ✅ All 7 trigger types fully documented with real-world examples -- ✅ List management features completely documented (mutation observers, etc.) -- ✅ Common use cases are covered with copy-paste examples -- ✅ Advanced users can extend the system with custom triggers -- ✅ Common framework integrations (React, Vue, Vanilla) all documented - -**Quality Standards:** - -- ✅ Performance best practices clearly stated for each pattern -- ✅ Accessibility considerations documented for all interaction types -- ✅ Browser compatibility clearly stated with workarounds -- ✅ Troubleshooting section for each major feature -- ✅ Migration paths from GSAP/Framer Motion documented -- ✅ Documentation follows monorepo standards and patterns - -**Validation Tests:** - -- ✅ 5+ external developers test documentation and provide feedback -- ✅ All code examples validated in TypeScript strict mode -- ✅ Examples tested in React, Vue, and vanilla JS environments -- ✅ SSR patterns validated in production-like environments - -### **Documentation Tools & Format:** - -- Markdown files for consistency with monorepo -- TypeScript code examples with syntax highlighting -- Interactive examples linking to playground -- Auto-generated API docs from TypeScript comments -- Clear cross-references between related concepts - ---- - -## **Identified Content Gaps & Specific Topics to Address** - -### **Critical Missing Documentation** - -1. **`listContainer` Feature** (🔴 High Priority) - - ```typescript - // This pattern is used throughout the codebase but poorly documented - { - key: 'gallery', - listContainer: '.gallery-grid', // Selects container - selector: '.gallery-item img', // Selects within each child - trigger: 'hover', - effects: [/* ... */] - } - ``` - - **Required Coverage:** - - How `listContainer` works with mutation observers - - When to use vs multiple separate elements - - Performance implications of list vs individual elements - - Staggered animation patterns - - Memory management for large lists - -2. **Element Selection Priority** (🔴 High Priority) - - **Resolution Order (needs clear flowchart):** - - ``` - 1. listContainer specified? - → Yes: Match children using selector (or all immediate children) - → No: Continue to step 2 - - 2. selector specified? - → Yes: querySelector within interact-element - → No: Use first child element (fallback) - ``` - - **Edge Cases to Document:** - - What happens when `listContainer` + `selector` matches nothing? - - Inheritance behavior from Interaction to Effect - - Selector specificity and combinators - -3. **Dynamic List Management** (🔴 High Priority) - - **API Methods Missing Docs:** - - `watchChildList(listContainer: string): void` - - `addListItems(root, key, listContainer, elements)` - - `removeListItems(elements)` - - `_observers` WeakMap structure - - **Behavior to Document:** - - Automatic mutation tracking setup - - When observers are created/destroyed - - Performance with rapid DOM changes - - Interaction with frameworks' virtual DOM - -4. **Effect Cascading with Conditions** (🟡 Medium Priority) - - **Pattern to Document:** - - ```typescript - // Effects cascade like CSS - last matching wins - effects: [ - { - key: 'card', - effectId: 'slide-mobile', - conditions: ['mobile'], // Applies on mobile - }, - { - key: 'card', - effectId: 'slide-tablet', - conditions: ['tablet'], // Overrides on tablet - }, - { - key: 'card', - effectId: 'slide-desktop', - conditions: ['desktop'], // Overrides on desktop - }, - ]; - ``` - -5. **SSR and Hydration** (🔴 High Priority) - - **Framework Patterns Needed:** - - Next.js App Router (use client boundaries) - - Next.js Pages Router (useEffect timing) - - Remix (clientLoader vs useEffect) - - SvelteKit (onMount timing) - - Common hydration pitfalls - -6. **Performance Best Practices** - - **Topics to Cover:** - - When to use `listContainer` (100+ items → use listContainer) - - Transform/opacity vs layout properties - - Event delegation considerations - - Memory implications of many interactions - - RequestAnimationFrame usage in handlers - -### **Documentation Quality Improvements Needed** - -1. **Add Troubleshooting Sections to Each Guide:** - - Common error messages and solutions - - Debug checklist with specific steps - - Browser DevTools integration tips - - Performance profiling guidance - -2. **Enhance All Code Examples:** - - ```typescript - // ✅ GOOD: Complete, typed, copy-paste ready - import { Interact, type InteractConfig } from '@wix/interact'; - - const config: InteractConfig = { - interactions: [ - { - key: 'my-button', - trigger: 'hover', - effects: [ - /* ... */ - ], - }, - ], - }; - - // Initialize in useEffect for React - useEffect(() => { - const instance = Interact.create(config); - return () => { - // Cleanup if needed - }; - }, []); - ``` - - **Every example should include:** - - TypeScript types - - Framework context (vanilla/React/Vue) - - Before/after HTML structure - - Common gotchas as comments - -3. **Add Migration Guides:** - - | From | To @wix/interact | Difficulty | - | ---------------------- | --------------------- | ---------- | - | GSAP Timeline | Chained interactions | Medium | - | Framer Motion variants | Effect configurations | Easy | - | CSS animations only | Time effects | Easy | - | IntersectionObserver | viewEnter trigger | Easy | - | ScrollTrigger | viewProgress trigger | Medium | - -### **Quick Reference Additions Needed** - -1. **Decision Trees/Flowcharts:** - - When to use which trigger type - - Element selection priority flowchart - - listContainer vs multiple elements decision tree - - Effect type selection guide - -2. **Comparison Tables:** - - Trigger types feature comparison - - Effect types capabilities matrix - - Browser support matrix - - Framework integration approaches - -3. **Cheat Sheets:** - - Common patterns quick reference - - Selector syntax examples - - Configuration templates - - TypeScript type helpers - ---- - -## **Next Steps to Execute Plan** - -**Phase 1: High Priority Documentation (Week 1-2)** - -1. Create "Lists and Dynamic Content" guide -2. Document list management APIs -3. Create element selection flowchart -4. Write SSR integration guide -5. Create 10+ list animation examples - -**Phase 2: Medium Priority Completion (Week 3-4)** 6. Complete "Understanding Triggers" guide 7. Expand selector pattern examples 8. Add troubleshooting sections 9. Create migration guides 10. Framework integration examples - -**Phase 3: Quality & Polish (Week 5-6)** 11. Validate all examples in strict TypeScript 12. Test with external developers 13. Add performance profiling guides 14. Create quick reference cards 15. Final review and team feedback - ---- - -**Ready to begin? Start with High Priority Item #1: Lists and Dynamic Content guide.** diff --git a/packages/interact/dev/LIST_PLAN.md b/packages/interact/dev/LIST_PLAN.md deleted file mode 100644 index 1b0cbb53..00000000 --- a/packages/interact/dev/LIST_PLAN.md +++ /dev/null @@ -1,212 +0,0 @@ -# General Context - -## Available Named Effects for List Animations - -**Entrance Effects (Time-based):** - -- `FadeIn`, `SlideIn`, `BounceIn`, `ExpandIn`, `RevealIn`, `BlurIn` - basic entrance animations -- `ShuttersIn` - specifically supports `staggered: boolean` property for built-in staggering -- `FlipIn`, `SpinIn`, `GlideIn`, `TurnIn` - dynamic entrance effects -- `ShapeIn` - shape-based reveals with direction control - -**Scroll Effects (Progress-based):** - -- `FadeScroll`, `BlurScroll`, `SlideScroll`, `RevealScroll`, `ShuttersScroll`, `ShapeScroll` - revealing scroll-driven animations -- `ParallaxScroll`, `MoveScroll`, `GrowScroll`, `ShrinkScroll`, `PanScroll`, `TurnScroll` - movement-based scroll effects -- `SpinScroll`, `TiltScroll`, `ArcScroll`, `Spin3dScroll`, `TiltScroll` - visual transformation effects - -**Background Effects:** - -- `BgParallax`, `BgFade`, `BgPan`, `BgZoom` - container-level background animations - -## Current Architecture Patterns - -**ViewProgress ranges**: `entry`, `cover`, `exit`, `contain` for different scroll phases -**Effect types**: `namedEffect`, `keyframeEffect`, `customEffect` for different complexity levels - -## Common List-based Scroll Animation Patters - -**Sticky Container List Animations** where the container is sticky inside a longer wrapper element, and animated using translation transforms while it's sticky inside the `contain` range -**Sticky Container List Item Animations** where the container is sticky inside a longer wrapper element, and its individual items, or their content, are animated while it's sticky inside the `contain` range using different offsets per item -**Sticky Item List Animations** where the items are sticky inside the container, and are animated into view, or in and out of view, using various effects like transforms, clip-path, or opacity, while using either the container as the timeline with different ranges, or each item uses itself as the timeline with any range, depending on the effect -**Sticky Item List Items' Contents Animations** where either the container or the items are sticky, and content inside the items use their containing item as the timeline for animations applied to them -**Animating while content is stuck in view** uses the `contain` range -**Animating while content enters view** uses the `entry` range -**Animating while content exits view** uses the `exit` range - ---- - -# **LIST SCROLL ANIMATION RULES GENERATION PLAN** - -## **Stage 1: Core List Animation Rule Categories (Foundation)** - -### **1.1 Sticky Container List Animations** - -- **Objective**: Create rules for animations applied to list containers that are sticky-positioned within their wrapper -- **Scope**: Container-level effects during sticky scroll phases -- **Key Patterns**: - - Container horizontal sliding during sticky phase - - Container parallax effects during sticky phase - - Container reveal transitions - - Background transformations synchronized with scroll - -### **1.2 Sticky Item List Animations** - -- **Objective**: Generate rules for animations on individual list items that are sticky-positioned within the container -- **Scope**: Item-level effects as items enter/exit sticky positioning -- **Key Patterns**: - - Item entrance/exit animations - - Progressive item reveals - - Item transformation during sticky phases - -### **1.3 Sticky Item List Content Animations** - -- **Objective**: Generate rules for animations on content of individual list items that are sticky-positioned within the container -- **Scope**: Coordinated animations with calculated delays -- **Key Patterns**: - - Entry staggering (sequential reveals) - - Exit staggering (sequential dismissals) - - Wave-like animation propagation - -## **Stage 2: Named Effect Rule Patterns (20 Rules)** - -### **2.1 Container-Level Named Effects (5 Rules)** - -- **Container Parallax Rule**: `BgParallax`, `BgReveal` for sticky container backgrounds -- **Container Transform Rule**: `PanScroll` for container-level horizontal transformation -- **Container Background Rule**: `BgPan`, `BgSkew` for dynamic backgrounds - -### **2.2 List Item Named Effects (10 Rules)** - -- **Item Entrance/Exit Rules**: `FadeScroll`, `SlideScroll`, `ShapeScroll`, `RevealScroll`, `MoveScroll`, `ShuttersScroll` for item reveals -- **Item Transformation Rules**: `GrowScroll`, `ShrinkScroll`, `SpinScroll`, `ArcScroll` for item scaling/rotation - -### **2.3 Content-Level Named Effects (5 Rules)** - -- **Content Reveal Rules**: `RevealScroll`, `FadeScroll` for text/image reveals within items -- **Content Motion Rules**: `TiltScroll`, `FlipScroll` for content dynamics -- **Content Progressive Rules**: Using entrance effects (`GrowScroll`, `FadeScroll`) triggered by scroll progress -- **Content Interactive Rules**: Combined hover + scroll effects for rich interactions - -## **Stage 3: Keyframe Effect Rule Patterns (15 Rules)** - -### **3.1 Custom Container Keyframes (5 Rules)** - -- **Multi-Property Container**: Combined transform, opacity, filter effects -- **Responsive Container**: Different keyframes for mobile vs. desktop -- **Timed Container**: Sequential keyframe phases during sticky periods -- **Background Container**: Complex background position/filter combinations -- **Layout Container**: Subtle spacing/padding adjustments during scroll - -### **3.2 Custom Item Keyframes (7 Rules)** - -- **Complex Item Entrance**: Multi-property entrance with unique timing -- **Item Position Flow**: Advanced positioning with perspective effects -- **Item Content Coordination**: Synchronizing item wrapper with content animations -- **Item Responsive**: Adaptive keyframes based on screen size -- **Item Interaction**: Hover-enhanced scroll effects -- **Item Exit Sequence**: Complex multi-stage exit animations -- **Item State Transition**: Animations between different list states - -### **3.3 Custom Content Keyframes (3 Rules)** - -- **Text Progressive**: Letter-by-letter or word-by-word reveals -- **Image Complex**: Multi-layer image animations with masks/filters -- **Media Synchronized**: Video/audio content synchronized with scroll - -## **Stage 4: Custom Effect Rule Patterns (12 Rules)** - -### **4.1 Stagger Calculation Rules (4 Rules)** - -- **Linear Stagger**: Even delays across all items `(index / total) * maxDelay` -- **Exponential Stagger**: Accelerating delays `Math.pow(index / total, 2) * maxDelay` -- **Wave Stagger**: Sine wave-based delays for organic feeling -- **Reverse Stagger**: Reverse-order animations for exit effects - -### **4.2 Dynamic Content Rules (4 Rules)** - -- **Counter Animation**: Scroll-driven number counting within items -- **Progress Tracking**: Visual progress indicators within list context -- **Interactive Data**: Data visualization updates during scroll -- **Dynamic Text**: Text content that changes based on scroll position - -### **4.3 Complex Coordination Rules (4 Rules)** - -- **Multi-Layer Lists**: Coordinating background, item, and content layers -- **Cross-Item Effects**: Items affecting neighboring items during animation -- **Conditional Staggering**: Different stagger patterns based on conditions -- **Adaptive Performance**: Reducing complexity based on list length/performance - -## **Stage 5: Configuration Factory Patterns (8 Factories)** - -### **5.1 List Type Factories (4 Factories)** - -- **`createStickyListConfig`**: For sticky-positioned list containers -- **`createStickyItemConfig`**: For lists with sticky-positioned items -- **`createScrollListConfig`**: For standard scroll-through lists -- **`createInfiniteListConfig`**: For infinite scroll list patterns - -### **5.2 Animation Style Factories (4 Factories)** - -- **`createStaggeredListConfig`**: Parameterized stagger generation -- **`createParallaxListConfig`**: Multi-layer parallax list effects -- **`createProgressiveListConfig`**: Progressive reveal/hide patterns -- **`createInteractiveListConfig`**: Combined hover/scroll list interactions - -## **Stage 6: Integration & Responsive Patterns (5 Rules)** - -### **6.1 Responsive Adaptation (3 Rules)** - -- **Mobile-First Lists**: Simplified animations for touch devices -- **Desktop Enhancement**: Complex effects for desktop hover/scroll -- **Performance Scaling**: Adaptive complexity based on device capabilities - -### **6.2 Accessibility & Performance (2 Rules)** - -- **Reduced Motion Support**: Alternative patterns for `prefers-reduced-motion` -- **Performance Optimization**: Hardware acceleration and efficient property usage - -## **Stage 7: Documentation & Examples (Final)** - -### **7.1 Comprehensive Documentation** - -- Complete rule documentation with examples -- Use case guides for different list scenarios -- Performance and accessibility guidelines - -### **7.2 Real-World Example Configurations** - -- E-commerce product lists -- Content feed animations -- Navigation menu dynamics -- Dashboard widget lists - ---- - -# **Success Criteria** - -1. **Comprehensive Coverage**: Rules address all major list animation scenarios (container, items, content) -2. **Performance Optimized**: All rules use hardware-accelerated properties and efficient patterns -3. **Responsive Ready**: Rules include mobile/desktop variations and adaptive complexity -4. **Developer Friendly**: Configuration factories reduce boilerplate and enable quick implementation -5. **Accessibility Compliant**: All rules respect motion preferences and performance constraints - ---- - -# **Implementation Approach** - -The plan follows a layered approach: - -- **Foundation** (Stage 1): Core categories and architectural patterns -- **Named Effects** (Stage 2): Leveraging existing @wix/motion effects with list-specific patterns -- **Custom Keyframes** (Stage 3): Precise control for unique list behaviors -- **Advanced Custom** (Stage 4): JavaScript-powered complex interactions -- **Factories** (Stage 5): Reusable configuration generators -- **Integration** (Stage 6): Responsive and accessibility considerations -- **Documentation** (Stage 7): Complete implementation guidance - -Each stage builds upon previous stages, ensuring comprehensive coverage while maintaining practical usability for developers implementing list scroll animations. - ---- - -**Switch to agent mode and type execute (or execute stage 1) to begin.** diff --git a/packages/interact/dev/RULES_PLAN.md b/packages/interact/dev/RULES_PLAN.md deleted file mode 100644 index 4f51c477..00000000 --- a/packages/interact/dev/RULES_PLAN.md +++ /dev/null @@ -1,288 +0,0 @@ -## Phase 1: Context Summary - -**Tools Utilized & Key Discoveries:** - -- Used `read_file` on core files (`interact.ts`, `types.ts`, `InteractElement.ts`, `utils.ts`) to understand the main API structure and configuration patterns -- Used `list_dir` to explore package structure and handlers organization, discovering modular trigger system -- Used `file_search` to find examples documentation with common usage patterns -- Used `read_file` on motion types and handler implementations to understand effect integration patterns - -**Key Files, Functions, Types & Structures Involved:** - -1. **Core API**: `Interact` class with static `create()` method, `InteractConfig` as main configuration object -2. **Core Types**: - - `InteractConfig` structure: `{effects, conditions?, interactions}` - - `Interaction`: `{key, selector? listContainer?, trigger, params?, conditions?, effects}` - - Effect types: `TimeEffect`, `ScrubEffect`, `TransitionEffect` with union `Effect` - - `TriggerType`: 7 types (`hover`, `click`, `viewEnter`, `pageVisible`, `animationEnd`, `viewProgress`, `pointerMove`) -3. **Custom Element**: `` with required `data-interact-key` attribute -4. **Handler System**: Modular trigger handlers in `/src/handlers/` with consistent `add/remove` pattern -5. **Motion Integration**: Named effects from `@wix/motion` (FadeIn, SlideIn, etc.) and custom keyframe effects - -**Current Data Flow & Observed Patterns:** - -- Configuration flows through: `InteractConfig` → `parseConfig()` → `InteractCache` → Element registration → Handler binding -- Two effect definition patterns: inline effects vs reusable effects with `effectId` references -- Conditions system enables responsive/conditional interactions using media queries -- Custom element wrapper manages DOM lifecycle and state via CSS custom states -- Template patterns evident in documentation for common use cases - -**Reference Implementations/Utilities Found:** - -- Rich examples in `/docs/examples/README.md` showing entrance animations, hover effects, scroll animations -- Template structures demonstrating configuration patterns -- Modular handler architecture with shared utilities (`generateId`, `createTransitionCSS`) -- Type-safe configuration patterns with comprehensive TypeScript definitions - -**Potential Challenges, Risks & Considerations:** - -- Complex discriminated union types require careful rule generation to maintain type safety -- Multiple effect types (Time/Scrub/Transition) need different generation strategies -- Handler registration lifecycle and cleanup patterns must be preserved -- Integration with motion library's NamedEffect types needs consideration for effect selection -- Custom element key matching requirements between configuration and DOM -- **List-based interactions**: `listContainer` with dynamic mutation observer behavior needs specialized rules -- **Element targeting complexity**: Priority order (listContainer → selector → firstElementChild) and inheritance patterns -- **Selector non-inheritance**: Unlike `key`, `selector` and `listContainer` are NOT inherited from Interaction to Effect level -- **Pointer-driven animations**: hitArea, centeredToTarget, symmetric/anti-symmetric patterns need careful handling - ---- - -## Phase 2: Formulate a Plan - -Based on the comprehensive exploration, here's a detailed plan for creating rules to generate Interact library code: - -### Stage 1: Core Configuration Generation Rules - -**What**: Rules for generating basic `InteractConfig` objects and `Interaction` structures per trigger type -**Where**: Focus on all 7 trigger types with their most common patterns and use cases -**Why**: Each trigger type has distinct patterns, parameters, and typical effects that warrant specialized rules -**How**: Create templates for interactions of specific types with placeholders, without actual effect content - -**Sub-stages (each in separate files):** - -#### Stage 1.1: Hover Trigger Rules (`hover-rules.md`) - -- Rule for hover effect configuration with state management pattern -- Rule for hover enter/leave animations pattern -- Rule for pointer-based hover interactions with alternate pattern -- Rule for pointer-based hover interactions with repeat pattern -- RUle for pointer-based hover interactions with play/pause pattern - -#### Stage 1.2: Click Trigger Rules (`click-rules.md`) - -- Rule for click effect configuration with TimeEffects and alternate pattern -- Rule for click effect configuration with TimeEffects and state pattern -- Rule for click effect configuration with TimeEffects and repeat pattern -- Rule for click effect configuration with state toggles and TransitionEffects pattern - -#### Stage 1.3: ViewEnter Trigger Rules (`viewenter-rules.md`) - -- Rule for entrance animation configuration with once type pattern -- Rule for entrance animation configuration with repeat type and separate source and target pattern -- Rule for entrance animation configuration with alternate type and separate source and target pattern -- Rule for loop animation configuration with state type pattern -- Rule for threshold and viewport intersection parameters pattern -- Rule for staggered entrance animations pattern - -#### Stage 1.4: ViewProgress Trigger Rules (`viewprogress-rules.md`) - -- Rule for range-based parallax/continuous animation control with namedEffects pattern -- Rule for range-based entry animation control with namedEffects pattern -- Rule for range-based exit animation control with namedEffects pattern -- Rule for range-based parallax/continuous animation control with keyframeEffects pattern -- Rule for range-based entry animation control with keyframeEffects pattern -- Rule for range-based exit animation control with keyframeEffects pattern -- Rule for range-based parallax/continuous animation control with customEffects pattern -- Rule for range-based entry animation control with customEffects pattern -- Rule for range-based exit animation control with customEffects pattern - -#### Stage 1.5: PointerMove Trigger Rules (`pointermove-rules.md`) - -- Rule for hit area configuration (`root` vs `self`) with appropriate use cases -- Rule for centering range to animation target using `centeredToTarget` configuration -- Rule for symmetric pointer effects (same animation in both directions) -- Rule for anti-symmetric pointer effects (inverted animation based on direction) -- Rules for pointer-based effects of single elements with namedEffect -- Rules for pointer-based parallax effects of a group of elements with namedEffect - -#### Stage 1.6: AnimationEnd Trigger Rules (`animationend-rules.md`) - -- Rule for configuration of entrance and loop animations chaining pattern -- Rule for configuration of entrance animations chaining of different elements pattern - -#### Stage 1.7: PageVisible Trigger Rules (`pagevisible-rules.md`) - -- Rule for page load animations -- Rule for global state management -- Rule for one-time initialization effects - -#### Stage 1.8: List-Based Interaction Rules (`scroll-list-rules.md`) - -- Rule for `listContainer` with automatic child detection (targets all immediate children) -- Rule for `listContainer` + `selector` for targeting specific elements within list items -- Rule for gallery/grid interaction patterns -- Rule for performance optimization with large lists - -**Common Deliverables:** - -- Template for base `InteractConfig` structure validation -- Shared utilities for key generation and validation -- Common effect pattern templates - -### Stage 2: Element Targeting and Selector Rules - -**What**: Rules for element targeting using `key`, `selector`, and `listContainer` -**Where**: Both Interaction and Effect levels with proper inheritance/non-inheritance patterns -**Why**: Element targeting is complex with priority order and non-obvious inheritance behavior - -**Deliverables:** - -- Rule for element targeting priority: listContainer → selector → firstElementChild -- Rule for when `key` defaults from Interaction to Effect (when Effect.key is omitted) -- Rule for selector non-inheritance: Effect.selector must be explicit (not inherited from Interaction.selector) -- Rule for listContainer non-inheritance: Effect.listContainer must be explicit -- Rule for cross-element targeting (source ≠ target) patterns -- Rule for self-targeting patterns (source = target or Effect.key omitted) -- Rule for nested selector patterns within custom elements -- Rule for combining listContainer + selector for list item children - -### Stage 3: Effect Generation Rules - -**What**: Rules for generating the three effect types with appropriate defaults -**Where**: Time effects (most common), transition effects, scrub effects -**Why**: Effects are the core value-add and have clear patterns from documentation - -**Deliverables:** - -- Rule for TimeEffect with named effects (FadeIn, SlideIn, etc.) -- Rule for TimeEffect with custom keyframes -- Rule for TransitionEffect with CSS properties -- Rule for ScrubEffect with scroll/pointer triggers -- Effect selection helper based on trigger type -- Rule for inline effects vs reusable effects with `effectId` references - -### Stage 4: Custom Element and DOM Integration Rules - -**What**: Rules for generating proper `` markup and key management -**Where**: HTML/JSX generation with proper `data-interact-key` attributes -**Why**: Required for library function and common source of integration errors - -**Deliverables:** - -- Rule for custom element wrapper generation -- Rule for element key generation and validation (matching data-interact-key to config key) -- Rule for React/JSX integration patterns -- Rule for Vue/Angular custom element usage -- Rule for ensuring at least one child element exists - -### Stage 5: Advanced Configuration Rules - -**What**: Rules for conditions, effect references, and complex patterns -**Where**: Responsive interactions, reusable effects, multistep animations -**Why**: Enables scaling beyond basic use cases - -**Deliverables:** - -- Rule for media query conditions (device size, orientation, reduced motion, dark mode) -- Rule for container query conditions -- Rule for reusable effect patterns with `effectId` -- Rule for animation sequences and chaining (using animationEnd trigger) -- Rule for responsive/conditional interactions with cascading effects -- Rule for combining multiple conditions with AND logic - -### Stage 6: Type Safety and Validation Rules - -**What**: Rules ensuring generated code maintains TypeScript safety -**Where**: Configuration validation, effect property validation, trigger parameter validation -**Why**: Critical for developer experience and catching errors early - -**Deliverables:** - -- Type validation rules for each configuration pattern -- Runtime validation helpers -- Error message generation for common mistakes -- IDE integration patterns -- Discriminated union handling for Effect types -- Parameter validation based on trigger type - -### Stage 7: Integration and Testing Rules - -**What**: Rules for testing generated interactions and integration patterns -**Where**: Unit test generation, E2E test patterns, performance validation -**Why**: Ensures generated code works correctly and performs well - -**Deliverables:** - -- Test case generation for interactions -- Performance validation rules -- Integration pattern validation -- Documentation generation for generated code -- Browser compatibility testing patterns -- Accessibility testing for animations (reduced motion) - ---- - -**Check-in Points:** - -- After Stage 1: Validate all 7 trigger types generate correct configuration patterns -- After Stage 2: Verify element targeting rules handle all selector/listContainer combinations -- After Stage 3: Ensure effect generation covers all three types (Time/Scrub/Transition) -- After Stage 4: Verify DOM integration works correctly with proper key matching -- After Stage 5: Test condition cascading and responsive patterns -- After Stage 6: Ensure type safety is maintained throughout all generated code -- Final: Comprehensive testing of all rule combinations and edge cases - -**Success Criteria:** - -- ✅ Generated code passes TypeScript compilation with no errors -- ✅ Generated interactions work as expected in browser -- ✅ Rules cover 100% of documented example patterns -- ✅ All 7 trigger types have comprehensive rule coverage -- ✅ Element targeting rules correctly handle priority order (listContainer → selector → firstElementChild) -- ✅ Selector and listContainer non-inheritance is properly enforced -- ✅ List-based interactions with mutation observers work correctly -- ✅ Pointer-driven animations support both symmetric and anti-symmetric patterns -- ✅ Conditions system supports media queries, container queries, and cascading -- ✅ AnimationEnd trigger enables proper animation chaining -- ✅ Integration with existing tooling (IDE, linting, testing) -- ✅ Generated code respects accessibility (reduced motion) preferences -- ✅ Performance patterns for large lists and frequent updates are included - ---- - -## Priority Implementation Order - -Based on current progress and critical gaps identified: - -### **Phase 1: Foundation (Completed ✅)** - -- Stage 1.1-1.3: Basic triggers (hover, click, viewEnter) ✅ -- Stage 1.4: ViewProgress rules ✅ -- Stage 1.5: PointerMove rules ✅ - -### **Phase 2: Critical Gaps (HIGH PRIORITY)** - -- **Stage 1.8**: List-Based Interaction Rules - Essential for galleries and grids -- **Stage 2**: Element Targeting Rules - Critical for understanding selector behavior -- **Stage 1.6-1.7**: AnimationEnd and PageVisible triggers - -### **Phase 3: Effect Completeness (MEDIUM PRIORITY)** - -- **Stage 3**: Complete effect generation rules with all three types -- Inline vs reusable effect patterns -- Effect cascading with conditions - -### **Phase 4: Integration & Advanced (LOWER PRIORITY)** - -- **Stage 4**: Custom element and DOM integration -- **Stage 5**: Advanced configuration with conditions -- **Stage 6-7**: Type safety, validation, and testing - -### **Next Immediate Actions:** - -1. Create `scroll-list-rules.md` with listContainer patterns -2. Create element targeting guide explaining selector inheritance/non-inheritance -3. Enhance pointer-move rules with symmetric/anti-symmetric patterns -4. Add animationEnd chaining patterns -5. Document performance optimization for large lists diff --git a/packages/interact/dev/VIEW_ENTER_SPEC.md b/packages/interact/dev/VIEW_ENTER_SPEC.md deleted file mode 100644 index ac3243c0..00000000 --- a/packages/interact/dev/VIEW_ENTER_SPEC.md +++ /dev/null @@ -1,28 +0,0 @@ -# `viewEnter` Beyond `type: 'once'` - -This is specification document for implementing the rest of the values for `type` property of `params` for `trigger: 'viewEnter'`. - -- Currently only `type: once` is implemented for the `viewEnter` trigger -- Other types that should also be implemented are: `alternate`, `repeat`, and `state` -- The initial flow for Interactions with these types are the same as for `once` -- These types should also track when the element exits the range \- `isIntersecting: false` -- We’ll start by using a default exit observer for each type \- without providing an API to specify its options -- We need to make sure we [persist](https://developer.mozilla.org/en-US/docs/Web/API/Animation/persist) the animation on these types. - -# `alternate` - -- When exiting the range the animation should be reversed. -- On subsequent re-entry the animation should be reversed (not play, since it was reversed on last exit) -- By default we can use the same observer as the one for entry. - -# `repeat` - -- By default we can use a separate observer that watches when the element is completely out of view -- When exiting the range the animation should be paused and set its progress to 0 (like “stop”). -- On subsequent re-entry the animation should be played from 0 - -# `state` - -- By default we can use a separate observer that watches when the element is completely out of view -- When exiting the range the animation should be paused. -- On subsequent re-entry the animation should be resumed (calling `.play()` should resume it) diff --git a/packages/interact/dev/sequences-spec.md b/packages/interact/dev/sequences-spec.md new file mode 100644 index 00000000..68f541f0 --- /dev/null +++ b/packages/interact/dev/sequences-spec.md @@ -0,0 +1,166 @@ +# Sequences + +This is a proposal for supporting sequenced effects (also known as “timelines”) for Interact that can be declared using the Config. + +# Technical Design + +## Config spec + +* A Sequence is a list of `Effect`s managed by a single trigger/timeline. +* `Effect`s in a `Sequence` are applied in their specified order inside `Interaction.sequence.effects`. +* Reusable `Sequence`s will be declared using a new `InteractConfig.sequences` property, which is a map of Sequence declarations by a unique key. +* `Sequence`s can be defined on an `Interaction` using a new `Interaction.sequences` property which is a list of `Sequence`s. +* Each Sequence will have an `effects` property which contains its child Effects. +* A `Sequence` does not have a `key` property, nor any of the other element targeting-related properties, since it by itself is not tied to an element. +* The `Effect`s inside a `Sequence` are the objects that define that related target. + +## The new `sequences` property + +```ts +/** + * Reusable Sequence declarations on the InteractConfig top-level + */ +type InteractConfig = { + sequences: {[key: string]: Sequence}; + //... +} + +/** + * Sequence definitions on each Interaction + * Like `effects`, can either reference a declaration using sequenceId + * Or specify values inline, or both and inlined overrides referenced declarations + */ +type Interaction = { + sequences: Sequence[]; + //... +}; + +/** + * The Sequence type + */ +type Sequence = { + delay: number; // default 0 + offset: number; // default 100 + offsetEasing: string | (p: number) => number; // linear + align: 'start' | 'end' | 'sequence' | 'sequence-reversed'; + effects: Effect[] +}; +``` + +## The `Sequence.delay` + +* A fixed offset of milliseconds to delay the playing of the entire Sequence +* Defaults to `0` + +## The `Sequence.offset` + +* A fixed amount of milliseconds to multiply the result of the `easing` function +* Defaults to `100` + +## The `Sequence.offsetEasing` + +* Either a JS function or a valid CSS `easing` value, or a valid `easing` name in `@wix/motion` library + * A JS function takes a `number` from 0 to 1\. + * An `easing` value, either valid from CSS, or in `@wix/motion`, will be translated to the corresponding function in JS or a CSS `calc()`. +* The mapping of each offset using the easing function as done as follows: + +```javascript +// `indices` is the array of indices from 0 to Length-1 +// `easing` is the easing function +// `offset` is the `sequence.offset` property +const last = indices.at(-1); +indices.map(n => easing(n / last) * last * offset | 0) // | 0 is basically flooring +``` + +### Easing examples + +```javascript +const items = [0, 1, 2, 3, 4] +const offset = 200 + +const linear = t => t +// 0, 200, 400, 300, 400 + +const quadIn = t => t ** 2 +// 0, 50, 200, 450, 800 + +const sinOut = t => Math.sin(t * Math.PI / 2) +// 0, 306, 565, 739, 800 +``` + +## The `Sequence.align` + +* **Ignore for now \- DO NOT implement** +* Specifies how to align the Effects inside the Sequence: + * `start` aligns to the beginning + * `end` aligns to the end + * `sequence` aligns each effect’s start to the end of its preceding effect + * `sequence-reverse` is same as `sequence` but starts from the last effect backwards + +# Effect on Effects’ `delay` + +* In initial phase this feature should only apply to `keyframeEffect`s and `namedEffect`s \- where we generate Web or CSS animations +* The result of calculated offset should be added to the Effect’s specified `delay` +* If an Effect is removed (e.g. when an Effect’s `condition` stops matching the state of its environment and needs to be removed) it should propagate to the corresponding Sequence to update the calculated delays +* If an entire Sequence is removed we should try to remove it completely without a significant overhead of propagating each Effect being removed. + +# Implementation + +* Create a new `Sequence` class in `@wix/motion` package that manages a list of `AnimationGroup` instances. +* A `Sequence` instance manages its own playback, similar to `AnimationGroup`, only difference is its `animations` property holds `AnimationGroup` instances. Therefor, it should extend `AnimationGroup` and have a similar API. +* Note that `Sequence` does not have a `target`, so all of its API endpoints that involve an element target should be written accordingly, or not exist if not relevant. +* In the `@eix/interact` package `Sequence`s will be created from an `InteractConfig` for every declaration inside `Interaction.sequences`. + +# Appendix + +## A CSS solution in a futuristic world where CSS math functions are widely supported + +* The index of each Effect in the Sequence and count of Effects in the Sequence are set on each target element. +* Generated `animation-delay` should be a `calc()` that includes the effect’s `delay` \+ the generated staggering offset as follows: + +```css +@property --interact-seq-c { + syntax: ""; + initial-value: 1; + inherits: false; +} + +@property --interact-seq-i { + syntax: ""; + initial-value: 0; + inherits: false; +} + +.target { + --_interact-delay: calc(pow(var(--interact-seq-i) / var(--interact-seq-c), 2)); /* quadIn - this is here for readability, don't actually have to add as a separate property */ + animation-delay: calc(effectDelay + var(--_interact-delay) * var(--interact-seq-c) * ); +} +``` + +```javascript +// According to initial design +{ + interactions: [{ + trigger: 'viewEnter', + sequence: { + offset: 150, + offsetEasing: 'ease-out' + }, + effects: [...] + }] +} + +// According to alternative design +{ + interactions: [{ + trigger: 'viewEnter', + sequences: [{ + offset: 150, + offsetEasing: 'ease-out', + effects: [{ + + }] + }] + }] +} +``` From ddf46333942646e6021e32aed9cb8eea67b08a9e Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Sun, 25 Jan 2026 18:51:39 +0200 Subject: [PATCH 02/29] Fixed review comments --- .../sequence_feature_implementation_84c01c97.plan.md | 2 +- packages/interact/dev/sequences-spec.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md index de7c5cff..ce2fd76f 100644 --- a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -108,7 +108,7 @@ export type Sequence = { sequenceId?: string; // for referencing reusable sequences delay?: number; // default 0 offset?: number; // default 100 - offsetEasing?: string | ((p: number) => number); + offsetEasing?: string | ((p: number) => number); // default linear effects: (Effect | EffectRef)[]; }; diff --git a/packages/interact/dev/sequences-spec.md b/packages/interact/dev/sequences-spec.md index 68f541f0..8fb1123a 100644 --- a/packages/interact/dev/sequences-spec.md +++ b/packages/interact/dev/sequences-spec.md @@ -39,11 +39,11 @@ type Interaction = { * The Sequence type */ type Sequence = { - delay: number; // default 0 - offset: number; // default 100 - offsetEasing: string | (p: number) => number; // linear - align: 'start' | 'end' | 'sequence' | 'sequence-reversed'; - effects: Effect[] + delay?: number; // default 0 + offset?: number; // default 100 + offsetEasing?: string | (p: number) => number; // linear + effects: Effect[]; + sequenceId: string; }; ``` From d3d95643d446a24ff837847a9af5b8c9d5734946 Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Sun, 25 Jan 2026 18:54:19 +0200 Subject: [PATCH 03/29] Fixed type --- .cursor/plans/sequence_feature_implementation_84c01c97.plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md index ce2fd76f..36c6641c 100644 --- a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -105,7 +105,7 @@ Update `packages/interact/src/types.ts`: ```typescript // New Sequence type export type Sequence = { - sequenceId?: string; // for referencing reusable sequences + sequenceId: string; // for referencing reusable sequences delay?: number; // default 0 offset?: number; // default 100 offsetEasing?: string | ((p: number) => number); // default linear From d96d47d9e84c10ae572ce1c3766679d3acf91c2f Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Sun, 25 Jan 2026 19:49:25 +0200 Subject: [PATCH 04/29] Fixed format --- ...ce_feature_implementation_84c01c97.plan.md | 34 +++---- packages/interact/dev/sequences-spec.md | 88 ++++++++++--------- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md index 36c6641c..6f0e2977 100644 --- a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -41,7 +41,7 @@ classDiagram +cancel() +onFinish() } - + class Sequence { +animationGroups: AnimationGroup[] +delay: number @@ -54,7 +54,7 @@ classDiagram +onFinish() -calculateOffsets() } - + Sequence --|> AnimationGroup : extends Sequence "1" --> "*" AnimationGroup : manages ``` @@ -71,7 +71,7 @@ Create new file `packages/motion/src/Sequence.ts`: - Implement `calculateOffsets()` method using the formula from spec: ```typescript const last = indices.at(-1); -indices.map(n => easing(n / last) * last * offset | 0); +indices.map((n) => (easing(n / last) * last * offset) | 0); ``` - Override playback methods (`play`, `pause`, `reverse`, `cancel`) to delegate to child `AnimationGroup` instances @@ -83,8 +83,8 @@ Update `packages/motion/src/types.ts`: ```typescript export type SequenceOptions = { - delay?: number; // default 0 - offset?: number; // default 100 + delay?: number; // default 0 + offset?: number; // default 100 offsetEasing?: string | ((p: number) => number); }; ``` @@ -105,9 +105,9 @@ Update `packages/interact/src/types.ts`: ```typescript // New Sequence type export type Sequence = { - sequenceId: string; // for referencing reusable sequences - delay?: number; // default 0 - offset?: number; // default 100 + sequenceId: string; // for referencing reusable sequences + delay?: number; // default 0 + offset?: number; // default 100 offsetEasing?: string | ((p: number) => number); // default linear effects: (Effect | EffectRef)[]; }; @@ -115,7 +115,7 @@ export type Sequence = { // Update InteractConfig export type InteractConfig = { effects: Record; - sequences?: Record; // NEW: reusable sequences + sequences?: Record; // NEW: reusable sequences conditions?: Record; interactions: Interaction[]; }; @@ -123,7 +123,7 @@ export type InteractConfig = { // Update Interaction export type Interaction = InteractionTrigger & { effects: ((Effect | EffectRef) & { interactionId?: string })[]; - sequences?: Sequence[]; // NEW: inline sequences + sequences?: Sequence[]; // NEW: inline sequences }; ``` @@ -134,9 +134,11 @@ Add sequences to the cache structure in `packages/interact/src/types.ts`: ```typescript export type InteractCache = { effects: { [effectId: string]: Effect }; - sequences: { [sequenceId: string]: Sequence }; // NEW + sequences: { [sequenceId: string]: Sequence }; // NEW conditions: { [conditionId: string]: Condition }; - interactions: { /* existing structure */ }; + interactions: { + /* existing structure */ + }; }; ``` @@ -177,14 +179,12 @@ The offset calculation follows this algorithm: function calculateOffsets( count: number, offset: number, - easingFn: (t: number) => number + easingFn: (t: number) => number, ): number[] { if (count <= 1) return [0]; - + const last = count - 1; - return Array.from({ length: count }, (_, i) => - (easingFn(i / last) * last * offset) | 0 - ); + return Array.from({ length: count }, (_, i) => (easingFn(i / last) * last * offset) | 0); } ``` diff --git a/packages/interact/dev/sequences-spec.md b/packages/interact/dev/sequences-spec.md index 8fb1123a..982a7e9e 100644 --- a/packages/interact/dev/sequences-spec.md +++ b/packages/interact/dev/sequences-spec.md @@ -6,13 +6,13 @@ This is a proposal for supporting sequenced effects (also known as “timelines ## Config spec -* A Sequence is a list of `Effect`s managed by a single trigger/timeline. -* `Effect`s in a `Sequence` are applied in their specified order inside `Interaction.sequence.effects`. -* Reusable `Sequence`s will be declared using a new `InteractConfig.sequences` property, which is a map of Sequence declarations by a unique key. -* `Sequence`s can be defined on an `Interaction` using a new `Interaction.sequences` property which is a list of `Sequence`s. -* Each Sequence will have an `effects` property which contains its child Effects. -* A `Sequence` does not have a `key` property, nor any of the other element targeting-related properties, since it by itself is not tied to an element. -* The `Effect`s inside a `Sequence` are the objects that define that related target. +- A Sequence is a list of `Effect`s managed by a single trigger/timeline. +- `Effect`s in a `Sequence` are applied in their specified order inside `Interaction.sequence.effects`. +- Reusable `Sequence`s will be declared using a new `InteractConfig.sequences` property, which is a map of Sequence declarations by a unique key. +- `Sequence`s can be defined on an `Interaction` using a new `Interaction.sequences` property which is a list of `Sequence`s. +- Each Sequence will have an `effects` property which contains its child Effects. +- A `Sequence` does not have a `key` property, nor any of the other element targeting-related properties, since it by itself is not tied to an element. +- The `Effect`s inside a `Sequence` are the objects that define that related target. ## The new `sequences` property @@ -49,91 +49,95 @@ type Sequence = { ## The `Sequence.delay` -* A fixed offset of milliseconds to delay the playing of the entire Sequence -* Defaults to `0` +- A fixed offset of milliseconds to delay the playing of the entire Sequence +- Defaults to `0` ## The `Sequence.offset` -* A fixed amount of milliseconds to multiply the result of the `easing` function -* Defaults to `100` +- A fixed amount of milliseconds to multiply the result of the `easing` function +- Defaults to `100` ## The `Sequence.offsetEasing` -* Either a JS function or a valid CSS `easing` value, or a valid `easing` name in `@wix/motion` library - * A JS function takes a `number` from 0 to 1\. - * An `easing` value, either valid from CSS, or in `@wix/motion`, will be translated to the corresponding function in JS or a CSS `calc()`. -* The mapping of each offset using the easing function as done as follows: +- Either a JS function or a valid CSS `easing` value, or a valid `easing` name in `@wix/motion` library + - A JS function takes a `number` from 0 to 1\. + - An `easing` value, either valid from CSS, or in `@wix/motion`, will be translated to the corresponding function in JS or a CSS `calc()`. +- The mapping of each offset using the easing function as done as follows: ```javascript // `indices` is the array of indices from 0 to Length-1 // `easing` is the easing function // `offset` is the `sequence.offset` property const last = indices.at(-1); -indices.map(n => easing(n / last) * last * offset | 0) // | 0 is basically flooring +indices.map((n) => (easing(n / last) * last * offset) | 0); // | 0 is basically flooring ``` ### Easing examples ```javascript -const items = [0, 1, 2, 3, 4] -const offset = 200 +const items = [0, 1, 2, 3, 4]; +const offset = 200; -const linear = t => t +const linear = (t) => t; // 0, 200, 400, 300, 400 -const quadIn = t => t ** 2 +const quadIn = (t) => t ** 2; // 0, 50, 200, 450, 800 -const sinOut = t => Math.sin(t * Math.PI / 2) +const sinOut = (t) => Math.sin((t * Math.PI) / 2); // 0, 306, 565, 739, 800 ``` ## The `Sequence.align` -* **Ignore for now \- DO NOT implement** -* Specifies how to align the Effects inside the Sequence: - * `start` aligns to the beginning - * `end` aligns to the end - * `sequence` aligns each effect’s start to the end of its preceding effect - * `sequence-reverse` is same as `sequence` but starts from the last effect backwards +- **Ignore for now \- DO NOT implement** +- Specifies how to align the Effects inside the Sequence: + - `start` aligns to the beginning + - `end` aligns to the end + - `sequence` aligns each effect’s start to the end of its preceding effect + - `sequence-reverse` is same as `sequence` but starts from the last effect backwards # Effect on Effects’ `delay` -* In initial phase this feature should only apply to `keyframeEffect`s and `namedEffect`s \- where we generate Web or CSS animations -* The result of calculated offset should be added to the Effect’s specified `delay` -* If an Effect is removed (e.g. when an Effect’s `condition` stops matching the state of its environment and needs to be removed) it should propagate to the corresponding Sequence to update the calculated delays -* If an entire Sequence is removed we should try to remove it completely without a significant overhead of propagating each Effect being removed. +- In initial phase this feature should only apply to `keyframeEffect`s and `namedEffect`s \- where we generate Web or CSS animations +- The result of calculated offset should be added to the Effect’s specified `delay` +- If an Effect is removed (e.g. when an Effect’s `condition` stops matching the state of its environment and needs to be removed) it should propagate to the corresponding Sequence to update the calculated delays +- If an entire Sequence is removed we should try to remove it completely without a significant overhead of propagating each Effect being removed. # Implementation -* Create a new `Sequence` class in `@wix/motion` package that manages a list of `AnimationGroup` instances. -* A `Sequence` instance manages its own playback, similar to `AnimationGroup`, only difference is its `animations` property holds `AnimationGroup` instances. Therefor, it should extend `AnimationGroup` and have a similar API. -* Note that `Sequence` does not have a `target`, so all of its API endpoints that involve an element target should be written accordingly, or not exist if not relevant. -* In the `@eix/interact` package `Sequence`s will be created from an `InteractConfig` for every declaration inside `Interaction.sequences`. +- Create a new `Sequence` class in `@wix/motion` package that manages a list of `AnimationGroup` instances. +- A `Sequence` instance manages its own playback, similar to `AnimationGroup`, only difference is its `animations` property holds `AnimationGroup` instances. Therefor, it should extend `AnimationGroup` and have a similar API. +- Note that `Sequence` does not have a `target`, so all of its API endpoints that involve an element target should be written accordingly, or not exist if not relevant. +- In the `@eix/interact` package `Sequence`s will be created from an `InteractConfig` for every declaration inside `Interaction.sequences`. # Appendix ## A CSS solution in a futuristic world where CSS math functions are widely supported -* The index of each Effect in the Sequence and count of Effects in the Sequence are set on each target element. -* Generated `animation-delay` should be a `calc()` that includes the effect’s `delay` \+ the generated staggering offset as follows: +- The index of each Effect in the Sequence and count of Effects in the Sequence are set on each target element. +- Generated `animation-delay` should be a `calc()` that includes the effect’s `delay` \+ the generated staggering offset as follows: ```css @property --interact-seq-c { - syntax: ""; + syntax: ''; initial-value: 1; inherits: false; } @property --interact-seq-i { - syntax: ""; + syntax: ''; initial-value: 0; inherits: false; } .target { - --_interact-delay: calc(pow(var(--interact-seq-i) / var(--interact-seq-c), 2)); /* quadIn - this is here for readability, don't actually have to add as a separate property */ - animation-delay: calc(effectDelay + var(--_interact-delay) * var(--interact-seq-c) * ); + --_interact-delay: calc( + pow(var(--interact-seq-i) / var(--interact-seq-c), 2) + ); /* quadIn - this is here for readability, don't actually have to add as a separate property */ + animation-delay: calc( + effectDelay + var(--_interact-delay) * var(--interact-seq-c) * + ); } ``` @@ -158,7 +162,7 @@ const sinOut = t => Math.sin(t * Math.PI / 2) offset: 150, offsetEasing: 'ease-out', effects: [{ - + }] }] }] From 835a015f3ed33f01af604d5f7dc9f7b2b866ee7c Mon Sep 17 00:00:00 2001 From: Yehonatan Daniv Date: Sun, 25 Jan 2026 21:57:21 +0200 Subject: [PATCH 05/29] Fixed format --- .../plans/sequence_feature_implementation_84c01c97.plan.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md index 6f0e2977..cd2d2698 100644 --- a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -69,6 +69,7 @@ Create new file `packages/motion/src/Sequence.ts`: - Store `animationGroups: AnimationGroup[]` instead of `animations: Animation[]` - Add properties: `delay`, `offset`, `offsetEasing` - Implement `calculateOffsets()` method using the formula from spec: + ```typescript const last = indices.at(-1); indices.map((n) => (easing(n / last) * last * offset) | 0); @@ -148,7 +149,6 @@ Modify `packages/interact/src/core/Interact.ts`: 1. Parse `config.sequences` into cache (similar to `config.effects`) 2. Process `interaction.sequences` array: - - Resolve `sequenceId` references from `config.sequences` - Process each effect within the sequence - Generate unique IDs for sequence effects @@ -203,4 +203,4 @@ The calculated offsets are added to each effect's existing `delay` property. 1. Unit tests for `Sequence` class offset calculations 2. Unit tests for easing function integration 3. Integration tests for sequence parsing in Interact -4. E2E tests for staggered animations with various easing functions \ No newline at end of file +4. E2E tests for staggered animations with various easing functions From cf61dd2a8bc0b7236db803289df92af1473b80f0 Mon Sep 17 00:00:00 2001 From: bar-goldenberg Date: Thu, 29 Jan 2026 12:55:28 +0200 Subject: [PATCH 06/29] sequnce implementation --- ...ce_feature_implementation_84c01c97.plan.md | 31 +- apps/demo/src/styles.css | 295 +++++++ apps/demo/src/web/App.tsx | 2 + apps/demo/src/web/components/SequenceDemo.tsx | 246 ++++++ .../demo/src/web/hooks/useInteractInstance.ts | 9 +- packages/interact/src/core/Interact.ts | 145 ++- packages/interact/src/core/add.ts | 2 +- packages/interact/src/types.ts | 68 +- packages/motion/src/Sequence.ts | 307 +++++++ packages/motion/src/index.ts | 2 + packages/motion/src/types.ts | 24 + packages/motion/test/Sequence.spec.ts | 823 ++++++++++++++++++ 12 files changed, 1916 insertions(+), 38 deletions(-) create mode 100644 apps/demo/src/web/components/SequenceDemo.tsx create mode 100644 packages/motion/src/Sequence.ts create mode 100644 packages/motion/test/Sequence.spec.ts diff --git a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md index cd2d2698..ef334875 100644 --- a/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md +++ b/.cursor/plans/sequence_feature_implementation_84c01c97.plan.md @@ -1,28 +1,3 @@ ---- -name: Sequence Feature Implementation -overview: Implement a new Sequence feature in @wix/motion that manages a list of AnimationGroup instances with staggered delays, and integrate it into @eix/interact for declarative configuration. -todos: - - id: motion-sequence-class - content: Create Sequence class in @wix/motion that extends AnimationGroup and manages AnimationGroup instances - status: pending - - id: motion-types - content: Add SequenceOptions type to @wix/motion/types.ts and export from index.ts - status: pending - - id: interact-types - content: Update InteractConfig, Interaction, and InteractCache types to include sequences - status: pending - - id: interact-parse-config - content: Update parseConfig in Interact.ts to process sequence declarations and sequence effects - status: pending - - id: interact-add-sequence - content: Update add.ts to create Sequence instances and apply calculated delay offsets - status: pending - - id: handler-integration - content: Update trigger handlers to work with Sequence instances for coordinated playback - status: pending -isProject: false ---- - # Sequence Feature Implementation This plan implements the Sequence feature as specified in [sequences-spec.md](packages/interact/dev/sequences-spec.md). The feature enables managing multiple Effects as a coordinated timeline with staggered delays. @@ -69,7 +44,6 @@ Create new file `packages/motion/src/Sequence.ts`: - Store `animationGroups: AnimationGroup[]` instead of `animations: Animation[]` - Add properties: `delay`, `offset`, `offsetEasing` - Implement `calculateOffsets()` method using the formula from spec: - ```typescript const last = indices.at(-1); indices.map((n) => (easing(n / last) * last * offset) | 0); @@ -97,7 +71,7 @@ Update `packages/motion/src/index.ts` to export: - `Sequence` class - `SequenceOptions` type -## Part 2: @eix/interact Package Changes +## Part 2: @wix/interact Package Changes ### 2.1 Update Types @@ -149,6 +123,7 @@ Modify `packages/interact/src/core/Interact.ts`: 1. Parse `config.sequences` into cache (similar to `config.effects`) 2. Process `interaction.sequences` array: + - Resolve `sequenceId` references from `config.sequences` - Process each effect within the sequence - Generate unique IDs for sequence effects @@ -203,4 +178,4 @@ The calculated offsets are added to each effect's existing `delay` property. 1. Unit tests for `Sequence` class offset calculations 2. Unit tests for easing function integration 3. Integration tests for sequence parsing in Interact -4. E2E tests for staggered animations with various easing functions +4. E2E tests for staggered animations with various easing functions \ No newline at end of file diff --git a/apps/demo/src/styles.css b/apps/demo/src/styles.css index 9b38972c..cb23523e 100644 --- a/apps/demo/src/styles.css +++ b/apps/demo/src/styles.css @@ -548,3 +548,298 @@ body { background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 99%, #fecfef 100%); color: #333; } + +/* Sequence Demo */ +.sequence-demo-section { + margin-top: 2rem; +} + +.sequence-demo-header { + margin-bottom: 1.5rem; +} + +.sequence-demo-header h3 { + margin: 0 0 0.5rem; + font-size: 1.5rem; +} + +.sequence-demo-description { + color: rgb(148 163 184 / 0.9); + line-height: 1.6; + margin: 0; +} + +.sequence-demo-description code { + background: rgb(30 41 59 / 0.8); + padding: 0.15em 0.4em; + border-radius: 4px; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.9em; + color: #a5b4fc; +} + +.easing-presets { + margin-bottom: 1.5rem; +} + +.easing-preset-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.easing-preset-btn { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.15rem; + padding: 0.5rem 0.75rem; + background: rgb(30 41 59 / 0.6); + border: 1px solid rgb(148 163 184 / 0.2); + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; +} + +.easing-preset-btn:hover { + background: rgb(30 41 59 / 0.9); + border-color: rgb(148 163 184 / 0.4); +} + +.easing-preset-btn.active { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.3) 0%, rgba(139, 92, 246, 0.3) 100%); + border-color: #8b5cf6; +} + +.preset-name { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.85rem; + font-weight: 600; + color: #e2e8f0; +} + +.preset-type { + font-size: 0.65rem; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.sequence-demo-layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + margin-bottom: 1.5rem; +} + +@media (max-width: 900px) { + .sequence-demo-layout { + grid-template-columns: 1fr; + } +} + +.sequence-config-editor { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.sequence-config-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 0.5rem; +} + +.parse-error { + font-size: 0.75rem; + color: #f87171; + background: rgba(248, 113, 113, 0.1); + padding: 0.25rem 0.5rem; + border-radius: 4px; +} + +.config-textarea { + width: 100%; + min-height: 400px; + padding: 1rem; + background: rgb(2 6 23 / 0.9); + border: 1px solid rgb(148 163 184 / 0.2); + border-radius: 8px; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.8rem; + line-height: 1.6; + color: #a5b4fc; + resize: vertical; + tab-size: 2; +} + +.config-textarea:focus { + outline: none; + border-color: rgb(59 130 246 / 0.5); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +.sequence-buttons { + display: flex; + gap: 0.75rem; +} + +.sequence-apply-button, +.sequence-reset-button { + flex: 1; + padding: 0.75rem 1.5rem; + background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); + border: none; + border-radius: 8px; + color: white; + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.sequence-reset-button { + background: rgb(71 85 105 / 0.8); +} + +.sequence-apply-button:hover, +.sequence-reset-button:hover { + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); +} + +.sequence-apply-button:active, +.sequence-reset-button:active { + transform: translateY(0); +} + +.sequence-demo-preview { + display: flex; + align-items: center; + justify-content: center; +} + +.sequence-trigger-area { + background: rgb(30 41 59 / 0.5); + border: 2px dashed rgb(148 163 184 / 0.3); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: border-color 0.2s ease, background 0.2s ease; +} + +.sequence-trigger-area:hover { + border-color: rgb(59 130 246 / 0.5); + background: rgb(30 41 59 / 0.7); +} + +.sequence-trigger-hint { + text-align: center; + color: rgb(148 163 184 / 0.7); + font-size: 0.85rem; + margin: 0 0 1rem; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +.sequence-items-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.75rem; +} + +@media (max-width: 600px) { + .sequence-items-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +.sequence-item { + aspect-ratio: 1; + min-width: 80px; + background: linear-gradient(135deg, var(--item-color) 0%, color-mix(in srgb, var(--item-color) 70%, white) 100%); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 20px color-mix(in srgb, var(--item-color) 40%, transparent); + transition: transform 0.2s ease; +} + +.sequence-item:hover { + transform: scale(1.05); +} + +.sequence-item-label { + color: white; + font-weight: 700; + font-size: 0.9rem; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} + +.sequence-timing-viz { + background: rgb(30 41 59 / 0.4); + border-radius: 0.75rem; + padding: 1rem 1.25rem; + margin-bottom: 1rem; +} + +.timing-bars { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.timing-bar-row { + display: grid; + grid-template-columns: 60px 1fr 60px; + align-items: center; + gap: 0.75rem; +} + +.timing-bar-label { + font-size: 0.8rem; + color: rgb(148 163 184 / 0.8); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.timing-bar-track { + height: 8px; + background: rgb(15 23 42 / 0.6); + border-radius: 4px; + overflow: hidden; +} + +.timing-bar-fill { + height: 100%; + border-radius: 4px; + transition: width 0.3s ease; +} + +.timing-bar-value { + font-size: 0.8rem; + color: rgb(148 163 184 / 0.9); + font-family: 'JetBrains Mono', 'Fira Code', monospace; + text-align: right; +} + +.sequence-formula { + background: rgb(30 41 59 / 0.4); + border-radius: 0.75rem; + padding: 1rem 1.25rem; + text-align: center; +} + +.formula-code { + display: inline-block; + background: rgb(15 23 42 / 0.8); + padding: 0.5em 1em; + border-radius: 6px; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.9rem; + color: #a5b4fc; +} diff --git a/apps/demo/src/web/App.tsx b/apps/demo/src/web/App.tsx index bf456e85..d338dbf9 100644 --- a/apps/demo/src/web/App.tsx +++ b/apps/demo/src/web/App.tsx @@ -3,6 +3,7 @@ import { ScrollShowcase } from './components/ScrollShowcase'; import { ResponsiveDemo } from './components/ResponsiveDemo'; import { SelectorConditionDemo } from './components/SelectorConditionDemo'; import { PointerMoveDemo } from './components/PointerMoveDemo'; +import { SequenceDemo } from './components/SequenceDemo'; const heroCopy = [ 'Tune triggers, easings, and delays in real time.', @@ -35,6 +36,7 @@ function App() { +
diff --git a/apps/demo/src/web/components/SequenceDemo.tsx b/apps/demo/src/web/components/SequenceDemo.tsx new file mode 100644 index 00000000..68f86b4b --- /dev/null +++ b/apps/demo/src/web/components/SequenceDemo.tsx @@ -0,0 +1,246 @@ +import { useState, useMemo, useCallback } from 'react'; +import type { InteractConfig } from '@wix/interact/web'; +import { useInteractInstance } from '../hooks/useInteractInstance'; + +const createConfig = (offsetEasing: string): InteractConfig => ({ + effects: { + 'seq-effect-0': { + key: 'seq-item-1', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-0', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + 'seq-effect-1': { + key: 'seq-item-2', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-1', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + 'seq-effect-2': { + key: 'seq-item-3', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-2', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + 'seq-effect-3': { + key: 'seq-item-4', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-3', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + 'seq-effect-4': { + key: 'seq-item-5', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-4', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + 'seq-effect-5': { + key: 'seq-item-6', + duration: 600, + easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)', + fill: 'both', + keyframeEffect: { + name: 'seq-entrance-5', + keyframes: [ + { transform: 'translateY(40px) scale(0.8)', opacity: 0 }, + { transform: 'translateY(0) scale(1)', opacity: 1 }, + ], + }, + }, + }, + sequences: { + 'entrance-sequence': { + sequenceId: 'entrance-sequence', + delay: 0, + offset: 120, + offsetEasing, + effects: [ + { effectId: 'seq-effect-0' }, + { effectId: 'seq-effect-1' }, + { effectId: 'seq-effect-2' }, + { effectId: 'seq-effect-3' }, + { effectId: 'seq-effect-4' }, + { effectId: 'seq-effect-5' }, + ], + }, + }, + interactions: [ + { + key: 'sequence-trigger', + trigger: 'viewEnter', + params: { type: 'repeat', threshold: 0.5 }, + sequences: [{ sequenceId: 'entrance-sequence' }], + }, + ], +}); + +// Easing presets showcasing different types +const easingPresets = [ + { name: 'linear', value: 'linear', description: 'Named easing' }, + { name: 'quadOut', value: 'quadOut', description: 'Named easing' }, + { name: 'expoIn', value: 'expoIn', description: 'Named easing' }, + { name: 'ease-out', value: 'cubic-bezier(0.4, 0, 0.2, 1)', description: 'CSS cubic-bezier' }, + { name: 'ease-in', value: 'cubic-bezier(0.4, 0, 1, 1)', description: 'CSS cubic-bezier' }, + { name: 'overshoot', value: 'cubic-bezier(0.34, 1.56, 0.64, 1)', description: 'CSS cubic-bezier (y > 1)' }, + { name: 'back-in', value: 'cubic-bezier(0.6, -0.28, 0.735, 0.045)', description: 'CSS cubic-bezier (y < 0)' }, +]; + +const sequenceItems = [ + { id: 1, label: 'First', color: '#3b82f6' }, + { id: 2, label: 'Second', color: '#8b5cf6' }, + { id: 3, label: 'Third', color: '#ec4899' }, + { id: 4, label: 'Fourth', color: '#f97316' }, + { id: 5, label: 'Fifth', color: '#22c55e' }, + { id: 6, label: 'Sixth', color: '#06b6d4' }, +]; + +export const SequenceDemo = () => { + const [triggerKey, setTriggerKey] = useState(0); + const [configText, setConfigText] = useState(() => JSON.stringify(createConfig('quadOut'), null, 2)); + const [parseError, setParseError] = useState(null); + const [activePreset, setActivePreset] = useState('quadOut'); + + const config = useMemo(() => { + try { + const parsed = JSON.parse(configText); + setParseError(null); + return parsed as InteractConfig; + } catch (e) { + setParseError((e as Error).message); + return createConfig('quadOut'); + } + }, [configText]); + + useInteractInstance(config, triggerKey); + + const handleApply = () => { + setTriggerKey((prev) => prev + 1); + }; + + const handleReset = () => { + setConfigText(JSON.stringify(createConfig('quadOut'), null, 2)); + setActivePreset('quadOut'); + setTriggerKey((prev) => prev + 1); + }; + + const handlePresetClick = useCallback((preset: typeof easingPresets[0]) => { + setConfigText(JSON.stringify(createConfig(preset.value), null, 2)); + setActivePreset(preset.value); + setTriggerKey((prev) => prev + 1); + }, []); + + return ( +
+
+

Sequence Feature

+

Staggered Animations

+

+ Use offsetEasing to control stagger timing. Supports named easings, + CSS cubic-bezier() strings, and custom functions. +

+
+ +
+

Try Different Easing Types

+
+ {easingPresets.map((preset) => ( + + ))} +
+
+ +
+
+
+

InteractConfig (editable JSON)

+ {parseError && Parse error: {parseError}} +
+