diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index b241081..43fbc44 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -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",
diff --git a/frontend/package.json b/frontend/package.json
index 747f7ed..cbe44ff 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -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",
diff --git a/frontend/src/components/TagDetailSidebar.tsx b/frontend/src/components/TagDetailSidebar.tsx
index da2c3b8..0006738 100644
--- a/frontend/src/components/TagDetailSidebar.tsx
+++ b/frontend/src/components/TagDetailSidebar.tsx
@@ -1,3 +1,4 @@
+import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import TagLink from '@/components/TagLink'
@@ -18,18 +19,23 @@ const renderTagTree = (
tag: Tag,
nestLevel: number,
path: string,
- ): ReactNode[] => {
+): ReactNode[] => {
const key = `${ path }-${ tag.id }`
const self = (
-
+
- )
+ )
return [self,
- ...(tag.children
- ?.sort ((a, b) => a.name < b.name ? -1 : 1)
- .flatMap (child => renderTagTree (child, nestLevel + 1, key)) ?? [])]
+ ...((tag.children
+ ?.sort ((a, b) => a.name < b.name ? -1 : 1)
+ .flatMap (child => renderTagTree (child, nestLevel + 1, key)))
+ ?? [])]
}
@@ -70,55 +76,60 @@ export default (({ post }: Props) => {
return (
- {CATEGORIES.map ((cat: Category) => cat in tags && (
-
-
{categoryNames[cat]}
-
- {tags[cat].map (tag => renderTagTree (tag, 0, `cat-${ cat }`))}
-
-
))}
- {post && (
-
-
情報
-
- - Id.: {post.id}
- {/* TODO: uploadedUser の取得を対応したらコメント外す */}
- {/*
- -
- <>耕作者: >
- {post.uploadedUser
- ? (
-
- {post.uploadedUser.name || '名もなきニジラー'}
- )
- : 'bot操作'}
-
- */}
- - 耕作日時: {(new Date (post.createdAt)).toLocaleString ()}
- -
- <>リンク: >
-
- {post.url}
-
-
- -
- {/* TODO: 表示形式きしょすぎるので何とかする */}
- <>オリジナルの投稿日時: >
- {!(post.originalCreatedFrom) && !(post.originalCreatedBefore)
- ? '不明'
- : (
- <>
- {post.originalCreatedFrom
- && `${ (new Date (post.originalCreatedFrom)).toLocaleString () } 以降 `}
- {post.originalCreatedBefore
- && `${ (new Date (post.originalCreatedBefore)).toLocaleString () } より前`}
- >)}
-
-
-
)}
+
+ {CATEGORIES.map ((cat: Category) => cat in tags && (
+
+ {categoryNames[cat]}
+
+
+
+ {tags[cat].map (tag => renderTagTree (tag, 0, `cat-${ cat }`))}
+
+
+ ))}
+ {post && (
+
+
情報
+
+ - Id.: {post.id}
+ {/* TODO: uploadedUser の取得を対応したらコメント外す */}
+ {/*
+ -
+ <>耕作者: >
+ {post.uploadedUser
+ ? (
+
+ {post.uploadedUser.name || '名もなきニジラー'}
+ )
+ : 'bot操作'}
+
+ */}
+ - 耕作日時: {(new Date (post.createdAt)).toLocaleString ()}
+ -
+ <>リンク: >
+
+ {post.url}
+
+
+ -
+ {/* TODO: 表示形式きしょすぎるので何とかする */}
+ <>オリジナルの投稿日時: >
+ {!(post.originalCreatedFrom) && !(post.originalCreatedBefore)
+ ? '不明'
+ : (
+ <>
+ {post.originalCreatedFrom
+ && `${ (new Date (post.originalCreatedFrom)).toLocaleString () } 以降 `}
+ {post.originalCreatedBefore
+ && `${ (new Date (post.originalCreatedBefore)).toLocaleString () } より前`}
+ >)}
+
+
+
)}
+
)
}) satisfies FC
diff --git a/frontend/src/components/TagSidebar.tsx b/frontend/src/components/TagSidebar.tsx
index a2e00b2..735490d 100644
--- a/frontend/src/components/TagSidebar.tsx
+++ b/frontend/src/components/TagSidebar.tsx
@@ -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,37 +61,52 @@ export default (({ posts }: Props) => {
return (
-
+
+
+ {tagsVsbl && (
+
+ タグ
+
+ {CATEGORIES.flatMap (cat => cat in tags ? (
+ tags[cat].map (tag => (
+ -
+
+
))) : [])}
+
+ 関聯
+ {posts.length > 0 && (
+ {
+ ev.preventDefault ()
+ void ((async () => {
+ try
+ {
+ const { data } = await axios.get (`${ API_BASE_URL }/posts/random`,
+ { params: { tags: tagsQuery.split (' ').filter (e => e !== '').join (' '),
+ match: (anyFlg ? 'any' : 'all') } })
+ navigate (`/posts/${ (data as Post).id }`)
+ }
+ catch
+ {
+ ;
+ }
+ }) ())
+ }}>
+ ランダム
+ )}
+ )}
+
+
- {subItem.name}
- )))}
- ))}
-
-
-
+
+ {menuOpen && (
+
+
+ {menu.map ((item, i) => (
+
+ {
+ if (i !== openItemIdx)
+ {
+ ev.preventDefault ()
+ setOpenItemIdx (i)
+ }
+ }}>
+ {item.name}
+
+ {i === openItemIdx && (
+ item.subMenu
+ .filter (subItem => subItem.visible ?? true)
+ .map ((subItem, j) => 'component' in subItem ? subItem.component : (
+
+ {subItem.name}
+ )))}
+ ))}
+
+
+ )}
+
>)
}) satisfies FC