feat: アニメーションの一部実装(#176) (#178)
#176 完了 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #178
This commit was merged in pull request #178.
This commit is contained in:
Generated
+43
@@ -17,6 +17,7 @@
|
|||||||
"camelcase-keys": "^9.1.3",
|
"camelcase-keys": "^9.1.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"framer-motion": "^12.23.26",
|
||||||
"humps": "^2.0.1",
|
"humps": "^2.0.1",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
@@ -3573,6 +3574,33 @@
|
|||||||
"url": "https://github.com/sponsors/rawify"
|
"url": "https://github.com/sponsors/rawify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.23.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
|
||||||
|
"integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.23.23",
|
||||||
|
"motion-utils": "^12.23.6",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -5216,6 +5244,21 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.23.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||||
|
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.23.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"camelcase-keys": "^9.1.3",
|
"camelcase-keys": "^9.1.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"framer-motion": "^12.23.26",
|
||||||
"humps": "^2.0.1",
|
"humps": "^2.0.1",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import TagLink from '@/components/TagLink'
|
import TagLink from '@/components/TagLink'
|
||||||
@@ -22,14 +23,19 @@ const renderTagTree = (
|
|||||||
const key = `${ path }-${ tag.id }`
|
const key = `${ path }-${ tag.id }`
|
||||||
|
|
||||||
const self = (
|
const self = (
|
||||||
<li key={key} className="mb-1">
|
<motion.li
|
||||||
|
key={key}
|
||||||
|
layout
|
||||||
|
transition={{ duration: .2, ease: 'easeOut' }}
|
||||||
|
className="mb-1">
|
||||||
<TagLink tag={tag} nestLevel={nestLevel}/>
|
<TagLink tag={tag} nestLevel={nestLevel}/>
|
||||||
</li>)
|
</motion.li>)
|
||||||
|
|
||||||
return [self,
|
return [self,
|
||||||
...(tag.children
|
...((tag.children
|
||||||
?.sort ((a, b) => a.name < b.name ? -1 : 1)
|
?.sort ((a, b) => a.name < b.name ? -1 : 1)
|
||||||
.flatMap (child => renderTagTree (child, nestLevel + 1, key)) ?? [])]
|
.flatMap (child => renderTagTree (child, nestLevel + 1, key)))
|
||||||
|
?? [])]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -70,13 +76,17 @@ export default (({ post }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<SidebarComponent>
|
<SidebarComponent>
|
||||||
<TagSearch/>
|
<TagSearch/>
|
||||||
|
<motion.div layout>
|
||||||
{CATEGORIES.map ((cat: Category) => cat in tags && (
|
{CATEGORIES.map ((cat: Category) => cat in tags && (
|
||||||
<div className="my-3" key={cat}>
|
<motion.div layout className="my-3" key={cat}>
|
||||||
<SubsectionTitle>{categoryNames[cat]}</SubsectionTitle>
|
<SubsectionTitle>{categoryNames[cat]}</SubsectionTitle>
|
||||||
<ul>
|
|
||||||
|
<motion.ul layout>
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
{tags[cat].map (tag => renderTagTree (tag, 0, `cat-${ cat }`))}
|
{tags[cat].map (tag => renderTagTree (tag, 0, `cat-${ cat }`))}
|
||||||
</ul>
|
</AnimatePresence>
|
||||||
</div>))}
|
</motion.ul>
|
||||||
|
</motion.div>))}
|
||||||
{post && (
|
{post && (
|
||||||
<div>
|
<div>
|
||||||
<SectionTitle>情報</SectionTitle>
|
<SectionTitle>情報</SectionTitle>
|
||||||
@@ -120,5 +130,6 @@ export default (({ post }: Props) => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>)}
|
</div>)}
|
||||||
|
</motion.div>
|
||||||
</SidebarComponent>)
|
</SidebarComponent>)
|
||||||
}) satisfies FC<Props>
|
}) satisfies FC<Props>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
@@ -8,7 +9,6 @@ import SectionTitle from '@/components/common/SectionTitle'
|
|||||||
import SidebarComponent from '@/components/layout/SidebarComponent'
|
import SidebarComponent from '@/components/layout/SidebarComponent'
|
||||||
import { API_BASE_URL } from '@/config'
|
import { API_BASE_URL } from '@/config'
|
||||||
import { CATEGORIES } from '@/consts'
|
import { CATEGORIES } from '@/consts'
|
||||||
import { cn } from '@/lib/utils'
|
|
||||||
|
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
|
||||||
@@ -61,7 +61,20 @@ export default (({ posts }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<SidebarComponent>
|
<SidebarComponent>
|
||||||
<TagSearch/>
|
<TagSearch/>
|
||||||
<div className={cn (!(tagsVsbl) && 'hidden', 'md:block mt-4')}>
|
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{tagsVsbl && (
|
||||||
|
<motion.div
|
||||||
|
key="sptags"
|
||||||
|
className="md:block mt-4"
|
||||||
|
variants={{ hidden: { clipPath: 'inset(0 0 100% 0)',
|
||||||
|
height: 0 },
|
||||||
|
visible: { clipPath: 'inset(0 0 0% 0)',
|
||||||
|
height: 'auto'} }}
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
exit="hidden"
|
||||||
|
transition={{ duration: .2, ease: 'easeOut' }}>
|
||||||
<SectionTitle>タグ</SectionTitle>
|
<SectionTitle>タグ</SectionTitle>
|
||||||
<ul>
|
<ul>
|
||||||
{CATEGORIES.flatMap (cat => cat in tags ? (
|
{CATEGORIES.flatMap (cat => cat in tags ? (
|
||||||
@@ -91,7 +104,9 @@ export default (({ posts }: Props) => {
|
|||||||
}}>
|
}}>
|
||||||
ランダム
|
ランダム
|
||||||
</a>)}
|
</a>)}
|
||||||
</div>
|
</motion.div>)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
<a href="#"
|
<a href="#"
|
||||||
className="md:hidden block my-2 text-center text-sm
|
className="md:hidden block my-2 text-center text-sm
|
||||||
text-gray-500 hover:text-gray-400
|
text-gray-500 hover:text-gray-400
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import toCamel from 'camelcase-keys'
|
import toCamel from 'camelcase-keys'
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import { Fragment, useState, useEffect } from 'react'
|
import { Fragment, useState, useEffect } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
@@ -136,8 +137,20 @@ export default (({ user }: Props) => {
|
|||||||
</Link>))}
|
</Link>))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn (menuOpen ? 'flex flex-col md:hidden' : 'hidden',
|
<AnimatePresence initial={false}>
|
||||||
'bg-yellow-200 dark:bg-red-975 items-start')}>
|
{menuOpen && (
|
||||||
|
<motion.div
|
||||||
|
key="spmenu"
|
||||||
|
className={cn ('flex flex-col md:hidden',
|
||||||
|
'bg-yellow-200 dark:bg-red-975 items-start')}
|
||||||
|
variants={{ closed: { clipPath: 'inset(0 0 100% 0)',
|
||||||
|
height: 0 },
|
||||||
|
open: { clipPath: 'inset(0 0 0% 0)',
|
||||||
|
height: 'auto' } }}
|
||||||
|
initial="closed"
|
||||||
|
animate="open"
|
||||||
|
exit="closed"
|
||||||
|
transition={{ duration: .2, ease: 'easeOut' }}>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
{menu.map ((item, i) => (
|
{menu.map ((item, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
@@ -167,6 +180,7 @@ export default (({ user }: Props) => {
|
|||||||
</Fragment>))}
|
</Fragment>))}
|
||||||
<TopNavUser user={user} sp/>
|
<TopNavUser user={user} sp/>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
</div>
|
</motion.div>)}
|
||||||
|
</AnimatePresence>
|
||||||
</>)
|
</>)
|
||||||
}) satisfies FC<Props>
|
}) satisfies FC<Props>
|
||||||
|
|||||||
Reference in New Issue
Block a user