diff --git a/.github/actions/submodules-checkout/action.yml b/.github/actions/submodules-checkout/action.yml
new file mode 100644
index 000000000000..0b47233e94ce
--- /dev/null
+++ b/.github/actions/submodules-checkout/action.yml
@@ -0,0 +1,46 @@
+name: CI setup
+
+description: |
+ Sets up the CI environment for the project.
+
+inputs:
+ submodules-ssh-key:
+ description: "The SSH key to private submodules to use for the checkout"
+ required: true
+
+runs:
+ using: "composite"
+
+ steps:
+ - name: Set up SSH for Git
+ if: ${{ inputs.submodules-ssh-key }}
+ run: |
+ mkdir -p ~/.ssh
+ echo "${{ inputs.submodules-ssh-key }}" > ~/.ssh/id_ed25519
+ chmod 600 ~/.ssh/id_ed25519
+ ssh-keyscan github.com >> ~/.ssh/known_hosts
+ shell: bash
+
+ - name: Verify SSH Connection (Optional)
+ if: ${{ inputs.submodules-ssh-key }}
+ run: |
+ ssh -T git@github.com || true
+ shell: bash
+
+ - name: Verify SSH Connection (Optional)
+ if: ${{ inputs.submodules-ssh-key }}
+ run: |
+ echo Branch is ${{ github.event.pull_request.head.ref || github.ref_name }}
+ shell: bash
+
+ - name: Try checkout submodules to the same branch as main repo
+ if: ${{ inputs.submodules-ssh-key }}
+ run: |
+ ./submodules.sh ${{ github.event.pull_request.head.ref || github.ref_name }}
+ shell: bash
+
+ - name: Show main readme
+ if: ${{ inputs.submodules-ssh-key }}
+ run: |
+ cat ./packages/sdk-components-animation/private-src/README.md || echo "No README found"
+ shell: bash
diff --git a/.github/workflows/check-submodules.yml b/.github/workflows/check-submodules.yml
new file mode 100644
index 000000000000..c97bdf016b2c
--- /dev/null
+++ b/.github/workflows/check-submodules.yml
@@ -0,0 +1,67 @@
+name: Check submodules
+
+on:
+ pull_request:
+
+# cancel in-progress runs on new commits to same PR (gitub.event.number)
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ checks:
+ timeout-minutes: 20
+
+ environment:
+ name: development
+
+ env:
+ DATABASE_URL: postgres://
+ AUTH_SECRET: test
+
+ runs-on: ubuntu-24.04-arm
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
+
+ - name: Check if any submodule branch matches github.ref_name
+ run: |
+ echo "C ${{ github.workflow }}-${{ github.event.number || github.sha }}"
+ # Get the current branch or tag name
+ REF_NAME="${{ github.event.pull_request.head.ref || github.ref_name }}"
+
+ echo "Branch is:" $REF_NAME
+
+ # List all submodule paths
+ SUBMODULES=$(git submodule status | awk '{print $2}')
+
+ # Check each submodule's branch
+ for SUBMODULE in $SUBMODULES; do
+ echo "Checking submodule: $SUBMODULE"
+ (
+ cd "$SUBMODULE"
+ # Get the current branch of the submodule
+ SUBMODULE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+ echo "Submodule branch: $SUBMODULE_BRANCH"
+
+ # Compare the submodule branch to the ref_name
+ if [ "$SUBMODULE_BRANCH" = "$REF_NAME" ]; then
+ echo "::error::Submodule '$SUBMODULE' is on branch '$SUBMODULE_BRANCH', which matches the current ref '$REF_NAME'."
+ exit 1
+ fi
+ )
+ if [ $? -ne 0 ]; then
+ exit 1 # Fail the workflow if any submodule branch matches
+ fi
+ done
+
+ echo "No submodule is on the same branch as the current ref '$REF_NAME'."
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index 8f166944635f..549d9a0c634f 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -20,12 +20,20 @@ jobs:
runs-on: ubuntu-latest
+ environment:
+ name: development
+
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # we need to fetch at least parent commit to satisfy Chromatic
ref: ${{ github.event.pull_request.head.sha || github.sha }} # HEAD commit instead of merge commit
+ # Storybook with submodules
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
+
- uses: ./.github/actions/ci-setup
- name: Chromatic
diff --git a/.github/workflows/cli-r2-static.yaml b/.github/workflows/cli-r2-static.yaml
index bbf61abfabbb..d21ecd265bc4 100644
--- a/.github/workflows/cli-r2-static.yaml
+++ b/.github/workflows/cli-r2-static.yaml
@@ -30,6 +30,8 @@ jobs:
with:
ref: ${{ github.sha }} # HEAD commit instead of merge commit
+ # Do not checkout submodules, they are not needed for this workflow
+
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
diff --git a/.github/workflows/cli-r2.yaml b/.github/workflows/cli-r2.yaml
index 4b36b5087bc6..b8e3247cf035 100644
--- a/.github/workflows/cli-r2.yaml
+++ b/.github/workflows/cli-r2.yaml
@@ -31,6 +31,11 @@ jobs:
with:
ref: ${{ github.sha }} # HEAD commit instead of merge commit
+ # We need submodules here as this is used for the cloudflare build
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
+
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
diff --git a/.github/workflows/fixtures-test.yml b/.github/workflows/fixtures-test.yml
index 5f37750af516..e627224a96f3 100644
--- a/.github/workflows/fixtures-test.yml
+++ b/.github/workflows/fixtures-test.yml
@@ -9,6 +9,12 @@ on:
builder-host:
required: true
type: string
+ environment:
+ required: true
+ type: string
+ secrets:
+ PRIVATE_GITHUB_DEPLOY_TOKEN:
+ required: true
permissions:
contents: read # to fetch code (actions/checkout)
@@ -23,6 +29,9 @@ jobs:
runs-on: ${{ matrix.os }}
+ environment:
+ name: ${{ inputs.environment }}
+
env:
DATABASE_URL: postgres://
AUTH_SECRET: test
@@ -32,7 +41,11 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.event.pull_request.head.sha || github.sha }} # HEAD commit instead of merge commit
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+ # Test that everything is working with submodules
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: ./.github/actions/ci-setup
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3fec2ba4d15f..ef5082bca988 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -18,6 +18,15 @@ jobs:
checks:
timeout-minutes: 20
+ strategy:
+ matrix:
+ environment:
+ - empty
+ - development
+
+ environment:
+ name: ${{ matrix.environment }}
+
env:
DATABASE_URL: postgres://
AUTH_SECRET: test
@@ -27,7 +36,12 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.event.pull_request.head.sha || github.sha }} # HEAD commit instead of merge commit
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+ # Will not checkout submodules on empty environment, and will on development
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: ./.github/actions/ci-setup
@@ -41,21 +55,35 @@ jobs:
- run: echo ===SHA USED=== ${{ github.event.pull_request.head.sha || github.sha }} # todo: remove after check whats happening on main
- - run: pnpm prettier --cache --check "**/*.{js,md,ts,tsx}"
+ - run: |
+ pnpm prettier --cache --check "**/*.{js,md,ts,tsx}"
+
+ - name: Lint
+ run: |
+ pnpm lint --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslint-cache
- - run: pnpm lint --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslint-cache
+ - name: Test
+ run: |
+ pnpm -r test
- - run: pnpm -r test
- - run: pnpm --filter=@webstudio-is/prisma-client build
- - run: pnpm -r typecheck
+ - name: Typecheck
+ run: |
+ pnpm -r typecheck
check-size:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04-arm
+
+ environment:
+ name: development
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.event.pull_request.head.sha || github.sha }} # HEAD commit instead of merge commit
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: ./.github/actions/ci-setup
diff --git a/.github/workflows/vercel-deploy-staging.yml b/.github/workflows/vercel-deploy-staging.yml
index ba79625ac6ec..883ccc710235 100644
--- a/.github/workflows/vercel-deploy-staging.yml
+++ b/.github/workflows/vercel-deploy-staging.yml
@@ -5,7 +5,7 @@ on:
# cancel in-progress runs on new commits to same PR (gitub.event.number)
concurrency:
- group: vercel-deploy-${{ github.workflow }}-${{ github.event.number || github.sha }}
+ group: vercel-deploy-${{ github.workflow }}-${{ github.sha }}
cancel-in-progress: true
permissions:
@@ -38,9 +38,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.event.pull_request.head.sha || github.sha }} # HEAD commit instead of merge commit
+ ref: ${{ github.sha }}
+
+ - uses: ./.github/actions/submodules-checkout
+ with:
+ submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
node-version: 20
@@ -85,6 +90,11 @@ jobs:
with:
builder-url: ${{ needs.deployment.outputs.builder-url }}
builder-host: ${{ needs.deployment.outputs.builder-host }}
+ environment: development
+ secrets:
+ # We are not passing the secret here (as it does not exist in the current environment).
+ # Instead, this serves as a signal to the calling workflow that it has permission to extract it from the environment.
+ PRIVATE_GITHUB_DEPLOY_TOKEN: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
delete-github-deployments:
needs: fixtures-test
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000000..76cab76f4653
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "packages/sdk-components-animation/private-src"]
+ path = packages/sdk-components-animation/private-src
+ url = git@github.com:webstudio-is/sdk-components-animation.git
+ branch = main
diff --git a/.storybook/main.ts b/.storybook/main.ts
index 191dd8425b15..9bbdb895e7a7 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -1,5 +1,19 @@
import * as path from "node:path";
import type { StorybookConfig } from "@storybook/react-vite";
+import { existsSync, readdirSync } from "node:fs";
+
+const isFolderEmpty = (folderPath: string) => {
+ if (!existsSync(folderPath)) {
+ return true; // Folder does not exist
+ }
+ const contents = readdirSync(folderPath);
+
+ return contents.length === 0;
+};
+
+const hasPrivateFolders = !isFolderEmpty(
+ path.join(__dirname, "../../packages/sdk-components-animation/private-src")
+);
const visualTestingStories: StorybookConfig["stories"] = [
{
@@ -37,6 +51,10 @@ export default {
directory: "../packages/sdk-components-react-radix",
titlePrefix: "SDK Components React Radix",
},
+ {
+ directory: "../packages/sdk-components-animation",
+ titlePrefix: "SDK Components Animation",
+ },
],
framework: {
name: "@storybook/react-vite",
@@ -50,6 +68,10 @@ export default {
async viteFinal(config) {
return {
...config,
+ optimizeDeps: {
+ exclude: ["scroll-timeline-polyfill"],
+ },
+
define: {
...config.define,
// storybook use "util" package internally which is bundled with stories
@@ -59,7 +81,17 @@ export default {
},
resolve: {
...config.resolve,
- conditions: ["webstudio", "import", "module", "browser", "default"],
+ conditions: hasPrivateFolders
+ ? [
+ "webstudio-private",
+ "webstudio",
+ "import",
+ "module",
+ "browser",
+ "default",
+ ]
+ : ["webstudio", "import", "module", "browser", "default"],
+
alias: [
{
find: "~",
diff --git a/@types/scroll-timeline.d.ts b/@types/scroll-timeline.d.ts
new file mode 100644
index 000000000000..eea03a78a8f6
--- /dev/null
+++ b/@types/scroll-timeline.d.ts
@@ -0,0 +1,14 @@
+type ScrollAxis = "block" | "inline" | "x" | "y";
+
+interface ScrollTimelineOptions {
+ source?: Element | Document | null;
+ axis?: ScrollAxis;
+}
+
+declare class ScrollTimeline extends AnimationTimeline {
+ constructor(options?: ScrollTimelineOptions);
+}
+
+declare class ViewTimeline extends ScrollTimeline {
+ constructor(options?: ScrollTimelineOptions);
+}
diff --git a/apps/builder/app/routes/_ui.playground.tsx b/apps/builder/app/routes/_ui.playground.tsx
new file mode 100644
index 000000000000..1336ca563972
--- /dev/null
+++ b/apps/builder/app/routes/_ui.playground.tsx
@@ -0,0 +1,92 @@
+import { Scroll } from "@webstudio-is/sdk-components-animation";
+import { parseCssValue } from "@webstudio-is/css-data";
+import { Box, styled } from "@webstudio-is/design-system";
+
+const H1 = styled("h1");
+
+const DEBUG = false;
+
+const Playground = () => {
+ return (
+
+
+
+ HELLO WORLD
+
+
+
+ Start scrolling, and when the current box scrolls out, the “HELLO WORLD”
+ header will fly in and become hoverable. (During the animation, it won’t
+ be hoverable.)
+
+
+
+ When you see this box, the “HELLO WORLD” header will fly out.
+
+
+ );
+};
+
+export default Playground;
+
+// Reduces Vercel function size from 29MB to 9MB for unknown reasons; effective when used in limited files.
+export const config = {
+ maxDuration: 30,
+};
diff --git a/apps/builder/vite.config.ts b/apps/builder/vite.config.ts
index 3357db040a3d..eced690c8c74 100644
--- a/apps/builder/vite.config.ts
+++ b/apps/builder/vite.config.ts
@@ -1,4 +1,4 @@
-import { resolve } from "node:path";
+import path, { resolve } from "node:path";
import { defineConfig, type CorsOptions } from "vite";
import { vitePlugin as remix } from "@remix-run/dev";
import { vercelPreset } from "@vercel/remix/vite";
@@ -9,13 +9,28 @@ import {
getAuthorizationServerOrigin,
isBuilderUrl,
} from "./app/shared/router-utils/origins";
-import { readFileSync } from "node:fs";
+import { readFileSync, readdirSync, existsSync } from "node:fs";
+
+const isFolderEmpty = (folderPath: string) => {
+ if (!existsSync(folderPath)) {
+ return true; // Folder does not exist
+ }
+ const contents = readdirSync(folderPath);
+
+ return contents.length === 0;
+};
+
+const hasPrivateFolders = !isFolderEmpty(
+ path.join(__dirname, "../../packages/sdk-components-animation/private-src")
+);
export default defineConfig(({ mode }) => {
if (mode === "test") {
return {
resolve: {
- conditions: ["webstudio"],
+ conditions: hasPrivateFolders
+ ? ["webstudio-private", "webstudio"]
+ : ["webstudio"],
alias: [
{
find: "~",
@@ -69,7 +84,10 @@ export default defineConfig(({ mode }) => {
},
],
resolve: {
- conditions: ["webstudio"],
+ conditions: hasPrivateFolders
+ ? ["webstudio-private", "webstudio"]
+ : ["webstudio"],
+
alias: [
{
find: "~",
diff --git a/package.json b/package.json
index 32bac180599e..5418bee673da 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,8 @@
"css-tree@2.3.1": "patches/css-tree@2.3.1.patch",
"@types/css-tree@2.3.1": "patches/@types__css-tree@2.3.1.patch",
"@radix-ui/react-scroll-area@1.0.5": "patches/@radix-ui__react-scroll-area@1.0.5.patch",
- "@remix-run/dev": "patches/@remix-run__dev.patch"
+ "@remix-run/dev": "patches/@remix-run__dev.patch",
+ "scroll-timeline-polyfill@1.1.0": "patches/scroll-timeline-polyfill@1.1.0.patch"
}
}
}
diff --git a/packages/sdk-components-animation/package.json b/packages/sdk-components-animation/package.json
index b130dc72275c..4c32dda89905 100644
--- a/packages/sdk-components-animation/package.json
+++ b/packages/sdk-components-animation/package.json
@@ -14,6 +14,7 @@
],
"exports": {
".": {
+ "webstudio-private": "./private-src/components.ts",
"webstudio": "./src/components.ts",
"types": "./lib/types/components.d.ts",
"import": "./lib/components.js"
@@ -51,9 +52,12 @@
"react-dom": "18.3.0-canary-14898b6a9-20240318"
},
"dependencies": {
+ "@webstudio-is/css-engine": "workspace:*",
"@webstudio-is/icons": "workspace:*",
"@webstudio-is/react-sdk": "workspace:*",
- "@webstudio-is/sdk": "workspace:*"
+ "@webstudio-is/sdk": "workspace:*",
+ "react-error-boundary": "^5.0.0",
+ "scroll-timeline-polyfill": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.70",
@@ -63,7 +67,10 @@
"@webstudio-is/sdk-components-react": "workspace:*",
"@webstudio-is/template": "workspace:*",
"@webstudio-is/tsconfig": "workspace:*",
+ "@webstudio-is/css-data": "workspace:*",
+ "@webstudio-is/design-system": "workspace:*",
"react": "18.3.0-canary-14898b6a9-20240318",
- "react-dom": "18.3.0-canary-14898b6a9-20240318"
+ "react-dom": "18.3.0-canary-14898b6a9-20240318",
+ "type-fest": "^4.32.0"
}
}
diff --git a/packages/sdk-components-animation/private-src b/packages/sdk-components-animation/private-src
new file mode 160000
index 000000000000..80edfcb8e401
--- /dev/null
+++ b/packages/sdk-components-animation/private-src
@@ -0,0 +1 @@
+Subproject commit 80edfcb8e4019f360c06622ff7543c78794b7a22
diff --git a/packages/sdk-components-animation/src/components.ts b/packages/sdk-components-animation/src/components.ts
index cb0ff5c3b541..b8a77f059a67 100644
--- a/packages/sdk-components-animation/src/components.ts
+++ b/packages/sdk-components-animation/src/components.ts
@@ -1 +1 @@
-export {};
+export { Scroll } from "./scroll";
diff --git a/packages/sdk-components-animation/src/scroll.tsx b/packages/sdk-components-animation/src/scroll.tsx
new file mode 100644
index 000000000000..17e0cc3f5aab
--- /dev/null
+++ b/packages/sdk-components-animation/src/scroll.tsx
@@ -0,0 +1,14 @@
+import { forwardRef, type ElementRef } from "react";
+import type { AnimationAction } from "./shared/animation-types";
+
+type ScrollProps = {
+ debug?: boolean;
+ children?: React.ReactNode;
+ action: AnimationAction;
+};
+
+export const Scroll = forwardRef, ScrollProps>(
+ ({ debug = false, action, ...props }, ref) => {
+ return ;
+ }
+);
diff --git a/packages/sdk-components-animation/src/shared/animation-types.tsx b/packages/sdk-components-animation/src/shared/animation-types.tsx
new file mode 100644
index 000000000000..e997c6e9f661
--- /dev/null
+++ b/packages/sdk-components-animation/src/shared/animation-types.tsx
@@ -0,0 +1,113 @@
+import type { StyleValue, UnitValue } from "@webstudio-is/css-engine";
+
+export type KeyframeStyles = { [property: string]: StyleValue | undefined };
+
+export type AnimationKeyframe = {
+ offset: number | undefined;
+ // We are using composite: auto as the default value for now
+ // composite?: CompositeOperationOrAuto;
+ styles: KeyframeStyles;
+};
+
+const RANGE_UNITS = [
+ "%",
+ "px",
+ // Does not supported by polyfill and we are converting it to px ourselfs
+ "cm",
+ "mm",
+ "q",
+ "in",
+ "pt",
+ "pc",
+ "em",
+ "rem",
+ "ex",
+ "rex",
+ "cap",
+ "rcap",
+ "ch",
+ "rch",
+ "lh",
+ "rlh",
+ "vw",
+ "svw",
+ "lvw",
+ "dvw",
+ "vh",
+ "svh",
+ "lvh",
+ "dvh",
+ "vi",
+ "svi",
+ "lvi",
+ "dvi",
+ "vb",
+ "svb",
+ "lvb",
+ "dvb",
+ "vmin",
+ "svmin",
+ "lvmin",
+ "dvmin",
+ "vmax",
+ "svmax",
+ "lvmax",
+ "dvmax",
+] as const;
+
+export type RangeUnit = (typeof RANGE_UNITS)[number];
+
+export const isRangeUnit = (value: unknown): value is RangeUnit =>
+ RANGE_UNITS.includes(value as RangeUnit);
+
+export type RangeUnitValue = { type: "unit"; value: number; unit: RangeUnit };
+
+({}) as RangeUnitValue satisfies UnitValue;
+
+type KeyframeEffectOptions = {
+ easing?: string;
+ fill?: FillMode;
+};
+
+/**
+ * Scroll does not support https://drafts.csswg.org/scroll-animations/#named-ranges
+ * However, for simplicity and type unification with the view, we will use the names "start" and "end,"
+ * which will be transformed as follows:
+ * - "start" → `calc(0% + range)`
+ * - "end" → `calc(100% - range)`
+ */
+type ScrollNamedRange = "start" | "end";
+
+/**
+ * Scroll does not support https://drafts.csswg.org/scroll-animations/#named-ranges
+ * However, for simplicity and type unification with the view, we will use the names "start" and "end,"
+ * See ScrollNamedRange type for more information.
+ */
+export type ScrollRangeValue = [name: ScrollNamedRange, value: RangeUnitValue];
+
+type ScrollRangeOptions = {
+ rangeStart?: ScrollRangeValue | undefined;
+ rangeEnd?: ScrollRangeValue | undefined;
+};
+
+/*
+type AnimationTiming = {
+ delay?: number;
+ duration?: number;
+ easing?: string;
+ fill?: FillMode;
+};
+*/
+
+type ScrollAction = {
+ type: "scroll";
+ source?: "closest" | "nearest" | "root";
+ axis?: "block" | "inline" | "x" | "y";
+ animations: {
+ timing: KeyframeEffectOptions & ScrollRangeOptions;
+ keyframes: AnimationKeyframe[];
+ }[];
+};
+
+// | ViewAction | ...
+export type AnimationAction = ScrollAction;
diff --git a/packages/sdk-components-animation/src/shared/proxy.ts b/packages/sdk-components-animation/src/shared/proxy.ts
deleted file mode 100644
index 8145a83a563f..000000000000
--- a/packages/sdk-components-animation/src/shared/proxy.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { createProxy } from "@webstudio-is/template";
-
-export const animation = createProxy("@webstudio-is/sdk-components-animation:");
diff --git a/packages/sdk-components-animation/tsconfig.json b/packages/sdk-components-animation/tsconfig.json
index d2e5040f8f2c..a7f8652f07c9 100644
--- a/packages/sdk-components-animation/tsconfig.json
+++ b/packages/sdk-components-animation/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "@webstudio-is/tsconfig/base.json"
+ "extends": "@webstudio-is/tsconfig/base.json",
+ "include": [
+ "src",
+ "../../@types/**/scroll-timeline.d.ts",
+ "private-src/scroll.stories.tsx",
+ "private-src/scroll.tsx"
+ ],
+ "compilerOptions": {
+ "types": ["react/experimental", "react-dom/experimental", "@types/node"]
+ }
}
diff --git a/patches/scroll-timeline-polyfill@1.1.0.patch b/patches/scroll-timeline-polyfill@1.1.0.patch
new file mode 100644
index 000000000000..8b78d3071d0d
--- /dev/null
+++ b/patches/scroll-timeline-polyfill@1.1.0.patch
@@ -0,0 +1,25 @@
+diff --git a/src/scroll-timeline-base.js b/src/scroll-timeline-base.js
+index 2c854928701c5dae23c985eb04f73a6ca2b661c3..abf1a7629568f411dd83dd509640850706d59528 100644
+--- a/src/scroll-timeline-base.js
++++ b/src/scroll-timeline-base.js
+@@ -162,13 +162,19 @@ function isValidAxis(axis) {
+ */
+ export function measureSource (source) {
+ const style = getComputedStyle(source);
++ const clientHeight = source.clientHeight;
+ return {
+ scrollLeft: source.scrollLeft,
+ scrollTop: source.scrollTop,
+ scrollWidth: source.scrollWidth,
+ scrollHeight: source.scrollHeight,
+ clientWidth: source.clientWidth,
+- clientHeight: source.clientHeight,
++ get clientHeight() {
++ if (document.scrollingElement === source) {
++ return window.innerHeight;
++ }
++ return clientHeight;
++ },
+ writingMode: style.writingMode,
+ direction: style.direction,
+ scrollPaddingTop: style.scrollPaddingTop,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 57c6cc58877e..19dc6862febf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,9 @@ patchedDependencies:
css-tree@2.3.1:
hash: epgcmebti7rfrc2ej4odb3t4jy
path: patches/css-tree@2.3.1.patch
+ scroll-timeline-polyfill@1.1.0:
+ hash: i4g3vdpump4efgy2hri5l5rsfm
+ path: patches/scroll-timeline-polyfill@1.1.0.patch
importers:
@@ -1914,6 +1917,9 @@ importers:
packages/sdk-components-animation:
dependencies:
+ '@webstudio-is/css-engine':
+ specifier: workspace:*
+ version: link:../css-engine
'@webstudio-is/icons':
specifier: workspace:*
version: link:../icons
@@ -1923,6 +1929,12 @@ importers:
'@webstudio-is/sdk':
specifier: workspace:*
version: link:../sdk
+ react-error-boundary:
+ specifier: ^5.0.0
+ version: 5.0.0(react@18.3.0-canary-14898b6a9-20240318)
+ scroll-timeline-polyfill:
+ specifier: ^1.1.0
+ version: 1.1.0(patch_hash=i4g3vdpump4efgy2hri5l5rsfm)
devDependencies:
'@types/react':
specifier: ^18.2.70
@@ -1930,6 +1942,12 @@ importers:
'@types/react-dom':
specifier: ^18.2.25
version: 18.2.25
+ '@webstudio-is/css-data':
+ specifier: workspace:*
+ version: link:../css-data
+ '@webstudio-is/design-system':
+ specifier: workspace:*
+ version: link:../design-system
'@webstudio-is/generate-arg-types':
specifier: workspace:*
version: link:../generate-arg-types
@@ -1951,6 +1969,9 @@ importers:
react-dom:
specifier: 18.3.0-canary-14898b6a9-20240318
version: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318)
+ type-fest:
+ specifier: ^4.32.0
+ version: 4.32.0
packages/sdk-components-react:
dependencies:
@@ -8398,6 +8419,9 @@ packages:
scheduler@0.24.0-canary-14898b6a9-20240318:
resolution: {integrity: sha512-ifDO3bUdooS4OlxvGxMyoDEC/aq14MvJLDd0thjrUSZGeLJA7WBc+sr9NZxIxrXfVqMl1GTGGPwXqRJZDNW76w==}
+ scroll-timeline-polyfill@1.1.0:
+ resolution: {integrity: sha512-BpL3gk3Ynt/5VYaDFUNUP/FTkDldwKQnWcA07g/mDHkMVS9pQUyUXBpsy4RZYAgsfFeI1tWcnPNrEFtCpQoO9Q==}
+
selfsigned@2.1.1:
resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==}
engines: {node: '>=10'}
@@ -16336,6 +16360,8 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ scroll-timeline-polyfill@1.1.0(patch_hash=i4g3vdpump4efgy2hri5l5rsfm): {}
+
selfsigned@2.1.1:
dependencies:
node-forge: 1.3.1
diff --git a/submodules.sh b/submodules.sh
new file mode 100755
index 000000000000..0c624f2d6cc0
--- /dev/null
+++ b/submodules.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+BRANCH="$1"
+
+
+git submodule update --init --recursive
+
+git submodule foreach '
+ # If a branch parameter is provided, use it; otherwise, determine the branch dynamically
+ if [ -n "'"$BRANCH"'" ]; then
+ SUBMODULE_BRANCH="'"$BRANCH"'"
+ else
+ SUBMODULE_BRANCH=$(git -C $toplevel rev-parse --abbrev-ref HEAD)
+ fi
+
+ echo "Checking out \"$SUBMODULE_BRANCH\" branch in \"$name\" submodule"
+
+ # Check if the branch exists in the remote
+ if git ls-remote --exit-code --heads origin "$SUBMODULE_BRANCH" > /dev/null; then
+ git checkout "$SUBMODULE_BRANCH" && git pull origin "$SUBMODULE_BRANCH"
+ else
+ # Fallback to "main" if the branch does not exist
+ git checkout "main" && git pull origin "main"
+ fi
+'
\ No newline at end of file