rift-ds 1.0.0 → 1.1.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 (37) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -3
  3. package/components/AppLayout/AppLayout.css +13 -3
  4. package/components/AppLayout/AppLayout.d.ts +5 -1
  5. package/components/AppLayout/AppLayout.js +4 -0
  6. package/components/AppSidebar/AppSidebar.css +237 -0
  7. package/components/AppSidebar/AppSidebar.d.ts +32 -3
  8. package/components/AppSidebar/AppSidebar.js +187 -16
  9. package/components/BottomNav/BottomNav.css +417 -0
  10. package/components/BottomNav/BottomNav.d.ts +73 -0
  11. package/components/BottomNav/BottomNav.js +100 -0
  12. package/components/Chart/AreaChart.js +67 -56
  13. package/components/Chart/BarChart.js +67 -56
  14. package/components/Chart/ComboChart.js +93 -82
  15. package/components/Chart/LineChart.js +59 -48
  16. package/components/Chart/PieChart.js +56 -45
  17. package/components/Chart/RadarChart.js +60 -49
  18. package/components/Chart/RadialChart.js +70 -59
  19. package/components/Chart/ScatterChart.js +61 -50
  20. package/components/Chart/StackedBarChart.js +64 -53
  21. package/components/Chart/Treemap.js +34 -23
  22. package/components/Nav/Nav.d.ts +11 -3
  23. package/components/Nav/Nav.js +3 -2
  24. package/components/SidebarPanel/SidebarPanel.css +291 -0
  25. package/components/SidebarPanel/SidebarPanel.d.ts +45 -0
  26. package/components/SidebarPanel/SidebarPanel.js +166 -0
  27. package/components/Stepper/Stepper.d.ts +3 -1
  28. package/components/Stepper/Stepper.js +1 -1
  29. package/components/TopAppBar/TopAppBar.css +464 -0
  30. package/components/TopAppBar/TopAppBar.d.ts +80 -0
  31. package/components/TopAppBar/TopAppBar.js +138 -0
  32. package/components/registry.json +25 -1
  33. package/components/registry.json.d.ts +25 -1
  34. package/components/registry.json.js +1 -1
  35. package/index.d.ts +3 -0
  36. package/index.js +6 -0
  37. package/package.json +5 -1
@@ -5,6 +5,7 @@ import { useLayer } from "../../behaviors/useLayer.js";
5
5
  import { useFocusScope } from "../../behaviors/useFocusScope.js";
6
6
  import { useScrollLock } from "../../behaviors/useScrollLock.js";
7
7
  import { Avatar } from "../Avatar/Avatar.js";
8
+ import { SidebarPanel } from "../SidebarPanel/SidebarPanel.js";
8
9
  import "./AppSidebar.css";
9
10
  import "../../fonts/material-symbols.css";
10
11
  function DefaultLogo() {
@@ -30,6 +31,11 @@ const AppSidebar = ({
30
31
  profile,
31
32
  activeKey,
32
33
  activeSubKey,
34
+ activeTertiaryKey,
35
+ subNav = "accordion",
36
+ defaultPanelOpen = true,
37
+ panelOpen: controlledPanelOpen,
38
+ onPanelOpenChange,
33
39
  defaultExpanded = false,
34
40
  expanded: controlledExpanded,
35
41
  onExpandedChange,
@@ -83,7 +89,20 @@ const AppSidebar = ({
83
89
  query.addEventListener("change", onChange);
84
90
  return () => query.removeEventListener("change", onChange);
85
91
  }, [mobileOpen]);
86
- const [openKeys, setOpenKeys] = useState(/* @__PURE__ */ new Set());
92
+ const [openKeys, setOpenKeys] = useState(() => {
93
+ const initial = /* @__PURE__ */ new Set();
94
+ if (subNav !== "panel" && (activeSubKey || activeTertiaryKey)) {
95
+ for (const section of sections) {
96
+ for (const item of section.items) {
97
+ const holdsActive = item.children?.some(
98
+ (sub) => sub.key === activeSubKey || sub.children?.some((leaf) => leaf.key === activeTertiaryKey)
99
+ );
100
+ if (holdsActive) initial.add(item.key);
101
+ }
102
+ }
103
+ }
104
+ return initial;
105
+ });
87
106
  const toggleAccordion = useCallback((key) => {
88
107
  setOpenKeys((prev) => {
89
108
  const next = new Set(prev);
@@ -95,6 +114,85 @@ const AppSidebar = ({
95
114
  return next;
96
115
  });
97
116
  }, []);
117
+ const isPanelMode = subNav === "panel";
118
+ const [panelSourceKey, setPanelSourceKey] = useState(() => {
119
+ for (const section of sections) {
120
+ for (const item of section.items) {
121
+ if (isPanelMode) {
122
+ if (item.children?.length && (item.key === activeKey || item.children.some((c) => c.key === activeSubKey))) {
123
+ return item.key;
124
+ }
125
+ } else {
126
+ for (const sub of item.children ?? []) {
127
+ if (sub.children?.length && (sub.key === activeSubKey || sub.children.some((c) => c.key === activeTertiaryKey))) {
128
+ return sub.key;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ return void 0;
135
+ });
136
+ let panelSource;
137
+ for (const section of sections) {
138
+ for (const item of section.items) {
139
+ if (isPanelMode && item.key === panelSourceKey && item.children?.length) {
140
+ panelSource = { label: item.label, items: item.children };
141
+ }
142
+ if (!isPanelMode) {
143
+ for (const sub of item.children ?? []) {
144
+ if (sub.key === panelSourceKey && sub.children?.length) {
145
+ panelSource = {
146
+ label: sub.label,
147
+ parentLabel: item.label,
148
+ items: sub.children.map(({ children: _deeper, ...leaf }) => leaf)
149
+ };
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ const [internalPanelOpen, setInternalPanelOpen] = useState(defaultPanelOpen);
156
+ const isPanelOpen = controlledPanelOpen ?? internalPanelOpen;
157
+ const setPanelOpen = useCallback(
158
+ (next) => {
159
+ setInternalPanelOpen(next);
160
+ if (next !== isPanelOpen) onPanelOpenChange?.(next);
161
+ },
162
+ [isPanelOpen, onPanelOpenChange]
163
+ );
164
+ const panelVisible = Boolean(panelSource) && isPanelOpen;
165
+ const panelId = useId();
166
+ const [drilled, setDrilled] = useState(false);
167
+ const focusAfterDrill = useRef(null);
168
+ const openMobile = useCallback(() => {
169
+ setDrilled(Boolean(panelSource));
170
+ setMobileOpen(true);
171
+ }, [panelSource]);
172
+ useEffect(() => {
173
+ const target = focusAfterDrill.current;
174
+ focusAfterDrill.current = null;
175
+ if (!target || !drawerRef.current) return;
176
+ const selector = target === "in" ? ".ds-sidebar-panel__back" : '[data-panel-source="true"]';
177
+ drawerRef.current.querySelector(selector)?.focus();
178
+ }, [drilled]);
179
+ const selectPanelSource = useCallback(
180
+ (key) => {
181
+ if (mobileOpen) {
182
+ setPanelSourceKey(key);
183
+ focusAfterDrill.current = "in";
184
+ setDrilled(true);
185
+ return;
186
+ }
187
+ if (key === panelSourceKey && isPanelOpen) {
188
+ setPanelOpen(false);
189
+ } else {
190
+ setPanelSourceKey(key);
191
+ setPanelOpen(true);
192
+ }
193
+ },
194
+ [mobileOpen, panelSourceKey, isPanelOpen, setPanelOpen]
195
+ );
98
196
  const classes = [
99
197
  baseClass,
100
198
  // The drawer always shows the expanded layout: an icon rail inside
@@ -102,6 +200,7 @@ const AppSidebar = ({
102
200
  isExpanded || mobileOpen ? `${baseClass}--expanded` : "",
103
201
  floating ? `${baseClass}--floating` : "",
104
202
  mobileOpen ? `${baseClass}--mobile-open` : "",
203
+ isPanelMode ? `${baseClass}--panel-mode` : "",
105
204
  className
106
205
  ].filter(Boolean).join(" ");
107
206
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -113,7 +212,7 @@ const AppSidebar = ({
113
212
  "aria-label": "Open navigation",
114
213
  "aria-expanded": mobileOpen,
115
214
  "aria-controls": drawerId,
116
- onClick: () => setMobileOpen(true),
215
+ onClick: openMobile,
117
216
  children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "menu" })
118
217
  }
119
218
  ),
@@ -153,12 +252,28 @@ const AppSidebar = ({
153
252
  )
154
253
  ] }),
155
254
  topSlot && /* @__PURE__ */ jsx("div", { className: `${baseClass}__top-slot`, children: topSlot }),
156
- sections.map((section, si) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__nav`, children: [
255
+ mobileOpen && drilled && panelSource ? /* @__PURE__ */ jsx(
256
+ SidebarPanel,
257
+ {
258
+ className: `${baseClass}__drill`,
259
+ items: panelSource.items,
260
+ title: panelSource.label,
261
+ activeKey: activeTertiaryKey ?? activeSubKey,
262
+ onBack: () => {
263
+ focusAfterDrill.current = "out";
264
+ setDrilled(false);
265
+ },
266
+ backLabel: panelSource.parentLabel ?? "Menu"
267
+ },
268
+ panelSourceKey
269
+ ) : /* @__PURE__ */ jsx("div", { className: `${baseClass}__sections`, children: sections.map((section, si) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__nav`, children: [
157
270
  section.category && /* @__PURE__ */ jsx("div", { className: `${baseClass}__category`, children: section.category }),
158
271
  section.items.map((item) => {
159
272
  const hasChildren = item.children && item.children.length > 0;
160
273
  const isOpen = openKeys.has(item.key);
161
- const isActive = activeKey === item.key;
274
+ const feedsPanel = isPanelMode && hasChildren;
275
+ const isSource = feedsPanel && item.key === panelSourceKey;
276
+ const isActive = isPanelMode && panelVisible ? isSource : activeKey === item.key;
162
277
  const isLink = Boolean(item.href) && !hasChildren;
163
278
  const itemClasses = [
164
279
  `${baseClass}__btn`,
@@ -181,7 +296,7 @@ const AppSidebar = ({
181
296
  {
182
297
  className: [
183
298
  `${baseClass}__btn-chevron material-symbols-rounded`,
184
- isOpen ? `${baseClass}__btn-chevron--open` : ""
299
+ isOpen && !isPanelMode ? `${baseClass}__btn-chevron--open` : ""
185
300
  ].filter(Boolean).join(" "),
186
301
  children: "chevron_right"
187
302
  }
@@ -204,18 +319,27 @@ const AppSidebar = ({
204
319
  type: "button",
205
320
  className: itemClasses,
206
321
  onClick: () => {
207
- if (hasChildren && isExpanded) {
208
- toggleAccordion(item.key);
322
+ if (feedsPanel) {
323
+ selectPanelSource(item.key);
324
+ } else if (hasChildren) {
325
+ if (!isExpanded && !mobileOpen) {
326
+ toggleExpanded();
327
+ setOpenKeys((prev) => new Set(prev).add(item.key));
328
+ } else {
329
+ toggleAccordion(item.key);
330
+ }
209
331
  }
210
332
  item.onClick?.();
211
333
  },
212
- "aria-expanded": hasChildren ? isOpen : void 0,
213
- "aria-current": isActive ? "page" : void 0,
214
- title: !isExpanded ? item.label : void 0,
334
+ "aria-expanded": feedsPanel ? mobileOpen ? void 0 : isSource && panelVisible : hasChildren ? isOpen : void 0,
335
+ "aria-controls": feedsPanel && !mobileOpen ? panelId : void 0,
336
+ "aria-current": activeKey === item.key ? "page" : void 0,
337
+ "data-panel-source": isSource ? "true" : void 0,
338
+ title: !isExpanded && !isPanelMode ? item.label : void 0,
215
339
  children: itemContent
216
340
  }
217
341
  ),
218
- hasChildren && /* @__PURE__ */ jsx(
342
+ hasChildren && !isPanelMode && /* @__PURE__ */ jsx(
219
343
  "div",
220
344
  {
221
345
  className: [
@@ -223,15 +347,43 @@ const AppSidebar = ({
223
347
  isOpen && isExpanded ? `${baseClass}__sub-items--open` : ""
224
348
  ].filter(Boolean).join(" "),
225
349
  children: item.children.map((sub) => {
226
- const subActive = activeSubKey === sub.key;
350
+ const subHasChildren = Boolean(sub.children?.length);
351
+ const subIsSource = subHasChildren && sub.key === panelSourceKey;
352
+ const subActive = panelVisible && subHasChildren ? subIsSource : activeSubKey === sub.key;
227
353
  const subClasses = [
228
354
  `${baseClass}__sub-btn`,
229
355
  subActive ? `${baseClass}__sub-btn--active` : ""
230
356
  ].filter(Boolean).join(" ");
231
- const subLabel = /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label });
357
+ const subLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
358
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label }),
359
+ sub.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-badge`, children: sub.badge }),
360
+ subHasChildren && /* @__PURE__ */ jsx(
361
+ "span",
362
+ {
363
+ className: `${baseClass}__sub-chevron material-symbols-rounded`,
364
+ "aria-hidden": "true",
365
+ children: "chevron_right"
366
+ }
367
+ )
368
+ ] });
232
369
  return /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
233
370
  /* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
234
- sub.href ? /* @__PURE__ */ jsx(
371
+ subHasChildren ? /* @__PURE__ */ jsx(
372
+ "button",
373
+ {
374
+ type: "button",
375
+ className: subClasses,
376
+ onClick: () => {
377
+ selectPanelSource(sub.key);
378
+ sub.onClick?.();
379
+ },
380
+ "aria-expanded": mobileOpen ? void 0 : subIsSource && panelVisible,
381
+ "aria-controls": mobileOpen ? void 0 : panelId,
382
+ "aria-current": activeSubKey === sub.key ? "page" : void 0,
383
+ "data-panel-source": subIsSource ? "true" : void 0,
384
+ children: subLabel
385
+ }
386
+ ) : sub.href ? /* @__PURE__ */ jsx(
235
387
  "a",
236
388
  {
237
389
  className: subClasses,
@@ -256,7 +408,7 @@ const AppSidebar = ({
256
408
  )
257
409
  ] }, item.key);
258
410
  })
259
- ] }, si))
411
+ ] }, si)) })
260
412
  ] }),
261
413
  /* @__PURE__ */ jsxs("div", { className: `${baseClass}__bottom`, children: [
262
414
  footerSlot && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer-slot`, children: footerSlot }),
@@ -287,7 +439,26 @@ const AppSidebar = ({
287
439
  )
288
440
  ] })
289
441
  ] })
290
- ] })
442
+ ] }),
443
+ panelSource && !mobileOpen && /* @__PURE__ */ jsx(
444
+ SidebarPanel,
445
+ {
446
+ id: panelId,
447
+ className: [
448
+ `${baseClass}__panel`,
449
+ panelVisible ? `${baseClass}__panel--visible` : "",
450
+ isExpanded ? `${baseClass}__panel--beside-expanded` : "",
451
+ isPanelMode ? `${baseClass}__panel--panel-mode` : "",
452
+ floating ? `${baseClass}__panel--floating` : ""
453
+ ].filter(Boolean).join(" "),
454
+ items: panelSource.items,
455
+ title: panelSource.label,
456
+ activeKey: activeTertiaryKey ?? activeSubKey,
457
+ onCollapse: () => setPanelOpen(false),
458
+ inert: !panelVisible
459
+ },
460
+ panelSourceKey
461
+ )
291
462
  ] });
292
463
  };
293
464
  export {