#178 feat: アニメーションの一部実装(#176)

Merged
みてるぞ merged 1 commits from feature/176 into main 2 days ago
  1. +43
    -0
      frontend/package-lock.json
  2. +1
    -0
      frontend/package.json
  3. +19
    -8
      frontend/src/components/TagDetailSidebar.tsx
  4. +18
    -3
      frontend/src/components/TagSidebar.tsx
  5. +17
    -3
      frontend/src/components/TopNav.tsx

+ 43
- 0
frontend/package-lock.json View File

@@ -17,6 +17,7 @@
"camelcase-keys": "^9.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.26",
"humps": "^2.0.1",
"lucide-react": "^0.511.0",
"markdown-it": "^14.1.0",
@@ -3573,6 +3574,33 @@
"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": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -5216,6 +5244,21 @@
"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": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",


+ 1
- 0
frontend/package.json View File

@@ -19,6 +19,7 @@
"camelcase-keys": "^9.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.26",
"humps": "^2.0.1",
"lucide-react": "^0.511.0",
"markdown-it": "^14.1.0",


+ 19
- 8
frontend/src/components/TagDetailSidebar.tsx View File

@@ -1,3 +1,4 @@
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useState } from 'react'

import TagLink from '@/components/TagLink'
@@ -22,14 +23,19 @@ const renderTagTree = (
const key = `${ path }-${ tag.id }`

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}/>
</li>)
</motion.li>)

return [self,
...(tag.children
...((tag.children
?.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 (
<SidebarComponent>
<TagSearch/>
<motion.div layout>
{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>
<ul>

<motion.ul layout>
<AnimatePresence initial={false}>
{tags[cat].map (tag => renderTagTree (tag, 0, `cat-${ cat }`))}
</ul>
</div>))}
</AnimatePresence>
</motion.ul>
</motion.div>))}
{post && (
<div>
<SectionTitle>情報</SectionTitle>
@@ -120,5 +130,6 @@ export default (({ post }: Props) => {
</li>
</ul>
</div>)}
</motion.div>
</SidebarComponent>)
}) satisfies FC<Props>

+ 18
- 3
frontend/src/components/TagSidebar.tsx View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'

@@ -8,7 +9,6 @@ import SectionTitle from '@/components/common/SectionTitle'
import SidebarComponent from '@/components/layout/SidebarComponent'
import { API_BASE_URL } from '@/config'
import { CATEGORIES } from '@/consts'
import { cn } from '@/lib/utils'

import type { FC } from 'react'

@@ -61,7 +61,20 @@ export default (({ posts }: Props) => {
return (
<SidebarComponent>
<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>
<ul>
{CATEGORIES.flatMap (cat => cat in tags ? (
@@ -91,7 +104,9 @@ export default (({ posts }: Props) => {
}}>
ランダム
</a>)}
</div>
</motion.div>)}
</AnimatePresence>

<a href="#"
className="md:hidden block my-2 text-center text-sm
text-gray-500 hover:text-gray-400


+ 17
- 3
frontend/src/components/TopNav.tsx View File

@@ -1,5 +1,6 @@
import axios from 'axios'
import toCamel from 'camelcase-keys'
import { AnimatePresence, motion } from 'framer-motion'
import { Fragment, useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom'

@@ -136,8 +137,20 @@ export default (({ user }: Props) => {
</Link>))}
</div>

<div className={cn (menuOpen ? 'flex flex-col md:hidden' : 'hidden',
'bg-yellow-200 dark:bg-red-975 items-start')}>
<AnimatePresence initial={false}>
{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/>
{menu.map ((item, i) => (
<Fragment key={i}>
@@ -167,6 +180,7 @@ export default (({ user }: Props) => {
</Fragment>))}
<TopNavUser user={user} sp/>
<Separator/>
</div>
</motion.div>)}
</AnimatePresence>
</>)
}) satisfies FC<Props>

Loading…
Cancel
Save