main 17bb3f4d83de cached
24 files
42.5 KB
11.1k tokens
25 symbols
1 requests
Download .txt
Repository: solidjs-community/solid-transition-group
Branch: main
Commit: 17bb3f4d83de
Files: 24
Total size: 42.5 KB

Directory structure:
gitextract_g0bh3662/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── README.md
├── astro.config.ts
├── dev/
│   ├── env.d.ts
│   ├── group.tsx
│   ├── kitchen-sink.tsx
│   ├── layout.astro
│   ├── pages/
│   │   ├── group.astro
│   │   └── index.astro
│   └── styles.css
├── netlify.toml
├── package.json
├── src/
│   └── index.ts
├── tailwind.config.cjs
├── test/
│   ├── index.test.tsx
│   └── server.test.tsx
├── tsconfig.build.json
├── tsconfig.json
└── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/tests.yml
================================================
name: Build and Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4

      - name: Setup Node.js environment
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Build
        run: pnpm run build
        env:
          CI: true

      - name: Test
        run: pnpm run test


================================================
FILE: .gitignore
================================================
node_modules/
dist/
lib/
coverage/
types/
*.tsbuildinfo


================================================
FILE: .prettierrc
================================================
{
  "trailingComma": "all",
  "tabWidth": 2,
  "printWidth": 100,
  "semi": true,
  "singleQuote": false,
  "useTabs": false,
  "arrowParens": "avoid",
  "bracketSpacing": true,
  "endOfLine": "lf",
  "plugins": []
}


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "esbenp.prettier-vscode",
    "astro-build.astro-vscode",
    "bradlc.vscode-tailwindcss"
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "cSpell.words": ["astrojs", "outin"],
  "css.validate": false
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020-2021 Ryan Carniato

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<p>
  <img width="100%" src="https://assets.solidjs.com/banner?project=Transition%20Group&type=core" alt="Solid Transition Group">
</p>

# Solid Transition Group

[![pnpm](https://img.shields.io/badge/maintained%20with-pnpm-cc00ff.svg?style=for-the-badge&logo=pnpm)](https://pnpm.io/)
[![version](https://img.shields.io/npm/v/solid-transition-group?style=for-the-badge)](https://www.npmjs.com/package/solid-transition-group)
[![downloads](https://img.shields.io/npm/dw/solid-transition-group?color=blue&style=for-the-badge)](https://www.npmjs.com/package/solid-transition-group)

Components for applying animations when children elements enter or leave the DOM. Influenced by React Transition Group and Vue Transitions for the SolidJS library.

## Installation

```bash
npm install solid-transition-group
# or
yarn add solid-transition-group
# or
pnpm add solid-transition-group
```

## Transition

`<Transition>` serve as transition effects for single element/component. The `<Transition>` only applies the transition behavior to the wrapped content inside; it doesn't render an extra DOM element, or show up in the inspected component hierarchy.

All props besides `children` are optional.

### Using with CSS

Usage with CSS is straightforward. Just add the `name` prop and the CSS classes will be automatically generated for you. The `name` prop is used as a prefix for the generated CSS classes. For example, if you use `name="slide-fade"`, the generated CSS classes will be `.slide-fade-enter`, `.slide-fade-enter-active`, etc.

The exitting element will be removed from the DOM when the first transition ends. You can override this behavior by providing a `done` callback to the `onExit` prop.

```tsx
import { Transition } from "solid-transition-group"

const [isVisible, setVisible] = createSignal(true)

<Transition name="slide-fade">
  <Show when={isVisible()}>
    <div>Hello</div>
  </Show>
</Transition>

setVisible(false) // triggers exit transition
```

Example CSS transition:

```css
.slide-fade-enter-active,
.slide-fade-exit-active {
  transition: opacity 0.3s, transform 0.3s;
}
.slide-fade-enter,
.slide-fade-exit-to {
  transform: translateX(10px);
  opacity: 0;
}
.slide-fade-enter {
  transform: translateX(-10px);
}
```

Props for customizing the CSS classes applied by `<Transition>`:

| Name               | Description                                                                                                                                                     |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`             | Used to automatically generate transition CSS class names. e.g. `name: 'fade'` will auto expand to `.fade-enter`, `.fade-enter-active`, etc. Defaults to `"s"`. |
| `enterClass`       | CSS class applied to the entering element at the start of the enter transition, and removed the frame after. Defaults to `"s-enter"`.                           |
| `enterToClass`     | CSS class applied to the entering element after the enter transition starts. Defaults to `"s-enter-to"`.                                                        |
| `enterActiveClass` | CSS class applied to the entering element for the entire duration of the enter transition. Defaults to `"s-enter-active"`.                                      |
| `exitClass`        | CSS class applied to the exiting element at the start of the exit transition, and removed the frame after. Defaults to `"s-exit"`.                              |
| `exitToClass`      | CSS class applied to the exiting element after the exit transition starts. Defaults to `"s-exit-to"`.                                                           |
| `exitActiveClass`  | CSS class applied to the exiting element for the entire duration of the exit transition. Defaults to `"s-exit-active"`.                                         |

### Using with JavaScript

You can also use JavaScript to animate the transition. The `<Transition>` component provides several events that you can use to hook into the transition lifecycle. The `onEnter` and `onExit` events are called when the transition starts, and the `onBeforeEnter` and `onBeforeExit` events are called before the transition starts. The `onAfterEnter` and `onAfterExit` events are called after the transition ends.

```jsx
<Transition
  onEnter={(el, done) => {
    const a = el.animate([{ opacity: 0 }, { opacity: 1 }], {
      duration: 600
    });
    a.finished.then(done);
  }}
  onExit={(el, done) => {
    const a = el.animate([{ opacity: 1 }, { opacity: 0 }], {
      duration: 600
    });
    a.finished.then(done);
  }}
>
  {show() && <div>Hello</div>}
</Transition>
```

**Events** proved by `<Transition>` for animating elements with JavaScript:

| Name            | Parameters                           | Description                                                                                                                                                                                                                                                                                                                                                                           |
| --------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onBeforeEnter` | `element: Element`                   | Function called before the enter transition starts. The `element` is not yet rendered.                                                                                                                                                                                                                                                                                                |
| `onEnter`       | `element: Element, done: () => void` | Function called when the enter transition starts. The `element` is rendered to the DOM. Call `done` to end the transition - removes the enter classes, and calls `onAfterEnter`. If the parameter for `done` is not provided, it will be called on `transitionend` or `animationend`.                                                                                                 |
| `onAfterEnter`  | `element: Element`                   | Function called after the enter transition ends. The `element` is removed from the DOM.                                                                                                                                                                                                                                                                                               |
| `onBeforeExit`  | `element: Element`                   | Function called before the exit transition starts. The `element` is still rendered, exit classes are not yet applied.                                                                                                                                                                                                                                                                 |
| `onExit`        | `element: Element, done: () => void` | Function called when the exit transition starts, after the exit classes are applied (`enterToClass` and `exitActiveClass`). The `element` is still rendered. Call `done` to end the transition - removes exit classes, calls `onAfterExit` and removes the element from the DOM. If the parameter for `done` is not provided, it will be called on `transitionend` or `animationend`. |
| `onAfterExit`   | `element: Element`                   | Function called after the exit transition ends. The `element` is removed from the DOM.                                                                                                                                                                                                                                                                                                |

### Changing Transition Mode

By default, `<Transition>` will apply the transition effect to both entering and exiting elements simultaneously. You can change this behavior by setting the `mode` prop to `"outin"` or `"inout"`. The `"outin"` mode will wait for the exiting element to finish before applying the transition to the entering element. The `"inout"` mode will wait for the entering element to finish before applying the transition to the exiting element.

By default the transition won't be applied on initial render. You can change this behavior by setting the `appear` prop to `true`.

> **Warning:** When using `appear` with SSR, the initial transition will be applied on the client-side, which might cause a flash of unstyled content.
> You need to handle applying the initial transition on the server-side yourself.

## TransitionGroup

### Props

- `moveClass` - CSS class applied to the moving elements for the entire duration of the move transition. Defaults to `"s-move"`.
- exposes the same props as `<Transition>` except `mode`.

### Usage

`<TransitionGroup>` serve as transition effects for multiple elements/components.

`<TransitionGroup>` supports moving transitions via CSS transform. When a child's position on screen has changed after an update, it will get applied a moving CSS class (auto generated from the name attribute or configured with the move-class attribute). If the CSS transform property is "transition-able" when the moving class is applied, the element will be smoothly animated to its destination using the FLIP technique.

```jsx
<ul>
  <TransitionGroup name="slide">
    <For each={state.items}>{item => <li>{item.text}</li>}</For>
  </TransitionGroup>
</ul>
```

## Demo

Kitchen sink demo: https://solid-transition-group.netlify.app/

Source code: https://github.com/solidjs-community/solid-transition-group/blob/main/dev/kitchen-sink.tsx

## FAQ

- **How to use with Portal?** - [Issue #8](https://github.com/solidjs-community/solid-transition-group/issues/8)
- **How to use with Outlet?** - [Issue #29](https://github.com/solidjs-community/solid-transition-group/issues/29)
- **Why elements are not connected in outin mode** - [Issue #34](https://github.com/solidjs-community/solid-transition-group/issues/34)


================================================
FILE: astro.config.ts
================================================
import path from "path";
import { fileURLToPath } from "url";
import { defineConfig } from "astro/config";
import solid from "@astrojs/solid-js";
import tailwind from "@astrojs/tailwind";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// https://astro.build/config
export default defineConfig({
  outDir: "dev/dist",
  srcDir: "dev",
  vite: {
    resolve: {
      alias: {
        "solid-transition-group": path.resolve(__dirname, "./src/index.ts"),
      },
    },
  },
  integrations: [solid(), tailwind()],
});


================================================
FILE: dev/env.d.ts
================================================
/// <reference types="astro/client" />


================================================
FILE: dev/group.tsx
================================================
import { Component, For, createSignal } from "solid-js";
import { TransitionGroup } from "solid-transition-group";

function shuffle<T>(array: T[]): T[] {
  return array.sort(() => Math.random() - 0.5);
}

const getRandomChar = () => ({ v: String.fromCharCode(65 + Math.floor(Math.random() * 26)) });

const Group: Component = () => {
  const [list, setList] = createSignal<{ v: string }[]>([
    { v: "S" },
    { v: "O" },
    { v: "L" },
    { v: "I" },
    { v: "D" },
    { v: "-" },
    { v: "T" },
    { v: "R" },
    { v: "A" },
    { v: "N" },
    { v: "S" },
    { v: "I" },
    { v: "T" },
    { v: "I" },
    { v: "O" },
    { v: "N" },
    { v: "-" },
    { v: "G" },
    { v: "R" },
    { v: "O" },
    { v: "U" },
    { v: "P" },
  ]);
  const randomIndex = () => Math.floor(Math.random() * list().length);

  return (
    <>
      <div class="flex gap-1">
        <button onClick={() => setList(p => shuffle([...p]))}>Shuffle</button>
        <button
          onClick={() => {
            const rand = randomIndex();
            setList(p => [...p.slice(0, rand), ...p.slice(rand + 2)]);
          }}
        >
          Remove
        </button>
        <button
          onClick={() => {
            const rand = randomIndex();
            setList(p => [
              ...p.slice(0, rand),
              getRandomChar(),
              getRandomChar(),
              getRandomChar(),
              ...p.slice(rand),
            ]);
          }}
        >
          Add
        </button>
      </div>
      <div class="mt-8 flex flex-wrap gap-1">
        <TransitionGroup name="group-item">
          <For each={list()}>
            {({ v }) => (
              <div class="group-item flex justify-center items-center w-5 h-6 bg-slate-700 rounded-sm">
                <span class="font-mono font-semibold">{v}</span>
              </div>
            )}
          </For>
        </TransitionGroup>
      </div>
    </>
  );
};

export default Group;


================================================
FILE: dev/kitchen-sink.tsx
================================================
import { Component, For, Show, createSignal } from "solid-js";
import { Transition, TransitionGroup } from "solid-transition-group";

function shuffle<T extends any[]>(array: T): T {
  return array.sort(() => Math.random() - 0.5);
}

const getRandomColor = () => `#${((Math.random() * 2 ** 24) | 0).toString(16)}`;

const Group: Component = () => {
  const [list, setList] = createSignal([1, 2, 3, 4, 5, 6, 7, 8, 9]);
  let nextId = 10;
  const randomIndex = () => Math.floor(Math.random() * list().length);

  return (
    <>
      <div class="flex gap-x-1">
        <button onClick={() => setList(p => shuffle([...p]))}>Shuffle</button>
        <button
          onClick={() => {
            const rand = randomIndex();
            setList(p => [...p.slice(0, rand), ...p.slice(rand + 1)]);
          }}
        >
          Remove
        </button>
        <button onClick={() => setList([])}>Remove All</button>
        <button
          onClick={() => {
            setList(
              shuffle(Array.from({ length: 50 }, (_, i) => i).filter(() => Math.random() > 0.5)),
            );
          }}
        >
          Randomize
        </button>
        <button
          onClick={() => {
            const rand = randomIndex();
            setList(p => [...p.slice(0, rand), nextId++, ...p.slice(rand)]);
          }}
        >
          Add
        </button>
      </div>
      <div class="p-8 relative flex flex-wrap gap-0.5">
        <TransitionGroup name="group-item">
          <For each={list()} fallback={<div>fallback</div>}>
            {() => (
              <div class="group-item">
                <svg width="20" height="20" viewBox="0 0 4 4">
                  <rect
                    width={2.5 - Math.random()}
                    height={2.5 - Math.random()}
                    transform={`translate(0.5, 0.5) rotate(${Math.random() * 360} 1.5 1)`}
                    fill={getRandomColor()}
                  />
                </svg>
              </div>
            )}
          </For>
        </TransitionGroup>
      </div>
    </>
  );
};

const SwitchCSS: Component = () => {
  const [page, setPage] = createSignal(1);

  return (
    <>
      <button onClick={() => setPage(p => ++p)}>Next</button>
      <br />
      <Transition mode="outin" name="fade">
        <Show when={page()} keyed>
          {i => <div style={{ "background-color": getRandomColor(), padding: "1rem" }}>{i}.</div>}
        </Show>
      </Transition>
    </>
  );
};

const SwitchJS: Component = () => {
  const [page, setPage] = createSignal(1);

  function onEnter(el: Element, done: VoidFunction) {
    const a = el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 500, easing: "ease" });
    a.finished.then(done);
  }
  function onExit(el: Element, done: VoidFunction) {
    const a = el.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 500, easing: "ease" });
    a.finished.then(done);
  }

  return (
    <>
      <button onClick={() => setPage(p => ++p)}>Next</button>
      <br />
      <Transition mode="outin" onEnter={onEnter} onExit={onExit}>
        <Show when={page()} keyed>
          {i => <div style={{ "background-color": getRandomColor(), padding: "1rem" }}>{i}.</div>}
        </Show>
      </Transition>
    </>
  );
};

const Collapse: Component = () => {
  const [show, toggleShow] = createSignal(true);

  const COLLAPSED_PROPERTIES = {
    height: 0,
    marginTop: 0,
    marginBottom: 0,
    paddingTop: 0,
    paddingBottom: 0,
    borderTopWidth: 0,
    borderBottomWidth: 0,
  };

  function getHeight(el: Element): string {
    const rect = el.getBoundingClientRect();
    return `${rect.height}px`;
  }

  function onEnter(el: Element, done: VoidFunction) {
    const a = el.animate(
      [
        COLLAPSED_PROPERTIES,
        {
          height: getHeight(el),
        },
      ],
      { duration: 500, easing: "ease" },
    );

    a.finished.then(done);
  }

  function onExit(el: Element, done: VoidFunction) {
    const a = el.animate(
      [
        {
          height: getHeight(el),
        },
        COLLAPSED_PROPERTIES,
      ],
      { duration: 500, easing: "ease" },
    );

    a.finished.then(done);
  }

  return (
    <>
      <button onClick={() => toggleShow(!show())}>{show() ? "Hide" : "Show"}</button>
      <br />
      <Transition mode="outin" name="collapse" onEnter={onEnter} onExit={onExit}>
        <Show when={show()}>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero,
            at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
          </p>
        </Show>
      </Transition>
    </>
  );
};

const Example = () => {
  const [show, toggleShow] = createSignal(true);

  return (
    <>
      <button onClick={() => toggleShow(!show())}>{show() ? "Hide" : "Show"}</button>
      <br />
      <br />
      <b>Transition:</b>
      <Transition name="slide-fade">
        {show() && (
          <div>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero,
            at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
          </div>
        )}
      </Transition>
      <br />
      <b>Animation:</b>
      <Transition name="bounce">
        {show() && (
          <div>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero,
            at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
          </div>
        )}
      </Transition>
      <br />
      <b>Custom JS:</b>
      <Transition
        onEnter={(el, done) => {
          const a = el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 600 });
          a.finished.then(done);
        }}
        onExit={(el, done) => {
          const a = el.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 600 });
          a.finished.then(done);
        }}
      >
        {show() && (
          <div>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero,
            at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
          </div>
        )}
      </Transition>
      <br />

      <b>Switch OutIn CSS</b>
      <br />
      <SwitchCSS />
      <br />

      <b>Switch OutIn JS</b>
      <br />
      <SwitchJS />
      <br />

      <b>Collapse OutIn CSS & JS</b>
      <br />
      <Collapse />
      <br />

      <b>Group</b>
      <br />
      <Group />
    </>
  );
};

export default Example;


================================================
FILE: dev/layout.astro
================================================
---
interface Props {
  title: string;
}
const { title } = Astro.props;

import "./styles.css";
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
  </head>
  <body>
    <nav>
      <ul class="flex flex-col p-3 gap-y-1">
        <li><a class="text-blue-500" href="/">Kitchen Sink</a></li>
        <li><a class="text-blue-500" href="/group">TransitionGroup</a></li>
      </ul>
    </nav>
    <main>
      <h1 class="mb-6">{title}</h1>
      <slot />
    </main>
  </body>
</html>


================================================
FILE: dev/pages/group.astro
================================================
---
import Layout from "../layout.astro";
import Exmaple from "../group";
---

<Layout title="<TransitionGroup>">
  <Exmaple client:idle />
</Layout>


================================================
FILE: dev/pages/index.astro
================================================
---
import Layout from "../layout.astro";
import Example from "../kitchen-sink";
---

<Layout title="Kitchen Sink">
  <Example client:idle />
</Layout>


================================================
FILE: dev/styles.css
================================================
body {
  background-color: #1e1e1e;
  color: #fff;
  font-family: "Roboto", sans-serif;
}
main {
  width: 100%;
  max-width: 600px;
  margin: 10vh auto;
}

h1 {
  font-size: 2rem;
  font-weight: 500;
}
h2 {
  font-size: 1.5rem;
  font-weight: 500;
}
h3 {
  font-size: 1.25rem;
  font-weight: 500;
}
h4 {
  font-size: 1rem;
  font-weight: 500;
}
h5 {
  font-size: 0.875rem;
  font-weight: 500;
}
h6 {
  font-size: 0.75rem;
  font-weight: 500;
}

button {
  @apply bg-blue-600 text-white font-medium py-1 px-2 rounded;
  @apply hover:bg-blue-700;
  @apply focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-opacity-50;
  @apply transition ease-in-out;
}

.container {
  position: relative;
}

.fade-enter-active,
.fade-exit-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-exit-to {
  opacity: 0;
}

.slide-fade-enter-active {
  transition: all 0.3s ease;
}
.slide-fade-exit-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-exit-to {
  transform: translateX(10px);
  opacity: 0;
}

.bounce-enter-active {
  animation: bounce-in 0.5s;
}
.bounce-exit-active {
  animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

.collapse-exit-active,
.collapse-enter-active {
  overflow: hidden;
}

.group-item {
  transition: all 0.5s;
}

.group-item-enter,
.group-item-exit-to {
  opacity: 0;
  transform: translateY(30px);
}
.group-item-exit-active {
  position: absolute;
}


================================================
FILE: netlify.toml
================================================
[build]
  base = "/"
  publish = "dev/dist/"
  command = "pnpm run build:site"

================================================
FILE: package.json
================================================
{
  "name": "solid-transition-group",
  "description": "Components to manage animations for SolidJS",
  "author": "Ryan Carniato",
  "license": "MIT",
  "version": "0.3.0",
  "homepage": "https://github.com/solidjs/solid-transition-group#readme",
  "repository": {
    "type": "git",
    "url": "https://github.com/solidjs/solid-transition-group"
  },
  "sideEffects": false,
  "private": false,
  "type": "module",
  "module": "dist/index.js",
  "types": "dist/index.d.ts",
  "exports": {
    "import": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "dev": "astro dev",
    "build:site": "astro build",
    "build": "tsc -b tsconfig.build.json",
    "test:client": "vitest",
    "test:ssr": "pnpm run test:client --mode ssr",
    "test": "concurrently pnpm:test:*",
    "format": "prettier -w **/*.{js,ts,json,css,tsx,jsx} --ignore-path .gitignore",
    "prepublishOnly": "pnpm build"
  },
  "devDependencies": {
    "@astrojs/solid-js": "^2.2.0",
    "@astrojs/tailwind": "^4.0.0",
    "astro": "^2.10.1",
    "concurrently": "^9.1.2",
    "jsdom": "^26.0.0",
    "prettier": "^3.4.2",
    "solid-js": "^1.9.4",
    "tailwindcss": "^3.3.3",
    "typescript": "^5.7.3",
    "vite-plugin-solid": "^2.11.0",
    "vitest": "^2.1.8"
  },
  "dependencies": {
    "@solid-primitives/refs": "^1.1.0",
    "@solid-primitives/transition-group": "^1.1.0"
  },
  "peerDependencies": {
    "solid-js": "^1.6.12"
  },
  "packageManager": "pnpm@9.15.0",
  "engines": {
    "node": ">=20.0.0",
    "pnpm": ">=9.0.0"
  }
}


================================================
FILE: src/index.ts
================================================
import { createMemo, type FlowComponent, type JSX } from "solid-js";
import { createSwitchTransition, createListTransition } from "@solid-primitives/transition-group";
import { resolveFirst, resolveElements } from "@solid-primitives/refs";

function createClassnames(props: TransitionProps & TransitionGroupProps) {
  return createMemo(() => {
    const name = props.name || "s";
    return {
      enterActive: (props.enterActiveClass || name + "-enter-active").split(" "),
      enter: (props.enterClass || name + "-enter").split(" "),
      enterTo: (props.enterToClass || name + "-enter-to").split(" "),
      exitActive: (props.exitActiveClass || name + "-exit-active").split(" "),
      exit: (props.exitClass || name + "-exit").split(" "),
      exitTo: (props.exitToClass || name + "-exit-to").split(" "),
      move: (props.moveClass || name + "-move").split(" "),
    };
  });
}

// https://github.com/solidjs-community/solid-transition-group/issues/12
// for the css transition be triggered properly on firefox
// we need to wait for two frames before changeing classes
function nextFrame(fn: () => void) {
  requestAnimationFrame(() => requestAnimationFrame(fn));
}

/**
 * Run an enter transition on an element - common for both Transition and TransitionGroup
 */
function enterTransition(
  classes: ReturnType<ReturnType<typeof createClassnames>>,
  events: TransitionEvents,
  el: Element,
  done?: VoidFunction,
) {
  const { onBeforeEnter, onEnter, onAfterEnter } = events;

  // before the elements are added to the DOM
  onBeforeEnter?.(el);

  el.classList.add(...classes.enter);
  el.classList.add(...classes.enterActive);

  // after the microtask the elements will be added to the DOM
  // and onEnter will be called in the same frame
  queueMicrotask(() => {
    // Don't animate element if it's not in the DOM
    // This can happen when elements are changed under Suspense
    if (!el.parentNode) return done?.();

    onEnter?.(el, () => endTransition());
  });

  nextFrame(() => {
    el.classList.remove(...classes.enter);
    el.classList.add(...classes.enterTo);

    if (!onEnter || onEnter.length < 2) {
      el.addEventListener("transitionend", endTransition);
      el.addEventListener("animationend", endTransition);
    }
  });

  function endTransition(e?: Event) {
    if (!e || e.target === el) {
      done?.(); // starts exit transition in "in-out" mode
      el.removeEventListener("transitionend", endTransition);
      el.removeEventListener("animationend", endTransition);
      el.classList.remove(...classes.enterActive);
      el.classList.remove(...classes.enterTo);
      onAfterEnter?.(el);
    }
  }
}

/**
 * @private
 *
 * Run an exit transition on an element - common for both Transition and TransitionGroup
 */
export function exitTransition(
  classes: ReturnType<ReturnType<typeof createClassnames>>,
  events: TransitionEvents,
  el: Element,
  done?: VoidFunction,
) {
  const { onBeforeExit, onExit, onAfterExit } = events;

  // Don't animate element if it's not in the DOM
  // This can happen when elements are changed under Suspense
  if (!el.parentNode) return done?.();

  onBeforeExit?.(el);

  el.classList.add(...classes.exit);
  el.classList.add(...classes.exitActive);

  onExit?.(el, () => endTransition());

  nextFrame(() => {
    el.classList.remove(...classes.exit);
    el.classList.add(...classes.exitTo);

    if (!onExit || onExit.length < 2) {
      el.addEventListener("transitionend", endTransition);
      el.addEventListener("animationend", endTransition);
    }
  });

  function endTransition(e?: Event) {
    if (!e || e.target === el) {
      // calling done() will remove element from the DOM,
      // but also trigger onChange callback in <TransitionGroup>.
      // Which is why the classes need to removed afterwards,
      // so that removing them won't change el styles when for the move transition
      done?.();
      el.removeEventListener("transitionend", endTransition);
      el.removeEventListener("animationend", endTransition);
      el.classList.remove(...classes.exitActive);
      el.classList.remove(...classes.exitTo);
      onAfterExit?.(el);
    }
  }
}

export type TransitionEvents = {
  /**
   * Function called before the enter transition starts.
   * The {@link element} is not yet rendered.
   */
  onBeforeEnter?: (element: Element) => void;
  /**
   * Function called when the enter transition starts.
   * The {@link element} is rendered to the DOM.
   *
   * Call {@link done} to end the transition - removes the enter classes,
   * and calls {@link TransitionEvents.onAfterEnter}.
   * If the parameter for {@link done} is not provided, it will be called on `transitionend` or `animationend`.
   */
  onEnter?: (element: Element, done: () => void) => void;
  /**
   * Function called after the enter transition ends.
   * The {@link element} is removed from the DOM.
   */
  onAfterEnter?: (element: Element) => void;
  /**
   * Function called before the exit transition starts.
   * The {@link element} is still rendered, exit classes are not yet applied.
   */
  onBeforeExit?: (element: Element) => void;
  /**
   * Function called when the exit transition starts, after the exit classes are applied
   * ({@link TransitionProps.enterToClass} and {@link TransitionProps.exitActiveClass}).
   * The {@link element} is still rendered.
   *
   * Call {@link done} to end the transition - removes exit classes,
   * calls {@link TransitionEvents.onAfterExit} and removes the element from the DOM.
   * If the parameter for {@link done} is not provided, it will be called on `transitionend` or `animationend`.
   */
  onExit?: (element: Element, done: () => void) => void;
  /**
   * Function called after the exit transition ends.
   * The {@link element} is removed from the DOM.
   */
  onAfterExit?: (element: Element) => void;
};

/**
 * Props for the {@link Transition} component.
 */
export type TransitionProps = TransitionEvents & {
  /**
   * Used to automatically generate transition CSS class names.
   * e.g. `name: 'fade'` will auto expand to `.fade-enter`, `.fade-enter-active`, etc.
   * Defaults to `"s"`.
   */
  name?: string;
  /**
   * CSS class applied to the entering element for the entire duration of the enter transition.
   * Defaults to `"s-enter-active"`.
   */
  enterActiveClass?: string;
  /**
   * CSS class applied to the entering element at the start of the enter transition, and removed the frame after.
   * Defaults to `"s-enter"`.
   */
  enterClass?: string;
  /**
   * CSS class applied to the entering element after the enter transition starts.
   * Defaults to `"s-enter-to"`.
   */
  enterToClass?: string;
  /**
   * CSS class applied to the exiting element for the entire duration of the exit transition.
   * Defaults to `"s-exit-active"`.
   */
  exitActiveClass?: string;
  /**
   * CSS class applied to the exiting element at the start of the exit transition, and removed the frame after.
   * Defaults to `"s-exit"`.
   */
  exitClass?: string;
  /**
   * CSS class applied to the exiting element after the exit transition starts.
   * Defaults to `"s-exit-to"`.
   */
  exitToClass?: string;
  /**
   * Whether to apply transition on initial render. Defaults to `false`.
   */
  appear?: boolean;
  /**
   * Controls the timing sequence of leaving/entering transitions.
   * Available modes are `"outin"` and `"inout"`;
   * Defaults to simultaneous.
   */
  mode?: "inout" | "outin";
};

const TRANSITION_MODE_MAP = {
  inout: "in-out",
  outin: "out-in",
} as const;

/**
 * The `<Transition>` component lets you apply enter and leave animations on element passed to `props.children`.
 *
 * It only supports transitioning a single element at a time.
 *
 * @param props {@link TransitionProps}
 */
export const Transition: FlowComponent<TransitionProps> = props => {
  const classnames = createClassnames(props);

  return createSwitchTransition(
    resolveFirst(() => props.children),
    {
      mode: TRANSITION_MODE_MAP[props.mode!],
      appear: props.appear,
      onEnter(el, done) {
        enterTransition(classnames(), props, el, done);
      },
      onExit(el, done) {
        exitTransition(classnames(), props, el, done);
      },
    },
  ) as unknown as JSX.Element;
};

/**
 * Props for the {@link TransitionGroup} component.
 */
export type TransitionGroupProps = Omit<TransitionProps, "mode"> & {
  /**
   * CSS class applied to the moving elements for the entire duration of the move transition.
   * Defaults to `"s-move"`.
   */
  moveClass?: string;
};

/**
 * The `<TransitionGroup>` component lets you apply enter and leave animations on elements passed to `props.children`.
 *
 * It supports transitioning multiple elements at a time and moving elements around.
 *
 * @param props {@link TransitionGroupProps}
 */
export const TransitionGroup: FlowComponent<TransitionGroupProps> = props => {
  const classnames = createClassnames(props);

  return createListTransition(resolveElements(() => props.children).toArray, {
    appear: props.appear,
    exitMethod: "keep-index",
    onChange({ added, removed, finishRemoved, list }) {
      const classes = classnames();

      // ENTER
      for (const el of added) {
        enterTransition(classes, props, el);
      }

      // MOVE
      const toMove: { el: HTMLElement | SVGElement; rect: DOMRect }[] = [];
      // get rects of elements before the changes to the DOM
      for (const el of list) {
        if (el.isConnected && (el instanceof HTMLElement || el instanceof SVGElement)) {
          toMove.push({ el, rect: el.getBoundingClientRect() });
        }
      }

      // wait for th new list to be rendered
      queueMicrotask(() => {
        const moved: (HTMLElement | SVGElement)[] = [];

        for (const { el, rect } of toMove) {
          if (el.isConnected) {
            const newRect = el.getBoundingClientRect(),
              dX = rect.left - newRect.left,
              dY = rect.top - newRect.top;
            if (dX || dY) {
              // set els to their old position before transition
              el.style.transform = `translate(${dX}px, ${dY}px)`;
              el.style.transitionDuration = "0s";
              moved.push(el);
            }
          }
        }

        document.body.offsetHeight; // force reflow

        for (const el of moved) {
          el.classList.add(...classes.move);

          // clear transition - els will move to their new position
          el.style.transform = el.style.transitionDuration = "";

          function endTransition(e: Event) {
            if (e.target === el || /transform$/.test((e as TransitionEvent).propertyName)) {
              el.removeEventListener("transitionend", endTransition);
              el.classList.remove(...classes.move);
            }
          }
          el.addEventListener("transitionend", endTransition);
        }
      });

      // EXIT
      for (const el of removed) {
        exitTransition(classes, props, el, () => finishRemoved([el]));
      }
    },
  }) as unknown as JSX.Element;
};


================================================
FILE: tailwind.config.cjs
================================================
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
  theme: {
    extend: {}
  },
  plugins: []
};


================================================
FILE: test/index.test.tsx
================================================
// Flush animation frames manually
const rafQueue: VoidFunction[] = [];
(globalThis as any).requestAnimationFrame = (cb: VoidFunction) => {
  rafQueue.push(cb);
};
function flushRaf() {
  const queue = rafQueue.slice();
  rafQueue.length = 0;
  queue.forEach(cb => cb());
}

import { Show, createRoot, createSignal } from "solid-js";
import { describe, expect, it } from "vitest";
import { Transition, exitTransition } from "../src/index.js";

describe("Transition", () => {
  it("matches the timing of vue out-in transition", async () => {
    const captured: [type: string, parentNode: boolean, classname: string][] = [];
    let runEnter!: VoidFunction;
    let runExit!: VoidFunction;

    function onBeforeEnter(el: Element) {
      captured.push(["before enter", el.parentNode !== null, el.className]);
      requestAnimationFrame(() => {
        captured.push(["1 frame", el.parentNode !== null, el.className]);
        requestAnimationFrame(() => {
          captured.push(["2 frame", el.parentNode !== null, el.className]);
          requestAnimationFrame(() => {
            captured.push(["3 frame", el.parentNode !== null, el.className]);
          });
        });
      });
    }
    function onEnter(el: Element, done: VoidFunction) {
      captured.push(["enter", el.parentNode !== null, el.className]);
      runEnter = done;
    }
    function onAfterEnter(el: Element) {
      captured.push(["after enter", el.parentNode !== null, el.className]);
    }
    function onBeforeExit(el: Element) {
      captured.push(["before exit", el.parentNode !== null, el.className]);
    }
    function onExit(el: Element, done: VoidFunction) {
      captured.push(["exit", el.parentNode !== null, el.className]);
      runExit = done;
    }
    function onAfterExit(el: Element) {
      captured.push(["after exit", el.parentNode !== null, el.className]);
    }

    const [page, setPage] = createSignal(1);

    const dispose = createRoot(dispose => {
      <div>
        <Transition
          mode="outin"
          onBeforeEnter={onBeforeEnter}
          onEnter={onEnter}
          onAfterEnter={onAfterEnter}
          onBeforeExit={onBeforeExit}
          onExit={onExit}
          onAfterExit={onAfterExit}
        >
          <Show when={page()} keyed>
            {i => <div>{i}</div>}
          </Show>
        </Transition>
      </div>;

      return dispose;
    });

    expect(captured).toHaveLength(0);

    setPage(2);

    flushRaf();
    flushRaf();

    runExit();

    // enter - await microtask
    await Promise.resolve();

    flushRaf();
    flushRaf();
    flushRaf();

    runEnter();

    expect(captured).toEqual([
      ["before exit", true, ""],
      ["exit", true, "s-exit s-exit-active"],
      ["before enter", false, ""],
      ["after exit", false, ""],
      ["enter", true, "s-enter s-enter-active"],
      ["1 frame", true, "s-enter s-enter-active"],
      ["2 frame", true, "s-enter s-enter-active"],
      ["3 frame", true, "s-enter-active s-enter-to"],
      ["after enter", true, ""],
    ]);

    dispose();
  });
});

describe("exitTransition", () => {
  it("removes exit classes after calling done()", () => {
    const parent = document.createElement("div");
    const el = document.createElement("div");
    parent.appendChild(el);

    let capturedClassName: string | null = null;

    exitTransition(
      {
        enterActive: ["s-enter-active"],
        enterTo: ["s-enter-to"],
        enter: ["s-enter"],
        exitActive: ["s-exit-active"],
        exitTo: ["s-exit-to"],
        exit: ["s-exit"],
        move: ["s-move"],
      },
      {},
      el,
      () => (capturedClassName = el.className),
    );

    expect(el.className).toBe("s-exit s-exit-active");
    expect(capturedClassName).toBe(null);

    flushRaf();
    flushRaf();

    expect(el.className).toBe("s-exit-active s-exit-to");
    expect(capturedClassName).toBe(null);

    el.dispatchEvent(new Event("transitionend"));

    expect(capturedClassName).toBe("s-exit-active s-exit-to");
  });
});


================================================
FILE: test/server.test.tsx
================================================
import { describe, expect, it } from "vitest";
import { Transition } from "../src/index.js";
import { renderToString } from "solid-js/web";

describe("Transition", () => {
  it("returns elements in SSR", () => {
    const html = renderToString(() => (
      <Transition>
        <div>hello</div>
      </Transition>
    ));

    expect(html).toBe("<div>hello</div>");
  });
});


================================================
FILE: tsconfig.build.json
================================================
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "composite": true,
    "rootDir": "src",
    "outDir": "dist",
    "noEmit": false
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "module": "NodeNext",
    "target": "ESNext",
    "moduleResolution": "NodeNext",
    "lib": ["DOM", "ESNext"],
    "types": [],
    "newLine": "LF",
    "noEmit": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "forceConsistentCasingInFileNames": true,
    "noUncheckedIndexedAccess": true,
    "skipLibCheck": true,
    "jsx": "preserve",
    "jsxImportSource": "solid-js",
    "paths": {
      "solid-transition-group": ["./src/index.ts"]
    }
  },
  "exclude": ["node_modules", "dist"]
}


================================================
FILE: vitest.config.ts
================================================
import { defineConfig } from "vitest/config";
import solidPlugin from "vite-plugin-solid";

export default defineConfig(({ mode }) => {
  // to test in server environment, run with "--mode ssr" or "--mode test:ssr" flag
  // loads only server.test.ts file
  const testSSR = mode === "test:ssr" || mode === "ssr";

  return {
    plugins: [
      solidPlugin({
        // https://github.com/solidjs/solid-refresh/issues/29
        hot: false,
        // For testing SSR we need to do a SSR JSX transform
        solid: { generate: testSSR ? "ssr" : "dom" },
      }),
    ],
    test: {
      watch: false,
      isolate: !testSSR,
      environment: testSSR ? "node" : "jsdom",
      transformMode: { web: [/\.[jt]sx$/] },
      ...(testSSR
        ? {
            include: ["test/server.test.{ts,tsx}"],
          }
        : {
            include: ["test/*.test.{ts,tsx}"],
            exclude: ["test/server.test.{ts,tsx}"],
          }),
    },
    resolve: {
      conditions: testSSR ? ["node"] : ["browser", "development"],
    },
  };
});
Download .txt
gitextract_g0bh3662/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── README.md
├── astro.config.ts
├── dev/
│   ├── env.d.ts
│   ├── group.tsx
│   ├── kitchen-sink.tsx
│   ├── layout.astro
│   ├── pages/
│   │   ├── group.astro
│   │   └── index.astro
│   └── styles.css
├── netlify.toml
├── package.json
├── src/
│   └── index.ts
├── tailwind.config.cjs
├── test/
│   ├── index.test.tsx
│   └── server.test.tsx
├── tsconfig.build.json
├── tsconfig.json
└── vitest.config.ts
Download .txt
SYMBOL INDEX (25 symbols across 4 files)

FILE: dev/group.tsx
  function shuffle (line 4) | function shuffle<T>(array: T[]): T[] {

FILE: dev/kitchen-sink.tsx
  function shuffle (line 4) | function shuffle<T extends any[]>(array: T): T {
  function onEnter (line 87) | function onEnter(el: Element, done: VoidFunction) {
  function onExit (line 91) | function onExit(el: Element, done: VoidFunction) {
  function getHeight (line 122) | function getHeight(el: Element): string {
  function onEnter (line 127) | function onEnter(el: Element, done: VoidFunction) {
  function onExit (line 141) | function onExit(el: Element, done: VoidFunction) {

FILE: src/index.ts
  function createClassnames (line 5) | function createClassnames(props: TransitionProps & TransitionGroupProps) {
  function nextFrame (line 23) | function nextFrame(fn: () => void) {
  function enterTransition (line 30) | function enterTransition(
  function exitTransition (line 81) | function exitTransition(
  type TransitionEvents (line 126) | type TransitionEvents = {
  type TransitionProps (line 171) | type TransitionProps = TransitionEvents & {
  constant TRANSITION_MODE_MAP (line 220) | const TRANSITION_MODE_MAP = {
  method onEnter (line 240) | onEnter(el, done) {
  method onExit (line 243) | onExit(el, done) {
  type TransitionGroupProps (line 253) | type TransitionGroupProps = Omit<TransitionProps, "mode"> & {
  method onChange (line 274) | onChange({ added, removed, finishRemoved, list }) {

FILE: test/index.test.tsx
  function flushRaf (line 6) | function flushRaf() {
  function onBeforeEnter (line 22) | function onBeforeEnter(el: Element) {
  function onEnter (line 34) | function onEnter(el: Element, done: VoidFunction) {
  function onAfterEnter (line 38) | function onAfterEnter(el: Element) {
  function onBeforeExit (line 41) | function onBeforeExit(el: Element) {
  function onExit (line 44) | function onExit(el: Element, done: VoidFunction) {
  function onAfterExit (line 48) | function onAfterExit(el: Element) {
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (47K chars).
[
  {
    "path": ".github/workflows/tests.yml",
    "chars": 635,
    "preview": "name: Build and Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  build:\n    runs-on:"
  },
  {
    "path": ".gitignore",
    "chars": 56,
    "preview": "node_modules/\ndist/\nlib/\ncoverage/\ntypes/\n*.tsbuildinfo\n"
  },
  {
    "path": ".prettierrc",
    "chars": 217,
    "preview": "{\n  \"trailingComma\": \"all\",\n  \"tabWidth\": 2,\n  \"printWidth\": 100,\n  \"semi\": true,\n  \"singleQuote\": false,\n  \"useTabs\": f"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 125,
    "preview": "{\n  \"recommendations\": [\n    \"esbenp.prettier-vscode\",\n    \"astro-build.astro-vscode\",\n    \"bradlc.vscode-tailwindcss\"\n "
  },
  {
    "path": ".vscode/settings.json",
    "chars": 68,
    "preview": "{\n  \"cSpell.words\": [\"astrojs\", \"outin\"],\n  \"css.validate\": false\n}\n"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "MIT License\n\nCopyright (c) 2020-2021 Ryan Carniato\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "README.md",
    "chars": 10602,
    "preview": "<p>\n  <img width=\"100%\" src=\"https://assets.solidjs.com/banner?project=Transition%20Group&type=core\" alt=\"Solid Transiti"
  },
  {
    "path": "astro.config.ts",
    "chars": 565,
    "preview": "import path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { defineConfig } from \"astro/config\";\nimport solid "
  },
  {
    "path": "dev/env.d.ts",
    "chars": 39,
    "preview": "/// <reference types=\"astro/client\" />\n"
  },
  {
    "path": "dev/group.tsx",
    "chars": 1964,
    "preview": "import { Component, For, createSignal } from \"solid-js\";\nimport { TransitionGroup } from \"solid-transition-group\";\n\nfunc"
  },
  {
    "path": "dev/kitchen-sink.tsx",
    "chars": 6600,
    "preview": "import { Component, For, Show, createSignal } from \"solid-js\";\nimport { Transition, TransitionGroup } from \"solid-transi"
  },
  {
    "path": "dev/layout.astro",
    "chars": 547,
    "preview": "---\ninterface Props {\n  title: string;\n}\nconst { title } = Astro.props;\n\nimport \"./styles.css\";\n---\n\n<html lang=\"en\">\n  "
  },
  {
    "path": "dev/pages/group.astro",
    "chars": 150,
    "preview": "---\nimport Layout from \"../layout.astro\";\nimport Exmaple from \"../group\";\n---\n\n<Layout title=\"<TransitionGroup>\">\n  <Exm"
  },
  {
    "path": "dev/pages/index.astro",
    "chars": 152,
    "preview": "---\nimport Layout from \"../layout.astro\";\nimport Example from \"../kitchen-sink\";\n---\n\n<Layout title=\"Kitchen Sink\">\n  <E"
  },
  {
    "path": "dev/styles.css",
    "chars": 1551,
    "preview": "body {\n  background-color: #1e1e1e;\n  color: #fff;\n  font-family: \"Roboto\", sans-serif;\n}\nmain {\n  width: 100%;\n  max-wi"
  },
  {
    "path": "netlify.toml",
    "chars": 78,
    "preview": "[build]\n  base = \"/\"\n  publish = \"dev/dist/\"\n  command = \"pnpm run build:site\""
  },
  {
    "path": "package.json",
    "chars": 1593,
    "preview": "{\n  \"name\": \"solid-transition-group\",\n  \"description\": \"Components to manage animations for SolidJS\",\n  \"author\": \"Ryan "
  },
  {
    "path": "src/index.ts",
    "chars": 11067,
    "preview": "import { createMemo, type FlowComponent, type JSX } from \"solid-js\";\nimport { createSwitchTransition, createListTransiti"
  },
  {
    "path": "tailwind.config.cjs",
    "chars": 179,
    "preview": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\"./**/*.{astro,html,js,jsx,md,mdx,svelte,ts,t"
  },
  {
    "path": "test/index.test.tsx",
    "chars": 4030,
    "preview": "// Flush animation frames manually\nconst rafQueue: VoidFunction[] = [];\n(globalThis as any).requestAnimationFrame = (cb:"
  },
  {
    "path": "test/server.test.tsx",
    "chars": 378,
    "preview": "import { describe, expect, it } from \"vitest\";\nimport { Transition } from \"../src/index.js\";\nimport { renderToString } f"
  },
  {
    "path": "tsconfig.build.json",
    "chars": 211,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"rootDir\": \"src\",\n    \"outDir\": \"dis"
  },
  {
    "path": "tsconfig.json",
    "chars": 604,
    "preview": "{\n  \"compilerOptions\": {\n    \"module\": \"NodeNext\",\n    \"target\": \"ESNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"lib\""
  },
  {
    "path": "vitest.config.ts",
    "chars": 1047,
    "preview": "import { defineConfig } from \"vitest/config\";\nimport solidPlugin from \"vite-plugin-solid\";\n\nexport default defineConfig("
  }
]

About this extraction

This page contains the full source code of the solidjs-community/solid-transition-group GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (42.5 KB), approximately 11.1k tokens, and a symbol index with 25 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!