@node-core/ui-components 0.0.1 → 1.0.1-f948ce8a25da149fc61135f90a43e189c4909f5d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/Common/AlertBox/index.module.css +76 -0
  2. package/Common/AlertBox/index.stories.tsx +73 -0
  3. package/Common/AlertBox/index.tsx +24 -0
  4. package/Common/AvatarGroup/Avatar/index.module.css +40 -0
  5. package/Common/AvatarGroup/Avatar/index.stories.tsx +22 -0
  6. package/Common/AvatarGroup/Avatar/index.tsx +67 -0
  7. package/Common/AvatarGroup/Overlay/index.module.css +31 -0
  8. package/Common/AvatarGroup/Overlay/index.stories.tsx +33 -0
  9. package/Common/AvatarGroup/Overlay/index.tsx +37 -0
  10. package/Common/AvatarGroup/__tests__/index.test.jsx +55 -0
  11. package/Common/AvatarGroup/index.module.css +25 -0
  12. package/Common/AvatarGroup/index.stories.tsx +56 -0
  13. package/Common/AvatarGroup/index.tsx +87 -0
  14. package/Common/Badge/index.module.css +38 -0
  15. package/Common/Badge/index.stories.tsx +38 -0
  16. package/Common/Badge/index.tsx +35 -0
  17. package/Common/BadgeGroup/index.module.css +77 -0
  18. package/Common/BadgeGroup/index.stories.tsx +35 -0
  19. package/Common/BadgeGroup/index.tsx +35 -0
  20. package/Common/Banner/index.module.css +42 -0
  21. package/Common/Banner/index.stories.tsx +29 -0
  22. package/Common/Banner/index.tsx +18 -0
  23. package/Common/BaseActiveLink/__tests__/index.test.jsx +52 -0
  24. package/Common/BaseActiveLink/index.tsx +34 -0
  25. package/Common/BaseButton/index.module.css +142 -0
  26. package/Common/BaseButton/index.stories.tsx +67 -0
  27. package/Common/BaseButton/index.tsx +59 -0
  28. package/Common/BaseCodeBox/index.module.css +84 -0
  29. package/Common/BaseCodeBox/index.stories.tsx +39 -0
  30. package/Common/BaseCodeBox/index.tsx +133 -0
  31. package/Common/BaseCrossLink/index.module.css +51 -0
  32. package/Common/BaseCrossLink/index.stories.tsx +38 -0
  33. package/Common/BaseCrossLink/index.tsx +46 -0
  34. package/Common/BaseLinkTabs/index.module.css +43 -0
  35. package/Common/BaseLinkTabs/index.stories.tsx +34 -0
  36. package/Common/BaseLinkTabs/index.tsx +53 -0
  37. package/Common/BasePagination/Ellipsis/index.module.css +10 -0
  38. package/Common/BasePagination/Ellipsis/index.stories.tsx +10 -0
  39. package/Common/BasePagination/Ellipsis/index.tsx +11 -0
  40. package/Common/BasePagination/PaginationListItem/__tests__/index.test.jsx +58 -0
  41. package/Common/BasePagination/PaginationListItem/index.module.css +27 -0
  42. package/Common/BasePagination/PaginationListItem/index.stories.tsx +40 -0
  43. package/Common/BasePagination/PaginationListItem/index.tsx +39 -0
  44. package/Common/BasePagination/PrevNextArrow.tsx +15 -0
  45. package/Common/BasePagination/__tests__/index.test.jsx +180 -0
  46. package/Common/BasePagination/index.module.css +39 -0
  47. package/Common/BasePagination/index.stories.tsx +67 -0
  48. package/Common/BasePagination/index.tsx +77 -0
  49. package/Common/BasePagination/useGetPageElements.tsx +132 -0
  50. package/Common/Blockquote/index.module.css +29 -0
  51. package/Common/Blockquote/index.stories.tsx +45 -0
  52. package/Common/Blockquote/index.tsx +11 -0
  53. package/Common/Breadcrumbs/BreadcrumbHomeLink/index.module.css +5 -0
  54. package/Common/Breadcrumbs/BreadcrumbHomeLink/index.tsx +30 -0
  55. package/Common/Breadcrumbs/BreadcrumbItem/index.module.css +41 -0
  56. package/Common/Breadcrumbs/BreadcrumbItem/index.tsx +42 -0
  57. package/Common/Breadcrumbs/BreadcrumbLink/index.module.css +22 -0
  58. package/Common/Breadcrumbs/BreadcrumbLink/index.tsx +37 -0
  59. package/Common/Breadcrumbs/BreadcrumbRoot/index.module.css +9 -0
  60. package/Common/Breadcrumbs/BreadcrumbRoot/index.tsx +20 -0
  61. package/Common/Breadcrumbs/BreadcrumbTruncatedItem/index.tsx +9 -0
  62. package/Common/Breadcrumbs/index.stories.tsx +94 -0
  63. package/Common/Breadcrumbs/index.tsx +81 -0
  64. package/Common/CodeTabs/index.module.css +56 -0
  65. package/Common/CodeTabs/index.stories.tsx +74 -0
  66. package/Common/CodeTabs/index.tsx +16 -0
  67. package/Common/GlowingBackdrop/index.module.css +32 -0
  68. package/Common/GlowingBackdrop/index.stories.tsx +10 -0
  69. package/Common/GlowingBackdrop/index.tsx +13 -0
  70. package/Common/LanguageDropDown/index.module.css +53 -0
  71. package/Common/LanguageDropDown/index.stories.tsx +19 -0
  72. package/Common/LanguageDropDown/index.tsx +56 -0
  73. package/Common/Modal/index.module.css +79 -0
  74. package/Common/Modal/index.stories.tsx +32 -0
  75. package/Common/Modal/index.tsx +48 -0
  76. package/Common/NodejsLogo/index.module.css +6 -0
  77. package/Common/NodejsLogo/index.stories.tsx +14 -0
  78. package/Common/NodejsLogo/index.tsx +26 -0
  79. package/Common/Notification/index.module.css +20 -0
  80. package/Common/Notification/index.stories.tsx +36 -0
  81. package/Common/Notification/index.tsx +34 -0
  82. package/Common/Preview/index.module.css +79 -0
  83. package/Common/Preview/index.stories.tsx +44 -0
  84. package/Common/Preview/index.tsx +25 -0
  85. package/Common/ProgressionSidebar/ProgressionSidebarGroup/index.module.css +47 -0
  86. package/Common/ProgressionSidebar/ProgressionSidebarGroup/index.tsx +35 -0
  87. package/Common/ProgressionSidebar/ProgressionSidebarIcon/index.tsx +16 -0
  88. package/Common/ProgressionSidebar/ProgressionSidebarItem/index.module.css +39 -0
  89. package/Common/ProgressionSidebar/ProgressionSidebarItem/index.tsx +32 -0
  90. package/Common/ProgressionSidebar/index.module.css +30 -0
  91. package/Common/ProgressionSidebar/index.stories.tsx +79 -0
  92. package/Common/ProgressionSidebar/index.tsx +59 -0
  93. package/Common/Select/__tests__/index.test.jsx +67 -0
  94. package/Common/Select/index.module.css +161 -0
  95. package/Common/Select/index.stories.tsx +111 -0
  96. package/Common/Select/index.tsx +187 -0
  97. package/Common/Separator/index.module.css +16 -0
  98. package/Common/Separator/index.stories.tsx +32 -0
  99. package/Common/Separator/index.tsx +27 -0
  100. package/Common/Skeleton/index.module.css +29 -0
  101. package/Common/Skeleton/index.tsx +39 -0
  102. package/Common/Tabs/__tests__/index.test.jsx +52 -0
  103. package/Common/Tabs/index.module.css +53 -0
  104. package/Common/Tabs/index.stories.tsx +50 -0
  105. package/Common/Tabs/index.tsx +54 -0
  106. package/Common/ThemeToggle/__tests__/index.test.jsx +35 -0
  107. package/Common/ThemeToggle/index.module.css +15 -0
  108. package/Common/ThemeToggle/index.stories.tsx +10 -0
  109. package/Common/ThemeToggle/index.tsx +15 -0
  110. package/Common/Tooltip/index.module.css +43 -0
  111. package/Common/Tooltip/index.stories.tsx +73 -0
  112. package/Common/Tooltip/index.tsx +48 -0
  113. package/Containers/Article/index.module.css +70 -0
  114. package/Containers/Article/index.stories.tsx +39 -0
  115. package/Containers/Article/index.tsx +9 -0
  116. package/Containers/Footer/index.module.css +46 -0
  117. package/Containers/Footer/index.stories.tsx +27 -0
  118. package/Containers/Footer/index.tsx +95 -0
  119. package/Containers/MetaBar/__tests__/index.test.jsx +63 -0
  120. package/Containers/MetaBar/index.module.css +84 -0
  121. package/Containers/MetaBar/index.stories.tsx +80 -0
  122. package/Containers/MetaBar/index.tsx +72 -0
  123. package/Containers/NavBar/NavItem/index.module.css +60 -0
  124. package/Containers/NavBar/NavItem/index.stories.tsx +38 -0
  125. package/Containers/NavBar/NavItem/index.tsx +44 -0
  126. package/Containers/NavBar/index.module.css +125 -0
  127. package/Containers/NavBar/index.stories.tsx +45 -0
  128. package/Containers/NavBar/index.tsx +94 -0
  129. package/Containers/Sidebar/SidebarGroup/index.module.css +26 -0
  130. package/Containers/Sidebar/SidebarGroup/index.stories.tsx +36 -0
  131. package/Containers/Sidebar/SidebarGroup/index.tsx +30 -0
  132. package/Containers/Sidebar/SidebarItem/index.module.css +35 -0
  133. package/Containers/Sidebar/SidebarItem/index.stories.tsx +15 -0
  134. package/Containers/Sidebar/SidebarItem/index.tsx +26 -0
  135. package/Containers/Sidebar/index.module.css +31 -0
  136. package/Containers/Sidebar/index.stories.tsx +84 -0
  137. package/Containers/Sidebar/index.tsx +58 -0
  138. package/Icons/HexagonGrid.stories.tsx +10 -0
  139. package/Icons/HexagonGrid.tsx +1434 -0
  140. package/Icons/InstallationMethod/Choco.tsx +78 -0
  141. package/Icons/InstallationMethod/Devbox.tsx +21 -0
  142. package/Icons/InstallationMethod/Docker.tsx +20 -0
  143. package/Icons/InstallationMethod/FNM.tsx +132 -0
  144. package/Icons/InstallationMethod/Homebrew.tsx +69 -0
  145. package/Icons/InstallationMethod/N.tsx +32 -0
  146. package/Icons/InstallationMethod/NVM.tsx +63 -0
  147. package/Icons/InstallationMethod/Volta.tsx +34 -0
  148. package/Icons/InstallationMethod/index.ts +10 -0
  149. package/Icons/Logos/JsGreen.tsx +24 -0
  150. package/Icons/Logos/JsWhite.tsx +37 -0
  151. package/Icons/Logos/Nodejs.tsx +188 -0
  152. package/Icons/Logos/NodejsStackedBlack.tsx +98 -0
  153. package/Icons/Logos/NodejsStackedDark.tsx +124 -0
  154. package/Icons/Logos/NodejsStackedLight.tsx +123 -0
  155. package/Icons/Logos/NodejsStackedWhite.tsx +98 -0
  156. package/Icons/Logos/index.ts +17 -0
  157. package/Icons/OperatingSystem/AIX.tsx +46 -0
  158. package/Icons/OperatingSystem/Apple.tsx +23 -0
  159. package/Icons/OperatingSystem/Linux.tsx +969 -0
  160. package/Icons/OperatingSystem/Microsoft.tsx +19 -0
  161. package/Icons/OperatingSystem/index.ts +6 -0
  162. package/Icons/PackageManager/Npm.tsx +21 -0
  163. package/Icons/PackageManager/Pnpm.tsx +22 -0
  164. package/Icons/PackageManager/Yarn.tsx +22 -0
  165. package/Icons/PackageManager/index.ts +5 -0
  166. package/Icons/Social/Bluesky.tsx +19 -0
  167. package/Icons/Social/Discord.tsx +20 -0
  168. package/Icons/Social/GitHub.tsx +16 -0
  169. package/Icons/Social/LinkedIn.tsx +16 -0
  170. package/Icons/Social/Mastodon.tsx +36 -0
  171. package/Icons/Social/Slack.tsx +31 -0
  172. package/Icons/Social/X.tsx +16 -0
  173. package/Icons/Social/index.ts +9 -0
  174. package/LICENSE +21 -0
  175. package/package.json +89 -5
  176. package/stylelint/__tests__/index.test.mjs +80 -0
  177. package/stylelint/one-utility-class-per-line.mjs +64 -0
  178. package/stylelint/utils.mjs +53 -0
  179. package/styles/animations.css +47 -0
  180. package/styles/base.css +17 -0
  181. package/styles/effects.css +12 -0
  182. package/styles/index.css +34 -0
  183. package/styles/markdown.css +173 -0
  184. package/styles/theme.css +175 -0
  185. package/types.ts +25 -0
  186. package/README.md +0 -1
@@ -0,0 +1,46 @@
1
+ @reference "../../styles/index.css";
2
+
3
+ .footer {
4
+ @apply flex
5
+ flex-col
6
+ items-center
7
+ gap-6
8
+ border-t
9
+ border-neutral-200
10
+ bg-white
11
+ py-4
12
+ sm:px-8
13
+ md:flex-row
14
+ md:justify-between
15
+ md:py-5
16
+ dark:border-neutral-900
17
+ dark:bg-neutral-950;
18
+
19
+ .sectionPrimary {
20
+ @apply flex
21
+ flex-wrap
22
+ content-start
23
+ items-center
24
+ justify-center
25
+ gap-1
26
+ self-stretch;
27
+
28
+ a {
29
+ @apply whitespace-nowrap;
30
+ }
31
+ }
32
+
33
+ .sectionSecondary {
34
+ @apply flex
35
+ flex-col
36
+ items-center
37
+ gap-1
38
+ md:flex-row;
39
+
40
+ .social {
41
+ @apply flex
42
+ items-center
43
+ gap-1;
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,27 @@
1
+ import type { Meta as MetaObj, StoryObj } from '@storybook/react';
2
+
3
+ import Footer from '#ui/Containers/Footer';
4
+
5
+ type Story = StoryObj<typeof Footer>;
6
+ type Meta = MetaObj<typeof Footer>;
7
+
8
+ export const Default: Story = {
9
+ args: {
10
+ navigation: {
11
+ footerLinks: [
12
+ { link: '/', text: 'Home' },
13
+ { link: 'https://openjsf.org', text: 'OpenJS Foundation' },
14
+ ],
15
+ socialLinks: [
16
+ { icon: 'github', link: 'https://github.com' },
17
+ { icon: 'mastodon', link: 'https://mastodon.social' },
18
+ { icon: 'twitter', link: 'https://twitter.com' },
19
+ { icon: 'slack', link: 'https://slack.com' },
20
+ { icon: 'linkedin', link: 'https://linkedin.com' },
21
+ { icon: 'bluesky', link: 'https://bsky.app' },
22
+ ],
23
+ },
24
+ },
25
+ };
26
+
27
+ export default { component: Footer } as Meta;
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import type { FC, SVGProps } from 'react';
4
+
5
+ import NavItem from '#ui/Containers/NavBar/NavItem';
6
+ import {
7
+ Bluesky,
8
+ Discord,
9
+ GitHub,
10
+ LinkedIn,
11
+ Mastodon,
12
+ Slack,
13
+ X,
14
+ } from '#ui/Icons/Social';
15
+ import type { LinkLike } from '#ui/types';
16
+
17
+ import styles from './index.module.css';
18
+
19
+ const footerSocialIcons: Record<string, React.FC<SVGProps<SVGSVGElement>>> = {
20
+ github: GitHub,
21
+ mastodon: Mastodon,
22
+ twitter: X,
23
+ slack: Slack,
24
+ linkedin: LinkedIn,
25
+ bluesky: Bluesky,
26
+ discord: Discord,
27
+ };
28
+
29
+ type Navigation = {
30
+ socialLinks: Array<{
31
+ icon: string;
32
+ link: string;
33
+ }>;
34
+ footerLinks: Array<{
35
+ text: string;
36
+ link: string;
37
+ }>;
38
+ };
39
+
40
+ const Footer: FC<{
41
+ pathname: string;
42
+ as: LinkLike;
43
+ navigation: Navigation;
44
+ }> = ({ pathname = '/', as = 'a', navigation }) => {
45
+ const openJSlink = navigation.footerLinks.at(-1)!;
46
+
47
+ return (
48
+ <footer className={styles.footer}>
49
+ <div className={styles.sectionPrimary}>
50
+ {navigation.footerLinks.slice(0, -1).map(item => (
51
+ <NavItem
52
+ key={item.link}
53
+ type="footer"
54
+ href={item.link}
55
+ as={as}
56
+ pathname={pathname}
57
+ >
58
+ {item.text}
59
+ </NavItem>
60
+ ))}
61
+ </div>
62
+
63
+ <div className={styles.sectionSecondary}>
64
+ <NavItem
65
+ type="footer"
66
+ href={openJSlink.link}
67
+ as={as}
68
+ pathname={pathname}
69
+ >
70
+ &copy; {openJSlink.text}
71
+ </NavItem>
72
+
73
+ <div className={styles.social}>
74
+ {navigation.socialLinks.map(link => {
75
+ const SocialIcon = footerSocialIcons[link.icon];
76
+
77
+ return (
78
+ <NavItem
79
+ key={link.icon}
80
+ href={link.link}
81
+ type="footer"
82
+ as={as}
83
+ pathname={pathname}
84
+ >
85
+ <SocialIcon width={20} height={20} aria-label={link.link} />
86
+ </NavItem>
87
+ );
88
+ })}
89
+ </div>
90
+ </div>
91
+ </footer>
92
+ );
93
+ };
94
+
95
+ export default Footer;
@@ -0,0 +1,63 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ import { render } from '@testing-library/react';
5
+
6
+ import MetaBar from '..';
7
+
8
+ describe('MetaBar', () => {
9
+ it('does not render h5s in the table of contents', () => {
10
+ const { queryByText, getByText } = render(
11
+ <MetaBar
12
+ items={{}}
13
+ headings={{
14
+ items: [
15
+ {
16
+ value: 'Heading Level 1',
17
+ depth: 1,
18
+ data: { id: 'heading-1' },
19
+ },
20
+ {
21
+ value: 'Heading Level 2',
22
+ depth: 2,
23
+ data: { id: 'heading-2' },
24
+ },
25
+ {
26
+ value: 'Heading Level 3',
27
+ depth: 3,
28
+ data: { id: 'heading-3' },
29
+ },
30
+ {
31
+ value: 'Heading Level 4',
32
+ depth: 4,
33
+ data: { id: 'heading-4' },
34
+ },
35
+ {
36
+ value: 'Heading Level 5',
37
+ depth: 5,
38
+ data: { id: 'heading-5' },
39
+ },
40
+ {
41
+ value: 'Heading Level 6',
42
+ depth: 6,
43
+ data: { id: 'heading-6' },
44
+ },
45
+ ],
46
+ }}
47
+ />
48
+ );
49
+
50
+ const h1Element = queryByText('Heading Level 1');
51
+ assert.ok(!h1Element?.ownerDocument);
52
+
53
+ getByText('Heading Level 2');
54
+ getByText('Heading Level 3');
55
+ getByText('Heading Level 4');
56
+
57
+ const h5Element = queryByText('Heading Level 5');
58
+ assert.ok(!h5Element?.ownerDocument);
59
+
60
+ const h6Element = queryByText('Heading Level 6');
61
+ assert.ok(!h6Element?.ownerDocument);
62
+ });
63
+ });
@@ -0,0 +1,84 @@
1
+ @reference "../../styles/index.css";
2
+
3
+ .wrapper {
4
+ @apply flex
5
+ w-full
6
+ flex-col
7
+ items-start
8
+ gap-8
9
+ overflow-y-auto
10
+ border-neutral-200
11
+ px-4
12
+ py-6
13
+ [overflow-wrap:anywhere]
14
+ lg:sticky
15
+ lg:top-0
16
+ lg:h-max
17
+ lg:min-h-screen
18
+ lg:px-6
19
+ dark:border-neutral-900;
20
+
21
+ dl {
22
+ @apply w-full;
23
+ }
24
+
25
+ dt {
26
+ @apply mb-2
27
+ text-sm
28
+ font-medium
29
+ text-neutral-800
30
+ dark:text-neutral-200;
31
+ }
32
+
33
+ dd {
34
+ @apply mb-8
35
+ flex
36
+ items-center
37
+ gap-2
38
+ text-sm
39
+ text-neutral-900
40
+ dark:text-white;
41
+
42
+ a {
43
+ @apply max-xs:inline-block
44
+ max-xs:py-1
45
+ font-semibold
46
+ text-neutral-900
47
+ underline
48
+ dark:text-white;
49
+
50
+ &:hover {
51
+ @apply text-neutral-800
52
+ dark:text-neutral-200;
53
+ }
54
+ }
55
+
56
+ ol {
57
+ @apply flex
58
+ list-none
59
+ flex-col
60
+ gap-1.5
61
+ p-0;
62
+ }
63
+
64
+ svg {
65
+ @apply size-4
66
+ text-neutral-600
67
+ dark:text-neutral-400;
68
+ }
69
+
70
+ &:last-child {
71
+ @apply mb-0;
72
+ }
73
+ }
74
+
75
+ [data-on-dark] {
76
+ @apply hidden
77
+ dark:block;
78
+ }
79
+
80
+ [data-on-light] {
81
+ @apply block
82
+ dark:hidden;
83
+ }
84
+ }
@@ -0,0 +1,80 @@
1
+ import { CodeBracketIcon } from '@heroicons/react/24/outline';
2
+ import type { Meta as MetaObj, StoryObj } from '@storybook/react';
3
+
4
+ import MetaBar from '#ui/Containers/MetaBar';
5
+ import GitHubIcon from '#ui/Icons/Social/GitHub';
6
+
7
+ type Story = StoryObj<typeof MetaBar>;
8
+ type Meta = MetaObj<typeof MetaBar>;
9
+
10
+ export const Default: Story = {
11
+ args: {
12
+ items: {
13
+ 'components.metabar.lastUpdated': new Date(
14
+ '17 October 2023'
15
+ ).toLocaleDateString(),
16
+ 'components.metabar.readingTime': '15 minutes',
17
+ 'components.metabar.addedIn': 'v1.0.0',
18
+ 'components.metabar.author': 'The Node.js Project',
19
+ 'components.metabar.authors': <p>...</p>,
20
+ 'components.metabar.contribute': (
21
+ <>
22
+ <GitHubIcon className="fill-neutral-700 dark:fill-neutral-100" />
23
+ <a href="/contribute">Edit this page</a>
24
+ </>
25
+ ),
26
+ 'components.metabar.viewAs': (
27
+ <>
28
+ <CodeBracketIcon />
29
+ <a href="/json">JSON</a>
30
+ </>
31
+ ),
32
+ },
33
+ headings: {
34
+ items: [
35
+ {
36
+ value: 'OpenSSL update assessment, and Node.js project plans',
37
+ depth: 1,
38
+ data: { id: 'heading-1' },
39
+ },
40
+ {
41
+ value: 'Summary',
42
+ depth: 2,
43
+ data: { id: 'summary' },
44
+ },
45
+ {
46
+ value: 'Analysis',
47
+ depth: 2,
48
+ data: { id: 'analysis' },
49
+ },
50
+ {
51
+ value: 'The c_rehash script allows command injection (CVE-2022-2068)',
52
+ depth: 3,
53
+ data: { id: 'the_c_rehash' },
54
+ },
55
+ {
56
+ value: 'Contact and future updates',
57
+ depth: 3,
58
+ data: { id: 'contact_and_future_updates' },
59
+ },
60
+ {
61
+ value: 'Email',
62
+ depth: 4,
63
+ data: { id: 'email' },
64
+ },
65
+ {
66
+ value: 'Slack',
67
+ depth: 4,
68
+ data: { id: 'slack' },
69
+ },
70
+ {
71
+ value: '#node-website',
72
+ depth: 5, // h5s do not get shown
73
+ data: { id: 'node-website' },
74
+ },
75
+ ],
76
+ },
77
+ },
78
+ };
79
+
80
+ export default { component: MetaBar } as Meta;
@@ -0,0 +1,72 @@
1
+ import type { Heading } from '@vcarl/remark-headings';
2
+ import { Fragment, useMemo } from 'react';
3
+ import type { FC } from 'react';
4
+
5
+ import type { LinkLike } from '#ui/types';
6
+
7
+ import styles from './index.module.css';
8
+
9
+ type MetaBarProps = {
10
+ items: Partial<Record<string, React.ReactNode>>;
11
+ headings?: {
12
+ items: Array<Heading>;
13
+ minDepth?: number;
14
+ };
15
+ as?: LinkLike;
16
+ heading: string;
17
+ };
18
+
19
+ const MetaBar: FC<MetaBarProps> = ({
20
+ items,
21
+ headings,
22
+ as: Component = 'a',
23
+ heading,
24
+ }) => {
25
+ // The default depth of headings to display in the table of contents.
26
+ const { minDepth = 2, items: headingItems = [] } = headings || {};
27
+
28
+ const filteredHeadings = useMemo(
29
+ () => headingItems.filter(({ depth }) => depth >= minDepth && depth <= 4),
30
+ [minDepth, headingItems]
31
+ );
32
+
33
+ return (
34
+ <div className={styles.wrapper}>
35
+ <dl>
36
+ {Object.entries(items)
37
+ .filter(([, value]) => !!value)
38
+ .map(([key, value]) => (
39
+ <Fragment key={key}>
40
+ <dt>{key}</dt>
41
+ <dd>{value}</dd>
42
+ </Fragment>
43
+ ))}
44
+
45
+ {filteredHeadings.length > 0 && (
46
+ <>
47
+ <dt>{heading}</dt>
48
+ <dd>
49
+ <ol>
50
+ {filteredHeadings.map(head => (
51
+ <li
52
+ key={head.value}
53
+ className={
54
+ head.depth === 3 ? 'pl-2' : head.depth === 4 ? 'pl-4' : ''
55
+ }
56
+ >
57
+ <Component href={`#${head?.data?.id}`}>
58
+ {' '}
59
+ {head.value}
60
+ </Component>
61
+ </li>
62
+ ))}
63
+ </ol>
64
+ </dd>
65
+ </>
66
+ )}
67
+ </dl>
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export default MetaBar;
@@ -0,0 +1,60 @@
1
+ @reference "../../../styles/index.css";
2
+
3
+ .navItem {
4
+ @apply inline-flex
5
+ items-center
6
+ gap-2
7
+ rounded-sm
8
+ px-3
9
+ py-2
10
+ motion-safe:transition-colors;
11
+
12
+ .label {
13
+ @apply text-base
14
+ font-medium
15
+ lg:text-sm;
16
+ }
17
+
18
+ .icon {
19
+ @apply size-3
20
+ text-neutral-500
21
+ dark:text-neutral-200;
22
+ }
23
+
24
+ &:hover {
25
+ @apply bg-neutral-100
26
+ dark:bg-neutral-900;
27
+ }
28
+
29
+ &.nav {
30
+ .label {
31
+ @apply text-neutral-900
32
+ dark:text-white;
33
+ }
34
+
35
+ &.active {
36
+ @apply bg-green-600;
37
+
38
+ .label {
39
+ @apply text-white;
40
+ }
41
+
42
+ .icon {
43
+ @apply text-white
44
+ opacity-50;
45
+ }
46
+ }
47
+ }
48
+
49
+ &.footer {
50
+ .label {
51
+ @apply text-neutral-800
52
+ dark:text-white;
53
+ }
54
+
55
+ &:hover {
56
+ @apply bg-neutral-100
57
+ dark:bg-neutral-900;
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,38 @@
1
+ import type { Meta as MetaObj, StoryObj } from '@storybook/react';
2
+
3
+ import NavItem from '#ui/Containers/NavBar/NavItem';
4
+
5
+ type Story = StoryObj<typeof NavItem>;
6
+ type Meta = MetaObj<typeof NavItem>;
7
+
8
+ export const Default: Story = {
9
+ args: {
10
+ href: '/learn',
11
+ children: 'Learn',
12
+ },
13
+ };
14
+
15
+ export const WithExternalLink: Story = {
16
+ args: {
17
+ href: 'https://nodejs.org/en',
18
+ children: 'Learn',
19
+ target: '_blank',
20
+ },
21
+ };
22
+
23
+ export const WithChildren: Story = {
24
+ args: {
25
+ href: 'https://nodejs.org/en',
26
+ children: <b>Learn</b>,
27
+ },
28
+ };
29
+
30
+ export const FooterItem: Story = {
31
+ args: {
32
+ href: '/about',
33
+ children: 'Trademark Policy',
34
+ type: 'footer',
35
+ },
36
+ };
37
+
38
+ export default { component: NavItem } as Meta;
@@ -0,0 +1,44 @@
1
+ import { ArrowUpRightIcon } from '@heroicons/react/24/solid';
2
+ import classNames from 'classnames';
3
+ import type { FC, HTMLAttributeAnchorTarget, PropsWithChildren } from 'react';
4
+
5
+ import BaseActiveLink from '#ui/Common/BaseActiveLink';
6
+ import type { LinkLike } from '#ui/types';
7
+
8
+ import styles from './index.module.css';
9
+
10
+ type NavItemType = 'nav' | 'footer';
11
+
12
+ type NavItemProps = {
13
+ href: string;
14
+ type?: NavItemType;
15
+ className?: string;
16
+ target?: HTMLAttributeAnchorTarget | undefined;
17
+
18
+ pathname: string;
19
+ as: LinkLike;
20
+ };
21
+
22
+ const NavItem: FC<PropsWithChildren<NavItemProps>> = ({
23
+ href = '',
24
+ type = 'nav',
25
+ children,
26
+ className,
27
+ target,
28
+ ...props
29
+ }) => (
30
+ <BaseActiveLink
31
+ target={target}
32
+ href={href}
33
+ className={classNames(styles.navItem, styles[type], className)}
34
+ activeClassName={styles.active}
35
+ allowSubPath={href.startsWith('/')}
36
+ {...props}
37
+ >
38
+ <span className={styles.label}>{children}</span>
39
+
40
+ {target === '_blank' && <ArrowUpRightIcon className={styles.icon} />}
41
+ </BaseActiveLink>
42
+ );
43
+
44
+ export default NavItem;