@juspay/blend-design-system 0.0.37-beta.8 → 0.0.37

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 (240) hide show
  1. package/dist/components/Accordion/index.d.ts +2 -0
  2. package/dist/components/AccordionV2/accordionV2.animations.d.ts +1 -1
  3. package/dist/components/Alert/index.d.ts +1 -0
  4. package/dist/components/Avatar/index.d.ts +1 -0
  5. package/dist/components/Breadcrumb/index.d.ts +1 -0
  6. package/dist/components/Button/index.d.ts +1 -0
  7. package/dist/components/ButtonGroup/index.d.ts +1 -0
  8. package/dist/components/Card/index.d.ts +1 -0
  9. package/dist/components/CardV2/CardV2.d.ts +45 -0
  10. package/dist/components/CardV2/CardV2.utils.d.ts +14 -0
  11. package/dist/components/CardV2/CardV2Components.d.ts +8 -0
  12. package/dist/components/CardV2/CardV2Context.d.ts +14 -0
  13. package/dist/components/CardV2/CardV2Skeleton.d.ts +6 -0
  14. package/dist/components/CardV2/accessibility/CardV2AccessibilityReport.d.ts +2 -0
  15. package/dist/components/CardV2/accessibility/index.d.ts +1 -0
  16. package/dist/components/CardV2/cardV2.dark.tokens.d.ts +3 -0
  17. package/dist/components/CardV2/cardV2.light.tokens.d.ts +3 -0
  18. package/dist/components/CardV2/cardV2.tokens.d.ts +5 -0
  19. package/dist/components/CardV2/cardV2.tokens.types.d.ts +92 -0
  20. package/dist/components/CardV2/cardV2.types.d.ts +106 -0
  21. package/dist/components/CardV2/index.d.ts +5 -0
  22. package/dist/components/Charts/index.d.ts +10 -0
  23. package/dist/components/Charts/types.d.ts +2 -0
  24. package/dist/components/ChatInput/index.d.ts +1 -0
  25. package/dist/components/Checkbox/index.d.ts +1 -0
  26. package/dist/components/CodeEditor/index.d.ts +1 -0
  27. package/dist/components/DataTable/PivotTableModal/pivotModal.styled.d.ts +184 -0
  28. package/dist/components/DataTable/TableBody/types.d.ts +3 -1
  29. package/dist/components/DataTable/hooks/index.d.ts +2 -0
  30. package/dist/components/DataTable/hooks/useReducedMotion.d.ts +1 -0
  31. package/dist/components/DataTable/hooks/useRowFlip.d.ts +6 -0
  32. package/dist/components/DataTable/types.d.ts +26 -0
  33. package/dist/components/DateRangePicker/CalendarGrid.d.ts +5 -2
  34. package/dist/components/DateRangePicker/types.d.ts +3 -1
  35. package/dist/components/DateRangePicker/utils.d.ts +5 -5
  36. package/dist/components/Directory/Directory.d.ts +1 -1
  37. package/dist/components/Directory/NavItem.d.ts +1 -1
  38. package/dist/components/Directory/Section.d.ts +1 -1
  39. package/dist/components/Directory/VirtualizedDirectory.d.ts +3 -0
  40. package/dist/components/Directory/directory.tokens.d.ts +7 -0
  41. package/dist/components/Directory/types.d.ts +38 -1
  42. package/dist/components/Directory/utils.d.ts +3 -1
  43. package/dist/components/Drawer/index.d.ts +1 -0
  44. package/dist/components/Inputs/MultiValueInput/index.d.ts +1 -0
  45. package/dist/components/Inputs/NumberInput/index.d.ts +1 -0
  46. package/dist/components/Inputs/OTPInput/index.d.ts +1 -0
  47. package/dist/components/Inputs/SearchInput/index.d.ts +1 -0
  48. package/dist/components/Inputs/TextArea/index.d.ts +1 -0
  49. package/dist/components/Inputs/TextInput/index.d.ts +1 -0
  50. package/dist/components/InputsV2/TextAreaV2/utils.d.ts +2 -1
  51. package/dist/components/InputsV2/TextInputV2/utils.d.ts +2 -1
  52. package/dist/components/InputsV2/UploadV2/UploadContainerV2.d.ts +24 -0
  53. package/dist/components/InputsV2/UploadV2/UploadV2.d.ts +31 -0
  54. package/dist/components/InputsV2/UploadV2/UploadV2.dark.tokens.d.ts +3 -0
  55. package/dist/components/InputsV2/UploadV2/UploadV2.light.tokens.d.ts +3 -0
  56. package/dist/components/InputsV2/UploadV2/UploadV2.tokens.d.ts +5 -0
  57. package/dist/components/InputsV2/UploadV2/UploadV2.tokens.types.d.ts +115 -0
  58. package/dist/components/InputsV2/UploadV2/UploadV2.types.d.ts +56 -0
  59. package/dist/components/InputsV2/UploadV2/index.d.ts +3 -0
  60. package/dist/components/InputsV2/UploadV2/utils.d.ts +32 -0
  61. package/dist/components/KeyValuePair/index.d.ts +1 -0
  62. package/dist/components/Menu/Menu.d.ts +1 -1
  63. package/dist/components/Menu/index.d.ts +1 -0
  64. package/dist/components/Menu/types.d.ts +7 -0
  65. package/dist/components/Menu/utils.d.ts +10 -2
  66. package/dist/components/Modal/index.d.ts +1 -0
  67. package/dist/components/ModalV2/ModalV2.d.ts +22 -0
  68. package/dist/components/ModalV2/ModalV2Footer.d.ts +10 -0
  69. package/dist/components/ModalV2/ModalV2Header.d.ts +15 -0
  70. package/dist/components/ModalV2/ModalV2Skeleton.d.ts +4 -0
  71. package/dist/components/ModalV2/index.d.ts +4 -0
  72. package/dist/components/ModalV2/mobileModalV2.d.ts +23 -0
  73. package/dist/components/ModalV2/modalAnimationV2.d.ts +3 -0
  74. package/dist/components/ModalV2/modalV2.dark.tokens.d.ts +3 -0
  75. package/dist/components/ModalV2/modalV2.light.tokens.d.ts +3 -0
  76. package/dist/components/ModalV2/modalV2.tokens.d.ts +5 -0
  77. package/dist/components/ModalV2/modalV2.tokens.types.d.ts +91 -0
  78. package/dist/components/ModalV2/modalV2.types.d.ts +66 -0
  79. package/dist/components/MultiSelect/index.d.ts +2 -0
  80. package/dist/components/Popover/index.d.ts +1 -0
  81. package/dist/components/Primitives/Block/Block.d.ts +1 -1
  82. package/dist/components/ProgressBar/index.d.ts +1 -0
  83. package/dist/components/Radio/index.d.ts +2 -0
  84. package/dist/components/Sidebar/SidebarContent.d.ts +9 -1
  85. package/dist/components/Sidebar/index.d.ts +1 -0
  86. package/dist/components/Sidebar/types.d.ts +9 -1
  87. package/dist/components/SidebarV2/SidebarV2Panel.d.ts +10 -2
  88. package/dist/components/SidebarV2/types.d.ts +9 -1
  89. package/dist/components/SingleSelect/index.d.ts +1 -0
  90. package/dist/components/Skeleton/SkeletonCompound.d.ts +24 -33
  91. package/dist/components/Snackbar/index.d.ts +2 -0
  92. package/dist/components/StatCard/index.d.ts +1 -0
  93. package/dist/components/Stepper/index.d.ts +1 -0
  94. package/dist/components/Switch/index.d.ts +2 -0
  95. package/dist/components/Tabs/index.d.ts +1 -0
  96. package/dist/components/Tags/index.d.ts +1 -0
  97. package/dist/components/Timeline/Timeline.d.ts +13 -37
  98. package/dist/components/Tooltip/index.d.ts +1 -0
  99. package/dist/components/Topbar/index.d.ts +1 -0
  100. package/dist/components/Upload/index.d.ts +1 -0
  101. package/dist/context/ThemeContext.d.ts +6 -0
  102. package/dist/context/useComponentToken.d.ts +4 -1
  103. package/dist/main.d.ts +6 -0
  104. package/dist/main.js +45081 -41934
  105. package/dist/node-B3rrXW84.js +1023 -0
  106. package/dist/node.d.ts +2 -0
  107. package/dist/node.js +21 -18
  108. package/dist/token-engine.js +1 -1
  109. package/lib/components/Accordion/AccordionItem.tsx +2 -6
  110. package/lib/components/Accordion/index.ts +2 -0
  111. package/lib/components/AccordionV2/AccordionV2Item.tsx +1 -1
  112. package/lib/components/AccordionV2/accordionV2.animations.ts +1 -5
  113. package/lib/components/Alert/index.ts +1 -0
  114. package/lib/components/Avatar/index.ts +1 -0
  115. package/lib/components/Breadcrumb/index.ts +1 -0
  116. package/lib/components/Button/index.ts +1 -0
  117. package/lib/components/ButtonGroup/index.ts +1 -0
  118. package/lib/components/Card/index.ts +1 -0
  119. package/lib/components/CardV2/CardV2.tsx +327 -0
  120. package/lib/components/CardV2/CardV2.utils.tsx +77 -0
  121. package/lib/components/CardV2/CardV2Components.tsx +297 -0
  122. package/lib/components/CardV2/CardV2Context.tsx +18 -0
  123. package/lib/components/CardV2/CardV2Skeleton.tsx +40 -0
  124. package/lib/components/CardV2/accessibility/CardV2AccessibilityReport.ts +284 -0
  125. package/lib/components/CardV2/accessibility/index.ts +1 -0
  126. package/lib/components/CardV2/cardV2.dark.tokens.ts +177 -0
  127. package/lib/components/CardV2/cardV2.light.tokens.ts +177 -0
  128. package/lib/components/CardV2/cardV2.tokens.ts +21 -0
  129. package/lib/components/CardV2/cardV2.tokens.types.ts +94 -0
  130. package/lib/components/CardV2/cardV2.types.ts +131 -0
  131. package/lib/components/CardV2/index.ts +13 -0
  132. package/lib/components/Charts/ChartLegend.tsx +25 -5
  133. package/lib/components/Charts/Charts.tsx +10 -0
  134. package/lib/components/Charts/index.ts +10 -0
  135. package/lib/components/Charts/types.tsx +2 -0
  136. package/lib/components/ChatInput/index.tsx +1 -0
  137. package/lib/components/Checkbox/index.ts +1 -0
  138. package/lib/components/CodeEditor/index.ts +1 -0
  139. package/lib/components/DataTable/DataTable.tsx +8 -0
  140. package/lib/components/DataTable/TableBody/index.tsx +47 -11
  141. package/lib/components/DataTable/TableBody/types.ts +7 -1
  142. package/lib/components/DataTable/TableCell/index.tsx +1 -1
  143. package/lib/components/DataTable/hooks/index.ts +2 -0
  144. package/lib/components/DataTable/hooks/useReducedMotion.ts +19 -0
  145. package/lib/components/DataTable/hooks/useRowFlip.ts +170 -0
  146. package/lib/components/DataTable/types.ts +32 -0
  147. package/lib/components/DateRangePicker/CalendarGrid.tsx +36 -6
  148. package/lib/components/DateRangePicker/DateRangePicker.tsx +22 -3
  149. package/lib/components/DateRangePicker/types.ts +6 -1
  150. package/lib/components/DateRangePicker/utils.ts +123 -12
  151. package/lib/components/Directory/Directory.tsx +31 -0
  152. package/lib/components/Directory/NavItem.tsx +177 -86
  153. package/lib/components/Directory/Section.tsx +9 -0
  154. package/lib/components/Directory/VirtualizedDirectory.tsx +670 -0
  155. package/lib/components/Directory/directory.tokens.ts +21 -0
  156. package/lib/components/Directory/types.ts +43 -1
  157. package/lib/components/Directory/utils.ts +81 -1
  158. package/lib/components/Drawer/index.ts +1 -0
  159. package/lib/components/Inputs/DropdownInput/DropdownInput.tsx +6 -2
  160. package/lib/components/Inputs/MultiValueInput/index.ts +1 -0
  161. package/lib/components/Inputs/NumberInput/NumberInput.tsx +6 -2
  162. package/lib/components/Inputs/NumberInput/index.ts +1 -0
  163. package/lib/components/Inputs/OTPInput/index.ts +1 -0
  164. package/lib/components/Inputs/SearchInput/index.ts +1 -0
  165. package/lib/components/Inputs/TextArea/TextArea.tsx +4 -2
  166. package/lib/components/Inputs/TextArea/index.ts +1 -0
  167. package/lib/components/Inputs/TextInput/TextInput.tsx +6 -2
  168. package/lib/components/Inputs/TextInput/index.ts +1 -0
  169. package/lib/components/Inputs/UnitInput/UnitInput.tsx +6 -2
  170. package/lib/components/InputsV2/NumberInputV2/NumberInputV2.tsx +6 -2
  171. package/lib/components/InputsV2/TextAreaV2/TextAreaV2.tsx +2 -1
  172. package/lib/components/InputsV2/TextAreaV2/utils.ts +3 -1
  173. package/lib/components/InputsV2/TextInputV2/TextInputV2.tsx +1 -0
  174. package/lib/components/InputsV2/TextInputV2/utils.ts +6 -8
  175. package/lib/components/InputsV2/UploadV2/UploadContainerV2.tsx +470 -0
  176. package/lib/components/InputsV2/UploadV2/UploadV2.dark.tokens.ts +348 -0
  177. package/lib/components/InputsV2/UploadV2/UploadV2.light.tokens.ts +348 -0
  178. package/lib/components/InputsV2/UploadV2/UploadV2.tokens.ts +20 -0
  179. package/lib/components/InputsV2/UploadV2/UploadV2.tokens.types.ts +117 -0
  180. package/lib/components/InputsV2/UploadV2/UploadV2.tsx +336 -0
  181. package/lib/components/InputsV2/UploadV2/UploadV2.types.ts +67 -0
  182. package/lib/components/InputsV2/UploadV2/index.ts +3 -0
  183. package/lib/components/InputsV2/UploadV2/utils.ts +170 -0
  184. package/lib/components/KeyValuePair/index.ts +1 -0
  185. package/lib/components/Menu/Menu.tsx +18 -2
  186. package/lib/components/Menu/SubMenu.tsx +25 -3
  187. package/lib/components/Menu/index.ts +1 -0
  188. package/lib/components/Menu/types.tsx +14 -0
  189. package/lib/components/Menu/utils.ts +46 -3
  190. package/lib/components/Modal/index.ts +1 -0
  191. package/lib/components/ModalV2/ModalV2.tsx +269 -0
  192. package/lib/components/ModalV2/ModalV2Footer.tsx +76 -0
  193. package/lib/components/ModalV2/ModalV2Header.tsx +144 -0
  194. package/lib/components/ModalV2/ModalV2Skeleton.tsx +80 -0
  195. package/lib/components/ModalV2/index.ts +4 -0
  196. package/lib/components/ModalV2/mobileModalV2.tsx +411 -0
  197. package/lib/components/ModalV2/modalAnimationV2.tsx +19 -0
  198. package/lib/components/ModalV2/modalV2.dark.tokens.ts +183 -0
  199. package/lib/components/ModalV2/modalV2.light.tokens.ts +183 -0
  200. package/lib/components/ModalV2/modalV2.tokens.ts +21 -0
  201. package/lib/components/ModalV2/modalV2.tokens.types.ts +94 -0
  202. package/lib/components/ModalV2/modalV2.types.ts +80 -0
  203. package/lib/components/MultiSelect/MultiSelect.tsx +2 -1
  204. package/lib/components/MultiSelect/MultiSelectTrigger.tsx +2 -1
  205. package/lib/components/MultiSelect/index.ts +2 -0
  206. package/lib/components/MultiSelectV2/MultiSelectV2Trigger.tsx +4 -1
  207. package/lib/components/Popover/index.ts +1 -0
  208. package/lib/components/Primitives/Block/Block.tsx +1 -1
  209. package/lib/components/ProgressBar/index.ts +1 -0
  210. package/lib/components/Radio/index.ts +2 -0
  211. package/lib/components/Sidebar/Sidebar.tsx +38 -0
  212. package/lib/components/Sidebar/SidebarContent.tsx +25 -1
  213. package/lib/components/Sidebar/index.ts +1 -0
  214. package/lib/components/Sidebar/types.ts +13 -1
  215. package/lib/components/SidebarV2/SidebarV2.tsx +30 -0
  216. package/lib/components/SidebarV2/SidebarV2Panel.tsx +25 -1
  217. package/lib/components/SidebarV2/types.ts +9 -1
  218. package/lib/components/SingleSelect/SingleSelect.tsx +2 -1
  219. package/lib/components/SingleSelect/SingleSelectTrigger.tsx +3 -1
  220. package/lib/components/SingleSelect/index.ts +1 -0
  221. package/lib/components/SingleSelectV2/SingleSelectV2Trigger.tsx +3 -1
  222. package/lib/components/Skeleton/SkeletonCompound.tsx +45 -31
  223. package/lib/components/Snackbar/index.ts +2 -0
  224. package/lib/components/StatCard/index.ts +1 -0
  225. package/lib/components/Stepper/index.ts +1 -0
  226. package/lib/components/Switch/index.ts +2 -0
  227. package/lib/components/Tabs/index.ts +1 -0
  228. package/lib/components/Tags/index.ts +1 -0
  229. package/lib/components/Timeline/Timeline.tsx +9 -1
  230. package/lib/components/Tooltip/index.ts +1 -0
  231. package/lib/components/Topbar/index.ts +1 -0
  232. package/lib/components/Upload/index.ts +1 -0
  233. package/lib/context/ThemeContext.tsx +16 -0
  234. package/lib/context/initComponentTokens.ts +11 -0
  235. package/lib/context/useComponentToken.ts +15 -1
  236. package/lib/hooks/useResponsiveTokens.ts +63 -0
  237. package/lib/main.ts +16 -0
  238. package/lib/node.ts +2 -0
  239. package/package.json +1 -1
  240. /package/dist/{node-CovJEwot.js → breadcrumbV2.tokens-CovJEwot.js} +0 -0
@@ -0,0 +1,670 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react'
2
+ import { ChevronDown } from 'lucide-react'
3
+ import { useVirtualizer } from '@tanstack/react-virtual'
4
+ import styled from 'styled-components'
5
+ import Block from '../Primitives/Block/Block'
6
+ import Text from '../Text/Text'
7
+ import { TruncatedTextWithTooltipV2 } from '../common/TruncatedTextWithTooltipV2'
8
+ import { TooltipV2Side } from '../TooltipV2/tooltipV2.types'
9
+ import { useResponsiveTokens } from '../../hooks/useResponsiveTokens'
10
+ import { addPxToValue } from '../../global-utils/GlobalUtils'
11
+ import { DirectoryTokenType } from './directory.tokens'
12
+ import {
13
+ flattenDirectoryData,
14
+ handleKeyDown,
15
+ normalizeExpandedItems,
16
+ normalizeDirectoryData,
17
+ } from './utils'
18
+ import type { DirectoryFlatRow, DirectoryProps, NavbarItem } from './types'
19
+
20
+ const DEFAULT_ROW_HEIGHT = 36
21
+ const DEFAULT_SECTION_HEIGHT = 28
22
+ const DEFAULT_VIEWPORT_HEIGHT = 340
23
+ const DEFAULT_OVERSCAN = 8
24
+ const DEFAULT_THRESHOLD = 100
25
+
26
+ const SectionRow = styled.div<{
27
+ $tokens: DirectoryTokenType
28
+ $isCollapsible: boolean
29
+ $isOpen: boolean
30
+ }>`
31
+ width: 100%;
32
+ display: flex;
33
+ align-items: center;
34
+ cursor: ${({ $isCollapsible }) => ($isCollapsible ? 'pointer' : 'default')};
35
+ padding: ${({ $tokens }) =>
36
+ `${$tokens.section.header.padding.y} ${$tokens.section.header.padding.x}`};
37
+
38
+ & > svg {
39
+ width: ${({ $tokens }) =>
40
+ $tokens.section.header.chevron.width} !important;
41
+ height: ${({ $tokens }) =>
42
+ $tokens.section.header.chevron.width} !important;
43
+ margin-left: auto;
44
+ transition: transform 150ms;
45
+ transform: ${({ $isOpen }) =>
46
+ $isOpen ? 'rotate(180deg)' : 'rotate(0)'};
47
+ }
48
+ `
49
+
50
+ const ItemRow = styled.div<{
51
+ $tokens: DirectoryTokenType
52
+ $isActive: boolean
53
+ $depth: number
54
+ }>`
55
+ width: 100%;
56
+ position: relative;
57
+ padding-left: ${({ $tokens, $depth }) =>
58
+ `calc(${$tokens.section.itemList.nested.paddingLeft} * ${$depth})`};
59
+ `
60
+
61
+ const ConnectorLayer = styled.span`
62
+ position: absolute;
63
+ inset: 0;
64
+ pointer-events: none;
65
+ `
66
+
67
+ const ConnectorVerticalLine = styled.span<{
68
+ $tokens: DirectoryTokenType
69
+ $column: number
70
+ $isCurrent?: boolean
71
+ $isLast?: boolean
72
+ }>`
73
+ position: absolute;
74
+ left: ${({ $tokens, $column }) =>
75
+ `calc(${$tokens.section.itemList.nested.paddingLeft} * ${$column} + ${$tokens.section.itemList.nested.border.leftOffset})`};
76
+ top: 0;
77
+ height: ${({ $tokens, $isCurrent, $isLast }) =>
78
+ $isCurrent && $isLast
79
+ ? `calc(${$tokens.section.itemList.nested.connector.elbowTop} + ${$tokens.section.itemList.nested.connector.elbowHeight})`
80
+ : '100%'};
81
+ border-left: ${({ $tokens }) =>
82
+ `${$tokens.section.itemList.nested.border.width} solid ${$tokens.section.itemList.nested.border.color}`};
83
+ `
84
+
85
+ const ConnectorElbow = styled.span<{
86
+ $tokens: DirectoryTokenType
87
+ $column: number
88
+ $hierarchyLineBorderRadius: React.CSSProperties['borderRadius']
89
+ }>`
90
+ position: absolute;
91
+ left: ${({ $tokens, $column }) =>
92
+ `calc(${$tokens.section.itemList.nested.paddingLeft} * ${$column} + ${$tokens.section.itemList.nested.border.leftOffset})`};
93
+ top: ${({ $tokens }) => $tokens.section.itemList.nested.connector.elbowTop};
94
+ width: ${({ $tokens }) =>
95
+ `calc(${$tokens.section.itemList.nested.paddingLeft} - ${$tokens.section.itemList.nested.border.leftOffset} + ${$tokens.section.itemList.nested.connector.elbowWidthOffset})`};
96
+ height: ${({ $tokens }) =>
97
+ $tokens.section.itemList.nested.connector.elbowHeight};
98
+ border-left: ${({ $tokens }) =>
99
+ `${$tokens.section.itemList.nested.border.width} solid ${$tokens.section.itemList.nested.border.color}`};
100
+ border-bottom: ${({ $tokens }) =>
101
+ `${$tokens.section.itemList.nested.border.width} solid ${$tokens.section.itemList.nested.border.color}`};
102
+ border-bottom-left-radius: ${({ $hierarchyLineBorderRadius }) =>
103
+ addPxToValue($hierarchyLineBorderRadius)};
104
+ `
105
+
106
+ const ItemButton = styled(Block)<{
107
+ $tokens: DirectoryTokenType
108
+ $isActive: boolean
109
+ $showHierarchyLines: boolean
110
+ }>`
111
+ width: ${({ $tokens, $showHierarchyLines }) =>
112
+ $showHierarchyLines
113
+ ? `calc(100% - ${$tokens.section.itemList.nested.connector.itemInset})`
114
+ : '100%'};
115
+ margin-left: ${({ $tokens, $showHierarchyLines }) =>
116
+ $showHierarchyLines
117
+ ? $tokens.section.itemList.nested.connector.itemInset
118
+ : 0};
119
+ min-width: 0;
120
+ border: none;
121
+ border-radius: ${({ $tokens }) =>
122
+ $tokens.section.itemList.item.borderRadius};
123
+ background-color: ${({ $tokens, $isActive }) =>
124
+ $isActive
125
+ ? $tokens.section.itemList.item.backgroundColor.active
126
+ : $tokens.section.itemList.item.backgroundColor.default};
127
+ color: ${({ $tokens, $isActive }) =>
128
+ $isActive
129
+ ? $tokens.section.itemList.item.color.active
130
+ : $tokens.section.itemList.item.color.default};
131
+ cursor: pointer;
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: flex-start;
135
+ gap: ${({ $tokens }) => $tokens.section.itemList.item.gap};
136
+ padding: ${({ $tokens, $showHierarchyLines }) =>
137
+ `${$tokens.section.itemList.item.padding.y} ${$tokens.section.itemList.item.padding.x} ${$tokens.section.itemList.item.padding.y} ${
138
+ $showHierarchyLines
139
+ ? $tokens.section.itemList.nested.connector.itemPaddingLeft
140
+ : $tokens.section.itemList.item.padding.x
141
+ }`};
142
+ font-size: ${({ $tokens }) =>
143
+ addPxToValue($tokens.section.itemList.item.fontSize)};
144
+ font-weight: ${({ $tokens }) => $tokens.section.itemList.item.fontWeight};
145
+ overflow: hidden;
146
+ text-decoration: none;
147
+ transition: ${({ $tokens }) => $tokens.section.itemList.item.transition};
148
+
149
+ &:hover,
150
+ &:focus-visible {
151
+ background-color: ${({ $tokens, $isActive }) =>
152
+ $isActive
153
+ ? $tokens.section.itemList.item.backgroundColor.active
154
+ : $tokens.section.itemList.item.backgroundColor.hover};
155
+ color: ${({ $tokens, $isActive }) =>
156
+ $isActive
157
+ ? $tokens.section.itemList.item.color.active
158
+ : $tokens.section.itemList.item.color.hover};
159
+ outline: none;
160
+ }
161
+ `
162
+
163
+ const IconWrapper = styled.span<{ $tokens: DirectoryTokenType }>`
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ flex-shrink: 0;
168
+ width: ${({ $tokens }) => $tokens.section.itemList.item.icon.width};
169
+ height: ${({ $tokens }) => $tokens.section.itemList.item.icon.width};
170
+
171
+ & > svg {
172
+ width: ${({ $tokens }) =>
173
+ $tokens.section.itemList.item.icon.width} !important;
174
+ height: ${({ $tokens }) =>
175
+ $tokens.section.itemList.item.icon.width} !important;
176
+ }
177
+ `
178
+
179
+ const ChevronWrapper = styled.span<{
180
+ $tokens: DirectoryTokenType
181
+ $isExpanded: boolean
182
+ }>`
183
+ display: flex;
184
+ align-items: center;
185
+ justify-content: center;
186
+ margin-left: auto;
187
+
188
+ & > svg {
189
+ width: ${({ $tokens }) =>
190
+ $tokens.section.itemList.item.chevron.width} !important;
191
+ height: ${({ $tokens }) =>
192
+ $tokens.section.itemList.item.chevron.width} !important;
193
+ transition: transform 150ms;
194
+ transform: ${({ $isExpanded }) =>
195
+ $isExpanded ? 'rotate(180deg)' : 'rotate(0)'};
196
+ }
197
+ `
198
+
199
+ const getDefaultOpenSections = (
200
+ directoryData: DirectoryProps['directoryData']
201
+ ) =>
202
+ normalizeDirectoryData(directoryData).reduce<Set<number>>(
203
+ (openSections, section, index) => {
204
+ if (section.defaultOpen !== false) {
205
+ openSections.add(index)
206
+ }
207
+ return openSections
208
+ },
209
+ new Set<number>()
210
+ )
211
+
212
+ const getFocusableRowIndex = (
213
+ rows: DirectoryFlatRow[],
214
+ currentIndex: number,
215
+ direction: 'up' | 'down'
216
+ ) => {
217
+ const step = direction === 'up' ? -1 : 1
218
+ let nextIndex = currentIndex + step
219
+
220
+ while (nextIndex >= 0 && nextIndex < rows.length) {
221
+ const row = rows[nextIndex]
222
+ if (
223
+ row?.type === 'item' ||
224
+ (row?.type === 'section' && row.section.isCollapsible !== false)
225
+ ) {
226
+ return nextIndex
227
+ }
228
+ nextIndex += step
229
+ }
230
+
231
+ return currentIndex
232
+ }
233
+
234
+ const VirtualizedDirectory = ({
235
+ directoryData: directoryDataProp,
236
+ activeItem: controlledActiveItem,
237
+ onActiveItemChange,
238
+ defaultActiveItem,
239
+ showHierarchyLines = false,
240
+ hierarchyLineBorderRadius = 0,
241
+ expandedItems,
242
+ defaultExpandedItems,
243
+ onExpandedItemsChange,
244
+ onItemExpand,
245
+ virtualization,
246
+ }: DirectoryProps) => {
247
+ const tokens = useResponsiveTokens<DirectoryTokenType>('DIRECTORY')
248
+ const directoryData = useMemo(
249
+ () => normalizeDirectoryData(directoryDataProp),
250
+ [directoryDataProp]
251
+ )
252
+ const scrollRef = useRef<HTMLDivElement | null>(null)
253
+ const isActiveControlled = controlledActiveItem !== undefined
254
+ const [internalActiveItem, setInternalActiveItem] = useState<string | null>(
255
+ defaultActiveItem ?? null
256
+ )
257
+ const activeItem = isActiveControlled
258
+ ? controlledActiveItem!
259
+ : internalActiveItem
260
+ const isExpandedControlled = expandedItems !== undefined
261
+ const [internalExpandedItems, setInternalExpandedItems] = useState<
262
+ Set<string>
263
+ >(() => normalizeExpandedItems(defaultExpandedItems))
264
+ const currentExpandedItems = useMemo(
265
+ () =>
266
+ isExpandedControlled
267
+ ? normalizeExpandedItems(expandedItems)
268
+ : internalExpandedItems,
269
+ [expandedItems, internalExpandedItems, isExpandedControlled]
270
+ )
271
+ const [openSections, setOpenSections] = useState<Set<number>>(() =>
272
+ getDefaultOpenSections(directoryData)
273
+ )
274
+ useEffect(() => {
275
+ setOpenSections(getDefaultOpenSections(directoryData))
276
+ }, [directoryData])
277
+
278
+ const rows = useMemo(
279
+ () =>
280
+ flattenDirectoryData(
281
+ directoryData,
282
+ currentExpandedItems,
283
+ openSections
284
+ ),
285
+ [currentExpandedItems, directoryData, openSections]
286
+ )
287
+ const rowHeight = virtualization?.rowHeight ?? DEFAULT_ROW_HEIGHT
288
+ const sectionHeight =
289
+ virtualization?.sectionHeight ?? DEFAULT_SECTION_HEIGHT
290
+ const viewportHeight =
291
+ virtualization?.viewportHeight ?? DEFAULT_VIEWPORT_HEIGHT
292
+ const overscan = virtualization?.overscan ?? DEFAULT_OVERSCAN
293
+ const threshold = virtualization?.threshold ?? DEFAULT_THRESHOLD
294
+ const useVirtualRows = rows.length >= threshold
295
+ const virtualizer = useVirtualizer({
296
+ count: rows.length,
297
+ getScrollElement: () =>
298
+ virtualization?.viewportRef?.current ?? scrollRef.current,
299
+ getItemKey: (index) => {
300
+ const row = rows[index]
301
+ if (!row) return index
302
+ return row.type === 'section'
303
+ ? `section-${row.sectionIndex}`
304
+ : row.itemPath
305
+ },
306
+ estimateSize: (index) =>
307
+ rows[index]?.type === 'section' ? sectionHeight : rowHeight,
308
+ overscan,
309
+ enabled: useVirtualRows,
310
+ initialRect: {
311
+ width: 0,
312
+ height: viewportHeight,
313
+ },
314
+ })
315
+ const virtualRows = virtualizer.getVirtualItems()
316
+ const fallbackRows =
317
+ useVirtualRows && virtualRows.length === 0
318
+ ? rows
319
+ .slice(0, Math.ceil(viewportHeight / rowHeight) + overscan)
320
+ .map((row, index) => ({
321
+ key:
322
+ row.type === 'section'
323
+ ? `section-${row.sectionIndex}`
324
+ : row.itemPath,
325
+ index,
326
+ start: index * rowHeight,
327
+ }))
328
+ : virtualRows
329
+
330
+ const setExpanded = (item: NavbarItem, itemPath: string, next: boolean) => {
331
+ const nextExpandedItems = new Set(currentExpandedItems)
332
+ if (next) {
333
+ nextExpandedItems.add(itemPath)
334
+ void onItemExpand?.(item, itemPath)
335
+ } else {
336
+ nextExpandedItems.delete(itemPath)
337
+ }
338
+
339
+ const nextExpandedItemsList = Array.from(nextExpandedItems)
340
+ if (!isExpandedControlled) {
341
+ setInternalExpandedItems(nextExpandedItems)
342
+ }
343
+ onExpandedItemsChange?.(nextExpandedItemsList)
344
+ }
345
+
346
+ const setActiveItem = (itemPath: string) => {
347
+ if (!isActiveControlled) {
348
+ setInternalActiveItem(itemPath)
349
+ }
350
+ onActiveItemChange?.(itemPath)
351
+ }
352
+
353
+ const focusRow = (rowIndex: number) => {
354
+ virtualizer.scrollToIndex(rowIndex, { align: 'auto' })
355
+
356
+ const focusRowElement = () => {
357
+ const focusableRow = scrollRef.current?.querySelector<HTMLElement>(
358
+ `[data-directory-row-index="${rowIndex}"]`
359
+ )
360
+ focusableRow?.focus()
361
+ }
362
+
363
+ focusRowElement()
364
+ if (window.requestAnimationFrame) {
365
+ window.requestAnimationFrame(focusRowElement)
366
+ } else {
367
+ window.setTimeout(focusRowElement, 0)
368
+ }
369
+ }
370
+
371
+ const navigateRows = (currentIndex: number, direction: 'up' | 'down') => {
372
+ const nextIndex = getFocusableRowIndex(rows, currentIndex, direction)
373
+ if (nextIndex !== currentIndex) {
374
+ focusRow(nextIndex)
375
+ }
376
+ }
377
+
378
+ const renderSection = (
379
+ row: Extract<DirectoryFlatRow, { type: 'section' }>,
380
+ rowIndex: number
381
+ ) => {
382
+ const isOpen = openSections.has(row.sectionIndex)
383
+ const isCollapsible = row.section.isCollapsible !== false
384
+
385
+ const toggleSection = () => {
386
+ if (!isCollapsible) return
387
+ setOpenSections((prev) => {
388
+ const next = new Set(prev)
389
+ if (next.has(row.sectionIndex)) {
390
+ next.delete(row.sectionIndex)
391
+ } else {
392
+ next.add(row.sectionIndex)
393
+ }
394
+ return next
395
+ })
396
+ }
397
+
398
+ return (
399
+ <SectionRow
400
+ $tokens={tokens}
401
+ $isOpen={isOpen}
402
+ $isCollapsible={isCollapsible}
403
+ role={isCollapsible ? 'button' : undefined}
404
+ tabIndex={isCollapsible ? 0 : undefined}
405
+ aria-expanded={isCollapsible ? isOpen : undefined}
406
+ aria-label={
407
+ isCollapsible && row.section.label
408
+ ? `${row.section.label}, ${isOpen ? 'expanded' : 'collapsed'}`
409
+ : undefined
410
+ }
411
+ onClick={toggleSection}
412
+ onKeyDown={(event) => {
413
+ if (event.key === 'Enter' || event.key === ' ') {
414
+ event.preventDefault()
415
+ toggleSection()
416
+ } else if (event.key === 'ArrowDown') {
417
+ event.preventDefault()
418
+ navigateRows(rowIndex, 'down')
419
+ } else if (event.key === 'ArrowUp') {
420
+ event.preventDefault()
421
+ navigateRows(rowIndex, 'up')
422
+ }
423
+ }}
424
+ data-directory-row-index={rowIndex}
425
+ >
426
+ <Text
427
+ variant="body.sm"
428
+ color={tokens.section.header.label.color}
429
+ fontWeight={tokens.section.header.label.fontWeight}
430
+ fontSize={tokens.section.header.label.fontSize}
431
+ style={{ whiteSpace: 'nowrap' }}
432
+ >
433
+ {row.section.label?.toUpperCase()}
434
+ </Text>
435
+ {isCollapsible && (
436
+ <ChevronDown color={tokens.section.header.chevron.color} />
437
+ )}
438
+ </SectionRow>
439
+ )
440
+ }
441
+
442
+ const renderItem = (
443
+ row: Extract<DirectoryFlatRow, { type: 'item' }>,
444
+ rowIndex: number
445
+ ) => {
446
+ const hasChildren = !!row.item.items?.length
447
+ const isExpanded = currentExpandedItems.has(row.itemPath)
448
+ const isActive =
449
+ row.item.isSelected !== undefined
450
+ ? row.item.isSelected && !hasChildren
451
+ : !hasChildren &&
452
+ (activeItem === row.itemPath || activeItem === row.item.label)
453
+
454
+ const Element = row.item.href ? 'a' : 'button'
455
+ const elementProps = row.item.href
456
+ ? { href: row.item.href }
457
+ : { type: 'button' }
458
+ const showItemHierarchyLines = showHierarchyLines && row.depth > 0
459
+ const ancestorLineColumns = row.ancestorIsLast
460
+ .slice(1)
461
+ .reduce<number[]>((columns, isLast, index) => {
462
+ if (!isLast) {
463
+ columns.push(index)
464
+ }
465
+ return columns
466
+ }, [])
467
+ const currentLineColumn = row.depth - 1
468
+ const activateItem = () => {
469
+ if (hasChildren) {
470
+ setExpanded(row.item, row.itemPath, !isExpanded)
471
+ } else {
472
+ setActiveItem(row.itemPath)
473
+ row.item.onClick?.()
474
+ }
475
+ }
476
+
477
+ return (
478
+ <ItemRow
479
+ $tokens={tokens}
480
+ $isActive={isActive}
481
+ $depth={row.depth}
482
+ data-directory-hierarchy-item={
483
+ showItemHierarchyLines ? 'true' : undefined
484
+ }
485
+ >
486
+ {showItemHierarchyLines && (
487
+ <ConnectorLayer aria-hidden="true">
488
+ {ancestorLineColumns.map((column) => (
489
+ <ConnectorVerticalLine
490
+ key={`ancestor-${column}`}
491
+ $tokens={tokens}
492
+ $column={column}
493
+ />
494
+ ))}
495
+ <ConnectorVerticalLine
496
+ $tokens={tokens}
497
+ $column={currentLineColumn}
498
+ $isCurrent
499
+ $isLast={row.isLast}
500
+ />
501
+ <ConnectorElbow
502
+ $tokens={tokens}
503
+ $column={currentLineColumn}
504
+ $hierarchyLineBorderRadius={
505
+ hierarchyLineBorderRadius
506
+ }
507
+ />
508
+ </ConnectorLayer>
509
+ )}
510
+ <ItemButton
511
+ as={Element}
512
+ {...elementProps}
513
+ $tokens={tokens}
514
+ $isActive={isActive}
515
+ $showHierarchyLines={showItemHierarchyLines}
516
+ aria-expanded={hasChildren ? isExpanded : undefined}
517
+ aria-label={row.item.label}
518
+ data-element="sidebar-sub-section"
519
+ data-id={row.item.label}
520
+ data-status={isActive ? 'selected' : 'not selected'}
521
+ data-directory-row-index={rowIndex}
522
+ onClick={(event: React.MouseEvent<HTMLElement>) => {
523
+ if (
524
+ event.button !== 0 ||
525
+ event.metaKey ||
526
+ event.ctrlKey ||
527
+ event.shiftKey ||
528
+ event.altKey
529
+ ) {
530
+ return
531
+ }
532
+
533
+ if (row.item.href) event.preventDefault()
534
+ activateItem()
535
+ }}
536
+ onKeyDown={(event: React.KeyboardEvent<HTMLElement>) =>
537
+ handleKeyDown(event, {
538
+ hasChildren,
539
+ isExpanded,
540
+ setIsExpanded: (value) =>
541
+ setExpanded(row.item, row.itemPath, value),
542
+ handleClick: activateItem,
543
+ index: rowIndex,
544
+ onNavigate: (direction, currentIndex) =>
545
+ navigateRows(currentIndex, direction),
546
+ })
547
+ }
548
+ >
549
+ {row.item.leftSlot &&
550
+ React.isValidElement(row.item.leftSlot) && (
551
+ <IconWrapper aria-hidden="true" $tokens={tokens}>
552
+ {React.cloneElement(
553
+ row.item.leftSlot as React.ReactElement<
554
+ React.SVGProps<SVGSVGElement> & {
555
+ size?: number
556
+ }
557
+ >,
558
+ {
559
+ color: isActive
560
+ ? tokens.section.itemList.item.color
561
+ .active
562
+ : tokens.section.itemList.item.color
563
+ .default,
564
+ }
565
+ )}
566
+ </IconWrapper>
567
+ )}
568
+ <Block flexGrow={1} minWidth={0} overflow="hidden">
569
+ <TruncatedTextWithTooltipV2
570
+ text={row.item.label}
571
+ side={TooltipV2Side.RIGHT}
572
+ />
573
+ </Block>
574
+ {row.item.rightSlot &&
575
+ React.isValidElement(row.item.rightSlot) && (
576
+ <Block aria-hidden="true">
577
+ {row.item.rightSlot}
578
+ </Block>
579
+ )}
580
+ {hasChildren && (
581
+ <ChevronWrapper
582
+ $tokens={tokens}
583
+ $isExpanded={isExpanded}
584
+ >
585
+ <ChevronDown
586
+ color={
587
+ tokens.section.itemList.item.chevron.color
588
+ }
589
+ />
590
+ </ChevronWrapper>
591
+ )}
592
+ </ItemButton>
593
+ </ItemRow>
594
+ )
595
+ }
596
+
597
+ const renderRow = (row: DirectoryFlatRow, rowIndex: number) =>
598
+ row.type === 'section'
599
+ ? renderSection(row, rowIndex)
600
+ : renderItem(row, rowIndex)
601
+
602
+ const content = useVirtualRows ? (
603
+ <Block
604
+ role="list"
605
+ width="100%"
606
+ style={{
607
+ height: `${virtualizer.getTotalSize()}px`,
608
+ position: 'relative',
609
+ flexShrink: 0,
610
+ }}
611
+ >
612
+ {fallbackRows.map((virtualRow) => {
613
+ const row = rows[virtualRow.index]
614
+ if (!row) return null
615
+ return (
616
+ <Block
617
+ key={virtualRow.key}
618
+ ref={virtualizer.measureElement}
619
+ role="listitem"
620
+ aria-setsize={rows.length}
621
+ aria-posinset={virtualRow.index + 1}
622
+ data-index={virtualRow.index}
623
+ style={{
624
+ position: 'absolute',
625
+ top: 0,
626
+ left: 0,
627
+ width: '100%',
628
+ transform: `translateY(${virtualRow.start}px)`,
629
+ }}
630
+ >
631
+ {renderRow(row, virtualRow.index)}
632
+ </Block>
633
+ )
634
+ })}
635
+ </Block>
636
+ ) : (
637
+ <Block role="list" width="100%">
638
+ {rows.map((row, index) => (
639
+ <Block
640
+ key={
641
+ row.type === 'section'
642
+ ? `section-${row.sectionIndex}`
643
+ : row.itemPath
644
+ }
645
+ role="listitem"
646
+ >
647
+ {renderRow(row, index)}
648
+ </Block>
649
+ ))}
650
+ </Block>
651
+ )
652
+
653
+ return (
654
+ <Block
655
+ as="nav"
656
+ ref={scrollRef}
657
+ width="100%"
658
+ height="100%"
659
+ flexGrow={1}
660
+ overflow={virtualization?.viewportRef ? 'visible' : 'auto'}
661
+ aria-label="Directory navigation"
662
+ paddingX={tokens.paddingX}
663
+ paddingY={tokens.paddingY}
664
+ >
665
+ {content}
666
+ </Block>
667
+ )
668
+ }
669
+
670
+ export default VirtualizedDirectory