@thecb/components 11.2.14-beta.0 → 11.2.14-beta.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "11.2.14-beta.0",
3
+ "version": "11.2.14-beta.2",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -4,7 +4,6 @@ import { Box, Motion } from "../../atoms/layouts";
4
4
  import { fallbackValues } from "./NavMenu.theme.js";
5
5
  import { themeComponent } from "../../../util/themeUtils";
6
6
 
7
- // Use transforms (x) rather than left/right
8
7
  const menuVariants = {
9
8
  invisible: {
10
9
  x: "-100vw",
@@ -23,17 +22,23 @@ const menuVariants = {
23
22
  };
24
23
 
25
24
  const ImposterMenu = styled(Motion)`
25
+ /* Overlay starts below the header */
26
26
  position: fixed;
27
- top: ${({ headerSize }) => headerSize};
27
+ // This isn't working top: ${({ headerSize }) => headerSize};
28
+ top: 74px;
28
29
  left: 0;
29
30
  right: 0;
31
+ bottom: 0;
32
+ /* Dim background for everything below the navbar */
33
+ background: ${({ themeValues }) =>
34
+ themeValues?.overlayColor || "rgba(0, 0, 0, 0.5)"};
30
35
  `;
31
36
 
32
37
  const NavMenuMobile = ({
33
38
  id,
34
39
  menuContent = [],
35
40
  visible = false,
36
- headerSize = "72px",
41
+ headerSize = "74px",
37
42
  themeValues
38
43
  }) => {
39
44
  return (
@@ -43,15 +48,17 @@ const NavMenuMobile = ({
43
48
  initial="invisible"
44
49
  animate={visible ? "visible" : "invisible"}
45
50
  headerSize={headerSize}
51
+ themeValues={themeValues}
46
52
  >
53
+ {/*
54
+ The Box is the “menu” portion.
55
+ It’s narrower, but the parent ImposterMenu is full-screen & gray.
56
+ */}
47
57
  <Box
48
- width="100vw"
58
+ width="100%"
59
+ maxWidth="400px"
49
60
  padding="1rem 0.5rem"
50
- extraStyles={`
51
- position: relative;
52
- max-width: 400px;
53
- height: calc(100vh - 72px);
54
- `}
61
+ extraStyles={`position: relative; max-width: 400px; height: calc(100vh - 72px);`}
55
62
  background={themeValues.backgroundColor}
56
63
  >
57
64
  {menuContent}
@@ -2,8 +2,20 @@ import { Canvas, Meta, Title, Story, Controls } from '@storybook/blocks';
2
2
 
3
3
  import * as NavMenuMobileStories from './NavMenuMobile.stories.js';
4
4
 
5
+ <Story />
6
+
5
7
  <Meta of={NavMenuMobileStories} />
6
8
 
7
9
  <Title />
8
10
 
9
- <Story />
11
+ The `NavMenuMobile` component is a responsive navigation menu designed specifically for mobile devices. It provides a clean and accessible interface for navigating through application sections. The menu can be toggled open or closed and supports customizable content and themes.
12
+
13
+ <Controls />
14
+
15
+
16
+ ### Features
17
+
18
+ - **Customizable Content**: The `menuContent` prop allows you to define the content displayed inside the menu.
19
+ - **Theming**: The component supports theming via the `themeValues` prop, enabling you to style the menu to match your application's design system.
20
+ - **Visibility Control**: The `visible` prop determines whether the menu is displayed, making it easy to toggle programmatically.
21
+ - **Header Positioning**: The `headerSize` prop ensures the menu is positioned correctly relative to the header.
@@ -1,9 +1,7 @@
1
1
  import React, { useState } from "react";
2
2
  import NavMenuMobile from "./NavMenuMobile";
3
- // import { Box, Button } from "../../layouts";
4
- import { Box } from "../../atoms/layouts";
5
- import { ButtonWithAction } from "../../atoms";
6
-
3
+ import { Box, Cluster } from "../../atoms/layouts";
4
+ import { HamburgerButton } from "../../atoms";
7
5
  import { fallbackValues } from "./NavMenu.theme";
8
6
 
9
7
  export default {
@@ -24,7 +22,7 @@ export default {
24
22
  </Box>
25
23
  ),
26
24
  visible: false,
27
- headerSize: "72px",
25
+ headerSize: "74px",
28
26
  themeValues: fallbackValues
29
27
  },
30
28
  argTypes: {
@@ -53,7 +51,7 @@ export default {
53
51
  description: "Height of the header, used to position the menu",
54
52
  table: {
55
53
  type: { summary: "string" },
56
- defaultValue: { summary: "72px" }
54
+ defaultValue: { summary: "74px" }
57
55
  }
58
56
  },
59
57
  themeValues: {
@@ -66,59 +64,37 @@ export default {
66
64
  }
67
65
  };
68
66
 
69
- export const ProfileMenu = {
67
+ export const BasicMenu = {
70
68
  args: {
71
- id: "profile-menu",
69
+ id: "basic-menu",
70
+ headerSize: "74px",
71
+ themeValues: fallbackValues,
72
72
  menuContent: (
73
- <Box>
74
- <p>Profile Item 1</p>
75
- <p>Profile Item 2</p>
76
- <p>Profile Item 3</p>
77
- </Box>
78
- ),
79
- visible: false,
80
- headerSize: "72px",
81
- themeValues: fallbackValues
73
+ <>
74
+ <p>Nav Item 1</p>
75
+ <p>Nav Item 2</p>
76
+ <p>Nav Item 3</p>
77
+ </>
78
+ )
82
79
  },
83
80
  render: args => {
84
- const [visible, setVisible] = useState(false);
85
-
86
- const toggleMenu = () => {
87
- setVisible(prev => !prev);
88
- };
89
-
90
- return (
91
- <Box>
92
- <ButtonWithAction onClick={toggleMenu}>
93
- {visible ? "Hide Menu" : "Show Menu"}
94
- </ButtonWithAction>
95
- <NavMenuMobile {...args} visible={visible} />
96
- </Box>
97
- );
98
- }
99
- };
100
-
101
- export const DefaultMenu = {
102
- args: {
103
- visible: true
104
- }
105
- };
106
-
107
- export const InteractiveMenu = {
108
- render: args => {
109
- const [visible, setVisible] = useState(false);
110
-
111
- const toggleMenu = () => {
112
- setVisible(prev => !prev);
113
- };
114
-
81
+ const [navMenuOpen, setNavMenuOpen] = useState(false);
115
82
  return (
116
- <Box>
117
- <ButtonWithAction onClick={toggleMenu}>
118
- {visible ? "Hide Menu" : "Show Menu"}
119
- </ButtonWithAction>
120
- <NavMenuMobile {...args} visible={visible} />
121
- </Box>
83
+ <>
84
+ <Cluster
85
+ extraStyles="width: 100%; height: 100vh;"
86
+ justify="center"
87
+ align="center"
88
+ >
89
+ <HamburgerButton
90
+ isActive={navMenuOpen}
91
+ onClick={() => setNavMenuOpen(!navMenuOpen)}
92
+ activeColor={args.themeValues.backgroundColor.profile}
93
+ inactiveColor={args.themeValues.backgroundColor.profile}
94
+ />
95
+ </Cluster>
96
+ <NavMenuMobile {...args} visible={navMenuOpen} />
97
+ </>
122
98
  );
123
99
  }
124
100
  };
@@ -1,258 +0,0 @@
1
- import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useRef,
6
- useState
7
- } from "react";
8
- import { test } from "ramda";
9
- import {
10
- Box,
11
- Cluster,
12
- constants,
13
- Stack,
14
- Paragraph,
15
- ExternalLink,
16
- InternalLink,
17
- Switcher,
18
- withWindowSize
19
- } from "@thecb/components";
20
- import { ThemeContext } from "styled-components";
21
- import { URL_TEST } from "../../../constants/regex_constants";
22
- import HeaderItem from "./HeaderItem";
23
- import RightArrowIcon from "../../icons/RightArrowIcon";
24
- import NavLabel from "./NavLabel";
25
- import NavLink, { NavLinkLevels } from "./NavLink";
26
- import { getCallToActionInfo } from "../../../util/dataAdapters";
27
-
28
- const KEY_ARROW_DOWN = 40;
29
- const KEY_ARROW_UP = 38;
30
- const KEY_TAB = 9;
31
-
32
- const Link = ({ url, id, onSetRef, children }) => {
33
- return test(URL_TEST, url) ? (
34
- <ExternalLink href={url} ref={element => onSetRef(id, element)}>
35
- {children}
36
- </ExternalLink>
37
- ) : (
38
- <InternalLink to={url} ref={element => onSetRef(id, element)}>
39
- {children}
40
- </InternalLink>
41
- );
42
- };
43
-
44
- const LinkMenu = ({
45
- link,
46
- onChangeMenu,
47
- hamburgerMenuStyle,
48
- kbNavUsed,
49
- themeValues
50
- }) => {
51
- const { isMobile } = useContext(ThemeContext);
52
- const linkElements = useRef({});
53
- const [focusLink, setFocusLink] = useState(-1);
54
-
55
- const setElementFocus = key => {
56
- if (kbNavUsed) {
57
- linkElements.current[key].focus();
58
- }
59
- };
60
-
61
- const handleSetRef = useCallback((id, element) => {
62
- return (linkElements.current[id] = element);
63
- }, []);
64
-
65
- useEffect(() => {
66
- const ref1Key = Object.keys(linkElements.current)[0];
67
- setElementFocus(ref1Key);
68
- setFocusLink(0);
69
- }, []);
70
-
71
- const SmallLink = ({ text, showArrow = false }) => (
72
- <Cluster justify="flex-start" align="center">
73
- <Paragraph variant={isMobile ? "p" : "pS"} color={themeValues.linkColor}>
74
- {text}
75
- </Paragraph>
76
- {showArrow && <RightArrowIcon size={18} color={themeValues.linkColor} />}
77
- </Cluster>
78
- );
79
-
80
- const handleKeyDown = event => {
81
- switch (event.keyCode) {
82
- case KEY_TAB:
83
- event.preventDefault();
84
- onChangeMenu(event.shiftKey ? "previous" : "next");
85
- break;
86
- case KEY_ARROW_DOWN:
87
- case KEY_ARROW_UP:
88
- event.preventDefault();
89
- const increment = event.keyCode === KEY_ARROW_DOWN ? 1 : -1;
90
- const length = Object.keys(linkElements.current).length;
91
- const newFocus = (focusLink + (increment % length) + length) % length;
92
- const newFocusKey = Object.keys(linkElements.current)[newFocus];
93
- setFocusLink(newFocus);
94
- setElementFocus(newFocusKey);
95
- break;
96
- default:
97
- break;
98
- }
99
- };
100
-
101
- const renderSubLinkSectionName = subLink => {
102
- const { ctaUrl: subLinkUrl } = getCallToActionInfo(
103
- subLink.callToActionLink,
104
- "",
105
- subLink.nameUrl
106
- );
107
- if (hamburgerMenuStyle) {
108
- return subLinkUrl ? (
109
- <NavLink
110
- url={subLinkUrl}
111
- label={subLink.name}
112
- themeValues={themeValues}
113
- navLevel={NavLinkLevels.SUB_MENU}
114
- />
115
- ) : (
116
- <NavLabel text={subLink.name} themeValues={themeValues} navLevel={1} />
117
- );
118
- } else {
119
- return subLinkUrl ? (
120
- <Link url={subLinkUrl} id={subLink.id} onSetRef={handleSetRef}>
121
- <HeaderItem
122
- name={subLink?.name}
123
- showArrow={!hamburgerMenuStyle}
124
- themeValues={themeValues}
125
- />
126
- </Link>
127
- ) : (
128
- <HeaderItem
129
- name={subLink?.name}
130
- showArrow={false}
131
- themeValues={themeValues}
132
- />
133
- );
134
- }
135
- };
136
-
137
- const renderSubLinkLink = (smallLink, index) => {
138
- if (hamburgerMenuStyle) {
139
- return (
140
- <NavLink
141
- key={`navlink-${index}`}
142
- url={smallLink.externalUrl}
143
- label={smallLink.text}
144
- themeValues={themeValues}
145
- navLevel={NavLinkLevels.DETAIL}
146
- />
147
- );
148
- } else {
149
- return (
150
- <Box padding={"0"} key={`link-${index}`}>
151
- <Link
152
- url={smallLink.externalUrl}
153
- id={smallLink.id}
154
- onSetRef={handleSetRef}
155
- >
156
- <SmallLink text={smallLink.text} />
157
- </Link>
158
- </Box>
159
- );
160
- }
161
- };
162
-
163
- const { ctaUrl } = getCallToActionInfo(
164
- link.callToActionLink,
165
- "",
166
- link.nameUrl
167
- );
168
-
169
- return (
170
- <Box
171
- padding={hamburgerMenuStyle ? "16px 0" : "0"}
172
- background={
173
- hamburgerMenuStyle ? constants.colors.ATHENS_GREY : "transparent"
174
- }
175
- onKeyDown={handleKeyDown}
176
- extraStyles={
177
- hamburgerMenuStyle ? "margin-left: 16px; margin-right: 16px;" : ""
178
- }
179
- >
180
- <Switcher breakpoint="45rem" largeChild="1" largeChildSize="2">
181
- {hamburgerMenuStyle ? (
182
- <Box padding={hamburgerMenuStyle ? "0 16px 16px" : 0}>
183
- <Paragraph variant="pS" color={constants.colors.CHARADE_GREY}>
184
- {link?.description}
185
- </Paragraph>
186
- </Box>
187
- ) : (
188
- <Box key="title-desc" padding={"0 16px 0 4px"}>
189
- <Stack childGap="8px">
190
- <Box padding="0">
191
- {ctaUrl ? (
192
- <Box padding="0">
193
- <Link url={ctaUrl} id={link.id} onSetRef={handleSetRef}>
194
- <HeaderItem
195
- name={link?.name}
196
- showArrow={!hamburgerMenuStyle}
197
- themeValues={themeValues}
198
- />
199
- </Link>
200
- </Box>
201
- ) : (
202
- <HeaderItem
203
- name={link?.name}
204
- showArrow={ctaUrl !== ""}
205
- themeValues={themeValues}
206
- />
207
- )}
208
- </Box>
209
- <Box padding={"16px 24px 16px 0"}>
210
- <Paragraph variant="pS">{link?.description}</Paragraph>
211
- </Box>
212
- </Stack>
213
- </Box>
214
- )}
215
- {link?.linkLists?.map((subLink, index) => (
216
- <Box
217
- padding={hamburgerMenuStyle ? "0" : "0 24px"}
218
- key={`subLink-${index}`}
219
- >
220
- <Stack childGap={"1rem"}>
221
- {renderSubLinkSectionName(subLink)}
222
- {subLink?.links?.map((smallLink, index) =>
223
- renderSubLinkLink(smallLink, index)
224
- )}
225
- </Stack>
226
- </Box>
227
- ))}
228
- <Box padding={hamburgerMenuStyle ? "16px 0" : "0 24px"}>
229
- <Stack childGap={"1rem"}>
230
- <Box padding={"0"}>
231
- {hamburgerMenuStyle ? (
232
- <NavLabel
233
- text={"Featured Services"}
234
- themeValues={themeValues}
235
- navLevel={1}
236
- />
237
- ) : (
238
- <HeaderItem
239
- name="Featured Services"
240
- showArrow={false}
241
- themeValues={themeValues}
242
- color={themeValues.linkColor}
243
- weight={constants.fontWeights.FONT_WEIGHT_SEMIBOLD}
244
- variant="p"
245
- />
246
- )}
247
- </Box>
248
- {link?.featuredLinks?.links?.map((featuredLink, index) =>
249
- renderSubLinkLink(featuredLink, index)
250
- )}
251
- </Stack>
252
- </Box>
253
- </Switcher>
254
- </Box>
255
- );
256
- };
257
-
258
- export default withWindowSize(LinkMenu);
@@ -1,138 +0,0 @@
1
- import React, { useContext } from "react";
2
- import { Box, Cover, Heading, Motion, withWindowSize } from "@thecb/components";
3
- import { ThemeContext } from "styled-components";
4
- import LinkMenu from "./LinkMenu";
5
- import HamburgerNavSection from "./HamburgerNavSection";
6
- import { borderWrapper } from "./style";
7
-
8
- const NavLinks = ({
9
- navigation,
10
- hamburgerMenuStyle,
11
- themeValues,
12
- navMenuOpen,
13
- hamburgerOpenSection,
14
- selectedNavMenu,
15
- setSearchMenuOpen,
16
- setSelectedMenu,
17
- setNavMenuOpen,
18
- setIsAnimating,
19
- setKeyboardNavUsed,
20
- setHamburgerOpenSection
21
- }) => {
22
- const { isMobile } = useContext(ThemeContext);
23
- const selectMenu = (index, kbNavUsed) => {
24
- setKeyboardNavUsed(kbNavUsed);
25
- if (navMenuOpen !== true) {
26
- setSearchMenuOpen(false);
27
- setSelectedMenu(index);
28
- setNavMenuOpen(true);
29
- setIsAnimating(true);
30
- setTimeout(() => {
31
- setIsAnimating(false);
32
- }, 600);
33
- } else if (selectedNavMenu !== index) {
34
- setSelectedMenu(index);
35
- setSearchMenuOpen(false);
36
- }
37
- };
38
-
39
- return (
40
- <>
41
- {navigation?.sections?.map((link, index) => {
42
- debugger;
43
- return (
44
- <Box
45
- padding="0"
46
- key={`nav-link-${index}`}
47
- extraStyles={!hamburgerMenuStyle && `height: 102px;`}
48
- >
49
- <Cover
50
- singleChild
51
- minHeight={isMobile ? "0%" : "100%"}
52
- key={`section-${index}`}
53
- >
54
- {hamburgerMenuStyle ? (
55
- <HamburgerNavSection
56
- link={link}
57
- toggleSection={() => {
58
- link?.id === hamburgerOpenSection
59
- ? setHamburgerOpenSection("")
60
- : setHamburgerOpenSection(link?.id);
61
- }}
62
- isOpen={hamburgerOpenSection === link?.id}
63
- name={link?.name}
64
- themeValues={themeValues}
65
- >
66
- <Motion
67
- padding="0"
68
- transition={{ duration: 0.3 }}
69
- positionTransition
70
- extraStyles={`transform-origin: 100% 0;`}
71
- >
72
- <LinkMenu
73
- key={link.id}
74
- link={link}
75
- hamburgerMenuStyle={hamburgerMenuStyle}
76
- themeValues={themeValues}
77
- kbNavUsed={false}
78
- onChangeMenu={() => {
79
- setNavMenuOpen(false);
80
- setSelectedMenu(100);
81
- }}
82
- />
83
- </Motion>
84
- </HamburgerNavSection>
85
- ) : (
86
- <Box
87
- padding="0"
88
- as="button"
89
- background="transparent"
90
- tabIndex="0"
91
- onClick={() => {
92
- setSearchMenuOpen(false);
93
- setSelectedMenu(index);
94
- setNavMenuOpen(!navMenuOpen);
95
- setIsAnimating(true);
96
- setTimeout(() => {
97
- setIsAnimating(false);
98
- }, 600);
99
- }}
100
- onMouseEnter={() => selectMenu(index, false)}
101
- onFocus={() => selectMenu(index, true)}
102
- >
103
- <Box padding="0 1.5rem">
104
- <Heading
105
- variant="h6"
106
- color={isMobile ? "#FFFFFF" : themeValues.linkColor}
107
- extraStyles={
108
- isMobile ? "font-size: 14px;" : `font-size: 16px;`
109
- }
110
- >
111
- {link?.name}
112
- </Heading>
113
- </Box>
114
- </Box>
115
- )}
116
- {!hamburgerMenuStyle && (
117
- <Motion
118
- padding="0"
119
- minWidth="100%"
120
- variants={borderWrapper}
121
- animate={navMenuOpen ? "open" : "closed"}
122
- layoutTransition
123
- extraStyles={`border-bottom: 3px solid ${
124
- selectedNavMenu === index
125
- ? `${themeValues.linkColor}`
126
- : `transparent`
127
- }`}
128
- />
129
- )}
130
- </Cover>
131
- </Box>
132
- );
133
- })}
134
- </>
135
- );
136
- };
137
-
138
- export default NavLinks;