experimental: Proof of concept of the Animation component using ScrollTimeline (#4797)

## Description

ref #2645

Proof of Concept for the Animation Component Using ScrollTimeline

For FF and Safari `scroll-timeline-polyfill` is used.

SSR Support https://animate.development.webstudio.is/playground
Storybook Part 1. 

https://6382151c8b47d4399fb9fc69-limlyrmprp.chromatic.com/?path=/story/sdk-components-animation-scroll-animations--in-out

Next steps:

- Add stories with additional examples i.e. closest usage, images etc.
https://scroll-driven-animations.style/
- Add support to view animations
- Add view animations examples
- Add enter/leave actions and animations support
- Builder UI
- Add lottie, rive etc compatibility (should work through
context.getAnimations()[0].effect.getComputedTiming().progress)

## Steps for reproduction

1. click button
2. expect xyz

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
This commit is contained in:
Ivan Starkov
2025-02-01 14:57:36 +03:00
committed by GitHub
parent 65e9e80025
commit 68a28b4eee
24 changed files with 581 additions and 24 deletions

View File

@ -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

67
.github/workflows/check-submodules.yml vendored Normal file
View File

@ -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'."

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}"
- run: pnpm lint --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslint-cache
- name: Lint
run: |
pnpm lint --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslint-cache
- run: pnpm -r test
- run: pnpm --filter=@webstudio-is/prisma-client build
- run: pnpm -r typecheck
- name: Test
run: |
pnpm -r test
- 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

View File

@ -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

4
.gitmodules vendored Normal file
View File

@ -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

View File

@ -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: "~",

14
@types/scroll-timeline.d.ts vendored Normal file
View File

@ -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);
}

View File

@ -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 (
<Box>
<Scroll
debug={DEBUG}
action={{
type: "scroll",
animations: [
{
timing: {
fill: "backwards",
rangeStart: ["start", { type: "unit", value: 0, unit: "%" }],
rangeEnd: ["start", { type: "unit", value: 200, unit: "px" }],
},
keyframes: [
{
offset: 0,
styles: {
transform: parseCssValue(
"transform",
"translate(0, -120px)"
),
opacity: parseCssValue("opacity", "0.2"),
},
},
],
},
{
timing: {
fill: "forwards",
rangeStart: ["end", { type: "unit", value: 200, unit: "px" }],
rangeEnd: ["end", { type: "unit", value: 0, unit: "%" }],
},
keyframes: [
{
offset: 1,
styles: {
transform: parseCssValue(
"transform",
"translate(0, 120px)"
),
opacity: parseCssValue("opacity", "0.0"),
},
},
],
},
],
}}
>
<H1
css={{
position: "fixed",
width: "100%",
textAlign: "center",
top: "80px",
margin: 0,
padding: 0,
"&:hover": {
color: "red",
},
}}
>
HELLO WORLD
</H1>
</Scroll>
<Box css={{ height: "200px", backgroundColor: "#eee", p: 4 }}>
Start scrolling, and when the current box scrolls out, the HELLO WORLD
header will fly in and become hoverable. (During the animation, it wont
be hoverable.)
</Box>
<Box css={{ height: "200vh" }}></Box>
<Box css={{ height: "200px", backgroundColor: "#eee", p: 4 }}>
When you see this box, the HELLO WORLD header will fly out.
</Box>
</Box>
);
};
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,
};

View File

@ -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: "~",

View File

@ -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"
}
}
}

View File

@ -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"
}
}

View File

@ -1 +1 @@
export {};
export { Scroll } from "./scroll";

View File

@ -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<ElementRef<"div">, ScrollProps>(
({ debug = false, action, ...props }, ref) => {
return <div ref={ref} style={{ display: "contents" }} {...props} />;
}
);

View File

@ -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;

View File

@ -1,3 +0,0 @@
import { createProxy } from "@webstudio-is/template";
export const animation = createProxy("@webstudio-is/sdk-components-animation:");

View File

@ -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"]
}
}

View File

@ -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,

26
pnpm-lock.yaml generated
View File

@ -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

25
submodules.sh Executable file
View File

@ -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
'