@hubspot/cms-component-library 0.1.0 → 0.2.0

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 (143) hide show
  1. package/components/componentLibrary/Accordion/AccordionContent/ContentFields.tsx +5 -3
  2. package/components/componentLibrary/Accordion/AccordionItem/StyleFields.tsx +5 -3
  3. package/components/componentLibrary/Accordion/AccordionItem/index.module.scss +2 -2
  4. package/components/componentLibrary/Accordion/AccordionItem/index.tsx +3 -3
  5. package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +5 -3
  6. package/components/componentLibrary/Accordion/AccordionTitle/index.module.scss +2 -2
  7. package/components/componentLibrary/Accordion/stories/Accordion.stories.tsx +80 -1
  8. package/components/componentLibrary/Accordion/stories/AccordionDecorator.tsx +14 -14
  9. package/components/componentLibrary/Button/ContentFields.tsx +5 -3
  10. package/components/componentLibrary/Button/StyleFields.tsx +5 -3
  11. package/components/componentLibrary/Button/index.module.scss +22 -14
  12. package/components/componentLibrary/Button/index.tsx +6 -6
  13. package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +30 -1
  14. package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +38 -1
  15. package/components/componentLibrary/Button/stories/ButtonDecorator.tsx +1 -1
  16. package/components/componentLibrary/Card/StyleFields.tsx +5 -3
  17. package/components/componentLibrary/Card/stories/Card.stories.tsx +46 -1
  18. package/components/componentLibrary/Card/stories/CardDecorator.tsx +1 -1
  19. package/components/componentLibrary/Divider/ContentFields.tsx +5 -3
  20. package/components/componentLibrary/Divider/StyleFields.tsx +5 -3
  21. package/components/componentLibrary/Divider/index.module.scss +6 -6
  22. package/components/componentLibrary/Divider/index.tsx +7 -3
  23. package/components/componentLibrary/Divider/stories/Divider.stories.tsx +44 -50
  24. package/components/componentLibrary/Divider/stories/{DividerDecorator.module.css → DividerDecorator.module.scss} +5 -4
  25. package/components/componentLibrary/Divider/stories/DividerDecorator.tsx +1 -1
  26. package/components/componentLibrary/Divider/types.ts +3 -1
  27. package/components/componentLibrary/Drawer/hooks/index.tsx +13 -0
  28. package/components/componentLibrary/Drawer/index.module.scss +94 -0
  29. package/components/componentLibrary/Drawer/index.tsx +131 -0
  30. package/components/componentLibrary/Drawer/llm.txt +416 -0
  31. package/components/componentLibrary/Drawer/stories/Drawer.stories.tsx +512 -0
  32. package/components/componentLibrary/Drawer/stories/DrawerDecorator.module.scss +8 -0
  33. package/components/componentLibrary/Drawer/stories/DrawerDecorator.tsx +18 -0
  34. package/components/componentLibrary/Drawer/types.ts +25 -0
  35. package/components/componentLibrary/Flex/stories/FlexDecorator.tsx +1 -1
  36. package/components/componentLibrary/Flex/types.ts +3 -1
  37. package/components/componentLibrary/Grid/stories/Grid.stories.tsx +454 -152
  38. package/components/componentLibrary/Grid/stories/GridDecorator.tsx +2 -2
  39. package/components/componentLibrary/Heading/ContentFields.tsx +5 -3
  40. package/components/componentLibrary/Heading/StyleFields.tsx +11 -9
  41. package/components/componentLibrary/Heading/index.tsx +3 -3
  42. package/components/componentLibrary/Heading/llm.txt +8 -8
  43. package/components/componentLibrary/Heading/stories/Heading.stories.tsx +3 -3
  44. package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +1 -1
  45. package/components/componentLibrary/Heading/types.ts +4 -4
  46. package/components/componentLibrary/Icon/ContentFields.tsx +5 -3
  47. package/components/componentLibrary/Icon/stories/Icon.stories.tsx +1 -1
  48. package/components/componentLibrary/Icon/stories/IconDecorator.tsx +1 -1
  49. package/components/componentLibrary/Image/ContentFields.tsx +5 -3
  50. package/components/componentLibrary/Image/index.tsx +4 -4
  51. package/components/componentLibrary/Image/llm.txt +17 -17
  52. package/components/componentLibrary/Image/stories/Image.stories.tsx +61 -18
  53. package/components/componentLibrary/Image/stories/ImageDecorator.tsx +1 -1
  54. package/components/componentLibrary/Image/types.ts +2 -2
  55. package/components/componentLibrary/LanguageSwitcher/ContentFields.tsx +18 -0
  56. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.module.scss +37 -0
  57. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.tsx +65 -0
  58. package/components/componentLibrary/LanguageSwitcher/StyleFields.tsx +48 -0
  59. package/components/componentLibrary/LanguageSwitcher/_dummyData.tsx +247 -0
  60. package/components/componentLibrary/LanguageSwitcher/assets/Globe.tsx +16 -0
  61. package/components/componentLibrary/LanguageSwitcher/index.module.scss +58 -0
  62. package/components/componentLibrary/LanguageSwitcher/index.tsx +125 -0
  63. package/components/componentLibrary/LanguageSwitcher/llm.txt +380 -0
  64. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcher.stories.tsx +349 -0
  65. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.module.scss +5 -0
  66. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.tsx +8 -0
  67. package/components/componentLibrary/LanguageSwitcher/types.ts +48 -0
  68. package/components/componentLibrary/LanguageSwitcher/utils.tsx +38 -0
  69. package/components/componentLibrary/Link/ContentFields.tsx +5 -3
  70. package/components/componentLibrary/Link/StyleFields.tsx +5 -3
  71. package/components/componentLibrary/Link/index.module.scss +10 -0
  72. package/components/componentLibrary/Link/index.tsx +24 -14
  73. package/components/componentLibrary/Link/stories/Link.stories.tsx +35 -5
  74. package/components/componentLibrary/Link/stories/LinkDecorator.tsx +11 -1
  75. package/components/componentLibrary/Link/types.ts +22 -13
  76. package/components/componentLibrary/List/ContentFields.tsx +5 -3
  77. package/components/componentLibrary/List/ListItem/ContentFields.tsx +6 -17
  78. package/components/componentLibrary/List/ListItem/index.module.scss +1 -13
  79. package/components/componentLibrary/List/ListItem/index.tsx +3 -30
  80. package/components/componentLibrary/List/ListItem/types.ts +1 -16
  81. package/components/componentLibrary/List/StyleFields.tsx +15 -18
  82. package/components/componentLibrary/List/index.module.scss +3 -0
  83. package/components/componentLibrary/List/index.tsx +5 -2
  84. package/components/componentLibrary/List/llm.txt +73 -103
  85. package/components/componentLibrary/List/stories/List.stories.tsx +56 -80
  86. package/components/componentLibrary/List/stories/ListDecorator.tsx +3 -6
  87. package/components/componentLibrary/List/types.ts +1 -3
  88. package/components/componentLibrary/Logo/_dummyLogoData.ts +12 -0
  89. package/components/componentLibrary/Logo/assets/hubspot-logo.png +0 -0
  90. package/components/componentLibrary/Logo/index.module.scss +22 -0
  91. package/components/componentLibrary/Logo/index.tsx +73 -0
  92. package/components/componentLibrary/Logo/llm.txt +262 -0
  93. package/components/componentLibrary/Logo/stories/Logo.stories.tsx +88 -0
  94. package/components/componentLibrary/Logo/stories/LogoDecorator.module.scss +10 -0
  95. package/components/componentLibrary/Logo/stories/LogoDecorator.tsx +8 -0
  96. package/components/componentLibrary/Logo/types.tsx +16 -0
  97. package/components/componentLibrary/Menu/ContentFields.tsx +16 -0
  98. package/components/componentLibrary/Menu/MenuItem/Chevron/index.module.scss +6 -0
  99. package/components/componentLibrary/Menu/MenuItem/Chevron/index.tsx +17 -0
  100. package/components/componentLibrary/Menu/MenuItem/index.module.scss +7 -0
  101. package/components/componentLibrary/Menu/MenuItem/index.tsx +266 -0
  102. package/components/componentLibrary/Menu/MenuItem/types.ts +17 -0
  103. package/components/componentLibrary/Menu/NavigationMenu/ContentFields.tsx +20 -0
  104. package/components/componentLibrary/Menu/NavigationMenu/index.tsx +18 -0
  105. package/components/componentLibrary/Menu/NavigationMenu/islands/NavigationMenuIsland.tsx +95 -0
  106. package/components/componentLibrary/Menu/NavigationMenu/islands/index.module.scss +100 -0
  107. package/components/componentLibrary/Menu/NavigationMenu/islands/types.ts +19 -0
  108. package/components/componentLibrary/Menu/NavigationMenu/llm.txt +197 -0
  109. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenu.stories.tsx +286 -0
  110. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.module.scss +15 -0
  111. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.tsx +12 -0
  112. package/components/componentLibrary/Menu/NavigationMenu/types.ts +3 -0
  113. package/components/componentLibrary/Menu/VerticalMenu/ContentFields.tsx +20 -0
  114. package/components/componentLibrary/Menu/VerticalMenu/index.tsx +18 -0
  115. package/components/componentLibrary/Menu/VerticalMenu/islands/index.module.scss +53 -0
  116. package/components/componentLibrary/Menu/VerticalMenu/islands/verticalMenuIsland.tsx +78 -0
  117. package/components/componentLibrary/Menu/VerticalMenu/llm.txt +177 -0
  118. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenu.stories.tsx +242 -0
  119. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.module.scss +19 -0
  120. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.tsx +12 -0
  121. package/components/componentLibrary/Menu/VerticalMenu/types.ts +21 -0
  122. package/components/componentLibrary/Menu/_dummyMenuData.js +1346 -0
  123. package/components/componentLibrary/Menu/types.ts +56 -0
  124. package/components/componentLibrary/Menu/utils/transformMenuData.ts +11 -0
  125. package/components/componentLibrary/_patterns/README.md +15 -17
  126. package/components/componentLibrary/_patterns/checklist-and-examples.md +17 -17
  127. package/components/componentLibrary/_patterns/component-structure.md +21 -23
  128. package/components/componentLibrary/_patterns/css-patterns.md +170 -18
  129. package/components/componentLibrary/_patterns/field-patterns.md +97 -27
  130. package/components/componentLibrary/_patterns/function-declaration-patterns.md +281 -0
  131. package/components/componentLibrary/_patterns/llm-txt.template.md +4 -2
  132. package/components/componentLibrary/_patterns/prop-naming-patterns.md +208 -0
  133. package/components/componentLibrary/_patterns/storybook-patterns.md +25 -8
  134. package/components/componentLibrary/_patterns/typescript-patterns.md +6 -3
  135. package/package.json +4 -2
  136. /package/components/componentLibrary/Button/stories/{ButtonDecorator.module.css → ButtonDecorator.module.scss} +0 -0
  137. /package/components/componentLibrary/Card/stories/{CardDecorator.module.css → CardDecorator.module.scss} +0 -0
  138. /package/components/componentLibrary/Flex/stories/{FlexDecorator.module.css → FlexDecorator.module.scss} +0 -0
  139. /package/components/componentLibrary/Grid/stories/{GridDecorator.module.css → GridDecorator.module.scss} +0 -0
  140. /package/components/componentLibrary/Heading/stories/{HeadingDecorator.module.css → HeadingDecorator.module.scss} +0 -0
  141. /package/components/componentLibrary/Icon/stories/{IconDecorator.module.css → IconDecorator.module.scss} +0 -0
  142. /package/components/componentLibrary/Image/stories/{ImageDecorator.module.css → ImageDecorator.module.scss} +0 -0
  143. /package/components/componentLibrary/Image/stories/assets/{catSmile.jpg → cat-smile.jpg} +0 -0
@@ -11,21 +11,21 @@
11
11
 
12
12
  // Horizontal divider (hr element)
13
13
  .horizontal {
14
- width: var(--hscl-divider-length, 100%);
14
+ width: var(--hscl-divider-width, 100%);
15
15
  height: 0;
16
16
  border: 0;
17
- border-top: var(--hscl-divider-thickness, 1px) var(--hscl-divider-borderStyle, solid) var(--hscl-divider-borderColor, currentColor);
17
+ border-top: var(--hscl-divider-borderWidth, 1px) var(--hscl-divider-borderStyle, solid) var(--hscl-divider-borderColor, currentColor);
18
18
  align-self: var(--hscl-divider-alignment, stretch);
19
- margin-block: var(--hscl-divider-spacing, 16px);
19
+ margin-block: var(--hscl-divider-marginBlock, 16px);
20
20
  }
21
21
 
22
22
  // Vertical divider (div element)
23
23
  .vertical {
24
24
  width: 0;
25
- height: var(--hscl-divider-length, 100%);
25
+ height: var(--hscl-divider-height, 100%);
26
26
  border: 0;
27
- border-left: var(--hscl-divider-thickness, 1px) var(--hscl-divider-borderStyle, solid) var(--hscl-divider-borderColor, currentColor);
27
+ border-left: var(--hscl-divider-borderWidth, 1px) var(--hscl-divider-borderStyle, solid) var(--hscl-divider-borderColor, currentColor);
28
28
  display: inline-block;
29
29
  align-self: var(--hscl-divider-alignment, stretch);
30
- margin-inline: var(--hscl-divider-spacing, 16px);
30
+ margin-inline: var(--hscl-divider-marginInline, 16px);
31
31
  }
@@ -38,10 +38,14 @@ const DividerComponent = ({
38
38
 
39
39
  const cssVariables: CSSVariables = {
40
40
  '--hscl-divider-alignment': getAlignmentCSSVar(alignment),
41
- '--hscl-divider-spacing': spacing,
42
41
  '--hscl-divider-borderStyle': borderStyle,
43
- '--hscl-divider-length': `${length}%`,
44
- '--hscl-divider-thickness': `${thickness}px`,
42
+ [`--hscl-divider-margin${
43
+ orientation === 'horizontal' ? 'Block' : 'Inline'
44
+ }`]: spacing,
45
+ [`--hscl-divider-${
46
+ orientation === 'horizontal' ? 'width' : 'height'
47
+ }`]: `${length}%`,
48
+ '--hscl-divider-borderWidth': `${thickness}px`,
45
49
  ...style,
46
50
  };
47
51
 
@@ -4,6 +4,7 @@ import { DividerProps } from '../types.js';
4
4
  import { withDividerStyles } from './DividerDecorator.js';
5
5
  import { SBContainer } from '@sb-utils/SBContainer.js';
6
6
  import { SBCard } from '@sb-utils/SBCard.js';
7
+ import type { CSSVariables } from '../../utils/types.js';
7
8
 
8
9
  const meta: Meta<DividerProps> = {
9
10
  title: 'Component Library/Divider',
@@ -53,61 +54,54 @@ export const Default: Story = {
53
54
 
54
55
  export const Colors: Story = {
55
56
  name: 'Colors',
56
- render: () => (
57
- <SBContainer flex direction="column" gap="large" minWidth="400px">
58
- <SBContainer addBackground>
59
- <h4>Default (currentColor)</h4>
60
- <p>Inherits text color from parent</p>
61
- <Divider thickness={2} />
62
- </SBContainer>
57
+ render: () => {
58
+ const blueColor: CSSVariables = {
59
+ '--hscl-divider-borderColor': '#3b82f6',
60
+ };
61
+ const greenColor: CSSVariables = {
62
+ '--hscl-divider-borderColor': '#10b981',
63
+ };
64
+ const redColor: CSSVariables = {
65
+ '--hscl-divider-borderColor': '#ef4444',
66
+ };
67
+ const transparentColor: CSSVariables = {
68
+ '--hscl-divider-borderColor': 'rgba(0, 0, 0, 0.2)',
69
+ };
70
+
71
+ return (
72
+ <SBContainer flex direction="column" gap="large" minWidth="400px">
73
+ <SBContainer addBackground>
74
+ <h4>Default (currentColor)</h4>
75
+ <p>Inherits text color from parent</p>
76
+ <Divider thickness={2} />
77
+ </SBContainer>
63
78
 
64
- <SBContainer addBackground>
65
- <h4>Custom Color (Blue)</h4>
66
- <p>Use CSS variables to customize color</p>
67
- <Divider
68
- thickness={2}
69
- style={
70
- { '--hscl-divider-borderColor': '#3b82f6' } as React.CSSProperties
71
- }
72
- />
73
- </SBContainer>
79
+ <SBContainer addBackground>
80
+ <h4>Custom Color (Blue)</h4>
81
+ <p>Use CSS variables to customize color</p>
82
+ <Divider thickness={2} style={blueColor} />
83
+ </SBContainer>
74
84
 
75
- <SBContainer addBackground>
76
- <h4>Custom Color (Green)</h4>
77
- <p>Any valid CSS color value works</p>
78
- <Divider
79
- thickness={2}
80
- style={
81
- { '--hscl-divider-borderColor': '#10b981' } as React.CSSProperties
82
- }
83
- />
84
- </SBContainer>
85
+ <SBContainer addBackground>
86
+ <h4>Custom Color (Green)</h4>
87
+ <p>Any valid CSS color value works</p>
88
+ <Divider thickness={2} style={greenColor} />
89
+ </SBContainer>
85
90
 
86
- <SBContainer addBackground>
87
- <h4>Custom Color (Red)</h4>
88
- <p>Great for error states or warnings</p>
89
- <Divider
90
- thickness={2}
91
- style={
92
- { '--hscl-divider-borderColor': '#ef4444' } as React.CSSProperties
93
- }
94
- />
95
- </SBContainer>
91
+ <SBContainer addBackground>
92
+ <h4>Custom Color (Red)</h4>
93
+ <p>Great for error states or warnings</p>
94
+ <Divider thickness={2} style={redColor} />
95
+ </SBContainer>
96
96
 
97
- <SBContainer addBackground>
98
- <h4>Semi-transparent</h4>
99
- <p>Use rgba for transparency effects</p>
100
- <Divider
101
- thickness={2}
102
- style={
103
- {
104
- '--hscl-divider-borderColor': 'rgba(0, 0, 0, 0.2)',
105
- } as React.CSSProperties
106
- }
107
- />
97
+ <SBContainer addBackground>
98
+ <h4>Semi-transparent</h4>
99
+ <p>Use rgba for transparency effects</p>
100
+ <Divider thickness={2} style={transparentColor} />
101
+ </SBContainer>
108
102
  </SBContainer>
109
- </SBContainer>
110
- ),
103
+ );
104
+ },
111
105
  };
112
106
 
113
107
  export const BorderStyles: Story = {
@@ -7,8 +7,9 @@
7
7
  min-width: 400px;
8
8
  --hscl-divider-borderColor: #cbd5e1;
9
9
  --hscl-divider-borderStyle: solid;
10
- --hscl-divider-thickness: 1px;
11
- --hscl-divider-length: 100%;
12
- --hscl-divider-spacing: 16px;
13
- --hscl-divider-alignment: stretch;
10
+ --hscl-divider-borderWidth: 1px;
11
+ --hscl-divider-width: 100%;
12
+ --hscl-divider-marginBlock: 16px;
13
+ --hscl-divider-marginInline: 16px;
14
+ --hscl-divider-alignSelf: stretch;
14
15
  }
@@ -1,5 +1,5 @@
1
1
  import type { Decorator } from '@storybook/react';
2
- import styles from './DividerDecorator.module.css';
2
+ import styles from './DividerDecorator.module.scss';
3
3
 
4
4
  export const withDividerStyles: Decorator = Story => (
5
5
  <div className={styles.decoratorContainer}>
@@ -1,3 +1,5 @@
1
+ import type { CSSVariables } from '../utils/types.js';
2
+
1
3
  export type DividerOrientation = 'horizontal' | 'vertical';
2
4
 
3
5
  export type DividerAlignment = 'stretch' | 'start' | 'center' | 'end';
@@ -15,7 +17,7 @@ export type DividerProps = {
15
17
  thickness?: number;
16
18
  variant?: 'primary' | 'secondary' | 'tertiary';
17
19
  className?: string;
18
- style?: React.CSSProperties;
20
+ style?: CSSVariables;
19
21
  };
20
22
 
21
23
  export type ContentFieldsProps = {
@@ -0,0 +1,13 @@
1
+ import { useState } from 'react';
2
+ import { UseDrawerReturn } from '../types.js';
3
+
4
+ export const useDrawer = (): UseDrawerReturn => {
5
+ const [isOpen, setIsOpen] = useState(false);
6
+
7
+ return {
8
+ isOpen,
9
+ open: () => setIsOpen(true),
10
+ close: () => setIsOpen(false),
11
+ toggle: () => setIsOpen(prev => !prev),
12
+ };
13
+ };
@@ -0,0 +1,94 @@
1
+ .drawer {
2
+ position: fixed;
3
+ background-color: var(--hscl-drawer-backgroundColor, white);
4
+ box-shadow: var(--hscl-drawer-boxShadow, 0 2px 8px rgba(0, 0, 0, 0.15));
5
+ z-index: var(--hscl-drawer-zIndex, 1000);
6
+ overflow: auto;
7
+ transition: transform 0.3s ease-in-out, visibility 0s 0.3s;
8
+ visibility: hidden;
9
+ pointer-events: none;
10
+ }
11
+
12
+ .drawer.open {
13
+ visibility: visible;
14
+ pointer-events: auto;
15
+ transition-delay: 0s;
16
+ }
17
+
18
+ @media (prefers-reduced-motion: reduce) {
19
+ .drawer {
20
+ transition: visibility 0s 0.3s;
21
+ }
22
+ }
23
+
24
+ .left {
25
+ top: 0;
26
+ left: 0;
27
+ bottom: 0;
28
+ width: var(--hscl-drawer-width);
29
+ transform: translateX(-100%);
30
+ }
31
+
32
+ .right {
33
+ top: 0;
34
+ right: 0;
35
+ bottom: 0;
36
+ width: var(--hscl-drawer-width);
37
+ transform: translateX(100%);
38
+ }
39
+
40
+ .top {
41
+ top: 0;
42
+ left: 0;
43
+ right: 0;
44
+ height: var(--hscl-drawer-height);
45
+ transform: translateY(-100%);
46
+ }
47
+
48
+ .bottom {
49
+ bottom: 0;
50
+ left: 0;
51
+ right: 0;
52
+ height: var(--hscl-drawer-height);
53
+ transform: translateY(100%);
54
+ }
55
+
56
+ .open.left {
57
+ transform: translateX(0);
58
+ }
59
+
60
+ .open.right {
61
+ transform: translateX(0);
62
+ }
63
+
64
+ .open.top {
65
+ transform: translateY(0);
66
+ }
67
+
68
+ .open.bottom {
69
+ transform: translateY(0);
70
+ }
71
+
72
+ .fullScreen {
73
+ top: 0 !important;
74
+ left: 0 !important;
75
+ right: 0 !important;
76
+ bottom: 0 !important;
77
+ width: 100% !important;
78
+ height: 100% !important;
79
+ }
80
+
81
+ .overlay {
82
+ position: fixed;
83
+ top: 0;
84
+ left: 0;
85
+ right: 0;
86
+ bottom: 0;
87
+ background-color: var(--hscl-drawer-overlay-backgroundColor, rgba(0, 0, 0, 0.5));
88
+ backdrop-filter: var(--hscl-drawer-overlay-backdropFilter, blur(3px));
89
+ z-index: var(--hscl-drawer-overlay-zIndex, 999);
90
+ }
91
+
92
+ :global(body.drawerOpen) {
93
+ overflow: hidden;
94
+ }
@@ -0,0 +1,131 @@
1
+ import { useEffect, useRef, useId } from 'react';
2
+ import styles from './index.module.scss';
3
+ import cx from '../utils/classname.js';
4
+ import { DrawerProps } from './types.js';
5
+
6
+ const drawerStack: string[] = [];
7
+
8
+ const Drawer = ({
9
+ open,
10
+ direction = 'right',
11
+ variant = 'primary',
12
+ className = '',
13
+ style = {},
14
+ children,
15
+ showOverlay = true,
16
+ onOverlayClick = () => {},
17
+ fullScreen = false,
18
+ size = '300px',
19
+ 'aria-label': ariaLabel,
20
+ 'aria-labelledby': ariaLabelledby,
21
+ ...rest
22
+ }: DrawerProps) => {
23
+ const drawerRef = useRef<HTMLDivElement>(null);
24
+ const drawerId = useId();
25
+ const isInStackRef = useRef(false);
26
+
27
+ const removeDrawerFromStack = () => {
28
+ const index = drawerStack.indexOf(drawerId);
29
+ if (index > -1) {
30
+ drawerStack.splice(index, 1);
31
+ }
32
+ isInStackRef.current = false;
33
+ // Only remove class if no drawers remain
34
+ if (drawerStack.length === 0) {
35
+ document.body.classList.remove('drawerOpen');
36
+ }
37
+ };
38
+
39
+ useEffect(() => {
40
+ // Add this drawer to the stack when it opens (if not already added)
41
+ if (open && !isInStackRef.current) {
42
+ drawerStack.push(drawerId);
43
+ isInStackRef.current = true;
44
+ document.body.classList.add('drawerOpen');
45
+ } else if (!open && isInStackRef.current) {
46
+ removeDrawerFromStack();
47
+ }
48
+
49
+ if (!open) {
50
+ return;
51
+ }
52
+
53
+ // Only close the topmost drawer when Escape is pressed
54
+ const handleEscape = (event: KeyboardEvent) => {
55
+ if (event.key === 'Escape') {
56
+ const topmostDrawerId = drawerStack[drawerStack.length - 1];
57
+ if (topmostDrawerId === drawerId) {
58
+ onOverlayClick();
59
+ }
60
+ }
61
+ };
62
+
63
+ document.addEventListener('keydown', handleEscape);
64
+
65
+ return () => {
66
+ document.removeEventListener('keydown', handleEscape);
67
+ };
68
+ }, [open, onOverlayClick, drawerId]);
69
+
70
+ // Cleanup: ensure drawer is removed from stack on unmount
71
+ useEffect(() => {
72
+ return () => {
73
+ if (isInStackRef.current) {
74
+ removeDrawerFromStack();
75
+ }
76
+ };
77
+ }, [drawerId]);
78
+
79
+ const variantClass = styles[`hscl-drawer-${variant}`]; // !todo: not used atm but keeping for when we need to add variant system.
80
+ const directionClass = styles[direction];
81
+ const openClass = open ? styles.open : '';
82
+ const fullScreenClass = fullScreen ? styles.fullScreen : '';
83
+
84
+ const defaultClasses = cx(
85
+ styles.drawer,
86
+ variantClass,
87
+ directionClass,
88
+ openClass,
89
+ fullScreenClass
90
+ );
91
+ const combinedClasses = cx(defaultClasses, className);
92
+
93
+ const cssVars = fullScreen
94
+ ? {}
95
+ : direction === 'left' || direction === 'right'
96
+ ? { '--hscl-drawer-width': size }
97
+ : { '--hscl-drawer-height': size };
98
+
99
+ const drawerStyle = {
100
+ ...cssVars,
101
+ ...style,
102
+ };
103
+
104
+ return (
105
+ <>
106
+ {showOverlay && open && (
107
+ <div
108
+ className={styles.overlay}
109
+ onClick={onOverlayClick}
110
+ aria-hidden="true"
111
+ />
112
+ )}
113
+ <div
114
+ ref={drawerRef}
115
+ className={combinedClasses}
116
+ style={drawerStyle}
117
+ role="dialog"
118
+ aria-modal="true"
119
+ aria-hidden={!open}
120
+ aria-label={ariaLabel}
121
+ aria-labelledby={ariaLabelledby}
122
+ {...rest}
123
+ >
124
+ {children}
125
+ </div>
126
+ </>
127
+ );
128
+ };
129
+
130
+ export default Drawer;
131
+ export { useDrawer } from './hooks/index.js';