From ee421abf8964498466913dd8f211c23fc071e500 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 17 Jan 2024 21:54:34 +0500 Subject: [PATCH] feat(DocPage): Add updatedAt meta (#198) --- demo/src/Components/DocPage/page-en.json | 3 +- demo/src/Components/DocPage/page-he.json | 3 +- demo/src/Components/DocPage/page-ru.json | 3 +- .../ContributorAvatars/Avatars/Avatar.tsx | 2 +- .../ContributorAvatars/Avatars/Details.tsx | 2 +- .../Avatars/HiddenAvatars.tsx | 2 +- .../ContributorAvatars.scss | 36 ++------- .../ContributorAvatars/ContributorAvatars.tsx | 17 +--- src/components/Contributors/Contributors.scss | 3 +- src/components/Contributors/Contributors.tsx | 2 +- src/components/DocPage/DocPage.scss | 19 +++-- src/components/DocPage/DocPage.tsx | 16 +++- .../UpdatedAtDate/UpdatedAtDate.scss | 11 +++ .../UpdatedAtDate/UpdatedAtDate.tsx | 34 ++++++++ src/i18n/en.json | 5 +- src/i18n/ru.json | 5 +- src/models/index.ts | 2 + src/utils/date.ts | 77 +++++++++++++++++++ 18 files changed, 178 insertions(+), 64 deletions(-) create mode 100644 src/components/UpdatedAtDate/UpdatedAtDate.scss create mode 100644 src/components/UpdatedAtDate/UpdatedAtDate.tsx create mode 100644 src/utils/date.ts diff --git a/demo/src/Components/DocPage/page-en.json b/demo/src/Components/DocPage/page-en.json index 8119c91a..f540e792 100644 --- a/demo/src/Components/DocPage/page-en.json +++ b/demo/src/Components/DocPage/page-en.json @@ -149,7 +149,8 @@ "name": "Anastasia Karavaeva", "url": "" } - ] + ], + "updatedAt": "2024-01-15T15:00:00.000Z" }, "toc": { "title": "Yandex.Cloud overview", diff --git a/demo/src/Components/DocPage/page-he.json b/demo/src/Components/DocPage/page-he.json index d7ab89aa..9aea66b8 100644 --- a/demo/src/Components/DocPage/page-he.json +++ b/demo/src/Components/DocPage/page-he.json @@ -114,7 +114,8 @@ "name": "DataUI VCS Robot", "url": "" } - ] + ], + "updatedAt": "2024-01-15T15:00:00.000Z" }, "toc": { "title": "סקירת פלטפורמה", diff --git a/demo/src/Components/DocPage/page-ru.json b/demo/src/Components/DocPage/page-ru.json index fa452b63..478ff891 100644 --- a/demo/src/Components/DocPage/page-ru.json +++ b/demo/src/Components/DocPage/page-ru.json @@ -114,7 +114,8 @@ "name": "DataUI VCS Robot", "url": "" } - ] + ], + "updatedAt": "2024-01-15T15:00:00.000Z" }, "toc": { "title": "Обзор платформы", diff --git a/src/components/ContributorAvatars/Avatars/Avatar.tsx b/src/components/ContributorAvatars/Avatars/Avatar.tsx index 23c84dab..c3db471f 100644 --- a/src/components/ContributorAvatars/Avatars/Avatar.tsx +++ b/src/components/ContributorAvatars/Avatars/Avatar.tsx @@ -8,7 +8,7 @@ import {getUserIdentificator} from '../utils'; import '../ContributorAvatars.scss'; -const b = block('contributor-avatars'); +const b = block('dc-contributor-avatars'); interface AvatarProps { avatarData: AvatarData; diff --git a/src/components/ContributorAvatars/Avatars/Details.tsx b/src/components/ContributorAvatars/Avatars/Details.tsx index 3e2c6010..d5dc9bc8 100644 --- a/src/components/ContributorAvatars/Avatars/Details.tsx +++ b/src/components/ContributorAvatars/Avatars/Details.tsx @@ -12,7 +12,7 @@ import Avatar from './Avatar'; import '../ContributorAvatars.scss'; -const b = block('contributor-avatars'); +const b = block('dc-contributor-avatars'); interface DetailsProps { contributors: Contributor[]; diff --git a/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx b/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx index da92d163..991b0786 100644 --- a/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx +++ b/src/components/ContributorAvatars/Avatars/HiddenAvatars.tsx @@ -9,7 +9,7 @@ import Details from './Details'; import '../ContributorAvatars.scss'; -const b = block('contributor-avatars'); +const b = block('dc-contributor-avatars'); const LOWER_BOUND_MORE_CONTRIBUTORS = 9; diff --git a/src/components/ContributorAvatars/ContributorAvatars.scss b/src/components/ContributorAvatars/ContributorAvatars.scss index 4fb94673..2c389774 100644 --- a/src/components/ContributorAvatars/ContributorAvatars.scss +++ b/src/components/ContributorAvatars/ContributorAvatars.scss @@ -26,43 +26,19 @@ $avatarWrapperBigSize: 33px; } } -.contributor-avatars { +.dc-contributor-avatars { &__one_contributor { display: flex; margin: 0 $avatarBorder; @include disable-link-styles; - & > * { - @include contributors-text(); - } - - @media screen and (max-width: map-get($screenBreakpoints, 'sm')) { - &_onlyAuthor > *:last-child { - margin-left: 5px; - } - - & > *:last-child, - & > .desktop { - display: none; - } - - &_onlyAuthor > *:last-child { - display: block; - } - - & > *:not(:last-child) { - margin-right: 0px; - } + a:hover, + a:active { + color: var(--g-color-text-link-hover); } - @media screen and (min-width: map-get($screenBreakpoints, 'sm')) { - & > .mobile { - display: none; - } - - & > *:last-child { - margin-right: 0px; - } + & > * { + @include contributors-text(); } } diff --git a/src/components/ContributorAvatars/ContributorAvatars.tsx b/src/components/ContributorAvatars/ContributorAvatars.tsx index ad0618f7..4f0c299a 100644 --- a/src/components/ContributorAvatars/ContributorAvatars.tsx +++ b/src/components/ContributorAvatars/ContributorAvatars.tsx @@ -13,7 +13,7 @@ import {getName} from './utils'; import './ContributorAvatars.scss'; -const b = block('contributor-avatars'); +const b = block('dc-contributor-avatars'); const MAX_DISPLAYED_CONTRIBUTORS = 3; @@ -31,8 +31,7 @@ const ContributorAvatars: React.FC = (props) => { } if (contributors.length === 1) { - const oneAvatar = getOneAvatar(contributors[0], isAuthor, onlyAuthor); - return oneAvatar; + return getOneAvatar(contributors[0], isAuthor, onlyAuthor); } const displayedContributors = [...contributors]; @@ -79,17 +78,7 @@ function getOneAvatar( return (
-
{getRedirectingAvatar(avatarData, contributor.url)}
-
- {isAuthor && onlyAuthor ? ( - getRedirectingAvatar(avatarData, contributor.url, true) - ) : ( - - )} -
+ {getRedirectingAvatar(avatarData, contributor.url)}
{getName(contributor, isAuthor)}
diff --git a/src/components/Contributors/Contributors.scss b/src/components/Contributors/Contributors.scss index 75e2e2b4..10ccc771 100644 --- a/src/components/Contributors/Contributors.scss +++ b/src/components/Contributors/Contributors.scss @@ -1,8 +1,7 @@ @import '../../styles/mixins'; -.contributors { +.dc-contributors { display: flex; - margin: 0 0 32px; &__title { @include contributors-text(); diff --git a/src/components/Contributors/Contributors.tsx b/src/components/Contributors/Contributors.tsx index 7db8e145..7f9b7f54 100644 --- a/src/components/Contributors/Contributors.tsx +++ b/src/components/Contributors/Contributors.tsx @@ -8,7 +8,7 @@ import {ContributorAvatars} from '../ContributorAvatars'; import './Contributors.scss'; -const b = block('contributors'); +const b = block('dc-contributors'); export interface ContributorsProps { users: Contributor[]; diff --git a/src/components/DocPage/DocPage.scss b/src/components/DocPage/DocPage.scss index 036c016b..008ce350 100644 --- a/src/components/DocPage/DocPage.scss +++ b/src/components/DocPage/DocPage.scss @@ -135,7 +135,7 @@ } &__title { - margin-bottom: 8px; + margin-bottom: 12px; @include text-size(display-2); } @@ -234,13 +234,20 @@ } } - &__separator { - margin: 0 5px 32px 0; - align-self: center; - } - &__page-contributors { display: flex; + margin-bottom: 24px; + flex-wrap: wrap; + + & > div { + margin-right: 20px; + margin-bottom: 8px; + line-height: 24px; + } + + & > div:last-child { + margin-right: 0; + } } @media screen and (max-width: 1440px) { diff --git a/src/components/DocPage/DocPage.tsx b/src/components/DocPage/DocPage.tsx index 8b06ef3b..f4caf820 100644 --- a/src/components/DocPage/DocPage.tsx +++ b/src/components/DocPage/DocPage.tsx @@ -28,6 +28,7 @@ import {HTML} from '../HTML'; import {MiniToc} from '../MiniToc'; import {SearchBar, withHighlightedSearchWords} from '../SearchBar'; import {TocNavPanel} from '../TocNavPanel'; +import UpdatedAtDate from '../UpdatedAtDate/UpdatedAtDate'; import './DocPage.scss'; @@ -384,16 +385,25 @@ class DocPage extends React.Component { const author = this.renderAuthor(!meta?.contributors?.length); const contributors = this.renderContributors(); - - const separator = author && contributors &&
{','}
; + const updatedAt = this.renderUpdatedAt(meta?.updatedAt); return (
- {author} {separator} {contributors} + {author} + {contributors} + {updatedAt}
); } + private renderUpdatedAt(updatedAt?: string) { + if (!updatedAt) { + return null; + } + + return ; + } + private renderAuthor(onlyAuthor: boolean) { const {meta} = this.props; diff --git a/src/components/UpdatedAtDate/UpdatedAtDate.scss b/src/components/UpdatedAtDate/UpdatedAtDate.scss new file mode 100644 index 00000000..ed1c9acc --- /dev/null +++ b/src/components/UpdatedAtDate/UpdatedAtDate.scss @@ -0,0 +1,11 @@ +@import '../../styles/mixins'; + +.dc-updated-at-date { + display: flex; + font-size: 13px; + font-weight: 400; + + &__wrapper { + align-self: center; + } +} diff --git a/src/components/UpdatedAtDate/UpdatedAtDate.tsx b/src/components/UpdatedAtDate/UpdatedAtDate.tsx new file mode 100644 index 00000000..e912a849 --- /dev/null +++ b/src/components/UpdatedAtDate/UpdatedAtDate.tsx @@ -0,0 +1,34 @@ +import React, {memo, useMemo} from 'react'; + +import block from 'bem-cn-lite'; + +import {getConfig} from '../../config'; +import {useTranslation} from '../../hooks'; +import {format} from '../../utils/date'; + +import './UpdatedAtDate.scss'; + +const b = block('dc-updated-at-date'); + +export interface UpdatedAtDateProps { + updatedAt: string; +} + +const UpdatedAtDate: React.FC = ({updatedAt}) => { + const {t} = useTranslation('updated-at-date'); + + const updatedAtFormatted = useMemo(() => { + const {localeCode} = getConfig(); + return format(updatedAt, 'longDate', localeCode); + }, [updatedAt]); + + return ( +
+
+ {t('title')} {updatedAtFormatted} +
+
+ ); +}; + +export default memo(UpdatedAtDate); diff --git a/src/i18n/en.json b/src/i18n/en.json index 336defac..35bcbcb1 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -57,7 +57,7 @@ "title": "Written by" }, "contributors": { - "title": "improved by" + "title": "Improved by" }, "feedback": { "like-text": "Helpful", @@ -113,5 +113,8 @@ "paginator": { "next": "Next page", "prev": "Previous page" + }, + "updated-at-date": { + "title": "Updated at" } } diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 874fff15..a2a1da5c 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -57,7 +57,7 @@ "title": "Статья создана" }, "contributors": { - "title": "улучшена" + "title": "Улучшена" }, "feedback": { "like-text": "Статья полезна", @@ -113,5 +113,8 @@ "paginator": { "next": "Следующая страница", "prev": "Предыдущая страница" + }, + "updated-at-date": { + "title": "Обновлена" } } diff --git a/src/models/index.ts b/src/models/index.ts index 6238747b..632ffc42 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -19,6 +19,7 @@ export enum ControlSizes { export interface Config { lang?: string; loc?: Loc; + localeCode?: string; } export interface DocSettings { @@ -92,6 +93,7 @@ export interface DocMeta { contributors?: Contributor[]; author?: unknown | Contributor; __system?: Record; + updatedAt?: string; } export interface TocData { diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 00000000..b295b5dd --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,77 @@ +interface DateTimeFormatter { + longDate: Intl.DateTimeFormat; + shortDate: Intl.DateTimeFormat; + longMonthDay: Intl.DateTimeFormat; + shortMonthDay: Intl.DateTimeFormat; + longDateTime: Intl.DateTimeFormat; + shortDateTime: Intl.DateTimeFormat; + shortTime: Intl.DateTimeFormat; + nanoTime: Intl.DateTimeFormat; + year: Intl.DateTimeFormat; +} + +const defaultRegion = 'en'; + +const dateTimeFormatters: Map = new Map(); + +function getDateTimeFormatter(localeCode: string): DateTimeFormatter { + if (!dateTimeFormatters.has(localeCode)) { + const formatters = { + // December 20, 2012 + longDate: new Intl.DateTimeFormat(localeCode, { + year: 'numeric', + month: 'long', + day: 'numeric', + }), + // 4/30/2021 + shortDate: new Intl.DateTimeFormat(localeCode, { + year: 'numeric', + month: 'numeric', + day: 'numeric', + }), + // December 20 + longMonthDay: new Intl.DateTimeFormat(localeCode, {month: 'long', day: 'numeric'}), + // 12/20 + shortMonthDay: new Intl.DateTimeFormat(localeCode, {month: 'numeric', day: 'numeric'}), + // April 27, 2021, 3:03 PM + longDateTime: new Intl.DateTimeFormat(localeCode, { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }), + // 4/30/2021, 2:30 PM + shortDateTime: new Intl.DateTimeFormat(localeCode, { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }), + // 12:30 + shortTime: new Intl.DateTimeFormat(localeCode, {hour: 'numeric', minute: 'numeric'}), + // 30:58 + nanoTime: new Intl.DateTimeFormat(localeCode, { + minute: 'numeric', + second: 'numeric', + }), + // 2023 + year: new Intl.DateTimeFormat(localeCode, { + year: 'numeric', + }), + }; + dateTimeFormatters.set(localeCode, formatters); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- formatter will be always inserted above + return dateTimeFormatters.get(localeCode)!; +} + +export const format = ( + date: string | number, + formatCode: keyof DateTimeFormatter, + localeCode = defaultRegion, +) => { + return getDateTimeFormatter(localeCode)[formatCode].format(new Date(date)); +};