@node-core/ui-components 0.0.1 → 1.0.1-0bb5d49b96e1f11e1ef6cf965861929693053ca6

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 (184) 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 +21 -0
  12. package/Common/AvatarGroup/index.stories.tsx +56 -0
  13. package/Common/AvatarGroup/index.tsx +83 -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 +78 -0
  29. package/Common/BaseCodeBox/index.stories.tsx +39 -0
  30. package/Common/BaseCodeBox/index.tsx +122 -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 +73 -0
  66. package/Common/CodeTabs/index.tsx +16 -0
  67. package/Common/DataTag/index.module.css +56 -0
  68. package/Common/DataTag/index.stories.tsx +40 -0
  69. package/Common/DataTag/index.tsx +39 -0
  70. package/Common/GlowingBackdrop/index.module.css +32 -0
  71. package/Common/GlowingBackdrop/index.stories.tsx +10 -0
  72. package/Common/GlowingBackdrop/index.tsx +13 -0
  73. package/Common/LanguageDropDown/index.module.css +53 -0
  74. package/Common/LanguageDropDown/index.stories.tsx +19 -0
  75. package/Common/LanguageDropDown/index.tsx +56 -0
  76. package/Common/Modal/index.module.css +79 -0
  77. package/Common/Modal/index.stories.tsx +32 -0
  78. package/Common/Modal/index.tsx +48 -0
  79. package/Common/NodejsLogo/index.module.css +6 -0
  80. package/Common/NodejsLogo/index.stories.tsx +14 -0
  81. package/Common/NodejsLogo/index.tsx +26 -0
  82. package/Common/Notification/index.module.css +20 -0
  83. package/Common/Notification/index.stories.tsx +36 -0
  84. package/Common/Notification/index.tsx +34 -0
  85. package/Common/Preview/index.module.css +79 -0
  86. package/Common/Preview/index.stories.tsx +44 -0
  87. package/Common/Preview/index.tsx +25 -0
  88. package/Common/Select/__tests__/index.test.jsx +67 -0
  89. package/Common/Select/index.module.css +161 -0
  90. package/Common/Select/index.stories.tsx +111 -0
  91. package/Common/Select/index.tsx +187 -0
  92. package/Common/Separator/index.module.css +16 -0
  93. package/Common/Separator/index.stories.tsx +32 -0
  94. package/Common/Separator/index.tsx +27 -0
  95. package/Common/Skeleton/index.module.css +30 -0
  96. package/Common/Skeleton/index.tsx +39 -0
  97. package/Common/Tabs/__tests__/index.test.jsx +52 -0
  98. package/Common/Tabs/index.module.css +54 -0
  99. package/Common/Tabs/index.stories.tsx +50 -0
  100. package/Common/Tabs/index.tsx +54 -0
  101. package/Common/ThemeToggle/__tests__/index.test.jsx +35 -0
  102. package/Common/ThemeToggle/index.module.css +15 -0
  103. package/Common/ThemeToggle/index.stories.tsx +10 -0
  104. package/Common/ThemeToggle/index.tsx +15 -0
  105. package/Common/Tooltip/index.module.css +43 -0
  106. package/Common/Tooltip/index.stories.tsx +73 -0
  107. package/Common/Tooltip/index.tsx +48 -0
  108. package/Containers/Article/index.module.css +70 -0
  109. package/Containers/Article/index.stories.tsx +39 -0
  110. package/Containers/Article/index.tsx +9 -0
  111. package/Containers/DocSideBar/index.tsx +0 -0
  112. package/Containers/Footer/index.module.css +46 -0
  113. package/Containers/Footer/index.stories.tsx +27 -0
  114. package/Containers/Footer/index.tsx +95 -0
  115. package/Containers/MetaBar/__tests__/index.test.jsx +63 -0
  116. package/Containers/MetaBar/index.module.css +91 -0
  117. package/Containers/MetaBar/index.stories.tsx +80 -0
  118. package/Containers/MetaBar/index.tsx +72 -0
  119. package/Containers/NavBar/NavItem/index.module.css +60 -0
  120. package/Containers/NavBar/NavItem/index.stories.tsx +38 -0
  121. package/Containers/NavBar/NavItem/index.tsx +44 -0
  122. package/Containers/NavBar/index.module.css +125 -0
  123. package/Containers/NavBar/index.stories.tsx +45 -0
  124. package/Containers/NavBar/index.tsx +94 -0
  125. package/Containers/Sidebar/ProgressionIcon/index.tsx +16 -0
  126. package/Containers/Sidebar/SidebarGroup/index.module.css +64 -0
  127. package/Containers/Sidebar/SidebarGroup/index.stories.tsx +36 -0
  128. package/Containers/Sidebar/SidebarGroup/index.tsx +44 -0
  129. package/Containers/Sidebar/SidebarItem/index.module.css +56 -0
  130. package/Containers/Sidebar/SidebarItem/index.stories.tsx +15 -0
  131. package/Containers/Sidebar/SidebarItem/index.tsx +43 -0
  132. package/Containers/Sidebar/index.module.css +30 -0
  133. package/Containers/Sidebar/index.stories.tsx +88 -0
  134. package/Containers/Sidebar/index.tsx +65 -0
  135. package/Icons/HexagonGrid.stories.tsx +10 -0
  136. package/Icons/HexagonGrid.tsx +1434 -0
  137. package/Icons/InstallationMethod/Choco.tsx +78 -0
  138. package/Icons/InstallationMethod/Devbox.tsx +21 -0
  139. package/Icons/InstallationMethod/Docker.tsx +20 -0
  140. package/Icons/InstallationMethod/FNM.tsx +132 -0
  141. package/Icons/InstallationMethod/Homebrew.tsx +69 -0
  142. package/Icons/InstallationMethod/N.tsx +32 -0
  143. package/Icons/InstallationMethod/NVM.tsx +63 -0
  144. package/Icons/InstallationMethod/Volta.tsx +34 -0
  145. package/Icons/InstallationMethod/index.ts +10 -0
  146. package/Icons/Logos/JsGreen.tsx +24 -0
  147. package/Icons/Logos/JsWhite.tsx +37 -0
  148. package/Icons/Logos/Nodejs.tsx +188 -0
  149. package/Icons/Logos/NodejsStackedBlack.tsx +98 -0
  150. package/Icons/Logos/NodejsStackedDark.tsx +124 -0
  151. package/Icons/Logos/NodejsStackedLight.tsx +123 -0
  152. package/Icons/Logos/NodejsStackedWhite.tsx +98 -0
  153. package/Icons/Logos/index.ts +17 -0
  154. package/Icons/OperatingSystem/AIX.tsx +46 -0
  155. package/Icons/OperatingSystem/Apple.tsx +23 -0
  156. package/Icons/OperatingSystem/Linux.tsx +969 -0
  157. package/Icons/OperatingSystem/Microsoft.tsx +19 -0
  158. package/Icons/OperatingSystem/index.ts +6 -0
  159. package/Icons/PackageManager/Npm.tsx +21 -0
  160. package/Icons/PackageManager/Pnpm.tsx +22 -0
  161. package/Icons/PackageManager/Yarn.tsx +22 -0
  162. package/Icons/PackageManager/index.ts +5 -0
  163. package/Icons/Social/Bluesky.tsx +19 -0
  164. package/Icons/Social/Discord.tsx +20 -0
  165. package/Icons/Social/GitHub.tsx +16 -0
  166. package/Icons/Social/LinkedIn.tsx +16 -0
  167. package/Icons/Social/Mastodon.tsx +36 -0
  168. package/Icons/Social/Slack.tsx +31 -0
  169. package/Icons/Social/X.tsx +16 -0
  170. package/Icons/Social/index.ts +9 -0
  171. package/LICENSE +21 -0
  172. package/MDX/CodeTabs.tsx +47 -0
  173. package/package.json +87 -5
  174. package/stylelint/__tests__/index.test.mjs +80 -0
  175. package/stylelint/one-utility-class-per-line.mjs +64 -0
  176. package/stylelint/utils.mjs +53 -0
  177. package/styles/animations.css +47 -0
  178. package/styles/base.css +17 -0
  179. package/styles/effects.css +12 -0
  180. package/styles/index.css +38 -0
  181. package/styles/markdown.css +173 -0
  182. package/styles/theme.css +175 -0
  183. package/types.ts +25 -0
  184. package/README.md +0 -1
@@ -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,91 @@
1
+ @reference "../../styles/index.css";
2
+
3
+ .wrapper {
4
+ @apply flex
5
+ w-full
6
+ flex-col
7
+ items-start
8
+ gap-8
9
+ border-neutral-200
10
+ px-4
11
+ py-6
12
+ [overflow-wrap:anywhere]
13
+ lg:sticky
14
+ lg:top-0
15
+ lg:max-h-screen
16
+ lg:px-6
17
+ dark:border-neutral-900;
18
+
19
+ dl {
20
+ @apply flex
21
+ w-full
22
+ flex-col
23
+ gap-1.5
24
+ pb-12
25
+ [scrollbar-width:none]
26
+ lg:max-h-[calc(100vh-var(--header-height))]
27
+ lg:overflow-y-auto
28
+ [&::-webkit-scrollbar]:hidden;
29
+ }
30
+
31
+ dt {
32
+ @apply mb-2
33
+ text-sm
34
+ font-medium
35
+ text-neutral-800
36
+ dark:text-neutral-200;
37
+ }
38
+
39
+ dd {
40
+ @apply mb-8
41
+ flex
42
+ items-center
43
+ gap-2
44
+ text-sm
45
+ text-neutral-900
46
+ dark:text-white;
47
+
48
+ a {
49
+ @apply max-xs:inline-block
50
+ max-xs:py-1
51
+ font-semibold
52
+ text-neutral-900
53
+ underline
54
+ dark:text-white;
55
+
56
+ &:hover {
57
+ @apply text-neutral-800
58
+ dark:text-neutral-200;
59
+ }
60
+ }
61
+
62
+ ol {
63
+ @apply flex
64
+ w-full
65
+ list-none
66
+ flex-col
67
+ gap-1.5
68
+ p-0;
69
+ }
70
+
71
+ svg {
72
+ @apply size-4
73
+ text-neutral-600
74
+ dark:text-neutral-400;
75
+ }
76
+
77
+ &:last-child {
78
+ @apply mb-0;
79
+ }
80
+ }
81
+
82
+ [data-on-dark] {
83
+ @apply hidden
84
+ dark:block;
85
+ }
86
+
87
+ [data-on-light] {
88
+ @apply block
89
+ dark:hidden;
90
+ }
91
+ }
@@ -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;
@@ -0,0 +1,125 @@
1
+ @reference "../../styles/index.css";
2
+
3
+ .container {
4
+ @apply border-neutral-200
5
+ bg-white
6
+ lg:flex
7
+ lg:h-16
8
+ lg:flex-row
9
+ lg:items-center
10
+ lg:gap-8
11
+ lg:border-b
12
+ lg:px-8
13
+ dark:border-neutral-900
14
+ dark:bg-neutral-950;
15
+ }
16
+
17
+ .nodeIconAndMobileItemsToggler {
18
+ @apply flex
19
+ h-16
20
+ shrink-0
21
+ items-center
22
+ border-b
23
+ border-neutral-200
24
+ px-4
25
+ lg:flex
26
+ lg:h-full
27
+ lg:items-center
28
+ lg:border-0
29
+ lg:px-0
30
+ dark:border-neutral-900;
31
+ }
32
+
33
+ .sidebarItemToggler {
34
+ @apply absolute
35
+ right-4
36
+ -z-10
37
+ -translate-y-[200%]
38
+ appearance-none
39
+ opacity-0;
40
+ }
41
+
42
+ .nodeIconWrapper {
43
+ @apply h-[30px]
44
+ flex-1;
45
+ }
46
+
47
+ .navInteractionIcon,
48
+ .sidebarItemToggler {
49
+ @apply size-6;
50
+ }
51
+
52
+ .sidebarItemTogglerLabel {
53
+ @apply block
54
+ cursor-pointer
55
+ lg:hidden;
56
+ }
57
+
58
+ .main {
59
+ @apply flex-1
60
+ flex-col
61
+ justify-between
62
+ gap-4
63
+ lg:flex
64
+ lg:flex-row
65
+ lg:items-center;
66
+ }
67
+
68
+ .navItems {
69
+ @apply flex
70
+ flex-col
71
+ gap-0
72
+ border-b
73
+ border-neutral-200
74
+ p-4
75
+ lg:flex-1
76
+ lg:flex-row
77
+ lg:gap-1
78
+ lg:border-0
79
+ lg:p-0
80
+ dark:border-neutral-900;
81
+ }
82
+
83
+ .actionsWrapper {
84
+ @apply flex
85
+ flex-row
86
+ flex-wrap
87
+ items-center
88
+ justify-between
89
+ gap-2
90
+ border-b
91
+ border-neutral-200
92
+ p-4
93
+ sm:flex-nowrap
94
+ lg:basis-96
95
+ lg:border-0
96
+ lg:p-0
97
+ dark:border-neutral-900;
98
+ }
99
+
100
+ span.searchButtonSkeleton {
101
+ @apply my-px
102
+ mr-2
103
+ flex-grow
104
+ rounded-xl;
105
+
106
+ &:empty {
107
+ @apply h-10;
108
+ }
109
+ }
110
+
111
+ .ghIconWrapper {
112
+ @apply size-9
113
+ rounded-md
114
+ p-2;
115
+
116
+ svg {
117
+ @apply fill-neutral-700
118
+ dark:fill-neutral-300;
119
+ }
120
+
121
+ &:hover {
122
+ @apply bg-neutral-100
123
+ dark:bg-neutral-900;
124
+ }
125
+ }
@@ -0,0 +1,45 @@
1
+ import type { Meta as MetaObj, StoryObj } from '@storybook/react';
2
+
3
+ import NavBar from '#ui/Containers/NavBar';
4
+
5
+ type Story = StoryObj<typeof NavBar>;
6
+ type Meta = MetaObj<typeof NavBar>;
7
+
8
+ export const Default: Story = {
9
+ args: {
10
+ as: 'a',
11
+ Logo: 'a',
12
+ pathname: '/',
13
+
14
+ children: <a>Some other child</a>,
15
+
16
+ navItems: [
17
+ {
18
+ text: 'Learn',
19
+ link: '/',
20
+ },
21
+ {
22
+ text: 'About',
23
+ link: '/about',
24
+ },
25
+ {
26
+ text: 'Docs',
27
+ link: '/docs',
28
+ },
29
+ {
30
+ text: 'Download',
31
+ link: '/download',
32
+ },
33
+ {
34
+ text: 'Blog',
35
+ link: '/blog',
36
+ },
37
+ {
38
+ text: 'Certification',
39
+ link: 'https://openjsf.org/certification',
40
+ },
41
+ ],
42
+ },
43
+ };
44
+
45
+ export default { component: NavBar } as Meta;
@@ -0,0 +1,94 @@
1
+ 'use client';
2
+
3
+ import Hamburger from '@heroicons/react/24/solid/Bars3Icon';
4
+ import XMark from '@heroicons/react/24/solid/XMarkIcon';
5
+ import * as Label from '@radix-ui/react-label';
6
+ import classNames from 'classnames';
7
+ import { useState } from 'react';
8
+ import type {
9
+ FC,
10
+ HTMLAttributeAnchorTarget,
11
+ PropsWithChildren,
12
+ ElementType,
13
+ } from 'react';
14
+
15
+ import NavItem from '#ui/Containers/NavBar/NavItem';
16
+ import type { FormattedMessage, LinkLike } from '#ui/types';
17
+
18
+ import style from './index.module.css';
19
+
20
+ const navInteractionIcons = {
21
+ show: <Hamburger className={style.navInteractionIcon} />,
22
+ close: <XMark className={style.navInteractionIcon} />,
23
+ };
24
+
25
+ type NavbarProps = {
26
+ navItems: Array<{
27
+ text: FormattedMessage;
28
+ link: string;
29
+ target?: HTMLAttributeAnchorTarget | undefined;
30
+ }>;
31
+ Logo: ElementType;
32
+ as: LinkLike;
33
+ pathname: string;
34
+ sidebarItemTogglerAriaLabel: string;
35
+ };
36
+
37
+ const NavBar: FC<PropsWithChildren<NavbarProps>> = ({
38
+ children,
39
+ Logo,
40
+ as: Component = 'a',
41
+ navItems,
42
+ pathname,
43
+ sidebarItemTogglerAriaLabel,
44
+ }) => {
45
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
46
+
47
+ return (
48
+ <nav className={style.container}>
49
+ <div className={style.nodeIconAndMobileItemsToggler}>
50
+ <Component className={style.nodeIconWrapper} href="/" aria-label="Home">
51
+ <Logo />
52
+ </Component>
53
+
54
+ <Label.Root
55
+ className={style.sidebarItemTogglerLabel}
56
+ htmlFor="sidebarItemToggler"
57
+ role="button"
58
+ aria-label={sidebarItemTogglerAriaLabel}
59
+ >
60
+ {navInteractionIcons[isMenuOpen ? 'close' : 'show']}
61
+ </Label.Root>
62
+ </div>
63
+
64
+ <input
65
+ className={classNames(['peer', style.sidebarItemToggler])}
66
+ id="sidebarItemToggler"
67
+ type="checkbox"
68
+ onChange={e => setIsMenuOpen(() => e.target.checked)}
69
+ aria-label={sidebarItemTogglerAriaLabel}
70
+ tabIndex={-1}
71
+ />
72
+
73
+ <div className={`${style.main} hidden peer-checked:flex`}>
74
+ <div className={style.navItems}>
75
+ {navItems.map(({ text, link, target }) => (
76
+ <NavItem
77
+ pathname={pathname}
78
+ as={Component}
79
+ key={link}
80
+ href={link}
81
+ target={target}
82
+ >
83
+ {text}
84
+ </NavItem>
85
+ ))}
86
+ </div>
87
+
88
+ <div className={style.actionsWrapper}>{children}</div>
89
+ </div>
90
+ </nav>
91
+ );
92
+ };
93
+
94
+ export default NavBar;