From a847816f23da167e0763e76394bf22cfe7962257 Mon Sep 17 00:00:00 2001 From: Riri Date: Sat, 6 Apr 2024 00:03:05 +0800 Subject: [PATCH] feat: support view transition --- build.config.ts | 4 +- .../BasicUseListTransition/index.tsx | 12 +- package.json | 18 +++ pnpm-lock.yaml | 149 +++++++++++------- src/hooks/useListTransition.tsx | 78 +++++---- src/status.ts | 1 + src/viewTransition.ts | 14 ++ tsconfig.json | 2 +- 8 files changed, 182 insertions(+), 96 deletions(-) create mode 100644 src/viewTransition.ts diff --git a/build.config.ts b/build.config.ts index 21d8a5d..3e6cf0a 100644 --- a/build.config.ts +++ b/build.config.ts @@ -6,13 +6,15 @@ export default defineBuildConfig({ 'src/hooks/useTransition.ts', 'src/hooks/useSwitchTransition/index.tsx', 'src/hooks/useListTransition.tsx', + 'src/viewTransition.ts', ], + externals: ['react', 'react-dom'], declaration: true, clean: true, rollup: { emitCJS: true, esbuild: { - // minify: true, + minify: true, }, }, }) diff --git a/docs/components/BasicUseListTransition/index.tsx b/docs/components/BasicUseListTransition/index.tsx index c53e56a..1cb4bb3 100644 --- a/docs/components/BasicUseListTransition/index.tsx +++ b/docs/components/BasicUseListTransition/index.tsx @@ -1,5 +1,6 @@ import { useRef, useState } from 'react' -import { getSimpleStatus, useListTransition } from 'transition-hooks' +import { useListTransition } from 'transition-hooks' +import { startViewTransition } from 'transition-hooks/viewTransition' import { Button } from '../Button' import { shuffle } from '../utils' @@ -7,24 +8,25 @@ const numbers = Array.from({ length: 5 }, (_, i) => i) export function BasicUseListTransition() { const [list, setList] = useState(numbers) const idRef = useRef(numbers.length) - const { transitionList } = useListTransition(list, { entered: false, timeout: 300, keyExtractor: i => i }) + const { transitionList } = useListTransition(list, { entered: false, timeout: 300, keyExtractor: i => i, viewTransition: startViewTransition }) return (
-
+
    - {transitionList((item, { status }) => { - const simpleStatus = getSimpleStatus(status) + {transitionList((item, { key, simpleStatus }) => { return (
  • - {item} diff --git a/package.json b/package.json index 425054e..13bb4b6 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,22 @@ ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs" + }, + "./useTransition": { + "import": "./dist/hooks/useTransition.mjs", + "require": "./dist/hooks/useTransition.cjs" + }, + "./useSwitchTransition": { + "import": "./dist/hooks/useSwitchTransition/index.mjs", + "require": "./dist/hooks/useSwitchTransition/index.cjs" + }, + "./useListTransition": { + "import": "./dist/hooks/useListTransition.mjs", + "require": "./dist/hooks/useListTransition.cjs" + }, + "./viewTransition": { + "import": "./dist/viewTransition.mjs", + "require": "./dist/viewTransition.cjs" } }, "main": "./dist/index.cjs", @@ -62,11 +78,13 @@ "@ririd/eslint-config": "^1.1.0", "@types/node": "^18.15.11", "@types/react": "^18.2.73", + "@types/react-dom": "^18.2.24", "bumpp": "^9.4.0", "eslint": "^8.56.0", "esno": "^4.7.0", "lint-staged": "15.2.2", "react": "^18.2.0", + "react-dom": "^18.2.0", "rimraf": "5.0.5", "simple-git-hooks": "^2.11.1", "transition-hooks": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f00ab91..e0fbad0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@types/react': specifier: ^18.2.73 version: 18.2.73 + '@types/react-dom': + specifier: ^18.2.24 + version: 18.2.24 bumpp: specifier: ^9.4.0 version: 9.4.0 @@ -32,6 +35,9 @@ importers: react: specifier: ^18.2.0 version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) rimraf: specifier: 5.0.5 version: 5.0.5 @@ -52,7 +58,7 @@ importers: version: 5.2.7(@types/node@18.15.11) vocs: specifier: 1.0.0-alpha.45 - version: 1.0.0-alpha.45(@types/node@18.15.11)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0)(rollup@4.13.2)(typescript@5.4.3) + version: 1.0.0-alpha.45(@types/node@18.15.11)(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0)(rollup@4.13.2)(typescript@5.4.3) playground: dependencies: @@ -1693,7 +1699,7 @@ packages: '@babel/runtime': 7.24.1 dev: true - /@radix-ui/react-accordion@1.1.2(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} peerDependencies: '@types/react': '*' @@ -1708,20 +1714,21 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collapsible': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-collection': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-arrow@1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: '@types/react': '*' @@ -1735,13 +1742,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-collapsible@1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: '@types/react': '*' @@ -1759,16 +1767,17 @@ packages: '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-collection@1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: '@types/react': '*' @@ -1784,9 +1793,10 @@ packages: '@babel/runtime': 7.24.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -1819,7 +1829,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-dialog@1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: '@types/react': '*' @@ -1836,16 +1846,17 @@ packages: '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 aria-hidden: 1.2.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1866,7 +1877,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-dismissable-layer@1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: '@types/react': '*' @@ -1882,10 +1893,11 @@ packages: '@babel/runtime': 7.24.1 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -1904,7 +1916,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-focus-scope@1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} peerDependencies: '@types/react': '*' @@ -1919,9 +1931,10 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -1949,7 +1962,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-label@2.0.2(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==} peerDependencies: '@types/react': '*' @@ -1963,13 +1976,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-navigation-menu@1.1.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-navigation-menu@1.1.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Cc+seCS3PmWmjI51ufGG7zp1cAAIRqHVw7C9LOA2TZ+R4hG6rDvHcTqIsEEFLmZO3zNVH72jOOE7kKNy8W+RtA==} peerDependencies: '@types/react': '*' @@ -1984,25 +1998,26 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-popover@1.0.7(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} peerDependencies: '@types/react': '*' @@ -2019,24 +2034,25 @@ packages: '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 aria-hidden: 1.2.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.73)(react@18.2.0) dev: true - /@radix-ui/react-popper@1.1.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} peerDependencies: '@types/react': '*' @@ -2051,21 +2067,22 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/rect': 1.0.1 '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-portal@1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: '@types/react': '*' @@ -2079,13 +2096,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-presence@1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: '@types/react': '*' @@ -2102,11 +2120,12 @@ packages: '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-primitive@1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: '@types/react': '*' @@ -2122,11 +2141,12 @@ packages: '@babel/runtime': 7.24.1 '@radix-ui/react-slot': 1.0.2(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-roving-focus@1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: '@types/react': '*' @@ -2141,15 +2161,16 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -2169,7 +2190,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-tabs@1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} peerDependencies: '@types/react': '*' @@ -2187,11 +2208,12 @@ packages: '@radix-ui/react-context': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.73)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -2298,7 +2320,7 @@ packages: react: 18.2.0 dev: true - /@radix-ui/react-visually-hidden@1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: '@types/react': '*' @@ -2312,8 +2334,9 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.73 + '@types/react-dom': 18.2.24 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -2777,6 +2800,12 @@ packages: '@types/react': 18.2.73 dev: false + /@types/react-dom@18.2.24: + resolution: {integrity: sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==} + dependencies: + '@types/react': 18.2.73 + dev: true + /@types/react@18.2.73: resolution: {integrity: sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==} dependencies: @@ -9376,7 +9405,7 @@ packages: optionalDependencies: fsevents: 2.3.3 - /vocs@1.0.0-alpha.45(@types/node@18.15.11)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0)(rollup@4.13.2)(typescript@5.4.3): + /vocs@1.0.0-alpha.45(@types/node@18.15.11)(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0)(rollup@4.13.2)(typescript@5.4.3): resolution: {integrity: sha512-FUDuN6V0FwAlG8QAg7VHBm0LAGsvtVwxU2FGIW+eTiFQFuuzsV9Ojh4vg3aAYR3VcICL9w7ipsyt+7D0b3dJNw==} hasBin: true peerDependencies: @@ -9389,13 +9418,13 @@ packages: '@mdx-js/rollup': 3.0.1(rollup@4.13.2) '@noble/hashes': 1.4.0 '@radix-ui/colors': 3.0.0 - '@radix-ui/react-accordion': 1.1.2(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-dialog': 1.0.5(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-accordion': 1.1.2(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-icons': 1.3.0(react@18.2.0) - '@radix-ui/react-label': 2.0.2(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-navigation-menu': 1.1.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-popover': 1.0.7(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-tabs': 1.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-label': 2.0.2(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-navigation-menu': 1.1.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popover': 1.0.7(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-tabs': 1.0.4(@types/react-dom@18.2.24)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) '@shikijs/rehype': 1.2.3 '@shikijs/transformers': 1.2.3 '@shikijs/twoslash': 1.2.3(typescript@5.4.3) diff --git a/src/hooks/useListTransition.tsx b/src/hooks/useListTransition.tsx index 4e3fa50..c188269 100644 --- a/src/hooks/useListTransition.tsx +++ b/src/hooks/useListTransition.tsx @@ -6,7 +6,7 @@ import type { Timeout } from '../helpers/getTimeout' import { getTimeout } from '../helpers/getTimeout' import useMemoizedFn from '../helpers/useMemorizeFn' -type RenderCallback = (item: Item, stage: StatusState) => React.ReactNode +type RenderCallback = (item: Item, stage: StatusState & { key: string | number }) => React.ReactNode type ItemWithState = { item: Item @@ -18,8 +18,22 @@ interface ItemWithKey { index: number } -export function useListTransition(list: Array, options?: { timeout: Timeout, entered?: boolean, keyExtractor?: (item: Item) => string | number }) { - const { timeout = 300, entered = true, keyExtractor: _keyExtractor } = options || {} +interface ListTransitionOptions { + timeout: Timeout + entered?: boolean + keyExtractor?: (item: Item) => string | number + viewTransition?: (fn: () => void) => void +} + +const noop = (fn: any) => fn() + +export function useListTransition(list: Array, options?: ListTransitionOptions) { + const { + timeout = 300, + entered = true, + keyExtractor: _keyExtractor, + viewTransition = noop, + } = options || {} const keyRef = useRef(0) const hasCustomKeyExtractor = !!_keyExtractor const keyExtractor = useMemoizedFn(_keyExtractor || (() => keyRef.current)) @@ -45,17 +59,19 @@ export function useListTransition(list: Array, options?: { timeout: // 1 add new items into list state if (newItemsWithIndex.length > 0) { - setListState(prevListState => - newItemsWithIndex.reduce( - (prev, { item, index }, _i) => - insertArray(prev, index, { - item, - key: hasCustomKeyExtractor ? keyExtractor(item) : keyRef.current++, - ...getState(STATUS.from), - }), - prevListState, - ), - ) + viewTransition(() => { + setListState(prevListState => + newItemsWithIndex.reduce( + (prev, { item, index }, _i) => + insertArray(prev, index, { + item, + key: hasCustomKeyExtractor ? keyExtractor(item) : keyRef.current++, + ...getState(STATUS.from), + }), + prevListState, + ), + ) + }) } // 2 enter those new items immediatly @@ -94,13 +110,15 @@ export function useListTransition(list: Array, options?: { timeout: const subtractItems = subtractItemStates.map(item => item.item) if (newItemsWithIndex.length === 0 && subtractItemStates.length > 0) { - setListState(prev => - prev.map(itemState => - subtractItemStates.includes(itemState) - ? { ...itemState, ...getState(STATUS.exiting) } - : itemState, - ), - ) + viewTransition(() => { + setListState(prev => + prev.map(itemState => + subtractItemStates.includes(itemState) + ? { ...itemState, ...getState(STATUS.exiting) } + : itemState, + ), + ) + }) setAnimationFrameTimeout(() => { setListState(prev => @@ -114,16 +132,18 @@ export function useListTransition(list: Array, options?: { timeout: && list.length === listState.length && list.some((item, index) => keyExtractor(item) !== listState[index].key) ) { - setListState( - list.map(item => ({ - item, - key: keyExtractor(item), - ...getState(STATUS.entered), - })), - ) + viewTransition(() => { + setListState( + list.map(item => ({ + item, + key: keyExtractor(item), + ...getState(STATUS.entered), + })), + ) + }) } }, - [list, listState, enterTimeout, exitTimeout, entered, keyExtractor, hasCustomKeyExtractor], + [list, listState, enterTimeout, exitTimeout, entered, keyExtractor, hasCustomKeyExtractor, viewTransition], ) function transitionList(renderCallback: RenderCallback) { diff --git a/src/status.ts b/src/status.ts index 746ac45..09528a2 100644 --- a/src/status.ts +++ b/src/status.ts @@ -14,6 +14,7 @@ export function getState(status: STATUS) { return { _s: status, status: STATUS[status] as Stage, + simpleStatus: getSimpleStatus(STATUS[status] as Stage), /** * status !== 'exited', */ diff --git a/src/viewTransition.ts b/src/viewTransition.ts new file mode 100644 index 0000000..8a46bf0 --- /dev/null +++ b/src/viewTransition.ts @@ -0,0 +1,14 @@ +import { flushSync } from 'react-dom' + +const isClient = typeof window !== 'undefined' +const isSupportViewTransition = isClient && 'startViewTransition' in document + +export function startViewTransition(fn: () => void) { + if (isSupportViewTransition) { + // @ts-expect-error startViewTransition is not in the type definition + document.startViewTransition(() => flushSync(fn)) + } + else { + fn() + } +} diff --git a/tsconfig.json b/tsconfig.json index 98d12ca..a5045e4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "jsx": "react-jsx", "lib": ["esnext", "dom"], "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "Bundler", "resolveJsonModule": true, "strict": true, "strictNullChecks": true,