Compare commits

..

3 Commits

Author SHA1 Message Date
467455c49a Update README 2024-08-07 08:53:18 -07:00
30d6d1dddd Fix semantic HTML for better embedding 2024-08-06 19:19:52 -07:00
dc95275086 Fix issue with item name generation 2024-08-06 17:49:02 -07:00
6 changed files with 68 additions and 52 deletions

View File

@ -1,3 +1,7 @@
# demos-react-animated-list # demos-react-animated-list
Demonstration of a solution for animating a re-ordered React list. Demonstration of a solution for animating a re-ordered React list using refs, local state, and a layout effect.
The source code here complements [this blog post](https://matthewcardarelli.com/blog/dev-tricks/2024/08/07/react-animated-list.html). It is for educational purposes, and should not be incorporated into other programs without robust testing.
All files in this repository are distributed under the MIT License.

View File

@ -2,10 +2,10 @@
"name": "demo-react-animated-list", "name": "demo-react-animated-list",
"version": "1.0.0", "version": "1.0.0",
"description": "A demo of an implementation of an animated list in React Typescript.", "description": "A demo of an implementation of an animated list in React Typescript.",
"main": "index.tsx", "main": "src/index.tsx",
"scripts": { "scripts": {
"build": "esbuild index.tsx --bundle --sourcemap --outfile=./dist/demo-react-animated-list.js", "build": "esbuild ./src/index.tsx --bundle --sourcemap --outfile=./dist/demo-react-animated-list.js",
"dev": "esbuild index.tsx --bundle --watch --serve --outdir=./public/scripts --servedir=./public", "dev": "esbuild ./src/index.tsx --bundle --watch --serve --outdir=./public/scripts --servedir=./public",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {

View File

@ -3,6 +3,16 @@ import { useAnimatedListItems } from './useAnimatedListItems';
type Mutation = 'reverse' | 'randomize' | 'insert' | 'remove'; type Mutation = 'reverse' | 'randomize' | 'insert' | 'remove';
function getRandomChar(): string {
return String.fromCharCode(98 + Math.floor(Math.random() * 25));
}
function getRandomItemName(): string {
return `Serial Number - ${new Array(16)
.fill('')
.map(() => getRandomChar())
.join('')}`;
}
/** /**
* Randomizer function shamelessly copied unmodified from https://stackoverflow.com/a/12646864 * Randomizer function shamelessly copied unmodified from https://stackoverflow.com/a/12646864
* *
@ -21,7 +31,7 @@ function shuffleArray(array) {
function insertRandomlyIntoArray(array: string[]): string[] { function insertRandomlyIntoArray(array: string[]): string[] {
const insertBefore = Math.floor(Math.random() * (array.length + 1)); const insertBefore = Math.floor(Math.random() * (array.length + 1));
const newItem = `Serial Number ${crypto.randomUUID()}`; const newItem = getRandomItemName();
if (insertBefore === 0) { if (insertBefore === 0) {
return [newItem, ...array]; return [newItem, ...array];
} }
@ -65,56 +75,52 @@ export const AnimatedListDemo = () => {
} }
return newState; return newState;
}, },
[ new Array(4).fill('').map(() => getRandomItemName()),
`Serial Number ${crypto.randomUUID()}`,
`Serial Number ${crypto.randomUUID()}`,
`Serial Number ${crypto.randomUUID()}`,
`Serial Number ${crypto.randomUUID()}`,
],
); );
const { updateItemRef, updateItemPositions, itemStyles } = const { updateItemRef, updateItemPositions, itemStyles } =
useAnimatedListItems({ keys: items }); useAnimatedListItems({ keys: items });
return ( return (
<main> <>
<button <div className="buttons">
type="button" <button
onClick={() => { type="button"
updateItemPositions(); onClick={() => {
mutateItems('reverse'); updateItemPositions();
}} mutateItems('reverse');
> }}
Reverse >
</button> Reverse
<button </button>
type="button" <button
onClick={() => { type="button"
updateItemPositions(); onClick={() => {
mutateItems('randomize'); updateItemPositions();
}} mutateItems('randomize');
> }}
Randomize >
</button> Randomize
<button </button>
type="button" <button
onClick={() => { type="button"
updateItemPositions(); onClick={() => {
mutateItems('insert'); updateItemPositions();
}} mutateItems('insert');
> }}
Insert >
</button> Insert
<button </button>
type="button" <button
onClick={() => { type="button"
updateItemPositions(); onClick={() => {
mutateItems('remove'); updateItemPositions();
}} mutateItems('remove');
> }}
Remove >
</button> Remove
</button>
</div>
<ol> <ol>
{items.map((item) => { {items.map((item) => {
return ( return (
@ -128,6 +134,6 @@ export const AnimatedListDemo = () => {
); );
})} })}
</ol> </ol>
</main> </>
); );
}; };

View File

@ -10,7 +10,7 @@ class AnimatedListComponent extends HTMLElement {
} }
connectedCallback() { connectedCallback() {
const rootNode = document.createElement('main'); const rootNode = document.createElement('section');
this.appendChild(rootNode); this.appendChild(rootNode);
this.root = createRoot(rootNode); this.root = createRoot(rootNode);
this.root.render(<AnimatedListDemo />); this.root.render(<AnimatedListDemo />);

View File

@ -8,5 +8,11 @@
"noEmit": true, "noEmit": true,
"alwaysStrict": true "alwaysStrict": true
}, },
"include": ["*.ts", "*.tsx"] "include": [
"*.ts",
"*.tsx",
"src/index.tsx",
"src/AnimatedListDemo.tsx",
"src/useAnimatedListItems.ts"
]
} }