From 3c071a38bf24f1744ed6722f90719e1df62f8768 Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Sun, 1 Sep 2024 22:12:07 +0900 Subject: [PATCH 1/7] feat: impl relative-time component --- app/components/Time.stories.tsx | 17 +++++++++++++ app/components/Time.tsx | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 app/components/Time.stories.tsx create mode 100644 app/components/Time.tsx diff --git a/app/components/Time.stories.tsx b/app/components/Time.stories.tsx new file mode 100644 index 0000000..2b1bbd3 --- /dev/null +++ b/app/components/Time.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Time } from './Time'; + +const meta = { + component: Time, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + date: new Date() + } +}; diff --git a/app/components/Time.tsx b/app/components/Time.tsx new file mode 100644 index 0000000..c720136 --- /dev/null +++ b/app/components/Time.tsx @@ -0,0 +1,45 @@ +interface TimeProps { + date: Date; +} + +const timeFormatter = new Intl.DateTimeFormat(); +const relativeTimeFormatter = new Intl.RelativeTimeFormat(undefined, {style: "short"}); + +const getRelativeTimeDiff = (date: Date, now = new Date()) => { + const diffMilliSeconds = now.getTime() - date.getTime() + const absDiff = Math.abs(diffMilliSeconds); + + if (absDiff < 60_000) { + const diffSeconds = Math.floor(diffMilliSeconds / 1_000) + return relativeTimeFormatter.format(-diffSeconds, 'second') + } else if (absDiff < 3_600_000) { + const diffMinutes = Math.floor(diffMilliSeconds / 60_000) + return relativeTimeFormatter.format(-diffMinutes, 'minute') + } else if (absDiff < 86_400_000) { + const diffHours = Math.floor(diffMilliSeconds / 3_600_000) + return relativeTimeFormatter.format(-diffHours, 'hour') + } + + const diffDays = Math.floor(diffMilliSeconds / 86_400_000) + if (Math.abs(diffDays) < 30) { + return relativeTimeFormatter.format(-diffDays, 'day') + } + + const diffMonths = Math.floor(diffDays / 30) + if (Math.abs(diffMonths) < 12) { + return relativeTimeFormatter.format(-diffMonths, 'month') + } + + const yearsDifference = Math.floor(diffMonths / 12) + return relativeTimeFormatter.format(-yearsDifference, 'year') +} + +export const Time = ({date}: TimeProps) => { + + const formattedDate = timeFormatter.format(date); + const formattedRelativeDate = getRelativeTimeDiff(date); + + return ( +

{formattedDate} ({formattedRelativeDate})

+ ) +} From 132a519936e6e250864b0d2caa5e5706801e6aad Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Sun, 1 Sep 2024 22:12:38 +0900 Subject: [PATCH 2/7] feat: impl announce component --- app/components/Announce.stories.tsx | 20 +++++++++++++ app/components/Announce.tsx | 44 +++++++++++++++++++++++++++++ app/components/announce.module.css | 23 +++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 app/components/Announce.stories.tsx create mode 100644 app/components/Announce.tsx create mode 100644 app/components/announce.module.css diff --git a/app/components/Announce.stories.tsx b/app/components/Announce.stories.tsx new file mode 100644 index 0000000..a2bffda --- /dev/null +++ b/app/components/Announce.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Announce } from './Announce'; + +const meta = { + component: Announce, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + announceType: "warn", + title: "定期メンテナンスのお知らせ", + body: "以下の日時に定期メンテナンスを行います", + updatedAt: new Date("2023-09-10T00:00:00Z") + } +}; diff --git a/app/components/Announce.tsx b/app/components/Announce.tsx new file mode 100644 index 0000000..fbea408 --- /dev/null +++ b/app/components/Announce.tsx @@ -0,0 +1,44 @@ +import { IconButton, Text, ThickCheckIcon, Tooltip } from "@radix-ui/themes"; +import styles from "./announce.module.css"; +import { t } from "i18next"; +import { Time } from "~/components/Time"; +import { ExclamationTriangleIcon, InfoCircledIcon } from "@radix-ui/react-icons"; + +interface AnnounceProps { + announceType: "warn" | "info"; + title: string; + body: string; + updatedAt: Date; +} + +export const Announce = ({ announceType, title, body, updatedAt }: AnnounceProps) => { + return (<> +
+
+ + ️{announceType === "warn" ? : } + + + {title} + +
+ + + + + +
+ +

+ {body} +

+ + + + + ); + } +; diff --git a/app/components/announce.module.css b/app/components/announce.module.css new file mode 100644 index 0000000..12d4c2d --- /dev/null +++ b/app/components/announce.module.css @@ -0,0 +1,23 @@ +.announceTitleContainer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + + .announceTitle { + display: flex; + flex-direction: row; + align-items: center; + } + + margin-bottom: .5em; + + p { + margin: 0; + padding: 0; + } + + span { + margin-right: 1rem; + } +} From a5a9ca3bf0207408fc986eca2881448ded3b7e4f Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Sun, 1 Sep 2024 22:12:57 +0900 Subject: [PATCH 3/7] feat: add i18n strings --- i18n/locales/en_US.json | 5 ++++- i18n/locales/ja_JP.json | 5 ++++- package.json | 1 + pnpm-lock.yaml | 12 ++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/i18n/locales/en_US.json b/i18n/locales/en_US.json index 26678ca..17e5949 100644 --- a/i18n/locales/en_US.json +++ b/i18n/locales/en_US.json @@ -15,5 +15,8 @@ "followers": "followers", "editProfile": "edit profile", "blocking": "blocking", - "followBack": "followback" + "followBack": "followback", + "notification.read": "Mark as read", + "announce.warn": "Warning", + "announce.info": "Information" } diff --git a/i18n/locales/ja_JP.json b/i18n/locales/ja_JP.json index ebf6fee..2f6c72c 100644 --- a/i18n/locales/ja_JP.json +++ b/i18n/locales/ja_JP.json @@ -15,5 +15,8 @@ "followers": "フォロワー", "editProfile": "プロフィールを編集", "blocking": "ブロック中", - "followBack": "フォローバック" + "followBack": "フォローバック", + "notification.read": "既読にする", + "announce.warn": "警告", + "announce.info": "情報" } diff --git a/package.json b/package.json index dc9af49..714660c 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.1", "@remix-run/node": "^2.10.3", "@remix-run/react": "^2.10.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d87d62..cd38265 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.3.1) '@radix-ui/themes': specifier: ^3.1.1 version: 3.1.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1428,6 +1431,11 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-icons@1.3.0': + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + '@radix-ui/react-id@1.1.0': resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: @@ -6612,6 +6620,10 @@ snapshots: '@types/react': 18.3.4 '@types/react-dom': 18.3.0 + '@radix-ui/react-icons@1.3.0(react@18.3.1)': + dependencies: + react: 18.3.1 + '@radix-ui/react-id@1.1.0(@types/react@18.3.4)(react@18.3.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) From 0bd2dc6fcb0b1c52d3f4dbec9c74a719e695b808 Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Sun, 1 Sep 2024 22:18:40 +0900 Subject: [PATCH 4/7] chore: format code --- app/components/Announce.stories.tsx | 6 ++-- app/components/Announce.tsx | 49 +++++++++++++++++++---------- app/components/Time.stories.tsx | 6 ++-- app/components/Time.tsx | 43 +++++++++++++------------ 4 files changed, 61 insertions(+), 43 deletions(-) diff --git a/app/components/Announce.stories.tsx b/app/components/Announce.stories.tsx index a2bffda..60bdca6 100644 --- a/app/components/Announce.stories.tsx +++ b/app/components/Announce.stories.tsx @@ -1,9 +1,9 @@ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryObj } from "@storybook/react"; -import { Announce } from './Announce'; +import { Announce } from "./Announce"; const meta = { - component: Announce, + component: Announce } satisfies Meta; export default meta; diff --git a/app/components/Announce.tsx b/app/components/Announce.tsx index fbea408..0bb37aa 100644 --- a/app/components/Announce.tsx +++ b/app/components/Announce.tsx @@ -2,7 +2,10 @@ import { IconButton, Text, ThickCheckIcon, Tooltip } from "@radix-ui/themes"; import styles from "./announce.module.css"; import { t } from "i18next"; import { Time } from "~/components/Time"; -import { ExclamationTriangleIcon, InfoCircledIcon } from "@radix-ui/react-icons"; +import { + ExclamationTriangleIcon, + InfoCircledIcon +} from "@radix-ui/react-icons"; interface AnnounceProps { announceType: "warn" | "info"; @@ -11,34 +14,46 @@ interface AnnounceProps { updatedAt: Date; } -export const Announce = ({ announceType, title, body, updatedAt }: AnnounceProps) => { - return (<> +export const Announce = ({ + announceType, + title, + body, + updatedAt +}: AnnounceProps) => { + return ( + <>
- - ️{announceType === "warn" ? : } + + + ️ + {announceType === "warn" ? ( + + ) : ( + + )} + {title}
- - + +
-

- {body} -

+

{body}

- - - ); - } -; + + ); +}; diff --git a/app/components/Time.stories.tsx b/app/components/Time.stories.tsx index 2b1bbd3..ae8084f 100644 --- a/app/components/Time.stories.tsx +++ b/app/components/Time.stories.tsx @@ -1,9 +1,9 @@ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryObj } from "@storybook/react"; -import { Time } from './Time'; +import { Time } from "./Time"; const meta = { - component: Time, + component: Time } satisfies Meta; export default meta; diff --git a/app/components/Time.tsx b/app/components/Time.tsx index c720136..eccb5f5 100644 --- a/app/components/Time.tsx +++ b/app/components/Time.tsx @@ -3,43 +3,46 @@ interface TimeProps { } const timeFormatter = new Intl.DateTimeFormat(); -const relativeTimeFormatter = new Intl.RelativeTimeFormat(undefined, {style: "short"}); +const relativeTimeFormatter = new Intl.RelativeTimeFormat(undefined, { + style: "short" +}); const getRelativeTimeDiff = (date: Date, now = new Date()) => { - const diffMilliSeconds = now.getTime() - date.getTime() + const diffMilliSeconds = now.getTime() - date.getTime(); const absDiff = Math.abs(diffMilliSeconds); if (absDiff < 60_000) { - const diffSeconds = Math.floor(diffMilliSeconds / 1_000) - return relativeTimeFormatter.format(-diffSeconds, 'second') + const diffSeconds = Math.floor(diffMilliSeconds / 1_000); + return relativeTimeFormatter.format(-diffSeconds, "second"); } else if (absDiff < 3_600_000) { - const diffMinutes = Math.floor(diffMilliSeconds / 60_000) - return relativeTimeFormatter.format(-diffMinutes, 'minute') + const diffMinutes = Math.floor(diffMilliSeconds / 60_000); + return relativeTimeFormatter.format(-diffMinutes, "minute"); } else if (absDiff < 86_400_000) { - const diffHours = Math.floor(diffMilliSeconds / 3_600_000) - return relativeTimeFormatter.format(-diffHours, 'hour') + const diffHours = Math.floor(diffMilliSeconds / 3_600_000); + return relativeTimeFormatter.format(-diffHours, "hour"); } - const diffDays = Math.floor(diffMilliSeconds / 86_400_000) + const diffDays = Math.floor(diffMilliSeconds / 86_400_000); if (Math.abs(diffDays) < 30) { - return relativeTimeFormatter.format(-diffDays, 'day') + return relativeTimeFormatter.format(-diffDays, "day"); } - const diffMonths = Math.floor(diffDays / 30) + const diffMonths = Math.floor(diffDays / 30); if (Math.abs(diffMonths) < 12) { - return relativeTimeFormatter.format(-diffMonths, 'month') + return relativeTimeFormatter.format(-diffMonths, "month"); } - const yearsDifference = Math.floor(diffMonths / 12) - return relativeTimeFormatter.format(-yearsDifference, 'year') -} - -export const Time = ({date}: TimeProps) => { + const yearsDifference = Math.floor(diffMonths / 12); + return relativeTimeFormatter.format(-yearsDifference, "year"); +}; +export const Time = ({ date }: TimeProps) => { const formattedDate = timeFormatter.format(date); const formattedRelativeDate = getRelativeTimeDiff(date); return ( -

{formattedDate} ({formattedRelativeDate})

- ) -} +

+ {formattedDate} ({formattedRelativeDate}) +

+ ); +}; From a92e90effed7e1b59e3527d197e34fd1ab5f6dfe Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Mon, 2 Sep 2024 11:40:51 +0900 Subject: [PATCH 5/7] fix: icon size --- app/components/Announce.tsx | 11 ++++------- app/components/announce.module.css | 14 +++++++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/components/Announce.tsx b/app/components/Announce.tsx index 0bb37aa..266f25c 100644 --- a/app/components/Announce.tsx +++ b/app/components/Announce.tsx @@ -29,22 +29,19 @@ export const Announce = ({ announceType === "warn" ? "announce.warn" : "announce.info" )} > - - ️ {announceType === "warn" ? ( - + ) : ( - + )} - {title} - - + + diff --git a/app/components/announce.module.css b/app/components/announce.module.css index 12d4c2d..69611dd 100644 --- a/app/components/announce.module.css +++ b/app/components/announce.module.css @@ -4,6 +4,17 @@ align-items: center; justify-content: space-between; + .announceIcon { + width: 1.5rem; + height: 1.5rem; + margin-right: .5rem; + } + + .readButtonIcon { + width: .8rem; + height: .8rem; + } + .announceTitle { display: flex; flex-direction: row; @@ -17,7 +28,4 @@ padding: 0; } - span { - margin-right: 1rem; - } } From 4a64344af67441a1acc52265048a2be8fa174c1f Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Mon, 2 Sep 2024 11:48:05 +0900 Subject: [PATCH 6/7] feat: remove fragments --- app/components/Announce.tsx | 16 ++++++++-------- app/components/Time.tsx | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/components/Announce.tsx b/app/components/Announce.tsx index 266f25c..42bfc8f 100644 --- a/app/components/Announce.tsx +++ b/app/components/Announce.tsx @@ -21,7 +21,7 @@ export const Announce = ({ updatedAt }: AnnounceProps) => { return ( - <> +
- {announceType === "warn" ? ( - - ) : ( - - )} + {announceType === "warn" ? ( + + ) : ( + + )} {title} @@ -41,7 +41,7 @@ export const Announce = ({
- +
@@ -51,6 +51,6 @@ export const Announce = ({ - +
); }; diff --git a/app/components/Time.tsx b/app/components/Time.tsx index eccb5f5..85952c0 100644 --- a/app/components/Time.tsx +++ b/app/components/Time.tsx @@ -8,21 +8,21 @@ const relativeTimeFormatter = new Intl.RelativeTimeFormat(undefined, { }); const getRelativeTimeDiff = (date: Date, now = new Date()) => { - const diffMilliSeconds = now.getTime() - date.getTime(); - const absDiff = Math.abs(diffMilliSeconds); + const diffMilliseconds = now.getTime() - date.getTime(); + const absDiff = Math.abs(diffMilliseconds); if (absDiff < 60_000) { - const diffSeconds = Math.floor(diffMilliSeconds / 1_000); + const diffSeconds = Math.floor(diffMilliseconds / 1_000); return relativeTimeFormatter.format(-diffSeconds, "second"); } else if (absDiff < 3_600_000) { - const diffMinutes = Math.floor(diffMilliSeconds / 60_000); + const diffMinutes = Math.floor(diffMilliseconds / 60_000); return relativeTimeFormatter.format(-diffMinutes, "minute"); } else if (absDiff < 86_400_000) { - const diffHours = Math.floor(diffMilliSeconds / 3_600_000); + const diffHours = Math.floor(diffMilliseconds / 3_600_000); return relativeTimeFormatter.format(-diffHours, "hour"); } - const diffDays = Math.floor(diffMilliSeconds / 86_400_000); + const diffDays = Math.floor(diffMilliseconds / 86_400_000); if (Math.abs(diffDays) < 30) { return relativeTimeFormatter.format(-diffDays, "day"); } @@ -32,8 +32,8 @@ const getRelativeTimeDiff = (date: Date, now = new Date()) => { return relativeTimeFormatter.format(-diffMonths, "month"); } - const yearsDifference = Math.floor(diffMonths / 12); - return relativeTimeFormatter.format(-yearsDifference, "year"); + const diffYears = Math.floor(diffMonths / 12); + return relativeTimeFormatter.format(-diffYears, "year"); }; export const Time = ({ date }: TimeProps) => { From 7f01fd8030426a910a7ada7eb0e50ac360e12f16 Mon Sep 17 00:00:00 2001 From: Tatsuto YAMAMOTO Date: Mon, 2 Sep 2024 20:28:38 +0900 Subject: [PATCH 7/7] fix: export some component props interface --- app/components/Announce.tsx | 2 +- app/components/Time.tsx | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/components/Announce.tsx b/app/components/Announce.tsx index 42bfc8f..d3b99e2 100644 --- a/app/components/Announce.tsx +++ b/app/components/Announce.tsx @@ -7,7 +7,7 @@ import { InfoCircledIcon } from "@radix-ui/react-icons"; -interface AnnounceProps { +export interface AnnounceProps { announceType: "warn" | "info"; title: string; body: string; diff --git a/app/components/Time.tsx b/app/components/Time.tsx index 85952c0..88ea7d7 100644 --- a/app/components/Time.tsx +++ b/app/components/Time.tsx @@ -1,4 +1,4 @@ -interface TimeProps { +export interface TimeProps { date: Date; } @@ -7,22 +7,28 @@ const relativeTimeFormatter = new Intl.RelativeTimeFormat(undefined, { style: "short" }); +const Millisecond = 1 as const; +const SECOND = Millisecond * 1000; +const MINUTE = SECOND * 60; +const HOUR = MINUTE * 60; +const DAY = HOUR * 24; + const getRelativeTimeDiff = (date: Date, now = new Date()) => { const diffMilliseconds = now.getTime() - date.getTime(); const absDiff = Math.abs(diffMilliseconds); - if (absDiff < 60_000) { - const diffSeconds = Math.floor(diffMilliseconds / 1_000); + if (absDiff < MINUTE) { + const diffSeconds = Math.floor(diffMilliseconds / SECOND); return relativeTimeFormatter.format(-diffSeconds, "second"); - } else if (absDiff < 3_600_000) { - const diffMinutes = Math.floor(diffMilliseconds / 60_000); + } else if (absDiff < HOUR) { + const diffMinutes = Math.floor(diffMilliseconds / MINUTE); return relativeTimeFormatter.format(-diffMinutes, "minute"); - } else if (absDiff < 86_400_000) { - const diffHours = Math.floor(diffMilliseconds / 3_600_000); + } else if (absDiff < DAY) { + const diffHours = Math.floor(diffMilliseconds / HOUR); return relativeTimeFormatter.format(-diffHours, "hour"); } - const diffDays = Math.floor(diffMilliseconds / 86_400_000); + const diffDays = Math.floor(diffMilliseconds / DAY); if (Math.abs(diffDays) < 30) { return relativeTimeFormatter.format(-diffDays, "day"); }