From 4f6e5080f9d02523404562978007c046474c6283 Mon Sep 17 00:00:00 2001 From: avivkeller <me@aviv.sh> Date: Thu, 13 Mar 2025 16:37:36 -0400 Subject: [PATCH 1/7] feat(ui): add ui-components package --- .github/workflows/lint-and-tests.yml | 12 +- COLLABORATOR_GUIDE.md | 102 +++-- apps/site/.storybook/main.ts | 75 ---- apps/site/.stylelintignore | 3 - apps/site/app/[locale]/layout.tsx | 2 +- .../next-data/og/[category]/[title]/route.tsx | 6 +- .../Blog/BlogHeader/index.stories.tsx | 23 - .../BlogPostCard/__tests__/index.test.mjs | 2 +- .../BlogPostCard/index.module.css | 0 .../{Common => Blog}/BlogPostCard/index.tsx | 2 +- apps/site/components/Common/ActiveLink.tsx | 14 + .../AvatarGroup/Avatar/index.module.css | 38 -- .../Common/AvatarGroup/Avatar/index.tsx | 61 --- .../AvatarGroup/Overlay/index.module.css | 29 -- .../AvatarGroup/Overlay/index.stories.tsx | 21 - .../components/Common/AvatarGroup/index.tsx | 80 ---- .../components/Common/Badge/index.module.css | 99 ---- .../components/Common/Banner/index.module.css | 40 -- .../Common/Blockquote/index.module.css | 27 -- .../Common/BlogPostCard/index.stories.tsx | 35 -- .../BreadcrumbItem/index.module.css | 39 -- .../BreadcrumbLink/index.module.css | 20 - .../BreadcrumbRoot/index.module.css | 7 - apps/site/components/Common/Button.tsx | 12 + .../components/Common/Button/index.module.css | 146 ------ apps/site/components/Common/CodeBox.tsx | 36 ++ .../Common/CodeBox/index.module.css | 82 ---- .../Common/CodeTabs/index.module.css | 54 --- .../site/components/Common/CodeTabs/index.tsx | 41 -- apps/site/components/Common/CrossLink.tsx | 19 + .../Common/CrossLink/index.module.css | 49 -- .../Common/GlowingBackdrop/index.module.css | 33 -- .../Common/LanguageDropDown/index.module.css | 49 -- apps/site/components/Common/LinkTabs.tsx | 15 + .../Common/LinkTabs/index.module.css | 41 -- .../Common/NodejsLogo/index.module.css | 4 - .../components/Common/NodejsLogo/index.tsx | 16 - .../Common/Notification/index.module.css | 18 - apps/site/components/Common/Pagination.tsx | 30 ++ .../Pagination/Ellipsis/index.module.css | 8 - .../PaginationListItem/index.module.css | 23 - .../Common/Pagination/index.module.css | 37 -- .../Common/Preview/index.module.css | 81 ---- .../ProgressionSidebarGroup/index.module.css | 53 --- .../ProgressionSidebarItem/index.module.css | 37 -- .../ProgressionSidebar/index.module.css | 28 -- .../components/Common/Select/index.module.css | 151 ------- .../Common/Skeleton/index.module.css | 28 -- .../components/Common/Tabs/index.module.css | 51 --- .../Common/ThemeToggle/index.module.css | 13 - .../Common/Tooltip/index.module.css | 41 -- .../Containers/Footer/index.module.css | 44 -- .../Containers/Footer/index.stories.tsx | 10 - .../components/Containers/Footer/index.tsx | 62 --- .../Containers/MetaBar/index.module.css | 82 ---- .../Containers/NavBar/index.module.css | 124 ------ .../components/Containers/NavBar/index.tsx | 109 ----- .../Sidebar/SidebarGroup/index.module.css | 24 - .../Containers/Sidebar/SidebarGroup/index.tsx | 24 - .../Sidebar/SidebarItem/index.module.css | 33 -- .../Containers/Sidebar/index.module.css | 29 -- .../DownloadButton/index.stories.tsx | 30 -- .../Release/InstallationMethodDropdown.tsx | 2 +- .../Release/OperatingSystemDropdown.tsx | 2 +- .../Release/PackageManagerDropdown.tsx | 2 +- .../Downloads/Release/PlatformDropdown.tsx | 2 +- .../Release/PrebuiltDownloadButtons.tsx | 2 +- .../Downloads/Release/ReleaseCodeBox.tsx | 4 +- .../Downloads/Release/VersionDropdown.tsx | 2 +- .../Icons/InstallationMethod/FNM.tsx | 99 ---- .../Icons/InstallationMethod/index.ts | 9 - .../components/Icons/OperatingSystem/index.ts | 6 - .../components/Icons/PackageManager/index.ts | 5 - .../MDX/Calendar/Event/index.stories.tsx | 18 - .../components/MDX/CodeBox/index.stories.tsx | 47 -- .../components/MDX/CodeTabs/index.stories.tsx | 56 --- apps/site/components/MDX/CodeTabs/index.tsx | 10 +- .../__design__/node-logos.stories.tsx | 60 --- .../__design__/package-manager.stories.tsx | 15 - .../__design__/platform-logos.stories.tsx | 29 -- .../__design__/social-logos.stories.tsx | 31 -- .../components/__design__/table.stories.tsx | 62 --- apps/site/components/withAvatarGroup.tsx | 10 +- apps/site/components/withBadge.tsx | 10 +- apps/site/components/withBanner.tsx | 13 +- apps/site/components/withBlogCategories.tsx | 2 +- apps/site/components/withBreadcrumbs.tsx | 16 +- apps/site/components/withFooter.tsx | 25 +- apps/site/components/withMetaBar.tsx | 29 +- apps/site/components/withNavBar.tsx | 61 ++- apps/site/components/withNodejsLogo.tsx | 15 +- .../components/withProgressionSidebar.tsx | 23 +- apps/site/components/withRouterSelect.tsx | 2 +- apps/site/components/withSidebar.tsx | 20 +- apps/site/eslint.config.js | 7 +- apps/site/layouts/GlowingBackdrop.tsx | 2 +- apps/site/layouts/Post.tsx | 2 +- apps/site/next.mdx.use.client.mjs | 3 +- apps/site/package.json | 21 +- apps/site/providers/notificationProvider.tsx | 3 +- apps/site/tailwind.config.ts | 187 +------- apps/site/tsconfig.json | 4 +- apps/site/turbo.json | 17 +- apps/site/util/downloadUtils.tsx | 8 +- package-lock.json | 421 +++++++++++++++--- package.json | 1 + packages/ui-components/.postcssrc.json | 11 + .../ui-components}/.storybook/constants.ts | 0 packages/ui-components/.storybook/main.ts | 34 ++ .../.storybook/preview-head.html | 0 .../ui-components}/.storybook/preview.tsx | 19 +- packages/ui-components/.stylelintignore | 1 + packages/ui-components/.stylelintrc.mjs | 46 ++ .../Common/AlertBox/index.module.css | 25 +- .../Common/AlertBox/index.stories.tsx | 2 +- .../ui-components}/Common/AlertBox/index.tsx | 0 .../AvatarGroup/Avatar/index.module.css | 19 + .../AvatarGroup/Avatar/index.stories.tsx | 7 +- .../Common/AvatarGroup/Avatar/index.tsx | 76 ++++ .../AvatarGroup/Overlay/index.module.css | 19 + .../AvatarGroup/Overlay/index.stories.tsx | 33 ++ .../Common/AvatarGroup/Overlay/index.tsx | 10 +- .../AvatarGroup/__tests__/index.test.mjs | 22 +- .../Common/AvatarGroup/index.module.css | 4 +- .../Common/AvatarGroup/index.stories.tsx | 10 +- .../Common/AvatarGroup/index.tsx | 90 ++++ .../Common/Badge/index.module.css | 63 +++ .../Common/Badge/index.stories.tsx | 2 +- .../ui-components}/Common/Badge/index.tsx | 10 +- .../Common/Banner/index.module.css | 28 ++ .../Common/Banner/index.stories.tsx | 12 +- .../ui-components}/Common/Banner/index.tsx | 10 +- .../BaseActiveLink}/__tests__/index.test.mjs | 23 - .../Common/BaseActiveLink}/index.tsx | 24 +- .../Common/BaseButton/index.module.css | 87 ++++ .../Common/BaseButton}/index.stories.tsx | 8 +- .../Common/BaseButton}/index.tsx | 25 +- .../Common/BaseCodeBox/index.module.css | 43 ++ .../Common/BaseCodeBox}/index.stories.tsx | 21 +- .../Common/BaseCodeBox}/index.tsx | 78 ++-- .../Common/BaseCrossLink/index.module.css | 23 + .../Common/BaseCrossLink}/index.stories.tsx | 8 +- .../Common/BaseCrossLink}/index.tsx | 31 +- .../Common/BaseLinkTabs/index.module.css | 19 + .../Common/BaseLinkTabs}/index.stories.tsx | 9 +- .../Common/BaseLinkTabs}/index.tsx | 23 +- .../BasePagination/Ellipsis/index.module.css | 3 + .../Ellipsis/index.stories.tsx | 2 +- .../Common/BasePagination}/Ellipsis/index.tsx | 0 .../__tests__/index.test.mjs | 2 +- .../PaginationListItem/index.module.css | 9 + .../PaginationListItem/index.stories.tsx | 2 +- .../PaginationListItem/index.tsx | 15 +- .../BasePagination}/__tests__/index.test.mjs | 23 +- .../Common/BasePagination/index.module.css | 32 ++ .../Common/BasePagination}/index.stories.tsx | 16 +- .../Common/BasePagination}/index.tsx | 45 +- .../BasePagination}/useGetPageElements.tsx | 27 +- .../Common/Blockquote/index.module.css | 11 + .../Common/Blockquote/index.stories.tsx | 2 +- .../Common/Blockquote/index.tsx | 0 .../BreadcrumbHomeLink/index.module.css | 0 .../Breadcrumbs/BreadcrumbHomeLink/index.tsx | 14 +- .../BreadcrumbItem/index.module.css | 25 ++ .../Breadcrumbs/BreadcrumbItem/index.tsx | 0 .../BreadcrumbLink/index.module.css | 11 + .../Breadcrumbs/BreadcrumbLink/index.tsx | 10 +- .../BreadcrumbRoot/index.module.css | 3 + .../Breadcrumbs/BreadcrumbRoot/index.tsx | 0 .../BreadcrumbTruncatedItem/index.tsx | 2 +- .../Common/Breadcrumbs/index.stories.tsx | 2 +- .../Common/Breadcrumbs/index.tsx | 23 +- .../Common/CodeTabs/index.module.css | 17 + .../Common/CodeTabs/index.stories.tsx | 25 +- .../ui-components/Common/CodeTabs/index.tsx | 16 + .../Common/GlowingBackdrop/index.module.css | 11 + .../Common/GlowingBackdrop/index.stories.tsx | 2 +- .../Common/GlowingBackdrop/index.tsx | 2 +- .../Common/LanguageDropDown/index.module.css | 23 + .../Common/LanguageDropDown/index.stories.tsx | 2 +- .../Common/LanguageDropDown/index.tsx | 12 +- .../Common/NodejsLogo/index.module.css | 3 + .../Common/NodejsLogo/index.stories.tsx | 2 +- .../ui-components/Common/NodejsLogo/index.tsx | 26 ++ .../Common/Notification/index.module.css | 7 + .../Common/Notification/index.stories.tsx | 2 +- .../Common/Notification/index.tsx | 0 .../ui-components}/Common/PrevNextArrow.tsx | 0 .../Common/Preview/index.module.css | 31 ++ .../Common/Preview/index.stories.tsx | 2 +- .../ui-components}/Common/Preview/index.tsx | 8 +- .../ProgressionSidebarGroup/index.module.css | 21 + .../ProgressionSidebarGroup/index.tsx | 17 +- .../ProgressionSidebarIcon/index.tsx | 0 .../ProgressionSidebarItem/index.module.css | 19 + .../ProgressionSidebarItem/index.tsx | 17 +- .../ProgressionSidebar/index.module.css | 11 + .../ProgressionSidebar/index.stories.tsx | 2 +- .../Common/ProgressionSidebar/index.tsx | 34 +- .../Common/Select/__tests__/index.test.mjs | 0 .../Common/Select/index.module.css | 71 +++ .../Common/Select/index.stories.tsx | 4 +- .../ui-components}/Common/Select/index.tsx | 5 +- .../Common/Skeleton/index.module.css | 17 + .../ui-components}/Common/Skeleton/index.tsx | 0 .../Common/Tabs/__tests__/index.test.mjs | 3 +- .../Common/Tabs/index.module.css | 27 ++ .../Common/Tabs/index.stories.tsx | 2 +- .../ui-components}/Common/Tabs/index.tsx | 0 .../ThemeToggle/__tests__/index.test.mjs | 0 .../Common/ThemeToggle/index.module.css | 7 + .../Common/ThemeToggle/index.stories.tsx | 2 +- .../Common/ThemeToggle/index.tsx | 11 +- .../Common/Tooltip/index.module.css | 27 ++ .../Common/Tooltip/index.stories.tsx | 5 +- .../ui-components}/Common/Tooltip/index.tsx | 0 .../Containers/Footer/index.module.css | 19 + .../Containers/Footer/index.stories.tsx | 27 ++ .../ui-components/Containers/Footer/index.tsx | 96 ++++ .../MetaBar/__tests__/index.test.mjs | 0 .../Containers/MetaBar/index.module.css | 43 ++ .../Containers/MetaBar/index.stories.tsx | 33 +- .../Containers/MetaBar/index.tsx | 31 +- .../NavBar/NavItem/index.module.css | 0 .../NavBar/NavItem/index.stories.tsx | 3 +- .../Containers/NavBar/NavItem/index.tsx | 14 +- .../Containers/NavBar/index.module.css | 56 +++ .../Containers/NavBar/index.stories.tsx | 17 +- .../ui-components/Containers/NavBar/index.tsx | 94 ++++ .../Sidebar/SidebarGroup/index.module.css | 11 + .../Sidebar/SidebarGroup/index.stories.tsx | 2 +- .../Containers/Sidebar/SidebarGroup/index.tsx | 33 ++ .../Sidebar/SidebarItem/index.module.css | 19 + .../Sidebar/SidebarItem/index.stories.tsx | 2 +- .../Containers/Sidebar/SidebarItem/index.tsx | 13 +- .../Containers/Sidebar/index.module.css | 11 + .../Containers/Sidebar/index.stories.tsx | 4 +- .../Containers/Sidebar/index.tsx | 31 +- .../Icons/HexagonGrid.stories.tsx | 2 +- .../ui-components}/Icons/HexagonGrid.tsx | 0 .../Icons/InstallationMethod/Choco.tsx | 0 .../Icons/InstallationMethod/Devbox.tsx | 0 .../Icons/InstallationMethod/Docker.tsx | 0 .../Icons/InstallationMethod/FNM.tsx | 132 ++++++ .../Icons/InstallationMethod/Homebrew.tsx | 0 .../Icons/InstallationMethod/NVM.tsx | 0 .../Icons/InstallationMethod/Volta.tsx | 0 .../Icons/InstallationMethod/index.ts | 9 + .../ui-components/Icons/Logos/JsGreen.tsx | 4 +- .../ui-components/Icons/Logos/JsWhite.tsx | 6 +- .../ui-components}/Icons/Logos/Nodejs.tsx | 12 +- .../Icons/Logos/NodejsStackedBlack.tsx | 4 +- .../Icons/Logos/NodejsStackedDark.tsx | 4 +- .../Icons/Logos/NodejsStackedLight.tsx | 4 +- .../Icons/Logos/NodejsStackedWhite.tsx | 4 +- packages/ui-components/Icons/Logos/index.ts | 17 + .../Icons/OperatingSystem/AIX.tsx | 0 .../Icons/OperatingSystem/Apple.tsx | 0 .../Icons/OperatingSystem/Linux.tsx | 0 .../Icons/OperatingSystem/Microsoft.tsx | 0 .../Icons/OperatingSystem/index.ts | 6 + .../Icons/PackageManager/Npm.tsx | 0 .../Icons/PackageManager/Pnpm.tsx | 0 .../Icons/PackageManager/Yarn.tsx | 0 .../Icons/PackageManager/index.ts | 5 + .../ui-components}/Icons/Social/Bluesky.tsx | 4 +- .../ui-components}/Icons/Social/Discord.tsx | 4 +- .../ui-components}/Icons/Social/GitHub.tsx | 4 +- .../ui-components}/Icons/Social/LinkedIn.tsx | 4 +- .../ui-components}/Icons/Social/Mastodon.tsx | 4 +- .../ui-components}/Icons/Social/Slack.tsx | 4 +- .../ui-components/Icons/Social/X.tsx | 4 +- packages/ui-components/Icons/Social/index.ts | 9 + .../__design__/colors.stories.tsx | 0 .../__design__/effects.stories.tsx | 0 .../__design__/font-family.stories.tsx | 0 .../__design__/list.stories.tsx | 0 .../__design__/node-logos.stories.tsx | 61 +++ .../__design__/platform-logos.stories.tsx | 34 ++ .../__design__/social-logos.stories.tsx | 33 ++ .../__design__/table.stories.tsx | 52 +++ .../__design__/text.stories.tsx | 0 packages/ui-components/__mocks__/styleMock.js | 1 + packages/ui-components/eslint.config.js | 68 +++ packages/ui-components/global.d.ts | 4 + packages/ui-components/jest.config.mjs | 30 ++ packages/ui-components/package.json | 61 +++ .../ui-components}/styles/base.css | 0 .../ui-components}/styles/effects.css | 0 .../ui-components}/styles/index.css | 0 .../ui-components}/styles/locals.css | 0 .../ui-components}/styles/markdown.css | 4 - packages/ui-components/tailwind.config.ts | 156 +++++++ packages/ui-components/tsconfig.json | 26 ++ packages/ui-components/turbo.json | 30 ++ packages/ui-components/types.ts | 25 ++ 296 files changed, 3450 insertions(+), 3556 deletions(-) delete mode 100644 apps/site/.storybook/main.ts delete mode 100644 apps/site/components/Blog/BlogHeader/index.stories.tsx rename apps/site/components/{Common => Blog}/BlogPostCard/__tests__/index.test.mjs (97%) rename apps/site/components/{Common => Blog}/BlogPostCard/index.module.css (100%) rename apps/site/components/{Common => Blog}/BlogPostCard/index.tsx (96%) create mode 100644 apps/site/components/Common/ActiveLink.tsx delete mode 100644 apps/site/components/Common/AvatarGroup/Avatar/index.module.css delete mode 100644 apps/site/components/Common/AvatarGroup/Avatar/index.tsx delete mode 100644 apps/site/components/Common/AvatarGroup/Overlay/index.module.css delete mode 100644 apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx delete mode 100644 apps/site/components/Common/AvatarGroup/index.tsx delete mode 100644 apps/site/components/Common/Badge/index.module.css delete mode 100644 apps/site/components/Common/Banner/index.module.css delete mode 100644 apps/site/components/Common/Blockquote/index.module.css delete mode 100644 apps/site/components/Common/BlogPostCard/index.stories.tsx delete mode 100644 apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css delete mode 100644 apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css delete mode 100644 apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css create mode 100644 apps/site/components/Common/Button.tsx delete mode 100644 apps/site/components/Common/Button/index.module.css create mode 100644 apps/site/components/Common/CodeBox.tsx delete mode 100644 apps/site/components/Common/CodeBox/index.module.css delete mode 100644 apps/site/components/Common/CodeTabs/index.module.css delete mode 100644 apps/site/components/Common/CodeTabs/index.tsx create mode 100644 apps/site/components/Common/CrossLink.tsx delete mode 100644 apps/site/components/Common/CrossLink/index.module.css delete mode 100644 apps/site/components/Common/GlowingBackdrop/index.module.css delete mode 100644 apps/site/components/Common/LanguageDropDown/index.module.css create mode 100644 apps/site/components/Common/LinkTabs.tsx delete mode 100644 apps/site/components/Common/LinkTabs/index.module.css delete mode 100644 apps/site/components/Common/NodejsLogo/index.module.css delete mode 100644 apps/site/components/Common/NodejsLogo/index.tsx delete mode 100644 apps/site/components/Common/Notification/index.module.css create mode 100644 apps/site/components/Common/Pagination.tsx delete mode 100644 apps/site/components/Common/Pagination/Ellipsis/index.module.css delete mode 100644 apps/site/components/Common/Pagination/PaginationListItem/index.module.css delete mode 100644 apps/site/components/Common/Pagination/index.module.css delete mode 100644 apps/site/components/Common/Preview/index.module.css delete mode 100644 apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css delete mode 100644 apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css delete mode 100644 apps/site/components/Common/ProgressionSidebar/index.module.css delete mode 100644 apps/site/components/Common/Select/index.module.css delete mode 100644 apps/site/components/Common/Skeleton/index.module.css delete mode 100644 apps/site/components/Common/Tabs/index.module.css delete mode 100644 apps/site/components/Common/ThemeToggle/index.module.css delete mode 100644 apps/site/components/Common/Tooltip/index.module.css delete mode 100644 apps/site/components/Containers/Footer/index.module.css delete mode 100644 apps/site/components/Containers/Footer/index.stories.tsx delete mode 100644 apps/site/components/Containers/Footer/index.tsx delete mode 100644 apps/site/components/Containers/MetaBar/index.module.css delete mode 100644 apps/site/components/Containers/NavBar/index.module.css delete mode 100644 apps/site/components/Containers/NavBar/index.tsx delete mode 100644 apps/site/components/Containers/Sidebar/SidebarGroup/index.module.css delete mode 100644 apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx delete mode 100644 apps/site/components/Containers/Sidebar/SidebarItem/index.module.css delete mode 100644 apps/site/components/Containers/Sidebar/index.module.css delete mode 100644 apps/site/components/Downloads/DownloadButton/index.stories.tsx delete mode 100644 apps/site/components/Icons/InstallationMethod/FNM.tsx delete mode 100644 apps/site/components/Icons/InstallationMethod/index.ts delete mode 100644 apps/site/components/Icons/OperatingSystem/index.ts delete mode 100644 apps/site/components/Icons/PackageManager/index.ts delete mode 100644 apps/site/components/MDX/Calendar/Event/index.stories.tsx delete mode 100644 apps/site/components/MDX/CodeBox/index.stories.tsx delete mode 100644 apps/site/components/MDX/CodeTabs/index.stories.tsx delete mode 100644 apps/site/components/__design__/node-logos.stories.tsx delete mode 100644 apps/site/components/__design__/package-manager.stories.tsx delete mode 100644 apps/site/components/__design__/platform-logos.stories.tsx delete mode 100644 apps/site/components/__design__/social-logos.stories.tsx delete mode 100644 apps/site/components/__design__/table.stories.tsx create mode 100644 packages/ui-components/.postcssrc.json rename {apps/site => packages/ui-components}/.storybook/constants.ts (100%) create mode 100644 packages/ui-components/.storybook/main.ts rename {apps/site => packages/ui-components}/.storybook/preview-head.html (100%) rename {apps/site => packages/ui-components}/.storybook/preview.tsx (50%) create mode 100644 packages/ui-components/.stylelintignore create mode 100644 packages/ui-components/.stylelintrc.mjs rename {apps/site/components => packages/ui-components}/Common/AlertBox/index.module.css (63%) rename {apps/site/components => packages/ui-components}/Common/AlertBox/index.stories.tsx (96%) rename {apps/site/components => packages/ui-components}/Common/AlertBox/index.tsx (100%) create mode 100644 packages/ui-components/Common/AvatarGroup/Avatar/index.module.css rename {apps/site/components => packages/ui-components}/Common/AvatarGroup/Avatar/index.stories.tsx (66%) create mode 100644 packages/ui-components/Common/AvatarGroup/Avatar/index.tsx create mode 100644 packages/ui-components/Common/AvatarGroup/Overlay/index.module.css create mode 100644 packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx rename {apps/site/components => packages/ui-components}/Common/AvatarGroup/Overlay/index.tsx (78%) rename {apps/site/components => packages/ui-components}/Common/AvatarGroup/__tests__/index.test.mjs (59%) rename {apps/site/components => packages/ui-components}/Common/AvatarGroup/index.module.css (79%) rename {apps/site/components => packages/ui-components}/Common/AvatarGroup/index.stories.tsx (78%) create mode 100644 packages/ui-components/Common/AvatarGroup/index.tsx create mode 100644 packages/ui-components/Common/Badge/index.module.css rename {apps/site/components => packages/ui-components}/Common/Badge/index.stories.tsx (91%) rename {apps/site/components => packages/ui-components}/Common/Badge/index.tsx (73%) create mode 100644 packages/ui-components/Common/Banner/index.module.css rename {apps/site/components => packages/ui-components}/Common/Banner/index.stories.tsx (61%) rename {apps/site/components => packages/ui-components}/Common/Banner/index.tsx (58%) rename {apps/site/components/Common/ActiveLink => packages/ui-components/Common/BaseActiveLink}/__tests__/index.test.mjs (63%) rename {apps/site/components/Common/ActiveLink => packages/ui-components/Common/BaseActiveLink}/index.tsx (60%) create mode 100644 packages/ui-components/Common/BaseButton/index.module.css rename {apps/site/components/Common/Button => packages/ui-components/Common/BaseButton}/index.stories.tsx (85%) rename {apps/site/components/Common/Button => packages/ui-components/Common/BaseButton}/index.tsx (79%) create mode 100644 packages/ui-components/Common/BaseCodeBox/index.module.css rename {apps/site/components/Common/CodeBox => packages/ui-components/Common/BaseCodeBox}/index.stories.tsx (62%) rename {apps/site/components/Common/CodeBox => packages/ui-components/Common/BaseCodeBox}/index.tsx (70%) create mode 100644 packages/ui-components/Common/BaseCrossLink/index.module.css rename {apps/site/components/Common/CrossLink => packages/ui-components/Common/BaseCrossLink}/index.stories.tsx (76%) rename {apps/site/components/Common/CrossLink => packages/ui-components/Common/BaseCrossLink}/index.tsx (55%) create mode 100644 packages/ui-components/Common/BaseLinkTabs/index.module.css rename {apps/site/components/Common/LinkTabs => packages/ui-components/Common/BaseLinkTabs}/index.stories.tsx (70%) rename {apps/site/components/Common/LinkTabs => packages/ui-components/Common/BaseLinkTabs}/index.tsx (65%) create mode 100644 packages/ui-components/Common/BasePagination/Ellipsis/index.module.css rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/Ellipsis/index.stories.tsx (74%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/Ellipsis/index.tsx (100%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/PaginationListItem/__tests__/index.test.mjs (92%) create mode 100644 packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/PaginationListItem/index.stories.tsx (87%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/PaginationListItem/index.tsx (75%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/__tests__/index.test.mjs (89%) create mode 100644 packages/ui-components/Common/BasePagination/index.module.css rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/index.stories.tsx (69%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/index.tsx (56%) rename {apps/site/components/Common/Pagination => packages/ui-components/Common/BasePagination}/useGetPageElements.tsx (79%) create mode 100644 packages/ui-components/Common/Blockquote/index.module.css rename {apps/site/components => packages/ui-components}/Common/Blockquote/index.stories.tsx (95%) rename {apps/site/components => packages/ui-components}/Common/Blockquote/index.tsx (100%) rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css (100%) rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx (59%) create mode 100644 packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.module.css rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbItem/index.tsx (100%) create mode 100644 packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.module.css rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbLink/index.tsx (80%) create mode 100644 packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbRoot/index.tsx (100%) rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx (66%) rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/index.stories.tsx (95%) rename {apps/site/components => packages/ui-components}/Common/Breadcrumbs/index.tsx (65%) create mode 100644 packages/ui-components/Common/CodeTabs/index.module.css rename {apps/site/components => packages/ui-components}/Common/CodeTabs/index.stories.tsx (78%) create mode 100644 packages/ui-components/Common/CodeTabs/index.tsx create mode 100644 packages/ui-components/Common/GlowingBackdrop/index.module.css rename {apps/site/components => packages/ui-components}/Common/GlowingBackdrop/index.stories.tsx (76%) rename {apps/site/components => packages/ui-components}/Common/GlowingBackdrop/index.tsx (75%) create mode 100644 packages/ui-components/Common/LanguageDropDown/index.module.css rename {apps/site/components => packages/ui-components}/Common/LanguageDropDown/index.stories.tsx (86%) rename {apps/site/components => packages/ui-components}/Common/LanguageDropDown/index.tsx (84%) create mode 100644 packages/ui-components/Common/NodejsLogo/index.module.css rename {apps/site/components => packages/ui-components}/Common/NodejsLogo/index.stories.tsx (81%) create mode 100644 packages/ui-components/Common/NodejsLogo/index.tsx create mode 100644 packages/ui-components/Common/Notification/index.module.css rename {apps/site/components => packages/ui-components}/Common/Notification/index.stories.tsx (91%) rename {apps/site/components => packages/ui-components}/Common/Notification/index.tsx (100%) rename {apps/site/components => packages/ui-components}/Common/PrevNextArrow.tsx (100%) create mode 100644 packages/ui-components/Common/Preview/index.module.css rename {apps/site/components => packages/ui-components}/Common/Preview/index.stories.tsx (93%) rename {apps/site/components => packages/ui-components}/Common/Preview/index.tsx (65%) create mode 100644 packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css rename {apps/site/components => packages/ui-components}/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx (58%) rename {apps/site/components => packages/ui-components}/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx (100%) create mode 100644 packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css rename {apps/site/components => packages/ui-components}/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx (52%) create mode 100644 packages/ui-components/Common/ProgressionSidebar/index.module.css rename {apps/site/components => packages/ui-components}/Common/ProgressionSidebar/index.stories.tsx (95%) rename {apps/site/components => packages/ui-components}/Common/ProgressionSidebar/index.tsx (58%) rename {apps/site/components => packages/ui-components}/Common/Select/__tests__/index.test.mjs (100%) create mode 100644 packages/ui-components/Common/Select/index.module.css rename {apps/site/components => packages/ui-components}/Common/Select/index.stories.tsx (94%) rename {apps/site/components => packages/ui-components}/Common/Select/index.tsx (97%) create mode 100644 packages/ui-components/Common/Skeleton/index.module.css rename {apps/site/components => packages/ui-components}/Common/Skeleton/index.tsx (100%) rename {apps/site/components => packages/ui-components}/Common/Tabs/__tests__/index.test.mjs (93%) create mode 100644 packages/ui-components/Common/Tabs/index.module.css rename {apps/site/components => packages/ui-components}/Common/Tabs/index.stories.tsx (94%) rename {apps/site/components => packages/ui-components}/Common/Tabs/index.tsx (100%) rename {apps/site/components => packages/ui-components}/Common/ThemeToggle/__tests__/index.test.mjs (100%) create mode 100644 packages/ui-components/Common/ThemeToggle/index.module.css rename {apps/site/components => packages/ui-components}/Common/ThemeToggle/index.stories.tsx (77%) rename {apps/site/components => packages/ui-components}/Common/ThemeToggle/index.tsx (73%) create mode 100644 packages/ui-components/Common/Tooltip/index.module.css rename {apps/site/components => packages/ui-components}/Common/Tooltip/index.stories.tsx (88%) rename {apps/site/components => packages/ui-components}/Common/Tooltip/index.tsx (100%) create mode 100644 packages/ui-components/Containers/Footer/index.module.css create mode 100644 packages/ui-components/Containers/Footer/index.stories.tsx create mode 100644 packages/ui-components/Containers/Footer/index.tsx rename {apps/site/components => packages/ui-components}/Containers/MetaBar/__tests__/index.test.mjs (100%) create mode 100644 packages/ui-components/Containers/MetaBar/index.module.css rename {apps/site/components => packages/ui-components}/Containers/MetaBar/index.stories.tsx (71%) rename {apps/site/components => packages/ui-components}/Containers/MetaBar/index.tsx (67%) rename {apps/site/components => packages/ui-components}/Containers/NavBar/NavItem/index.module.css (100%) rename {apps/site/components => packages/ui-components}/Containers/NavBar/NavItem/index.stories.tsx (87%) rename {apps/site/components => packages/ui-components}/Containers/NavBar/NavItem/index.tsx (78%) create mode 100644 packages/ui-components/Containers/NavBar/index.module.css rename {apps/site/components => packages/ui-components}/Containers/NavBar/index.stories.tsx (64%) create mode 100644 packages/ui-components/Containers/NavBar/index.tsx create mode 100644 packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css rename {apps/site/components => packages/ui-components}/Containers/Sidebar/SidebarGroup/index.stories.tsx (89%) create mode 100644 packages/ui-components/Containers/Sidebar/SidebarGroup/index.tsx create mode 100644 packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css rename {apps/site/components => packages/ui-components}/Containers/Sidebar/SidebarItem/index.stories.tsx (78%) rename {apps/site/components => packages/ui-components}/Containers/Sidebar/SidebarItem/index.tsx (56%) create mode 100644 packages/ui-components/Containers/Sidebar/index.module.css rename {apps/site/components => packages/ui-components}/Containers/Sidebar/index.stories.tsx (93%) rename {apps/site/components => packages/ui-components}/Containers/Sidebar/index.tsx (57%) rename {apps/site/components => packages/ui-components}/Icons/HexagonGrid.stories.tsx (77%) rename {apps/site/components => packages/ui-components}/Icons/HexagonGrid.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/Choco.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/Devbox.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/Docker.tsx (100%) create mode 100644 packages/ui-components/Icons/InstallationMethod/FNM.tsx rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/Homebrew.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/NVM.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/InstallationMethod/Volta.tsx (100%) create mode 100644 packages/ui-components/Icons/InstallationMethod/index.ts rename apps/site/components/Icons/Logos/JsIconGreen.tsx => packages/ui-components/Icons/Logos/JsGreen.tsx (95%) rename apps/site/components/Icons/Logos/JsIconWhite.tsx => packages/ui-components/Icons/Logos/JsWhite.tsx (94%) rename {apps/site/components => packages/ui-components}/Icons/Logos/Nodejs.tsx (98%) rename {apps/site/components => packages/ui-components}/Icons/Logos/NodejsStackedBlack.tsx (98%) rename {apps/site/components => packages/ui-components}/Icons/Logos/NodejsStackedDark.tsx (98%) rename {apps/site/components => packages/ui-components}/Icons/Logos/NodejsStackedLight.tsx (98%) rename {apps/site/components => packages/ui-components}/Icons/Logos/NodejsStackedWhite.tsx (98%) create mode 100644 packages/ui-components/Icons/Logos/index.ts rename {apps/site/components => packages/ui-components}/Icons/OperatingSystem/AIX.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/OperatingSystem/Apple.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/OperatingSystem/Linux.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/OperatingSystem/Microsoft.tsx (100%) create mode 100644 packages/ui-components/Icons/OperatingSystem/index.ts rename {apps/site/components => packages/ui-components}/Icons/PackageManager/Npm.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/PackageManager/Pnpm.tsx (100%) rename {apps/site/components => packages/ui-components}/Icons/PackageManager/Yarn.tsx (100%) create mode 100644 packages/ui-components/Icons/PackageManager/index.ts rename {apps/site/components => packages/ui-components}/Icons/Social/Bluesky.tsx (90%) rename {apps/site/components => packages/ui-components}/Icons/Social/Discord.tsx (96%) rename {apps/site/components => packages/ui-components}/Icons/Social/GitHub.tsx (94%) rename {apps/site/components => packages/ui-components}/Icons/Social/LinkedIn.tsx (85%) rename {apps/site/components => packages/ui-components}/Icons/Social/Mastodon.tsx (96%) rename {apps/site/components => packages/ui-components}/Icons/Social/Slack.tsx (92%) rename apps/site/components/Icons/Social/Twitter.tsx => packages/ui-components/Icons/Social/X.tsx (82%) create mode 100644 packages/ui-components/Icons/Social/index.ts rename {apps/site/components => packages/ui-components}/__design__/colors.stories.tsx (100%) rename {apps/site/components => packages/ui-components}/__design__/effects.stories.tsx (100%) rename {apps/site/components => packages/ui-components}/__design__/font-family.stories.tsx (100%) rename {apps/site/components => packages/ui-components}/__design__/list.stories.tsx (100%) create mode 100644 packages/ui-components/__design__/node-logos.stories.tsx create mode 100644 packages/ui-components/__design__/platform-logos.stories.tsx create mode 100644 packages/ui-components/__design__/social-logos.stories.tsx create mode 100644 packages/ui-components/__design__/table.stories.tsx rename {apps/site/components => packages/ui-components}/__design__/text.stories.tsx (100%) create mode 100644 packages/ui-components/__mocks__/styleMock.js create mode 100644 packages/ui-components/eslint.config.js create mode 100644 packages/ui-components/global.d.ts create mode 100644 packages/ui-components/jest.config.mjs create mode 100644 packages/ui-components/package.json rename {apps/site => packages/ui-components}/styles/base.css (100%) rename {apps/site => packages/ui-components}/styles/effects.css (100%) rename {apps/site => packages/ui-components}/styles/index.css (100%) rename {apps/site => packages/ui-components}/styles/locals.css (100%) rename {apps/site => packages/ui-components}/styles/markdown.css (97%) create mode 100644 packages/ui-components/tailwind.config.ts create mode 100644 packages/ui-components/tsconfig.json create mode 100644 packages/ui-components/turbo.json create mode 100644 packages/ui-components/types.ts diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index 4f1dacb2c68ad..ecc06785214af 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -204,7 +204,7 @@ jobs: # sha reference has no stable git tag reference or URL. see https://github.com/chromaui/chromatic-cli/issues/797 uses: chromaui/action@30b6228aa809059d46219e0f556752e8672a7e26 with: - workingDir: apps/site + workingDir: packages/ui-components buildScriptName: storybook:build projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} exitOnceUploaded: true @@ -220,6 +220,10 @@ jobs: uses: MishaKav/jest-coverage-comment@d74238813c33e6ea20530ff91b5ea37953d11c91 # v1.0.27 with: title: 'Unit Test Coverage Report' - junitxml-path: ./apps/site/junit.xml - junitxml-title: Unit Test Report - coverage-summary-path: ./apps/site/coverage/coverage-summary.json + multiple-junitxml-files: | + General, ./junit.xml + @node-core/ui-components, ./packages/ui-components/junit.xml + @node-core/website, ./apps/site/junit.xml + multiple-files: | + @node-core/ui-components, ./packages/ui-components/coverage/coverage-summary.json + @node-core/website, ./apps/site/coverage/coverage-summary.json diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 00b0939cf28fd..f38908df6793d 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -102,10 +102,10 @@ The Website also uses several other Open Source libraries (not limited to) liste Locations are subject to change. (If you are someone updating these paths, please document those changes here.) -- React Components are defined on `apps/site/components` +- React Components are defined on `apps/site/components` and `packages/ui-components` - React Templates are defined on `apps/site/layouts` -- Global Stylesheets are declared on `apps/site/styles` - - Styles are done with [PostCSS][] +- Global Stylesheets are declared on `packages/ui-components/styles` + - Styles are done with [PostCSS][] and [Tailwind][] - Public files are stored on `apps/site/public` - Static Images, JavaScript files, and others are stored within `apps/site/public/static` - Internationalisation is done on `apps/site/i18n` @@ -126,7 +126,7 @@ please document those changes here.) - Generation of build-time indexes such as blog data - Multi-Purpose Scripts are stored within `apps/site/scripts` - Such as Node.js Release Blog Post generation -- Storybook Configuration is done within `apps/site/.storybook` +- Storybook Configuration is done within `packages/ui-components/.storybook` - We use an almost out-of-the-box Storybook Experience with a few extra customisations ### Adding new Pages @@ -197,8 +197,6 @@ Finally, if you're unfamiliar with how to use Tailwind or how to use Tailwind wi - We discourage the usage of any plain CSS styles and tokens, when in doubt ask for help - We require that you define one Tailwind Token per line, just as shown on the example above, since this improves readability - Only write CSS within CSS Modules, avoid writing CSS within JavaScript files -- We recommend creating mixins for reusable animations, effects and more - - You can create Mixins within the `apps/site/styles/mixins` folder > \[!NOTE]\ > Tailwind is already configured for this repository. You don't need to import any Tailwind module within your CSS module.\ @@ -211,26 +209,76 @@ Finally, if you're unfamiliar with how to use Tailwind or how to use Tailwind wi ### Best practices when creating a Component -- All React Components should be placed within the `apps/site/components` folder. -- Each Component should be placed, whenever possible, within a sub-folder, which we call the "Domain" of the Component - - The domain represents where these Components belong to or where they will be used. - - For example, Components used within Article Pages or that are part of the structure of an Article or the Article Layouts, - should be placed within `apps/site/components/Article` -- Each component should have its folder with the name of the Component -- The structure of each component folder follows the following template: +- **All React components** should be placed within either `@node-core/ui-components` (for reusable components) or `apps/site/components` (for website-specific components). +- **Generic UI components** that are not tied to the website should be placed in the `@node-core/ui-components` package. + - These components should be **framework-agnostic** and must not rely on Next.js-specific features such as `usePathname()` or `useTranslations()`. + - If a component previously relied on Next.js, it should now accept these values as **props** instead. +- **Website-specific components** that rely on Next.js or are tied to the website should remain in `apps/site/components`. + - These components can use Next.js-specific hooks, API calls, or configurations. + - When using a generic UI component that requires Next.js functionality, pass it as a **prop** instead of modifying the component. +- **Each component** should be placed within a sub-folder, which we call the **"Domain"** of the component. + - The domain represents where the component belongs or where it will be used. + - For example, components used within article pages or related to the structure of an article should be placed within `@node-core/ui-components/Common/Article`. +- **Each component should have its own folder** with the name of the component. +- The structure of each component folder follows this template: + ```text - ComponentName - - index.tsx // the component itself - - index.module.css // all styles of the component are placed there - - index.stories.tsx // component Storybook stories - - __tests__ // component tests (such as unit tests, etc) - - index.test.mjs // unit tests should be done in ESM and not TypeScript + - index.tsx // The component itself + - index.module.css // Component-specific styles + - index.stories.tsx // Storybook stories (only for @node-core/ui-components) + - __tests__/ // Component tests (such as unit tests, etc.) + - index.test.mjs // Unit tests should be done in ESM, not TypeScript ``` -- React Hooks belonging to a single Component should be placed within the Component's folder - - If the Hook as a wider usability or can be used by other components, it should be placed in the root `hooks` folder. -- If the Component has "sub-components" they should follow the same philosophy as the Component itself. - - For example, if the Component `ComponentName` has a sub-component called `SubComponentName`, - then it should be placed within `ComponentName/SubComponentName` + +- **If a component requires Next.js features, it should be wrapped within `apps/site`** rather than being modified directly in `@node-core/ui-components`. + + - Example: A component that requires `usePathname()` should **not** call it directly inside `@node-core/ui-components`. Instead: + - The **base component** should accept `pathname` as a prop. + - The **wrapper component** in `apps/site` should call `usePathname()` and pass it to the base component. + + Example structure: + + - **Base Component (`@node-core/ui-components`)** + + ```tsx + const BaseComponent: FC<...> = ({ pathname, ariaLabel }) => { + return <... ariaLabel={ariaLabel}></...>; + }; + ``` + + - **Wrapper Component (`apps/site/components`)** + + ```tsx + const Component: FC<...> = (...) => { + const pathname = usePathname(); + const t = useTranslations(); + + return <BaseComponent pathname={pathname} ariaLabel={t('my.key')} />; + }; + ``` + + - **Importing Components:** + - **For website-specific functionality**, import the wrapper from `apps/site/components`. + - **For direct UI use cases**, import from `@node-core/ui-components`. + +- **Storybook is now a dependency of `@node-core/ui-components`** and should not be included in `apps/site`. + + - Storybook stories should be written only for components in `@node-core/ui-components`. + +- **React Hooks that belong to a single component should be placed within that component’s folder.** + + - If the hook has a **wider usability** or can be used by multiple components, it should be placed in the `apps/site/hooks` folder. + - These hooks should only exist in `apps/site`. + +- **If a component has sub-components, they should follow the same structure as the main component.** + - Example: If `ComponentName` has a sub-component called `SubComponentName`, it should be placed within: + ```text + - ComponentName/ + - index.tsx + - SubComponentName/ + - index.tsx + ``` #### How a new Component should look like when freshly created @@ -272,7 +320,7 @@ To add a new download installation method, follow these steps: - Add a new entry to the `INSTALL_METHODS` array. - Each entry should have the following properties: - - `iconImage`: The React component of the icon image for the installation method. This should be an SVG component stored within `apps/site/components/Icons/InstallationMethod` and must follow the other icon component references (being a `FC` supporting `SVGSVGElement` props). + - `iconImage`: The React component of the icon image for the installation method. This should be an SVG component stored within `@node-core/ui-components/Icons/InstallationMethod` and must follow the other icon component references (being a `FC` supporting `SVGSVGElement` props). - Don't forget to add it on the `index.tsx` file from the `InstallationMethod` folder. - `recommended`: A boolean indicating if this method is recommended. This property is available only for official installation methods. - `url`: The URL for the installation method. @@ -379,7 +427,7 @@ Each new feature or bug fix should be accompanied by a unit test (when deemed va We use [Jest][] as our test runner and [React Testing Library][] for our React unit tests. We also use [Storybook][] to document our components. -Each component should have a storybook story that documents the component's usage. +Components within `packages/ui-components` should have a storybook story that documents the component's usage. Visual Regression Testing is automatically done via [Chromatic](https://www.chromatic.com/) to ensure that Components are rendered correctly. @@ -407,7 +455,7 @@ They also allow Developers to preview Components and be able to test them manual ```tsx import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import NameOfComponent from '@components/PathTo/YourComponent'; +import NameOfComponent from '@node-core/ui-components/PathTo/YourComponent'; type Story = StoryObj<typeof NameOfComponent>; type Meta = MetaObj<typeof NameOfComponent>; @@ -548,7 +596,7 @@ The Node.js Website uses Tailwind as a CSS Framework for crafting our React Comp #### Font Families on the Website We use `next/fonts` Open Sans as the default font for the Node.js Website. -The font is configured as a CSS variable and then configured on `tailwind.config.js` as the default font for the Website. +The font is configured as a CSS variable and then configured on `packages/ui-components/tailwind.config.ts` as the default font for the Website. #### Why we use RadixUI? diff --git a/apps/site/.storybook/main.ts b/apps/site/.storybook/main.ts deleted file mode 100644 index f6b01fc299b9a..0000000000000 --- a/apps/site/.storybook/main.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { join } from 'node:path'; - -import type { StorybookConfig } from '@storybook/react-webpack5'; - -const mocksFolder = join(__dirname, '../components/__mocks__'); - -const config: StorybookConfig = { - stories: ['../components/**/*.stories.tsx'], - logLevel: 'error', - staticDirs: ['../public'], - typescript: { reactDocgen: false, check: false }, - core: { disableTelemetry: true, disableWhatsNewNotifications: true }, - framework: '@storybook/react-webpack5', - swc: () => ({ - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - transform: { - react: { - runtime: 'automatic', - }, - }, - }, - }), - addons: [ - '@storybook/addon-webpack5-compiler-swc', - '@storybook/addon-controls', - '@storybook/addon-interactions', - '@storybook/addon-themes', - '@storybook/addon-viewport', - { - name: '@storybook/addon-styling-webpack', - options: { - rules: [ - { - test: /\.css$/, - use: [ - 'style-loader', - { loader: 'css-loader', options: { url: false } }, - 'postcss-loader', - ], - }, - ], - }, - }, - ], - webpack: async config => ({ - ...config, - // We want to conform as much as possible with our target settings - target: 'browserslist:development', - // Performance Hints do not make sense on Storybook as it is bloated by design - performance: { hints: false }, - // `nodevu` is a Node.js-specific package that requires Node.js modules - // this is incompatible with Storybook. So we just mock the module - resolve: { - ...config.resolve, - alias: { - 'next/image': join(mocksFolder, './next-image.mjs'), - 'next-intl/navigation': join(mocksFolder, './next-intl.mjs'), - '@/client-context': join(mocksFolder, './client-context.mjs'), - '@': join(__dirname, '../'), - }, - }, - // Removes Pesky Critical Dependency Warnings due to `next/font` - ignoreWarnings: [ - e => - e.message.includes('was not found in') || - e.message.includes('generated code contains'), - ], - }), -}; - -export default config; diff --git a/apps/site/.stylelintignore b/apps/site/.stylelintignore index cb71469e033ce..a8600c22f4b77 100644 --- a/apps/site/.stylelintignore +++ b/apps/site/.stylelintignore @@ -10,8 +10,5 @@ public # Jest coverage -# Storybook -storybook-static - # Old Styles styles/old diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index b1887eea5c80e..451521fa975ba 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -10,7 +10,7 @@ import { availableLocalesMap, defaultLocale } from '@/next.locales.mjs'; import { LocaleProvider } from '@/providers/localeProvider'; import { ThemeProvider } from '@/providers/themeProvider'; -import '@/styles/index.css'; +import '@node-core/ui-components/styles/index.css'; const fontClasses = classNames(IBM_PLEX_MONO.variable, OPEN_SANS.variable); diff --git a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx index 844c9fa6753f5..effcd1719afc8 100644 --- a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx +++ b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx @@ -1,7 +1,7 @@ +import HexagonGrid from '@node-core/ui-components/Icons/HexagonGrid'; +import JsWhiteIcon from '@node-core/ui-components/Icons/Logos/JsWhite'; import { ImageResponse } from 'next/og'; -import HexagonGrid from '@/components/Icons/HexagonGrid'; -import JsIconWhite from '@/components/Icons/Logos/JsIconWhite'; import { DEFAULT_CATEGORY_OG_TYPE } from '@/next.constants.mjs'; import { defaultLocale } from '@/next.locales.mjs'; import tailwindConfig from '@/tailwind.config'; @@ -37,7 +37,7 @@ export const GET = async (_: Request, props: StaticParams) => { <HexagonGrid style={{ background: gridBackground }} /> <div tw="absolute mx-auto flex max-w-xl flex-col text-center text-3xl font-semibold text-white"> - <JsIconWhite width={71} height={80} tw="mx-auto" /> + <JsWhiteIcon width={71} height={80} tw="mx-auto" /> <h2>{params.title.slice(0, 100)}</h2> </div> diff --git a/apps/site/components/Blog/BlogHeader/index.stories.tsx b/apps/site/components/Blog/BlogHeader/index.stories.tsx deleted file mode 100644 index 0e29b475119bc..0000000000000 --- a/apps/site/components/Blog/BlogHeader/index.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import BlogHeader from '@/components/Blog/BlogHeader'; - -type Story = StoryObj<typeof BlogHeader>; -type Meta = MetaObj<typeof BlogHeader>; - -export const Default: Story = { - args: { - // See `@/site.json` for the `rssFeeds` object - category: 'all', - }, - decorators: [ - // We need to wrap to allow global styles to be applied (markdown styles) - Story => ( - <main> - <Story /> - </main> - ), - ], -}; - -export default { component: BlogHeader } as Meta; diff --git a/apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs b/apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs similarity index 97% rename from apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs rename to apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs index e4752d4b09dc9..3094ba35e9c03 100644 --- a/apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs +++ b/apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; -import BlogPostCard from '@/components/Common/BlogPostCard'; +import BlogPostCard from '@/components/Blog/BlogPostCard'; function renderBlogPostCard({ title = 'Blog post title', diff --git a/apps/site/components/Common/BlogPostCard/index.module.css b/apps/site/components/Blog/BlogPostCard/index.module.css similarity index 100% rename from apps/site/components/Common/BlogPostCard/index.module.css rename to apps/site/components/Blog/BlogPostCard/index.module.css diff --git a/apps/site/components/Common/BlogPostCard/index.tsx b/apps/site/components/Blog/BlogPostCard/index.tsx similarity index 96% rename from apps/site/components/Common/BlogPostCard/index.tsx rename to apps/site/components/Blog/BlogPostCard/index.tsx index 30815ad9ed62c..b99c1ac13a465 100644 --- a/apps/site/components/Common/BlogPostCard/index.tsx +++ b/apps/site/components/Blog/BlogPostCard/index.tsx @@ -1,8 +1,8 @@ +import Preview from '@node-core/ui-components/Common/Preview'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import FormattedTime from '@/components/Common/FormattedTime'; -import Preview from '@/components/Common/Preview'; import Link from '@/components/Link'; import WithAvatarGroup from '@/components/withAvatarGroup'; import type { BlogCategory } from '@/types'; diff --git a/apps/site/components/Common/ActiveLink.tsx b/apps/site/components/Common/ActiveLink.tsx new file mode 100644 index 0000000000000..63f6b2f2e16eb --- /dev/null +++ b/apps/site/components/Common/ActiveLink.tsx @@ -0,0 +1,14 @@ +'use client'; + +import type { ActiveLocalizedLinkProps } from '@node-core/ui-components/Common/BaseActiveLink'; +import BaseActiveLink from '@node-core/ui-components/Common/BaseActiveLink'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; +import { usePathname } from '@/navigation.mjs'; + +const ActiveLink: FC< + Omit<ActiveLocalizedLinkProps, 'pathname' | 'as'> +> = props => <BaseActiveLink pathname={usePathname()} as={Link} {...props} />; + +export default ActiveLink; diff --git a/apps/site/components/Common/AvatarGroup/Avatar/index.module.css b/apps/site/components/Common/AvatarGroup/Avatar/index.module.css deleted file mode 100644 index b96dded805343..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Avatar/index.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.item { - @apply xs:max-h-10 - xs:max-w-10 - flex - h-full - max-h-12 - w-full - max-w-12 - items-center - justify-center - rounded-full - border-2 - border-transparent - bg-neutral-100 - object-cover - text-xs - text-neutral-800 - dark:bg-neutral-900 - dark:text-neutral-300; -} - -.avatar { - @apply size-8; - - .wrapper { - @apply max-xs:block - max-xs:py-0; - } -} - -.small { - @apply xs:size-8 - size-10; -} - -.medium { - @apply size-10; -} diff --git a/apps/site/components/Common/AvatarGroup/Avatar/index.tsx b/apps/site/components/Common/AvatarGroup/Avatar/index.tsx deleted file mode 100644 index 3e7f98724cb80..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Avatar/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import classNames from 'classnames'; -import Image from 'next/image'; -import type { HTMLAttributes } from 'react'; -import { forwardRef } from 'react'; - -import Link from '@/components/Link'; - -import styles from './index.module.css'; - -export type AvatarProps = { - image?: string; - name?: string; - nickname: string; - fallback?: string; - size?: 'small' | 'medium'; - url?: string; -}; - -// @TODO: We temporarily removed the Avatar Radix UI primitive, since it was causing flashing -// during initial load and not being able to render nicely when images are already cached. -// @see https://github.com/radix-ui/primitives/pull/3008 -const Avatar = forwardRef< - HTMLSpanElement, - HTMLAttributes<HTMLSpanElement> & AvatarProps ->(({ image, nickname, name, fallback, url, size = 'small', ...props }, ref) => { - const Wrapper = url ? Link : 'div'; - - return ( - <span - {...props} - ref={ref} - className={classNames(styles.avatar, styles[size], props.className)} - > - <Wrapper - href={url || undefined} - target={url ? '_blank' : undefined} - className={styles.wrapper} - > - {image && ( - <Image - width={40} - height={40} - loading="lazy" - decoding="async" - src={image} - alt={name || nickname} - className={styles.item} - /> - )} - - {!image && ( - <span className={classNames(styles.item, styles[size])}> - {fallback} - </span> - )} - </Wrapper> - </span> - ); -}); - -export default Avatar; diff --git a/apps/site/components/Common/AvatarGroup/Overlay/index.module.css b/apps/site/components/Common/AvatarGroup/Overlay/index.module.css deleted file mode 100644 index 7c8188658b62c..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Overlay/index.module.css +++ /dev/null @@ -1,29 +0,0 @@ -.overlay { - @apply flex - min-w-56 - items-center - gap-2 - p-3; -} - -.user { - @apply grow; -} - -.name { - @apply font-semibold - text-neutral-900 - dark:text-neutral-300; -} - -.nickname { - @apply font-medium - text-neutral-700 - dark:text-neutral-500; -} - -.arrow { - @apply w-3 - fill-neutral-600 - dark:fill-white; -} diff --git a/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx b/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx deleted file mode 100644 index 1c462999ccd77..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import AvatarOverlay from '@/components/Common/AvatarGroup/Overlay'; -import { getAuthorWithId, getAuthorWithName } from '@/util/authorUtils'; - -type Story = StoryObj<typeof AvatarOverlay>; -type Meta = MetaObj<typeof AvatarOverlay>; - -export const Default: Story = { - args: getAuthorWithId(['nodejs'], true)[0], -}; - -export const FallBack: Story = { - args: getAuthorWithName(['Node.js'], true)[0], -}; - -export const WithoutName: Story = { - args: getAuthorWithId(['canerakdas'], true)[0], -}; - -export default { component: AvatarOverlay } as Meta; diff --git a/apps/site/components/Common/AvatarGroup/index.tsx b/apps/site/components/Common/AvatarGroup/index.tsx deleted file mode 100644 index 2bacc71b8ab7b..0000000000000 --- a/apps/site/components/Common/AvatarGroup/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client'; - -import classNames from 'classnames'; -import type { FC } from 'react'; -import { useState, useMemo, Fragment } from 'react'; - -import type { AvatarProps } from '@/components/Common/AvatarGroup/Avatar'; -import Avatar from '@/components/Common/AvatarGroup/Avatar'; -import avatarstyles from '@/components/Common/AvatarGroup/Avatar/index.module.css'; -import AvatarOverlay from '@/components/Common/AvatarGroup/Overlay'; -import Tooltip from '@/components/Common/Tooltip'; - -import styles from './index.module.css'; - -type AvatarGroupProps = { - avatars: Array<AvatarProps>; - limit?: number; - isExpandable?: boolean; - size?: AvatarProps['size']; - container?: HTMLElement; -}; - -const AvatarGroup: FC<AvatarGroupProps> = ({ - avatars, - limit = 10, - isExpandable = true, - size = 'small', - container, -}) => { - const [showMore, setShowMore] = useState(false); - - const renderAvatars = useMemo( - () => avatars.slice(0, showMore ? avatars.length : limit), - [showMore, avatars, limit] - ); - - return ( - <div - className={classNames(styles.avatarGroup, styles[size], { - [styles.expandable]: avatars.length > limit, - })} - > - {renderAvatars.map(({ ...avatar }) => ( - <Fragment key={avatar.nickname}> - <Tooltip - asChild - container={container} - content={<AvatarOverlay {...avatar} />} - > - <Avatar - {...avatar} - size={size} - className={classNames({ - 'cursor-pointer': avatar.url, - 'pointer-events-none': !avatar.url, - })} - /> - </Tooltip> - </Fragment> - ))} - - {avatars.length > limit && ( - <span - onClick={isExpandable ? () => setShowMore(prev => !prev) : undefined} - className={classNames( - avatarstyles.avatar, - avatarstyles[size], - 'cursor-pointer' - )} - > - <span className={avatarstyles.item}> - {`${showMore ? '-' : '+'}${avatars.length - limit}`} - </span> - </span> - )} - </div> - ); -}; - -export default AvatarGroup; diff --git a/apps/site/components/Common/Badge/index.module.css b/apps/site/components/Common/Badge/index.module.css deleted file mode 100644 index 40c28b3285188..0000000000000 --- a/apps/site/components/Common/Badge/index.module.css +++ /dev/null @@ -1,99 +0,0 @@ -.wrapper { - @apply flex - w-fit - items-center - rounded-full - border - py-1 - pl-1 - pr-2.5 - text-sm - font-medium; - - .icon { - @apply size-4; - } - - .badge { - @apply mr-2 - rounded-full - border - px-2.5 - py-0.5 - text-center - text-white; - } - - .message { - @apply mr-1; - } - - &.default { - @apply border-green-200 - bg-green-100 - dark:border-green-700 - dark:bg-neutral-900; - - .icon { - @apply text-green-500 - dark:text-green-300; - } - - .badge { - @apply border-green-200 - bg-green-600 - dark:border-green-600; - } - - .message { - @apply text-green-700 - dark:text-green-300; - } - } - - &.error { - @apply border-danger-200 - bg-danger-100 - dark:border-danger-700 - dark:bg-neutral-900; - - .icon { - @apply text-danger-500 - dark:text-danger-300; - } - - .badge { - @apply border-danger-200 - bg-danger-600 - dark:border-danger-600; - } - - .message { - @apply text-danger-700 - dark:text-danger-300; - } - } - - &.warning { - @apply border-warning-200 - bg-warning-100 - dark:border-warning-700 - dark:bg-neutral-900; - - .icon { - @apply text-warning-500 - dark:text-warning-300; - } - - .badge { - @apply border-warning-200 - bg-warning-600 - dark:border-warning-600; - } - - .message { - @apply text-warning-700 - dark:text-warning-300; - } - } -} diff --git a/apps/site/components/Common/Banner/index.module.css b/apps/site/components/Common/Banner/index.module.css deleted file mode 100644 index f3c2b9c1ec77c..0000000000000 --- a/apps/site/components/Common/Banner/index.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.banner { - @apply flex - w-full - flex-row - items-center - justify-center - gap-2 - px-8 - py-3 - text-sm; - - &, - a { - @apply text-white - dark:text-white; - } - - a { - @apply w-fit - underline - decoration-white/50; - } - - svg { - @apply size-4 - text-white/50; - } -} - -.default { - @apply bg-green-600; -} - -.error { - @apply bg-danger-600; -} - -.warning { - @apply bg-warning-600; -} diff --git a/apps/site/components/Common/Blockquote/index.module.css b/apps/site/components/Common/Blockquote/index.module.css deleted file mode 100644 index 410802f9cb90f..0000000000000 --- a/apps/site/components/Common/Blockquote/index.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.wrapper { - @apply flex - max-w-2xl - flex-col - items-start - gap-4 - self-stretch - border-l-2 - border-green-600 - py-2 - pl-5 - text-lg - font-semibold - text-neutral-900 - dark:border-green-400 - dark:text-white; - - & cite { - @apply font-regular - text-base - not-italic; - - &::before { - @apply content-['—_']; - } - } -} diff --git a/apps/site/components/Common/BlogPostCard/index.stories.tsx b/apps/site/components/Common/BlogPostCard/index.stories.tsx deleted file mode 100644 index dca5f00ccaa1c..0000000000000 --- a/apps/site/components/Common/BlogPostCard/index.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import BlogPostCard from '@/components/Common/BlogPostCard'; - -type Story = StoryObj<typeof BlogPostCard>; -type Meta = MetaObj<typeof BlogPostCard>; - -export const Default: Story = { - args: { - title: 'Node.js March 17th Infrastructure Incident Post-mortem', - category: 'vulnerability', - description: - 'Starting on March 15th and going through to March 17th (with much of the issue being mitigated on the 16th), users were receiving intermittent 404 responses when trying to download Node.js from nodejs.org, or even accessing parts of the website.', - authors: ['Claudio Wunder'], - slug: '/blog/vulnerability/something', - date: new Date('17 October 2023'), - }, - decorators: [ - Story => ( - <div className="max-w-lg"> - <Story /> - </div> - ), - ], -}; - -export const MoreThanOneAuthor: Story = { - ...Default, - args: { - ...Default.args, - authors: [...(Default.args?.authors ?? []), 'Brian Muenzenmeyer'], - }, -}; - -export default { component: BlogPostCard } as Meta; diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css deleted file mode 100644 index 713fad32dac39..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css +++ /dev/null @@ -1,39 +0,0 @@ -.item { - @apply flex - max-w-fit - items-center - gap-5 - truncate - text-sm - font-medium; - - &:not(:last-child) { - @apply shrink-0; - } - - a { - @apply shrink - grow; - } - - &, - > a, - > a:hover { - @apply text-neutral-800 - motion-safe:transition-colors - dark:text-neutral-200; - } - - &.visuallyHidden { - @apply hidden; - } - - .separator { - @apply size-4 - max-w-fit - shrink-0 - grow - text-neutral-600 - dark:text-neutral-400; - } -} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css deleted file mode 100644 index ea0d6b9db96b5..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.link { - @apply max-w-fit - truncate; - - &.active { - @apply rounded - bg-green-600 - px-2 - py-1 - font-semibold - text-white - motion-safe:transition-colors - dark:text-white; - - &:hover { - @apply bg-green-700 - text-white; - } - } -} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css deleted file mode 100644 index 51043398d4657..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.list { - @apply xs:w-full - flex - w-screen - gap-5 - px-6; -} diff --git a/apps/site/components/Common/Button.tsx b/apps/site/components/Common/Button.tsx new file mode 100644 index 0000000000000..87d98febce2d1 --- /dev/null +++ b/apps/site/components/Common/Button.tsx @@ -0,0 +1,12 @@ +import BaseButton, { + type ButtonProps, +} from '@node-core/ui-components/Common/BaseButton'; +import type { FC, ComponentProps } from 'react'; + +import Link from '@/components/Link'; + +const Button: FC< + Omit<ButtonProps, 'as'> & Omit<ComponentProps<typeof Link>, 'as' | 'size'> +> = props => <BaseButton as={Link} {...props} />; + +export default Button; diff --git a/apps/site/components/Common/Button/index.module.css b/apps/site/components/Common/Button/index.module.css deleted file mode 100644 index 6f2d69b8e9bef..0000000000000 --- a/apps/site/components/Common/Button/index.module.css +++ /dev/null @@ -1,146 +0,0 @@ -.button { - @apply px-4.5 - relative - inline-flex - items-center - justify-center - gap-2 - py-2.5 - text-center - font-semibold - motion-safe:transition-colors; - - svg { - @apply size-5; - } - - &[aria-disabled='true'] { - @apply cursor-not-allowed; - } - - &.small { - @apply px-3 - py-2 - text-sm; - } - - &.neutral { - @apply rounded - bg-neutral-900 - text-white - dark:text-neutral-200; - - &:hover:not([aria-disabled='true']) { - @apply bg-neutral-800; - } - - &[aria-disabled='true'] { - @apply bg-neutral-900 - opacity-50; - } - - &:focus { - @apply bg-neutral-800; - } - } - - &.primary { - @apply rounded - border - border-green-600 - bg-green-600 - text-white - shadow-sm; - - &:hover:not([aria-disabled='true']) { - @apply border-green-700 - bg-green-700 - text-white; - } - - &:focus { - @apply border-green-700 - bg-green-700; - } - - &[aria-disabled='true'] { - @apply bg-green-600 - opacity-50; - } - } - - &.secondary { - @apply rounded-lg - text-neutral-800 - dark:text-neutral-200; - - &:hover:not([aria-disabled='true']) { - @apply bg-neutral-100 - text-neutral-800 - dark:bg-neutral-900 - dark:text-neutral-200; - } - - &[aria-disabled='true'] { - @apply bg-transparent - opacity-50; - } - - &:focus { - @apply bg-neutral-100 - text-neutral-800 - dark:bg-neutral-900 - dark:text-neutral-200; - } - } - - &.special { - @apply rounded-lg - border - border-green-600/30 - bg-green-600/10 - text-white - shadow-sm; - - &::before { - @apply bg-gradient-glow-backdrop - absolute - left-0 - right-0 - top-0 - -z-10 - mx-auto - h-full - w-full - opacity-30 - content-['']; - } - - &::after { - @apply absolute - -top-px - left-0 - right-0 - mx-auto - h-px - w-2/5 - bg-gradient-to-r - from-green-600/0 - via-green-600 - to-green-600/0 - content-['']; - } - - &[aria-disabled='true'] { - @apply opacity-50; - } - - &:hover:not([aria-disabled='true']) { - @apply bg-green-600/20; - } - - &:focus { - @apply bg-green-600/20; - } - } -} diff --git a/apps/site/components/Common/CodeBox.tsx b/apps/site/components/Common/CodeBox.tsx new file mode 100644 index 0000000000000..b95304a11a08e --- /dev/null +++ b/apps/site/components/Common/CodeBox.tsx @@ -0,0 +1,36 @@ +'use client'; + +import BaseCodeBox from '@node-core/ui-components/Common/BaseCodeBox'; +import { useTranslations } from 'next-intl'; +import type { FC, PropsWithChildren, ReactNode } from 'react'; + +import Link from '@/components/Link'; +import { useCopyToClipboard, useNotification } from '@/hooks'; + +type CodeBoxProps = { + language: string; + className?: string; + showCopyButton?: boolean; +}; + +const CodeBox: FC<PropsWithChildren<CodeBoxProps>> = props => { + const [, copyToClipboard] = useCopyToClipboard(); + const notify = useNotification(); + const t = useTranslations(); + + const onCopy = (text: string, message: ReactNode) => { + copyToClipboard(text); + notify({ duration: 300, message }); + }; + return ( + <BaseCodeBox + as={Link} + onCopy={onCopy} + {...props} + copyText={t('components.common.codebox.copy')} + copiedText={t('components.common.codebox.copied')} + /> + ); +}; + +export default CodeBox; diff --git a/apps/site/components/Common/CodeBox/index.module.css b/apps/site/components/Common/CodeBox/index.module.css deleted file mode 100644 index 424d721f17e29..0000000000000 --- a/apps/site/components/Common/CodeBox/index.module.css +++ /dev/null @@ -1,82 +0,0 @@ -.root { - @apply w-full - rounded - border - border-neutral-900 - bg-neutral-950; - - .content { - @apply m-0 - p-4; - - & > code { - @apply font-ibm-plex-mono - font-regular - scrollbar-thin - grid - overflow-x-auto - bg-transparent - p-0 - text-sm - leading-snug - text-neutral-400 - [counter-reset:line]; - - & > [class='line'] { - @apply relative - min-w-0 - pl-8; - - &:not(:empty:last-child)::before { - @apply inline-block - content-['']; - } - - &:not(:empty:last-child)::after { - @apply w-4.5 - font-ibm-plex-mono - absolute - left-0 - top-0 - mr-4 - text-right - text-neutral-600 - [content:counter(line)] - [counter-increment:line]; - } - } - } - } - - & > .footer { - @apply flex - items-center - justify-between - border-t - border-t-neutral-900 - px-4 - py-3 - text-sm - font-medium; - - & > .language { - @apply text-neutral-400; - } - - & > .action { - @apply px-3 - py-1.5 - font-medium; - } - } -} - -.notification { - @apply flex - items-center - gap-3; -} - -.icon { - @apply size-4; -} diff --git a/apps/site/components/Common/CodeTabs/index.module.css b/apps/site/components/Common/CodeTabs/index.module.css deleted file mode 100644 index 5c79f7ecd4fd4..0000000000000 --- a/apps/site/components/Common/CodeTabs/index.module.css +++ /dev/null @@ -1,54 +0,0 @@ -.root { - > [role='tabpanel'] > :first-child { - @apply rounded-t-none; - } - - > div:nth-of-type(1) { - @apply flex - rounded-t - border-x - border-t - border-neutral-900 - bg-neutral-950 - px-2 - pt-3 - md:px-4; - - .trigger { - @apply border-b - border-b-transparent - px-1 - text-neutral-200; - - &[data-state='active'] { - @apply border-b-green-400 - text-green-400; - } - } - - .link { - @apply hidden - items-center - gap-2 - text-center - text-neutral-200 - motion-safe:transition-colors - lg:flex; - - & > .icon { - @apply size-4 - text-neutral-300; - } - - &:is(:link, :visited) { - &:hover { - @apply text-neutral-400; - - & > .icon { - @apply text-neutral-600; - } - } - } - } - } -} diff --git a/apps/site/components/Common/CodeTabs/index.tsx b/apps/site/components/Common/CodeTabs/index.tsx deleted file mode 100644 index 614db0017079b..0000000000000 --- a/apps/site/components/Common/CodeTabs/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; -import type { ComponentProps, FC, PropsWithChildren } from 'react'; - -import Tabs from '@/components/Common/Tabs'; -import { Link } from '@/navigation.mjs'; - -import styles from './index.module.css'; - -type CodeTabsProps = Pick< - ComponentProps<typeof Tabs>, - 'tabs' | 'defaultValue' -> & { - linkUrl?: string; - linkText?: string; -}; - -const CodeTabs: FC<PropsWithChildren<CodeTabsProps>> = ({ - children, - linkUrl, - linkText, - ...props -}) => ( - <Tabs - {...props} - className={styles.root} - triggerClassName={styles.trigger} - addons={ - linkUrl && - linkText && ( - <Link className={styles.link} href={linkUrl}> - {linkText} - <ArrowUpRightIcon className={styles.icon} /> - </Link> - ) - } - > - {children} - </Tabs> -); - -export default CodeTabs; diff --git a/apps/site/components/Common/CrossLink.tsx b/apps/site/components/Common/CrossLink.tsx new file mode 100644 index 0000000000000..85ef3fc0c8809 --- /dev/null +++ b/apps/site/components/Common/CrossLink.tsx @@ -0,0 +1,19 @@ +import BaseCrossLink from '@node-core/ui-components/Common/BaseCrossLink'; +import type { CrossLinkProps } from '@node-core/ui-components/Common/BaseCrossLink'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; + +const CrossLink: FC<Omit<CrossLinkProps, 'as' | 'label'>> = props => { + const t = useTranslations(); + return ( + <BaseCrossLink + label={t(`components.common.crossLink.${props.type}`)} + as={Link} + {...props} + /> + ); +}; + +export default CrossLink; diff --git a/apps/site/components/Common/CrossLink/index.module.css b/apps/site/components/Common/CrossLink/index.module.css deleted file mode 100644 index 0e2d8d959e1dd..0000000000000 --- a/apps/site/components/Common/CrossLink/index.module.css +++ /dev/null @@ -1,49 +0,0 @@ -.crossLink { - @apply flex - flex-col - items-start - gap-2 - truncate - rounded - border - border-solid - border-neutral-300 - bg-white - p-3 - no-underline - dark:border-neutral-900 - dark:bg-neutral-950; - - .header { - @apply flex - items-center - gap-2 - self-stretch - text-xs - text-neutral-800 - dark:text-neutral-100; - - &.reverse { - @apply flex-row-reverse - justify-start; - } - - .icon { - @apply size-4 - text-neutral-600 - dark:text-neutral-400; - } - } - - .content { - @apply self-stretch - truncate - text-sm - text-neutral-900 - dark:text-white; - - &.reverse { - @apply text-right; - } - } -} diff --git a/apps/site/components/Common/GlowingBackdrop/index.module.css b/apps/site/components/Common/GlowingBackdrop/index.module.css deleted file mode 100644 index a7f2d817c3091..0000000000000 --- a/apps/site/components/Common/GlowingBackdrop/index.module.css +++ /dev/null @@ -1,33 +0,0 @@ -.glowingBackdrop { - @apply absolute - left-0 - top-0 - -z-10 - size-full - opacity-50 - md:opacity-100; - - &::after { - @apply absolute - inset-0 - m-auto - aspect-square - w-[300px] - rounded-full - bg-green-300 - blur-[120px] - content-[''] - dark:bg-green-700; - } - - svg { - @apply absolute - inset-0 - m-auto - aspect-[1.67] - max-w-[1004px] - object-cover - text-neutral-300 - dark:text-neutral-900/75; - } -} diff --git a/apps/site/components/Common/LanguageDropDown/index.module.css b/apps/site/components/Common/LanguageDropDown/index.module.css deleted file mode 100644 index 7f1a0d1052106..0000000000000 --- a/apps/site/components/Common/LanguageDropDown/index.module.css +++ /dev/null @@ -1,49 +0,0 @@ -.languageDropdown { - @apply h-9 - w-9 - rounded-md - p-2 - text-neutral-700 - motion-safe:transition-colors - dark:text-neutral-300; - - &:hover { - @apply bg-neutral-100 - dark:bg-neutral-900; - } -} - -.dropDownContent { - @apply max-h-80 - w-48 - overflow-hidden - rounded - border - border-neutral-200 - bg-white - shadow-lg - dark:border-neutral-900 - dark:bg-neutral-950; - - > div { - @apply max-h-80 - w-48 overflow-y-auto; - } -} - -.dropDownItem { - @apply cursor-pointer - px-2.5 - py-1.5 - text-sm - font-medium - text-neutral-800 - outline-none - data-[highlighted]:bg-green-600 - data-[highlighted]:text-white - dark:text-white; -} - -.currentDropDown { - @apply bg-green-600 text-white; -} diff --git a/apps/site/components/Common/LinkTabs.tsx b/apps/site/components/Common/LinkTabs.tsx new file mode 100644 index 0000000000000..7f45ef41b8994 --- /dev/null +++ b/apps/site/components/Common/LinkTabs.tsx @@ -0,0 +1,15 @@ +'use client'; + +import BaseLinkTabs from '@node-core/ui-components/Common/BaseLinkTabs'; +import type { LinkTabsProps } from '@node-core/ui-components/Common/BaseLinkTabs'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; +import { useRouter } from '@/navigation.mjs'; + +const LinkTabs: FC<Omit<LinkTabsProps, 'as' | 'onSelect'>> = props => { + const { push } = useRouter(); + return <BaseLinkTabs onSelect={value => push(value)} as={Link} {...props} />; +}; + +export default LinkTabs; diff --git a/apps/site/components/Common/LinkTabs/index.module.css b/apps/site/components/Common/LinkTabs/index.module.css deleted file mode 100644 index c8f766392b409..0000000000000 --- a/apps/site/components/Common/LinkTabs/index.module.css +++ /dev/null @@ -1,41 +0,0 @@ -.tabsList { - @apply font-open-sans - max-xs:hidden - mb-6 - mt-10 - flex - gap-2 - border-b - border-b-neutral-200 - dark:border-b-neutral-800; - - .tabsTrigger { - @apply border-b-2 - border-b-transparent - px-1 - pb-[11px] - text-sm - font-semibold - text-neutral-800 - dark:text-neutral-200; - - &[data-state='active'] { - @apply border-b-green-600 - text-green-600 - dark:border-b-green-400 - dark:text-green-400; - } - } -} - -.tabsSelect { - @apply sm:visible - md:hidden; - - > span { - @apply max-xs:flex - my-6 - hidden - w-full; - } -} diff --git a/apps/site/components/Common/NodejsLogo/index.module.css b/apps/site/components/Common/NodejsLogo/index.module.css deleted file mode 100644 index 2e74ac633f684..0000000000000 --- a/apps/site/components/Common/NodejsLogo/index.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.nodejsLogo { - @apply h-6 - w-20; -} diff --git a/apps/site/components/Common/NodejsLogo/index.tsx b/apps/site/components/Common/NodejsLogo/index.tsx deleted file mode 100644 index 7ca383e10c505..0000000000000 --- a/apps/site/components/Common/NodejsLogo/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { FC } from 'react'; - -import Nodejs from '@/components/Icons/Logos/Nodejs'; -import type { LogoVariant } from '@/types'; - -import style from './index.module.css'; - -type NodejsLogoProps = { - variant?: LogoVariant; -}; - -const NodejsLogo: FC<NodejsLogoProps> = ({ variant = 'default' }) => ( - <Nodejs variant={variant} className={style.nodejsLogo} /> -); - -export default NodejsLogo; diff --git a/apps/site/components/Common/Notification/index.module.css b/apps/site/components/Common/Notification/index.module.css deleted file mode 100644 index 981e1b9d03cb8..0000000000000 --- a/apps/site/components/Common/Notification/index.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.root { - @apply m-6 - rounded - border - border-neutral-200 - bg-white - px-4 - py-3 - shadow-lg - dark:border-neutral-800 - dark:bg-neutral-900; -} - -.message { - @apply font-medium - text-green-600 - dark:text-white; -} diff --git a/apps/site/components/Common/Pagination.tsx b/apps/site/components/Common/Pagination.tsx new file mode 100644 index 0000000000000..05a759cba1d31 --- /dev/null +++ b/apps/site/components/Common/Pagination.tsx @@ -0,0 +1,30 @@ +import BasePagination from '@node-core/ui-components/Common/BasePagination'; +import type { PaginationProps } from '@node-core/ui-components/Common/BasePagination'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; + +const Pagination: FC< + Omit<PaginationProps, 'as' | 'labels' | 'getPageLabel'> +> = props => { + const t = useTranslations(); + return ( + <BasePagination + as={Link} + labels={{ + aria: t('components.common.pagination.defaultLabel'), + prevAria: t('components.common.pagination.prevAriaLabel'), + prev: t('components.pagination.previous'), + nextAria: t('components.common.pagination.nextAriaLabel'), + next: t('components.pagination.next'), + }} + getPageLabel={pageNumber => + t('components.common.pagination.pageLabel', { pageNumber }) + } + {...props} + /> + ); +}; + +export default Pagination; diff --git a/apps/site/components/Common/Pagination/Ellipsis/index.module.css b/apps/site/components/Common/Pagination/Ellipsis/index.module.css deleted file mode 100644 index b4ce7ce86b786..0000000000000 --- a/apps/site/components/Common/Pagination/Ellipsis/index.module.css +++ /dev/null @@ -1,8 +0,0 @@ -.ellipsis { - @apply w-10 - px-3 - py-2.5 - leading-none - text-neutral-800 - dark:text-neutral-200; -} diff --git a/apps/site/components/Common/Pagination/PaginationListItem/index.module.css b/apps/site/components/Common/Pagination/PaginationListItem/index.module.css deleted file mode 100644 index ca629ffce0a31..0000000000000 --- a/apps/site/components/Common/Pagination/PaginationListItem/index.module.css +++ /dev/null @@ -1,23 +0,0 @@ -.listItem, -.listItem:link, -.listItem:active { - @apply aria-current:bg-green-600 - aria-current:text-white - flex - size-10 - items-center - justify-center - rounded - px-3 - py-2.5 - text-neutral-800 - motion-safe:transition-colors - dark:text-neutral-200; - - &:hover { - @apply bg-neutral-100 - text-neutral-800 - dark:bg-neutral-900 - dark:text-neutral-200; - } -} diff --git a/apps/site/components/Common/Pagination/index.module.css b/apps/site/components/Common/Pagination/index.module.css deleted file mode 100644 index 9a7b5936e7e98..0000000000000 --- a/apps/site/components/Common/Pagination/index.module.css +++ /dev/null @@ -1,37 +0,0 @@ -.pagination { - @apply grid - items-center - justify-between - gap-y-5 - [grid-template-areas:'pages_pages_pages''prev_._next'] - md:gap-y-0 - md:[grid-template-areas:'prev_pages_next']; -} - -.previousButton, -.nextButton { - @apply text-sm; -} - -.previousButton { - @apply [grid-area:prev]; -} - -.nextButton { - @apply [grid-area:next]; -} - -.arrowIcon { - @apply h-5 - shrink-0 - text-neutral-600 - dark:text-neutral-400; -} - -.list { - @apply flex - list-none - justify-center - gap-1 - [grid-area:pages]; -} diff --git a/apps/site/components/Common/Preview/index.module.css b/apps/site/components/Common/Preview/index.module.css deleted file mode 100644 index d2a68ff0df3fb..0000000000000 --- a/apps/site/components/Common/Preview/index.module.css +++ /dev/null @@ -1,81 +0,0 @@ -.root { - @apply @container/preview - relative - flex - aspect-[1.90/1] - items-center - rounded - border - border-neutral-900 - bg-neutral-950; - - &::after { - @apply bg-gradient-radial - @md/preview:blur-3xl - absolute - inset-0 - m-auto - aspect-square - w-1/3 - rounded-full - blur-2xl - content-['']; - - &.announcements { - @apply from-green-700/90; - } - - &.release { - @apply from-info-600/90; - } - - &.vulnerability { - @apply from-warning-600/90; - } - } - - .container { - @apply @sm/preview:text-base - @md/preview:gap-6 - @md/preview:text-lg - @lg/preview:gap-8 - @lg/preview:text-xl - @xl/preview:gap-12 - @xl/preview:text-2xl - @2xl/preview:text-3xl - z-10 - mx-auto - flex - w-2/3 - max-w-xl - flex-col - gap-4 - text-center - text-xs - font-semibold - text-white; - - .hexagon { - @apply @md/preview:h-3/5 - @md/preview:w-3/5 - @lg/preview:h-2/3 - @lg/preview:w-2/3 - @xl/preview:h-3/5 - @xl/preview:w-3/5 - @2xl/preview:h-2/3 - @2xl/preview:w-2/3 - absolute - inset-0 - m-auto - size-full; - } - - .logo { - @apply @md/preview:size-14 - @lg/preview:size-16 - @xl/preview:size-20 - mx-auto - size-6; - } - } -} diff --git a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css b/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css deleted file mode 100644 index b9a9678173251..0000000000000 --- a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css +++ /dev/null @@ -1,53 +0,0 @@ -.group { - @apply flex - flex-col - gap-4 - text-sm - font-medium - text-neutral-800 - dark:text-neutral-200; - - .items { - @apply relative - -left-1 - flex - flex-col - gap-2; - - &::after { - @apply absolute - left-[0.45rem] - top-0 - z-10 - h-full - w-px - bg-neutral-200 - content-[''] - dark:bg-neutral-800; - } - - a { - &:first-child::before { - @apply absolute - bottom-[calc(50%+0.25rem)] - left-0 - h-20 - w-4 - bg-white - content-[''] - dark:bg-neutral-950; - } - - &:last-child::after { - @apply absolute - left-0 - top-[calc(50%+0.25rem)] - h-20 - w-4 - bg-white - content-[''] - dark:bg-neutral-950; - } - } - } -} diff --git a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css b/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css deleted file mode 100644 index 1783a37464340..0000000000000 --- a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css +++ /dev/null @@ -1,37 +0,0 @@ -a.item { - @apply font-regular - relative - z-20 - flex - w-full - items-center - gap-1 - overflow-hidden - text-sm - text-neutral-800 - dark:text-neutral-200; - - &:hover { - @apply text-neutral-900 - motion-safe:transition-colors - dark:text-white; - } - - svg { - @apply shrink-0 - fill-neutral-200 - stroke-white - stroke-[4] - dark:fill-neutral-800 - dark:stroke-neutral-950; - } - - &.active { - @apply text-neutral-900 - dark:text-white; - - svg { - @apply fill-green-500; - } - } -} diff --git a/apps/site/components/Common/ProgressionSidebar/index.module.css b/apps/site/components/Common/ProgressionSidebar/index.module.css deleted file mode 100644 index c986e4572678c..0000000000000 --- a/apps/site/components/Common/ProgressionSidebar/index.module.css +++ /dev/null @@ -1,28 +0,0 @@ -.wrapper { - @apply flex - w-full - flex-col - gap-8 - overflow-auto - border-r-0 - border-neutral-200 - bg-white - px-4 - py-6 - sm:border-r - md:max-w-xs - lg:px-6 - dark:border-neutral-900 - dark:bg-neutral-950; - - > section { - @apply hidden - sm:flex; - } - - > span { - @apply flex - w-full - sm:hidden; - } -} diff --git a/apps/site/components/Common/Select/index.module.css b/apps/site/components/Common/Select/index.module.css deleted file mode 100644 index 1e13248039a83..0000000000000 --- a/apps/site/components/Common/Select/index.module.css +++ /dev/null @@ -1,151 +0,0 @@ -.select { - @apply inline-flex - flex-col - gap-1.5; - - .label { - @apply block - w-full - text-sm - font-medium - text-neutral-800 - dark:text-neutral-200; - } - - .trigger { - @apply shadow-xs - inline-flex - h-11 - w-full - min-w-[17rem] - items-center - justify-between - gap-2 - rounded - border - border-neutral-300 - bg-white - px-3.5 - py-2.5 - text-left - text-base - font-medium - text-neutral-900 - outline-none - focus:border-neutral-500 - focus:ring-1 - focus:ring-neutral-500 - data-[placeholder]:text-neutral-800 - dark:border-neutral-800 - dark:bg-neutral-950 - dark:text-white - dark:focus:border-neutral-600 - dark:focus:ring-neutral-600 - dark:data-[placeholder]:text-neutral-200; - } - - .trigger span { - @apply flex - h-5 - items-center - gap-2; - } - - .icon { - @apply size-5 - text-neutral-600 - dark:text-neutral-400; - } -} - -.dropdown { - @apply max-h-48 - max-w-xs - overflow-hidden - overflow-y-auto - rounded-md - border - border-neutral-200 - bg-white - shadow-lg - dark:border-neutral-800 - dark:bg-neutral-950; - - .item { - @apply select-none - truncate - px-2.5 - py-1.5 - text-sm - font-medium; - } - - .text { - @apply text-neutral-800 - data-[highlighted]:bg-green-500 - data-[highlighted]:text-white - data-[highlighted]:outline-none - dark:text-neutral-200 - dark:data-[highlighted]:bg-green-600 - dark:data-[highlighted]:text-white; - } - - .text > span { - @apply flex - items-center - gap-2; - } - - .text > span > span { - @apply max-w-64 - truncate - text-wrap; - } - - .label { - @apply text-neutral-600 - dark:text-neutral-400; - } -} - -.dropdown:has(.label) .text > span { - &:has(svg) > svg { - @apply ml-3; - } - - &:not(&:has(svg)) > span { - @apply ml-3; - } -} - -.inline { - .trigger { - @apply h-auto - min-w-fit - px-2.5 - py-2 - text-sm - font-medium; - } - - .icon { - @apply size-4; - } - - .text { - @apply text-neutral-900 - data-[highlighted]:bg-neutral-100 - data-[disabled]:text-neutral-600 - data-[highlighted]:text-neutral-900 - dark:text-white - dark:data-[highlighted]:bg-neutral-900 - dark:data-[disabled]:text-neutral-700 - dark:data-[highlighted]:text-white; - } - - &.dropdown { - @apply mt-1 - w-[calc(100%+1.5rem)] - rounded; - } -} diff --git a/apps/site/components/Common/Skeleton/index.module.css b/apps/site/components/Common/Skeleton/index.module.css deleted file mode 100644 index 389dc00ca4499..0000000000000 --- a/apps/site/components/Common/Skeleton/index.module.css +++ /dev/null @@ -1,28 +0,0 @@ -.skeleton { - @apply dark:animate-pulse-dark - pointer-events-none - animate-pulse - cursor-default - select-none - rounded-md - border-none - bg-clip-border - text-transparent - shadow-none - outline-none; -} - -.skeleton[data-inline-skeleton] { - @apply leading-none; -} - -.skeleton:empty { - @apply block - h-3; -} - -.skeleton > *, -.skeleton::after, -.skeleton::before { - @apply invisible; -} diff --git a/apps/site/components/Common/Tabs/index.module.css b/apps/site/components/Common/Tabs/index.module.css deleted file mode 100644 index 1703d143e6b3a..0000000000000 --- a/apps/site/components/Common/Tabs/index.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.tabsRoot { - @apply max-w-full; - - .tabsList { - @apply font-open-sans - scrollbar-thin - flex - gap-2 - overflow-x-auto; - - .tabsTrigger { - @apply whitespace-nowrap - border-b-2 - border-b-transparent - px-1 - pb-[11px] - text-sm - font-semibold - text-neutral-800 - dark:text-neutral-200; - - .tabSecondaryLabel { - @apply pl-1 - text-neutral-500 - dark:text-neutral-800; - } - - &[data-state='active'] { - @apply border-b-green-600 - text-green-600 - dark:border-b-green-400 - dark:text-green-400; - - .tabSecondaryLabel { - @apply text-green-800 - dark:text-green-600; - } - } - } - - .addons { - @apply ml-auto - border-b-2 - border-b-transparent - px-1 - pb-[11px] - text-sm - font-semibold; - } - } -} diff --git a/apps/site/components/Common/ThemeToggle/index.module.css b/apps/site/components/Common/ThemeToggle/index.module.css deleted file mode 100644 index c2cd6bb230a91..0000000000000 --- a/apps/site/components/Common/ThemeToggle/index.module.css +++ /dev/null @@ -1,13 +0,0 @@ -.themeToggle { - @apply size-9 - rounded-md - p-2 - text-neutral-700 - motion-safe:transition-colors - dark:text-neutral-300; - - &:hover { - @apply bg-neutral-100 - dark:bg-neutral-900; - } -} diff --git a/apps/site/components/Common/Tooltip/index.module.css b/apps/site/components/Common/Tooltip/index.module.css deleted file mode 100644 index 475db244ae82b..0000000000000 --- a/apps/site/components/Common/Tooltip/index.module.css +++ /dev/null @@ -1,41 +0,0 @@ -.content { - @apply rounded-md - border - bg-white - text-sm - font-medium - text-neutral-900 - shadow-lg - dark:bg-neutral-950 - dark:text-white; - - &.default { - @apply border-neutral-200 - dark:border-neutral-900; - - .arrow { - @apply fill-neutral-200 - dark:fill-neutral-900; - } - } - - &.error { - @apply border-danger-400 - dark:border-danger-900; - - .arrow { - @apply fill-danger-400 - dark:fill-danger-900; - } - } - - &.warning { - @apply border-warning-400 - dark:border-warning-900; - - .arrow { - @apply fill-warning-400 - dark:fill-warning-900; - } - } -} diff --git a/apps/site/components/Containers/Footer/index.module.css b/apps/site/components/Containers/Footer/index.module.css deleted file mode 100644 index e388fa5341dba..0000000000000 --- a/apps/site/components/Containers/Footer/index.module.css +++ /dev/null @@ -1,44 +0,0 @@ -.footer { - @apply flex - flex-col - items-center - gap-6 - border-t - border-neutral-200 - bg-white - py-4 - sm:px-8 - md:flex-row - md:justify-between - md:py-5 - dark:border-neutral-900 - dark:bg-neutral-950; - - .sectionPrimary { - @apply flex - flex-wrap - content-start - items-center - justify-center - gap-1 - self-stretch; - - a { - @apply whitespace-nowrap; - } - } - - .sectionSecondary { - @apply flex - flex-col - items-center - gap-1 - md:flex-row; - - .social { - @apply flex - items-center - gap-1; - } - } -} diff --git a/apps/site/components/Containers/Footer/index.stories.tsx b/apps/site/components/Containers/Footer/index.stories.tsx deleted file mode 100644 index ec44bb46b083b..0000000000000 --- a/apps/site/components/Containers/Footer/index.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Footer from '@/components/Containers/Footer'; - -type Story = StoryObj<typeof Footer>; -type Meta = MetaObj<typeof Footer>; - -export const Default: Story = {}; - -export default { component: Footer } as Meta; diff --git a/apps/site/components/Containers/Footer/index.tsx b/apps/site/components/Containers/Footer/index.tsx deleted file mode 100644 index 3723b3c284f96..0000000000000 --- a/apps/site/components/Containers/Footer/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useTranslations } from 'next-intl'; -import type { FC, SVGProps } from 'react'; - -import NavItem from '@/components/Containers/NavBar/NavItem'; -import Bluesky from '@/components/Icons/Social/Bluesky'; -import Discord from '@/components/Icons/Social/Discord'; -import GitHub from '@/components/Icons/Social/GitHub'; -import LinkedIn from '@/components/Icons/Social/LinkedIn'; -import Mastodon from '@/components/Icons/Social/Mastodon'; -import Slack from '@/components/Icons/Social/Slack'; -import Twitter from '@/components/Icons/Social/Twitter'; -import { siteNavigation } from '@/next.json.mjs'; - -import styles from './index.module.css'; - -const footerSocialIcons: Record<string, React.FC<SVGProps<SVGSVGElement>>> = { - github: GitHub, - mastodon: Mastodon, - twitter: Twitter, - slack: Slack, - linkedin: LinkedIn, - bluesky: Bluesky, - discord: Discord, -}; - -const Footer: FC = () => { - const t = useTranslations(); - - const openJSlink = siteNavigation.footerLinks.at(-1)!; - - return ( - <footer className={styles.footer}> - <div className={styles.sectionPrimary}> - {siteNavigation.footerLinks.slice(0, -1).map(item => ( - <NavItem type="footer" href={item.link} key={item.link}> - {t(item.text)} - </NavItem> - ))} - </div> - - <div className={styles.sectionSecondary}> - <NavItem type="footer" href={openJSlink.link}> - © {openJSlink.text} - </NavItem> - - <div className={styles.social}> - {siteNavigation.socialLinks.map(link => { - const SocialIcon = footerSocialIcons[link.icon]; - - return ( - <NavItem key={link.icon} href={link.link} type="footer"> - <SocialIcon width={20} height={20} aria-label={link.link} /> - </NavItem> - ); - })} - </div> - </div> - </footer> - ); -}; - -export default Footer; diff --git a/apps/site/components/Containers/MetaBar/index.module.css b/apps/site/components/Containers/MetaBar/index.module.css deleted file mode 100644 index 8d867a2b2e0d1..0000000000000 --- a/apps/site/components/Containers/MetaBar/index.module.css +++ /dev/null @@ -1,82 +0,0 @@ -.wrapper { - @apply flex - w-full - flex-col - items-start - gap-8 - overflow-y-auto - border-neutral-200 - px-4 - py-6 - [overflow-wrap:anywhere] - lg:sticky - lg:top-0 - lg:h-max - lg:min-h-screen - lg:px-6 - dark:border-neutral-900; - - dl { - @apply w-full; - } - - dt { - @apply mb-2 - text-sm - font-medium - text-neutral-800 - dark:text-neutral-200; - } - - dd { - @apply mb-8 - flex - items-center - gap-2 - text-sm - text-neutral-900 - dark:text-white; - - a { - @apply max-xs:inline-block - max-xs:py-1 - font-semibold - text-neutral-900 - underline - dark:text-white; - - &:hover { - @apply text-neutral-800 - dark:text-neutral-200; - } - } - - ol { - @apply flex - list-none - flex-col - gap-1.5 - p-0; - } - - svg { - @apply size-4 - text-neutral-600 - dark:text-neutral-400; - } - - &:last-child { - @apply mb-0; - } - } - - [data-on-dark] { - @apply hidden - dark:block; - } - - [data-on-light] { - @apply block - dark:hidden; - } -} diff --git a/apps/site/components/Containers/NavBar/index.module.css b/apps/site/components/Containers/NavBar/index.module.css deleted file mode 100644 index 77f318d069a88..0000000000000 --- a/apps/site/components/Containers/NavBar/index.module.css +++ /dev/null @@ -1,124 +0,0 @@ -.container { - @apply border-neutral-200 - bg-white - lg:flex - lg:h-16 - lg:flex-row - lg:items-center - lg:gap-8 - lg:border-b - lg:px-8 - dark:border-neutral-900 - dark:bg-neutral-950; -} - -.nodeIconAndMobileItemsToggler { - @apply flex - h-16 - shrink-0 - items-center - border-b - border-neutral-200 - px-4 - lg:flex - lg:h-full - lg:items-center - lg:border-0 - lg:px-0 - dark:border-neutral-900; -} - -.sidebarItemToggler { - @apply absolute - right-4 - -z-10 - -translate-y-[200%] - appearance-none - opacity-0; -} - -.nodeIconWrapper { - @apply h-[30px] - flex-1; -} - -.navInteractionIcon, -.sidebarItemToggler { - @apply size-6; -} - -.sidebarItemTogglerLabel { - @apply block - cursor-pointer - lg:hidden; -} - -.main { - @apply hidden - flex-1 - flex-col - justify-between - gap-4 - lg:flex - lg:flex-row - lg:items-center; -} - -.navItems { - @apply flex - flex-col - gap-0 - border-b - border-neutral-200 - p-4 - lg:flex-1 - lg:flex-row - lg:gap-1 - lg:border-0 - lg:p-0 - dark:border-neutral-900; -} - -.actionsWrapper { - @apply flex - flex-row - flex-wrap - items-center - justify-between - gap-2 - border-b - border-neutral-200 - p-4 - sm:flex-nowrap - lg:basis-96 - lg:border-0 - lg:p-0 - dark:border-neutral-900; -} - -span.searchButtonSkeleton { - @apply my-px - mr-2 - flex-grow - rounded-xl; - - &:empty { - @apply h-10; - } -} - -.ghIconWrapper { - @apply size-9 - rounded-md - p-2; - - svg { - @apply fill-neutral-700 - dark:fill-neutral-300; - } - - &:hover { - @apply bg-neutral-100 - dark:bg-neutral-900; - } -} diff --git a/apps/site/components/Containers/NavBar/index.tsx b/apps/site/components/Containers/NavBar/index.tsx deleted file mode 100644 index 840ef6d242a35..0000000000000 --- a/apps/site/components/Containers/NavBar/index.tsx +++ /dev/null @@ -1,109 +0,0 @@ -'use client'; - -import Hamburger from '@heroicons/react/24/solid/Bars3Icon'; -import XMark from '@heroicons/react/24/solid/XMarkIcon'; -import * as Label from '@radix-ui/react-label'; -import classNames from 'classnames'; -import dynamic from 'next/dynamic'; -import { useTranslations } from 'next-intl'; -import { useState } from 'react'; -import type { FC, ComponentProps, HTMLAttributeAnchorTarget } from 'react'; - -import LanguageDropdown from '@/components/Common/LanguageDropDown'; -import Skeleton from '@/components/Common/Skeleton'; -import ThemeToggle from '@/components/Common/ThemeToggle'; -import NavItem from '@/components/Containers/NavBar/NavItem'; -import GitHub from '@/components/Icons/Social/GitHub'; -import Link from '@/components/Link'; -import WithNodejsLogo from '@/components/withNodejsLogo'; -import type { FormattedMessage } from '@/types'; - -import style from './index.module.css'; - -const SearchButton = dynamic(() => import('@/components/Common/Search'), { - ssr: false, - loading: () => ( - <Skeleton className={style.searchButtonSkeleton} loading={true} /> - ), -}); - -const navInteractionIcons = { - show: <Hamburger className={style.navInteractionIcon} />, - close: <XMark className={style.navInteractionIcon} />, -}; - -type NavbarProps = { - navItems: Array<{ - text: FormattedMessage; - link: string; - target?: HTMLAttributeAnchorTarget | undefined; - }>; - languages: ComponentProps<typeof LanguageDropdown>; - onThemeTogglerClick: () => void; -}; - -const NavBar: FC<NavbarProps> = ({ - navItems, - languages, - onThemeTogglerClick, -}) => { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const t = useTranslations(); - - return ( - <nav className={`${style.container}`}> - <div className={style.nodeIconAndMobileItemsToggler}> - <Link className={style.nodeIconWrapper} href="/" aria-label="Home"> - <WithNodejsLogo /> - </Link> - - <Label.Root - className={style.sidebarItemTogglerLabel} - htmlFor="sidebarItemToggler" - > - {navInteractionIcons[isMenuOpen ? 'close' : 'show']} - </Label.Root> - </div> - - <input - className={classNames(['peer', style.sidebarItemToggler])} - id="sidebarItemToggler" - type="checkbox" - onChange={e => setIsMenuOpen(() => e.target.checked)} - aria-label={t(`components.containers.navBar.controls.toggle`)} - /> - - <div className={`${style.main} peer-checked:flex`}> - <div className={style.navItems}> - {navItems.map(({ text, link, target }) => ( - <NavItem key={link} href={link} target={target}> - {text} - </NavItem> - ))} - </div> - - <div className={style.actionsWrapper}> - <SearchButton /> - - <ThemeToggle onClick={onThemeTogglerClick} /> - - <LanguageDropdown - onChange={languages.onChange} - availableLanguages={languages.availableLanguages} - currentLanguage={languages.currentLanguage} - /> - - <Link - className={style.ghIconWrapper} - href="https://github.com/nodejs/node" - aria-label="Node.js Github" - > - <GitHub /> - </Link> - </div> - </div> - </nav> - ); -}; - -export default NavBar; diff --git a/apps/site/components/Containers/Sidebar/SidebarGroup/index.module.css b/apps/site/components/Containers/Sidebar/SidebarGroup/index.module.css deleted file mode 100644 index 52f0d7507fadf..0000000000000 --- a/apps/site/components/Containers/Sidebar/SidebarGroup/index.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.group { - @apply flex - w-full - flex-col - gap-2; -} - -.groupName { - @apply px-2 - py-1 - text-xs - font-semibold - text-neutral-800 - dark:text-neutral-600; -} - -.itemList { - @apply m-0 - flex - flex-col - items-start - gap-0.5 - p-0; -} diff --git a/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx b/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx deleted file mode 100644 index f22de23fa5408..0000000000000 --- a/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { ComponentProps, FC } from 'react'; - -import SidebarItem from '@/components/Containers/Sidebar/SidebarItem'; -import type { FormattedMessage } from '@/types'; - -import styles from './index.module.css'; - -type SidebarGroupProps = { - groupName: FormattedMessage; - items: Array<ComponentProps<typeof SidebarItem>>; -}; - -const SidebarGroup: FC<SidebarGroupProps> = ({ groupName, items }) => ( - <section className={styles.group}> - <label className={styles.groupName}>{groupName}</label> - <ul className={styles.itemList}> - {items.map(({ label, link }) => ( - <SidebarItem key={link} label={label} link={link} /> - ))} - </ul> - </section> -); - -export default SidebarGroup; diff --git a/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css b/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css deleted file mode 100644 index 38c89b9fd6028..0000000000000 --- a/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css +++ /dev/null @@ -1,33 +0,0 @@ -.sideBarItem { - @apply flex - w-full - list-none - text-neutral-800 - dark:text-neutral-200; - - a { - @apply inline-flex - items-center - gap-2 - p-2; - } - - .label { - @apply font-regular - w-full - text-sm; - } - - .icon { - @apply size-3 - text-neutral-500 - dark:text-neutral-200; - } -} - -.active { - @apply rounded - bg-green-600 - text-white - dark:text-white; -} diff --git a/apps/site/components/Containers/Sidebar/index.module.css b/apps/site/components/Containers/Sidebar/index.module.css deleted file mode 100644 index 7324ae7ba9f29..0000000000000 --- a/apps/site/components/Containers/Sidebar/index.module.css +++ /dev/null @@ -1,29 +0,0 @@ -.wrapper { - @apply flex - w-full - flex-col - items-start - gap-8 - overflow-auto - overflow-x-hidden - border-r-neutral-200 - bg-white - px-4 - py-6 - sm:border-r - md:max-w-xs - lg:px-6 - dark:border-r-neutral-900 - dark:bg-neutral-950; - - > section { - @apply hidden - sm:flex; - } - - > span { - @apply flex - w-full - sm:hidden; - } -} diff --git a/apps/site/components/Downloads/DownloadButton/index.stories.tsx b/apps/site/components/Downloads/DownloadButton/index.stories.tsx deleted file mode 100644 index 84c68376a49a9..0000000000000 --- a/apps/site/components/Downloads/DownloadButton/index.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import DownloadButton from '@/components/Downloads/DownloadButton'; - -type Story = StoryObj<typeof DownloadButton>; -type Meta = MetaObj<typeof DownloadButton>; - -export const Default: Story = { - args: { - release: { - currentStart: '2023-04-18', - ltsStart: '2023-10-24', - maintenanceStart: '2024-10-22', - endOfLife: '2026-04-30', - status: 'LTS', - major: 20, - version: '20.11.0', - versionWithPrefix: 'v20.11.0', - codename: 'Iron', - isLts: true, - npm: '10.2.4', - v8: '11.3.244.8', - releaseDate: '2024-01-09', - modules: '115', - }, - children: 'Download Node.js', - }, -}; - -export default { component: DownloadButton } as Meta; diff --git a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx index a5d4a9307db52..c857c9b0ddf87 100644 --- a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx +++ b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { InstallationMethod } from '@/types/release'; import { nextItem, INSTALL_METHODS, parseCompat } from '@/util/downloadUtils'; diff --git a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx index 536707ad2c506..578c0e0e0be15 100644 --- a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx +++ b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { useClientContext } from '@/hooks'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { UserOS } from '@/types/userOS'; diff --git a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx index 3faf61f0a1e9e..ff58252e08420 100644 --- a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx +++ b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { PackageManager } from '@/types/release'; import { nextItem, PACKAGE_MANAGERS, parseCompat } from '@/util/downloadUtils'; diff --git a/apps/site/components/Downloads/Release/PlatformDropdown.tsx b/apps/site/components/Downloads/Release/PlatformDropdown.tsx index de595264ab5d9..a6a30bbaef6a7 100644 --- a/apps/site/components/Downloads/Release/PlatformDropdown.tsx +++ b/apps/site/components/Downloads/Release/PlatformDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useEffect, useContext, useMemo } from 'react'; -import Select from '@/components/Common/Select'; import { useClientContext } from '@/hooks'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { UserPlatform } from '@/types/userOS'; diff --git a/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx b/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx index 5a943b0472a8b..b968e68651c2b 100644 --- a/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx +++ b/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx @@ -1,12 +1,12 @@ 'use client'; import { CloudArrowDownIcon } from '@heroicons/react/24/outline'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; import { useTranslations } from 'next-intl'; import { useContext } from 'react'; import type { FC } from 'react'; import Button from '@/components/Common/Button'; -import Skeleton from '@/components/Common/Skeleton'; import { ReleaseContext } from '@/providers/releaseProvider'; import { OperatingSystemLabel, diff --git a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx index 70de6d02fc989..7678f7ac66f16 100644 --- a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx +++ b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx @@ -1,12 +1,12 @@ 'use client'; +import AlertBox from '@node-core/ui-components/Common/AlertBox'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useContext, useMemo } from 'react'; -import AlertBox from '@/components/Common/AlertBox'; import CodeBox from '@/components/Common/CodeBox'; -import Skeleton from '@/components/Common/Skeleton'; import Link from '@/components/Link'; import LinkWithArrow from '@/components/LinkWithArrow'; import { createSval } from '@/next.jsx.compiler.mjs'; diff --git a/apps/site/components/Downloads/Release/VersionDropdown.tsx b/apps/site/components/Downloads/Release/VersionDropdown.tsx index 9cdff66faab24..2f8b1b015c358 100644 --- a/apps/site/components/Downloads/Release/VersionDropdown.tsx +++ b/apps/site/components/Downloads/Release/VersionDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useContext } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext, ReleasesContext } from '@/providers/releaseProvider'; const getDropDownStatus = (version: string, status: string) => { diff --git a/apps/site/components/Icons/InstallationMethod/FNM.tsx b/apps/site/components/Icons/InstallationMethod/FNM.tsx deleted file mode 100644 index 90407358527b3..0000000000000 --- a/apps/site/components/Icons/InstallationMethod/FNM.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { FC, SVGProps } from 'react'; - -// @todo: replace with original vector (https://github.com/Schniz/fnm/issues/798#issuecomment-2068220441) -// this is a rough tracing of the available raster image -const FNM: FC<SVGProps<SVGSVGElement>> = props => ( - <svg - width="32" - height="32" - viewBox="0 0 32 32" - fill="none" - xmlns="http://www.w3.org/2000/svg" - {...props} - > - <g clipPath="url(#clip0_9_63)"> - <path - d="M1.335 29.3115C1.6927 20.3502 3.86906 17.6129 12.4625 18.1841L1.335 29.3115Z" - fill="#FFA700" - /> - <path - d="M1.33445 29.3111C10.2957 28.9534 13.0331 26.7771 12.4619 18.1836L1.33445 29.3111Z" - fill="#FFA700" - /> - <circle cx="8.06134" cy="22.6108" r="3.34002" fill="#FFC920" /> - <path - d="M8.06101 8.39001L12.0369 8.63409L7.99678 16.541C5.44038 15.0251 3.62263 14.4213 1.36169 14.2415L8.06101 8.39001Z" - fill="#EC423C" - /> - <path - d="M1.36169 14.2415C5.11912 13.5681 7.43848 12.6734 12.0369 8.63416L7.99678 16.541C5.44038 15.0252 3.62263 14.4214 1.36169 14.2415Z" - fill="black" - fillOpacity="0.1" - /> - <path - d="M30.6523 0.00493863C25.904 0.00502983 3.51938 7.45518 8.66979 21.9874L30.6523 0.00493863Z" - fill="#DAD5D4" - /> - <path - d="M30.6472 0.00728336C30.6471 4.75558 23.1969 27.1402 8.66469 21.9898L30.6472 0.00728336Z" - fill="#DAD5D4" - /> - <path - d="M23.4046 2.61321L26.6702 1.70942L29.0319 4.07117L28.1281 7.33672L24.8626 8.24052L22.5008 5.87876L23.4046 2.61321Z" - fill="#EC423C" - /> - <path - d="M7.25505 23.4488L14.5967 16.1072C14.2755 15.497 7.94876 20.6997 7.67899 21.0658C7.40922 21.4319 7.29288 22.2291 7.25505 23.4488Z" - fill="#EC423C" - /> - <path - d="M7.25251 23.4459L14.5941 16.1042C15.2043 16.4254 10.0016 22.7522 9.63548 23.0219C9.26936 23.2917 8.47216 23.4081 7.25251 23.4459Z" - fill="#EC423C" - /> - <path - d="M30.6472 0.00728336C30.6471 4.75558 23.1969 27.1402 8.66469 21.9898C14.4457 15.5825 21.7231 10.277 30.6472 0.00728336Z" - fill="black" - fillOpacity="0.1" - /> - <mask - id="path-12-outside-1_9_63" - maskUnits="userSpaceOnUse" - x="4.55376" - y="9.34656" - width="24.7487" - height="24.7487" - fill="black" - > - <rect - fill="white" - x="4.55376" - y="9.34656" - width="24.7487" - height="24.7487" - /> - <path d="M10.1695 22.4334C10.7254 21.8775 11.3141 21.4742 11.9354 21.2235L12.4177 22.4252C12.0308 22.5942 11.6683 22.8476 11.3304 23.1855C11.0742 23.4417 10.9353 23.6733 10.9135 23.8804C10.8971 24.0821 10.9952 24.2892 11.2078 24.5017L11.7882 25.0822L13.2025 23.6679L13.9547 24.7143L12.6875 25.9815L15.8595 29.1535L14.576 30.437L11.404 27.265L10.4883 28.1806L9.58906 27.2814L10.5047 26.3657L9.88337 25.7444C9.43646 25.2975 9.22935 24.7797 9.26205 24.1911C9.29475 23.5916 9.59724 23.0057 10.1695 22.4334ZM13.1912 23.1397L14.3194 22.0115L14.908 22.4202C14.8862 22.006 14.9298 21.6409 15.0388 21.3248C15.1478 20.9977 15.3386 20.698 15.6111 20.4255C15.9871 20.0494 16.3932 19.8723 16.8292 19.8941C17.2707 19.9104 17.6958 20.123 18.1045 20.5317L21.2929 23.7201L20.0012 25.0118L17.1889 22.1995C16.9164 21.927 16.6984 21.7689 16.5349 21.7253C16.3714 21.6817 16.2106 21.739 16.0525 21.897C15.9217 22.0278 15.8372 22.1995 15.7991 22.4121C15.7609 22.6137 15.7582 22.8617 15.7909 23.156L18.824 26.1891L17.5323 27.4808L13.1912 23.1397ZM20.9136 15.1229C21.2243 14.8122 21.554 14.6896 21.9029 14.755C22.2517 14.8095 22.6468 15.0575 23.0883 15.499L26.3012 18.7118L25.1975 19.8155L22.1318 16.7498C21.9737 16.5917 21.8484 16.4991 21.7557 16.4718C21.6685 16.4391 21.584 16.4636 21.5023 16.5454C21.3497 16.698 21.2952 16.9433 21.3388 17.2812L24.5353 20.4777L23.6197 21.3933L20.5539 18.3276C20.3959 18.1696 20.2705 18.0769 20.1779 18.0496C20.0907 18.0169 20.0062 18.0415 19.9244 18.1232C19.7718 18.2758 19.7173 18.5211 19.7609 18.859L22.9575 22.0555L21.8375 23.1756L17.4964 18.8345L18.4365 17.8943L18.9434 18.2213C18.8889 17.8834 18.8862 17.6027 18.9352 17.3793C18.9843 17.1449 19.1015 16.9351 19.2868 16.7498C19.4557 16.5808 19.641 16.4827 19.8427 16.4555C20.0389 16.4228 20.2487 16.4691 20.4722 16.5944C20.4231 15.9568 20.5703 15.4663 20.9136 15.1229Z" /> - </mask> - <path - d="M10.1695 22.4334C10.7254 21.8775 11.3141 21.4742 11.9354 21.2235L12.4177 22.4252C12.0308 22.5942 11.6683 22.8476 11.3304 23.1855C11.0742 23.4417 10.9353 23.6733 10.9135 23.8804C10.8971 24.0821 10.9952 24.2892 11.2078 24.5017L11.7882 25.0822L13.2025 23.6679L13.9547 24.7143L12.6875 25.9815L15.8595 29.1535L14.576 30.437L11.404 27.265L10.4883 28.1806L9.58906 27.2814L10.5047 26.3657L9.88337 25.7444C9.43646 25.2975 9.22935 24.7797 9.26205 24.1911C9.29475 23.5916 9.59724 23.0057 10.1695 22.4334ZM13.1912 23.1397L14.3194 22.0115L14.908 22.4202C14.8862 22.006 14.9298 21.6409 15.0388 21.3248C15.1478 20.9977 15.3386 20.698 15.6111 20.4255C15.9871 20.0494 16.3932 19.8723 16.8292 19.8941C17.2707 19.9104 17.6958 20.123 18.1045 20.5317L21.2929 23.7201L20.0012 25.0118L17.1889 22.1995C16.9164 21.927 16.6984 21.7689 16.5349 21.7253C16.3714 21.6817 16.2106 21.739 16.0525 21.897C15.9217 22.0278 15.8372 22.1995 15.7991 22.4121C15.7609 22.6137 15.7582 22.8617 15.7909 23.156L18.824 26.1891L17.5323 27.4808L13.1912 23.1397ZM20.9136 15.1229C21.2243 14.8122 21.554 14.6896 21.9029 14.755C22.2517 14.8095 22.6468 15.0575 23.0883 15.499L26.3012 18.7118L25.1975 19.8155L22.1318 16.7498C21.9737 16.5917 21.8484 16.4991 21.7557 16.4718C21.6685 16.4391 21.584 16.4636 21.5023 16.5454C21.3497 16.698 21.2952 16.9433 21.3388 17.2812L24.5353 20.4777L23.6197 21.3933L20.5539 18.3276C20.3959 18.1696 20.2705 18.0769 20.1779 18.0496C20.0907 18.0169 20.0062 18.0415 19.9244 18.1232C19.7718 18.2758 19.7173 18.5211 19.7609 18.859L22.9575 22.0555L21.8375 23.1756L17.4964 18.8345L18.4365 17.8943L18.9434 18.2213C18.8889 17.8834 18.8862 17.6027 18.9352 17.3793C18.9843 17.1449 19.1015 16.9351 19.2868 16.7498C19.4557 16.5808 19.641 16.4827 19.8427 16.4555C20.0389 16.4228 20.2487 16.4691 20.4722 16.5944C20.4231 15.9568 20.5703 15.4663 20.9136 15.1229Z" - fill="#FFA700" - /> - <path - d="M11.9354 21.2235L13.4852 20.6014L12.862 19.0487L11.3105 19.6748L11.9354 21.2235ZM12.4177 22.4252L13.086 23.9557L14.5712 23.3072L13.9676 21.8032L12.4177 22.4252ZM10.9135 23.8804L9.25262 23.7056L9.25053 23.7255L9.24891 23.7455L10.9135 23.8804ZM11.7882 25.0822L10.6073 26.2631L11.7882 27.4439L12.9691 26.2631L11.7882 25.0822ZM13.2025 23.6679L14.5586 22.6932L13.4115 21.0972L12.0217 22.487L13.2025 23.6679ZM13.9547 24.7143L15.1355 25.8952L16.1389 24.8918L15.3107 23.7396L13.9547 24.7143ZM12.6875 25.9815L11.5066 24.8006L10.3257 25.9815L11.5066 27.1624L12.6875 25.9815ZM15.8595 29.1535L17.0404 30.3344L18.2213 29.1535L17.0404 27.9726L15.8595 29.1535ZM14.576 30.437L13.3951 31.6179L14.576 32.7988L15.7569 31.6179L14.576 30.437ZM11.404 27.265L12.5849 26.0841L11.404 24.9032L10.2231 26.0841L11.404 27.265ZM10.4883 28.1806L9.30747 29.3615L10.4883 30.5424L11.6692 29.3615L10.4883 28.1806ZM9.58906 27.2814L8.40818 26.1005L7.22731 27.2814L8.40818 28.4622L9.58906 27.2814ZM10.5047 26.3657L11.6856 27.5466L12.8664 26.3657L11.6856 25.1848L10.5047 26.3657ZM9.26205 24.1911L10.9295 24.2837L10.9296 24.282L9.26205 24.1911ZM11.3504 23.6143C11.7747 23.1899 12.18 22.9256 12.5603 22.7721L11.3105 19.6748C10.4481 20.0227 9.67612 20.565 8.98863 21.2525L11.3504 23.6143ZM10.3855 21.8455L10.8679 23.0473L13.9676 21.8032L13.4852 20.6014L10.3855 21.8455ZM11.7495 20.8947C11.1478 21.1575 10.6157 21.5385 10.1495 22.0046L12.5113 24.3664C12.721 24.1567 12.9137 24.0309 13.086 23.9557L11.7495 20.8947ZM10.1495 22.0046C9.80318 22.351 9.33483 22.9247 9.25262 23.7056L12.5743 24.0553C12.5533 24.2545 12.4861 24.3772 12.4661 24.4106C12.4461 24.4438 12.4492 24.4285 12.5113 24.3664L10.1495 22.0046ZM9.24891 23.7455C9.17686 24.6341 9.63926 25.295 10.0269 25.6826L12.3886 23.3209C12.3512 23.2834 12.6174 23.5301 12.578 24.0154L9.24891 23.7455ZM10.0269 25.6826L10.6073 26.2631L12.9691 23.9013L12.3886 23.3209L10.0269 25.6826ZM12.9691 26.2631L14.3834 24.8487L12.0217 22.487L10.6073 23.9013L12.9691 26.2631ZM11.8465 24.6425L12.5986 25.689L15.3107 23.7396L14.5586 22.6932L11.8465 24.6425ZM12.7738 23.5334L11.5066 24.8006L13.8684 27.1624L15.1355 25.8952L12.7738 23.5334ZM11.5066 27.1624L14.6786 30.3344L17.0404 27.9726L13.8684 24.8006L11.5066 27.1624ZM14.6786 27.9726L13.3951 29.2561L15.7569 31.6179L17.0404 30.3344L14.6786 27.9726ZM15.7569 29.2561L12.5849 26.0841L10.2231 28.4459L13.3951 31.6179L15.7569 29.2561ZM10.2231 26.0841L9.30747 26.9998L11.6692 29.3615L12.5849 28.4459L10.2231 26.0841ZM11.6692 26.9998L10.7699 26.1005L8.40818 28.4622L9.30747 29.3615L11.6692 26.9998ZM10.7699 28.4622L11.6856 27.5466L9.32382 25.1848L8.40818 26.1005L10.7699 28.4622ZM11.6856 25.1848L11.0642 24.5635L8.7025 26.9253L9.32382 27.5466L11.6856 25.1848ZM11.0642 24.5635C10.926 24.4253 10.9252 24.3607 10.9295 24.2837L7.59461 24.0985C7.53348 25.1987 7.94691 26.1697 8.7025 26.9253L11.0642 24.5635ZM10.9296 24.282C10.9331 24.2182 10.9666 23.9981 11.3504 23.6143L8.98863 21.2525C8.22791 22.0132 7.65643 22.965 7.59452 24.1001L10.9296 24.282ZM13.1912 23.1397L12.0103 21.9588L10.8294 23.1397L12.0103 24.3205L13.1912 23.1397ZM14.3194 22.0115L15.2719 20.6398L14.1255 19.8436L13.1385 20.8306L14.3194 22.0115ZM14.908 22.4202L13.9554 23.7919L16.7548 25.736L16.5757 22.3325L14.908 22.4202ZM15.0388 21.3248L16.6176 21.8692L16.6204 21.861L16.6231 21.8529L15.0388 21.3248ZM16.8292 19.8941L16.7458 21.562L16.7566 21.5625L16.7674 21.5629L16.8292 19.8941ZM21.2929 23.7201L22.4738 24.901L23.6547 23.7201L22.4738 22.5392L21.2929 23.7201ZM20.0012 25.0118L18.8203 26.1927L20.0012 27.3736L21.1821 26.1927L20.0012 25.0118ZM16.5349 21.7253L16.9652 20.1117L16.9652 20.1117L16.5349 21.7253ZM15.7991 22.4121L17.44 22.7225L17.4415 22.7148L17.4428 22.7071L15.7991 22.4121ZM15.7909 23.156L14.1311 23.3404L14.1958 23.9227L14.61 24.3369L15.7909 23.156ZM18.824 26.1891L20.0048 27.3699L21.1857 26.1891L20.0048 25.0082L18.824 26.1891ZM17.5323 27.4808L16.3514 28.6616L17.5323 29.8425L18.7131 28.6616L17.5323 27.4808ZM14.3721 24.3205L15.5002 23.1924L13.1385 20.8306L12.0103 21.9588L14.3721 24.3205ZM13.3668 23.3832L13.9554 23.7919L15.8606 21.0485L15.2719 20.6398L13.3668 23.3832ZM16.5757 22.3325C16.5626 22.0829 16.5932 21.9398 16.6176 21.8692L13.46 20.7803C13.2664 21.3419 13.2098 21.9292 13.2403 22.508L16.5757 22.3325ZM16.6231 21.8529C16.6409 21.7996 16.6809 21.7174 16.7919 21.6063L14.4302 19.2446C13.9962 19.6786 13.6547 20.1959 13.4545 20.7966L16.6231 21.8529ZM16.7919 21.6063C16.8612 21.5371 16.888 21.5314 16.8654 21.5413C16.854 21.5462 16.8352 21.5528 16.8107 21.5573C16.7862 21.5619 16.7636 21.5629 16.7458 21.562L16.9126 18.2261C15.9043 18.1757 15.0611 18.6137 14.4302 19.2446L16.7919 21.6063ZM16.7674 21.5629C16.7322 21.5616 16.7165 21.5516 16.7323 21.5595C16.7539 21.5703 16.8197 21.6087 16.9237 21.7126L19.2854 19.3509C18.6732 18.7386 17.8706 18.2615 16.891 18.2252L16.7674 21.5629ZM16.9237 21.7126L20.112 24.901L22.4738 22.5392L19.2854 19.3509L16.9237 21.7126ZM20.112 22.5392L18.8203 23.8309L21.1821 26.1927L22.4738 24.901L20.112 22.5392ZM21.1821 23.8309L18.3698 21.0186L16.008 23.3804L18.8203 26.1927L21.1821 23.8309ZM18.3698 21.0186C18.0922 20.7411 17.5995 20.2809 16.9652 20.1117L16.1046 23.339C16.0488 23.3241 16.0031 23.3077 15.9683 23.2935C15.9333 23.2792 15.9062 23.2659 15.8873 23.256C15.851 23.2368 15.835 23.2245 15.8408 23.2286C15.8456 23.2322 15.8629 23.2452 15.8931 23.2717C15.9228 23.2978 15.9611 23.3334 16.008 23.3804L18.3698 21.0186ZM16.9652 20.1117C16.0421 19.8656 15.2967 20.2911 14.8717 20.7161L17.2334 23.0779C17.1892 23.1221 17.0654 23.2328 16.8517 23.3089C16.6165 23.3926 16.3524 23.405 16.1046 23.339L16.9652 20.1117ZM14.8717 20.7161C14.4613 21.1265 14.2434 21.6266 14.1554 22.117L17.4428 22.7071C17.4378 22.7349 17.4243 22.7905 17.3895 22.8612C17.3537 22.9339 17.3019 23.0094 17.2334 23.0779L14.8717 20.7161ZM14.1582 22.1016C14.0788 22.521 14.0875 22.9475 14.1311 23.3404L17.4507 22.9716C17.4405 22.8793 17.4379 22.8124 17.4384 22.7676C17.4389 22.7222 17.4424 22.7096 17.44 22.7225L14.1582 22.1016ZM14.61 24.3369L17.6431 27.3699L20.0048 25.0082L16.9718 21.9751L14.61 24.3369ZM17.6431 25.0082L16.3514 26.2999L18.7131 28.6616L20.0048 27.3699L17.6431 25.0082ZM18.7131 26.2999L14.3721 21.9588L12.0103 24.3205L16.3514 28.6616L18.7131 26.2999ZM21.9029 14.755L21.5951 16.3964L21.62 16.4011L21.645 16.405L21.9029 14.755ZM26.3012 18.7118L27.482 19.8927L28.6629 18.7118L27.482 17.531L26.3012 18.7118ZM25.1975 19.8155L24.0166 20.9964L25.1975 22.1773L26.3784 20.9964L25.1975 19.8155ZM21.7557 16.4718L21.1693 18.0355L21.2262 18.0568L21.2845 18.074L21.7557 16.4718ZM21.3388 17.2812L19.6825 17.4949L19.7553 18.0595L20.1579 18.462L21.3388 17.2812ZM24.5353 20.4777L25.7162 21.6586L26.8971 20.4777L25.7162 19.2968L24.5353 20.4777ZM23.6197 21.3933L22.4388 22.5742L23.6197 23.7551L24.8005 22.5742L23.6197 21.3933ZM20.1779 18.0496L19.5915 19.6133L19.6484 19.6347L19.7066 19.6518L20.1779 18.0496ZM19.7609 18.859L18.1046 19.0727L18.1775 19.6373L18.58 20.0399L19.7609 18.859ZM22.9575 22.0555L24.1383 23.2364L25.3192 22.0555L24.1383 20.8747L22.9575 22.0555ZM21.8375 23.1756L20.6566 24.3564L21.8375 25.5373L23.0183 24.3564L21.8375 23.1756ZM17.4964 18.8345L16.3155 17.6536L15.1346 18.8345L16.3155 20.0154L17.4964 18.8345ZM18.4365 17.8943L19.3419 16.491L18.209 15.7601L17.2556 16.7134L18.4365 17.8943ZM18.9434 18.2213L18.038 19.6246L21.1892 21.6577L20.5921 17.9554L18.9434 18.2213ZM18.9352 17.3793L20.5664 17.7373L20.5681 17.7294L20.5698 17.7214L18.9352 17.3793ZM19.8427 16.4555L20.0663 18.1104L20.0918 18.107L20.1172 18.1028L19.8427 16.4555ZM20.4722 16.5944L19.6551 18.0509L22.3766 19.5776L22.1373 16.4664L20.4722 16.5944ZM22.0945 16.3038C22.1212 16.2771 22.0847 16.3228 21.9761 16.3632C21.8541 16.4086 21.716 16.4191 21.5951 16.3964L22.2106 13.1136C21.1888 12.922 20.3282 13.3465 19.7328 13.942L22.0945 16.3038ZM21.645 16.405C21.5362 16.388 21.5088 16.356 21.5731 16.3964C21.6357 16.4357 21.7475 16.52 21.9074 16.6798L24.2691 14.3181C23.7474 13.7963 23.0388 13.2422 22.1607 13.105L21.645 16.405ZM21.9074 16.6798L25.1203 19.8927L27.482 17.531L24.2691 14.3181L21.9074 16.6798ZM25.1203 17.531L24.0166 18.6346L26.3784 20.9964L27.482 19.8927L25.1203 17.531ZM26.3784 18.6346L23.3126 15.5689L20.9509 17.9307L24.0166 20.9964L26.3784 18.6346ZM23.3126 15.5689C23.1528 15.4091 22.7656 15.0281 22.2269 14.8697L21.2845 18.074C21.1642 18.0386 21.0765 17.9953 21.0265 17.9678C20.9747 17.9393 20.9408 17.9153 20.9266 17.9047C20.9008 17.8857 20.9076 17.8874 20.9509 17.9307L23.3126 15.5689ZM22.3421 14.9081C21.9824 14.7733 21.5704 14.7431 21.1613 14.8619C20.7771 14.9734 20.5007 15.1852 20.3214 15.3645L22.6831 17.7263C22.5856 17.8239 22.3923 17.9824 22.0926 18.0695C21.7679 18.1637 21.4418 18.1377 21.1693 18.0355L22.3421 14.9081ZM20.3214 15.3645C19.6319 16.054 19.6113 16.9435 19.6825 17.4949L22.995 17.0675C22.9898 17.0267 23.0039 17.0856 22.9772 17.2061C22.9471 17.3416 22.8663 17.5432 22.6831 17.7263L20.3214 15.3645ZM20.1579 18.462L23.3544 21.6586L25.7162 19.2968L22.5196 16.1003L20.1579 18.462ZM23.3544 19.2968L22.4388 20.2125L24.8005 22.5742L25.7162 21.6586L23.3544 19.2968ZM24.8005 20.2125L21.7348 17.1467L19.3731 19.5085L22.4388 22.5742L24.8005 20.2125ZM21.7348 17.1467C21.575 16.9869 21.1877 16.6059 20.6491 16.4475L19.7066 19.6518C19.5863 19.6164 19.4987 19.5731 19.4487 19.5456C19.3968 19.5171 19.363 19.4931 19.3487 19.4826C19.323 19.4635 19.3298 19.4652 19.3731 19.5085L21.7348 17.1467ZM20.7642 16.486C20.4046 16.3511 19.9926 16.321 19.5835 16.4397C19.1993 16.5513 18.9229 16.763 18.7436 16.9423L21.1053 19.3041C21.0077 19.4017 20.8145 19.5603 20.5147 19.6473C20.1901 19.7416 19.8639 19.7155 19.5915 19.6133L20.7642 16.486ZM18.7436 16.9423C18.054 17.6319 18.0335 18.5214 18.1046 19.0727L21.4172 18.6453C21.4119 18.6045 21.4261 18.6635 21.3993 18.7839C21.3692 18.9194 21.2884 19.121 21.1053 19.3041L18.7436 16.9423ZM18.58 20.0399L21.7766 23.2364L24.1383 20.8747L20.9418 17.6781L18.58 20.0399ZM21.7766 20.8747L20.6566 21.9947L23.0183 24.3564L24.1383 23.2364L21.7766 20.8747ZM23.0183 21.9947L18.6772 17.6536L16.3155 20.0154L20.6566 24.3564L23.0183 21.9947ZM18.6772 20.0154L19.6174 19.0752L17.2556 16.7134L16.3155 17.6536L18.6772 20.0154ZM17.5312 19.2976L18.038 19.6246L19.8488 16.818L19.3419 16.491L17.5312 19.2976ZM20.5921 17.9554C20.5754 17.8518 20.5708 17.7821 20.5704 17.7412C20.57 17.6994 20.5741 17.7022 20.5664 17.7373L17.304 17.0212C17.1934 17.5251 17.2218 18.035 17.2947 18.4873L20.5921 17.9554ZM20.5698 17.7214C20.5596 17.7702 20.5414 17.8183 20.518 17.8604C20.495 17.9014 20.4741 17.9242 20.4676 17.9307L18.1059 15.5689C17.7136 15.9612 17.4224 16.4552 17.3006 17.0372L20.5698 17.7214ZM20.4676 17.9307C20.4505 17.9478 20.4072 17.9866 20.3338 18.0254C20.2579 18.0656 20.1664 18.0969 20.0663 18.1104L19.619 14.8005C18.9946 14.8849 18.4867 15.1881 18.1059 15.5689L20.4676 17.9307ZM20.1172 18.1028C19.8419 18.1486 19.6664 18.0573 19.6551 18.0509L21.2892 15.138C20.831 14.8809 20.2358 14.6969 19.5681 14.8082L20.1172 18.1028ZM22.1373 16.4664C22.1247 16.3034 22.1435 16.2343 22.1453 16.2282C22.1459 16.2263 22.1428 16.237 22.1323 16.2547C22.1217 16.2729 22.1083 16.29 22.0945 16.3038L19.7328 13.942C18.9433 14.7315 18.7333 15.7628 18.8071 16.7225L22.1373 16.4664Z" - fill="white" - mask="url(#path-12-outside-1_9_63)" - /> - </g> - <defs> - <clipPath id="clip0_9_63"> - <rect - width="29.3408" - height="32" - fill="white" - transform="translate(1.32959)" - /> - </clipPath> - </defs> - </svg> -); - -export default FNM; diff --git a/apps/site/components/Icons/InstallationMethod/index.ts b/apps/site/components/Icons/InstallationMethod/index.ts deleted file mode 100644 index 41c7897e2172f..0000000000000 --- a/apps/site/components/Icons/InstallationMethod/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Choco from '@/components/Icons/InstallationMethod/Choco'; -import Devbox from '@/components/Icons/InstallationMethod/Devbox'; -import Docker from '@/components/Icons/InstallationMethod/Docker'; -import FNM from '@/components/Icons/InstallationMethod/FNM'; -import Homebrew from '@/components/Icons/InstallationMethod/Homebrew'; -import NVM from '@/components/Icons/InstallationMethod/NVM'; -import Volta from '@/components/Icons/InstallationMethod/Volta'; - -export default { Choco, Devbox, Docker, FNM, Homebrew, NVM, Volta }; diff --git a/apps/site/components/Icons/OperatingSystem/index.ts b/apps/site/components/Icons/OperatingSystem/index.ts deleted file mode 100644 index 167e970f87135..0000000000000 --- a/apps/site/components/Icons/OperatingSystem/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import AIX from '@/components/Icons/OperatingSystem/AIX'; -import Apple from '@/components/Icons/OperatingSystem/Apple'; -import Linux from '@/components/Icons/OperatingSystem/Linux'; -import Microsoft from '@/components/Icons/OperatingSystem/Microsoft'; - -export default { AIX, Apple, Linux, Microsoft }; diff --git a/apps/site/components/Icons/PackageManager/index.ts b/apps/site/components/Icons/PackageManager/index.ts deleted file mode 100644 index 4ba989918ca4c..0000000000000 --- a/apps/site/components/Icons/PackageManager/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import NPM from '@/components/Icons/PackageManager/Npm'; -import PNPM from '@/components/Icons/PackageManager/Pnpm'; -import YARN from '@/components/Icons/PackageManager/Yarn'; - -export default { NPM, PNPM, YARN }; diff --git a/apps/site/components/MDX/Calendar/Event/index.stories.tsx b/apps/site/components/MDX/Calendar/Event/index.stories.tsx deleted file mode 100644 index d35c692533176..0000000000000 --- a/apps/site/components/MDX/Calendar/Event/index.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Event from '@/components/MDX/Calendar/Event'; - -type Story = StoryObj<typeof Event>; -type Meta = MetaObj<typeof Event>; - -export const Default: Story = { - args: { - start: { date: '2024-02-19T12:30:00.000Z' }, - end: { date: '2024-02-19T16:00:00.000Z' }, - summary: 'Example Event', - location: 'Event Location', - description: 'This is an example event description.', - }, -}; - -export default { component: Event } as Meta; diff --git a/apps/site/components/MDX/CodeBox/index.stories.tsx b/apps/site/components/MDX/CodeBox/index.stories.tsx deleted file mode 100644 index 8982fe9ef4e0b..0000000000000 --- a/apps/site/components/MDX/CodeBox/index.stories.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import { VFile } from 'vfile'; - -import { compile } from '@/next.mdx.compiler.mjs'; -import { MDX_COMPONENTS } from '@/next.mdx.components.mjs'; - -type Props = { children: string }; - -type Story = StoryObj<Props>; -type Meta = MetaObj<Props>; - -export const Default: Story = { - args: { - children: `\`\`\`javascript -const http = require('http'); - -const hostname = '127.0.0.1'; -const port = 3000; - -const server = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('Content-Type', 'text/plain'); - res.end('Hello World'); -}); - -server.listen(port, hostname, () => { - console.log(\`Server running at http://\${hostname}:\${port}/\`); -}); -\`\`\``, - }, -}; - -export default { - title: 'MDX/CodeBox', - render: (_, { loaded: { Content } }) => Content, - loaders: [ - async ({ args }) => { - const { content } = await compile( - new VFile(args.children), - 'mdx', - MDX_COMPONENTS - ); - - return { Content: content }; - }, - ], -} as Meta; diff --git a/apps/site/components/MDX/CodeTabs/index.stories.tsx b/apps/site/components/MDX/CodeTabs/index.stories.tsx deleted file mode 100644 index dd6474be252f1..0000000000000 --- a/apps/site/components/MDX/CodeTabs/index.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import { VFile } from 'vfile'; - -import { compile } from '@/next.mdx.compiler.mjs'; -import { MDX_COMPONENTS } from '@/next.mdx.components.mjs'; - -type Props = { children: string }; - -type Story = StoryObj<Props>; -type Meta = MetaObj<Props>; - -export const Default: Story = { - args: { - children: `\`\`\`mjs -const { createHmac } = await import('node:crypto'); - -const secret = 'abcdefg'; -const hash = createHmac('sha256', secret) - .update('I love cupcakes') - .digest('hex'); - -console.log(hash); -// Prints: -// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e -\`\`\` - -\`\`\`cjs displayName="CommonJS" showCopyButton="true" -const { createHmac } = require('node:crypto'); - -const secret = 'abcdefg'; -const hash = createHmac('sha256', secret) - .update('I love cupcakes') - .digest('hex'); - -console.log(hash); -// Prints: -// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e -\`\`\``, - }, -}; - -export default { - title: 'MDX/CodeTabs', - render: (_, { loaded: { Content } }) => Content, - loaders: [ - async ({ args }) => { - const { content } = await compile( - new VFile(args.children), - 'mdx', - MDX_COMPONENTS - ); - - return { Content: content }; - }, - ], -} as Meta; diff --git a/apps/site/components/MDX/CodeTabs/index.tsx b/apps/site/components/MDX/CodeTabs/index.tsx index 45adc0f5cf751..cebf603d2b37a 100644 --- a/apps/site/components/MDX/CodeTabs/index.tsx +++ b/apps/site/components/MDX/CodeTabs/index.tsx @@ -1,12 +1,8 @@ +import CodeTabs from '@node-core/ui-components/Common/CodeTabs'; import * as TabsPrimitive from '@radix-ui/react-tabs'; -import type { ComponentProps, FC, ReactElement } from 'react'; +import type { FC, ReactElement } from 'react'; -import CodeTabs from '@/components/Common/CodeTabs'; - -type MDXCodeTabsProps = Pick< - ComponentProps<typeof CodeTabs>, - 'linkText' | 'linkUrl' -> & { +type MDXCodeTabsProps = { children: Array<ReactElement<unknown>>; languages: string; displayNames?: string; diff --git a/apps/site/components/__design__/node-logos.stories.tsx b/apps/site/components/__design__/node-logos.stories.tsx deleted file mode 100644 index 157a5cd3df709..0000000000000 --- a/apps/site/components/__design__/node-logos.stories.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import JsIconGreen from '@/components/Icons/Logos/JsIconGreen'; -import JsIconWhite from '@/components/Icons/Logos/JsIconWhite'; -import NodejsLogo from '@/components/Icons/Logos/Nodejs'; -import NodejsStackedBlack from '@/components/Icons/Logos/NodejsStackedBlack'; -import NodejsStackedDark from '@/components/Icons/Logos/NodejsStackedDark'; -import NodejsStackedLight from '@/components/Icons/Logos/NodejsStackedLight'; -import NodejsStackedWhite from '@/components/Icons/Logos/NodejsStackedWhite'; - -export const HorizontalLogo: StoryObj = { - render: () => <NodejsLogo width={267} height={80} />, -}; - -export const PrideLogo: StoryObj = { - render: () => <NodejsLogo variant="pride" width={267} height={80} />, -}; - -export const StackedLogos: StoryObj = { - render: () => ( - <div className="flex flex-row gap-4"> - <div className="flex flex-col gap-2"> - <NodejsStackedDark - className="block dark:hidden" - width={267} - height={164} - /> - - <NodejsStackedBlack - className="block dark:hidden" - width={267} - height={164} - /> - - <NodejsStackedLight - className="hidden dark:block" - width={267} - height={164} - /> - - <NodejsStackedWhite - className="hidden dark:block" - width={267} - height={164} - /> - </div> - </div> - ), -}; - -export const JSSymbols: StoryObj = { - render: () => ( - <div className="flex flex-row gap-4"> - <JsIconWhite className="hidden dark:block" width={30} height={30} /> - <JsIconGreen className="block dark:hidden" width={30} height={30} /> - </div> - ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/package-manager.stories.tsx b/apps/site/components/__design__/package-manager.stories.tsx deleted file mode 100644 index 76ecbe338b8eb..0000000000000 --- a/apps/site/components/__design__/package-manager.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import PackageManagerIcons from '@/components/Icons/PackageManager'; - -export const PackageManager: StoryObj = { - render: () => ( - <div className="flex space-x-4"> - <PackageManagerIcons.NPM width={64} height={64} /> - <PackageManagerIcons.PNPM width={64} height={64} /> - <PackageManagerIcons.YARN width={64} height={64} /> - </div> - ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/platform-logos.stories.tsx b/apps/site/components/__design__/platform-logos.stories.tsx deleted file mode 100644 index 39d6b20c7534f..0000000000000 --- a/apps/site/components/__design__/platform-logos.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import InstallMethodIcons from '@/components/Icons/InstallationMethod'; -import OSIcons from '@/components/Icons/OperatingSystem'; - -export const PlatformLogos: StoryObj = { - render: () => ( - <div className="flex flex-row gap-4"> - <div className="flex flex-col items-center gap-4"> - <OSIcons.Apple width={64} height={64} /> - <OSIcons.Linux width={64} height={64} /> - <OSIcons.Microsoft width={64} height={64} /> - <OSIcons.AIX width={64} height={64} /> - </div> - <div className="flex flex-col items-center gap-4"> - <InstallMethodIcons.Docker width={64} height={64} /> - <InstallMethodIcons.Homebrew width={64} height={64} /> - <InstallMethodIcons.NVM width={64} height={64} /> - <InstallMethodIcons.Devbox width={64} height={64} /> - <InstallMethodIcons.Volta width={64} height={64} /> - </div> - <div className="flex flex-col items-center gap-4"> - <InstallMethodIcons.Choco width={64} height={64} /> - </div> - </div> - ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/social-logos.stories.tsx b/apps/site/components/__design__/social-logos.stories.tsx deleted file mode 100644 index 0fc12674a0ddc..0000000000000 --- a/apps/site/components/__design__/social-logos.stories.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Bluesky from '@/components/Icons/Social/Bluesky'; -import Discord from '@/components/Icons/Social/Discord'; -import GitHub from '@/components/Icons/Social/GitHub'; -import LinkedIn from '@/components/Icons/Social/LinkedIn'; -import Mastodon from '@/components/Icons/Social/Mastodon'; -import Slack from '@/components/Icons/Social/Slack'; -import Twitter from '@/components/Icons/Social/Twitter'; - -export const SocialMediaLogos: StoryObj = { - render: () => ( - <div className="flex flex-row gap-4"> - <div className="flex flex-col items-center gap-4"> - <GitHub width={64} height={64} /> - <Mastodon width={64} height={64} /> - <LinkedIn width={64} height={64} /> - </div> - <div className="flex flex-col items-center gap-4"> - <Slack width={64} height={64} /> - <Twitter width={64} height={64} /> - <Bluesky width={64} height={64} /> - </div> - <div className="flex flex-col items-center gap-4"> - <Discord width={64} height={64} /> - </div> - </div> - ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/table.stories.tsx b/apps/site/components/__design__/table.stories.tsx deleted file mode 100644 index fb5be4b91ecd4..0000000000000 --- a/apps/site/components/__design__/table.stories.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -export const Table: StoryObj = { - render: () => ( - <main> - <table> - <thead> - <tr> - <th>Column 1</th> - <th>Column 2</th> - <th>Column 3</th> - </tr> - </thead> - <tbody> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - </tbody> - </table> - </main> - ), -}; - -export const HeadlessTable: StoryObj = { - render: () => ( - <main> - <table> - <tbody> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - <tr> - <td>Data 1</td> - <td>Data 2</td> - <td>Data 3</td> - </tr> - </tbody> - </table> - </main> - ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/withAvatarGroup.tsx b/apps/site/components/withAvatarGroup.tsx index dbd8dee55e530..10bbcfc8732fb 100644 --- a/apps/site/components/withAvatarGroup.tsx +++ b/apps/site/components/withAvatarGroup.tsx @@ -1,12 +1,16 @@ +'use client'; + +import AvatarGroup from '@node-core/ui-components/Common/AvatarGroup'; +import Image from 'next/image'; import type { ComponentProps, FC } from 'react'; -import AvatarGroup from '@/components/Common/AvatarGroup'; +import Link from '@/components/Link'; import type { AuthorProps } from '@/types'; import { getAuthors } from '@/util/authorUtils'; type WithAvatarGroupProps = Omit< ComponentProps<typeof AvatarGroup>, - 'avatars' + 'avatars' | 'as' > & AuthorProps; @@ -22,6 +26,8 @@ const WithAvatarGroup: FC<WithAvatarGroupProps> = ({ names: names, clickable: clickable, })} + as={Link} + img={Image} {...props} /> ); diff --git a/apps/site/components/withBadge.tsx b/apps/site/components/withBadge.tsx index b927605692ab1..7b91c84c373c5 100644 --- a/apps/site/components/withBadge.tsx +++ b/apps/site/components/withBadge.tsx @@ -1,6 +1,7 @@ +import Badge from '@node-core/ui-components/Common/Badge'; import type { FC } from 'react'; -import Badge from '@/components/Common/Badge'; +import Link from '@/components/Link'; import { siteConfig } from '@/next.json.mjs'; import { dateIsBetween } from '@/util/dateUtils'; @@ -9,7 +10,12 @@ const WithBadge: FC<{ section: string }> = ({ section }) => { if (badge && dateIsBetween(badge.startDate, badge.endDate)) { return ( - <Badge badgeText={badge.title} kind={badge.kind} href={badge.link}> + <Badge + as={Link} + badgeText={badge.title} + kind={badge.kind} + href={badge.link} + > {badge.text} </Badge> ); diff --git a/apps/site/components/withBanner.tsx b/apps/site/components/withBanner.tsx index af571479ad8f4..32061a10c7284 100644 --- a/apps/site/components/withBanner.tsx +++ b/apps/site/components/withBanner.tsx @@ -1,6 +1,8 @@ +import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; +import Banner from '@node-core/ui-components/Common/Banner'; import type { FC } from 'react'; -import Banner from '@/components/Common/Banner'; +import Link from '@/components/Link'; import { siteConfig } from '@/next.json.mjs'; import { dateIsBetween } from '@/util/dateUtils'; @@ -9,8 +11,13 @@ const WithBanner: FC<{ section: string }> = ({ section }) => { if (banner && dateIsBetween(banner.startDate, banner.endDate)) { return ( - <Banner type={banner.type} link={banner.link}> - {banner.text} + <Banner type={banner.type}> + {banner.link ? ( + <Link href={banner.link}>{banner.text}</Link> + ) : ( + banner.text + )} + {banner.link && <ArrowUpRightIcon />} </Banner> ); } diff --git a/apps/site/components/withBlogCategories.tsx b/apps/site/components/withBlogCategories.tsx index f061e93013187..3c4c4cbcb8d5b 100644 --- a/apps/site/components/withBlogCategories.tsx +++ b/apps/site/components/withBlogCategories.tsx @@ -1,7 +1,7 @@ import { useTranslations } from 'next-intl'; import type { ComponentProps, FC } from 'react'; -import BlogPostCard from '@/components/Common/BlogPostCard'; +import BlogPostCard from '@/components/Blog/BlogPostCard'; import LinkTabs from '@/components/Common/LinkTabs'; import Pagination from '@/components/Common/Pagination'; import type { BlogPostsRSC } from '@/types'; diff --git a/apps/site/components/withBreadcrumbs.tsx b/apps/site/components/withBreadcrumbs.tsx index 7d4c166c11350..2a740173061ae 100644 --- a/apps/site/components/withBreadcrumbs.tsx +++ b/apps/site/components/withBreadcrumbs.tsx @@ -1,9 +1,11 @@ 'use client'; +import type { BreadcrumbLink } from '@node-core/ui-components/Common/Breadcrumbs'; +import Breadcrumbs from '@node-core/ui-components/Common/Breadcrumbs'; +import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import type { BreadcrumbLink } from '@/components/Common/Breadcrumbs'; -import Breadcrumbs from '@/components/Common/Breadcrumbs'; +import Link from '@/components/Link'; import { useClientContext, useMediaQuery, useSiteNavigation } from '@/hooks'; import type { NavigationKeys } from '@/types'; import { dashToCamelCase } from '@/util/stringUtils'; @@ -14,6 +16,7 @@ type WithBreadcrumbsProps = { const WithBreadcrumbs: FC<WithBreadcrumbsProps> = ({ navKeys = [] }) => { const { getSideNavigation } = useSiteNavigation(); + const t = useTranslations(); const { pathname } = useClientContext(); const isMobileScreen = useMediaQuery('(max-width: 639px)'); @@ -57,7 +60,14 @@ const WithBreadcrumbs: FC<WithBreadcrumbsProps> = ({ navKeys = [] }) => { }, [] as Array<BreadcrumbLink>); }; - return <Breadcrumbs links={getBreadrumbs()} maxLength={maxLength} />; + return ( + <Breadcrumbs + links={getBreadrumbs()} + maxLength={maxLength} + as={Link} + homeLinkAriaLabel={t('components.common.breadcrumbs.navigateToHome')} + /> + ); }; export default WithBreadcrumbs; diff --git a/apps/site/components/withFooter.tsx b/apps/site/components/withFooter.tsx index abdc64b295f53..53b9c96592b54 100644 --- a/apps/site/components/withFooter.tsx +++ b/apps/site/components/withFooter.tsx @@ -1,7 +1,28 @@ +'use client'; + +import Footer from '@node-core/ui-components/Containers/Footer'; +import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import Footer from '@/components/Containers/Footer'; +import Link from '@/components/Link'; +import { usePathname } from '@/navigation.mjs'; +import { siteNavigation } from '@/next.json.mjs'; + +const WithFooter: FC = () => { + const t = useTranslations(); + const pathname = usePathname(); + + const { socialLinks, footerLinks } = siteNavigation; + const updatedFooterLinks = footerLinks + .slice(0, -1) + .map(link => ({ ...link, text: t(link.text) })); + + // Add OpenJS link + updatedFooterLinks.push(footerLinks.at(-1)!); + + const navigation = { socialLinks, footerLinks: updatedFooterLinks }; -const WithFooter: FC = () => <Footer />; + return <Footer navigation={navigation} as={Link} pathname={pathname} />; +}; export default WithFooter; diff --git a/apps/site/components/withMetaBar.tsx b/apps/site/components/withMetaBar.tsx index fab6acf616060..133568e2793a8 100644 --- a/apps/site/components/withMetaBar.tsx +++ b/apps/site/components/withMetaBar.tsx @@ -1,10 +1,10 @@ 'use client'; +import MetaBar from '@node-core/ui-components/Containers/MetaBar'; +import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub'; import { useFormatter, useLocale, useTranslations } from 'next-intl'; import type { FC } from 'react'; -import MetaBar from '@/components/Containers/MetaBar'; -import GitHub from '@/components/Icons/Social/GitHub'; import Link from '@/components/Link'; import WithAvatarGroup from '@/components/withAvatarGroup'; import { useClientContext } from '@/hooks/react-client'; @@ -42,21 +42,24 @@ const WithMetaBar: FC = () => { return ( <MetaBar + heading={t('components.metabar.tableOfContents')} + as={Link} items={{ - 'components.metabar.lastUpdated': lastUpdated, - 'components.metabar.readingTime': readingTimeText, + [t('components.metabar.lastUpdated')]: lastUpdated, + [t('components.metabar.readingTime')]: readingTimeText, ...(usernames.length && { - [`components.metabar.${usernames.length > 1 ? 'authors' : 'author'}`]: - ( - <WithAvatarGroup - usernames={usernames} - limit={isMobileResolution ? 7 : isTabletResolution ? 5 : 9} - /> - ), + [t( + `components.metabar.${usernames.length > 1 ? 'authors' : 'author'}` + )]: ( + <WithAvatarGroup + usernames={usernames} + limit={isMobileResolution ? 7 : isTabletResolution ? 5 : 9} + /> + ), }), - 'components.metabar.contribute': ( + [t('components.metabar.contribute')]: ( <> - <GitHub className="fill-neutral-700 dark:fill-neutral-100" /> + <GitHubIcon className="fill-neutral-700 dark:fill-neutral-100" /> <Link href={ locale === defaultLocale.code diff --git a/apps/site/components/withNavBar.tsx b/apps/site/components/withNavBar.tsx index 21b39f48f44d3..9850ad9f31c8d 100644 --- a/apps/site/components/withNavBar.tsx +++ b/apps/site/components/withNavBar.tsx @@ -1,43 +1,86 @@ 'use client'; -import { useLocale } from 'next-intl'; +import LanguageDropdown from '@node-core/ui-components/Common/LanguageDropDown'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; +import ThemeToggle from '@node-core/ui-components/Common/ThemeToggle'; +import NavBar from '@node-core/ui-components/Containers/NavBar'; +// TODO(@AvivKeller): I don't like that we are importing styles from another module +import styles from '@node-core/ui-components/Containers/NavBar/index.module.css'; +import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub'; +import type { SimpleLocaleConfig } from '@node-core/ui-components/types'; +import dynamic from 'next/dynamic'; +import { useLocale, useTranslations } from 'next-intl'; import { useTheme } from 'next-themes'; import type { FC } from 'react'; -import NavBar from '@/components/Containers/NavBar'; +import Link from '@/components/Link'; import WithBanner from '@/components/withBanner'; +import WithNodejsLogo from '@/components/withNodejsLogo'; import { useSiteNavigation } from '@/hooks'; import { useRouter, usePathname } from '@/navigation.mjs'; import { availableLocales } from '@/next.locales.mjs'; +const SearchButton = dynamic(() => import('@/components/Common/Search'), { + ssr: false, + loading: () => ( + <Skeleton className={styles.searchButtonSkeleton} loading={true} /> + ), +}); + const WithNavBar: FC = () => { const { navigationItems } = useSiteNavigation(); const { resolvedTheme, setTheme } = useTheme(); const { replace } = useRouter(); const pathname = usePathname(); + const t = useTranslations(); const locale = useLocale(); const toggleCurrentTheme = () => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark'); + const changeLanguage = (locale: SimpleLocaleConfig) => + replace(pathname!, { locale: locale.code }); + return ( <div> <WithBanner section="index" /> <NavBar - onThemeTogglerClick={toggleCurrentTheme} - languages={{ - currentLanguage: locale, - availableLanguages: availableLocales, - onChange: locale => replace(pathname!, { locale: locale.code }), - }} navItems={navigationItems.map(([, { label, link, target }]) => ({ link, text: label, target, }))} - /> + pathname={pathname} + as={Link} + Logo={WithNodejsLogo} + sidebarItemTogglerAriaLabel={t( + 'components.containers.navBar.controls.toggle' + )} + > + <SearchButton /> + + <ThemeToggle + onClick={toggleCurrentTheme} + ariaLabel={t('components.common.themeToggle.label')} + /> + + <LanguageDropdown + onChange={changeLanguage} + availableLanguages={availableLocales} + currentLanguage={locale} + ariaLabel={t('components.common.languageDropdown.label')} + /> + + <Link + href="https://github.com/nodejs/node" + aria-label="Node.js Github" + className={styles.ghIconWrapper} + > + <GitHubIcon /> + </Link> + </NavBar> </div> ); }; diff --git a/apps/site/components/withNodejsLogo.tsx b/apps/site/components/withNodejsLogo.tsx index d6c270d50641f..0b76506a77904 100644 --- a/apps/site/components/withNodejsLogo.tsx +++ b/apps/site/components/withNodejsLogo.tsx @@ -1,10 +1,17 @@ +import NodejsLogo from '@node-core/ui-components/Common/NodejsLogo'; +import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import NodejsLogo from '@/components/Common/NodejsLogo'; import { siteConfig } from '@/next.json.mjs'; -const WithNodejsLogo: FC = () => ( - <NodejsLogo variant={siteConfig.logoVariant} /> -); +const WithNodejsLogo: FC = () => { + const t = useTranslations(); + return ( + <NodejsLogo + variant={siteConfig.logoVariant} + ariaLabel={t('layouts.logo')} + /> + ); +}; export default WithNodejsLogo; diff --git a/apps/site/components/withProgressionSidebar.tsx b/apps/site/components/withProgressionSidebar.tsx index b71995a37c808..ab7397d4a1259 100644 --- a/apps/site/components/withProgressionSidebar.tsx +++ b/apps/site/components/withProgressionSidebar.tsx @@ -1,8 +1,12 @@ -import type { RichTranslationValues } from 'next-intl'; +'use client'; + +import ProgressionSidebar from '@node-core/ui-components/Common/ProgressionSidebar'; +import { usePathname } from 'next/navigation'; +import { useTranslations, type RichTranslationValues } from 'next-intl'; import type { FC } from 'react'; -import ProgressionSidebar from '@/components/Common/ProgressionSidebar'; -import { useSiteNavigation } from '@/hooks'; +import { useSiteNavigation } from '@/hooks/server'; +import { useRouter } from '@/navigation.mjs'; import type { NavigationKeys } from '@/types'; type WithProgressionSidebarProps = { @@ -15,7 +19,9 @@ const WithProgressionSidebar: FC<WithProgressionSidebarProps> = ({ context, }) => { const { getSideNavigation } = useSiteNavigation(); - + const pathname = usePathname(); + const t = useTranslations(); + const { push } = useRouter(); const [[, sidebarNavigation]] = getSideNavigation([navKey], context); const mappedProgressionSidebarItems = sidebarNavigation.items.map( @@ -25,7 +31,14 @@ const WithProgressionSidebar: FC<WithProgressionSidebarProps> = ({ }) ); - return <ProgressionSidebar groups={mappedProgressionSidebarItems} />; + return ( + <ProgressionSidebar + groups={mappedProgressionSidebarItems} + pathname={pathname!} + title={t('components.common.sidebar.title')} + onSelect={push} + /> + ); }; export default WithProgressionSidebar; diff --git a/apps/site/components/withRouterSelect.tsx b/apps/site/components/withRouterSelect.tsx index f0efadac95640..7c526a8f4011c 100644 --- a/apps/site/components/withRouterSelect.tsx +++ b/apps/site/components/withRouterSelect.tsx @@ -1,8 +1,8 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import type { ComponentProps, FC } from 'react'; -import Select from '@/components/Common/Select'; import { useRouter } from '@/navigation.mjs'; type WithSidebarSelectProps = Pick< diff --git a/apps/site/components/withSidebar.tsx b/apps/site/components/withSidebar.tsx index 8f358e450ecdf..83645ecda14b7 100644 --- a/apps/site/components/withSidebar.tsx +++ b/apps/site/components/withSidebar.tsx @@ -1,8 +1,12 @@ -import type { RichTranslationValues } from 'next-intl'; +'use client'; + +import Sidebar from '@node-core/ui-components/Containers/Sidebar'; +import { usePathname } from 'next/navigation'; +import { useTranslations, type RichTranslationValues } from 'next-intl'; import type { FC } from 'react'; -import Sidebar from '@/components/Containers/Sidebar'; import { useSiteNavigation } from '@/hooks/server'; +import { useRouter } from '@/navigation.mjs'; import type { NavigationKeys } from '@/types'; type WithSidebarProps = { @@ -12,6 +16,9 @@ type WithSidebarProps = { const WithSidebar: FC<WithSidebarProps> = ({ navKeys, context }) => { const { getSideNavigation } = useSiteNavigation(); + const pathname = usePathname()!; + const t = useTranslations(); + const { push } = useRouter(); const mappedSidebarItems = getSideNavigation(navKeys, context).map( ([, { label, items }]) => ({ @@ -20,7 +27,14 @@ const WithSidebar: FC<WithSidebarProps> = ({ navKeys, context }) => { }) ); - return <Sidebar groups={mappedSidebarItems} />; + return ( + <Sidebar + groups={mappedSidebarItems} + pathname={pathname} + title={t('components.common.sidebar.title')} + onSelect={value => push(value)} + /> + ); }; export default WithSidebar; diff --git a/apps/site/eslint.config.js b/apps/site/eslint.config.js index 8c682bd3def2c..dbeafd7b881d1 100644 --- a/apps/site/eslint.config.js +++ b/apps/site/eslint.config.js @@ -2,7 +2,6 @@ import { FlatCompat } from '@eslint/eslintrc'; import importX from 'eslint-plugin-import-x'; import * as mdx from 'eslint-plugin-mdx'; import react from 'eslint-plugin-react'; -import storybook from 'eslint-plugin-storybook'; import tseslint from 'typescript-eslint'; // eslint-disable-next-line no-relative-import-paths/no-relative-import-paths @@ -81,13 +80,9 @@ export default tseslint.config( }, }, { - files: ['.storybook/**', '**/*.mjs', '**/*.test.*'], + files: ['**/*.mjs', '**/*.test.*'], rules: { 'no-relative-import-paths/no-relative-import-paths': 'off', }, - }, - { - files: ['components/**/*.stories.tsx'], - extends: [...storybook.configs['flat/recommended']], } ); diff --git a/apps/site/layouts/GlowingBackdrop.tsx b/apps/site/layouts/GlowingBackdrop.tsx index a291865f640d8..491eb03b0bc7a 100644 --- a/apps/site/layouts/GlowingBackdrop.tsx +++ b/apps/site/layouts/GlowingBackdrop.tsx @@ -1,7 +1,7 @@ +import GlowingBackdrop from '@node-core/ui-components/Common/GlowingBackdrop'; import classNames from 'classnames'; import type { FC, PropsWithChildren } from 'react'; -import GlowingBackdrop from '@/components/Common/GlowingBackdrop'; import WithFooter from '@/components/withFooter'; import WithNavBar from '@/components/withNavBar'; diff --git a/apps/site/layouts/Post.tsx b/apps/site/layouts/Post.tsx index 767cab456dc92..57ee76f80347d 100644 --- a/apps/site/layouts/Post.tsx +++ b/apps/site/layouts/Post.tsx @@ -1,6 +1,6 @@ +import Preview from '@node-core/ui-components/Common/Preview'; import type { FC, PropsWithChildren } from 'react'; -import Preview from '@/components/Common/Preview'; import WithAvatarGroup from '@/components/withAvatarGroup'; import WithBlogCrossLinks from '@/components/withBlogCrossLinks'; import WithFooter from '@/components/withFooter'; diff --git a/apps/site/next.mdx.use.client.mjs b/apps/site/next.mdx.use.client.mjs index 64584887b2a7a..ca3f20882f805 100644 --- a/apps/site/next.mdx.use.client.mjs +++ b/apps/site/next.mdx.use.client.mjs @@ -1,6 +1,7 @@ 'use strict'; -import Blockquote from './components/Common/Blockquote'; +import Blockquote from '@node-core/ui-components/Common/Blockquote'; + import Button from './components/Common/Button'; import DownloadButton from './components/Downloads/DownloadButton'; import DownloadLink from './components/Downloads/DownloadLink'; diff --git a/apps/site/package.json b/apps/site/package.json index caa3a0d316248..f7cb279653141 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -16,8 +16,6 @@ "lint": "turbo run lint:md lint:snippets lint:js lint:css", "lint:fix": "turbo run lint:md lint:js lint:css --no-cache -- --fix", "sync-orama": "node ./scripts/orama-search/sync-orama-cloud.mjs", - "storybook": "cross-env NODE_NO_WARNINGS=1 storybook dev -p 6006 --no-open", - "storybook:build": "cross-env NODE_NO_WARNINGS=1 storybook build --quiet --webpack-stats-json", "test:unit": "cross-env NODE_NO_WARNINGS=1 jest", "test:unit:watch": "npm run test:unit -- --watch", "test": "turbo test:unit" @@ -25,22 +23,15 @@ "dependencies": { "@heroicons/react": "~2.2.0", "@mdx-js/mdx": "^3.1.0", + "@node-core/ui-components": "*", "@node-core/website-i18n": "*", "@nodevu/core": "0.3.0", "@opentelemetry/api": "1.9.0", "@orama/react-components": "^0.5.1", "@oramacloud/client": "^2.1.4", - "@radix-ui/react-accessible-icon": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.6", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-scroll-area": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", - "@radix-ui/react-tooltip": "^1.1.8", - "@savvywombat/tailwindcss-grid-areas": "~4.0.0", - "@tailwindcss/container-queries": "~0.1.1", "@vcarl/remark-headings": "~0.1.0", "@vercel/analytics": "~1.5.0", "@vercel/otel": "~1.10.1", @@ -48,7 +39,6 @@ "autoprefixer": "~10.4.20", "classnames": "~2.5.1", "cross-env": "7.0.3", - "dedent": "1.5.3", "feed": "~4.2.2", "github-slugger": "~2.0.0", "glob": "~11.0.1", @@ -79,13 +69,6 @@ "devDependencies": { "@eslint/compat": "~1.2.7", "@next/eslint-plugin-next": "15.2.0", - "@storybook/addon-controls": "^8.6.3", - "@storybook/addon-interactions": "^8.6.3", - "@storybook/addon-styling-webpack": "^1.0.1", - "@storybook/addon-themes": "^8.6.3", - "@storybook/addon-viewport": "^8.6.3", - "@storybook/addon-webpack5-compiler-swc": "^2.1.0", - "@storybook/react-webpack5": "^8.6.3", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.2.0", "@testing-library/user-event": "~14.6.1", @@ -96,14 +79,12 @@ "eslint-plugin-mdx": "~3.1.5", "eslint-plugin-react": "~7.37.4", "eslint-plugin-react-hooks": "5.2.0", - "eslint-plugin-storybook": "~0.11.3", "handlebars": "4.7.8", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jest-junit": "16.0.0", "remark-frontmatter": "5.0.0", "remark-preset-lint-node": "5.1.2", - "storybook": "^8.6.3", "stylelint": "16.15.0", "stylelint-config-standard": "37.0.0", "stylelint-order": "6.0.4", diff --git a/apps/site/providers/notificationProvider.tsx b/apps/site/providers/notificationProvider.tsx index 5b7e9590950f0..cd07fe6c1e029 100644 --- a/apps/site/providers/notificationProvider.tsx +++ b/apps/site/providers/notificationProvider.tsx @@ -1,3 +1,4 @@ +import Notification from '@node-core/ui-components/Common/Notification'; import * as Toast from '@radix-ui/react-toast'; import type { Dispatch, @@ -8,8 +9,6 @@ import type { } from 'react'; import { createContext, useEffect, useState } from 'react'; -import Notification from '@/components/Common/Notification'; - type NotificationContextType = { message: string | ReactNode; duration: number; diff --git a/apps/site/tailwind.config.ts b/apps/site/tailwind.config.ts index 49e90dc0ac42a..30b7baf1d0bf9 100644 --- a/apps/site/tailwind.config.ts +++ b/apps/site/tailwind.config.ts @@ -1,186 +1 @@ -import type { Config } from 'tailwindcss'; -import plugin from 'tailwindcss/plugin'; - -export default { - content: [ - './pages/**/*.{tsx,mdx}', - './components/**/*.tsx', - './providers/**/*.tsx', - './layouts/**/*.tsx', - './.storybook/preview.tsx', - './.storybook/main.ts', - './app/**/*.tsx', - ], - theme: { - colors: { - green: { - 100: '#EDF2EB', - 200: '#C5E5B4', - 300: '#99CC7D', - 400: '#84BA64', - 500: '#5FA04E', - 600: '#417E38', - 700: '#2C682C', - 800: '#2C682C', - 900: '#1A3F1D', - }, - neutral: { - 100: '#F6F7F9', - 200: '#E9EDF0', - 300: '#D9E1E4', - 400: '#CBD4D9', - 500: '#B1BCC2', - 600: '#929FA5', - 700: '#6E7B83', - 800: '#556066', - 900: '#2C3437', - 950: '#0D121C', - }, - danger: { - 100: '#FBF1F0', - 200: '#FAD3D4', - 300: '#FAB6B7', - 400: '#FA8E8E', - 500: '#F65354', - 600: '#DE1A1B', - 700: '#B80C0C', - 800: '#900E0E', - 900: '#661514', - }, - warning: { - 100: '#FDF3E7', - 200: '#FAD9B0', - 300: '#F5BC75', - 400: '#E99C40', - 500: '#D07912', - 600: '#AE5F00', - 700: '#8B4D04', - 800: '#683D08', - 900: '#4D2F0B', - }, - info: { - 100: '#E9F4FA', - 200: '#BCE6FC', - 300: '#8ED4F8', - 400: '#52BAED', - 500: '#229AD6', - 600: '#0C7BB3', - 700: '#066291', - 800: '#074D71', - 900: '#0A3953', - }, - accent1: { - 100: '#F7F1FB', - 200: '#EAD9FB', - 300: '#DBBDF9', - 400: '#C79BF2', - 500: '#AF74E8', - 600: '#9756D6', - 700: '#7D3CBE', - 800: '#642B9E', - 900: '#361B52', - }, - accent2: { - 100: '#FBF0F4', - 200: '#FBD4E6', - 300: '#FBB4D2', - 400: '#F68BB7', - 500: '#ED5393', - 600: '#D6246E', - 700: '#B01356', - 800: '#8B1245', - 900: '#411526', - }, - pulse: { - 100: '#0000330F', - 200: '#00002D17', - 300: '#DDEAF814', - 400: '#D3EDF81D', - }, - white: '#FFFFFF', - transparent: 'transparent', - shadow: '#101828', - inherit: 'inherit', - }, - fontSize: { - xs: ['0.75rem', '1rem'], - sm: ['0.875rem', '1.25rem'], - base: ['1rem', '1.5rem'], - lg: ['1.125rem', '1.75rem'], - xl: ['1.25rem', '1.875rem'], - '2xl': ['1.5rem', '2rem'], - '3xl': ['1.875rem', '2.25rem'], - '4xl': ['2.25rem', '2.5rem'], - '5xl': ['3rem', '3rem'], - '6xl': ['3.75rem', '3.75rem'], - '7xl': ['4.5rem', '4.5rem'], - }, - fontWeight: { - regular: '400', - medium: '500', - semibold: '600', - bold: '700', - }, - fontFamily: { - 'open-sans': ['var(--font-open-sans)'], - 'ibm-plex-mono': ['var(--font-ibm-plex-mono)'], - }, - extend: { - screens: { xs: '670px' }, - backgroundImage: { - 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', - 'gradient-subtle': - 'linear-gradient(180deg, theme(colors.neutral.100 / 50%) 0%, theme(colors.neutral.100 / 0%) 48.32%)', - 'gradient-subtle-dark': - 'linear-gradient(180deg, theme(colors.neutral.900 / 50%) 0%, theme(colors.neutral.900 / 0%) 48.32%)', - 'gradient-subtle-gray': - 'linear-gradient(180deg, theme(colors.neutral.900) 0%, theme(colors.neutral.900 / 80%) 100%)', - 'gradient-subtle-white': - 'linear-gradient(180deg, theme(colors.white) 0%, theme(colors.white / 80%) 100%)', - 'gradient-glow-backdrop': - 'radial-gradient(8em circle at calc(50%) 10px, theme(colors.green.500), transparent 30%)', - }, - boxShadow: { - xs: '0px 1px 2px 0px theme(colors.shadow / 5%)', - lg: '0px 4px 6px -2px theme(colors.shadow / 3%), 0px 12px 16px -4px theme(colors.shadow / 8%)', - }, - spacing: { '4.5': '1.125rem', '18': '4.5rem' }, - aria: { current: 'current="page"' }, - maxWidth: { '8xl': '95rem' }, - animation: { - surf: 'surf 1s infinite ease-in-out', - pulse: 'pulse 500ms infinite alternate-reverse', - 'pulse-dark': 'pulse-dark 500ms infinite alternate-reverse', - }, - keyframes: { - surf: { - '0%': { transform: 'translate(0, 0)' }, - '25%': { transform: 'translate(0, 6px)' }, - '50%': { transform: 'translate(0, -6px)' }, - '75%': { transform: 'translate(0, 3px)' }, - '100%': { transform: 'translate(0, 0)' }, - }, - pulse: { - from: { background: 'theme("colors.pulse.100")' }, - to: { background: 'theme("colors.pulse.200")' }, - }, - 'pulse-dark': { - from: { background: 'theme("colors.pulse.300")' }, - to: { background: 'theme("colors.pulse.400")' }, - }, - }, - }, - }, - darkMode: ['selector', '[data-theme="dark"]'], - plugins: [ - require('@savvywombat/tailwindcss-grid-areas'), - require('@tailwindcss/container-queries'), - plugin(function ({ addUtilities }) { - addUtilities({ - '.scrollbar-thin': { - 'scrollbar-width': 'thin', - }, - }); - }), - ], -} satisfies Config; +export { default } from '@node-core/ui-components/tailwind.config'; diff --git a/apps/site/tsconfig.json b/apps/site/tsconfig.json index 3aee2c1724e0e..e65aab6fc99c8 100644 --- a/apps/site/tsconfig.json +++ b/apps/site/tsconfig.json @@ -24,9 +24,7 @@ "**/*.mdx", "**/*.ts", "**/*.tsx", - ".next/types/**/*.ts", - ".storybook/**/*.ts", - ".storybook/**/*.tsx" + ".next/types/**/*.ts" ], "exclude": ["node_modules", ".next"] } diff --git a/apps/site/turbo.json b/apps/site/turbo.json index 3e6e21152528f..4ebfd0d562679 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -100,7 +100,7 @@ "inputs": [ "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", "{next-data,scripts,i18n}/**/*.{mjs,json}", - "{.storybook,public}/**/*.{ts,js,json}", + "public/**/*.{ts,js,json}", "*.{json,ts,tsx}" ] }, @@ -122,21 +122,6 @@ "format": { "cache": false }, - "storybook": { - "cache": false, - "persistent": true - }, - "storybook:build": { - "inputs": [ - "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx}", - "{app,components,layouts,pages,styles}/**/*.css", - "{next-data,scripts,i18n}/**/*.{mjs,json}", - "{.storybook,public}/**/*.{ts,js,css,json}", - "{app,pages}/**/*.{mdx,md}", - "*.{md,mdx,json,ts,tsx,mjs,yml}" - ], - "outputs": ["storybook-static/**"] - }, "test:unit": { "inputs": [ "{app,components,hooks,i18n,layouts,middlewares,pages,providers,types,util}/**/*.{ts,tsx,mjs}", diff --git a/apps/site/util/downloadUtils.tsx b/apps/site/util/downloadUtils.tsx index 0c537fb0f69fb..e61a0ff004dac 100644 --- a/apps/site/util/downloadUtils.tsx +++ b/apps/site/util/downloadUtils.tsx @@ -1,9 +1,9 @@ +import type { SelectValue } from '@node-core/ui-components/Common/Select'; +import * as InstallMethodIcons from '@node-core/ui-components/Icons/InstallationMethod'; +import * as OSIcons from '@node-core/ui-components/Icons/OperatingSystem'; +import * as PackageManagerIcons from '@node-core/ui-components/Icons/PackageManager'; import satisfies from 'semver/functions/satisfies'; -import type { SelectValue } from '@/components/Common/Select'; -import InstallMethodIcons from '@/components/Icons/InstallationMethod'; -import OSIcons from '@/components/Icons/OperatingSystem'; -import PackageManagerIcons from '@/components/Icons/PackageManager'; import type { NodeReleaseStatus } from '@/types'; import type * as Types from '@/types/release'; import type { UserOS, UserPlatform } from '@/types/userOS'; diff --git a/package-lock.json b/package-lock.json index 4b1a7391df476..dbf7632a32eba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,22 +37,15 @@ "dependencies": { "@heroicons/react": "~2.2.0", "@mdx-js/mdx": "^3.1.0", + "@node-core/ui-components": "*", "@node-core/website-i18n": "*", "@nodevu/core": "0.3.0", "@opentelemetry/api": "1.9.0", "@orama/react-components": "^0.5.1", "@oramacloud/client": "^2.1.4", - "@radix-ui/react-accessible-icon": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.6", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-scroll-area": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", - "@radix-ui/react-tooltip": "^1.1.8", - "@savvywombat/tailwindcss-grid-areas": "~4.0.0", - "@tailwindcss/container-queries": "~0.1.1", "@vcarl/remark-headings": "~0.1.0", "@vercel/analytics": "~1.5.0", "@vercel/otel": "~1.10.1", @@ -60,7 +53,6 @@ "autoprefixer": "~10.4.20", "classnames": "~2.5.1", "cross-env": "7.0.3", - "dedent": "1.5.3", "feed": "~4.2.2", "github-slugger": "~2.0.0", "glob": "~11.0.1", @@ -91,13 +83,6 @@ "devDependencies": { "@eslint/compat": "~1.2.7", "@next/eslint-plugin-next": "15.2.0", - "@storybook/addon-controls": "^8.6.3", - "@storybook/addon-interactions": "^8.6.3", - "@storybook/addon-styling-webpack": "^1.0.1", - "@storybook/addon-themes": "^8.6.3", - "@storybook/addon-viewport": "^8.6.3", - "@storybook/addon-webpack5-compiler-swc": "^2.1.0", - "@storybook/react-webpack5": "^8.6.3", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.2.0", "@testing-library/user-event": "~14.6.1", @@ -108,14 +93,12 @@ "eslint-plugin-mdx": "~3.1.5", "eslint-plugin-react": "~7.37.4", "eslint-plugin-react-hooks": "5.2.0", - "eslint-plugin-storybook": "~0.11.3", "handlebars": "4.7.8", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jest-junit": "16.0.0", "remark-frontmatter": "5.0.0", "remark-preset-lint-node": "5.1.2", - "storybook": "^8.6.3", "stylelint": "16.15.0", "stylelint-config-standard": "37.0.0", "stylelint-order": "6.0.4", @@ -125,25 +108,6 @@ "user-agent-data-types": "0.4.2" } }, - "apps/site/node_modules/eslint-plugin-storybook": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.3.tgz", - "integrity": "sha512-gDBnBZiyk4ZG7OMSJRaHBcuJ8TMCXgMIQ3HB/XvtN0SvSio2ZOIeYD3yGj39g/DbyCe/Bg02j/ip9tWlDEs82A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/csf": "^0.1.11", - "@typescript-eslint/utils": "^8.8.1", - "ts-dedent": "^2.2.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "eslint": ">=8", - "typescript": ">=4.8.4 <5.8.0" - } - }, "node_modules/@adobe/css-tools": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", @@ -2540,6 +2504,19 @@ "node": ">=8" } }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -3371,6 +3348,10 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@node-core/ui-components": { + "resolved": "packages/ui-components", + "link": true + }, "node_modules/@node-core/website": { "resolved": "apps/site", "link": true @@ -4139,29 +4120,6 @@ "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, - "node_modules/@radix-ui/react-accessible-icon": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.2.tgz", - "integrity": "sha512-+rnMO0SEfzkcHr93RshkQVpOA26MtGOv4pcS9QUnLg4F8+GDmCJ8c2FEPhPz5e7arf31EzbTqJxFbzg3qen14g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-visually-hidden": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", @@ -5851,6 +5809,24 @@ "tslib": "^2.8.0" } }, + "node_modules/@swc/jest": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.37.tgz", + "integrity": "sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, "node_modules/@swc/types": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.19.tgz", @@ -9301,6 +9277,7 @@ "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -10878,6 +10855,25 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-storybook": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.3.tgz", + "integrity": "sha512-gDBnBZiyk4ZG7OMSJRaHBcuJ8TMCXgMIQ3HB/XvtN0SvSio2ZOIeYD3yGj39g/DbyCe/Bg02j/ip9tWlDEs82A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/csf": "^0.1.11", + "@typescript-eslint/utils": "^8.8.1", + "ts-dedent": "^2.2.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "eslint": ">=8", + "typescript": ">=4.8.4 <5.8.0" + } + }, "node_modules/eslint-scope": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", @@ -12389,6 +12385,13 @@ "node": ">=0.10.0" } }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "dev": true, + "license": "(Apache-2.0 OR MPL-1.1)" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -12896,6 +12899,19 @@ "postcss": "^8.1.0" } }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dev": true, + "license": "MIT", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -16230,6 +16246,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -26412,6 +26435,284 @@ "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } + }, + "packages/ui-components": { + "name": "@node-core/ui-components", + "dependencies": { + "@heroicons/react": "^2.2.0", + "@radix-ui/react-dropdown-menu": "~2.1.6", + "@radix-ui/react-label": "~2.1.2", + "@radix-ui/react-scroll-area": "~1.2.3", + "@radix-ui/react-select": "~2.1.6", + "@radix-ui/react-tabs": "~1.1.3", + "@radix-ui/react-toast": "~1.2.6", + "@radix-ui/react-tooltip": "~1.1.8", + "@savvywombat/tailwindcss-grid-areas": "~4.0.0", + "@tailwindcss/container-queries": "~0.1.1", + "@vcarl/remark-headings": "~0.1.0", + "classnames": "~2.5.1", + "tailwindcss": "~3.4.17" + }, + "devDependencies": { + "@node-core/website-i18n": "*", + "@storybook/addon-controls": "^8.6.3", + "@storybook/addon-interactions": "^8.6.3", + "@storybook/addon-styling-webpack": "^1.0.1", + "@storybook/addon-themes": "^8.6.3", + "@storybook/addon-viewport": "^8.6.3", + "@storybook/addon-webpack5-compiler-swc": "^2.1.0", + "@storybook/react-webpack5": "^8.6.3", + "@swc/jest": "^0.2.37", + "@testing-library/jest-dom": "~6.6.3", + "@testing-library/react": "~16.2.0", + "@testing-library/user-event": "~14.6.1", + "eslint-plugin-react": "~7.37.4", + "eslint-plugin-storybook": "~0.11.3", + "identity-obj-proxy": "^3.0.0", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", + "jest-junit": "16.0.0", + "storybook": "^8.6.3", + "stylelint": "^16.15.0", + "stylelint-config-standard": "^37.0.0", + "stylelint-order": "6.0.4", + "stylelint-selector-bem-pattern": "4.0.1", + "typescript": "~5.8.2", + "typescript-eslint": "~8.26.0" + }, + "engines": { + "node": ">=20" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz", + "integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/type-utils": "8.26.0", + "@typescript-eslint/utils": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/parser": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz", + "integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/typescript-estree": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/scope-manager": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz", + "integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/type-utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz", + "integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.26.0", + "@typescript-eslint/utils": "8.26.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/types": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz", + "integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz", + "integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/typescript-estree": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "packages/ui-components/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz", + "integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/ui-components/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/ui-components/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "packages/ui-components/node_modules/typescript-eslint": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.0.tgz", + "integrity": "sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.26.0", + "@typescript-eslint/parser": "8.26.0", + "@typescript-eslint/utils": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } } } } diff --git a/package.json b/package.json index 9eb1905513cf8..51c2e580cc9c0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "scripts": { "dev": "turbo run dev", "build": "turbo run build", + "storybook": "turbo run storybook", "storybook:build": "turbo run storybook:build", "start": "turbo run start", "check-types": "turbo check-types", diff --git a/packages/ui-components/.postcssrc.json b/packages/ui-components/.postcssrc.json new file mode 100644 index 0000000000000..f48fb87dce6e0 --- /dev/null +++ b/packages/ui-components/.postcssrc.json @@ -0,0 +1,11 @@ +{ + "plugins": { + "postcss-mixins": {}, + "postcss-simple-vars": {}, + "postcss-calc": {}, + "postcss-import": {}, + "tailwindcss/nesting": {}, + "tailwindcss": {}, + "autoprefixer": {} + } +} diff --git a/apps/site/.storybook/constants.ts b/packages/ui-components/.storybook/constants.ts similarity index 100% rename from apps/site/.storybook/constants.ts rename to packages/ui-components/.storybook/constants.ts diff --git a/packages/ui-components/.storybook/main.ts b/packages/ui-components/.storybook/main.ts new file mode 100644 index 0000000000000..a7642fb25a7e4 --- /dev/null +++ b/packages/ui-components/.storybook/main.ts @@ -0,0 +1,34 @@ +import type { StorybookConfig } from '@storybook/react-webpack5'; + +const config: StorybookConfig = { + stories: ['../**/*.stories.tsx'], + logLevel: 'error', + typescript: { reactDocgen: false, check: false }, + core: { disableTelemetry: true, disableWhatsNewNotifications: true }, + framework: '@storybook/react-webpack5', + swc: () => ({ jsc: { transform: { react: { runtime: 'automatic' } } } }), + addons: [ + '@storybook/addon-webpack5-compiler-swc', + '@storybook/addon-controls', + '@storybook/addon-interactions', + '@storybook/addon-themes', + '@storybook/addon-viewport', + { + name: '@storybook/addon-styling-webpack', + options: { + rules: [ + { + test: /\.css$/, + use: [ + 'style-loader', + { loader: 'css-loader', options: { url: false } }, + 'postcss-loader', + ], + }, + ], + }, + }, + ], +}; + +export default config; diff --git a/apps/site/.storybook/preview-head.html b/packages/ui-components/.storybook/preview-head.html similarity index 100% rename from apps/site/.storybook/preview-head.html rename to packages/ui-components/.storybook/preview-head.html diff --git a/apps/site/.storybook/preview.tsx b/packages/ui-components/.storybook/preview.tsx similarity index 50% rename from apps/site/.storybook/preview.tsx rename to packages/ui-components/.storybook/preview.tsx index 1cc9e387dce32..09aae8acad918 100644 --- a/apps/site/.storybook/preview.tsx +++ b/packages/ui-components/.storybook/preview.tsx @@ -1,10 +1,8 @@ -import englishLocale from '@node-core/website-i18n/locales/en.json'; +import * as Toast from '@radix-ui/react-toast'; import { withThemeByDataAttribute } from '@storybook/addon-themes'; import type { Preview, ReactRenderer } from '@storybook/react'; -import { NextIntlClientProvider } from 'next-intl'; -import { STORYBOOK_MODES, STORYBOOK_SIZES } from '@/.storybook/constants'; -import { NotificationProvider } from '@/providers/notificationProvider'; +import { STORYBOOK_MODES, STORYBOOK_SIZES } from './constants'; import '../styles/index.css'; @@ -15,15 +13,10 @@ const preview: Preview = { }, decorators: [ Story => ( - <NextIntlClientProvider - locale="en" - timeZone="Etc/UTC" - messages={englishLocale} - > - <NotificationProvider viewportClassName="absolute top-0 left-0 list-none"> - <Story /> - </NotificationProvider> - </NextIntlClientProvider> + <Toast.Provider> + <Story /> + <Toast.Viewport /> + </Toast.Provider> ), withThemeByDataAttribute<ReactRenderer>({ themes: { light: '', dark: 'dark' }, diff --git a/packages/ui-components/.stylelintignore b/packages/ui-components/.stylelintignore new file mode 100644 index 0000000000000..20687473be0b3 --- /dev/null +++ b/packages/ui-components/.stylelintignore @@ -0,0 +1 @@ +storybook-static diff --git a/packages/ui-components/.stylelintrc.mjs b/packages/ui-components/.stylelintrc.mjs new file mode 100644 index 0000000000000..1858d79f3f0ed --- /dev/null +++ b/packages/ui-components/.stylelintrc.mjs @@ -0,0 +1,46 @@ +// These are all the custom `@` (at) rules that we use within our custom PostCSS plugins +const CUSTOM_AT_RULES = [ + // Tailwind-specific at-rules + 'apply', + 'layer', + 'responsive', + 'screen', + 'tailwind', + 'variants', + // PostCSS-specific at-rules + 'define-mixin', + 'mixin', +]; + +// Enforces certain selectors to be only in camelCase notation +// We use these for id selectors and classname selectors +const ONLY_ALLOW_CAMEL_CASE_SELECTORS = [ + /^(?:[a-z]+(?:[A-Z][a-z]*)*)$/, + { message: s => `Expected '${s}' to be in camelCase` }, +]; + +export default { + extends: ['stylelint-config-standard'], + plugins: ['stylelint-order', 'stylelint-selector-bem-pattern'], + rules: { + // Enforces Element Class Names to be camelCase + 'selector-class-pattern': ONLY_ALLOW_CAMEL_CASE_SELECTORS, + // Enforces Element IDs to be camelCase + 'selector-id-pattern': ONLY_ALLOW_CAMEL_CASE_SELECTORS, + // Allow Tailwind-based CSS Rules + 'at-rule-no-unknown': [true, { ignoreAtRules: CUSTOM_AT_RULES }], + 'at-rule-no-deprecated': [true, { ignoreAtRules: CUSTOM_AT_RULES }], + // Allow the Global CSS Selector + 'selector-pseudo-class-no-unknown': [ + true, + { ignorePseudoClasses: ['global'] }, + ], + // Enforces the order of the CSS properties to be in alphabetical order + 'order/properties-alphabetical-order': true, + 'no-descending-specificity': null, + // Disables the Level-4 Media Queries; Since they're more exotic and less known + 'media-feature-range-notation': 'prefix', + // Adopts the import notation from `postcss-import` + 'import-notation': 'string', + }, +}; diff --git a/apps/site/components/Common/AlertBox/index.module.css b/packages/ui-components/Common/AlertBox/index.module.css similarity index 63% rename from apps/site/components/Common/AlertBox/index.module.css rename to packages/ui-components/Common/AlertBox/index.module.css index c3e31ae888704..ef2a4346dcd00 100644 --- a/apps/site/components/Common/AlertBox/index.module.css +++ b/packages/ui-components/Common/AlertBox/index.module.css @@ -1,19 +1,8 @@ .alertBox { - @apply flex - flex-row - items-center - gap-2 - rounded - px-4 - py-3 - text-sm - text-white; + @apply flex flex-row items-center gap-2 rounded px-4 py-3 text-sm text-white; a { - @apply font-bold - text-white - underline - hover:text-white; + @apply font-bold text-white underline hover:text-white; &:hover { @apply no-underline; @@ -21,9 +10,7 @@ } &.small { - @apply gap-1 - py-2 - text-xs; + @apply gap-1 py-2 text-xs; .title { @apply px-1; @@ -31,13 +18,11 @@ } .title { - @apply rounded-sm - px-1.5; + @apply rounded-sm px-1.5; } svg { - @apply inline - size-5; + @apply inline size-5; } &.info { diff --git a/apps/site/components/Common/AlertBox/index.stories.tsx b/packages/ui-components/Common/AlertBox/index.stories.tsx similarity index 96% rename from apps/site/components/Common/AlertBox/index.stories.tsx rename to packages/ui-components/Common/AlertBox/index.stories.tsx index e5b5340faba61..5329069af7b89 100644 --- a/apps/site/components/Common/AlertBox/index.stories.tsx +++ b/packages/ui-components/Common/AlertBox/index.stories.tsx @@ -1,7 +1,7 @@ import { ExclamationCircleIcon } from '@heroicons/react/24/solid'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import AlertBox from '@/components/Common/AlertBox'; +import AlertBox from '@node-core/ui-components/Common/AlertBox'; type Story = StoryObj<typeof AlertBox>; type Meta = MetaObj<typeof AlertBox>; diff --git a/apps/site/components/Common/AlertBox/index.tsx b/packages/ui-components/Common/AlertBox/index.tsx similarity index 100% rename from apps/site/components/Common/AlertBox/index.tsx rename to packages/ui-components/Common/AlertBox/index.tsx diff --git a/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css b/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css new file mode 100644 index 0000000000000..eb32975fd87ef --- /dev/null +++ b/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css @@ -0,0 +1,19 @@ +.item { + @apply xs:max-h-10 xs:max-w-10 flex h-full max-h-12 w-full max-w-12 items-center justify-center rounded-full border-2 border-transparent bg-neutral-100 object-cover text-xs text-neutral-800 dark:bg-neutral-900 dark:text-neutral-300; +} + +.avatar { + @apply size-8; + + .wrapper { + @apply max-xs:block max-xs:py-0; + } +} + +.small { + @apply xs:size-8 size-10; +} + +.medium { + @apply size-10; +} diff --git a/apps/site/components/Common/AvatarGroup/Avatar/index.stories.tsx b/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx similarity index 66% rename from apps/site/components/Common/AvatarGroup/Avatar/index.stories.tsx rename to packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx index e676fc02dc440..677dba7c6c72b 100644 --- a/apps/site/components/Common/AvatarGroup/Avatar/index.stories.tsx +++ b/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx @@ -1,15 +1,14 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Avatar from '@/components/Common/AvatarGroup/Avatar'; -import { getGitHubAvatarUrl } from '@/util/gitHubUtils'; +import Avatar from '@node-core/ui-components/Common/AvatarGroup/Avatar'; type Story = StoryObj<typeof Avatar>; type Meta = MetaObj<typeof Avatar>; export const Default: Story = { args: { - image: getGitHubAvatarUrl('ovflowd'), - nickname: 'ovflowd', + image: 'https://avatars.githubusercontent.com/avivkeller', + nickname: 'avivkeller', }, }; diff --git a/packages/ui-components/Common/AvatarGroup/Avatar/index.tsx b/packages/ui-components/Common/AvatarGroup/Avatar/index.tsx new file mode 100644 index 0000000000000..fcf15fc679599 --- /dev/null +++ b/packages/ui-components/Common/AvatarGroup/Avatar/index.tsx @@ -0,0 +1,76 @@ +import classNames from 'classnames'; +import type { HTMLAttributes, ElementType } from 'react'; +import { forwardRef } from 'react'; + +import type { LinkLike } from '@node-core/ui-components/types'; + +import styles from './index.module.css'; + +export type AvatarProps = { + image?: string; + name?: string; + nickname: string; + fallback?: string; + size?: 'small' | 'medium'; + url?: string; + as?: LinkLike | 'div'; + img?: ElementType | 'img'; +}; + +// @TODO: We temporarily removed the Avatar Radix UI primitive, since it was causing flashing +// during initial load and not being able to render nicely when images are already cached. +// @see https://github.com/radix-ui/primitives/pull/3008 +const Avatar = forwardRef< + HTMLSpanElement, + HTMLAttributes<HTMLSpanElement> & AvatarProps +>( + ( + { + image, + nickname, + name, + fallback, + url, + size = 'small', + as: Component = 'a', + img: Image = 'img', + ...props + }, + ref + ) => { + if (!url) Component = 'div'; + return ( + <span + {...props} + ref={ref} + className={classNames(styles.avatar, styles[size], props.className)} + > + <Component + href={url || undefined} + target={url ? '_blank' : undefined} + className={styles.wrapper} + > + {image && ( + <Image + width={40} + height={40} + loading="lazy" + decoding="async" + src={image} + alt={name || nickname} + className={styles.item} + /> + )} + + {!image && ( + <span className={classNames(styles.item, styles[size])}> + {fallback} + </span> + )} + </Component> + </span> + ); + } +); + +export default Avatar; diff --git a/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css b/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css new file mode 100644 index 0000000000000..51102089fff83 --- /dev/null +++ b/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css @@ -0,0 +1,19 @@ +.overlay { + @apply flex min-w-56 items-center gap-2 p-3; +} + +.user { + @apply grow; +} + +.name { + @apply font-semibold text-neutral-900 dark:text-neutral-300; +} + +.nickname { + @apply font-medium text-neutral-700 dark:text-neutral-500; +} + +.arrow { + @apply w-3 fill-neutral-600 dark:fill-white; +} diff --git a/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx b/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx new file mode 100644 index 0000000000000..a20795a28d455 --- /dev/null +++ b/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import AvatarOverlay from '@node-core/ui-components/Common/AvatarGroup/Overlay'; + +type Story = StoryObj<typeof AvatarOverlay>; +type Meta = MetaObj<typeof AvatarOverlay>; + +const author = { + image: 'https://avatars.githubusercontent.com/avivkeller', + name: 'Aviv Keller', + nickname: 'avivkeller', + fallback: 'AK', +}; + +export const Default: Story = { + args: author, +}; + +export const FallBack: Story = { + args: { + nickname: author.nickname, + fallback: author.fallback, + }, +}; + +export const WithoutName: Story = { + args: { + ...author, + name: undefined, + }, +}; + +export default { component: AvatarOverlay } as Meta; diff --git a/apps/site/components/Common/AvatarGroup/Overlay/index.tsx b/packages/ui-components/Common/AvatarGroup/Overlay/index.tsx similarity index 78% rename from apps/site/components/Common/AvatarGroup/Overlay/index.tsx rename to packages/ui-components/Common/AvatarGroup/Overlay/index.tsx index 0fa95e1978363..9e74a9a34b85c 100644 --- a/apps/site/components/Common/AvatarGroup/Overlay/index.tsx +++ b/packages/ui-components/Common/AvatarGroup/Overlay/index.tsx @@ -1,8 +1,7 @@ import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; import type { ComponentProps, FC } from 'react'; -import Avatar from '@/components/Common/AvatarGroup/Avatar'; -import Link from '@/components/Link'; +import Avatar from '@node-core/ui-components/Common/AvatarGroup/Avatar'; import styles from './index.module.css'; @@ -16,14 +15,17 @@ const AvatarOverlay: FC<AvatarOverlayProps> = ({ nickname, fallback, url, + as: Component = 'a', + img, }) => ( - <Link className={styles.overlay} href={url} target="_blank"> + <Component className={styles.overlay} href={url} target="_blank"> <Avatar image={image} name={name} nickname={nickname} fallback={fallback} size="medium" + img={img} /> <div className={styles.user}> @@ -31,7 +33,7 @@ const AvatarOverlay: FC<AvatarOverlayProps> = ({ {nickname && <div className={styles.nickname}>{nickname}</div>} </div> <ArrowUpRightIcon className={styles.arrow} /> - </Link> + </Component> ); export default AvatarOverlay; diff --git a/apps/site/components/Common/AvatarGroup/__tests__/index.test.mjs b/packages/ui-components/Common/AvatarGroup/__tests__/index.test.mjs similarity index 59% rename from apps/site/components/Common/AvatarGroup/__tests__/index.test.mjs rename to packages/ui-components/Common/AvatarGroup/__tests__/index.test.mjs index ad3986cc3631e..61ae2f643b5bc 100644 --- a/apps/site/components/Common/AvatarGroup/__tests__/index.test.mjs +++ b/packages/ui-components/Common/AvatarGroup/__tests__/index.test.mjs @@ -2,8 +2,6 @@ import { render, fireEvent } from '@testing-library/react'; import AvatarGroup from '../index'; -import { getGitHubAvatarUrl } from '@/util/gitHubUtils'; - const names = [ 'ovflowd', 'bmuenzenmeyer', @@ -11,6 +9,7 @@ const names = [ 'HinataKah0', 'Harkunwar', 'rodion-arr', + 'avivkeller', 'mikeesto', 'bnb', 'benhalverson', @@ -22,25 +21,32 @@ const names = [ ]; const avatars = names.map(name => ({ - image: getGitHubAvatarUrl(name), + image: `https://avatars.githubusercontent.com/${name}`, nickname: name, })); +const limit = 2; +const remaining = names.length - limit; + describe('AvatarGroup', () => { it('renders the AvatarGroup component properly', () => { - const { getByText } = render(<AvatarGroup avatars={avatars} limit={2} />); + const { getByText } = render( + <AvatarGroup avatars={avatars} limit={limit} /> + ); - const showMoreButton = getByText('+12'); + const showMoreButton = getByText(`+${remaining}`); expect(showMoreButton).toBeInTheDocument(); }); it('displays the rest of the avatars when "show more" button is clicked', () => { - const { getByText } = render(<AvatarGroup avatars={avatars} limit={2} />); + const { getByText } = render( + <AvatarGroup avatars={avatars} limit={limit} /> + ); - const showMoreButton = getByText('+12'); + const showMoreButton = getByText(`+${remaining}`); fireEvent.click(showMoreButton); - const hideMoreButton = getByText('-12'); + const hideMoreButton = getByText(`-${remaining}`); expect(hideMoreButton).toBeInTheDocument(); }); }); diff --git a/apps/site/components/Common/AvatarGroup/index.module.css b/packages/ui-components/Common/AvatarGroup/index.module.css similarity index 79% rename from apps/site/components/Common/AvatarGroup/index.module.css rename to packages/ui-components/Common/AvatarGroup/index.module.css index 91766d2b7faed..012397d593eb9 100644 --- a/apps/site/components/Common/AvatarGroup/index.module.css +++ b/packages/ui-components/Common/AvatarGroup/index.module.css @@ -1,7 +1,5 @@ .avatarGroup { - @apply flex - flex-wrap - items-center; + @apply flex flex-wrap items-center; &:not(.expandable) { > span { diff --git a/apps/site/components/Common/AvatarGroup/index.stories.tsx b/packages/ui-components/Common/AvatarGroup/index.stories.tsx similarity index 78% rename from apps/site/components/Common/AvatarGroup/index.stories.tsx rename to packages/ui-components/Common/AvatarGroup/index.stories.tsx index 2a927ac95ba66..928f369286ddb 100644 --- a/apps/site/components/Common/AvatarGroup/index.stories.tsx +++ b/packages/ui-components/Common/AvatarGroup/index.stories.tsx @@ -1,7 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import AvatarGroup from '@/components/Common/AvatarGroup'; -import { getAuthorWithId } from '@/util/authorUtils'; +import AvatarGroup from '@node-core/ui-components/Common/AvatarGroup'; type Story = StoryObj<typeof AvatarGroup>; type Meta = MetaObj<typeof AvatarGroup>; @@ -21,10 +20,15 @@ const names = [ 'Wai-Dung', 'manishprivet', 'araujogui', + 'avivkeller', ]; const defaultProps = { - avatars: getAuthorWithId(names, true), + avatars: names.map(name => ({ + image: `https://avatars.githubusercontent.com/${name}`, + nickname: name, + fallback: 'FB', + })), }; export const Default: Story = { diff --git a/packages/ui-components/Common/AvatarGroup/index.tsx b/packages/ui-components/Common/AvatarGroup/index.tsx new file mode 100644 index 0000000000000..53bc0d6f61a03 --- /dev/null +++ b/packages/ui-components/Common/AvatarGroup/index.tsx @@ -0,0 +1,90 @@ +'use client'; + +import classNames from 'classnames'; +import type { FC, ElementType } from 'react'; +import { useState, useMemo } from 'react'; + +import type { AvatarProps } from '@node-core/ui-components/Common/AvatarGroup/Avatar'; +import Avatar from '@node-core/ui-components/Common/AvatarGroup/Avatar'; +import avatarstyles from '@node-core/ui-components/Common/AvatarGroup/Avatar/index.module.css'; +import AvatarOverlay from '@node-core/ui-components/Common/AvatarGroup/Overlay'; +import Tooltip from '@node-core/ui-components/Common/Tooltip'; +import type { LinkLike } from '@node-core/ui-components/types'; + +import styles from './index.module.css'; + +type AvatarGroupProps = { + avatars: Array<AvatarProps>; + limit?: number; + isExpandable?: boolean; + size?: AvatarProps['size']; + container?: HTMLElement; + as?: LinkLike; + img?: ElementType | 'img'; +}; + +const AvatarGroup: FC<AvatarGroupProps> = ({ + avatars, + limit = 10, + isExpandable = true, + size = 'small', + container, + as, + img, +}) => { + const [showMore, setShowMore] = useState(false); + + const renderAvatars = useMemo( + () => avatars.slice(0, showMore ? avatars.length : limit), + [avatars, limit, showMore] + ); + + const handleShowMoreClick = isExpandable + ? () => setShowMore(prev => !prev) + : undefined; + + return ( + <div + className={classNames(styles.avatarGroup, styles[size], { + [styles.expandable]: avatars.length > limit, + })} + > + {renderAvatars.map(avatar => ( + <Tooltip + key={avatar.nickname} + asChild + container={container} + content={<AvatarOverlay {...avatar} as={as} img={img} />} + > + <Avatar + {...avatar} + size={size} + className={classNames({ + 'cursor-pointer': avatar.url, + 'pointer-events-none': !avatar.url, + })} + as={as} + img={img} + /> + </Tooltip> + ))} + + {avatars.length > limit && ( + <span + onClick={handleShowMoreClick} + className={classNames( + avatarstyles.avatar, + avatarstyles[size], + 'cursor-pointer' + )} + > + <span className={avatarstyles.item}> + {`${showMore ? '-' : '+'}${avatars.length - limit}`} + </span> + </span> + )} + </div> + ); +}; + +export default AvatarGroup; diff --git a/packages/ui-components/Common/Badge/index.module.css b/packages/ui-components/Common/Badge/index.module.css new file mode 100644 index 0000000000000..66c1c33c1845e --- /dev/null +++ b/packages/ui-components/Common/Badge/index.module.css @@ -0,0 +1,63 @@ +.wrapper { + @apply flex w-fit items-center rounded-full border py-1 pl-1 pr-2.5 text-sm font-medium; + + .icon { + @apply size-4; + } + + .badge { + @apply mr-2 rounded-full border px-2.5 py-0.5 text-center text-white; + } + + .message { + @apply mr-1; + } + + &.default { + @apply border-green-200 bg-green-100 dark:border-green-700 dark:bg-neutral-900; + + .icon { + @apply text-green-500 dark:text-green-300; + } + + .badge { + @apply border-green-200 bg-green-600 dark:border-green-600; + } + + .message { + @apply text-green-700 dark:text-green-300; + } + } + + &.error { + @apply border-danger-200 bg-danger-100 dark:border-danger-700 dark:bg-neutral-900; + + .icon { + @apply text-danger-500 dark:text-danger-300; + } + + .badge { + @apply border-danger-200 bg-danger-600 dark:border-danger-600; + } + + .message { + @apply text-danger-700 dark:text-danger-300; + } + } + + &.warning { + @apply border-warning-200 bg-warning-100 dark:border-warning-700 dark:bg-neutral-900; + + .icon { + @apply text-warning-500 dark:text-warning-300; + } + + .badge { + @apply border-warning-200 bg-warning-600 dark:border-warning-600; + } + + .message { + @apply text-warning-700 dark:text-warning-300; + } + } +} diff --git a/apps/site/components/Common/Badge/index.stories.tsx b/packages/ui-components/Common/Badge/index.stories.tsx similarity index 91% rename from apps/site/components/Common/Badge/index.stories.tsx rename to packages/ui-components/Common/Badge/index.stories.tsx index 0b200b997c009..b37dfa5b0a621 100644 --- a/apps/site/components/Common/Badge/index.stories.tsx +++ b/packages/ui-components/Common/Badge/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Badge from '@/components/Common/Badge'; +import Badge from '@node-core/ui-components/Common/Badge'; type Story = StoryObj<typeof Badge>; type Meta = MetaObj<typeof Badge>; diff --git a/apps/site/components/Common/Badge/index.tsx b/packages/ui-components/Common/Badge/index.tsx similarity index 73% rename from apps/site/components/Common/Badge/index.tsx rename to packages/ui-components/Common/Badge/index.tsx index 8463fccf52e62..49590ebefe08e 100644 --- a/apps/site/components/Common/Badge/index.tsx +++ b/packages/ui-components/Common/Badge/index.tsx @@ -1,26 +1,28 @@ import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon'; import type { ComponentProps, FC, PropsWithChildren } from 'react'; -import Link from '@/components/Link'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type BadgeProps = { kind?: 'default' | 'warning' | 'error'; badgeText?: string; -} & ComponentProps<typeof Link>; + as: LinkLike; +} & ComponentProps<LinkLike>; const Badge: FC<PropsWithChildren<BadgeProps>> = ({ kind = 'default', badgeText, children, + as: Component = 'a', ...args }) => ( - <Link className={`${styles.wrapper} ${styles[kind]}`} {...args}> + <Component className={`${styles.wrapper} ${styles[kind]}`} {...args}> {badgeText && <span className={styles.badge}>{badgeText}</span>} <span className={styles.message}>{children}</span> {args.href && <ArrowRightIcon className={styles.icon} />} - </Link> + </Component> ); export default Badge; diff --git a/packages/ui-components/Common/Banner/index.module.css b/packages/ui-components/Common/Banner/index.module.css new file mode 100644 index 0000000000000..2b96322f8b72a --- /dev/null +++ b/packages/ui-components/Common/Banner/index.module.css @@ -0,0 +1,28 @@ +.banner { + @apply flex w-full flex-row items-center justify-center gap-2 px-8 py-3 text-sm; + + &, + a { + @apply text-white dark:text-white; + } + + a { + @apply w-fit underline decoration-white/50; + } + + svg { + @apply size-4 text-white/50; + } +} + +.default { + @apply bg-green-600; +} + +.error { + @apply bg-danger-600; +} + +.warning { + @apply bg-warning-600; +} diff --git a/apps/site/components/Common/Banner/index.stories.tsx b/packages/ui-components/Common/Banner/index.stories.tsx similarity index 61% rename from apps/site/components/Common/Banner/index.stories.tsx rename to packages/ui-components/Common/Banner/index.stories.tsx index cf489c8d983a6..f893f3cab801e 100644 --- a/apps/site/components/Common/Banner/index.stories.tsx +++ b/packages/ui-components/Common/Banner/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Banner from '@/components/Common/Banner'; +import Banner from '@node-core/ui-components/Common/Banner'; type Story = StoryObj<typeof Banner>; type Meta = MetaObj<typeof Banner>; @@ -9,7 +9,6 @@ export const Default: Story = { args: { children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', type: 'default', - link: 'https://github.com/openjs-foundation/summit/issues/360', }, }; @@ -17,7 +16,6 @@ export const Error: Story = { args: { children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', type: 'error', - link: 'https://github.com/nodejs/nodejs.org/issues/4495', }, }; @@ -25,14 +23,6 @@ export const Warning: Story = { args: { children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', type: 'warning', - link: 'https://github.com/nodejs/nodejs.org/issues/4495', - }, -}; - -export const NoLink: Story = { - args: { - children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - type: 'default', }, }; diff --git a/apps/site/components/Common/Banner/index.tsx b/packages/ui-components/Common/Banner/index.tsx similarity index 58% rename from apps/site/components/Common/Banner/index.tsx rename to packages/ui-components/Common/Banner/index.tsx index f72b98e15a79e..0eb3f9faf73ef 100644 --- a/apps/site/components/Common/Banner/index.tsx +++ b/packages/ui-components/Common/Banner/index.tsx @@ -1,23 +1,17 @@ -import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; import type { FC, PropsWithChildren } from 'react'; -import Link from '@/components/Link'; - import styles from './index.module.css'; -type BannerProps = { - link?: string; +export type BannerProps = { type?: 'default' | 'warning' | 'error'; }; const Banner: FC<PropsWithChildren<BannerProps>> = ({ type = 'default', - link, children, }) => ( <div className={`${styles.banner} ${styles[type] || styles.default}`}> - {link ? <Link href={link}>{children}</Link> : children} - {link && <ArrowUpRightIcon />} + {children} </div> ); diff --git a/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs b/packages/ui-components/Common/BaseActiveLink/__tests__/index.test.mjs similarity index 63% rename from apps/site/components/Common/ActiveLink/__tests__/index.test.mjs rename to packages/ui-components/Common/BaseActiveLink/__tests__/index.test.mjs index df11e03aa6f3f..2ce5778f60b63 100644 --- a/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs +++ b/packages/ui-components/Common/BaseActiveLink/__tests__/index.test.mjs @@ -2,11 +2,6 @@ import { render, screen } from '@testing-library/react'; import ActiveLink from '..'; -jest.mock('@/navigation.mjs', () => ({ - ...jest.requireActual('@/navigation.mjs'), - usePathname: jest.fn(), -})); - describe('ActiveLink', () => { it('renders as localized link', () => { render( @@ -43,22 +38,4 @@ describe('ActiveLink', () => { 'link active' ); }); - - it('does not set active class when href base does not match', () => { - const { usePathname } = require('@/navigation.mjs'); - usePathname.mockReturnValue('/not-link/sublink'); - - render( - <ActiveLink - className="link" - activeClassName="active" - allowSubPath={true} - href={'/link/sub-link'} - > - Link - </ActiveLink> - ); - - expect(screen.findByText('Link')).resolves.toHaveAttribute('class', 'link'); - }); }); diff --git a/apps/site/components/Common/ActiveLink/index.tsx b/packages/ui-components/Common/BaseActiveLink/index.tsx similarity index 60% rename from apps/site/components/Common/ActiveLink/index.tsx rename to packages/ui-components/Common/BaseActiveLink/index.tsx index bdf566d87dc71..40d85fd9c0db7 100644 --- a/apps/site/components/Common/ActiveLink/index.tsx +++ b/packages/ui-components/Common/BaseActiveLink/index.tsx @@ -1,26 +1,24 @@ -'use client'; - import classNames from 'classnames'; import type { ComponentProps, FC } from 'react'; -import Link from '@/components/Link'; -import { usePathname } from '@/navigation.mjs'; +import type { LinkLike } from '@node-core/ui-components/types'; -type ActiveLocalizedLinkProps = ComponentProps<typeof Link> & { +export type ActiveLocalizedLinkProps = ComponentProps<LinkLike> & { activeClassName?: string; allowSubPath?: boolean; + pathname?: string; + as?: LinkLike; }; -const ActiveLink: FC<ActiveLocalizedLinkProps> = ({ - children, +const BaseActiveLink: FC<ActiveLocalizedLinkProps> = ({ activeClassName = 'active', allowSubPath = false, className, href = '', + pathname = '/', + as: Component = 'a', ...props }) => { - const pathname = usePathname(); - const finalClassName = classNames(className, { [activeClassName]: allowSubPath ? // When using allowSubPath we want only to check if @@ -30,11 +28,7 @@ const ActiveLink: FC<ActiveLocalizedLinkProps> = ({ : href.toString() === pathname, }); - return ( - <Link className={finalClassName} href={href} {...props}> - {children} - </Link> - ); + return <Component className={finalClassName} href={href} {...props} />; }; -export default ActiveLink; +export default BaseActiveLink; diff --git a/packages/ui-components/Common/BaseButton/index.module.css b/packages/ui-components/Common/BaseButton/index.module.css new file mode 100644 index 0000000000000..83ceac2c94430 --- /dev/null +++ b/packages/ui-components/Common/BaseButton/index.module.css @@ -0,0 +1,87 @@ +.button { + @apply px-4.5 relative inline-flex items-center justify-center gap-2 py-2.5 text-center font-semibold motion-safe:transition-colors; + + svg { + @apply size-5; + } + + &[aria-disabled='true'] { + @apply cursor-not-allowed; + } + + &.small { + @apply px-3 py-2 text-sm; + } + + &.neutral { + @apply rounded bg-neutral-900 text-white dark:text-neutral-200; + + &:hover:not([aria-disabled='true']) { + @apply bg-neutral-800; + } + + &[aria-disabled='true'] { + @apply bg-neutral-900 opacity-50; + } + + &:focus { + @apply bg-neutral-800; + } + } + + &.primary { + @apply rounded border border-green-600 bg-green-600 text-white shadow-sm; + + &:hover:not([aria-disabled='true']) { + @apply border-green-700 bg-green-700 text-white; + } + + &:focus { + @apply border-green-700 bg-green-700; + } + + &[aria-disabled='true'] { + @apply bg-green-600 opacity-50; + } + } + + &.secondary { + @apply rounded-lg text-neutral-800 dark:text-neutral-200; + + &:hover:not([aria-disabled='true']) { + @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + } + + &[aria-disabled='true'] { + @apply bg-transparent opacity-50; + } + + &:focus { + @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + } + } + + &.special { + @apply rounded-lg border border-green-600/30 bg-green-600/10 text-white shadow-sm; + + &::before { + @apply bg-gradient-glow-backdrop absolute left-0 right-0 top-0 -z-10 mx-auto h-full w-full opacity-30 content-['']; + } + + &::after { + @apply absolute -top-px left-0 right-0 mx-auto h-px w-2/5 bg-gradient-to-r from-green-600/0 via-green-600 to-green-600/0 content-['']; + } + + &[aria-disabled='true'] { + @apply opacity-50; + } + + &:hover:not([aria-disabled='true']) { + @apply bg-green-600/20; + } + + &:focus { + @apply bg-green-600/20; + } + } +} diff --git a/apps/site/components/Common/Button/index.stories.tsx b/packages/ui-components/Common/BaseButton/index.stories.tsx similarity index 85% rename from apps/site/components/Common/Button/index.stories.tsx rename to packages/ui-components/Common/BaseButton/index.stories.tsx index b518ac0fbd3f7..6d0e741cc361a 100644 --- a/apps/site/components/Common/Button/index.stories.tsx +++ b/packages/ui-components/Common/BaseButton/index.stories.tsx @@ -1,10 +1,10 @@ import { ArrowRightIcon } from '@heroicons/react/24/solid'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Button from '@/components/Common/Button'; +import BaseButton from '@node-core/ui-components/Common/BaseButton'; -type Story = StoryObj<typeof Button>; -type Meta = MetaObj<typeof Button>; +type Story = StoryObj<typeof BaseButton>; +type Meta = MetaObj<typeof BaseButton>; export const Neutral: Story = { args: { @@ -57,7 +57,7 @@ export const WithIcon: Story = { }; export default { - component: Button, + component: BaseButton, argTypes: { size: { options: ['default', 'small'], diff --git a/apps/site/components/Common/Button/index.tsx b/packages/ui-components/Common/BaseButton/index.tsx similarity index 79% rename from apps/site/components/Common/Button/index.tsx rename to packages/ui-components/Common/BaseButton/index.tsx index 218051d2a78fd..109b4988af78a 100644 --- a/apps/site/components/Common/Button/index.tsx +++ b/packages/ui-components/Common/BaseButton/index.tsx @@ -1,32 +1,31 @@ -'use client'; - import classNames from 'classnames'; import type { FC, AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react'; -import Link from '@/components/Link'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; -type ButtonProps = ( +export type ButtonProps = ( | AnchorHTMLAttributes<HTMLAnchorElement> | ButtonHTMLAttributes<HTMLButtonElement> ) & { kind?: 'neutral' | 'primary' | 'secondary' | 'special'; size?: 'default' | 'small'; disabled?: boolean; + as?: LinkLike; }; -const Button: FC<ButtonProps> = ({ +const BaseButton: FC<ButtonProps> = ({ kind = 'primary', size = 'default', disabled = false, - children, className, + as: Component = 'a', ...props }) => { - if ('href' in props) { + if ('href' in props && Component) { return ( - <Link + <Component role="button" href={disabled ? undefined : props.href} aria-disabled={disabled} @@ -38,9 +37,7 @@ const Button: FC<ButtonProps> = ({ )} tabIndex={disabled ? -1 : 0} {...props} - > - {children} - </Link> + /> ); } @@ -55,10 +52,8 @@ const Button: FC<ButtonProps> = ({ )} type="button" {...(props as ButtonHTMLAttributes<HTMLButtonElement>)} - > - {children} - </button> + /> ); }; -export default Button; +export default BaseButton; diff --git a/packages/ui-components/Common/BaseCodeBox/index.module.css b/packages/ui-components/Common/BaseCodeBox/index.module.css new file mode 100644 index 0000000000000..a88eee655aaec --- /dev/null +++ b/packages/ui-components/Common/BaseCodeBox/index.module.css @@ -0,0 +1,43 @@ +.root { + @apply w-full rounded border border-neutral-900 bg-neutral-950; + + .content { + @apply m-0 p-4; + + & > code { + @apply font-ibm-plex-mono font-regular grid overflow-x-auto bg-transparent p-0 text-sm leading-snug text-neutral-400 [counter-reset:line]; + + & > [class='line'] { + @apply relative min-w-0 pl-8; + + &:not(:empty:last-child)::before { + @apply inline-block content-['']; + } + + &:not(:empty:last-child)::after { + @apply w-4.5 font-ibm-plex-mono absolute left-0 top-0 mr-4 text-right text-neutral-600 [content:counter(line)] [counter-increment:line]; + } + } + } + } + + & > .footer { + @apply flex items-center justify-between border-t border-t-neutral-900 px-4 py-3 text-sm font-medium; + + & > .language { + @apply text-neutral-400; + } + + & > .action { + @apply px-3 py-1.5 font-medium; + } + } +} + +.notification { + @apply flex items-center gap-3; +} + +.icon { + @apply size-4; +} diff --git a/apps/site/components/Common/CodeBox/index.stories.tsx b/packages/ui-components/Common/BaseCodeBox/index.stories.tsx similarity index 62% rename from apps/site/components/Common/CodeBox/index.stories.tsx rename to packages/ui-components/Common/BaseCodeBox/index.stories.tsx index f44b33e17707d..003b6b2c2692d 100644 --- a/apps/site/components/Common/CodeBox/index.stories.tsx +++ b/packages/ui-components/Common/BaseCodeBox/index.stories.tsx @@ -1,9 +1,9 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import CodeBox from '@/components/Common/CodeBox'; +import BaseCodeBox from '@node-core/ui-components/Common/BaseCodeBox'; -type Story = StoryObj<typeof CodeBox>; -type Meta = MetaObj<typeof CodeBox>; +type Story = StoryObj<typeof BaseCodeBox>; +type Meta = MetaObj<typeof BaseCodeBox>; const content = `const http = require('http'); @@ -20,19 +20,20 @@ server.listen(port, hostname, () => { console.log(\`Server running at http://\${hostname}:\${port}/\`); });`; +const args = { + language: 'JavaScript (CJS)', + children: <code>{content}</code>, +}; + export const Default: Story = { - args: { - language: 'JavaScript (CJS)', - children: <code>{content}</code>, - }, + args, }; export const WithCopyButton: Story = { args: { - language: 'JavaScript (CJS)', + ...args, showCopyButton: true, - children: <code>{content}</code>, }, }; -export default { component: CodeBox } as Meta; +export default { component: BaseCodeBox } as Meta; diff --git a/apps/site/components/Common/CodeBox/index.tsx b/packages/ui-components/Common/BaseCodeBox/index.tsx similarity index 70% rename from apps/site/components/Common/CodeBox/index.tsx rename to packages/ui-components/Common/BaseCodeBox/index.tsx index e0f640f119958..b51d89c67cf11 100644 --- a/apps/site/components/Common/CodeBox/index.tsx +++ b/packages/ui-components/Common/BaseCodeBox/index.tsx @@ -1,16 +1,15 @@ 'use client'; import { - CodeBracketIcon, DocumentDuplicateIcon, + CodeBracketIcon, } from '@heroicons/react/24/outline'; import classNames from 'classnames'; -import { useTranslations } from 'next-intl'; -import type { FC, PropsWithChildren, ReactElement } from 'react'; +import type { FC, PropsWithChildren, ReactElement, ReactNode } from 'react'; import { Fragment, isValidElement, useRef } from 'react'; -import Button from '@/components/Common/Button'; -import { useCopyToClipboard, useNotification } from '@/hooks'; +import BaseButton from '@node-core/ui-components/Common/BaseButton'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; @@ -67,60 +66,63 @@ const transformCode = <T extends ReactElement<PropsWithChildren>>( ); }; -type CodeBoxProps = { +interface CodeBoxProps { language: string; - showCopyButton?: boolean; className?: string; -}; + onCopy: (text: string, message: ReactNode) => void; + as?: LinkLike; + copyText: string; + copiedText: string; + showCopyButton?: boolean; +} -const CodeBox: FC<PropsWithChildren<CodeBoxProps>> = ({ +const BaseCodeBox: FC<PropsWithChildren<CodeBoxProps>> = ({ children, language, - showCopyButton = true, className, -}) => { - const ref = useRef<HTMLPreElement>(null); - - const notify = useNotification(); - const [, copyToClipboard] = useCopyToClipboard(); - const t = useTranslations(); - - const onCopy = async () => { - if (ref.current?.textContent) { - copyToClipboard(ref.current.textContent); - - notify({ - duration: 3000, - message: ( - <div className={styles.notification}> - <CodeBracketIcon className={styles.icon} /> - {t('components.common.codebox.copied')} - </div> - ), - }); + onCopy, + copiedText, + copyText, + as = 'a', + showCopyButton = true, +}: PropsWithChildren<CodeBoxProps>) => { + const containerRef = useRef<HTMLPreElement>(null); + + const handleCopy = (): void => { + const text = containerRef.current?.textContent; + if (text) { + onCopy( + text, + <div className={styles.notification}> + <CodeBracketIcon className={styles.icon} /> + {copiedText} + </div> + ); } }; return ( <div className={styles.root}> <pre - ref={ref} + ref={containerRef} className={classNames(styles.content, className)} tabIndex={0} - dir="ltr" > {transformCode(children as ReactElement<PropsWithChildren>, language)} </pre> - {language && ( <div className={styles.footer}> <span className={styles.language}>{language}</span> - {showCopyButton && ( - <Button kind="neutral" className={styles.action} onClick={onCopy}> + <BaseButton + as={as} + className={styles.action} + kind="neutral" + onClick={handleCopy} + > <DocumentDuplicateIcon className={styles.icon} /> - {t('components.common.codebox.copy')} - </Button> + {copyText} + </BaseButton> )} </div> )} @@ -128,4 +130,4 @@ const CodeBox: FC<PropsWithChildren<CodeBoxProps>> = ({ ); }; -export default CodeBox; +export default BaseCodeBox; diff --git a/packages/ui-components/Common/BaseCrossLink/index.module.css b/packages/ui-components/Common/BaseCrossLink/index.module.css new file mode 100644 index 0000000000000..534ccb342cad3 --- /dev/null +++ b/packages/ui-components/Common/BaseCrossLink/index.module.css @@ -0,0 +1,23 @@ +.crossLink { + @apply flex flex-col items-start gap-2 truncate rounded border border-solid border-neutral-300 bg-white p-3 no-underline dark:border-neutral-900 dark:bg-neutral-950; + + .header { + @apply flex items-center gap-2 self-stretch text-xs text-neutral-800 dark:text-neutral-100; + + &.reverse { + @apply flex-row-reverse justify-start; + } + + .icon { + @apply size-4 text-neutral-600 dark:text-neutral-400; + } + } + + .content { + @apply self-stretch truncate text-sm text-neutral-900 dark:text-white; + + &.reverse { + @apply text-right; + } + } +} diff --git a/apps/site/components/Common/CrossLink/index.stories.tsx b/packages/ui-components/Common/BaseCrossLink/index.stories.tsx similarity index 76% rename from apps/site/components/Common/CrossLink/index.stories.tsx rename to packages/ui-components/Common/BaseCrossLink/index.stories.tsx index 36ccf37f396dc..e34f67cfa666a 100644 --- a/apps/site/components/Common/CrossLink/index.stories.tsx +++ b/packages/ui-components/Common/BaseCrossLink/index.stories.tsx @@ -1,9 +1,9 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import CrossLink from '@/components/Common/CrossLink'; +import BaseCrossLink from '@node-core/ui-components/Common/BaseCrossLink'; -type Story = StoryObj<typeof CrossLink>; -type Meta = MetaObj<typeof CrossLink>; +type Story = StoryObj<typeof BaseCrossLink>; +type Meta = MetaObj<typeof BaseCrossLink>; export const Prev: Story = { args: { @@ -35,4 +35,4 @@ export const Next: Story = { ], }; -export default { component: CrossLink } as Meta; +export default { component: BaseCrossLink } as Meta; diff --git a/apps/site/components/Common/CrossLink/index.tsx b/packages/ui-components/Common/BaseCrossLink/index.tsx similarity index 55% rename from apps/site/components/Common/CrossLink/index.tsx rename to packages/ui-components/Common/BaseCrossLink/index.tsx index e96bef0b5a35f..517711dfa7bb6 100644 --- a/apps/site/components/Common/CrossLink/index.tsx +++ b/packages/ui-components/Common/BaseCrossLink/index.tsx @@ -1,31 +1,38 @@ import classNames from 'classnames'; -import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import PrevNextArrow from '@/components/Common/PrevNextArrow'; -import Link from '@/components/Link'; -import type { FormattedMessage } from '@/types'; +import PrevNextArrow from '@node-core/ui-components/Common/PrevNextArrow'; +import type { + LinkLike, + FormattedMessage, +} from '@node-core/ui-components/types'; import styles from './index.module.css'; -type CrossLinkProps = { +export type CrossLinkProps = { type: 'previous' | 'next'; text: FormattedMessage; + label: string; link: string; + as: LinkLike; }; -const CrossLink: FC<CrossLinkProps> = ({ type, text, link }) => { - const t = useTranslations(); - +const BaseCrossLink: FC<CrossLinkProps> = ({ + type, + label, + text, + link, + as: Component = 'a', +}) => { return ( - <Link className={styles.crossLink} href={link}> + <Component className={styles.crossLink} href={link}> <span className={classNames(styles.header, { [styles.reverse]: type === 'next', })} > <PrevNextArrow className={styles.icon} type={type} /> - {t(`components.common.crossLink.${type}`)} + {label} </span> <span @@ -35,8 +42,8 @@ const CrossLink: FC<CrossLinkProps> = ({ type, text, link }) => { > {text} </span> - </Link> + </Component> ); }; -export default CrossLink; +export default BaseCrossLink; diff --git a/packages/ui-components/Common/BaseLinkTabs/index.module.css b/packages/ui-components/Common/BaseLinkTabs/index.module.css new file mode 100644 index 0000000000000..966255eb045cb --- /dev/null +++ b/packages/ui-components/Common/BaseLinkTabs/index.module.css @@ -0,0 +1,19 @@ +.tabsList { + @apply font-open-sans max-xs:hidden mb-6 mt-10 flex gap-2 border-b border-b-neutral-200 dark:border-b-neutral-800; + + .tabsTrigger { + @apply border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold text-neutral-800 dark:text-neutral-200; + + &[data-state='active'] { + @apply border-b-green-600 text-green-600 dark:border-b-green-400 dark:text-green-400; + } + } +} + +.tabsSelect { + @apply sm:visible md:hidden; + + > span { + @apply max-xs:flex my-6 hidden w-full; + } +} diff --git a/apps/site/components/Common/LinkTabs/index.stories.tsx b/packages/ui-components/Common/BaseLinkTabs/index.stories.tsx similarity index 70% rename from apps/site/components/Common/LinkTabs/index.stories.tsx rename to packages/ui-components/Common/BaseLinkTabs/index.stories.tsx index a8bf1cef29079..76bf7e499c3eb 100644 --- a/apps/site/components/Common/LinkTabs/index.stories.tsx +++ b/packages/ui-components/Common/BaseLinkTabs/index.stories.tsx @@ -1,9 +1,9 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import LinkTabs from '@/components/Common/LinkTabs'; +import BaseLinkTabs from '@node-core/ui-components/Common/BaseLinkTabs'; -type Story = StoryObj<typeof LinkTabs>; -type Meta = MetaObj<typeof LinkTabs>; +type Story = StoryObj<typeof BaseLinkTabs>; +type Meta = MetaObj<typeof BaseLinkTabs>; export const Default: Story = { args: { @@ -27,7 +27,8 @@ export const Default: Story = { ], activeTab: 'prebuilt', children: <p>Tab content goes here</p>, + onSelect: console.log, }, }; -export default { component: LinkTabs } as Meta; +export default { component: BaseLinkTabs } as Meta; diff --git a/apps/site/components/Common/LinkTabs/index.tsx b/packages/ui-components/Common/BaseLinkTabs/index.tsx similarity index 65% rename from apps/site/components/Common/LinkTabs/index.tsx rename to packages/ui-components/Common/BaseLinkTabs/index.tsx index 5ad243dc847b4..04578e0da7336 100644 --- a/apps/site/components/Common/LinkTabs/index.tsx +++ b/packages/ui-components/Common/BaseLinkTabs/index.tsx @@ -1,43 +1,48 @@ import type { FC, PropsWithChildren } from 'react'; -import Link from '@/components/Link'; -import WithRouterSelect from '@/components/withRouterSelect'; +import Select from '@node-core/ui-components/Common/Select'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type LinkTab = { key: string; label: string; link: string }; -type LinkTabsProps = { +export type LinkTabsProps = PropsWithChildren<{ label?: string; tabs: Array<LinkTab>; activeTab: string; -}; + as?: LinkLike; + onSelect: (value: string) => void; +}>; -const LinkTabs: FC<PropsWithChildren<LinkTabsProps>> = ({ +const BaseLinkTabs: FC<LinkTabsProps> = ({ tabs, label, activeTab, children, + as: Component = 'a', + onSelect, }) => ( <> <div className={styles.tabsList}> {tabs.map(tab => ( - <Link + <Component key={tab.key} href={tab.link} className={styles.tabsTrigger} data-state={tab.key === activeTab ? 'active' : 'inactive'} > {tab.label} - </Link> + </Component> ))} </div> <div className={styles.tabsSelect}> - <WithRouterSelect + <Select label={label} defaultValue={tabs.find(tab => tab.key === activeTab)?.link} values={tabs.map(tab => ({ label: tab.label, value: tab.link }))} + onChange={onSelect} /> </div> @@ -45,4 +50,4 @@ const LinkTabs: FC<PropsWithChildren<LinkTabsProps>> = ({ </> ); -export default LinkTabs; +export default BaseLinkTabs; diff --git a/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css b/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css new file mode 100644 index 0000000000000..e14f27f87af9a --- /dev/null +++ b/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css @@ -0,0 +1,3 @@ +.ellipsis { + @apply w-10 px-3 py-2.5 leading-none text-neutral-800 dark:text-neutral-200; +} diff --git a/apps/site/components/Common/Pagination/Ellipsis/index.stories.tsx b/packages/ui-components/Common/BasePagination/Ellipsis/index.stories.tsx similarity index 74% rename from apps/site/components/Common/Pagination/Ellipsis/index.stories.tsx rename to packages/ui-components/Common/BasePagination/Ellipsis/index.stories.tsx index 0949bcc20cf72..916c1f6c9b802 100644 --- a/apps/site/components/Common/Pagination/Ellipsis/index.stories.tsx +++ b/packages/ui-components/Common/BasePagination/Ellipsis/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Ellipsis from '@/components/Common/Pagination/Ellipsis'; +import Ellipsis from '@node-core/ui-components/Common/BasePagination/Ellipsis'; type Story = StoryObj<typeof Ellipsis>; type Meta = MetaObj<typeof Ellipsis>; diff --git a/apps/site/components/Common/Pagination/Ellipsis/index.tsx b/packages/ui-components/Common/BasePagination/Ellipsis/index.tsx similarity index 100% rename from apps/site/components/Common/Pagination/Ellipsis/index.tsx rename to packages/ui-components/Common/BasePagination/Ellipsis/index.tsx diff --git a/apps/site/components/Common/Pagination/PaginationListItem/__tests__/index.test.mjs b/packages/ui-components/Common/BasePagination/PaginationListItem/__tests__/index.test.mjs similarity index 92% rename from apps/site/components/Common/Pagination/PaginationListItem/__tests__/index.test.mjs rename to packages/ui-components/Common/BasePagination/PaginationListItem/__tests__/index.test.mjs index 9c0d2b26f8c6e..eff421e52fa06 100644 --- a/apps/site/components/Common/Pagination/PaginationListItem/__tests__/index.test.mjs +++ b/packages/ui-components/Common/BasePagination/PaginationListItem/__tests__/index.test.mjs @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; -import PaginationListItem from '@/components/Common/Pagination/PaginationListItem'; +import PaginationListItem from '@node-core/ui-components/Common/BasePagination/PaginationListItem'; function renderPaginationListItem({ url, diff --git a/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css b/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css new file mode 100644 index 0000000000000..f5232bfd0f946 --- /dev/null +++ b/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css @@ -0,0 +1,9 @@ +.listItem, +.listItem:link, +.listItem:active { + @apply aria-current:bg-green-600 aria-current:text-white flex size-10 items-center justify-center rounded px-3 py-2.5 text-neutral-800 motion-safe:transition-colors dark:text-neutral-200; + + &:hover { + @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + } +} diff --git a/apps/site/components/Common/Pagination/PaginationListItem/index.stories.tsx b/packages/ui-components/Common/BasePagination/PaginationListItem/index.stories.tsx similarity index 87% rename from apps/site/components/Common/Pagination/PaginationListItem/index.stories.tsx rename to packages/ui-components/Common/BasePagination/PaginationListItem/index.stories.tsx index ed4de699b966e..5a0934c9a39d9 100644 --- a/apps/site/components/Common/Pagination/PaginationListItem/index.stories.tsx +++ b/packages/ui-components/Common/BasePagination/PaginationListItem/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import PaginationListItem from '@/components/Common/Pagination/PaginationListItem'; +import PaginationListItem from '@node-core/ui-components/Common/BasePagination/PaginationListItem'; type Story = StoryObj<typeof PaginationListItem>; type Meta = MetaObj<typeof PaginationListItem>; diff --git a/apps/site/components/Common/Pagination/PaginationListItem/index.tsx b/packages/ui-components/Common/BasePagination/PaginationListItem/index.tsx similarity index 75% rename from apps/site/components/Common/Pagination/PaginationListItem/index.tsx rename to packages/ui-components/Common/BasePagination/PaginationListItem/index.tsx index a1fd525c7a58f..66543d4c8601a 100644 --- a/apps/site/components/Common/Pagination/PaginationListItem/index.tsx +++ b/packages/ui-components/Common/BasePagination/PaginationListItem/index.tsx @@ -1,7 +1,6 @@ -import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import Link from '@/components/Link'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; @@ -11,6 +10,8 @@ export type PaginationListItemProps = { // One-based number of the current page currentPage: number; totalPages: number; + as?: LinkLike; + label: string; }; const PaginationListItem: FC<PaginationListItemProps> = ({ @@ -18,19 +19,19 @@ const PaginationListItem: FC<PaginationListItemProps> = ({ pageNumber, currentPage, totalPages, + as: Component = 'a', + label, }) => { - const t = useTranslations(); - return ( <li key={pageNumber} aria-setsize={totalPages} aria-posinset={pageNumber}> - <Link + <Component href={url} - aria-label={t('components.common.pagination.pageLabel', { pageNumber })} + aria-label={label} className={styles.listItem} {...(pageNumber === currentPage && { 'aria-current': 'page' })} > <span>{pageNumber}</span> - </Link> + </Component> </li> ); }; diff --git a/apps/site/components/Common/Pagination/__tests__/index.test.mjs b/packages/ui-components/Common/BasePagination/__tests__/index.test.mjs similarity index 89% rename from apps/site/components/Common/Pagination/__tests__/index.test.mjs rename to packages/ui-components/Common/BasePagination/__tests__/index.test.mjs index b28e69af50f65..8e4f5b710db0e 100644 --- a/apps/site/components/Common/Pagination/__tests__/index.test.mjs +++ b/packages/ui-components/Common/BasePagination/__tests__/index.test.mjs @@ -1,6 +1,15 @@ import { render, screen } from '@testing-library/react'; -import Pagination from '@/components/Common/Pagination'; +import BasePagination from '@node-core/ui-components/Common/BasePagination'; + +const getPageLabel = number => number.toString(); +const labels = { + aria: 'Aria', + prevAria: 'Previous Aria', + prev: 'Previous', + nextAria: 'Next Aria', + next: 'Next', +}; function renderPagination({ currentPage = 1, @@ -12,10 +21,12 @@ function renderPagination({ .map(item => ({ url: `${item.url}-${Math.random()}` })); render( - <Pagination + <BasePagination currentPage={currentPage} pages={parsedPages} currentPageSiblingsCount={currentPageSiblingsCount} + getPageLabel={getPageLabel} + labels={labels} /> ); @@ -34,13 +45,13 @@ describe('Pagination', () => { expect( screen.getByRole('button', { - name: 'components.common.pagination.prevAriaLabel', + name: labels.prevAria, }) ).toBeVisible(); expect( screen.getByRole('button', { - name: 'components.common.pagination.nextAriaLabel', + name: labels.nextAria, }) ).toBeVisible(); }); @@ -160,7 +171,7 @@ describe('Pagination', () => { expect( screen.getByRole('button', { - name: 'components.common.pagination.prevAriaLabel', + name: labels.prevAria, }) ).toHaveAttribute('aria-disabled'); }); @@ -173,7 +184,7 @@ describe('Pagination', () => { expect( screen.getByRole('button', { - name: 'components.common.pagination.nextAriaLabel', + name: labels.nextAria, }) ).toHaveAttribute('aria-disabled'); }); diff --git a/packages/ui-components/Common/BasePagination/index.module.css b/packages/ui-components/Common/BasePagination/index.module.css new file mode 100644 index 0000000000000..01124af149fb0 --- /dev/null +++ b/packages/ui-components/Common/BasePagination/index.module.css @@ -0,0 +1,32 @@ +.pagination { + @apply grid items-center justify-between gap-y-5 md:gap-y-0; + + grid-template-areas: 'pages pages pages' 'prev . next'; +} + +@screen md { + .pagination { + grid-template-areas: 'prev pages next'; + } +} + +.previousButton, +.nextButton { + @apply text-sm; +} + +.previousButton { + @apply [grid-area:prev]; +} + +.nextButton { + @apply [grid-area:next]; +} + +.arrowIcon { + @apply h-5 shrink-0 text-neutral-600 dark:text-neutral-400; +} + +.list { + @apply flex list-none justify-center gap-1 [grid-area:pages]; +} diff --git a/apps/site/components/Common/Pagination/index.stories.tsx b/packages/ui-components/Common/BasePagination/index.stories.tsx similarity index 69% rename from apps/site/components/Common/Pagination/index.stories.tsx rename to packages/ui-components/Common/BasePagination/index.stories.tsx index 2fc4287fea3c3..98fb6397da289 100644 --- a/apps/site/components/Common/Pagination/index.stories.tsx +++ b/packages/ui-components/Common/BasePagination/index.stories.tsx @@ -1,15 +1,23 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Pagination from '@/components/Common/Pagination'; +import BasePagination from '@node-core/ui-components/Common/BasePagination'; -type Story = StoryObj<typeof Pagination>; -type Meta = MetaObj<typeof Pagination>; +type Story = StoryObj<typeof BasePagination>; +type Meta = MetaObj<typeof BasePagination>; export const Default: Story = { args: { currentPage: 1, currentPageSiblingsCount: 1, pages: [{ url: '1' }, { url: '2' }, { url: '3' }], + getPageLabel: value => `Page: ${value}`, + labels: { + aria: 'Aria', + prevAria: 'Previous Aria', + prev: 'Previous', + nextAria: 'Next Aria', + next: 'Next', + }, }, }; @@ -56,4 +64,4 @@ export const TwoEllipses: Story = { }, }; -export default { component: Pagination } as Meta; +export default { component: BasePagination } as Meta; diff --git a/apps/site/components/Common/Pagination/index.tsx b/packages/ui-components/Common/BasePagination/index.tsx similarity index 56% rename from apps/site/components/Common/Pagination/index.tsx rename to packages/ui-components/Common/BasePagination/index.tsx index 969cf4dc86fc3..4cabe6e6e2683 100644 --- a/apps/site/components/Common/Pagination/index.tsx +++ b/packages/ui-components/Common/BasePagination/index.tsx @@ -1,66 +1,77 @@ import { ArrowRightIcon, ArrowLeftIcon } from '@heroicons/react/20/solid'; -import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import Button from '@/components/Common/Button'; -import { useGetPageElements } from '@/components/Common/Pagination/useGetPageElements'; +import Button from '@node-core/ui-components/Common/BaseButton'; +import { useGetPageElements } from '@node-core/ui-components/Common/BasePagination/useGetPageElements'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type Page = { url: string }; -type PaginationProps = { +export type PaginationProps = { // One-based number of the current page currentPage: number; pages: Array<Page>; // The number of page buttons on each side of the current page button // @default 1 currentPageSiblingsCount?: number; + as?: LinkLike; + getPageLabel: (pageNumber: number) => string; + labels: { + aria: string; + prevAria: string; + prev: string; + nextAria: string; + next: string; + }; }; -const Pagination: FC<PaginationProps> = ({ +const BasePagination: FC<PaginationProps> = ({ currentPage, pages, + as = 'a', currentPageSiblingsCount = 1, + labels, + getPageLabel, }) => { - const t = useTranslations(); - const parsedPages = useGetPageElements( currentPage, pages, - currentPageSiblingsCount + currentPageSiblingsCount, + as, + getPageLabel ); return ( - <nav - aria-label={t('components.common.pagination.defaultLabel')} - className={styles.pagination} - > + <nav aria-label={labels.aria} className={styles.pagination}> <Button - aria-label={t('components.common.pagination.prevAriaLabel')} + as={as} + aria-label={labels.prevAria} disabled={currentPage === 1} kind="secondary" className={styles.previousButton} href={pages[currentPage - 2]?.url} > <ArrowLeftIcon className={styles.arrowIcon} /> - <span>{t('components.common.pagination.prev')}</span> + <span>{labels.prev}</span> </Button> <ol className={styles.list}>{parsedPages}</ol> <Button - aria-label={t('components.common.pagination.nextAriaLabel')} + as={as} + aria-label={labels.nextAria} disabled={currentPage === pages.length} kind="secondary" className={styles.nextButton} href={pages[currentPage]?.url} > - <span>{t('components.common.pagination.next')}</span> + <span>{labels.next}</span> <ArrowRightIcon className={styles.arrowIcon} /> </Button> </nav> ); }; -export default Pagination; +export default BasePagination; diff --git a/apps/site/components/Common/Pagination/useGetPageElements.tsx b/packages/ui-components/Common/BasePagination/useGetPageElements.tsx similarity index 79% rename from apps/site/components/Common/Pagination/useGetPageElements.tsx rename to packages/ui-components/Common/BasePagination/useGetPageElements.tsx index 9ea65422e02ac..eabceb995c5ec 100644 --- a/apps/site/components/Common/Pagination/useGetPageElements.tsx +++ b/packages/ui-components/Common/BasePagination/useGetPageElements.tsx @@ -1,20 +1,25 @@ import type { ComponentProps } from 'react'; -import type Pagination from '@/components/Common/Pagination'; -import Ellipsis from '@/components/Common/Pagination/Ellipsis'; -import type { PaginationListItemProps } from '@/components/Common/Pagination/PaginationListItem/index'; -import PaginationListItem from '@/components/Common/Pagination/PaginationListItem/index'; +import type BasePagination from '@node-core/ui-components/Common/BasePagination'; +import Ellipsis from '@node-core/ui-components/Common/BasePagination/Ellipsis'; +import type { PaginationListItemProps } from '@node-core/ui-components/Common/BasePagination/PaginationListItem'; +import PaginationListItem from '@node-core/ui-components/Common/BasePagination/PaginationListItem'; +import type { LinkLike } from '@node-core/ui-components/types'; const parsePages = ( - pages: ComponentProps<typeof Pagination>['pages'], + pages: ComponentProps<typeof BasePagination>['pages'], currentPage: number, - totalPages: number + totalPages: number, + as: LinkLike, + getLabel: (pageNumber: number) => string ): Array<PaginationListItemProps> => pages.map(({ url }, index) => ({ url, pageNumber: index + 1, currentPage, totalPages, + label: getLabel(index + 1), + as, })); const createPaginationListItems = ( @@ -31,12 +36,14 @@ const MAXIMUM_AMOUNT_OF_ELLIPSES = 2; // React MUI's Pagination component as reference. More info here: // https://github.com/mui/material-ui/blob/master/packages/mui-material/src/usePagination/usePagination.js export const useGetPageElements = ( - currentPage: ComponentProps<typeof Pagination>['currentPage'], - pages: ComponentProps<typeof Pagination>['pages'], - currentPageSiblingsCount: number + currentPage: ComponentProps<typeof BasePagination>['currentPage'], + pages: ComponentProps<typeof BasePagination>['pages'], + currentPageSiblingsCount: number, + as: LinkLike, + getLabel: (pageNumber: number) => string ) => { const totalPages = pages.length; - const parsedPages = parsePages(pages, currentPage, totalPages); + const parsedPages = parsePages(pages, currentPage, totalPages, as, getLabel); const currentPageIndex = currentPage - 1; // We multiply it by two (2) as siblings are located on both left and right sides diff --git a/packages/ui-components/Common/Blockquote/index.module.css b/packages/ui-components/Common/Blockquote/index.module.css new file mode 100644 index 0000000000000..f13d733741d0a --- /dev/null +++ b/packages/ui-components/Common/Blockquote/index.module.css @@ -0,0 +1,11 @@ +.wrapper { + @apply flex max-w-2xl flex-col items-start gap-4 self-stretch border-l-2 border-green-600 py-2 pl-5 text-lg font-semibold text-neutral-900 dark:border-green-400 dark:text-white; + + & cite { + @apply font-regular text-base not-italic; + + &::before { + @apply content-['—_']; + } + } +} diff --git a/apps/site/components/Common/Blockquote/index.stories.tsx b/packages/ui-components/Common/Blockquote/index.stories.tsx similarity index 95% rename from apps/site/components/Common/Blockquote/index.stories.tsx rename to packages/ui-components/Common/Blockquote/index.stories.tsx index d00a56c084f33..7d0bb884f0d62 100644 --- a/apps/site/components/Common/Blockquote/index.stories.tsx +++ b/packages/ui-components/Common/Blockquote/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Blockquote from '@/components/Common/Blockquote'; +import Blockquote from '@node-core/ui-components/Common/Blockquote'; type Story = StoryObj<typeof Blockquote>; type Meta = MetaObj<typeof Blockquote>; diff --git a/apps/site/components/Common/Blockquote/index.tsx b/packages/ui-components/Common/Blockquote/index.tsx similarity index 100% rename from apps/site/components/Common/Blockquote/index.tsx rename to packages/ui-components/Common/Blockquote/index.tsx diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css b/packages/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css similarity index 100% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx b/packages/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx similarity index 59% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx index dc366d30e4f98..c8ed33c1385ad 100644 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx @@ -1,8 +1,7 @@ import HomeIcon from '@heroicons/react/24/outline/HomeIcon'; -import { useTranslations } from 'next-intl'; import type { ComponentProps, FC } from 'react'; -import BreadcrumbLink from '@/components/Common/Breadcrumbs/BreadcrumbLink'; +import BreadcrumbLink from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbLink'; import styles from './index.module.css'; @@ -16,15 +15,12 @@ const BreadcrumbHomeLink: FC<BreadcrumbHomeLinkProps> = ({ href = '/', ...props }) => { - const t = useTranslations(); - - const navigateToHome = t('components.common.breadcrumbs.navigateToHome'); - + const ariaLabel = props['aria-label']; return ( - <BreadcrumbLink href={href} aria-label={navigateToHome} {...props}> + <BreadcrumbLink href={href} {...props}> <HomeIcon - title={navigateToHome} - aria-label={navigateToHome} + title={ariaLabel} + aria-label={ariaLabel} className={styles.icon} /> </BreadcrumbLink> diff --git a/packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.module.css b/packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.module.css new file mode 100644 index 0000000000000..35f6968d03714 --- /dev/null +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.module.css @@ -0,0 +1,25 @@ +.item { + @apply flex max-w-fit items-center gap-5 truncate text-sm font-medium; + + &:last-child { + @apply w-full; + } + + a { + @apply shrink grow; + } + + &, + > a, + > a:hover { + @apply text-neutral-800 motion-safe:transition-colors dark:text-neutral-200; + } + + &.visuallyHidden { + @apply hidden; + } + + .separator { + @apply size-4 max-w-fit shrink-0 grow text-neutral-600 dark:text-neutral-400; + } +} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.tsx b/packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.tsx similarity index 100% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.tsx rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbItem/index.tsx diff --git a/packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.module.css b/packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.module.css new file mode 100644 index 0000000000000..14f71131540ec --- /dev/null +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.module.css @@ -0,0 +1,11 @@ +.link { + @apply max-w-fit truncate; + + &.active { + @apply rounded bg-green-600 px-2 py-1 font-semibold text-white motion-safe:transition-colors dark:text-white; + + &:hover { + @apply bg-green-700 text-white; + } + } +} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.tsx b/packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.tsx similarity index 80% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.tsx rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.tsx index 93f58ae3a5d96..61c7969f0dfea 100644 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.tsx +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbLink/index.tsx @@ -1,20 +1,22 @@ import classNames from 'classnames'; import type { ComponentProps, FC } from 'react'; -import Link from '@/components/Link'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type BreadcrumbLinkProps = { active?: boolean; -} & ComponentProps<typeof Link>; + as: LinkLike; +} & ComponentProps<LinkLike>; const BreadcrumbLink: FC<BreadcrumbLinkProps> = ({ href, active, + as: Component = 'a', ...props }) => ( - <Link + <Component itemScope itemType="http://schema.org/Thing" itemProp="item" @@ -29,7 +31,7 @@ const BreadcrumbLink: FC<BreadcrumbLinkProps> = ({ {...props} > <span itemProp="name">{props.children}</span> - </Link> + </Component> ); export default BreadcrumbLink; diff --git a/packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css b/packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css new file mode 100644 index 0000000000000..4f13b9c6c1bd2 --- /dev/null +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css @@ -0,0 +1,3 @@ +.list { + @apply xs:w-full flex w-screen gap-5 px-6; +} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.tsx b/packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.tsx similarity index 100% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.tsx rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbRoot/index.tsx diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx b/packages/ui-components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx similarity index 66% rename from apps/site/components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx rename to packages/ui-components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx index 557baee9c1085..5177da5a6ab01 100644 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx +++ b/packages/ui-components/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx @@ -1,4 +1,4 @@ -import BreadcrumbItem from '@/components/Common/Breadcrumbs/BreadcrumbItem'; +import BreadcrumbItem from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbItem'; const BreadcrumbTruncatedItem = () => ( <BreadcrumbItem disableMicrodata> diff --git a/apps/site/components/Common/Breadcrumbs/index.stories.tsx b/packages/ui-components/Common/Breadcrumbs/index.stories.tsx similarity index 95% rename from apps/site/components/Common/Breadcrumbs/index.stories.tsx rename to packages/ui-components/Common/Breadcrumbs/index.stories.tsx index 678def7290c08..e591ba7e5f0b8 100644 --- a/apps/site/components/Common/Breadcrumbs/index.stories.tsx +++ b/packages/ui-components/Common/Breadcrumbs/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Breadcrumbs from '@/components/Common/Breadcrumbs'; +import Breadcrumbs from '@node-core/ui-components/Common/Breadcrumbs'; type Story = StoryObj<typeof Breadcrumbs>; type Meta = MetaObj<typeof Breadcrumbs>; diff --git a/apps/site/components/Common/Breadcrumbs/index.tsx b/packages/ui-components/Common/Breadcrumbs/index.tsx similarity index 65% rename from apps/site/components/Common/Breadcrumbs/index.tsx rename to packages/ui-components/Common/Breadcrumbs/index.tsx index 13c796305cb47..acf8c0ee10204 100644 --- a/apps/site/components/Common/Breadcrumbs/index.tsx +++ b/packages/ui-components/Common/Breadcrumbs/index.tsx @@ -1,12 +1,15 @@ import type { FC } from 'react'; import { useMemo } from 'react'; -import BreadcrumbHomeLink from '@/components/Common/Breadcrumbs/BreadcrumbHomeLink'; -import BreadcrumbItem from '@/components/Common/Breadcrumbs/BreadcrumbItem'; -import BreadcrumbLink from '@/components/Common/Breadcrumbs/BreadcrumbLink'; -import BreadcrumbRoot from '@/components/Common/Breadcrumbs/BreadcrumbRoot'; -import BreadcrumbTruncatedItem from '@/components/Common/Breadcrumbs/BreadcrumbTruncatedItem'; -import type { FormattedMessage } from '@/types'; +import BreadcrumbHomeLink from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbHomeLink'; +import BreadcrumbItem from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbItem'; +import BreadcrumbLink from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbLink'; +import BreadcrumbRoot from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbRoot'; +import BreadcrumbTruncatedItem from '@node-core/ui-components/Common/Breadcrumbs/BreadcrumbTruncatedItem'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; export type BreadcrumbLink = { label: FormattedMessage; @@ -17,12 +20,16 @@ type BreadcrumbsProps = { links: Array<BreadcrumbLink>; maxLength?: number; hideHome?: boolean; + as: LinkLike; + homeLinkAriaLabel?: string; }; const Breadcrumbs: FC<BreadcrumbsProps> = ({ links = [], maxLength = 5, hideHome = false, + as = 'a', + homeLinkAriaLabel, }) => { const totalLength = links.length + +!hideHome; const lengthOffset = maxLength - totalLength; @@ -44,7 +51,7 @@ const Breadcrumbs: FC<BreadcrumbsProps> = ({ hideSeparator={isLastItem} position={position + +!hideHome} > - <BreadcrumbLink href={link.href} active={isLastItem}> + <BreadcrumbLink as={as} href={link.href} active={isLastItem}> {link.label} </BreadcrumbLink> </BreadcrumbItem> @@ -57,7 +64,7 @@ const Breadcrumbs: FC<BreadcrumbsProps> = ({ <BreadcrumbRoot> {!hideHome && ( <BreadcrumbItem position={1}> - <BreadcrumbHomeLink /> + <BreadcrumbHomeLink as={as} aria-label={homeLinkAriaLabel} /> </BreadcrumbItem> )} {isOverflow && <BreadcrumbTruncatedItem />} diff --git a/packages/ui-components/Common/CodeTabs/index.module.css b/packages/ui-components/Common/CodeTabs/index.module.css new file mode 100644 index 0000000000000..e63b5cb512483 --- /dev/null +++ b/packages/ui-components/Common/CodeTabs/index.module.css @@ -0,0 +1,17 @@ +.root { + > [role='tabpanel'] > :first-child { + @apply rounded-t-none; + } + + > div:nth-of-type(1) { + @apply flex rounded-t border-x border-t border-neutral-900 bg-neutral-950 px-2 pt-3 md:px-4; + + .trigger { + @apply border-b border-b-transparent px-1 text-neutral-200; + + &[data-state='active'] { + @apply border-b-green-400 text-green-400; + } + } + } +} diff --git a/apps/site/components/Common/CodeTabs/index.stories.tsx b/packages/ui-components/Common/CodeTabs/index.stories.tsx similarity index 78% rename from apps/site/components/Common/CodeTabs/index.stories.tsx rename to packages/ui-components/Common/CodeTabs/index.stories.tsx index b6a3b9e5f0b9c..0cd1d6c43eb36 100644 --- a/apps/site/components/Common/CodeTabs/index.stories.tsx +++ b/packages/ui-components/Common/CodeTabs/index.stories.tsx @@ -2,8 +2,8 @@ import * as TabsPrimitive from '@radix-ui/react-tabs'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; import type { FC } from 'react'; -import CodeBox from '@/components/Common/CodeBox'; -import CodeTabs from '@/components/Common/CodeTabs'; +import BaseCodeBox from '@node-core/ui-components/Common/BaseCodeBox'; +import CodeTabs from '@node-core/ui-components/Common/CodeTabs'; type Story = StoryObj<typeof CodeTabs>; type Meta = MetaObj<typeof CodeTabs>; @@ -38,30 +38,29 @@ server.listen(port, hostname, () => { console.log(\`Server running at http://\${hostname}:\${port}/\`); });`; +const boxProps = { + onCopy: console.log, + copyText: '[Copy Text]', + copiedText: '[Copied Text]', +}; + const TabsContent: FC = () => ( <> <TabsPrimitive.Content key="mjs" value="mjs"> - <CodeBox language="JavaScript (MJS)" showCopyButton> + <BaseCodeBox language="JavaScript (MJS)" {...boxProps}> <code>{mjsContent}</code> - </CodeBox> + </BaseCodeBox> </TabsPrimitive.Content> <TabsPrimitive.Content key="cjs" value="cjs"> - <CodeBox language="JavaScript (CJS)" showCopyButton> + <BaseCodeBox language="JavaScript (CJS)" {...boxProps}> <code>{cjsContent}</code> - </CodeBox> + </BaseCodeBox> </TabsPrimitive.Content> </> ); export const Default: Story = {}; -export const WithMoreOptions: Story = { - args: { - linkUrl: 'https://github.com/nodejs/nodejs.org', - linkText: 'More options', - }, -}; - export default { component: CodeTabs, args: { diff --git a/packages/ui-components/Common/CodeTabs/index.tsx b/packages/ui-components/Common/CodeTabs/index.tsx new file mode 100644 index 0000000000000..e56a19543cb0c --- /dev/null +++ b/packages/ui-components/Common/CodeTabs/index.tsx @@ -0,0 +1,16 @@ +import type { ComponentProps, FC } from 'react'; + +import Tabs from '@node-core/ui-components/Common/Tabs'; + +import styles from './index.module.css'; + +type CodeTabsProps = Pick< + ComponentProps<typeof Tabs>, + 'tabs' | 'defaultValue' | 'children' | 'addons' +>; + +const CodeTabs: FC<CodeTabsProps> = ({ ...props }) => ( + <Tabs {...props} className={styles.root} triggerClassName={styles.trigger} /> +); + +export default CodeTabs; diff --git a/packages/ui-components/Common/GlowingBackdrop/index.module.css b/packages/ui-components/Common/GlowingBackdrop/index.module.css new file mode 100644 index 0000000000000..37c2ac062afab --- /dev/null +++ b/packages/ui-components/Common/GlowingBackdrop/index.module.css @@ -0,0 +1,11 @@ +.glowingBackdrop { + @apply absolute left-0 top-0 -z-10 size-full opacity-50 md:opacity-100; + + &::after { + @apply absolute inset-0 m-auto aspect-square w-[300px] rounded-full bg-green-300 blur-[120px] content-[''] dark:bg-green-700; + } + + svg { + @apply absolute inset-0 m-auto aspect-[1.67] max-w-[1004px] object-cover text-neutral-300 dark:text-neutral-900/75; + } +} diff --git a/apps/site/components/Common/GlowingBackdrop/index.stories.tsx b/packages/ui-components/Common/GlowingBackdrop/index.stories.tsx similarity index 76% rename from apps/site/components/Common/GlowingBackdrop/index.stories.tsx rename to packages/ui-components/Common/GlowingBackdrop/index.stories.tsx index 5fdc861abfcc7..6db52628da914 100644 --- a/apps/site/components/Common/GlowingBackdrop/index.stories.tsx +++ b/packages/ui-components/Common/GlowingBackdrop/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import GlowingBackdrop from '@/components/Common/GlowingBackdrop'; +import GlowingBackdrop from '@node-core/ui-components/Common/GlowingBackdrop'; type Story = StoryObj<typeof GlowingBackdrop>; type Meta = MetaObj<typeof GlowingBackdrop>; diff --git a/apps/site/components/Common/GlowingBackdrop/index.tsx b/packages/ui-components/Common/GlowingBackdrop/index.tsx similarity index 75% rename from apps/site/components/Common/GlowingBackdrop/index.tsx rename to packages/ui-components/Common/GlowingBackdrop/index.tsx index e5b10fe51abb0..bd774964e1500 100644 --- a/apps/site/components/Common/GlowingBackdrop/index.tsx +++ b/packages/ui-components/Common/GlowingBackdrop/index.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; -import HexagonGrid from '@/components/Icons/HexagonGrid'; +import HexagonGrid from '@node-core/ui-components/Icons/HexagonGrid'; import styles from './index.module.css'; diff --git a/packages/ui-components/Common/LanguageDropDown/index.module.css b/packages/ui-components/Common/LanguageDropDown/index.module.css new file mode 100644 index 0000000000000..99757bc919228 --- /dev/null +++ b/packages/ui-components/Common/LanguageDropDown/index.module.css @@ -0,0 +1,23 @@ +.languageDropdown { + @apply h-9 w-9 rounded-md p-2 text-neutral-700 motion-safe:transition-colors dark:text-neutral-300; + + &:hover { + @apply bg-neutral-100 dark:bg-neutral-900; + } +} + +.dropDownContent { + @apply max-h-80 w-48 overflow-hidden rounded border border-neutral-200 bg-white shadow-lg dark:border-neutral-900 dark:bg-neutral-950; + + > div { + @apply max-h-80 w-48 overflow-y-auto; + } +} + +.dropDownItem { + @apply cursor-pointer px-2.5 py-1.5 text-sm font-medium text-neutral-800 outline-none data-[highlighted]:bg-green-600 data-[highlighted]:text-white dark:text-white; +} + +.currentDropDown { + @apply bg-green-600 text-white; +} diff --git a/apps/site/components/Common/LanguageDropDown/index.stories.tsx b/packages/ui-components/Common/LanguageDropDown/index.stories.tsx similarity index 86% rename from apps/site/components/Common/LanguageDropDown/index.stories.tsx rename to packages/ui-components/Common/LanguageDropDown/index.stories.tsx index 2f5ac8f98f0fe..200674881139d 100644 --- a/apps/site/components/Common/LanguageDropDown/index.stories.tsx +++ b/packages/ui-components/Common/LanguageDropDown/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import LanguageDropDown from '@/components/Common/LanguageDropDown'; +import LanguageDropDown from '@node-core/ui-components/Common/LanguageDropDown'; type Story = StoryObj<typeof LanguageDropDown>; type Meta = MetaObj<typeof LanguageDropDown>; diff --git a/apps/site/components/Common/LanguageDropDown/index.tsx b/packages/ui-components/Common/LanguageDropDown/index.tsx similarity index 84% rename from apps/site/components/Common/LanguageDropDown/index.tsx rename to packages/ui-components/Common/LanguageDropDown/index.tsx index 5c71e3c7b3450..d9f845198b483 100644 --- a/apps/site/components/Common/LanguageDropDown/index.tsx +++ b/packages/ui-components/Common/LanguageDropDown/index.tsx @@ -1,29 +1,25 @@ import { LanguageIcon } from '@heroicons/react/24/outline'; -import type { LocaleConfig } from '@node-core/website-i18n/types'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import classNames from 'classnames'; -import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import styles from './index.module.css'; +import type { SimpleLocaleConfig } from '@node-core/ui-components/types'; -type SimpleLocaleConfig = Pick<LocaleConfig, 'name' | 'code' | 'localName'>; +import styles from './index.module.css'; type LanguageDropDownProps = { onChange?: (newLocale: SimpleLocaleConfig) => void; currentLanguage: string; availableLanguages: Array<SimpleLocaleConfig>; + ariaLabel: string; }; const LanguageDropdown: FC<LanguageDropDownProps> = ({ onChange = () => {}, currentLanguage, availableLanguages, + ariaLabel, }) => { - const t = useTranslations(); - - const ariaLabel = t('components.common.languageDropdown.label'); - return ( <DropdownMenu.Root> <DropdownMenu.Trigger asChild> diff --git a/packages/ui-components/Common/NodejsLogo/index.module.css b/packages/ui-components/Common/NodejsLogo/index.module.css new file mode 100644 index 0000000000000..cf1c469d1c78d --- /dev/null +++ b/packages/ui-components/Common/NodejsLogo/index.module.css @@ -0,0 +1,3 @@ +.nodejsLogo { + @apply h-6 w-20; +} diff --git a/apps/site/components/Common/NodejsLogo/index.stories.tsx b/packages/ui-components/Common/NodejsLogo/index.stories.tsx similarity index 81% rename from apps/site/components/Common/NodejsLogo/index.stories.tsx rename to packages/ui-components/Common/NodejsLogo/index.stories.tsx index 691b885d1d283..f8c739ef55a01 100644 --- a/apps/site/components/Common/NodejsLogo/index.stories.tsx +++ b/packages/ui-components/Common/NodejsLogo/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import NodejsLogo from '@/components/Common/NodejsLogo'; +import NodejsLogo from '@node-core/ui-components/Common/NodejsLogo'; type Story = StoryObj<typeof NodejsLogo>; type Meta = MetaObj<typeof NodejsLogo>; diff --git a/packages/ui-components/Common/NodejsLogo/index.tsx b/packages/ui-components/Common/NodejsLogo/index.tsx new file mode 100644 index 0000000000000..38c0c995b0d58 --- /dev/null +++ b/packages/ui-components/Common/NodejsLogo/index.tsx @@ -0,0 +1,26 @@ +import type { FC } from 'react'; + +import NodejsIcon from '@node-core/ui-components/Icons/Logos/Nodejs'; +import type { LogoVariant } from '@node-core/ui-components/types'; + +import style from './index.module.css'; + +type NodejsLogoProps = { + variant?: LogoVariant; + ariaLabel?: string; +}; + +const NodejsLogo: FC<NodejsLogoProps> = ({ + variant = 'default', + ariaLabel, +}) => { + return ( + <NodejsIcon + variant={variant} + className={style.nodejsLogo} + ariaLabel={ariaLabel} + /> + ); +}; + +export default NodejsLogo; diff --git a/packages/ui-components/Common/Notification/index.module.css b/packages/ui-components/Common/Notification/index.module.css new file mode 100644 index 0000000000000..89bc810108f51 --- /dev/null +++ b/packages/ui-components/Common/Notification/index.module.css @@ -0,0 +1,7 @@ +.root { + @apply m-6 rounded border border-neutral-200 bg-white px-4 py-3 shadow-lg dark:border-neutral-800 dark:bg-neutral-900; +} + +.message { + @apply font-medium text-green-600 dark:text-white; +} diff --git a/apps/site/components/Common/Notification/index.stories.tsx b/packages/ui-components/Common/Notification/index.stories.tsx similarity index 91% rename from apps/site/components/Common/Notification/index.stories.tsx rename to packages/ui-components/Common/Notification/index.stories.tsx index 884d0bb441876..3bff7438edb61 100644 --- a/apps/site/components/Common/Notification/index.stories.tsx +++ b/packages/ui-components/Common/Notification/index.stories.tsx @@ -1,7 +1,7 @@ import { CodeBracketIcon } from '@heroicons/react/24/solid'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Notification from '@/components/Common/Notification'; +import Notification from '@node-core/ui-components/Common/Notification'; type Story = StoryObj<typeof Notification>; type Meta = MetaObj<typeof Notification>; diff --git a/apps/site/components/Common/Notification/index.tsx b/packages/ui-components/Common/Notification/index.tsx similarity index 100% rename from apps/site/components/Common/Notification/index.tsx rename to packages/ui-components/Common/Notification/index.tsx diff --git a/apps/site/components/Common/PrevNextArrow.tsx b/packages/ui-components/Common/PrevNextArrow.tsx similarity index 100% rename from apps/site/components/Common/PrevNextArrow.tsx rename to packages/ui-components/Common/PrevNextArrow.tsx diff --git a/packages/ui-components/Common/Preview/index.module.css b/packages/ui-components/Common/Preview/index.module.css new file mode 100644 index 0000000000000..ff8dcbbc5fe81 --- /dev/null +++ b/packages/ui-components/Common/Preview/index.module.css @@ -0,0 +1,31 @@ +.root { + @apply @container/preview relative flex aspect-[1.90/1] items-center rounded border border-neutral-900 bg-neutral-950; + + &::after { + @apply bg-gradient-radial @md/preview:blur-3xl absolute inset-0 m-auto aspect-square w-1/3 rounded-full blur-2xl content-['']; + + &.announcements { + @apply from-green-700/90; + } + + &.release { + @apply from-info-600/90; + } + + &.vulnerability { + @apply from-warning-600/90; + } + } + + .container { + @apply @sm/preview:text-base @md/preview:gap-6 @md/preview:text-lg @lg/preview:gap-8 @lg/preview:text-xl @xl/preview:gap-12 @xl/preview:text-2xl @2xl/preview:text-3xl z-10 mx-auto flex w-2/3 max-w-xl flex-col gap-4 text-center text-xs font-semibold text-white; + + .hexagon { + @apply @md/preview:h-3/5 @md/preview:w-3/5 @lg/preview:h-2/3 @lg/preview:w-2/3 @xl/preview:h-3/5 @xl/preview:w-3/5 @2xl/preview:h-2/3 @2xl/preview:w-2/3 absolute inset-0 m-auto size-full; + } + + .logo { + @apply @md/preview:size-14 @lg/preview:size-16 @xl/preview:size-20 mx-auto size-6; + } + } +} diff --git a/apps/site/components/Common/Preview/index.stories.tsx b/packages/ui-components/Common/Preview/index.stories.tsx similarity index 93% rename from apps/site/components/Common/Preview/index.stories.tsx rename to packages/ui-components/Common/Preview/index.stories.tsx index 602ee26f11c26..92a606add2b11 100644 --- a/apps/site/components/Common/Preview/index.stories.tsx +++ b/packages/ui-components/Common/Preview/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Preview from '@/components/Common/Preview'; +import Preview from '@node-core/ui-components/Common/Preview'; type Story = StoryObj<typeof Preview>; type Meta = MetaObj<typeof Preview>; diff --git a/apps/site/components/Common/Preview/index.tsx b/packages/ui-components/Common/Preview/index.tsx similarity index 65% rename from apps/site/components/Common/Preview/index.tsx rename to packages/ui-components/Common/Preview/index.tsx index 489fdc01b18a4..16d06d039805e 100644 --- a/apps/site/components/Common/Preview/index.tsx +++ b/packages/ui-components/Common/Preview/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; import type { FC } from 'react'; -import HexagonGrid from '@/components/Icons/HexagonGrid'; -import JsIconWhite from '@/components/Icons/Logos/JsIconWhite'; -import type { BlogPreviewType } from '@/types'; +import HexagonGrid from '@node-core/ui-components/Icons/HexagonGrid'; +import JsWhiteIcon from '@node-core/ui-components/Icons/Logos/JsWhite'; +import type { BlogPreviewType } from '@node-core/ui-components/types'; import styles from './index.module.css'; @@ -16,7 +16,7 @@ const Preview: FC<PreviewProps> = ({ type = 'announcements', title }) => ( <div className={classNames(styles.root, styles[type])}> <div className={styles.container} aria-hidden={true}> <HexagonGrid className={styles.hexagon} /> - <JsIconWhite className={styles.logo} /> + <JsWhiteIcon className={styles.logo} /> {title} </div> </div> diff --git a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css new file mode 100644 index 0000000000000..5b649e634bd88 --- /dev/null +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css @@ -0,0 +1,21 @@ +.group { + @apply flex flex-col gap-4 text-sm font-medium text-neutral-800 dark:text-neutral-200; + + .items { + @apply relative -left-1 flex flex-col gap-2; + + &::after { + @apply absolute left-[0.45rem] top-0 z-10 h-full w-px bg-neutral-200 content-[''] dark:bg-neutral-800; + } + + a { + &:first-child::before { + @apply absolute bottom-[calc(50%+0.25rem)] left-0 h-20 w-4 bg-white content-[''] dark:bg-neutral-950; + } + + &:last-child::after { + @apply absolute left-0 top-[calc(50%+0.25rem)] h-20 w-4 bg-white content-[''] dark:bg-neutral-950; + } + } + } +} diff --git a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx similarity index 58% rename from apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx rename to packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx index 0a64e67aa3f08..20219c29e6528 100644 --- a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx @@ -1,24 +1,35 @@ import type { ComponentProps, FC } from 'react'; -import ProgressionSidebarItem from '@/components/Common/ProgressionSidebar/ProgressionSidebarItem'; -import type { FormattedMessage } from '@/types'; +import ProgressionSidebarItem from '@node-core/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; import styles from './index.module.css'; type ProgressionSidebarGroupProps = { groupName: FormattedMessage; items: Array<ComponentProps<typeof ProgressionSidebarItem>>; + pathname?: string; + as?: LinkLike; }; const ProgressionSidebarGroup: FC<ProgressionSidebarGroupProps> = ({ groupName, items, + ...props }) => ( <section className={styles.group}> {groupName} <div className={styles.items}> {items.map(({ label, link }) => ( - <ProgressionSidebarItem key={link} label={label} link={link} /> + <ProgressionSidebarItem + key={link} + label={label} + link={link} + {...props} + /> ))} </div> </section> diff --git a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx similarity index 100% rename from apps/site/components/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx rename to packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx diff --git a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css new file mode 100644 index 0000000000000..0831e9ae7e67f --- /dev/null +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css @@ -0,0 +1,19 @@ +a.item { + @apply font-regular relative z-20 flex w-full items-center gap-1 overflow-hidden text-sm text-neutral-800 dark:text-neutral-200; + + &:hover { + @apply text-neutral-900 motion-safe:transition-colors dark:text-white; + } + + svg { + @apply shrink-0 fill-neutral-200 stroke-white stroke-[4] dark:fill-neutral-800 dark:stroke-neutral-950; + } + + &.active { + @apply text-neutral-900 dark:text-white; + + svg { + @apply fill-green-500; + } + } +} diff --git a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx similarity index 52% rename from apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx rename to packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx index 047beac8d7326..1ffcb13dadea0 100644 --- a/apps/site/components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx @@ -1,28 +1,35 @@ import type { FC } from 'react'; -import ActiveLink from '@/components/Common/ActiveLink'; -import ProgressionSidebarIcon from '@/components/Common/ProgressionSidebar/ProgressionSidebarIcon'; -import type { FormattedMessage } from '@/types'; +import BaseActiveLink from '@node-core/ui-components/Common/BaseActiveLink'; +import ProgressionSidebarIcon from '@node-core/ui-components/Common/ProgressionSidebar/ProgressionSidebarIcon'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; import styles from './index.module.css'; type ProgressionSidebarItemProps = { label: FormattedMessage; link: string; + as?: LinkLike; + pathname?: string; }; const ProgressionSidebarItem: FC<ProgressionSidebarItemProps> = ({ label, link, + ...props }) => ( - <ActiveLink + <BaseActiveLink className={styles.item} activeClassName={styles.active} href={link} + {...props} > <ProgressionSidebarIcon /> {label} - </ActiveLink> + </BaseActiveLink> ); export default ProgressionSidebarItem; diff --git a/packages/ui-components/Common/ProgressionSidebar/index.module.css b/packages/ui-components/Common/ProgressionSidebar/index.module.css new file mode 100644 index 0000000000000..8975615d544b4 --- /dev/null +++ b/packages/ui-components/Common/ProgressionSidebar/index.module.css @@ -0,0 +1,11 @@ +.wrapper { + @apply flex w-full flex-col gap-8 overflow-auto border-r-0 border-neutral-200 bg-white px-4 py-6 sm:border-r md:max-w-xs lg:px-6 dark:border-neutral-900 dark:bg-neutral-950; + + > section { + @apply hidden sm:flex; + } + + > span { + @apply flex w-full sm:hidden; + } +} diff --git a/apps/site/components/Common/ProgressionSidebar/index.stories.tsx b/packages/ui-components/Common/ProgressionSidebar/index.stories.tsx similarity index 95% rename from apps/site/components/Common/ProgressionSidebar/index.stories.tsx rename to packages/ui-components/Common/ProgressionSidebar/index.stories.tsx index cff59dc659816..f2594b1d82c75 100644 --- a/apps/site/components/Common/ProgressionSidebar/index.stories.tsx +++ b/packages/ui-components/Common/ProgressionSidebar/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import ProgressionSidebar from '@/components/Common/ProgressionSidebar'; +import ProgressionSidebar from '@node-core/ui-components/Common/ProgressionSidebar'; type Story = StoryObj<typeof ProgressionSidebar>; type Meta = MetaObj<typeof ProgressionSidebar>; diff --git a/apps/site/components/Common/ProgressionSidebar/index.tsx b/packages/ui-components/Common/ProgressionSidebar/index.tsx similarity index 58% rename from apps/site/components/Common/ProgressionSidebar/index.tsx rename to packages/ui-components/Common/ProgressionSidebar/index.tsx index 2fdec5637c339..f571c850af269 100644 --- a/apps/site/components/Common/ProgressionSidebar/index.tsx +++ b/packages/ui-components/Common/ProgressionSidebar/index.tsx @@ -1,26 +1,29 @@ 'use client'; -import { useTranslations } from 'next-intl'; -import type { ComponentProps, FC } from 'react'; -import { useRef } from 'react'; +import { useRef, type ComponentProps, type FC } from 'react'; -import ProgressionSidebarGroup from '@/components/Common/ProgressionSidebar/ProgressionSidebarGroup'; -import WithRouterSelect from '@/components/withRouterSelect'; -import { useClientContext, useNavigationState } from '@/hooks'; +import ProgressionSidebarGroup from '@node-core/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup'; +import Select from '@node-core/ui-components/Common/Select'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type ProgressionSidebarProps = { groups: Array<ComponentProps<typeof ProgressionSidebarGroup>>; + pathname?: string; + title: string; + onSelect: (value: string) => void; + as?: LinkLike; }; -const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => { - const t = useTranslations(); - const { pathname } = useClientContext(); +const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ + groups, + pathname, + title, + onSelect, + as, +}) => { const ref = useRef<HTMLElement>(null); - - useNavigationState('progressionSidebar', ref); - const selectItems = groups.map(({ items, groupName }) => ({ label: groupName, items: items.map(({ label, link }) => ({ value: link, label })), @@ -33,8 +36,9 @@ const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => { return ( <nav className={styles.wrapper} ref={ref}> - <WithRouterSelect - label={t('components.common.sidebar.title')} + <Select + label={title} + onChange={onSelect} values={selectItems} defaultValue={currentItem?.value} /> @@ -44,6 +48,8 @@ const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => { key={groupName.toString()} groupName={groupName} items={items} + as={as} + pathname={pathname} /> ))} </nav> diff --git a/apps/site/components/Common/Select/__tests__/index.test.mjs b/packages/ui-components/Common/Select/__tests__/index.test.mjs similarity index 100% rename from apps/site/components/Common/Select/__tests__/index.test.mjs rename to packages/ui-components/Common/Select/__tests__/index.test.mjs diff --git a/packages/ui-components/Common/Select/index.module.css b/packages/ui-components/Common/Select/index.module.css new file mode 100644 index 0000000000000..8c4125439c55c --- /dev/null +++ b/packages/ui-components/Common/Select/index.module.css @@ -0,0 +1,71 @@ +.select { + @apply inline-flex flex-col gap-1.5; + + .label { + @apply block w-full text-sm font-medium text-neutral-800 dark:text-neutral-200; + } + + .trigger { + @apply shadow-xs inline-flex h-11 w-full min-w-[17rem] items-center justify-between gap-2 rounded border border-neutral-300 bg-white px-3.5 py-2.5 text-left text-base font-medium text-neutral-900 outline-none focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500 data-[placeholder]:text-neutral-800 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white dark:focus:border-neutral-600 dark:focus:ring-neutral-600 dark:data-[placeholder]:text-neutral-200; + } + + .trigger span { + @apply flex h-5 items-center gap-2; + } + + .icon { + @apply size-5 text-neutral-600 dark:text-neutral-400; + } +} + +.dropdown { + @apply max-h-48 max-w-xs overflow-hidden overflow-y-auto rounded-md border border-neutral-200 bg-white shadow-lg dark:border-neutral-800 dark:bg-neutral-950; + + .item { + @apply select-none truncate px-2.5 py-1.5 text-sm font-medium; + } + + .text { + @apply text-neutral-800 data-[highlighted]:bg-green-500 data-[highlighted]:text-white data-[highlighted]:outline-none dark:text-neutral-200 dark:data-[highlighted]:bg-green-600 dark:data-[highlighted]:text-white; + } + + .text > span { + @apply flex items-center gap-2; + } + + .text > span > span { + @apply max-w-64 truncate text-wrap; + } + + .label { + @apply text-neutral-600 dark:text-neutral-400; + } +} + +.dropdown:has(.label) .text > span { + &:has(svg) > svg { + @apply ml-3; + } + + &:not(&:has(svg)) > span { + @apply ml-3; + } +} + +.inline { + .trigger { + @apply h-auto min-w-fit px-2.5 py-2 text-sm font-medium; + } + + .icon { + @apply size-4; + } + + .text { + @apply text-neutral-900 data-[highlighted]:bg-neutral-100 data-[disabled]:text-neutral-600 data-[highlighted]:text-neutral-900 dark:text-white dark:data-[highlighted]:bg-neutral-900 dark:data-[disabled]:text-neutral-700 dark:data-[highlighted]:text-white; + } + + &.dropdown { + @apply mt-1 w-[calc(100%+1.5rem)] rounded; + } +} diff --git a/apps/site/components/Common/Select/index.stories.tsx b/packages/ui-components/Common/Select/index.stories.tsx similarity index 94% rename from apps/site/components/Common/Select/index.stories.tsx rename to packages/ui-components/Common/Select/index.stories.tsx index 4c24dd474aca0..23a1bede20d45 100644 --- a/apps/site/components/Common/Select/index.stories.tsx +++ b/packages/ui-components/Common/Select/index.stories.tsx @@ -1,7 +1,7 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Select from '@/components/Common/Select'; -import OSIcons from '@/components/Icons/OperatingSystem'; +import Select from '@node-core/ui-components/Common/Select'; +import * as OSIcons from '@node-core/ui-components/Icons/OperatingSystem'; type Story = StoryObj<typeof Select>; type Meta = MetaObj<typeof Select>; diff --git a/apps/site/components/Common/Select/index.tsx b/packages/ui-components/Common/Select/index.tsx similarity index 97% rename from apps/site/components/Common/Select/index.tsx rename to packages/ui-components/Common/Select/index.tsx index b2abbbd1b848e..88df36658e897 100644 --- a/apps/site/components/Common/Select/index.tsx +++ b/packages/ui-components/Common/Select/index.tsx @@ -7,8 +7,8 @@ import classNames from 'classnames'; import { useEffect, useId, useMemo, useState } from 'react'; import type { ReactElement, ReactNode } from 'react'; -import Skeleton from '@/components/Common/Skeleton'; -import type { FormattedMessage } from '@/types'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; +import type { FormattedMessage } from '@node-core/ui-components/types'; import styles from './index.module.css'; @@ -114,7 +114,6 @@ const Select = <T extends string>({ )); // We explicitly want to recalculate these values only when the values themselves changed // This is to prevent re-rendering and re-calcukating the values on every render - // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(values)]); // Both change the internal state and emit the change event diff --git a/packages/ui-components/Common/Skeleton/index.module.css b/packages/ui-components/Common/Skeleton/index.module.css new file mode 100644 index 0000000000000..8c2564b7de4c5 --- /dev/null +++ b/packages/ui-components/Common/Skeleton/index.module.css @@ -0,0 +1,17 @@ +.skeleton { + @apply pointer-events-none animate-pulse cursor-default select-none rounded-md border-none bg-clip-border text-transparent shadow-none outline-none; +} + +.skeleton[data-inline-skeleton] { + @apply leading-none; +} + +.skeleton:empty { + @apply block h-3; +} + +.skeleton > *, +.skeleton::after, +.skeleton::before { + @apply invisible; +} diff --git a/apps/site/components/Common/Skeleton/index.tsx b/packages/ui-components/Common/Skeleton/index.tsx similarity index 100% rename from apps/site/components/Common/Skeleton/index.tsx rename to packages/ui-components/Common/Skeleton/index.tsx diff --git a/apps/site/components/Common/Tabs/__tests__/index.test.mjs b/packages/ui-components/Common/Tabs/__tests__/index.test.mjs similarity index 93% rename from apps/site/components/Common/Tabs/__tests__/index.test.mjs rename to packages/ui-components/Common/Tabs/__tests__/index.test.mjs index df4cfc4677afa..0d319164bd5bd 100644 --- a/apps/site/components/Common/Tabs/__tests__/index.test.mjs +++ b/packages/ui-components/Common/Tabs/__tests__/index.test.mjs @@ -2,7 +2,6 @@ import * as TabsPrimitive from '@radix-ui/react-tabs'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import Link from '../../../Link'; import Tabs from '../index'; const Sut = ({ addons }) => { @@ -43,7 +42,7 @@ describe('Tabs', () => { }); it('should render the given addons', async () => { - render(<Sut addons={<Link href="/">addon</Link>} />); + render(<Sut addons={<a href="/">addon</a>} />); expect(screen.getByRole('link', { name: 'addon' })).toBeInTheDocument(); }); diff --git a/packages/ui-components/Common/Tabs/index.module.css b/packages/ui-components/Common/Tabs/index.module.css new file mode 100644 index 0000000000000..e3778adc52ac7 --- /dev/null +++ b/packages/ui-components/Common/Tabs/index.module.css @@ -0,0 +1,27 @@ +.tabsRoot { + @apply max-w-full; + + .tabsList { + @apply font-open-sans flex gap-2 overflow-x-auto; + + .tabsTrigger { + @apply whitespace-nowrap border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold text-neutral-800 dark:text-neutral-200; + + .tabSecondaryLabel { + @apply pl-1 text-neutral-500 dark:text-neutral-800; + } + + &[data-state='active'] { + @apply border-b-green-600 text-green-600 dark:border-b-green-400 dark:text-green-400; + + .tabSecondaryLabel { + @apply text-green-800 dark:text-green-600; + } + } + } + + .addons { + @apply ml-auto border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold; + } + } +} diff --git a/apps/site/components/Common/Tabs/index.stories.tsx b/packages/ui-components/Common/Tabs/index.stories.tsx similarity index 94% rename from apps/site/components/Common/Tabs/index.stories.tsx rename to packages/ui-components/Common/Tabs/index.stories.tsx index 5cd3767843033..0325422768505 100644 --- a/apps/site/components/Common/Tabs/index.stories.tsx +++ b/packages/ui-components/Common/Tabs/index.stories.tsx @@ -2,7 +2,7 @@ import * as TabsPrimitive from '@radix-ui/react-tabs'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; import type { ComponentProps } from 'react'; -import Tabs from '@/components/Common/Tabs'; +import Tabs from '@node-core/ui-components/Common/Tabs'; type Story = StoryObj<typeof Tabs>; type Meta = MetaObj<typeof Tabs>; diff --git a/apps/site/components/Common/Tabs/index.tsx b/packages/ui-components/Common/Tabs/index.tsx similarity index 100% rename from apps/site/components/Common/Tabs/index.tsx rename to packages/ui-components/Common/Tabs/index.tsx diff --git a/apps/site/components/Common/ThemeToggle/__tests__/index.test.mjs b/packages/ui-components/Common/ThemeToggle/__tests__/index.test.mjs similarity index 100% rename from apps/site/components/Common/ThemeToggle/__tests__/index.test.mjs rename to packages/ui-components/Common/ThemeToggle/__tests__/index.test.mjs diff --git a/packages/ui-components/Common/ThemeToggle/index.module.css b/packages/ui-components/Common/ThemeToggle/index.module.css new file mode 100644 index 0000000000000..b4ab38e2a3fa2 --- /dev/null +++ b/packages/ui-components/Common/ThemeToggle/index.module.css @@ -0,0 +1,7 @@ +.themeToggle { + @apply size-9 rounded-md p-2 text-neutral-700 motion-safe:transition-colors dark:text-neutral-300; + + &:hover { + @apply bg-neutral-100 dark:bg-neutral-900; + } +} diff --git a/apps/site/components/Common/ThemeToggle/index.stories.tsx b/packages/ui-components/Common/ThemeToggle/index.stories.tsx similarity index 77% rename from apps/site/components/Common/ThemeToggle/index.stories.tsx rename to packages/ui-components/Common/ThemeToggle/index.stories.tsx index d249c438179ed..a09fe58a118c0 100644 --- a/apps/site/components/Common/ThemeToggle/index.stories.tsx +++ b/packages/ui-components/Common/ThemeToggle/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import ThemeToggle from '@/components/Common/ThemeToggle'; +import ThemeToggle from '@node-core/ui-components/Common/ThemeToggle'; type Story = StoryObj<typeof ThemeToggle>; type Meta = MetaObj<typeof ThemeToggle>; diff --git a/apps/site/components/Common/ThemeToggle/index.tsx b/packages/ui-components/Common/ThemeToggle/index.tsx similarity index 73% rename from apps/site/components/Common/ThemeToggle/index.tsx rename to packages/ui-components/Common/ThemeToggle/index.tsx index ac2f06fefc8f4..01ada3d2765be 100644 --- a/apps/site/components/Common/ThemeToggle/index.tsx +++ b/packages/ui-components/Common/ThemeToggle/index.tsx @@ -1,18 +1,17 @@ import { MoonIcon, SunIcon } from '@heroicons/react/24/outline'; -import { useTranslations } from 'next-intl'; import type { FC, MouseEvent } from 'react'; import styles from './index.module.css'; type ThemeToggleProps = { onClick?: (event: MouseEvent<HTMLButtonElement>) => void; + ariaLabel: string; }; -const ThemeToggle: FC<ThemeToggleProps> = ({ onClick = () => {} }) => { - const t = useTranslations(); - - const ariaLabel = t('components.common.themeToggle.label'); - +const ThemeToggle: FC<ThemeToggleProps> = ({ + onClick = () => {}, + ariaLabel, +}) => { return ( <button type="button" diff --git a/packages/ui-components/Common/Tooltip/index.module.css b/packages/ui-components/Common/Tooltip/index.module.css new file mode 100644 index 0000000000000..1d2cc80ea0a18 --- /dev/null +++ b/packages/ui-components/Common/Tooltip/index.module.css @@ -0,0 +1,27 @@ +.content { + @apply rounded-md border bg-white text-sm font-medium text-neutral-900 shadow-lg dark:bg-neutral-950 dark:text-white; + + &.default { + @apply border-neutral-200 dark:border-neutral-900; + + .arrow { + @apply fill-neutral-200 dark:fill-neutral-900; + } + } + + &.error { + @apply border-danger-400 dark:border-danger-900; + + .arrow { + @apply fill-danger-400 dark:fill-danger-900; + } + } + + &.warning { + @apply border-warning-400 dark:border-warning-900; + + .arrow { + @apply fill-warning-400 dark:fill-warning-900; + } + } +} diff --git a/apps/site/components/Common/Tooltip/index.stories.tsx b/packages/ui-components/Common/Tooltip/index.stories.tsx similarity index 88% rename from apps/site/components/Common/Tooltip/index.stories.tsx rename to packages/ui-components/Common/Tooltip/index.stories.tsx index f37905ad80603..bf4a087dc13b7 100644 --- a/apps/site/components/Common/Tooltip/index.stories.tsx +++ b/packages/ui-components/Common/Tooltip/index.stories.tsx @@ -1,13 +1,12 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Button from '@/components/Common/Button'; -import Tooltip from '@/components/Common/Tooltip'; +import Tooltip from '@node-core/ui-components/Common/Tooltip'; type Story = StoryObj<typeof Tooltip>; type Meta = MetaObj<typeof Tooltip>; const defaultProps = { - children: <Button>Rocket Turtle</Button>, + children: <a>Rocket Turtle</a>, content: <div className="p-3">🚀 🐢</div>, }; diff --git a/apps/site/components/Common/Tooltip/index.tsx b/packages/ui-components/Common/Tooltip/index.tsx similarity index 100% rename from apps/site/components/Common/Tooltip/index.tsx rename to packages/ui-components/Common/Tooltip/index.tsx diff --git a/packages/ui-components/Containers/Footer/index.module.css b/packages/ui-components/Containers/Footer/index.module.css new file mode 100644 index 0000000000000..5a541545ac4ee --- /dev/null +++ b/packages/ui-components/Containers/Footer/index.module.css @@ -0,0 +1,19 @@ +.footer { + @apply flex flex-col items-center gap-6 border-t border-neutral-200 bg-white py-4 sm:px-8 md:flex-row md:justify-between md:py-5 dark:border-neutral-900 dark:bg-neutral-950; + + .sectionPrimary { + @apply flex flex-wrap content-start items-center justify-center gap-1 self-stretch; + + a { + @apply whitespace-nowrap; + } + } + + .sectionSecondary { + @apply flex flex-col items-center gap-1 md:flex-row; + + .social { + @apply flex items-center gap-1; + } + } +} diff --git a/packages/ui-components/Containers/Footer/index.stories.tsx b/packages/ui-components/Containers/Footer/index.stories.tsx new file mode 100644 index 0000000000000..872a0bf48c1ff --- /dev/null +++ b/packages/ui-components/Containers/Footer/index.stories.tsx @@ -0,0 +1,27 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import Footer from '@node-core/ui-components/Containers/Footer'; + +type Story = StoryObj<typeof Footer>; +type Meta = MetaObj<typeof Footer>; + +export const Default: Story = { + args: { + navigation: { + footerLinks: [ + { link: '/', text: 'Home' }, + { link: 'https://openjsf.org', text: 'OpenJS Foundation' }, + ], + socialLinks: [ + { icon: 'github', link: 'https://github.com' }, + { icon: 'mastodon', link: 'https://mastodon.social' }, + { icon: 'twitter', link: 'https://twitter.com' }, + { icon: 'slack', link: 'https://slack.com' }, + { icon: 'linkedin', link: 'https://linkedin.com' }, + { icon: 'bluesky', link: 'https://bsky.app' }, + ], + }, + }, +}; + +export default { component: Footer } as Meta; diff --git a/packages/ui-components/Containers/Footer/index.tsx b/packages/ui-components/Containers/Footer/index.tsx new file mode 100644 index 0000000000000..802a190679881 --- /dev/null +++ b/packages/ui-components/Containers/Footer/index.tsx @@ -0,0 +1,96 @@ +'use client'; + +import type { FC, SVGProps } from 'react'; + +import NavItem from '@node-core/ui-components/Containers/NavBar/NavItem'; +import { + Bluesky, + Discord, + GitHub, + LinkedIn, + Mastodon, + Slack, + X, +} from '@node-core/ui-components/Icons/Social'; + +import type { LinkLike } from '@node-core/ui-components/types'; + +import styles from './index.module.css'; + +const footerSocialIcons: Record<string, React.FC<SVGProps<SVGSVGElement>>> = { + github: GitHub, + mastodon: Mastodon, + twitter: X, + slack: Slack, + linkedin: LinkedIn, + bluesky: Bluesky, + discord: Discord, +}; + +type Navigation = { + socialLinks: Array<{ + icon: string; + link: string; + }>; + footerLinks: Array<{ + text: string; + link: string; + }>; +}; + +const Footer: FC<{ + pathname: string; + as: LinkLike; + navigation: Navigation; +}> = ({ pathname = '/', as = 'a', navigation }) => { + const openJSlink = navigation.footerLinks.at(-1)!; + + return ( + <footer className={styles.footer}> + <div className={styles.sectionPrimary}> + {navigation.footerLinks.slice(0, -1).map(item => ( + <NavItem + key={item.link} + type="footer" + href={item.link} + as={as} + pathname={pathname} + > + {item.text} + </NavItem> + ))} + </div> + + <div className={styles.sectionSecondary}> + <NavItem + type="footer" + href={openJSlink.link} + as={as} + pathname={pathname} + > + © {openJSlink.text} + </NavItem> + + <div className={styles.social}> + {navigation.socialLinks.map(link => { + const SocialIcon = footerSocialIcons[link.icon]; + + return ( + <NavItem + key={link.icon} + href={link.link} + type="footer" + as={as} + pathname={pathname} + > + <SocialIcon width={20} height={20} aria-label={link.link} /> + </NavItem> + ); + })} + </div> + </div> + </footer> + ); +}; + +export default Footer; diff --git a/apps/site/components/Containers/MetaBar/__tests__/index.test.mjs b/packages/ui-components/Containers/MetaBar/__tests__/index.test.mjs similarity index 100% rename from apps/site/components/Containers/MetaBar/__tests__/index.test.mjs rename to packages/ui-components/Containers/MetaBar/__tests__/index.test.mjs diff --git a/packages/ui-components/Containers/MetaBar/index.module.css b/packages/ui-components/Containers/MetaBar/index.module.css new file mode 100644 index 0000000000000..02d5703188157 --- /dev/null +++ b/packages/ui-components/Containers/MetaBar/index.module.css @@ -0,0 +1,43 @@ +.wrapper { + @apply flex w-full flex-col items-start gap-8 overflow-y-auto border-neutral-200 px-4 py-6 [overflow-wrap:anywhere] lg:sticky lg:top-0 lg:h-max lg:min-h-screen lg:px-6 dark:border-neutral-900; + + dl { + @apply w-full; + } + + dt { + @apply mb-2 text-sm font-medium text-neutral-800 dark:text-neutral-200; + } + + dd { + @apply mb-8 flex items-center gap-2 text-sm text-neutral-900 dark:text-white; + + a { + @apply max-xs:inline-block max-xs:py-1 font-semibold text-neutral-900 underline dark:text-white; + + &:hover { + @apply text-neutral-800 dark:text-neutral-200; + } + } + + ol { + @apply flex list-none flex-col gap-1.5 p-0; + } + + svg { + @apply size-4 text-neutral-600 dark:text-neutral-400; + } + + &:last-child { + @apply mb-0; + } + } + + [data-on-dark] { + @apply hidden dark:block; + } + + [data-on-light] { + @apply block dark:hidden; + } +} diff --git a/apps/site/components/Containers/MetaBar/index.stories.tsx b/packages/ui-components/Containers/MetaBar/index.stories.tsx similarity index 71% rename from apps/site/components/Containers/MetaBar/index.stories.tsx rename to packages/ui-components/Containers/MetaBar/index.stories.tsx index adc1cb49d6c6a..9f70782713604 100644 --- a/apps/site/components/Containers/MetaBar/index.stories.tsx +++ b/packages/ui-components/Containers/MetaBar/index.stories.tsx @@ -1,31 +1,12 @@ import { CodeBracketIcon } from '@heroicons/react/24/outline'; import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import MetaBar from '@/components/Containers/MetaBar'; -import GitHub from '@/components/Icons/Social/GitHub'; -import Link from '@/components/Link'; -import WithAvatarGroup from '@/components/withAvatarGroup'; +import MetaBar from '@node-core/ui-components/Containers/MetaBar'; +import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub'; type Story = StoryObj<typeof MetaBar>; type Meta = MetaObj<typeof MetaBar>; -const names = [ - 'ovflowd', - 'bmuenzenmeyer', - 'AugustinMauroy', - 'HinataKah0', - 'Harkunwar', - 'rodion-arr', - 'mikeesto', - 'bnb', - 'benhalverson', - 'aymen94', - 'shanpriyan', - 'Wai-Dung', - 'manishprivet', - 'araujogui', -]; - export const Default: Story = { args: { items: { @@ -35,19 +16,17 @@ export const Default: Story = { 'components.metabar.readingTime': '15 minutes', 'components.metabar.addedIn': 'v1.0.0', 'components.metabar.author': 'The Node.js Project', - 'components.metabar.authors': ( - <WithAvatarGroup usernames={names} limit={6} /> - ), + 'components.metabar.authors': <p>...</p>, 'components.metabar.contribute': ( <> - <GitHub className="fill-neutral-700 dark:fill-neutral-100" /> - <Link href="/contribute">Edit this page</Link> + <GitHubIcon className="fill-neutral-700 dark:fill-neutral-100" /> + <a href="/contribute">Edit this page</a> </> ), 'components.metabar.viewAs': ( <> <CodeBracketIcon /> - <Link href="/json">JSON</Link> + <a href="/json">JSON</a> </> ), }, diff --git a/apps/site/components/Containers/MetaBar/index.tsx b/packages/ui-components/Containers/MetaBar/index.tsx similarity index 67% rename from apps/site/components/Containers/MetaBar/index.tsx rename to packages/ui-components/Containers/MetaBar/index.tsx index e1b06d74f7104..eaca311870cee 100644 --- a/apps/site/components/Containers/MetaBar/index.tsx +++ b/packages/ui-components/Containers/MetaBar/index.tsx @@ -1,27 +1,31 @@ import type { Heading } from '@vcarl/remark-headings'; -import { useTranslations } from 'next-intl'; import { Fragment, useMemo } from 'react'; import type { FC } from 'react'; -import Link from '@/components/Link'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type MetaBarProps = { - items: Partial<Record<IntlMessageKeys, React.ReactNode>>; + items: Partial<Record<string, React.ReactNode>>; headings?: { items: Array<Heading>; minDepth?: number; }; + as?: LinkLike; + heading: string; }; -const MetaBar: FC<MetaBarProps> = ({ items, headings }) => { - const t = useTranslations(); - +const MetaBar: FC<MetaBarProps> = ({ + items, + headings, + as: Component = 'a', + heading, +}) => { // The default depth of headings to display in the table of contents. const { minDepth = 2, items: headingItems = [] } = headings || {}; - const heading = useMemo( + const filteredHeadings = useMemo( () => headingItems.filter(({ depth }) => depth >= minDepth && depth <= 4), [minDepth, headingItems] ); @@ -33,24 +37,27 @@ const MetaBar: FC<MetaBarProps> = ({ items, headings }) => { .filter(([, value]) => !!value) .map(([key, value]) => ( <Fragment key={key}> - <dt>{t(key as IntlMessageKeys)}</dt> + <dt>{key}</dt> <dd>{value}</dd> </Fragment> ))} - {heading.length > 0 && ( + {filteredHeadings.length > 0 && ( <> - <dt>{t('components.metabar.tableOfContents')}</dt> + <dt>{heading}</dt> <dd> <ol> - {heading.map(head => ( + {filteredHeadings.map(head => ( <li key={head.value} className={ head.depth === 3 ? 'pl-2' : head.depth === 4 ? 'pl-4' : '' } > - <Link href={`#${head?.data?.id}`}> {head.value}</Link> + <Component href={`#${head?.data?.id}`}> + {' '} + {head.value} + </Component> </li> ))} </ol> diff --git a/apps/site/components/Containers/NavBar/NavItem/index.module.css b/packages/ui-components/Containers/NavBar/NavItem/index.module.css similarity index 100% rename from apps/site/components/Containers/NavBar/NavItem/index.module.css rename to packages/ui-components/Containers/NavBar/NavItem/index.module.css diff --git a/apps/site/components/Containers/NavBar/NavItem/index.stories.tsx b/packages/ui-components/Containers/NavBar/NavItem/index.stories.tsx similarity index 87% rename from apps/site/components/Containers/NavBar/NavItem/index.stories.tsx rename to packages/ui-components/Containers/NavBar/NavItem/index.stories.tsx index e861afef7691f..3cfae44cdf8fd 100644 --- a/apps/site/components/Containers/NavBar/NavItem/index.stories.tsx +++ b/packages/ui-components/Containers/NavBar/NavItem/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import NavItem from '@/components/Containers/NavBar/NavItem'; +import NavItem from '@node-core/ui-components/Containers/NavBar/NavItem'; type Story = StoryObj<typeof NavItem>; type Meta = MetaObj<typeof NavItem>; @@ -16,6 +16,7 @@ export const WithExternalLink: Story = { args: { href: 'https://nodejs.org/en', children: 'Learn', + target: '_blank', }, }; diff --git a/apps/site/components/Containers/NavBar/NavItem/index.tsx b/packages/ui-components/Containers/NavBar/NavItem/index.tsx similarity index 78% rename from apps/site/components/Containers/NavBar/NavItem/index.tsx rename to packages/ui-components/Containers/NavBar/NavItem/index.tsx index 9e9ff0ce7c248..2daaa4f8c6ce7 100644 --- a/apps/site/components/Containers/NavBar/NavItem/index.tsx +++ b/packages/ui-components/Containers/NavBar/NavItem/index.tsx @@ -2,7 +2,8 @@ import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; import classNames from 'classnames'; import type { FC, HTMLAttributeAnchorTarget, PropsWithChildren } from 'react'; -import ActiveLink from '@/components/Common/ActiveLink'; +import BaseActiveLink from '@node-core/ui-components/Common/BaseActiveLink'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; @@ -13,6 +14,9 @@ type NavItemProps = { type?: NavItemType; className?: string; target?: HTMLAttributeAnchorTarget | undefined; + + pathname: string; + as: LinkLike; }; const NavItem: FC<PropsWithChildren<NavItemProps>> = ({ @@ -21,18 +25,20 @@ const NavItem: FC<PropsWithChildren<NavItemProps>> = ({ children, className, target, + ...props }) => ( - <ActiveLink + <BaseActiveLink + target={target} href={href} className={classNames(styles.navItem, styles[type], className)} activeClassName={styles.active} allowSubPath={href.startsWith('/')} - target={target} + {...props} > <span className={styles.label}>{children}</span> {target === '_blank' && <ArrowUpRightIcon className={styles.icon} />} - </ActiveLink> + </BaseActiveLink> ); export default NavItem; diff --git a/packages/ui-components/Containers/NavBar/index.module.css b/packages/ui-components/Containers/NavBar/index.module.css new file mode 100644 index 0000000000000..2df56c1494350 --- /dev/null +++ b/packages/ui-components/Containers/NavBar/index.module.css @@ -0,0 +1,56 @@ +.container { + @apply border-neutral-200 bg-white lg:flex lg:h-16 lg:flex-row lg:items-center lg:gap-8 lg:border-b lg:px-8 dark:border-neutral-900 dark:bg-neutral-950; +} + +.nodeIconAndMobileItemsToggler { + @apply flex h-16 shrink-0 items-center border-b border-neutral-200 px-4 lg:flex lg:h-full lg:items-center lg:border-0 lg:px-0 dark:border-neutral-900; +} + +.sidebarItemToggler { + @apply absolute right-4 -z-10 -translate-y-[200%] appearance-none opacity-0; +} + +.nodeIconWrapper { + @apply h-[30px] flex-1; +} + +.navInteractionIcon, +.sidebarItemToggler { + @apply size-6; +} + +.sidebarItemTogglerLabel { + @apply block cursor-pointer lg:hidden; +} + +.main { + @apply hidden flex-1 flex-col justify-between gap-4 lg:flex lg:flex-row lg:items-center; +} + +.navItems { + @apply flex flex-col gap-0 border-b border-neutral-200 p-4 lg:flex-1 lg:flex-row lg:gap-1 lg:border-0 lg:p-0 dark:border-neutral-900; +} + +.actionsWrapper { + @apply flex flex-row flex-wrap items-center justify-between gap-2 border-b border-neutral-200 p-4 sm:flex-nowrap lg:basis-96 lg:border-0 lg:p-0 dark:border-neutral-900; +} + +span.searchButtonSkeleton { + @apply my-px mr-2 flex-grow rounded-xl; + + &:empty { + @apply h-10; + } +} + +.ghIconWrapper { + @apply size-9 rounded-md p-2; + + svg { + @apply fill-neutral-700 dark:fill-neutral-300; + } + + &:hover { + @apply bg-neutral-100 dark:bg-neutral-900; + } +} diff --git a/apps/site/components/Containers/NavBar/index.stories.tsx b/packages/ui-components/Containers/NavBar/index.stories.tsx similarity index 64% rename from apps/site/components/Containers/NavBar/index.stories.tsx rename to packages/ui-components/Containers/NavBar/index.stories.tsx index 5d42306a661bc..665b574fa85aa 100644 --- a/apps/site/components/Containers/NavBar/index.stories.tsx +++ b/packages/ui-components/Containers/NavBar/index.stories.tsx @@ -1,12 +1,18 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import NavBar from '@/components/Containers/NavBar'; +import NavBar from '@node-core/ui-components/Containers/NavBar'; type Story = StoryObj<typeof NavBar>; type Meta = MetaObj<typeof NavBar>; export const Default: Story = { args: { + as: 'a', + Logo: 'a', + pathname: '/', + + children: <a>Some other child</a>, + navItems: [ { text: 'Learn', @@ -33,15 +39,6 @@ export const Default: Story = { link: 'https://openjsf.org/certification', }, ], - languages: { - availableLanguages: [ - { name: 'English', code: 'en', localName: 'English' }, - { name: 'French', code: 'fr', localName: 'Français' }, - { name: 'Spanish', code: 'es', localName: 'Español' }, - ], - currentLanguage: 'en', - }, - onThemeTogglerClick: () => {}, }, }; diff --git a/packages/ui-components/Containers/NavBar/index.tsx b/packages/ui-components/Containers/NavBar/index.tsx new file mode 100644 index 0000000000000..4e6a6c890ef77 --- /dev/null +++ b/packages/ui-components/Containers/NavBar/index.tsx @@ -0,0 +1,94 @@ +'use client'; + +import Hamburger from '@heroicons/react/24/solid/Bars3Icon'; +import XMark from '@heroicons/react/24/solid/XMarkIcon'; +import * as Label from '@radix-ui/react-label'; +import classNames from 'classnames'; +import { useState } from 'react'; +import type { + FC, + HTMLAttributeAnchorTarget, + PropsWithChildren, + ElementType, +} from 'react'; + +import NavItem from '@node-core/ui-components/Containers/NavBar/NavItem'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; + +import style from './index.module.css'; + +const navInteractionIcons = { + show: <Hamburger className={style.navInteractionIcon} />, + close: <XMark className={style.navInteractionIcon} />, +}; + +type NavbarProps = { + navItems: Array<{ + text: FormattedMessage; + link: string; + target?: HTMLAttributeAnchorTarget | undefined; + }>; + Logo: ElementType; + as: LinkLike; + pathname: string; + sidebarItemTogglerAriaLabel: string; +}; + +const NavBar: FC<PropsWithChildren<NavbarProps>> = ({ + children, + Logo, + as: Component = 'a', + navItems, + pathname, + sidebarItemTogglerAriaLabel, +}) => { + const [isMenuOpen, setIsMenuOpen] = useState(false); + + return ( + <nav className={`${style.container}`}> + <div className={style.nodeIconAndMobileItemsToggler}> + <Component className={style.nodeIconWrapper} href="/" aria-label="Home"> + <Logo /> + </Component> + + <Label.Root + className={classNames(['peer', style.sidebarItemToggler])} + htmlFor="sidebarItemToggler" + > + {navInteractionIcons[isMenuOpen ? 'close' : 'show']} + </Label.Root> + </div> + + <input + className={classNames(['peer', style.sidebarItemToggler])} + id="sidebarItemToggler" + type="checkbox" + onChange={e => setIsMenuOpen(() => e.target.checked)} + aria-label={sidebarItemTogglerAriaLabel} + /> + + <div className={`${style.main} peer-checked:flex`}> + <div className={style.navItems}> + {navItems.map(({ text, link, target }) => ( + <NavItem + pathname={pathname} + as={Component} + key={link} + href={link} + target={target} + > + {text} + </NavItem> + ))} + </div> + + <div className={style.actionsWrapper}>{children}</div> + </div> + </nav> + ); +}; + +export default NavBar; diff --git a/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css new file mode 100644 index 0000000000000..69642fb219a48 --- /dev/null +++ b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css @@ -0,0 +1,11 @@ +.group { + @apply flex w-full flex-col gap-2; +} + +.groupName { + @apply px-2 py-1 text-xs font-semibold text-neutral-800 dark:text-neutral-600; +} + +.itemList { + @apply m-0 flex flex-col items-start gap-0.5 p-0; +} diff --git a/apps/site/components/Containers/Sidebar/SidebarGroup/index.stories.tsx b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.stories.tsx similarity index 89% rename from apps/site/components/Containers/Sidebar/SidebarGroup/index.stories.tsx rename to packages/ui-components/Containers/Sidebar/SidebarGroup/index.stories.tsx index 3ee85d621bf0b..5fd052df21822 100644 --- a/apps/site/components/Containers/Sidebar/SidebarGroup/index.stories.tsx +++ b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import SidebarGroup from '@/components/Containers/Sidebar/SidebarGroup'; +import SidebarGroup from '@node-core/ui-components/Containers/Sidebar/SidebarGroup'; type Story = StoryObj<typeof SidebarGroup>; type Meta = MetaObj<typeof SidebarGroup>; diff --git a/packages/ui-components/Containers/Sidebar/SidebarGroup/index.tsx b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.tsx new file mode 100644 index 0000000000000..e26183fe0e58e --- /dev/null +++ b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.tsx @@ -0,0 +1,33 @@ +import type { ComponentProps, FC } from 'react'; + +import SidebarItem from '@node-core/ui-components/Containers/Sidebar/SidebarItem'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; + +import styles from './index.module.css'; + +type SidebarGroupProps = { + groupName: FormattedMessage; + items: Array<Omit<ComponentProps<typeof SidebarItem>, 'as' | 'pathname'>>; + as?: LinkLike; + pathname?: string; +}; + +const SidebarGroup: FC<SidebarGroupProps> = ({ + groupName, + items, + ...props +}) => ( + <section className={styles.group}> + <label className={styles.groupName}>{groupName}</label> + <ul className={styles.itemList}> + {items.map(({ label, link }) => ( + <SidebarItem key={link} label={label} link={link} {...props} /> + ))} + </ul> + </section> +); + +export default SidebarGroup; diff --git a/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css b/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css new file mode 100644 index 0000000000000..d37d727ba6a80 --- /dev/null +++ b/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css @@ -0,0 +1,19 @@ +.sideBarItem { + @apply flex w-full list-none text-neutral-800 dark:text-neutral-200; + + a { + @apply inline-flex items-center gap-2 p-2; + } + + .label { + @apply font-regular w-full text-sm; + } + + .icon { + @apply size-3 text-neutral-500 dark:text-neutral-200; + } +} + +.active { + @apply rounded bg-green-600 text-white dark:text-white; +} diff --git a/apps/site/components/Containers/Sidebar/SidebarItem/index.stories.tsx b/packages/ui-components/Containers/Sidebar/SidebarItem/index.stories.tsx similarity index 78% rename from apps/site/components/Containers/Sidebar/SidebarItem/index.stories.tsx rename to packages/ui-components/Containers/Sidebar/SidebarItem/index.stories.tsx index 3ed6e4383e819..a8cb4c50fda36 100644 --- a/apps/site/components/Containers/Sidebar/SidebarItem/index.stories.tsx +++ b/packages/ui-components/Containers/Sidebar/SidebarItem/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import SidebarItem from '@/components/Containers/Sidebar/SidebarItem'; +import SidebarItem from '@node-core/ui-components/Containers/Sidebar/SidebarItem'; type Story = StoryObj<typeof SidebarItem>; type Meta = MetaObj<typeof SidebarItem>; diff --git a/apps/site/components/Containers/Sidebar/SidebarItem/index.tsx b/packages/ui-components/Containers/Sidebar/SidebarItem/index.tsx similarity index 56% rename from apps/site/components/Containers/Sidebar/SidebarItem/index.tsx rename to packages/ui-components/Containers/Sidebar/SidebarItem/index.tsx index 6650182a133d9..417cb0744f517 100644 --- a/apps/site/components/Containers/Sidebar/SidebarItem/index.tsx +++ b/packages/ui-components/Containers/Sidebar/SidebarItem/index.tsx @@ -1,19 +1,24 @@ import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; import type { FC } from 'react'; -import ActiveLink from '@/components/Common/ActiveLink'; -import type { FormattedMessage } from '@/types'; +import ActiveLink from '@node-core/ui-components/Common/BaseActiveLink'; +import type { + FormattedMessage, + LinkLike, +} from '@node-core/ui-components/types'; import styles from './index.module.css'; type SidebarItemProps = { label: FormattedMessage; link: string; + as?: LinkLike; + pathname?: string; }; -const SidebarItem: FC<SidebarItemProps> = ({ label, link }) => ( +const SidebarItem: FC<SidebarItemProps> = ({ label, link, ...props }) => ( <li className={styles.sideBarItem}> - <ActiveLink href={link} activeClassName={styles.active}> + <ActiveLink href={link} activeClassName={styles.active} {...props}> <span className={styles.label}>{label}</span> {link.startsWith('http') && <ArrowUpRightIcon className={styles.icon} />} diff --git a/packages/ui-components/Containers/Sidebar/index.module.css b/packages/ui-components/Containers/Sidebar/index.module.css new file mode 100644 index 0000000000000..f3b6b9ecc9d38 --- /dev/null +++ b/packages/ui-components/Containers/Sidebar/index.module.css @@ -0,0 +1,11 @@ +.wrapper { + @apply flex w-full flex-col items-start gap-8 overflow-auto overflow-x-hidden border-r-neutral-200 bg-white px-4 py-6 sm:border-r md:max-w-xs lg:px-6 dark:border-r-neutral-900 dark:bg-neutral-950; + + > section { + @apply hidden sm:flex; + } + + > span { + @apply flex w-full sm:hidden; + } +} diff --git a/apps/site/components/Containers/Sidebar/index.stories.tsx b/packages/ui-components/Containers/Sidebar/index.stories.tsx similarity index 93% rename from apps/site/components/Containers/Sidebar/index.stories.tsx rename to packages/ui-components/Containers/Sidebar/index.stories.tsx index d854bdb70e7b5..4fcb32d9379fe 100644 --- a/apps/site/components/Containers/Sidebar/index.stories.tsx +++ b/packages/ui-components/Containers/Sidebar/index.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import Sidebar from '@/components/Containers/Sidebar'; +import Sidebar from '@node-core/ui-components/Containers/Sidebar'; type Story = StoryObj<typeof Sidebar>; type Meta = MetaObj<typeof Sidebar>; @@ -76,6 +76,8 @@ export const Default: Story = { ], }, ], + title: 'Sidebar', + onSelect: console.log, }, }; diff --git a/apps/site/components/Containers/Sidebar/index.tsx b/packages/ui-components/Containers/Sidebar/index.tsx similarity index 57% rename from apps/site/components/Containers/Sidebar/index.tsx rename to packages/ui-components/Containers/Sidebar/index.tsx index ced013a038b84..d2756905294dc 100644 --- a/apps/site/components/Containers/Sidebar/index.tsx +++ b/packages/ui-components/Containers/Sidebar/index.tsx @@ -1,20 +1,26 @@ -import { useTranslations } from 'next-intl'; import type { ComponentProps, FC } from 'react'; -import SidebarGroup from '@/components/Containers/Sidebar/SidebarGroup'; -import WithRouterSelect from '@/components/withRouterSelect'; -import { useClientContext } from '@/hooks/react-server'; +import Select from '@node-core/ui-components/Common/Select'; +import SidebarGroup from '@node-core/ui-components/Containers/Sidebar/SidebarGroup'; +import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; type SidebarProps = { - groups: Array<ComponentProps<typeof SidebarGroup>>; + groups: Array<Omit<ComponentProps<typeof SidebarGroup>, 'as' | 'pathname'>>; + pathname?: string; + title: string; + onSelect: (value: string) => void; + as?: LinkLike; }; -const SideBar: FC<SidebarProps> = ({ groups }) => { - const t = useTranslations(); - const { pathname } = useClientContext(); - +const SideBar: FC<SidebarProps> = ({ + groups, + pathname, + title, + onSelect, + as, +}) => { const selectItems = groups.map(({ items, groupName }) => ({ label: groupName, items: items.map(({ label, link }) => ({ value: link, label })), @@ -28,10 +34,11 @@ const SideBar: FC<SidebarProps> = ({ groups }) => { return ( <aside className={styles.wrapper}> {selectItems.length > 0 && ( - <WithRouterSelect - label={t('components.common.sidebar.title')} + <Select + label={title} values={selectItems} defaultValue={currentItem?.value} + onChange={onSelect} /> )} @@ -40,6 +47,8 @@ const SideBar: FC<SidebarProps> = ({ groups }) => { key={groupName.toString()} groupName={groupName} items={items} + pathname={pathname} + as={as} /> ))} </aside> diff --git a/apps/site/components/Icons/HexagonGrid.stories.tsx b/packages/ui-components/Icons/HexagonGrid.stories.tsx similarity index 77% rename from apps/site/components/Icons/HexagonGrid.stories.tsx rename to packages/ui-components/Icons/HexagonGrid.stories.tsx index 731b2a5ad237c..c5d56a3674728 100644 --- a/apps/site/components/Icons/HexagonGrid.stories.tsx +++ b/packages/ui-components/Icons/HexagonGrid.stories.tsx @@ -1,6 +1,6 @@ import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import HexagonGrid from '@/components/Icons/HexagonGrid'; +import HexagonGrid from '@node-core/ui-components/Icons/HexagonGrid'; type Story = StoryObj<typeof HexagonGrid>; type Meta = MetaObj<typeof HexagonGrid>; diff --git a/apps/site/components/Icons/HexagonGrid.tsx b/packages/ui-components/Icons/HexagonGrid.tsx similarity index 100% rename from apps/site/components/Icons/HexagonGrid.tsx rename to packages/ui-components/Icons/HexagonGrid.tsx diff --git a/apps/site/components/Icons/InstallationMethod/Choco.tsx b/packages/ui-components/Icons/InstallationMethod/Choco.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/Choco.tsx rename to packages/ui-components/Icons/InstallationMethod/Choco.tsx diff --git a/apps/site/components/Icons/InstallationMethod/Devbox.tsx b/packages/ui-components/Icons/InstallationMethod/Devbox.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/Devbox.tsx rename to packages/ui-components/Icons/InstallationMethod/Devbox.tsx diff --git a/apps/site/components/Icons/InstallationMethod/Docker.tsx b/packages/ui-components/Icons/InstallationMethod/Docker.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/Docker.tsx rename to packages/ui-components/Icons/InstallationMethod/Docker.tsx diff --git a/packages/ui-components/Icons/InstallationMethod/FNM.tsx b/packages/ui-components/Icons/InstallationMethod/FNM.tsx new file mode 100644 index 0000000000000..2ca3f84d46b67 --- /dev/null +++ b/packages/ui-components/Icons/InstallationMethod/FNM.tsx @@ -0,0 +1,132 @@ +import type { FC, SVGProps } from 'react'; + +const FNM: FC<SVGProps<SVGSVGElement>> = props => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="32" + height="32" + fill="none" + viewBox="0 0 1142 1269" + {...props} + > + <mask + id="b" + width="1142" + height="1141" + x="0" + y="0" + maskUnits="userSpaceOnUse" + style={{ maskType: 'alpha' }} + > + <path + fill="#A0041E" + d="M.88 553.942 261.56 325.848l521.357 32.585 32.585 521.357-228.094 260.68s.033-195.478-195.509-391.019C196.357 553.909.88 553.942.88 553.942" + ></path> + <path + fill="#FFAC33" + d="M0 1140.47s-1.172-259.995 97.266-358.434c98.439-98.438 359.802-91.661 359.802-91.661s-.033 254.585-97.787 352.335c-97.755 97.76-359.28 97.76-359.28 97.76" + ></path> + <path + fill="#FFCC4D" + d="M261.526 1010.13c71.985 0 130.34-58.355 130.34-130.34 0-71.984-58.355-130.339-130.34-130.339-71.984 0-130.339 58.355-130.339 130.339s58.355 130.34 130.339 130.34" + ></path> + <path + fill="url(#a)" + d="M1141.32 0S815.468 0 424.45 325.848C228.942 488.772 228.942 782.036 294.111 847.206s358.433 65.169 521.357-130.34C1141.32 325.848 1141.32 0 1141.32 0" + ></path> + <path + fill="#A0041E" + d="M228.974 912.376s0-76.259 19.065-95.324c19.064-19.064 247.859-209.692 266.905-190.646s-171.601 247.84-190.666 266.905-95.304 19.065-95.304 19.065" + ></path> + </mask> + <g mask="url(#b)"> + <path + fill="#DA5045" + d="M1 554.094 261.679 326l521.357 32.585 32.585 521.357-228.094 260.678s.033-195.476-195.509-391.017C196.476 554.061 1 554.094 1 554.094" + ></path> + <path + fill="#000" + fillOpacity="0.06" + d="M-5 557.5c176.085-36.724 269.853-84.021 426.5-225.5l-152 323.5z" + ></path> + <path + fill="#FFAC33" + d="M0 1140.47s-1.172-259.995 97.266-358.434c98.439-98.438 359.802-91.661 359.802-91.661s-.033 254.585-97.787 352.335c-97.755 97.76-359.28 97.76-359.28 97.76" + ></path> + <path + fill="#FFCC4D" + d="M261.526 1010.13c71.985 0 130.34-58.355 130.34-130.34 0-71.984-58.355-130.339-130.34-130.339-71.984 0-130.339 58.355-130.339 130.339s58.355 130.34 130.339 130.34" + ></path> + <path + fill="#D9D5D4" + d="M1141.32 0S815.469 0 424.451 325.848c-195.509 162.924-195.509 456.188-130.34 521.358 65.17 65.169 358.433 65.169 521.358-130.34C1141.32 325.848 1141.32 0 1141.32 0" + ></path> + <path + fill="#DA5045" + d="M228.974 912.376s0-76.259 19.065-95.324c19.064-19.064 247.859-209.692 266.905-190.646s-171.601 247.84-190.666 266.905-95.304 19.065-95.304 19.065" + ></path> + <mask + id="c" + width="253" + height="253" + x="823" + y="66" + maskUnits="userSpaceOnUse" + style={{ maskType: 'alpha' }} + > + <path + fill="#62AB5C" + stroke="#63AC5D" + d="m823.635 227.688 37.272-126.542 123.781-34.511 90.192 90.19-30.83 127.462-130.225 33.591z" + ></path> + </mask> + <g mask="url(#c)"> + <path + fill="#DA5045" + d="M1010 433.376 760.134 183.513l-16.566-16.565 207.989-207.99 266.433 266.429z" + ></path> + </g> + <path + fill="#000" + fillOpacity="0.13" + d="M979 1136 297.5 841l216.422-216C786.213 382.599 926.373 235.651 1171.21-31z" + ></path> + </g> + <mask + id="d" + width="915.703" + height="915.703" + x="141.068" + y="389.054" + fill="#000" + maskUnits="userSpaceOnUse" + > + <path fill="#fff" d="M141.068 389.054h915.703v915.703H141.068z"></path> + <path d="M337.103 867.387c-45.767 45.767-44.495 95.348-11.124 128.72l24.155 24.153-35.596 35.6 34.96 34.96 35.597-35.6 123.316 123.32 49.898-49.9-123.316-123.32 49.263-49.259-29.24-40.682-54.983 54.984-22.566-22.566c-16.527-16.527-15.256-31.147 4.767-51.17 12.078-12.077 26.38-22.565 42.271-29.557l-18.752-46.721c-21.294 8.582-43.859 22.248-68.65 47.038m287.825 194.633 50.217-50.22-117.913-117.909c-2.543-21.612-.636-38.139 10.17-48.945 12.395-12.395 23.201-9.217 44.178 11.76l109.331 109.331 50.217-50.216-123.952-123.952c-32.1-32.1-68.014-33.053-96.936-4.131-21.613 21.612-29.24 45.766-27.333 77.549l-22.884-15.891-43.86 43.859zm131.456-480.427c-15.256 15.256-18.752 35.279-17.163 57.209-17.48-9.853-33.371-6.674-46.084 6.039-14.938 14.937-17.163 32.418-13.349 57.208l-19.705-12.713-36.55 36.55 168.765 168.765 43.542-43.542-124.27-124.27c-1.907-15.255 1.272-23.519 6.357-28.604 6.674-6.674 12.713-3.814 24.472 7.946l119.185 119.184 35.596-35.596-124.269-124.27c-1.907-15.255.953-23.201 6.356-28.604 6.674-6.674 12.713-3.814 24.473 7.946l119.184 119.184 42.906-42.906-124.905-124.906c-34.643-34.643-60.704-38.456-84.541-14.62"></path> + </mask> + <path + fill="#FFAC33" + d="M337.103 867.387c-45.767 45.767-44.495 95.348-11.124 128.72l24.155 24.153-35.596 35.6 34.96 34.96 35.597-35.6 123.316 123.32 49.898-49.9-123.316-123.32 49.263-49.259-29.24-40.682-54.983 54.984-22.566-22.566c-16.527-16.527-15.256-31.147 4.767-51.17 12.078-12.077 26.38-22.565 42.271-29.557l-18.752-46.721c-21.294 8.582-43.859 22.248-68.65 47.038m287.825 194.633 50.217-50.22-117.913-117.909c-2.543-21.612-.636-38.139 10.17-48.945 12.395-12.395 23.201-9.217 44.178 11.76l109.331 109.331 50.217-50.216-123.952-123.952c-32.1-32.1-68.014-33.053-96.936-4.131-21.613 21.612-29.24 45.766-27.333 77.549l-22.884-15.891-43.86 43.859zm131.456-480.427c-15.256 15.256-18.752 35.279-17.163 57.209-17.48-9.853-33.371-6.674-46.084 6.039-14.938 14.937-17.163 32.418-13.349 57.208l-19.705-12.713-36.55 36.55 168.765 168.765 43.542-43.542-124.27-124.27c-1.907-15.255 1.272-23.519 6.357-28.604 6.674-6.674 12.713-3.814 24.472 7.946l119.185 119.184 35.596-35.596-124.269-124.27c-1.907-15.255.953-23.201 6.356-28.604 6.674-6.674 12.713-3.814 24.473 7.946l119.184 119.184 42.906-42.906-124.905-124.906c-34.643-34.643-60.704-38.456-84.541-14.62" + ></path> + <path + fill="#fff" + d="m350.134 1020.26 45.046 45.05 45.046-45.05-45.046-45.045zm-35.596 35.6-45.046-45.05-45.046 45.05 45.046 45.04zm34.96 34.96-45.046 45.04 45.046 45.05 45.046-45.05zm35.597-35.6 45.046-45.04-45.046-45.05-45.046 45.05zm123.316 123.32-45.046 45.04 45.046 45.05 45.046-45.05zm49.898-49.9 45.046 45.05 45.046-45.05-45.046-45.05zm-123.316-123.32-45.046-45.043-45.046 45.043 45.046 45.05zm49.263-49.259 45.046 45.049 38.274-38.277-31.591-43.953zm-29.24-40.682 51.729-37.18-43.758-60.882-53.017 53.016zm-54.983 54.984-45.046 45.047 45.046 45.04 45.046-45.04zm24.472-103.293 25.656 58.31 56.427-24.828-22.962-57.211zm-18.752-46.721 59.121-23.728-23.762-59.204-59.17 23.845zm-113.696 1.992c-31.063 31.064-51.501 68.926-53.8 110.539-2.34 42.351 14.647 80.24 42.676 108.27l90.092-90.09c-5.342-5.342-5.676-8.926-5.553-11.15.164-2.961 1.974-12.773 16.677-27.477zm-11.124 218.809 24.155 24.16 90.092-90.095-24.155-24.155zm24.155-65.935-35.596 35.595 90.092 90.09 35.596-35.59zM269.492 1100.9l34.96 34.96 90.092-90.09-34.96-34.96zm125.052 34.96 35.597-35.59-90.092-90.09-35.597 35.59zm-54.495-35.59 123.316 123.31 90.092-90.09-123.316-123.31zm213.408 123.31 49.898-49.89-90.092-90.1-49.898 49.9zm49.898-139.99L480.039 960.277l-90.092 90.093 123.316 123.32zm-123.316-33.22 49.263-49.26-90.092-90.095-49.263 49.262zm55.946-131.49-29.24-40.681-103.458 74.36 29.24 40.682zM409.97 870.333l-54.983 54.984 90.092 90.093 54.983-54.985zm35.109 54.984-22.566-22.566-90.092 90.092 22.566 22.567zm-22.566-22.566c-.782-.782 1.338 1.08 3.518 5.697 2.512 5.32 4.307 12.667 3.552 20.898-.706 7.69-3.317 12.646-4.292 14.265-.852 1.415-.762.813 1.989-1.938l-90.092-90.092c-12.984 12.985-31.635 35.107-34.482 66.119-3.209 34.972 14.933 60.361 29.715 75.143zm4.767 38.922c7.514-7.513 15.434-13.017 22.881-16.293l-51.312-116.62c-24.335 10.707-45.02 26.18-61.661 42.821zm56.346-98.332-18.752-46.72-118.241 47.457 18.752 46.72zm-101.684-82.079c-29.481 11.881-59.216 30.41-89.885 61.079l90.092 90.092c18.912-18.911 34.309-27.714 47.416-32.996zm242.986 300.758-45.046 45.05 45.046 45.04 45.046-45.04zm50.217-50.22 45.046 45.05 45.046-45.05-45.046-45.042zM557.232 893.891l-63.269 7.443 2.584 21.965 15.639 15.638zm163.679 72.146-45.046 45.043 45.046 45.05 45.047-45.05zm50.217-50.216 45.046 45.046 45.046-45.046-45.046-45.046zm-248.221-50.534-36.337 52.325 107.786 74.852-7.859-130.993zm-22.884-15.891 36.337-52.326-43.733-30.37-37.65 37.65zm-43.86 43.859-45.046-45.046-45.046 45.046 45.046 45.046zm213.811 213.815 50.217-50.22-90.092-90.092-50.217 50.212zm50.217-140.312L602.278 848.845l-90.092 90.092 117.913 117.913zm-99.691-80.31c-.426-3.626-.563-6.198-.567-7.936-.004-1.748.131-2.156.026-1.589-.115.625-.536 2.511-1.786 5.077-1.305 2.681-3.216 5.483-5.725 7.992L522.356 799.9c-33.098 33.098-31.285 76.85-28.393 101.434zm-8.052 3.544c-.753.754-5.433 5.344-14.163 8.507-10.064 3.647-20.355 3.56-28.714 1.289-6.934-1.884-10.414-4.685-9.721-4.176.497.366 2.545 2.001 6.684 6.14l90.092-90.092c-10.252-10.253-28.757-28.062-53.651-34.825-15.232-4.138-31.86-4.006-48.092 1.875-14.897 5.398-25.576 14.239-32.527 21.19zm-45.914 11.76 109.331 109.328 90.093-90.089L656.626 811.66zm199.424 109.328 50.216-50.213-90.092-90.092-50.217 50.216zm50.216-140.305L692.222 746.823l-90.092 90.092 123.952 123.952zM692.222 746.823c-23.943-23.943-55.326-42.147-92.269-43.561-37.811-1.447-70.444 15.114-94.759 39.43l90.092 90.092c2.469-2.469 3.703-2.92 3.348-2.765-.521.229-1.864.624-3.555.559-1.608-.061-2.005-.478-.932.056 1.198.597 3.983 2.281 7.983 6.281zm-187.028-4.131c-36.508 36.507-48.693 79.482-45.878 126.41l127.181-7.631c-.998-16.637 2.072-21.971 8.789-28.687zm54.05 70.27L536.36 797.07l-72.674 104.651 22.884 15.891zm-104.267-8.612-43.86 43.859 90.092 90.092 43.86-43.859zm-43.86 133.951 168.765 168.769 90.092-90.1-168.765-168.761zm328.104-299.499-31.28 55.496 103.396 58.278-8.578-118.378zm-59.433 63.247-34.536 53.531 119.041 76.801-21.541-140.019zm-19.705-12.713 34.536-53.531-43.216-27.881-36.366 36.366zm-36.55 36.55-45.046-45.046-45.046 45.046 45.046 45.046zm168.765 168.765-45.046 45.046 45.046 45.046 45.046-45.046zm43.542-43.542 45.046 45.046 45.046-45.046-45.046-45.046zm-124.27-124.27-63.212 7.902 2.711 21.689 15.455 15.455zm150.014 98.526-45.046 45.046 45.046 45.046 45.046-45.046zm35.596-35.596 45.046 45.046 45.046-45.046-45.046-45.046zm-124.269-124.27-63.213 7.902 2.711 21.689 15.456 15.455zm150.013 98.526-45.046 45.046 45.046 45.046 45.046-45.046zm42.906-42.906 45.05 45.046 45.04-45.046-45.04-45.046zM711.338 536.547c-33.958 33.958-37.873 76.252-35.655 106.859l127.076-9.208c-.475-6.557.105-9.434.268-10.042.046-.171-.043.215-.422.891a7.6 7.6 0 0 1-1.175 1.592zm59.163 46.758c-18.768-10.578-41.31-16.552-65.481-12.958-23.931 3.559-43.019 15.537-56.929 29.448l90.092 90.092c-1.198 1.197-5.984 5.229-14.421 6.484-8.677 1.29-14.533-1.347-15.821-2.073zm-122.41 16.49c-16.009 16.009-26.481 35.165-31.024 56.952-4.185 20.072-2.661 39.271-.243 54.989l125.928-19.374c-.658-4.273-.863-7.035-.893-8.731-.029-1.638.118-1.758-.065-.877-.213 1.021-.709 2.61-1.648 4.332-.924 1.697-1.793 2.63-1.963 2.801zm66.233 48.723-19.705-12.713-69.072 107.062 19.705 12.713zm-99.287-4.228-36.55 36.55 90.092 90.092 36.55-36.55zm-36.55 126.642 168.765 168.765 90.092-90.092L668.579 680.84zm258.857 168.765 43.542-43.542-90.092-90.092-43.542 43.542zm43.542-133.634-124.27-124.27-90.092 90.092 124.27 124.27zm-106.103-87.125c-.278-2.225.401.393-.981 5.736-1.612 6.234-5.21 12.988-10.829 18.607l-90.092-90.092c-23.749 23.749-27.889 54.624-24.523 81.552zm-11.81 24.343c-3.029 3.029-10.397 9.474-22.417 12.939-13.12 3.782-25.099 2.124-33.567-.875-7.206-2.552-11.331-5.851-11.68-6.124-.623-.487-.125-.164 2.044 2.006l90.092-90.092c-5.01-5.011-18.743-19.097-37.921-25.89-12.283-4.35-27.758-6.206-44.255-1.45-15.397 4.437-26.022 13.028-32.388 19.394zm-65.62 7.946 119.185 119.184 90.092-90.092-119.185-119.184zM906.63 870.411l35.596-35.596-90.092-90.092-35.596 35.596zm35.596-125.688-124.269-124.27-90.092 90.092 124.269 124.27zm-106.103-87.125c-.356-2.854.413-.138-1.004 5.481-.759 3.009-2.055 6.487-4.111 10.077a42 42 0 0 1-6.695 8.785l-90.092-90.092c-24.404 24.404-27.807 55.283-24.523 81.552zm-11.81 24.343c-3.029 3.029-10.397 9.474-22.416 12.939-13.121 3.781-25.1 2.124-33.568-.875-7.205-2.552-11.331-5.851-11.68-6.124-.623-.488-.125-.164 2.045 2.006l90.092-90.092c-5.011-5.011-18.744-19.098-37.922-25.89-12.282-4.35-27.757-6.206-44.255-1.451-15.396 4.438-26.022 13.029-32.388 19.395zm-65.619 7.946 119.184 119.184 90.092-90.092-119.184-119.184zM967.97 809.071l42.91-42.906-90.096-90.092-42.906 42.906zm42.91-132.998L885.971 551.167l-90.092 90.092 124.905 124.906zM885.971 551.167c-20.39-20.39-47.292-41.431-80.896-47.031-39.377-6.563-71.502 10.176-93.737 32.411l90.092 90.092c1.377-1.376.247.209-3.653 1.719a26.7 26.7 0 0 1-6.936 1.637c-2.586.26-4.881.122-6.712-.184-3.527-.587-4.232-1.591-1.702.008 2.561 1.617 7.017 5.006 13.452 11.44z" + mask="url(#d)" + ></path> + <defs> + <linearGradient + id="a" + x1="406.603" + x2="825.103" + y1="413" + y2="941" + gradientUnits="userSpaceOnUse" + > + <stop stopColor="#D9D5D4"></stop> + <stop offset="1" stopColor="#928989"></stop> + </linearGradient> + </defs> + </svg> +); + +export default FNM; diff --git a/apps/site/components/Icons/InstallationMethod/Homebrew.tsx b/packages/ui-components/Icons/InstallationMethod/Homebrew.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/Homebrew.tsx rename to packages/ui-components/Icons/InstallationMethod/Homebrew.tsx diff --git a/apps/site/components/Icons/InstallationMethod/NVM.tsx b/packages/ui-components/Icons/InstallationMethod/NVM.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/NVM.tsx rename to packages/ui-components/Icons/InstallationMethod/NVM.tsx diff --git a/apps/site/components/Icons/InstallationMethod/Volta.tsx b/packages/ui-components/Icons/InstallationMethod/Volta.tsx similarity index 100% rename from apps/site/components/Icons/InstallationMethod/Volta.tsx rename to packages/ui-components/Icons/InstallationMethod/Volta.tsx diff --git a/packages/ui-components/Icons/InstallationMethod/index.ts b/packages/ui-components/Icons/InstallationMethod/index.ts new file mode 100644 index 0000000000000..a44debeb655f0 --- /dev/null +++ b/packages/ui-components/Icons/InstallationMethod/index.ts @@ -0,0 +1,9 @@ +import Choco from '@node-core/ui-components/Icons/InstallationMethod/Choco'; +import Devbox from '@node-core/ui-components/Icons/InstallationMethod/Devbox'; +import Docker from '@node-core/ui-components/Icons/InstallationMethod/Docker'; +import FNM from '@node-core/ui-components/Icons/InstallationMethod/FNM'; +import Homebrew from '@node-core/ui-components/Icons/InstallationMethod/Homebrew'; +import NVM from '@node-core/ui-components/Icons/InstallationMethod/NVM'; +import Volta from '@node-core/ui-components/Icons/InstallationMethod/Volta'; + +export { Choco, Devbox, Docker, FNM, Homebrew, NVM, Volta }; diff --git a/apps/site/components/Icons/Logos/JsIconGreen.tsx b/packages/ui-components/Icons/Logos/JsGreen.tsx similarity index 95% rename from apps/site/components/Icons/Logos/JsIconGreen.tsx rename to packages/ui-components/Icons/Logos/JsGreen.tsx index dc835cb1d2af7..7071553a28a67 100644 --- a/apps/site/components/Icons/Logos/JsIconGreen.tsx +++ b/packages/ui-components/Icons/Logos/JsGreen.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const JsIconGreen: FC<SVGProps<SVGSVGElement>> = props => ( +const JsGreenIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="71" height="80" @@ -21,4 +21,4 @@ const JsIconGreen: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default JsIconGreen; +export default JsGreenIcon; diff --git a/apps/site/components/Icons/Logos/JsIconWhite.tsx b/packages/ui-components/Icons/Logos/JsWhite.tsx similarity index 94% rename from apps/site/components/Icons/Logos/JsIconWhite.tsx rename to packages/ui-components/Icons/Logos/JsWhite.tsx index 1063212ba130a..0f388784a11c6 100644 --- a/apps/site/components/Icons/Logos/JsIconWhite.tsx +++ b/packages/ui-components/Icons/Logos/JsWhite.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react'; -import type { TailwindSVG } from '@/types/og'; +import type { TailwindSVG } from '@node-core/ui-components/types'; -const JsIconWhite: FC<TailwindSVG> = props => ( +const JsWhiteIcon: FC<TailwindSVG> = props => ( <svg width="71" height="80" @@ -34,4 +34,4 @@ const JsIconWhite: FC<TailwindSVG> = props => ( </svg> ); -export default JsIconWhite; +export default JsWhiteIcon; diff --git a/apps/site/components/Icons/Logos/Nodejs.tsx b/packages/ui-components/Icons/Logos/Nodejs.tsx similarity index 98% rename from apps/site/components/Icons/Logos/Nodejs.tsx rename to packages/ui-components/Icons/Logos/Nodejs.tsx index 08f1624bcec6b..c4c197126ddb9 100644 --- a/apps/site/components/Icons/Logos/Nodejs.tsx +++ b/packages/ui-components/Icons/Logos/Nodejs.tsx @@ -1,26 +1,26 @@ import classNames from 'classnames'; -import { useTranslations } from 'next-intl'; import type { FC, SVGProps } from 'react'; -import type { LogoVariant } from '@/types'; +import type { LogoVariant } from '@node-core/ui-components/types'; type NodeJsLogoProps = SVGProps<SVGSVGElement> & { variant?: LogoVariant; + ariaLabel?: string; }; -const Nodejs: FC<NodeJsLogoProps> = ({ +const NodejsIcon: FC<NodeJsLogoProps> = ({ className, variant = 'default', + ariaLabel = '', ...props }) => { - const t = useTranslations(); return ( <svg width="267" height="80" viewBox="0 0 267 80" fill="none" - aria-label={t('layouts.logo')} + aria-label={ariaLabel} xmlns="http://www.w3.org/2000/svg" className={classNames('fill-[#333333] dark:fill-white', className)} {...props} @@ -185,4 +185,4 @@ const Nodejs: FC<NodeJsLogoProps> = ({ ); }; -export default Nodejs; +export default NodejsIcon; diff --git a/apps/site/components/Icons/Logos/NodejsStackedBlack.tsx b/packages/ui-components/Icons/Logos/NodejsStackedBlack.tsx similarity index 98% rename from apps/site/components/Icons/Logos/NodejsStackedBlack.tsx rename to packages/ui-components/Icons/Logos/NodejsStackedBlack.tsx index 1401ff613303d..51f41235594ad 100644 --- a/apps/site/components/Icons/Logos/NodejsStackedBlack.tsx +++ b/packages/ui-components/Icons/Logos/NodejsStackedBlack.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const NodejsStackedBlack: FC<SVGProps<SVGSVGElement>> = props => ( +const NodejsStackedBlackIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="267" height="164" @@ -95,4 +95,4 @@ const NodejsStackedBlack: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default NodejsStackedBlack; +export default NodejsStackedBlackIcon; diff --git a/apps/site/components/Icons/Logos/NodejsStackedDark.tsx b/packages/ui-components/Icons/Logos/NodejsStackedDark.tsx similarity index 98% rename from apps/site/components/Icons/Logos/NodejsStackedDark.tsx rename to packages/ui-components/Icons/Logos/NodejsStackedDark.tsx index b4082253c667b..fdce4911e705b 100644 --- a/apps/site/components/Icons/Logos/NodejsStackedDark.tsx +++ b/packages/ui-components/Icons/Logos/NodejsStackedDark.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const NodejsStackedDark: FC<SVGProps<SVGSVGElement>> = props => ( +const NodejsStackedDarkIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="289" height="177" @@ -121,4 +121,4 @@ const NodejsStackedDark: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default NodejsStackedDark; +export default NodejsStackedDarkIcon; diff --git a/apps/site/components/Icons/Logos/NodejsStackedLight.tsx b/packages/ui-components/Icons/Logos/NodejsStackedLight.tsx similarity index 98% rename from apps/site/components/Icons/Logos/NodejsStackedLight.tsx rename to packages/ui-components/Icons/Logos/NodejsStackedLight.tsx index 6e47796744849..7c436610465dd 100644 --- a/apps/site/components/Icons/Logos/NodejsStackedLight.tsx +++ b/packages/ui-components/Icons/Logos/NodejsStackedLight.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const NodejsStackedLight: FC<SVGProps<SVGSVGElement>> = props => ( +const NodejsStackedLightIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="319" height="195" @@ -120,4 +120,4 @@ const NodejsStackedLight: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default NodejsStackedLight; +export default NodejsStackedLightIcon; diff --git a/apps/site/components/Icons/Logos/NodejsStackedWhite.tsx b/packages/ui-components/Icons/Logos/NodejsStackedWhite.tsx similarity index 98% rename from apps/site/components/Icons/Logos/NodejsStackedWhite.tsx rename to packages/ui-components/Icons/Logos/NodejsStackedWhite.tsx index 19d83835bb149..f88bb26ab8b2b 100644 --- a/apps/site/components/Icons/Logos/NodejsStackedWhite.tsx +++ b/packages/ui-components/Icons/Logos/NodejsStackedWhite.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const NodejsStackedWhite: FC<SVGProps<SVGSVGElement>> = props => ( +const NodejsStackedWhiteIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="320" height="196" @@ -95,4 +95,4 @@ const NodejsStackedWhite: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default NodejsStackedWhite; +export default NodejsStackedWhiteIcon; diff --git a/packages/ui-components/Icons/Logos/index.ts b/packages/ui-components/Icons/Logos/index.ts new file mode 100644 index 0000000000000..de487f949bc23 --- /dev/null +++ b/packages/ui-components/Icons/Logos/index.ts @@ -0,0 +1,17 @@ +import JsGreen from '@node-core/ui-components/Icons/Logos/JsGreen'; +import JsWhite from '@node-core/ui-components/Icons/Logos/JsWhite'; +import Nodejs from '@node-core/ui-components/Icons/Logos/Nodejs'; +import NodejsStackedBlack from '@node-core/ui-components/Icons/Logos/NodejsStackedBlack'; +import NodejsStackedDark from '@node-core/ui-components/Icons/Logos/NodejsStackedDark'; +import NodejsStackedLight from '@node-core/ui-components/Icons/Logos/NodejsStackedLight'; +import NodejsStackedWhite from '@node-core/ui-components/Icons/Logos/NodejsStackedWhite'; + +export { + JsGreen, + JsWhite, + Nodejs, + NodejsStackedBlack, + NodejsStackedDark, + NodejsStackedLight, + NodejsStackedWhite, +}; diff --git a/apps/site/components/Icons/OperatingSystem/AIX.tsx b/packages/ui-components/Icons/OperatingSystem/AIX.tsx similarity index 100% rename from apps/site/components/Icons/OperatingSystem/AIX.tsx rename to packages/ui-components/Icons/OperatingSystem/AIX.tsx diff --git a/apps/site/components/Icons/OperatingSystem/Apple.tsx b/packages/ui-components/Icons/OperatingSystem/Apple.tsx similarity index 100% rename from apps/site/components/Icons/OperatingSystem/Apple.tsx rename to packages/ui-components/Icons/OperatingSystem/Apple.tsx diff --git a/apps/site/components/Icons/OperatingSystem/Linux.tsx b/packages/ui-components/Icons/OperatingSystem/Linux.tsx similarity index 100% rename from apps/site/components/Icons/OperatingSystem/Linux.tsx rename to packages/ui-components/Icons/OperatingSystem/Linux.tsx diff --git a/apps/site/components/Icons/OperatingSystem/Microsoft.tsx b/packages/ui-components/Icons/OperatingSystem/Microsoft.tsx similarity index 100% rename from apps/site/components/Icons/OperatingSystem/Microsoft.tsx rename to packages/ui-components/Icons/OperatingSystem/Microsoft.tsx diff --git a/packages/ui-components/Icons/OperatingSystem/index.ts b/packages/ui-components/Icons/OperatingSystem/index.ts new file mode 100644 index 0000000000000..161eba3f61f41 --- /dev/null +++ b/packages/ui-components/Icons/OperatingSystem/index.ts @@ -0,0 +1,6 @@ +import AIX from '@node-core/ui-components/Icons/OperatingSystem/AIX'; +import Apple from '@node-core/ui-components/Icons/OperatingSystem/Apple'; +import Linux from '@node-core/ui-components/Icons/OperatingSystem/Linux'; +import Microsoft from '@node-core/ui-components/Icons/OperatingSystem/Microsoft'; + +export { AIX, Apple, Linux, Microsoft }; diff --git a/apps/site/components/Icons/PackageManager/Npm.tsx b/packages/ui-components/Icons/PackageManager/Npm.tsx similarity index 100% rename from apps/site/components/Icons/PackageManager/Npm.tsx rename to packages/ui-components/Icons/PackageManager/Npm.tsx diff --git a/apps/site/components/Icons/PackageManager/Pnpm.tsx b/packages/ui-components/Icons/PackageManager/Pnpm.tsx similarity index 100% rename from apps/site/components/Icons/PackageManager/Pnpm.tsx rename to packages/ui-components/Icons/PackageManager/Pnpm.tsx diff --git a/apps/site/components/Icons/PackageManager/Yarn.tsx b/packages/ui-components/Icons/PackageManager/Yarn.tsx similarity index 100% rename from apps/site/components/Icons/PackageManager/Yarn.tsx rename to packages/ui-components/Icons/PackageManager/Yarn.tsx diff --git a/packages/ui-components/Icons/PackageManager/index.ts b/packages/ui-components/Icons/PackageManager/index.ts new file mode 100644 index 0000000000000..2bf58ab964c5a --- /dev/null +++ b/packages/ui-components/Icons/PackageManager/index.ts @@ -0,0 +1,5 @@ +import NPM from '@node-core/ui-components/Icons/PackageManager/Npm'; +import PNPM from '@node-core/ui-components/Icons/PackageManager/Pnpm'; +import YARN from '@node-core/ui-components/Icons/PackageManager/Yarn'; + +export { NPM, PNPM, YARN }; diff --git a/apps/site/components/Icons/Social/Bluesky.tsx b/packages/ui-components/Icons/Social/Bluesky.tsx similarity index 90% rename from apps/site/components/Icons/Social/Bluesky.tsx rename to packages/ui-components/Icons/Social/Bluesky.tsx index 944e763d67514..374a0a74fdfe1 100644 --- a/apps/site/components/Icons/Social/Bluesky.tsx +++ b/packages/ui-components/Icons/Social/Bluesky.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const Bluesky: FC<SVGProps<SVGSVGElement>> = props => ( +const BlueskyIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -16,4 +16,4 @@ const Bluesky: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default Bluesky; +export default BlueskyIcon; diff --git a/apps/site/components/Icons/Social/Discord.tsx b/packages/ui-components/Icons/Social/Discord.tsx similarity index 96% rename from apps/site/components/Icons/Social/Discord.tsx rename to packages/ui-components/Icons/Social/Discord.tsx index 74a369997acfb..b026ceac2008c 100644 --- a/apps/site/components/Icons/Social/Discord.tsx +++ b/packages/ui-components/Icons/Social/Discord.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const Bluesky: FC<SVGProps<SVGSVGElement>> = props => ( +const Discord: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -17,4 +17,4 @@ const Bluesky: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default Bluesky; +export default Discord; diff --git a/apps/site/components/Icons/Social/GitHub.tsx b/packages/ui-components/Icons/Social/GitHub.tsx similarity index 94% rename from apps/site/components/Icons/Social/GitHub.tsx rename to packages/ui-components/Icons/Social/GitHub.tsx index e771e66f2e6e6..cec572a73ce95 100644 --- a/apps/site/components/Icons/Social/GitHub.tsx +++ b/packages/ui-components/Icons/Social/GitHub.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const GitHub: FC<SVGProps<SVGSVGElement>> = props => ( +const GitHubIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -13,4 +13,4 @@ const GitHub: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default GitHub; +export default GitHubIcon; diff --git a/apps/site/components/Icons/Social/LinkedIn.tsx b/packages/ui-components/Icons/Social/LinkedIn.tsx similarity index 85% rename from apps/site/components/Icons/Social/LinkedIn.tsx rename to packages/ui-components/Icons/Social/LinkedIn.tsx index 4569e8680faca..f3077a9444a58 100644 --- a/apps/site/components/Icons/Social/LinkedIn.tsx +++ b/packages/ui-components/Icons/Social/LinkedIn.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const LinkedIn: FC<SVGProps<SVGSVGElement>> = props => ( +const LinkedInIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -13,4 +13,4 @@ const LinkedIn: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default LinkedIn; +export default LinkedInIcon; diff --git a/apps/site/components/Icons/Social/Mastodon.tsx b/packages/ui-components/Icons/Social/Mastodon.tsx similarity index 96% rename from apps/site/components/Icons/Social/Mastodon.tsx rename to packages/ui-components/Icons/Social/Mastodon.tsx index c78185b77ddf3..90034d7a866ee 100644 --- a/apps/site/components/Icons/Social/Mastodon.tsx +++ b/packages/ui-components/Icons/Social/Mastodon.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const Mastodon: FC<SVGProps<SVGSVGElement>> = props => ( +const MastodonIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -33,4 +33,4 @@ const Mastodon: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default Mastodon; +export default MastodonIcon; diff --git a/apps/site/components/Icons/Social/Slack.tsx b/packages/ui-components/Icons/Social/Slack.tsx similarity index 92% rename from apps/site/components/Icons/Social/Slack.tsx rename to packages/ui-components/Icons/Social/Slack.tsx index 82f454e65d661..37098a5b12879 100644 --- a/apps/site/components/Icons/Social/Slack.tsx +++ b/packages/ui-components/Icons/Social/Slack.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const Slack: FC<SVGProps<SVGSVGElement>> = props => ( +const SlackIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -28,4 +28,4 @@ const Slack: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default Slack; +export default SlackIcon; diff --git a/apps/site/components/Icons/Social/Twitter.tsx b/packages/ui-components/Icons/Social/X.tsx similarity index 82% rename from apps/site/components/Icons/Social/Twitter.tsx rename to packages/ui-components/Icons/Social/X.tsx index e6373f539a68f..4c1c9d583b90f 100644 --- a/apps/site/components/Icons/Social/Twitter.tsx +++ b/packages/ui-components/Icons/Social/X.tsx @@ -1,6 +1,6 @@ import type { FC, SVGProps } from 'react'; -const Twitter: FC<SVGProps<SVGSVGElement>> = props => ( +const XIcon: FC<SVGProps<SVGSVGElement>> = props => ( <svg width="20" height="20" @@ -13,4 +13,4 @@ const Twitter: FC<SVGProps<SVGSVGElement>> = props => ( </svg> ); -export default Twitter; +export default XIcon; diff --git a/packages/ui-components/Icons/Social/index.ts b/packages/ui-components/Icons/Social/index.ts new file mode 100644 index 0000000000000..4f9da6edd82f3 --- /dev/null +++ b/packages/ui-components/Icons/Social/index.ts @@ -0,0 +1,9 @@ +import Bluesky from '@node-core/ui-components/Icons/Social/Bluesky'; +import Discord from '@node-core/ui-components/Icons/Social/Discord'; +import GitHub from '@node-core/ui-components/Icons/Social/GitHub'; +import LinkedIn from '@node-core/ui-components/Icons/Social/LinkedIn'; +import Mastodon from '@node-core/ui-components/Icons/Social/Mastodon'; +import Slack from '@node-core/ui-components/Icons/Social/Slack'; +import X from '@node-core/ui-components/Icons/Social/X'; + +export { Bluesky, Discord, GitHub, LinkedIn, Mastodon, Slack, X }; diff --git a/apps/site/components/__design__/colors.stories.tsx b/packages/ui-components/__design__/colors.stories.tsx similarity index 100% rename from apps/site/components/__design__/colors.stories.tsx rename to packages/ui-components/__design__/colors.stories.tsx diff --git a/apps/site/components/__design__/effects.stories.tsx b/packages/ui-components/__design__/effects.stories.tsx similarity index 100% rename from apps/site/components/__design__/effects.stories.tsx rename to packages/ui-components/__design__/effects.stories.tsx diff --git a/apps/site/components/__design__/font-family.stories.tsx b/packages/ui-components/__design__/font-family.stories.tsx similarity index 100% rename from apps/site/components/__design__/font-family.stories.tsx rename to packages/ui-components/__design__/font-family.stories.tsx diff --git a/apps/site/components/__design__/list.stories.tsx b/packages/ui-components/__design__/list.stories.tsx similarity index 100% rename from apps/site/components/__design__/list.stories.tsx rename to packages/ui-components/__design__/list.stories.tsx diff --git a/packages/ui-components/__design__/node-logos.stories.tsx b/packages/ui-components/__design__/node-logos.stories.tsx new file mode 100644 index 0000000000000..61abc86dc6ac5 --- /dev/null +++ b/packages/ui-components/__design__/node-logos.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import { + JsGreen, + JsWhite, + Nodejs, + NodejsStackedBlack, + NodejsStackedDark, + NodejsStackedLight, + NodejsStackedWhite, +} from '@node-core/ui-components/Icons/Logos'; + +export const HorizontalLogo: StoryObj = { + render: () => <Nodejs width={267} height={80} />, +}; + +export const PrideLogo: StoryObj = { + render: () => <Nodejs variant="pride" width={267} height={80} />, +}; + +export const StackedLogos: StoryObj = { + render: () => ( + <div className="flex flex-row gap-4"> + <div className="flex flex-col gap-2"> + {[NodejsStackedDark, NodejsStackedBlack].map((Icon, index) => ( + <Icon + key={index} + className="block dark:hidden" + width={267} + height={164} + /> + ))} + {[NodejsStackedLight, NodejsStackedWhite].map((Icon, index) => ( + <Icon + key={index} + className="hidden dark:block" + width={267} + height={164} + /> + ))} + </div> + </div> + ), +}; + +export const JSSymbols: StoryObj = { + render: () => ( + <div className="flex flex-row gap-4"> + {[JsWhite, JsGreen].map((Icon, index) => ( + <Icon + key={index} + className={index === 0 ? 'hidden dark:block' : 'block dark:hidden'} + width={30} + height={30} + /> + ))} + </div> + ), +}; + +export default { title: 'Design System' } as MetaObj; diff --git a/packages/ui-components/__design__/platform-logos.stories.tsx b/packages/ui-components/__design__/platform-logos.stories.tsx new file mode 100644 index 0000000000000..fa457bf7d6142 --- /dev/null +++ b/packages/ui-components/__design__/platform-logos.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import { + Docker, + Homebrew, + NVM, + Devbox, + Choco, +} from '@node-core/ui-components/Icons/InstallationMethod'; +import { + Apple, + Linux, + Microsoft, + AIX, +} from '@node-core/ui-components/Icons/OperatingSystem'; + +const osIcons = [Apple, Linux, Microsoft, AIX]; +const installMethodIcons = [Docker, Homebrew, NVM, Devbox, Choco]; + +export const PlatformLogos: StoryObj = { + render: () => ( + <div className="flex flex-row gap-4"> + {[osIcons, installMethodIcons].map((icons, idx) => ( + <div key={idx} className="flex flex-col items-center gap-4"> + {icons.map((Icon, index) => ( + <Icon key={index} width={64} height={64} /> + ))} + </div> + ))} + </div> + ), +}; + +export default { title: 'Design System' } as MetaObj; diff --git a/packages/ui-components/__design__/social-logos.stories.tsx b/packages/ui-components/__design__/social-logos.stories.tsx new file mode 100644 index 0000000000000..ab7ddea04721c --- /dev/null +++ b/packages/ui-components/__design__/social-logos.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import { + Bluesky, + Discord, + GitHub, + LinkedIn, + Mastodon, + Slack, + X, +} from '@node-core/ui-components/Icons/Social'; + +const socialIcons = [ + [GitHub, Mastodon, LinkedIn], + [Slack, X, Bluesky], + [Discord], +]; + +export const SocialMediaLogos: StoryObj = { + render: () => ( + <div className="flex flex-row gap-4"> + {socialIcons.map((group, idx) => ( + <div key={idx} className="flex flex-col items-center gap-4"> + {group.map((Icon, index) => ( + <Icon key={index} width={64} height={64} /> + ))} + </div> + ))} + </div> + ), +}; + +export default { title: 'Design System' } as MetaObj; diff --git a/packages/ui-components/__design__/table.stories.tsx b/packages/ui-components/__design__/table.stories.tsx new file mode 100644 index 0000000000000..1bdd818e2a32b --- /dev/null +++ b/packages/ui-components/__design__/table.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +const tableData = [ + ['Data 1', 'Data 2', 'Data 3'], + ['Data 1', 'Data 2', 'Data 3'], + ['Data 1', 'Data 2', 'Data 3'], +]; + +export const Table: StoryObj = { + render: () => ( + <main> + <table> + <thead> + <tr> + {['Column 1', 'Column 2', 'Column 3'].map((col, idx) => ( + <th key={idx}>{col}</th> + ))} + </tr> + </thead> + <tbody> + {tableData.map((row, rowIndex) => ( + <tr key={rowIndex}> + {row.map((cell, cellIndex) => ( + <td key={cellIndex}>{cell}</td> + ))} + </tr> + ))} + </tbody> + </table> + </main> + ), +}; + +export const HeadlessTable: StoryObj = { + render: () => ( + <main> + <table> + <tbody> + {tableData.map((row, rowIndex) => ( + <tr key={rowIndex}> + {row.map((cell, cellIndex) => ( + <td key={cellIndex}>{cell}</td> + ))} + </tr> + ))} + </tbody> + </table> + </main> + ), +}; + +export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/text.stories.tsx b/packages/ui-components/__design__/text.stories.tsx similarity index 100% rename from apps/site/components/__design__/text.stories.tsx rename to packages/ui-components/__design__/text.stories.tsx diff --git a/packages/ui-components/__mocks__/styleMock.js b/packages/ui-components/__mocks__/styleMock.js new file mode 100644 index 0000000000000..f053ebf7976e3 --- /dev/null +++ b/packages/ui-components/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/packages/ui-components/eslint.config.js b/packages/ui-components/eslint.config.js new file mode 100644 index 0000000000000..7edede4a34117 --- /dev/null +++ b/packages/ui-components/eslint.config.js @@ -0,0 +1,68 @@ +import importX from 'eslint-plugin-import-x'; +import react from 'eslint-plugin-react'; +import storybook from 'eslint-plugin-storybook'; +import tseslint from 'typescript-eslint'; + +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + extends: [ + react.configs.flat['jsx-runtime'], + ...tseslint.configs.recommended, + importX.flatConfigs.typescript, + ], + files: ['**/*.{js,mjs,ts,tsx}'], + rules: { + '@typescript-eslint/array-type': ['error', { default: 'generic' }], + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/no-require-imports': 'off', + }, + settings: { + react: { + version: 'detect', + }, + }, + }, + { + files: ['**/*.{tsx}'], + rules: { + '@typescript-eslint/consistent-type-definitions': ['error', 'type'], + 'react/no-unescaped-entities': 'off', + 'react/function-component-definition': [ + 'error', + { + namedComponents: 'arrow-function', + unnamedComponents: 'arrow-function', + }, + ], + 'no-restricted-syntax': [ + 'error', + { + selector: + "ImportDeclaration[source.value='react'][specifiers.0.type='ImportDefaultSpecifier']", + message: + 'Default React import not allowed since we use the TypeScript jsx-transform. If you need a global type that collides with a React named export (such as `MouseEvent`), try using `globalThis.MouseHandler`', + }, + { + selector: + "ImportDeclaration[source.value='react'] :matches(ImportNamespaceSpecifier)", + message: + 'Named * React import is not allowed. Please import what you need from React with Named Imports', + }, + ], + }, + }, + { + files: ['.storybook/**', '**/*.mjs', '**/*.test.*'], + rules: { + 'no-relative-import-paths/no-relative-import-paths': 'off', + }, + }, + { + files: ['components/**/*.stories.tsx'], + extends: [...storybook.configs['flat/recommended']], + } +); diff --git a/packages/ui-components/global.d.ts b/packages/ui-components/global.d.ts new file mode 100644 index 0000000000000..d774364b20387 --- /dev/null +++ b/packages/ui-components/global.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/packages/ui-components/jest.config.mjs b/packages/ui-components/jest.config.mjs new file mode 100644 index 0000000000000..6e656274fd70f --- /dev/null +++ b/packages/ui-components/jest.config.mjs @@ -0,0 +1,30 @@ +export default { + setupFilesAfterEnv: ['@testing-library/jest-dom'], + moduleDirectories: ['node_modules', '<rootDir>/'], + testEnvironment: 'jest-environment-jsdom', + testMatch: ['**/__tests__/*.test.mjs'], + coverageReporters: ['json', 'json-summary'], + reporters: ['default', 'jest-junit'], + moduleNameMapper: { + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js', + }, + transform: { + '^.+\\.(js|jsx|ts|tsx|mjs)$': [ + '@swc/jest', + { + jsc: { + transform: { + react: { + runtime: 'automatic', + }, + }, + parser: { + syntax: 'typescript', + tsx: true, + }, + }, + }, + ], + }, +}; diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json new file mode 100644 index 0000000000000..21efbccc540bd --- /dev/null +++ b/packages/ui-components/package.json @@ -0,0 +1,61 @@ +{ + "name": "@node-core/ui-components", + "type": "module", + "scripts": { + "check-types": "tsc --noEmit", + "lint:js": "eslint \"**/*.{js,mjs,ts,tsx}\"", + "lint:css": "stylelint \"**/*.css\" --allow-empty-input --cache --cache-strategy=content --cache-location=.stylelintcache", + "lint": "turbo run lint:js lint:css", + "lint:fix": "turbo run lint:js lint:css --no-cache -- --fix", + "storybook": "cross-env NODE_NO_WARNINGS=1 storybook dev -p 6006 --quiet --no-open", + "storybook:build": "cross-env NODE_NO_WARNINGS=1 storybook build --quiet --webpack-stats-json", + "test:unit": "cross-env NODE_NO_WARNINGS=1 jest", + "test:unit:watch": "npm run test:unit -- --watch", + "test": "turbo test:unit" + }, + "engines": { + "node": ">=20" + }, + "dependencies": { + "@heroicons/react": "^2.2.0", + "@radix-ui/react-dropdown-menu": "~2.1.6", + "@radix-ui/react-label": "~2.1.2", + "@radix-ui/react-scroll-area": "~1.2.3", + "@radix-ui/react-select": "~2.1.6", + "@radix-ui/react-tabs": "~1.1.3", + "@radix-ui/react-toast": "~1.2.6", + "@radix-ui/react-tooltip": "~1.1.8", + "@savvywombat/tailwindcss-grid-areas": "~4.0.0", + "@tailwindcss/container-queries": "~0.1.1", + "@vcarl/remark-headings": "~0.1.0", + "classnames": "~2.5.1", + "tailwindcss": "~3.4.17" + }, + "devDependencies": { + "@node-core/website-i18n": "*", + "@storybook/addon-controls": "^8.6.3", + "@storybook/addon-interactions": "^8.6.3", + "@storybook/addon-styling-webpack": "^1.0.1", + "@storybook/addon-themes": "^8.6.3", + "@storybook/addon-viewport": "^8.6.3", + "@storybook/addon-webpack5-compiler-swc": "^2.1.0", + "@storybook/react-webpack5": "^8.6.3", + "@swc/jest": "^0.2.37", + "@testing-library/jest-dom": "~6.6.3", + "@testing-library/react": "~16.2.0", + "@testing-library/user-event": "~14.6.1", + "eslint-plugin-react": "~7.37.4", + "eslint-plugin-storybook": "~0.11.3", + "identity-obj-proxy": "^3.0.0", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", + "jest-junit": "16.0.0", + "storybook": "^8.6.3", + "stylelint": "^16.15.0", + "stylelint-config-standard": "^37.0.0", + "stylelint-order": "6.0.4", + "stylelint-selector-bem-pattern": "4.0.1", + "typescript": "~5.8.2", + "typescript-eslint": "~8.26.0" + } +} diff --git a/apps/site/styles/base.css b/packages/ui-components/styles/base.css similarity index 100% rename from apps/site/styles/base.css rename to packages/ui-components/styles/base.css diff --git a/apps/site/styles/effects.css b/packages/ui-components/styles/effects.css similarity index 100% rename from apps/site/styles/effects.css rename to packages/ui-components/styles/effects.css diff --git a/apps/site/styles/index.css b/packages/ui-components/styles/index.css similarity index 100% rename from apps/site/styles/index.css rename to packages/ui-components/styles/index.css diff --git a/apps/site/styles/locals.css b/packages/ui-components/styles/locals.css similarity index 100% rename from apps/site/styles/locals.css rename to packages/ui-components/styles/locals.css diff --git a/apps/site/styles/markdown.css b/packages/ui-components/styles/markdown.css similarity index 97% rename from apps/site/styles/markdown.css rename to packages/ui-components/styles/markdown.css index 4f60ba4a38a91..3984a3ed9c03c 100644 --- a/apps/site/styles/markdown.css +++ b/packages/ui-components/styles/markdown.css @@ -93,10 +93,6 @@ main { &:has(code) { @apply max-xs:decoration-neutral-800 dark:max-xs:decoration-neutral-200; - - code { - @apply text-inherit; - } } } diff --git a/packages/ui-components/tailwind.config.ts b/packages/ui-components/tailwind.config.ts new file mode 100644 index 0000000000000..b560e851bce82 --- /dev/null +++ b/packages/ui-components/tailwind.config.ts @@ -0,0 +1,156 @@ +import type { Config } from 'tailwindcss'; + +export default { + content: ['./**/*.tsx'], + theme: { + colors: { + green: { + 100: '#EDF2EB', + 200: '#C5E5B4', + 300: '#99CC7D', + 400: '#84BA64', + 500: '#5FA04E', + 600: '#417E38', + 700: '#2C682C', + 800: '#2C682C', + 900: '#1A3F1D', + }, + neutral: { + 100: '#F6F7F9', + 200: '#E9EDF0', + 300: '#D9E1E4', + 400: '#CBD4D9', + 500: '#B1BCC2', + 600: '#929FA5', + 700: '#6E7B83', + 800: '#556066', + 900: '#2C3437', + 950: '#0D121C', + }, + danger: { + 100: '#FBF1F0', + 200: '#FAD3D4', + 300: '#FAB6B7', + 400: '#FA8E8E', + 500: '#F65354', + 600: '#DE1A1B', + 700: '#B80C0C', + 800: '#900E0E', + 900: '#661514', + }, + warning: { + 100: '#FDF3E7', + 200: '#FAD9B0', + 300: '#F5BC75', + 400: '#E99C40', + 500: '#D07912', + 600: '#AE5F00', + 700: '#8B4D04', + 800: '#683D08', + 900: '#4D2F0B', + }, + info: { + 100: '#E9F4FA', + 200: '#BCE6FC', + 300: '#8ED4F8', + 400: '#52BAED', + 500: '#229AD6', + 600: '#0C7BB3', + 700: '#066291', + 800: '#074D71', + 900: '#0A3953', + }, + accent1: { + 100: '#F7F1FB', + 200: '#EAD9FB', + 300: '#DBBDF9', + 400: '#C79BF2', + 500: '#AF74E8', + 600: '#9756D6', + 700: '#7D3CBE', + 800: '#642B9E', + 900: '#361B52', + }, + accent2: { + 100: '#FBF0F4', + 200: '#FBD4E6', + 300: '#FBB4D2', + 400: '#F68BB7', + 500: '#ED5393', + 600: '#D6246E', + 700: '#B01356', + 800: '#8B1245', + 900: '#411526', + }, + white: '#FFFFFF', + transparent: 'transparent', + shadow: '#101828', + }, + fontSize: { + xs: ['0.75rem', '1rem'], + sm: ['0.875rem', '1.25rem'], + base: ['1rem', '1.5rem'], + lg: ['1.125rem', '1.75rem'], + xl: ['1.25rem', '1.875rem'], + '2xl': ['1.5rem', '2rem'], + '3xl': ['1.875rem', '2.25rem'], + '4xl': ['2.25rem', '2.5rem'], + '5xl': ['3rem', '3rem'], + '6xl': ['3.75rem', '3.75rem'], + '7xl': ['4.5rem', '4.5rem'], + }, + fontWeight: { + regular: '400', + medium: '500', + semibold: '600', + bold: '700', + }, + // TODO(@avivkeller): The --font-xyz-xyz variable is assigned by Next.js in apps/site. + // Ideally, this package shouldn't depend on the consuming application's + // variables, it should remain self-contained. + fontFamily: { + 'open-sans': ['var(--font-open-sans)'], + 'ibm-plex-mono': ['var(--font-ibm-plex-mono)'], + }, + extend: { + screens: { xs: '670px' }, + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-subtle': + 'linear-gradient(180deg, theme(colors.neutral.100 / 50%) 0%, theme(colors.neutral.100 / 0%) 48.32%)', + 'gradient-subtle-dark': + 'linear-gradient(180deg, theme(colors.neutral.900 / 50%) 0%, theme(colors.neutral.900 / 0%) 48.32%)', + 'gradient-subtle-gray': + 'linear-gradient(180deg, theme(colors.neutral.900) 0%, theme(colors.neutral.900 / 80%) 100%)', + 'gradient-subtle-white': + 'linear-gradient(180deg, theme(colors.white) 0%, theme(colors.white / 80%) 100%)', + 'gradient-glow-backdrop': + 'radial-gradient(8em circle at calc(50%) 10px, theme(colors.green.500), transparent 30%)', + }, + boxShadow: { + xs: '0px 1px 2px 0px theme(colors.shadow / 5%)', + lg: '0px 4px 6px -2px theme(colors.shadow / 3%), 0px 12px 16px -4px theme(colors.shadow / 8%)', + }, + spacing: { '4.5': '1.125rem', '18': '4.5rem' }, + aria: { current: 'current="page"' }, + maxWidth: { '8xl': '95rem' }, + animation: { + surf: 'surf 1s infinite ease-in-out', + }, + keyframes: { + surf: { + '0%': { transform: 'translate(0, 0)' }, + '25%': { transform: 'translate(0, 6px)' }, + '50%': { transform: 'translate(0, -6px)' }, + '75%': { transform: 'translate(0, 3px)' }, + '100%': { transform: 'translate(0, 0)' }, + }, + }, + }, + }, + darkMode: ['selector', '[data-theme="dark"]'], + plugins: [ + require('@savvywombat/tailwindcss-grid-areas'), + require('@tailwindcss/container-queries'), + ], +} satisfies Config; diff --git a/packages/ui-components/tsconfig.json b/packages/ui-components/tsconfig.json new file mode 100644 index 0000000000000..70e9aa7cd51c2 --- /dev/null +++ b/packages/ui-components/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { "@node-core/ui-components/*": ["./*"] } + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ".storybook/**/*.ts", + ".storybook/**/*.tsx" + ], + "exclude": ["node_modules"] +} diff --git a/packages/ui-components/turbo.json b/packages/ui-components/turbo.json new file mode 100644 index 0000000000000..aa32f3355f61f --- /dev/null +++ b/packages/ui-components/turbo.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "globalEnv": ["NODE_ENV"], + "tasks": { + "build": {}, + "lint:js": { + "inputs": ["**/*.{json,mjs,js,ts,tsx}"] + }, + "lint:css": { + "inputs": ["**/*.css"], + "outputs": [".stylelintcache"] + }, + "lint:fix": { + "cache": false + }, + "storybook": { + "cache": false, + "persistent": true + }, + "storybook:build": { + "inputs": ["**/*.{ts,tsx,mjs,css,json}"], + "outputs": ["storybook-static/**"] + }, + "test:unit": { + "inputs": ["**/*.{ts,tsx,mjs}"], + "outputs": ["coverage/**", "junit.xml"] + } + } +} diff --git a/packages/ui-components/types.ts b/packages/ui-components/types.ts new file mode 100644 index 0000000000000..319b3be22d6e4 --- /dev/null +++ b/packages/ui-components/types.ts @@ -0,0 +1,25 @@ +import type { LocaleConfig } from '@node-core/website-i18n/types'; +import type { + SVGProps, + AnchorHTMLAttributes, + ReactElement, + JSXElementConstructor, + ReactNode, +} from 'react'; + +export type LogoVariant = 'default' | 'pride'; +export type TailwindSVG = SVGProps<SVGSVGElement> & { tw?: string }; +export type BlogPreviewType = 'announcements' | 'release' | 'vulnerability'; +export type LinkLike = + | JSXElementConstructor<AnchorHTMLAttributes<HTMLAnchorElement>> + | 'a'; + +export type FormattedMessage = + | string + | ReactElement<HTMLElement, string | JSXElementConstructor<HTMLElement>> + | ReadonlyArray<ReactNode>; + +export type SimpleLocaleConfig = Pick< + LocaleConfig, + 'name' | 'code' | 'localName' +>; From 6365d7e7dd174a2aaab3fb4b15ad21dc26d16f43 Mon Sep 17 00:00:00 2001 From: avivkeller <me@aviv.sh> Date: Tue, 18 Mar 2025 17:37:01 -0400 Subject: [PATCH 2/7] fixup! feat(ui): add ui-components package --- packages/ui-components/Containers/Footer/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui-components/Containers/Footer/index.tsx b/packages/ui-components/Containers/Footer/index.tsx index 802a190679881..d6a2dacbeb482 100644 --- a/packages/ui-components/Containers/Footer/index.tsx +++ b/packages/ui-components/Containers/Footer/index.tsx @@ -12,7 +12,6 @@ import { Slack, X, } from '@node-core/ui-components/Icons/Social'; - import type { LinkLike } from '@node-core/ui-components/types'; import styles from './index.module.css'; From 6ec81def529d76e53e4dcf075cc63c5c687b0d44 Mon Sep 17 00:00:00 2001 From: avivkeller <me@aviv.sh> Date: Tue, 18 Mar 2025 20:33:13 -0400 Subject: [PATCH 3/7] fixup! fixup! feat(ui): add ui-components package --- apps/site/components/withProgressionSidebar.tsx | 2 ++ apps/site/components/withSidebar.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/site/components/withProgressionSidebar.tsx b/apps/site/components/withProgressionSidebar.tsx index ab7397d4a1259..7994de82db2d4 100644 --- a/apps/site/components/withProgressionSidebar.tsx +++ b/apps/site/components/withProgressionSidebar.tsx @@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation'; import { useTranslations, type RichTranslationValues } from 'next-intl'; import type { FC } from 'react'; +import Link from '@/components/Link'; import { useSiteNavigation } from '@/hooks/server'; import { useRouter } from '@/navigation.mjs'; import type { NavigationKeys } from '@/types'; @@ -37,6 +38,7 @@ const WithProgressionSidebar: FC<WithProgressionSidebarProps> = ({ pathname={pathname!} title={t('components.common.sidebar.title')} onSelect={push} + as={Link} /> ); }; diff --git a/apps/site/components/withSidebar.tsx b/apps/site/components/withSidebar.tsx index 83645ecda14b7..a74be89323d85 100644 --- a/apps/site/components/withSidebar.tsx +++ b/apps/site/components/withSidebar.tsx @@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation'; import { useTranslations, type RichTranslationValues } from 'next-intl'; import type { FC } from 'react'; +import Link from '@/components/Link'; import { useSiteNavigation } from '@/hooks/server'; import { useRouter } from '@/navigation.mjs'; import type { NavigationKeys } from '@/types'; @@ -33,6 +34,7 @@ const WithSidebar: FC<WithSidebarProps> = ({ navKeys, context }) => { pathname={pathname} title={t('components.common.sidebar.title')} onSelect={value => push(value)} + as={Link} /> ); }; From 923f3090147799b4d65a81d2895c4d168c119417 Mon Sep 17 00:00:00 2001 From: Aviv Keller <me@aviv.sh> Date: Thu, 20 Mar 2025 10:47:19 -0400 Subject: [PATCH 4/7] allow manual deployment of linting --- .github/workflows/lint-and-tests.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index ecc06785214af..ce87f6c3db8f0 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -16,6 +16,7 @@ on: types: - labeled merge_group: + workflow_dispatch: defaults: run: @@ -49,13 +50,13 @@ jobs: run: echo "turbo_args=--force=true" >> "$GITHUB_OUTPUT" lint: - # This Job should run either on `merge_groups` or `push` events + # This Job should run either on `merge_groups`, `push`, or `workflow_dispatch` events # or `pull_request_target` event with a `labeled` action with a label named `github_actions:pull-request` # since we want to run lint checks against any changes on pull requests, or the final patch on merge groups # or if direct pushes happen to main (or when changes in general land on the `main` (default) branch) # Note that the reason why we run this on pushes against `main` is that on rare cases, maintainers might do direct pushes against `main` if: | - (github.event_name == 'push' || github.event_name == 'merge_group') || + (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request_target' && github.event.label.name == 'github_actions:pull-request') @@ -111,7 +112,7 @@ jobs: # the Pull Request comes from a Crowdin Branch, as we don't want to run ESLint and Prettier on Crowdin PRs # Note: Linting and Prettifying of files on Crowdin PRs is handled by the `translations-pr.yml` Workflow if: | - (github.event_name == 'push' || github.event_name == 'merge_group') || + (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.ref != 'chore/crowdin') # We want to enforce that the actual `turbo@latest` package is used instead of a possible hijack from the user @@ -140,12 +141,12 @@ jobs: key: cache-lint-${{ hashFiles('package-lock.json') }}-${{ hashFiles('.turbo/cache/**') }} tests: - # This Job should run either on `merge_groups` or `push` events + # This Job should run either on `merge_groups`, `push`, or `workflow_dispatch` events # or `pull_request_target` event with a `labeled` action with a label named `github_actions:pull-request` # since we want to run lint checks against any changes on pull requests and on final patches against a pull request. # We don't need to execute the tests again on pushes against (`main`) as the merge group should already handle that if: | - (github.event_name == 'push' || github.event_name == 'merge_group') || + (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request_target' && github.event.label.name == 'github_actions:pull-request') @@ -197,7 +198,7 @@ jobs: # We only need to run Storybook Builds and Storybook Visual Regression Tests within Pull Requests that actually # introduce changes to the Storybook. Hence, we skip running these on Crowdin PRs and Dependabot PRs if: | - github.event_name == 'push' || + github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'dependabot/') == false && github.event.pull_request.head.ref != 'chore/crowdin') From fb0ba90b4d6c4563d5598bfdc89040590639f3d1 Mon Sep 17 00:00:00 2001 From: Aviv Keller <me@aviv.sh> Date: Thu, 20 Mar 2025 11:09:05 -0400 Subject: [PATCH 5/7] Fix indentation in COLLABORATOR_GUIDE.md --- COLLABORATOR_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index f38908df6793d..00316821f8541 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -224,7 +224,7 @@ Finally, if you're unfamiliar with how to use Tailwind or how to use Tailwind wi ```text - ComponentName - - index.tsx // The component itself + - index.tsx // The component itself - index.module.css // Component-specific styles - index.stories.tsx // Storybook stories (only for @node-core/ui-components) - __tests__/ // Component tests (such as unit tests, etc.) From 1b204f513b11ae1c24fc8477e70cc937e9db0158 Mon Sep 17 00:00:00 2001 From: avivkeller <me@aviv.sh> Date: Sat, 22 Mar 2025 09:14:30 -0400 Subject: [PATCH 6/7] resolve reviews --- apps/site/app/[locale]/layout.tsx | 2 +- .../site}/styles/base.css | 0 apps/site/styles/effects.css | 30 +++ apps/site/styles/index.css | 13 ++ .../site}/styles/locals.css | 0 apps/site/styles/markdown.css | 172 ++++++++++++++++++ .../Common/AlertBox/index.module.css | 25 ++- .../AvatarGroup/Avatar/index.module.css | 25 ++- .../AvatarGroup/Avatar/index.stories.tsx | 4 +- .../AvatarGroup/Overlay/index.module.css | 18 +- .../AvatarGroup/Overlay/index.stories.tsx | 8 +- .../Common/AvatarGroup/index.module.css | 4 +- .../Common/Badge/index.module.css | 64 +++++-- .../Common/Banner/index.module.css | 20 +- .../Common/BaseButton/index.module.css | 89 +++++++-- .../Common/BaseCodeBox/index.module.css | 57 +++++- .../Common/BaseCrossLink/index.module.css | 36 +++- .../Common/BaseCrossLink/index.tsx | 2 +- .../Common/BaseLinkTabs/index.module.css | 32 +++- .../BasePagination/Ellipsis/index.module.css | 7 +- .../PaginationListItem/index.module.css | 18 +- .../{ => BasePagination}/PrevNextArrow.tsx | 0 .../Common/BasePagination/index.module.css | 27 +-- .../Common/CodeTabs/index.module.css | 43 ++++- .../Common/GlowingBackdrop/index.module.css | 28 ++- .../Common/LanguageDropDown/index.module.css | 36 +++- .../Common/NodejsLogo/index.module.css | 3 +- .../Common/Notification/index.module.css | 15 +- .../Common/Preview/index.module.css | 60 +++++- .../ProgressionSidebarGroup/index.module.css | 42 ++++- .../ProgressionSidebarItem/index.module.css | 26 ++- .../ProgressionSidebar/index.module.css | 23 ++- .../Common/Select/index.module.css | 108 +++++++++-- .../Common/Skeleton/index.module.css | 14 +- .../Common/Tabs/index.module.css | 36 +++- .../Common/ThemeToggle/index.module.css | 10 +- .../Common/Tooltip/index.module.css | 28 ++- .../Containers/Footer/index.module.css | 33 +++- .../Containers/MetaBar/index.module.css | 57 +++++- .../Containers/NavBar/index.module.css | 92 ++++++++-- .../Sidebar/SidebarGroup/index.module.css | 19 +- .../Sidebar/SidebarItem/index.module.css | 38 ++-- .../Containers/Sidebar/index.module.css | 24 ++- packages/ui-components/styles/effects.css | 31 ---- packages/ui-components/styles/index.css | 3 - packages/ui-components/tailwind.config.ts | 8 + 46 files changed, 1202 insertions(+), 228 deletions(-) rename {packages/ui-components => apps/site}/styles/base.css (100%) create mode 100644 apps/site/styles/effects.css create mode 100644 apps/site/styles/index.css rename {packages/ui-components => apps/site}/styles/locals.css (100%) create mode 100644 apps/site/styles/markdown.css rename packages/ui-components/Common/{ => BasePagination}/PrevNextArrow.tsx (100%) diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index 451521fa975ba..b1887eea5c80e 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -10,7 +10,7 @@ import { availableLocalesMap, defaultLocale } from '@/next.locales.mjs'; import { LocaleProvider } from '@/providers/localeProvider'; import { ThemeProvider } from '@/providers/themeProvider'; -import '@node-core/ui-components/styles/index.css'; +import '@/styles/index.css'; const fontClasses = classNames(IBM_PLEX_MONO.variable, OPEN_SANS.variable); diff --git a/packages/ui-components/styles/base.css b/apps/site/styles/base.css similarity index 100% rename from packages/ui-components/styles/base.css rename to apps/site/styles/base.css diff --git a/apps/site/styles/effects.css b/apps/site/styles/effects.css new file mode 100644 index 0000000000000..a4c4df007ded9 --- /dev/null +++ b/apps/site/styles/effects.css @@ -0,0 +1,30 @@ +.turtle { + @apply animate-surf + absolute + z-10 + translate-x-0 + translate-y-0; +} + +.turtle img { + @apply h-auto + w-24 + md:w-48; +} + +.turtle::after { + @apply absolute + -left-full + top-[20%] + -z-10 + block + h-36 + w-36 + -rotate-90 + select-none + bg-[url('/static/images/smoke.gif')] + opacity-[0.15] + content-[''] + md:-left-1/2 + md:top-1/2; +} diff --git a/apps/site/styles/index.css b/apps/site/styles/index.css new file mode 100644 index 0000000000000..2c1c1b1a53051 --- /dev/null +++ b/apps/site/styles/index.css @@ -0,0 +1,13 @@ +@charset "utf-8"; + +/* IDE Support + * We recommend Stylelint and Tailwind Extensions for better IDE support. + * @see https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss + * @see https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint +*/ + +@import '@node-core/ui-components/styles/index.css'; +@import './base.css'; +@import './markdown.css'; +@import './effects.css'; +@import './locals.css'; diff --git a/packages/ui-components/styles/locals.css b/apps/site/styles/locals.css similarity index 100% rename from packages/ui-components/styles/locals.css rename to apps/site/styles/locals.css diff --git a/apps/site/styles/markdown.css b/apps/site/styles/markdown.css new file mode 100644 index 0000000000000..3984a3ed9c03c --- /dev/null +++ b/apps/site/styles/markdown.css @@ -0,0 +1,172 @@ +main { + @apply flex + w-full + flex-col + gap-6; + + hr { + @apply w-full + border-t + border-t-neutral-200 + bg-white + dark:border-t-neutral-900 + dark:bg-neutral-950; + } + + h1 { + @apply text-3xl; + } + + h2 { + @apply text-2xl; + } + + h3 { + @apply text-xl; + } + + h4 { + @apply text-lg; + } + + h5, + h6 { + @apply text-base; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + &[id] a { + @apply text-neutral-900 + dark:text-white; + } + } + + h1, + h2, + h3, + h4, + h5, + h6, + strong { + @apply font-semibold + text-neutral-900 + dark:text-white; + } + + code { + @apply font-ibm-plex-mono + rounded + bg-neutral-100 + px-1 + text-base + font-semibold + text-neutral-900 + dark:bg-neutral-900 + dark:text-white; + } + + p { + @apply text-neutral-900 + dark:text-white; + } + + a, + .anchor { + @apply max-xs:font-semibold + text-green-600 + dark:text-green-400; + + &:hover { + @apply text-green-900 + dark:text-green-300; + } + + &[role='button'] { + @apply max-xs:font-regular; + } + + &:has(code) { + @apply max-xs:decoration-neutral-800 + dark:max-xs:decoration-neutral-200; + } + } + + ul { + @apply list-disc + pl-9 + pr-5 + leading-6 + text-neutral-900 + dark:text-white; + + li div:has(> pre) { + @apply !my-1; + } + } + + ol { + @apply list-decimal + px-5 + leading-6 + text-neutral-900 + dark:text-white; + + li div:has(> pre) { + @apply !my-1; + } + } + + table { + @apply mb-1 + w-full + border-separate + border-spacing-0 + rounded + border + border-neutral-200 + text-left + text-sm + dark:border-neutral-800; + + th, + td { + @apply max-xs:block + max-xs:border-l-0 + border + border-r-0 + border-t-0 + border-neutral-200 + px-4 + py-2 + text-neutral-900 + dark:border-neutral-800 + dark:text-white; + + > a { + @apply pr-2; + } + } + + th { + @apply font-semibold; + } + + tr:last-child td { + @apply sm:border-b-0; + + &:last-child { + @apply max-xs:border-b-0; + } + } + + td:first-child, + th:first-child { + @apply sm:border-l-0; + } + } +} diff --git a/packages/ui-components/Common/AlertBox/index.module.css b/packages/ui-components/Common/AlertBox/index.module.css index ef2a4346dcd00..c3e31ae888704 100644 --- a/packages/ui-components/Common/AlertBox/index.module.css +++ b/packages/ui-components/Common/AlertBox/index.module.css @@ -1,8 +1,19 @@ .alertBox { - @apply flex flex-row items-center gap-2 rounded px-4 py-3 text-sm text-white; + @apply flex + flex-row + items-center + gap-2 + rounded + px-4 + py-3 + text-sm + text-white; a { - @apply font-bold text-white underline hover:text-white; + @apply font-bold + text-white + underline + hover:text-white; &:hover { @apply no-underline; @@ -10,7 +21,9 @@ } &.small { - @apply gap-1 py-2 text-xs; + @apply gap-1 + py-2 + text-xs; .title { @apply px-1; @@ -18,11 +31,13 @@ } .title { - @apply rounded-sm px-1.5; + @apply rounded-sm + px-1.5; } svg { - @apply inline size-5; + @apply inline + size-5; } &.info { diff --git a/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css b/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css index eb32975fd87ef..b96dded805343 100644 --- a/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css +++ b/packages/ui-components/Common/AvatarGroup/Avatar/index.module.css @@ -1,17 +1,36 @@ .item { - @apply xs:max-h-10 xs:max-w-10 flex h-full max-h-12 w-full max-w-12 items-center justify-center rounded-full border-2 border-transparent bg-neutral-100 object-cover text-xs text-neutral-800 dark:bg-neutral-900 dark:text-neutral-300; + @apply xs:max-h-10 + xs:max-w-10 + flex + h-full + max-h-12 + w-full + max-w-12 + items-center + justify-center + rounded-full + border-2 + border-transparent + bg-neutral-100 + object-cover + text-xs + text-neutral-800 + dark:bg-neutral-900 + dark:text-neutral-300; } .avatar { @apply size-8; .wrapper { - @apply max-xs:block max-xs:py-0; + @apply max-xs:block + max-xs:py-0; } } .small { - @apply xs:size-8 size-10; + @apply xs:size-8 + size-10; } .medium { diff --git a/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx b/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx index 677dba7c6c72b..b1520f9f3cf44 100644 --- a/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx +++ b/packages/ui-components/Common/AvatarGroup/Avatar/index.stories.tsx @@ -7,8 +7,8 @@ type Meta = MetaObj<typeof Avatar>; export const Default: Story = { args: { - image: 'https://avatars.githubusercontent.com/avivkeller', - nickname: 'avivkeller', + image: 'https://avatars.githubusercontent.com/ghost', + nickname: 'ghost', }, }; diff --git a/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css b/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css index 51102089fff83..7c8188658b62c 100644 --- a/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css +++ b/packages/ui-components/Common/AvatarGroup/Overlay/index.module.css @@ -1,5 +1,9 @@ .overlay { - @apply flex min-w-56 items-center gap-2 p-3; + @apply flex + min-w-56 + items-center + gap-2 + p-3; } .user { @@ -7,13 +11,19 @@ } .name { - @apply font-semibold text-neutral-900 dark:text-neutral-300; + @apply font-semibold + text-neutral-900 + dark:text-neutral-300; } .nickname { - @apply font-medium text-neutral-700 dark:text-neutral-500; + @apply font-medium + text-neutral-700 + dark:text-neutral-500; } .arrow { - @apply w-3 fill-neutral-600 dark:fill-white; + @apply w-3 + fill-neutral-600 + dark:fill-white; } diff --git a/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx b/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx index a20795a28d455..77dbb5b53bd57 100644 --- a/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx +++ b/packages/ui-components/Common/AvatarGroup/Overlay/index.stories.tsx @@ -6,10 +6,10 @@ type Story = StoryObj<typeof AvatarOverlay>; type Meta = MetaObj<typeof AvatarOverlay>; const author = { - image: 'https://avatars.githubusercontent.com/avivkeller', - name: 'Aviv Keller', - nickname: 'avivkeller', - fallback: 'AK', + image: 'https://avatars.githubusercontent.com/ghost', + name: 'Ghost User', + nickname: 'ghost', + fallback: 'GU', }; export const Default: Story = { diff --git a/packages/ui-components/Common/AvatarGroup/index.module.css b/packages/ui-components/Common/AvatarGroup/index.module.css index 012397d593eb9..91766d2b7faed 100644 --- a/packages/ui-components/Common/AvatarGroup/index.module.css +++ b/packages/ui-components/Common/AvatarGroup/index.module.css @@ -1,5 +1,7 @@ .avatarGroup { - @apply flex flex-wrap items-center; + @apply flex + flex-wrap + items-center; &:not(.expandable) { > span { diff --git a/packages/ui-components/Common/Badge/index.module.css b/packages/ui-components/Common/Badge/index.module.css index 66c1c33c1845e..40c28b3285188 100644 --- a/packages/ui-components/Common/Badge/index.module.css +++ b/packages/ui-components/Common/Badge/index.module.css @@ -1,12 +1,27 @@ .wrapper { - @apply flex w-fit items-center rounded-full border py-1 pl-1 pr-2.5 text-sm font-medium; + @apply flex + w-fit + items-center + rounded-full + border + py-1 + pl-1 + pr-2.5 + text-sm + font-medium; .icon { @apply size-4; } .badge { - @apply mr-2 rounded-full border px-2.5 py-0.5 text-center text-white; + @apply mr-2 + rounded-full + border + px-2.5 + py-0.5 + text-center + text-white; } .message { @@ -14,50 +29,71 @@ } &.default { - @apply border-green-200 bg-green-100 dark:border-green-700 dark:bg-neutral-900; + @apply border-green-200 + bg-green-100 + dark:border-green-700 + dark:bg-neutral-900; .icon { - @apply text-green-500 dark:text-green-300; + @apply text-green-500 + dark:text-green-300; } .badge { - @apply border-green-200 bg-green-600 dark:border-green-600; + @apply border-green-200 + bg-green-600 + dark:border-green-600; } .message { - @apply text-green-700 dark:text-green-300; + @apply text-green-700 + dark:text-green-300; } } &.error { - @apply border-danger-200 bg-danger-100 dark:border-danger-700 dark:bg-neutral-900; + @apply border-danger-200 + bg-danger-100 + dark:border-danger-700 + dark:bg-neutral-900; .icon { - @apply text-danger-500 dark:text-danger-300; + @apply text-danger-500 + dark:text-danger-300; } .badge { - @apply border-danger-200 bg-danger-600 dark:border-danger-600; + @apply border-danger-200 + bg-danger-600 + dark:border-danger-600; } .message { - @apply text-danger-700 dark:text-danger-300; + @apply text-danger-700 + dark:text-danger-300; } } &.warning { - @apply border-warning-200 bg-warning-100 dark:border-warning-700 dark:bg-neutral-900; + @apply border-warning-200 + bg-warning-100 + dark:border-warning-700 + dark:bg-neutral-900; .icon { - @apply text-warning-500 dark:text-warning-300; + @apply text-warning-500 + dark:text-warning-300; } .badge { - @apply border-warning-200 bg-warning-600 dark:border-warning-600; + @apply border-warning-200 + bg-warning-600 + dark:border-warning-600; } .message { - @apply text-warning-700 dark:text-warning-300; + @apply text-warning-700 + dark:text-warning-300; } } } diff --git a/packages/ui-components/Common/Banner/index.module.css b/packages/ui-components/Common/Banner/index.module.css index 2b96322f8b72a..f3c2b9c1ec77c 100644 --- a/packages/ui-components/Common/Banner/index.module.css +++ b/packages/ui-components/Common/Banner/index.module.css @@ -1,17 +1,29 @@ .banner { - @apply flex w-full flex-row items-center justify-center gap-2 px-8 py-3 text-sm; + @apply flex + w-full + flex-row + items-center + justify-center + gap-2 + px-8 + py-3 + text-sm; &, a { - @apply text-white dark:text-white; + @apply text-white + dark:text-white; } a { - @apply w-fit underline decoration-white/50; + @apply w-fit + underline + decoration-white/50; } svg { - @apply size-4 text-white/50; + @apply size-4 + text-white/50; } } diff --git a/packages/ui-components/Common/BaseButton/index.module.css b/packages/ui-components/Common/BaseButton/index.module.css index 83ceac2c94430..6f2d69b8e9bef 100644 --- a/packages/ui-components/Common/BaseButton/index.module.css +++ b/packages/ui-components/Common/BaseButton/index.module.css @@ -1,5 +1,14 @@ .button { - @apply px-4.5 relative inline-flex items-center justify-center gap-2 py-2.5 text-center font-semibold motion-safe:transition-colors; + @apply px-4.5 + relative + inline-flex + items-center + justify-center + gap-2 + py-2.5 + text-center + font-semibold + motion-safe:transition-colors; svg { @apply size-5; @@ -10,18 +19,24 @@ } &.small { - @apply px-3 py-2 text-sm; + @apply px-3 + py-2 + text-sm; } &.neutral { - @apply rounded bg-neutral-900 text-white dark:text-neutral-200; + @apply rounded + bg-neutral-900 + text-white + dark:text-neutral-200; &:hover:not([aria-disabled='true']) { @apply bg-neutral-800; } &[aria-disabled='true'] { - @apply bg-neutral-900 opacity-50; + @apply bg-neutral-900 + opacity-50; } &:focus { @@ -30,46 +45,90 @@ } &.primary { - @apply rounded border border-green-600 bg-green-600 text-white shadow-sm; + @apply rounded + border + border-green-600 + bg-green-600 + text-white + shadow-sm; &:hover:not([aria-disabled='true']) { - @apply border-green-700 bg-green-700 text-white; + @apply border-green-700 + bg-green-700 + text-white; } &:focus { - @apply border-green-700 bg-green-700; + @apply border-green-700 + bg-green-700; } &[aria-disabled='true'] { - @apply bg-green-600 opacity-50; + @apply bg-green-600 + opacity-50; } } &.secondary { - @apply rounded-lg text-neutral-800 dark:text-neutral-200; + @apply rounded-lg + text-neutral-800 + dark:text-neutral-200; &:hover:not([aria-disabled='true']) { - @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + @apply bg-neutral-100 + text-neutral-800 + dark:bg-neutral-900 + dark:text-neutral-200; } &[aria-disabled='true'] { - @apply bg-transparent opacity-50; + @apply bg-transparent + opacity-50; } &:focus { - @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + @apply bg-neutral-100 + text-neutral-800 + dark:bg-neutral-900 + dark:text-neutral-200; } } &.special { - @apply rounded-lg border border-green-600/30 bg-green-600/10 text-white shadow-sm; + @apply rounded-lg + border + border-green-600/30 + bg-green-600/10 + text-white + shadow-sm; &::before { - @apply bg-gradient-glow-backdrop absolute left-0 right-0 top-0 -z-10 mx-auto h-full w-full opacity-30 content-['']; + @apply bg-gradient-glow-backdrop + absolute + left-0 + right-0 + top-0 + -z-10 + mx-auto + h-full + w-full + opacity-30 + content-['']; } &::after { - @apply absolute -top-px left-0 right-0 mx-auto h-px w-2/5 bg-gradient-to-r from-green-600/0 via-green-600 to-green-600/0 content-['']; + @apply absolute + -top-px + left-0 + right-0 + mx-auto + h-px + w-2/5 + bg-gradient-to-r + from-green-600/0 + via-green-600 + to-green-600/0 + content-['']; } &[aria-disabled='true'] { diff --git a/packages/ui-components/Common/BaseCodeBox/index.module.css b/packages/ui-components/Common/BaseCodeBox/index.module.css index a88eee655aaec..424d721f17e29 100644 --- a/packages/ui-components/Common/BaseCodeBox/index.module.css +++ b/packages/ui-components/Common/BaseCodeBox/index.module.css @@ -1,41 +1,80 @@ .root { - @apply w-full rounded border border-neutral-900 bg-neutral-950; + @apply w-full + rounded + border + border-neutral-900 + bg-neutral-950; .content { - @apply m-0 p-4; + @apply m-0 + p-4; & > code { - @apply font-ibm-plex-mono font-regular grid overflow-x-auto bg-transparent p-0 text-sm leading-snug text-neutral-400 [counter-reset:line]; + @apply font-ibm-plex-mono + font-regular + scrollbar-thin + grid + overflow-x-auto + bg-transparent + p-0 + text-sm + leading-snug + text-neutral-400 + [counter-reset:line]; & > [class='line'] { - @apply relative min-w-0 pl-8; + @apply relative + min-w-0 + pl-8; &:not(:empty:last-child)::before { - @apply inline-block content-['']; + @apply inline-block + content-['']; } &:not(:empty:last-child)::after { - @apply w-4.5 font-ibm-plex-mono absolute left-0 top-0 mr-4 text-right text-neutral-600 [content:counter(line)] [counter-increment:line]; + @apply w-4.5 + font-ibm-plex-mono + absolute + left-0 + top-0 + mr-4 + text-right + text-neutral-600 + [content:counter(line)] + [counter-increment:line]; } } } } & > .footer { - @apply flex items-center justify-between border-t border-t-neutral-900 px-4 py-3 text-sm font-medium; + @apply flex + items-center + justify-between + border-t + border-t-neutral-900 + px-4 + py-3 + text-sm + font-medium; & > .language { @apply text-neutral-400; } & > .action { - @apply px-3 py-1.5 font-medium; + @apply px-3 + py-1.5 + font-medium; } } } .notification { - @apply flex items-center gap-3; + @apply flex + items-center + gap-3; } .icon { diff --git a/packages/ui-components/Common/BaseCrossLink/index.module.css b/packages/ui-components/Common/BaseCrossLink/index.module.css index 534ccb342cad3..0e2d8d959e1dd 100644 --- a/packages/ui-components/Common/BaseCrossLink/index.module.css +++ b/packages/ui-components/Common/BaseCrossLink/index.module.css @@ -1,20 +1,46 @@ .crossLink { - @apply flex flex-col items-start gap-2 truncate rounded border border-solid border-neutral-300 bg-white p-3 no-underline dark:border-neutral-900 dark:bg-neutral-950; + @apply flex + flex-col + items-start + gap-2 + truncate + rounded + border + border-solid + border-neutral-300 + bg-white + p-3 + no-underline + dark:border-neutral-900 + dark:bg-neutral-950; .header { - @apply flex items-center gap-2 self-stretch text-xs text-neutral-800 dark:text-neutral-100; + @apply flex + items-center + gap-2 + self-stretch + text-xs + text-neutral-800 + dark:text-neutral-100; &.reverse { - @apply flex-row-reverse justify-start; + @apply flex-row-reverse + justify-start; } .icon { - @apply size-4 text-neutral-600 dark:text-neutral-400; + @apply size-4 + text-neutral-600 + dark:text-neutral-400; } } .content { - @apply self-stretch truncate text-sm text-neutral-900 dark:text-white; + @apply self-stretch + truncate + text-sm + text-neutral-900 + dark:text-white; &.reverse { @apply text-right; diff --git a/packages/ui-components/Common/BaseCrossLink/index.tsx b/packages/ui-components/Common/BaseCrossLink/index.tsx index 517711dfa7bb6..3c15a34c83c48 100644 --- a/packages/ui-components/Common/BaseCrossLink/index.tsx +++ b/packages/ui-components/Common/BaseCrossLink/index.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import type { FC } from 'react'; -import PrevNextArrow from '@node-core/ui-components/Common/PrevNextArrow'; +import PrevNextArrow from '@node-core/ui-components/Common/BasePagination/PrevNextArrow'; import type { LinkLike, FormattedMessage, diff --git a/packages/ui-components/Common/BaseLinkTabs/index.module.css b/packages/ui-components/Common/BaseLinkTabs/index.module.css index 966255eb045cb..c8f766392b409 100644 --- a/packages/ui-components/Common/BaseLinkTabs/index.module.css +++ b/packages/ui-components/Common/BaseLinkTabs/index.module.css @@ -1,19 +1,41 @@ .tabsList { - @apply font-open-sans max-xs:hidden mb-6 mt-10 flex gap-2 border-b border-b-neutral-200 dark:border-b-neutral-800; + @apply font-open-sans + max-xs:hidden + mb-6 + mt-10 + flex + gap-2 + border-b + border-b-neutral-200 + dark:border-b-neutral-800; .tabsTrigger { - @apply border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold text-neutral-800 dark:text-neutral-200; + @apply border-b-2 + border-b-transparent + px-1 + pb-[11px] + text-sm + font-semibold + text-neutral-800 + dark:text-neutral-200; &[data-state='active'] { - @apply border-b-green-600 text-green-600 dark:border-b-green-400 dark:text-green-400; + @apply border-b-green-600 + text-green-600 + dark:border-b-green-400 + dark:text-green-400; } } } .tabsSelect { - @apply sm:visible md:hidden; + @apply sm:visible + md:hidden; > span { - @apply max-xs:flex my-6 hidden w-full; + @apply max-xs:flex + my-6 + hidden + w-full; } } diff --git a/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css b/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css index e14f27f87af9a..b4ce7ce86b786 100644 --- a/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css +++ b/packages/ui-components/Common/BasePagination/Ellipsis/index.module.css @@ -1,3 +1,8 @@ .ellipsis { - @apply w-10 px-3 py-2.5 leading-none text-neutral-800 dark:text-neutral-200; + @apply w-10 + px-3 + py-2.5 + leading-none + text-neutral-800 + dark:text-neutral-200; } diff --git a/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css b/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css index f5232bfd0f946..ca629ffce0a31 100644 --- a/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css +++ b/packages/ui-components/Common/BasePagination/PaginationListItem/index.module.css @@ -1,9 +1,23 @@ .listItem, .listItem:link, .listItem:active { - @apply aria-current:bg-green-600 aria-current:text-white flex size-10 items-center justify-center rounded px-3 py-2.5 text-neutral-800 motion-safe:transition-colors dark:text-neutral-200; + @apply aria-current:bg-green-600 + aria-current:text-white + flex + size-10 + items-center + justify-center + rounded + px-3 + py-2.5 + text-neutral-800 + motion-safe:transition-colors + dark:text-neutral-200; &:hover { - @apply bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200; + @apply bg-neutral-100 + text-neutral-800 + dark:bg-neutral-900 + dark:text-neutral-200; } } diff --git a/packages/ui-components/Common/PrevNextArrow.tsx b/packages/ui-components/Common/BasePagination/PrevNextArrow.tsx similarity index 100% rename from packages/ui-components/Common/PrevNextArrow.tsx rename to packages/ui-components/Common/BasePagination/PrevNextArrow.tsx diff --git a/packages/ui-components/Common/BasePagination/index.module.css b/packages/ui-components/Common/BasePagination/index.module.css index 01124af149fb0..9a7b5936e7e98 100644 --- a/packages/ui-components/Common/BasePagination/index.module.css +++ b/packages/ui-components/Common/BasePagination/index.module.css @@ -1,13 +1,11 @@ .pagination { - @apply grid items-center justify-between gap-y-5 md:gap-y-0; - - grid-template-areas: 'pages pages pages' 'prev . next'; -} - -@screen md { - .pagination { - grid-template-areas: 'prev pages next'; - } + @apply grid + items-center + justify-between + gap-y-5 + [grid-template-areas:'pages_pages_pages''prev_._next'] + md:gap-y-0 + md:[grid-template-areas:'prev_pages_next']; } .previousButton, @@ -24,9 +22,16 @@ } .arrowIcon { - @apply h-5 shrink-0 text-neutral-600 dark:text-neutral-400; + @apply h-5 + shrink-0 + text-neutral-600 + dark:text-neutral-400; } .list { - @apply flex list-none justify-center gap-1 [grid-area:pages]; + @apply flex + list-none + justify-center + gap-1 + [grid-area:pages]; } diff --git a/packages/ui-components/Common/CodeTabs/index.module.css b/packages/ui-components/Common/CodeTabs/index.module.css index e63b5cb512483..5c79f7ecd4fd4 100644 --- a/packages/ui-components/Common/CodeTabs/index.module.css +++ b/packages/ui-components/Common/CodeTabs/index.module.css @@ -4,13 +4,50 @@ } > div:nth-of-type(1) { - @apply flex rounded-t border-x border-t border-neutral-900 bg-neutral-950 px-2 pt-3 md:px-4; + @apply flex + rounded-t + border-x + border-t + border-neutral-900 + bg-neutral-950 + px-2 + pt-3 + md:px-4; .trigger { - @apply border-b border-b-transparent px-1 text-neutral-200; + @apply border-b + border-b-transparent + px-1 + text-neutral-200; &[data-state='active'] { - @apply border-b-green-400 text-green-400; + @apply border-b-green-400 + text-green-400; + } + } + + .link { + @apply hidden + items-center + gap-2 + text-center + text-neutral-200 + motion-safe:transition-colors + lg:flex; + + & > .icon { + @apply size-4 + text-neutral-300; + } + + &:is(:link, :visited) { + &:hover { + @apply text-neutral-400; + + & > .icon { + @apply text-neutral-600; + } + } } } } diff --git a/packages/ui-components/Common/GlowingBackdrop/index.module.css b/packages/ui-components/Common/GlowingBackdrop/index.module.css index 37c2ac062afab..a7f2d817c3091 100644 --- a/packages/ui-components/Common/GlowingBackdrop/index.module.css +++ b/packages/ui-components/Common/GlowingBackdrop/index.module.css @@ -1,11 +1,33 @@ .glowingBackdrop { - @apply absolute left-0 top-0 -z-10 size-full opacity-50 md:opacity-100; + @apply absolute + left-0 + top-0 + -z-10 + size-full + opacity-50 + md:opacity-100; &::after { - @apply absolute inset-0 m-auto aspect-square w-[300px] rounded-full bg-green-300 blur-[120px] content-[''] dark:bg-green-700; + @apply absolute + inset-0 + m-auto + aspect-square + w-[300px] + rounded-full + bg-green-300 + blur-[120px] + content-[''] + dark:bg-green-700; } svg { - @apply absolute inset-0 m-auto aspect-[1.67] max-w-[1004px] object-cover text-neutral-300 dark:text-neutral-900/75; + @apply absolute + inset-0 + m-auto + aspect-[1.67] + max-w-[1004px] + object-cover + text-neutral-300 + dark:text-neutral-900/75; } } diff --git a/packages/ui-components/Common/LanguageDropDown/index.module.css b/packages/ui-components/Common/LanguageDropDown/index.module.css index 99757bc919228..7f1a0d1052106 100644 --- a/packages/ui-components/Common/LanguageDropDown/index.module.css +++ b/packages/ui-components/Common/LanguageDropDown/index.module.css @@ -1,21 +1,47 @@ .languageDropdown { - @apply h-9 w-9 rounded-md p-2 text-neutral-700 motion-safe:transition-colors dark:text-neutral-300; + @apply h-9 + w-9 + rounded-md + p-2 + text-neutral-700 + motion-safe:transition-colors + dark:text-neutral-300; &:hover { - @apply bg-neutral-100 dark:bg-neutral-900; + @apply bg-neutral-100 + dark:bg-neutral-900; } } .dropDownContent { - @apply max-h-80 w-48 overflow-hidden rounded border border-neutral-200 bg-white shadow-lg dark:border-neutral-900 dark:bg-neutral-950; + @apply max-h-80 + w-48 + overflow-hidden + rounded + border + border-neutral-200 + bg-white + shadow-lg + dark:border-neutral-900 + dark:bg-neutral-950; > div { - @apply max-h-80 w-48 overflow-y-auto; + @apply max-h-80 + w-48 overflow-y-auto; } } .dropDownItem { - @apply cursor-pointer px-2.5 py-1.5 text-sm font-medium text-neutral-800 outline-none data-[highlighted]:bg-green-600 data-[highlighted]:text-white dark:text-white; + @apply cursor-pointer + px-2.5 + py-1.5 + text-sm + font-medium + text-neutral-800 + outline-none + data-[highlighted]:bg-green-600 + data-[highlighted]:text-white + dark:text-white; } .currentDropDown { diff --git a/packages/ui-components/Common/NodejsLogo/index.module.css b/packages/ui-components/Common/NodejsLogo/index.module.css index cf1c469d1c78d..2e74ac633f684 100644 --- a/packages/ui-components/Common/NodejsLogo/index.module.css +++ b/packages/ui-components/Common/NodejsLogo/index.module.css @@ -1,3 +1,4 @@ .nodejsLogo { - @apply h-6 w-20; + @apply h-6 + w-20; } diff --git a/packages/ui-components/Common/Notification/index.module.css b/packages/ui-components/Common/Notification/index.module.css index 89bc810108f51..981e1b9d03cb8 100644 --- a/packages/ui-components/Common/Notification/index.module.css +++ b/packages/ui-components/Common/Notification/index.module.css @@ -1,7 +1,18 @@ .root { - @apply m-6 rounded border border-neutral-200 bg-white px-4 py-3 shadow-lg dark:border-neutral-800 dark:bg-neutral-900; + @apply m-6 + rounded + border + border-neutral-200 + bg-white + px-4 + py-3 + shadow-lg + dark:border-neutral-800 + dark:bg-neutral-900; } .message { - @apply font-medium text-green-600 dark:text-white; + @apply font-medium + text-green-600 + dark:text-white; } diff --git a/packages/ui-components/Common/Preview/index.module.css b/packages/ui-components/Common/Preview/index.module.css index ff8dcbbc5fe81..d2a68ff0df3fb 100644 --- a/packages/ui-components/Common/Preview/index.module.css +++ b/packages/ui-components/Common/Preview/index.module.css @@ -1,8 +1,25 @@ .root { - @apply @container/preview relative flex aspect-[1.90/1] items-center rounded border border-neutral-900 bg-neutral-950; + @apply @container/preview + relative + flex + aspect-[1.90/1] + items-center + rounded + border + border-neutral-900 + bg-neutral-950; &::after { - @apply bg-gradient-radial @md/preview:blur-3xl absolute inset-0 m-auto aspect-square w-1/3 rounded-full blur-2xl content-['']; + @apply bg-gradient-radial + @md/preview:blur-3xl + absolute + inset-0 + m-auto + aspect-square + w-1/3 + rounded-full + blur-2xl + content-['']; &.announcements { @apply from-green-700/90; @@ -18,14 +35,47 @@ } .container { - @apply @sm/preview:text-base @md/preview:gap-6 @md/preview:text-lg @lg/preview:gap-8 @lg/preview:text-xl @xl/preview:gap-12 @xl/preview:text-2xl @2xl/preview:text-3xl z-10 mx-auto flex w-2/3 max-w-xl flex-col gap-4 text-center text-xs font-semibold text-white; + @apply @sm/preview:text-base + @md/preview:gap-6 + @md/preview:text-lg + @lg/preview:gap-8 + @lg/preview:text-xl + @xl/preview:gap-12 + @xl/preview:text-2xl + @2xl/preview:text-3xl + z-10 + mx-auto + flex + w-2/3 + max-w-xl + flex-col + gap-4 + text-center + text-xs + font-semibold + text-white; .hexagon { - @apply @md/preview:h-3/5 @md/preview:w-3/5 @lg/preview:h-2/3 @lg/preview:w-2/3 @xl/preview:h-3/5 @xl/preview:w-3/5 @2xl/preview:h-2/3 @2xl/preview:w-2/3 absolute inset-0 m-auto size-full; + @apply @md/preview:h-3/5 + @md/preview:w-3/5 + @lg/preview:h-2/3 + @lg/preview:w-2/3 + @xl/preview:h-3/5 + @xl/preview:w-3/5 + @2xl/preview:h-2/3 + @2xl/preview:w-2/3 + absolute + inset-0 + m-auto + size-full; } .logo { - @apply @md/preview:size-14 @lg/preview:size-16 @xl/preview:size-20 mx-auto size-6; + @apply @md/preview:size-14 + @lg/preview:size-16 + @xl/preview:size-20 + mx-auto + size-6; } } } diff --git a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css index 5b649e634bd88..b9a9678173251 100644 --- a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css @@ -1,20 +1,52 @@ .group { - @apply flex flex-col gap-4 text-sm font-medium text-neutral-800 dark:text-neutral-200; + @apply flex + flex-col + gap-4 + text-sm + font-medium + text-neutral-800 + dark:text-neutral-200; .items { - @apply relative -left-1 flex flex-col gap-2; + @apply relative + -left-1 + flex + flex-col + gap-2; &::after { - @apply absolute left-[0.45rem] top-0 z-10 h-full w-px bg-neutral-200 content-[''] dark:bg-neutral-800; + @apply absolute + left-[0.45rem] + top-0 + z-10 + h-full + w-px + bg-neutral-200 + content-[''] + dark:bg-neutral-800; } a { &:first-child::before { - @apply absolute bottom-[calc(50%+0.25rem)] left-0 h-20 w-4 bg-white content-[''] dark:bg-neutral-950; + @apply absolute + bottom-[calc(50%+0.25rem)] + left-0 + h-20 + w-4 + bg-white + content-[''] + dark:bg-neutral-950; } &:last-child::after { - @apply absolute left-0 top-[calc(50%+0.25rem)] h-20 w-4 bg-white content-[''] dark:bg-neutral-950; + @apply absolute + left-0 + top-[calc(50%+0.25rem)] + h-20 + w-4 + bg-white + content-[''] + dark:bg-neutral-950; } } } diff --git a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css index 0831e9ae7e67f..1783a37464340 100644 --- a/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css +++ b/packages/ui-components/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css @@ -1,16 +1,34 @@ a.item { - @apply font-regular relative z-20 flex w-full items-center gap-1 overflow-hidden text-sm text-neutral-800 dark:text-neutral-200; + @apply font-regular + relative + z-20 + flex + w-full + items-center + gap-1 + overflow-hidden + text-sm + text-neutral-800 + dark:text-neutral-200; &:hover { - @apply text-neutral-900 motion-safe:transition-colors dark:text-white; + @apply text-neutral-900 + motion-safe:transition-colors + dark:text-white; } svg { - @apply shrink-0 fill-neutral-200 stroke-white stroke-[4] dark:fill-neutral-800 dark:stroke-neutral-950; + @apply shrink-0 + fill-neutral-200 + stroke-white + stroke-[4] + dark:fill-neutral-800 + dark:stroke-neutral-950; } &.active { - @apply text-neutral-900 dark:text-white; + @apply text-neutral-900 + dark:text-white; svg { @apply fill-green-500; diff --git a/packages/ui-components/Common/ProgressionSidebar/index.module.css b/packages/ui-components/Common/ProgressionSidebar/index.module.css index 8975615d544b4..c986e4572678c 100644 --- a/packages/ui-components/Common/ProgressionSidebar/index.module.css +++ b/packages/ui-components/Common/ProgressionSidebar/index.module.css @@ -1,11 +1,28 @@ .wrapper { - @apply flex w-full flex-col gap-8 overflow-auto border-r-0 border-neutral-200 bg-white px-4 py-6 sm:border-r md:max-w-xs lg:px-6 dark:border-neutral-900 dark:bg-neutral-950; + @apply flex + w-full + flex-col + gap-8 + overflow-auto + border-r-0 + border-neutral-200 + bg-white + px-4 + py-6 + sm:border-r + md:max-w-xs + lg:px-6 + dark:border-neutral-900 + dark:bg-neutral-950; > section { - @apply hidden sm:flex; + @apply hidden + sm:flex; } > span { - @apply flex w-full sm:hidden; + @apply flex + w-full + sm:hidden; } } diff --git a/packages/ui-components/Common/Select/index.module.css b/packages/ui-components/Common/Select/index.module.css index 8c4125439c55c..1e13248039a83 100644 --- a/packages/ui-components/Common/Select/index.module.css +++ b/packages/ui-components/Common/Select/index.module.css @@ -1,44 +1,110 @@ .select { - @apply inline-flex flex-col gap-1.5; + @apply inline-flex + flex-col + gap-1.5; .label { - @apply block w-full text-sm font-medium text-neutral-800 dark:text-neutral-200; + @apply block + w-full + text-sm + font-medium + text-neutral-800 + dark:text-neutral-200; } .trigger { - @apply shadow-xs inline-flex h-11 w-full min-w-[17rem] items-center justify-between gap-2 rounded border border-neutral-300 bg-white px-3.5 py-2.5 text-left text-base font-medium text-neutral-900 outline-none focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500 data-[placeholder]:text-neutral-800 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white dark:focus:border-neutral-600 dark:focus:ring-neutral-600 dark:data-[placeholder]:text-neutral-200; + @apply shadow-xs + inline-flex + h-11 + w-full + min-w-[17rem] + items-center + justify-between + gap-2 + rounded + border + border-neutral-300 + bg-white + px-3.5 + py-2.5 + text-left + text-base + font-medium + text-neutral-900 + outline-none + focus:border-neutral-500 + focus:ring-1 + focus:ring-neutral-500 + data-[placeholder]:text-neutral-800 + dark:border-neutral-800 + dark:bg-neutral-950 + dark:text-white + dark:focus:border-neutral-600 + dark:focus:ring-neutral-600 + dark:data-[placeholder]:text-neutral-200; } .trigger span { - @apply flex h-5 items-center gap-2; + @apply flex + h-5 + items-center + gap-2; } .icon { - @apply size-5 text-neutral-600 dark:text-neutral-400; + @apply size-5 + text-neutral-600 + dark:text-neutral-400; } } .dropdown { - @apply max-h-48 max-w-xs overflow-hidden overflow-y-auto rounded-md border border-neutral-200 bg-white shadow-lg dark:border-neutral-800 dark:bg-neutral-950; + @apply max-h-48 + max-w-xs + overflow-hidden + overflow-y-auto + rounded-md + border + border-neutral-200 + bg-white + shadow-lg + dark:border-neutral-800 + dark:bg-neutral-950; .item { - @apply select-none truncate px-2.5 py-1.5 text-sm font-medium; + @apply select-none + truncate + px-2.5 + py-1.5 + text-sm + font-medium; } .text { - @apply text-neutral-800 data-[highlighted]:bg-green-500 data-[highlighted]:text-white data-[highlighted]:outline-none dark:text-neutral-200 dark:data-[highlighted]:bg-green-600 dark:data-[highlighted]:text-white; + @apply text-neutral-800 + data-[highlighted]:bg-green-500 + data-[highlighted]:text-white + data-[highlighted]:outline-none + dark:text-neutral-200 + dark:data-[highlighted]:bg-green-600 + dark:data-[highlighted]:text-white; } .text > span { - @apply flex items-center gap-2; + @apply flex + items-center + gap-2; } .text > span > span { - @apply max-w-64 truncate text-wrap; + @apply max-w-64 + truncate + text-wrap; } .label { - @apply text-neutral-600 dark:text-neutral-400; + @apply text-neutral-600 + dark:text-neutral-400; } } @@ -54,7 +120,12 @@ .inline { .trigger { - @apply h-auto min-w-fit px-2.5 py-2 text-sm font-medium; + @apply h-auto + min-w-fit + px-2.5 + py-2 + text-sm + font-medium; } .icon { @@ -62,10 +133,19 @@ } .text { - @apply text-neutral-900 data-[highlighted]:bg-neutral-100 data-[disabled]:text-neutral-600 data-[highlighted]:text-neutral-900 dark:text-white dark:data-[highlighted]:bg-neutral-900 dark:data-[disabled]:text-neutral-700 dark:data-[highlighted]:text-white; + @apply text-neutral-900 + data-[highlighted]:bg-neutral-100 + data-[disabled]:text-neutral-600 + data-[highlighted]:text-neutral-900 + dark:text-white + dark:data-[highlighted]:bg-neutral-900 + dark:data-[disabled]:text-neutral-700 + dark:data-[highlighted]:text-white; } &.dropdown { - @apply mt-1 w-[calc(100%+1.5rem)] rounded; + @apply mt-1 + w-[calc(100%+1.5rem)] + rounded; } } diff --git a/packages/ui-components/Common/Skeleton/index.module.css b/packages/ui-components/Common/Skeleton/index.module.css index 8c2564b7de4c5..261615ad0156a 100644 --- a/packages/ui-components/Common/Skeleton/index.module.css +++ b/packages/ui-components/Common/Skeleton/index.module.css @@ -1,5 +1,14 @@ .skeleton { - @apply pointer-events-none animate-pulse cursor-default select-none rounded-md border-none bg-clip-border text-transparent shadow-none outline-none; + @apply pointer-events-none + animate-pulse + cursor-default + select-none + rounded-md + border-none + bg-clip-border + text-transparent + shadow-none + outline-none; } .skeleton[data-inline-skeleton] { @@ -7,7 +16,8 @@ } .skeleton:empty { - @apply block h-3; + @apply block + h-3; } .skeleton > *, diff --git a/packages/ui-components/Common/Tabs/index.module.css b/packages/ui-components/Common/Tabs/index.module.css index e3778adc52ac7..1703d143e6b3a 100644 --- a/packages/ui-components/Common/Tabs/index.module.css +++ b/packages/ui-components/Common/Tabs/index.module.css @@ -2,26 +2,50 @@ @apply max-w-full; .tabsList { - @apply font-open-sans flex gap-2 overflow-x-auto; + @apply font-open-sans + scrollbar-thin + flex + gap-2 + overflow-x-auto; .tabsTrigger { - @apply whitespace-nowrap border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold text-neutral-800 dark:text-neutral-200; + @apply whitespace-nowrap + border-b-2 + border-b-transparent + px-1 + pb-[11px] + text-sm + font-semibold + text-neutral-800 + dark:text-neutral-200; .tabSecondaryLabel { - @apply pl-1 text-neutral-500 dark:text-neutral-800; + @apply pl-1 + text-neutral-500 + dark:text-neutral-800; } &[data-state='active'] { - @apply border-b-green-600 text-green-600 dark:border-b-green-400 dark:text-green-400; + @apply border-b-green-600 + text-green-600 + dark:border-b-green-400 + dark:text-green-400; .tabSecondaryLabel { - @apply text-green-800 dark:text-green-600; + @apply text-green-800 + dark:text-green-600; } } } .addons { - @apply ml-auto border-b-2 border-b-transparent px-1 pb-[11px] text-sm font-semibold; + @apply ml-auto + border-b-2 + border-b-transparent + px-1 + pb-[11px] + text-sm + font-semibold; } } } diff --git a/packages/ui-components/Common/ThemeToggle/index.module.css b/packages/ui-components/Common/ThemeToggle/index.module.css index b4ab38e2a3fa2..c2cd6bb230a91 100644 --- a/packages/ui-components/Common/ThemeToggle/index.module.css +++ b/packages/ui-components/Common/ThemeToggle/index.module.css @@ -1,7 +1,13 @@ .themeToggle { - @apply size-9 rounded-md p-2 text-neutral-700 motion-safe:transition-colors dark:text-neutral-300; + @apply size-9 + rounded-md + p-2 + text-neutral-700 + motion-safe:transition-colors + dark:text-neutral-300; &:hover { - @apply bg-neutral-100 dark:bg-neutral-900; + @apply bg-neutral-100 + dark:bg-neutral-900; } } diff --git a/packages/ui-components/Common/Tooltip/index.module.css b/packages/ui-components/Common/Tooltip/index.module.css index 1d2cc80ea0a18..475db244ae82b 100644 --- a/packages/ui-components/Common/Tooltip/index.module.css +++ b/packages/ui-components/Common/Tooltip/index.module.css @@ -1,27 +1,41 @@ .content { - @apply rounded-md border bg-white text-sm font-medium text-neutral-900 shadow-lg dark:bg-neutral-950 dark:text-white; + @apply rounded-md + border + bg-white + text-sm + font-medium + text-neutral-900 + shadow-lg + dark:bg-neutral-950 + dark:text-white; &.default { - @apply border-neutral-200 dark:border-neutral-900; + @apply border-neutral-200 + dark:border-neutral-900; .arrow { - @apply fill-neutral-200 dark:fill-neutral-900; + @apply fill-neutral-200 + dark:fill-neutral-900; } } &.error { - @apply border-danger-400 dark:border-danger-900; + @apply border-danger-400 + dark:border-danger-900; .arrow { - @apply fill-danger-400 dark:fill-danger-900; + @apply fill-danger-400 + dark:fill-danger-900; } } &.warning { - @apply border-warning-400 dark:border-warning-900; + @apply border-warning-400 + dark:border-warning-900; .arrow { - @apply fill-warning-400 dark:fill-warning-900; + @apply fill-warning-400 + dark:fill-warning-900; } } } diff --git a/packages/ui-components/Containers/Footer/index.module.css b/packages/ui-components/Containers/Footer/index.module.css index 5a541545ac4ee..e388fa5341dba 100644 --- a/packages/ui-components/Containers/Footer/index.module.css +++ b/packages/ui-components/Containers/Footer/index.module.css @@ -1,8 +1,27 @@ .footer { - @apply flex flex-col items-center gap-6 border-t border-neutral-200 bg-white py-4 sm:px-8 md:flex-row md:justify-between md:py-5 dark:border-neutral-900 dark:bg-neutral-950; + @apply flex + flex-col + items-center + gap-6 + border-t + border-neutral-200 + bg-white + py-4 + sm:px-8 + md:flex-row + md:justify-between + md:py-5 + dark:border-neutral-900 + dark:bg-neutral-950; .sectionPrimary { - @apply flex flex-wrap content-start items-center justify-center gap-1 self-stretch; + @apply flex + flex-wrap + content-start + items-center + justify-center + gap-1 + self-stretch; a { @apply whitespace-nowrap; @@ -10,10 +29,16 @@ } .sectionSecondary { - @apply flex flex-col items-center gap-1 md:flex-row; + @apply flex + flex-col + items-center + gap-1 + md:flex-row; .social { - @apply flex items-center gap-1; + @apply flex + items-center + gap-1; } } } diff --git a/packages/ui-components/Containers/MetaBar/index.module.css b/packages/ui-components/Containers/MetaBar/index.module.css index 02d5703188157..8d867a2b2e0d1 100644 --- a/packages/ui-components/Containers/MetaBar/index.module.css +++ b/packages/ui-components/Containers/MetaBar/index.module.css @@ -1,31 +1,68 @@ .wrapper { - @apply flex w-full flex-col items-start gap-8 overflow-y-auto border-neutral-200 px-4 py-6 [overflow-wrap:anywhere] lg:sticky lg:top-0 lg:h-max lg:min-h-screen lg:px-6 dark:border-neutral-900; + @apply flex + w-full + flex-col + items-start + gap-8 + overflow-y-auto + border-neutral-200 + px-4 + py-6 + [overflow-wrap:anywhere] + lg:sticky + lg:top-0 + lg:h-max + lg:min-h-screen + lg:px-6 + dark:border-neutral-900; dl { @apply w-full; } dt { - @apply mb-2 text-sm font-medium text-neutral-800 dark:text-neutral-200; + @apply mb-2 + text-sm + font-medium + text-neutral-800 + dark:text-neutral-200; } dd { - @apply mb-8 flex items-center gap-2 text-sm text-neutral-900 dark:text-white; + @apply mb-8 + flex + items-center + gap-2 + text-sm + text-neutral-900 + dark:text-white; a { - @apply max-xs:inline-block max-xs:py-1 font-semibold text-neutral-900 underline dark:text-white; + @apply max-xs:inline-block + max-xs:py-1 + font-semibold + text-neutral-900 + underline + dark:text-white; &:hover { - @apply text-neutral-800 dark:text-neutral-200; + @apply text-neutral-800 + dark:text-neutral-200; } } ol { - @apply flex list-none flex-col gap-1.5 p-0; + @apply flex + list-none + flex-col + gap-1.5 + p-0; } svg { - @apply size-4 text-neutral-600 dark:text-neutral-400; + @apply size-4 + text-neutral-600 + dark:text-neutral-400; } &:last-child { @@ -34,10 +71,12 @@ } [data-on-dark] { - @apply hidden dark:block; + @apply hidden + dark:block; } [data-on-light] { - @apply block dark:hidden; + @apply block + dark:hidden; } } diff --git a/packages/ui-components/Containers/NavBar/index.module.css b/packages/ui-components/Containers/NavBar/index.module.css index 2df56c1494350..77f318d069a88 100644 --- a/packages/ui-components/Containers/NavBar/index.module.css +++ b/packages/ui-components/Containers/NavBar/index.module.css @@ -1,17 +1,45 @@ .container { - @apply border-neutral-200 bg-white lg:flex lg:h-16 lg:flex-row lg:items-center lg:gap-8 lg:border-b lg:px-8 dark:border-neutral-900 dark:bg-neutral-950; + @apply border-neutral-200 + bg-white + lg:flex + lg:h-16 + lg:flex-row + lg:items-center + lg:gap-8 + lg:border-b + lg:px-8 + dark:border-neutral-900 + dark:bg-neutral-950; } .nodeIconAndMobileItemsToggler { - @apply flex h-16 shrink-0 items-center border-b border-neutral-200 px-4 lg:flex lg:h-full lg:items-center lg:border-0 lg:px-0 dark:border-neutral-900; + @apply flex + h-16 + shrink-0 + items-center + border-b + border-neutral-200 + px-4 + lg:flex + lg:h-full + lg:items-center + lg:border-0 + lg:px-0 + dark:border-neutral-900; } .sidebarItemToggler { - @apply absolute right-4 -z-10 -translate-y-[200%] appearance-none opacity-0; + @apply absolute + right-4 + -z-10 + -translate-y-[200%] + appearance-none + opacity-0; } .nodeIconWrapper { - @apply h-[30px] flex-1; + @apply h-[30px] + flex-1; } .navInteractionIcon, @@ -20,23 +48,59 @@ } .sidebarItemTogglerLabel { - @apply block cursor-pointer lg:hidden; + @apply block + cursor-pointer + lg:hidden; } .main { - @apply hidden flex-1 flex-col justify-between gap-4 lg:flex lg:flex-row lg:items-center; + @apply hidden + flex-1 + flex-col + justify-between + gap-4 + lg:flex + lg:flex-row + lg:items-center; } .navItems { - @apply flex flex-col gap-0 border-b border-neutral-200 p-4 lg:flex-1 lg:flex-row lg:gap-1 lg:border-0 lg:p-0 dark:border-neutral-900; + @apply flex + flex-col + gap-0 + border-b + border-neutral-200 + p-4 + lg:flex-1 + lg:flex-row + lg:gap-1 + lg:border-0 + lg:p-0 + dark:border-neutral-900; } .actionsWrapper { - @apply flex flex-row flex-wrap items-center justify-between gap-2 border-b border-neutral-200 p-4 sm:flex-nowrap lg:basis-96 lg:border-0 lg:p-0 dark:border-neutral-900; + @apply flex + flex-row + flex-wrap + items-center + justify-between + gap-2 + border-b + border-neutral-200 + p-4 + sm:flex-nowrap + lg:basis-96 + lg:border-0 + lg:p-0 + dark:border-neutral-900; } span.searchButtonSkeleton { - @apply my-px mr-2 flex-grow rounded-xl; + @apply my-px + mr-2 + flex-grow + rounded-xl; &:empty { @apply h-10; @@ -44,13 +108,17 @@ span.searchButtonSkeleton { } .ghIconWrapper { - @apply size-9 rounded-md p-2; + @apply size-9 + rounded-md + p-2; svg { - @apply fill-neutral-700 dark:fill-neutral-300; + @apply fill-neutral-700 + dark:fill-neutral-300; } &:hover { - @apply bg-neutral-100 dark:bg-neutral-900; + @apply bg-neutral-100 + dark:bg-neutral-900; } } diff --git a/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css index 69642fb219a48..52f0d7507fadf 100644 --- a/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css +++ b/packages/ui-components/Containers/Sidebar/SidebarGroup/index.module.css @@ -1,11 +1,24 @@ .group { - @apply flex w-full flex-col gap-2; + @apply flex + w-full + flex-col + gap-2; } .groupName { - @apply px-2 py-1 text-xs font-semibold text-neutral-800 dark:text-neutral-600; + @apply px-2 + py-1 + text-xs + font-semibold + text-neutral-800 + dark:text-neutral-600; } .itemList { - @apply m-0 flex flex-col items-start gap-0.5 p-0; + @apply m-0 + flex + flex-col + items-start + gap-0.5 + p-0; } diff --git a/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css b/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css index d37d727ba6a80..7324ae7ba9f29 100644 --- a/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css +++ b/packages/ui-components/Containers/Sidebar/SidebarItem/index.module.css @@ -1,19 +1,29 @@ -.sideBarItem { - @apply flex w-full list-none text-neutral-800 dark:text-neutral-200; +.wrapper { + @apply flex + w-full + flex-col + items-start + gap-8 + overflow-auto + overflow-x-hidden + border-r-neutral-200 + bg-white + px-4 + py-6 + sm:border-r + md:max-w-xs + lg:px-6 + dark:border-r-neutral-900 + dark:bg-neutral-950; - a { - @apply inline-flex items-center gap-2 p-2; + > section { + @apply hidden + sm:flex; } - .label { - @apply font-regular w-full text-sm; + > span { + @apply flex + w-full + sm:hidden; } - - .icon { - @apply size-3 text-neutral-500 dark:text-neutral-200; - } -} - -.active { - @apply rounded bg-green-600 text-white dark:text-white; } diff --git a/packages/ui-components/Containers/Sidebar/index.module.css b/packages/ui-components/Containers/Sidebar/index.module.css index f3b6b9ecc9d38..7324ae7ba9f29 100644 --- a/packages/ui-components/Containers/Sidebar/index.module.css +++ b/packages/ui-components/Containers/Sidebar/index.module.css @@ -1,11 +1,29 @@ .wrapper { - @apply flex w-full flex-col items-start gap-8 overflow-auto overflow-x-hidden border-r-neutral-200 bg-white px-4 py-6 sm:border-r md:max-w-xs lg:px-6 dark:border-r-neutral-900 dark:bg-neutral-950; + @apply flex + w-full + flex-col + items-start + gap-8 + overflow-auto + overflow-x-hidden + border-r-neutral-200 + bg-white + px-4 + py-6 + sm:border-r + md:max-w-xs + lg:px-6 + dark:border-r-neutral-900 + dark:bg-neutral-950; > section { - @apply hidden sm:flex; + @apply hidden + sm:flex; } > span { - @apply flex w-full sm:hidden; + @apply flex + w-full + sm:hidden; } } diff --git a/packages/ui-components/styles/effects.css b/packages/ui-components/styles/effects.css index 874840458209a..bececd16aae56 100644 --- a/packages/ui-components/styles/effects.css +++ b/packages/ui-components/styles/effects.css @@ -10,34 +10,3 @@ h1.special { md:leading-[4rem] md:-tracking-[0.06rem]; } - -.turtle { - @apply animate-surf - absolute - z-10 - translate-x-0 - translate-y-0; -} - -.turtle img { - @apply h-auto - w-24 - md:w-48; -} - -.turtle::after { - @apply absolute - -left-full - top-[20%] - -z-10 - block - h-36 - w-36 - -rotate-90 - select-none - bg-[url('/static/images/smoke.gif')] - opacity-[0.15] - content-[''] - md:-left-1/2 - md:top-1/2; -} diff --git a/packages/ui-components/styles/index.css b/packages/ui-components/styles/index.css index 51f236e207c48..824dbf9a86ce7 100644 --- a/packages/ui-components/styles/index.css +++ b/packages/ui-components/styles/index.css @@ -9,7 +9,4 @@ @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; -@import './base.css'; -@import './markdown.css'; @import './effects.css'; -@import './locals.css'; diff --git a/packages/ui-components/tailwind.config.ts b/packages/ui-components/tailwind.config.ts index b560e851bce82..b7e3e319d4fa7 100644 --- a/packages/ui-components/tailwind.config.ts +++ b/packages/ui-components/tailwind.config.ts @@ -1,4 +1,5 @@ import type { Config } from 'tailwindcss'; +import plugin from 'tailwindcss/plugin'; export default { content: ['./**/*.tsx'], @@ -152,5 +153,12 @@ export default { plugins: [ require('@savvywombat/tailwindcss-grid-areas'), require('@tailwindcss/container-queries'), + plugin(function ({ addUtilities }) { + addUtilities({ + '.scrollbar-thin': { + 'scrollbar-width': 'thin', + }, + }); + }), ], } satisfies Config; From b581fedd32eab7fea6a4fef2e18ad962a4e1886c Mon Sep 17 00:00:00 2001 From: avivkeller <me@aviv.sh> Date: Sat, 22 Mar 2025 09:20:22 -0400 Subject: [PATCH 7/7] undo allowing manual deployment --- .github/workflows/lint-and-tests.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index ce87f6c3db8f0..86995c912fe6b 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -16,7 +16,6 @@ on: types: - labeled merge_group: - workflow_dispatch: defaults: run: @@ -50,13 +49,13 @@ jobs: run: echo "turbo_args=--force=true" >> "$GITHUB_OUTPUT" lint: - # This Job should run either on `merge_groups`, `push`, or `workflow_dispatch` events + # This Job should run either on `merge_groups` or `push` events # or `pull_request_target` event with a `labeled` action with a label named `github_actions:pull-request` # since we want to run lint checks against any changes on pull requests, or the final patch on merge groups # or if direct pushes happen to main (or when changes in general land on the `main` (default) branch) # Note that the reason why we run this on pushes against `main` is that on rare cases, maintainers might do direct pushes against `main` if: | - (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || + (github.event_name == 'push' || github.event_name == 'merge_group') || (github.event_name == 'pull_request_target' && github.event.label.name == 'github_actions:pull-request') @@ -112,7 +111,7 @@ jobs: # the Pull Request comes from a Crowdin Branch, as we don't want to run ESLint and Prettier on Crowdin PRs # Note: Linting and Prettifying of files on Crowdin PRs is handled by the `translations-pr.yml` Workflow if: | - (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || + (github.event_name == 'push' || github.event_name == 'merge_group') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.ref != 'chore/crowdin') # We want to enforce that the actual `turbo@latest` package is used instead of a possible hijack from the user @@ -141,12 +140,12 @@ jobs: key: cache-lint-${{ hashFiles('package-lock.json') }}-${{ hashFiles('.turbo/cache/**') }} tests: - # This Job should run either on `merge_groups`, `push`, or `workflow_dispatch` events + # This Job should run either on `merge_groups` or `push` events # or `pull_request_target` event with a `labeled` action with a label named `github_actions:pull-request` # since we want to run lint checks against any changes on pull requests and on final patches against a pull request. # We don't need to execute the tests again on pushes against (`main`) as the merge group should already handle that if: | - (github.event_name == 'push' || github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') || + (github.event_name == 'push' || github.event_name == 'merge_group') || (github.event_name == 'pull_request_target' && github.event.label.name == 'github_actions:pull-request') @@ -198,7 +197,7 @@ jobs: # We only need to run Storybook Builds and Storybook Visual Regression Tests within Pull Requests that actually # introduce changes to the Storybook. Hence, we skip running these on Crowdin PRs and Dependabot PRs if: | - github.event_name == 'push' || github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'dependabot/') == false && github.event.pull_request.head.ref != 'chore/crowdin') @@ -222,9 +221,8 @@ jobs: with: title: 'Unit Test Coverage Report' multiple-junitxml-files: | - General, ./junit.xml @node-core/ui-components, ./packages/ui-components/junit.xml - @node-core/website, ./apps/site/junit.xml + @nodejs/website, ./apps/site/junit.xml multiple-files: | @node-core/ui-components, ./packages/ui-components/coverage/coverage-summary.json - @node-core/website, ./apps/site/coverage/coverage-summary.json + @nodejs/website, ./apps/site/coverage/coverage-summary.json