|
| 1 | +import client from "../../../utils/cms"; |
| 2 | +import Layout from "../../../layout/main"; |
| 3 | +import Link from "next/link"; |
| 4 | +import { css } from "../../../../styled-system/css"; |
| 5 | +import HMeta from "../../../components/headermeta"; |
| 6 | +import Image from "next/image"; |
| 7 | +import { useEffect, useState } from "react"; |
| 8 | +import { motion, useScroll, useMotionValueEvent } from "framer-motion"; |
| 9 | +import blog from "../../blog"; |
| 10 | +//const { scrollYProgress } = useScroll(); |
| 11 | + |
| 12 | +// Add a function to process blog.content and apply styles to <code> tags |
| 13 | +export default function BlogId({ project }) { |
| 14 | + // Process blog.content to style <code> tags |
| 15 | + const cmsstyle = ` |
| 16 | + <style> |
| 17 | + h2 { |
| 18 | + font-size: 1.5rem; |
| 19 | + margin-top: 2rem; |
| 20 | + margin-bottom: 1rem; |
| 21 | + font-weight: bold; |
| 22 | + } |
| 23 | + h3 { |
| 24 | + font-size: 1.3rem; |
| 25 | + margin-top: 1.2rem; |
| 26 | + margin-bottom: 0.7rem; |
| 27 | + } |
| 28 | +
|
| 29 | + pre { |
| 30 | + line-height: 1.4; |
| 31 | + overflow-x: scroll; |
| 32 | + } |
| 33 | +
|
| 34 | + pre::before { |
| 35 | + content: "→ scroll →"; |
| 36 | + display: block; |
| 37 | + height: 5vh; |
| 38 | + } |
| 39 | +
|
| 40 | + code { |
| 41 | + font-family: 'Courier New', Courier, monospace; |
| 42 | + } |
| 43 | + </style> |
| 44 | + `; |
| 45 | + |
| 46 | + // only for devmode CMS data view |
| 47 | + const [isDevmodeOpen, setIsDevmodeOpen] = useState(false); |
| 48 | + |
| 49 | + // motion scroll progress bar script start |
| 50 | + const { scrollY } = useScroll(); |
| 51 | + const [scrollYValue, setScrollY] = useState(0); |
| 52 | + const [scrollDirection, setScrollDirection] = useState("down") |
| 53 | + |
| 54 | + useMotionValueEvent(scrollY, "change", (current) => { |
| 55 | + const diff = current - (scrollY?.getPrevious() ?? 0) |
| 56 | + setScrollDirection(diff > 0 ? "down" : "up") |
| 57 | + }) |
| 58 | + const { scrollYProgress } = useScroll(); |
| 59 | + |
| 60 | + useEffect(() => { |
| 61 | + const handleScroll = () => setScrollY(window.scrollY ?? window.pageYOffset ?? 0); |
| 62 | + handleScroll(); |
| 63 | + window.addEventListener('scroll', handleScroll, { passive: true }); |
| 64 | + return () => window.removeEventListener('scroll', handleScroll); |
| 65 | + }, []); |
| 66 | + // motion scroll progress bar script end |
| 67 | + |
| 68 | + |
| 69 | + // get formatted date |
| 70 | + const persedIsoDate = new Date(project.publishedAt); |
| 71 | + const updatedDate = new Date(project.updatedAt); |
| 72 | + |
| 73 | + const fomattedDate = persedIsoDate.toLocaleString("ja-JP", { |
| 74 | + year: "numeric", |
| 75 | + month: "2-digit", |
| 76 | + day: "2-digit", |
| 77 | + hour: "2-digit", |
| 78 | + minute: "2-digit", |
| 79 | + }); |
| 80 | + |
| 81 | + const [year, month, day, hour, minute] = fomattedDate.match(/\d+/g); |
| 82 | + |
| 83 | + const updatedFomattedDate = updatedDate.toLocaleString("ja-JP", { |
| 84 | + year: "numeric", |
| 85 | + month: "2-digit", |
| 86 | + day: "2-digit", |
| 87 | + hour: "2-digit", |
| 88 | + minute: "2-digit", |
| 89 | + }); |
| 90 | + |
| 91 | + const [updatedYear, updatedMonth, updatedDay, updatedHour, updatedMinute] = updatedFomattedDate.match(/\d+/g); |
| 92 | + |
| 93 | + return ( |
| 94 | + <Layout> |
| 95 | + <HMeta |
| 96 | + pageTitle={project.title} |
| 97 | + pageDescription="My development projects" |
| 98 | + pagePath={`/blog/${project.id}`} |
| 99 | + /> |
| 100 | + |
| 101 | + <motion.div |
| 102 | + className={css({ |
| 103 | + position: "fixed", |
| 104 | + top: 0, |
| 105 | + left: 0, |
| 106 | + width: "100%", |
| 107 | + height: "5px", |
| 108 | + backgroundColor: "#aa00ff", |
| 109 | + transformOrigin: scrollDirection === "down" ? "0% 100%" : "0% 0%", |
| 110 | + zIndex: 9999, |
| 111 | + })} |
| 112 | + style={{ scaleX: scrollYProgress }} /> |
| 113 | + |
| 114 | + {process.env.NODE_ENV === "development" && ( |
| 115 | + <div |
| 116 | + className={css({ |
| 117 | + position: "fixed", |
| 118 | + top: 0, |
| 119 | + left: 0, |
| 120 | + color: "#ff0000", |
| 121 | + backgroundColor: "rgba(0, 0, 0, 0.5)", |
| 122 | + overflow: "scroll", |
| 123 | + zIndex: 0, |
| 124 | + padding: "20px", |
| 125 | + })} |
| 126 | + > |
| 127 | + {isDevmodeOpen ? ( |
| 128 | + <div> |
| 129 | + dev |
| 130 | + CMS Data |
| 131 | + <code> |
| 132 | + <pre>{JSON.stringify(project, null, 2)}</pre> |
| 133 | + </code> |
| 134 | + <button onClick={() => setIsDevmodeOpen(false)}> |
| 135 | + Close |
| 136 | + </button> |
| 137 | + </div> |
| 138 | + ) : ( |
| 139 | + <button onClick={() => setIsDevmodeOpen(true)}> |
| 140 | + Open Devmode CMS Data |
| 141 | + </button> |
| 142 | + )} |
| 143 | + </div> |
| 144 | + )} |
| 145 | + <div> |
| 146 | + <h1 className={css({ |
| 147 | + fontSize: "28px", |
| 148 | + fontWeight: "bold", |
| 149 | + padding: "0px 40px", |
| 150 | + })}> |
| 151 | + {project.name} |
| 152 | + </h1> |
| 153 | + <h3 className={css({ |
| 154 | + fontSize: "23px", |
| 155 | + padding: "0 40px", |
| 156 | + })}> |
| 157 | + {project.id} |
| 158 | + </h3> |
| 159 | + <div className={css({ |
| 160 | + fontSize: "18px", |
| 161 | + padding: "10px 40px", |
| 162 | + })}> |
| 163 | + URL: |
| 164 | + <Link href={project.url} target="_blank" rel="noopener noreferrer"> |
| 165 | + {project.url} |
| 166 | + </Link> |
| 167 | + </div> |
| 168 | + </div> |
| 169 | + <div |
| 170 | + className={css({ |
| 171 | + fontSize: "22px", |
| 172 | + padding: "10px 40px", |
| 173 | + borderBottom: "1px solid #f0d0ff", |
| 174 | + })} |
| 175 | + > |
| 176 | + <p>created {`${year}/${month}/${day} - ${hour} : ${minute}`}</p> |
| 177 | + {project.updatedAt !== project.publishedAt ? ( |
| 178 | + <p>updated {`${updatedYear}/${updatedMonth}/${updatedDay} - ${updatedHour} : ${updatedMinute}`}</p> |
| 179 | + ) : (<div>no updated</div>)} |
| 180 | + </div> |
| 181 | + <div |
| 182 | + className={css({ |
| 183 | + p: 4, |
| 184 | + })} |
| 185 | + > |
| 186 | + <div |
| 187 | + className={css({ |
| 188 | + gap: "14px", |
| 189 | + display: "flex", |
| 190 | + flexDirection: "column", |
| 191 | + justifyContent: "center", |
| 192 | + paddingY: "10px", |
| 193 | + maxWidth: "800px", |
| 194 | + margin: "0 auto", |
| 195 | + lineHeight: "1.6", |
| 196 | + })} |
| 197 | + > |
| 198 | + <div |
| 199 | + dangerouslySetInnerHTML={{ |
| 200 | + __html: `${project.content}${cmsstyle}`, |
| 201 | + }} |
| 202 | + /> |
| 203 | + <Link href="/dev">Back to list</Link> |
| 204 | + </div> |
| 205 | + </div> |
| 206 | + </Layout> |
| 207 | + ); |
| 208 | +} |
| 209 | + |
| 210 | +// 静的生成のためのパスを指定します |
| 211 | +export const getStaticPaths = async () => { |
| 212 | + const data = await client.get({ endpoint: "projects" }); |
| 213 | + |
| 214 | + const paths = data.contents.map((content) => `/dev/${content.id}`); |
| 215 | + return { paths, fallback: false }; |
| 216 | +}; |
| 217 | + |
| 218 | +// データをテンプレートに受け渡す部分の処理を記述します |
| 219 | +export const getStaticProps = async (context) => { |
| 220 | + const id = context.params.id; |
| 221 | + const data = await client.get({ endpoint: "projects", contentId: id }); |
| 222 | + |
| 223 | + return { |
| 224 | + props: { |
| 225 | + project: data, |
| 226 | + }, |
| 227 | + }; |
| 228 | +}; |
0 commit comments