rift-ds 1.0.1 → 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 +8 -2
  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
@@ -0,0 +1,417 @@
1
+ /* ============================================
2
+ BOTTOM NAV — the mobile tab bar
3
+ Three platforms share one markup:
4
+ default the Rift bar, fully on-token
5
+ ios the iOS 26 Liquid Glass capsule
6
+ android the Material 3 Expressive bar
7
+ Each slot is icon over label; the selection
8
+ is carried per platform (tint, lens, pill).
9
+ ============================================ */
10
+
11
+ /* ============================================
12
+ BASE (shared by every platform)
13
+ ============================================ */
14
+ .ds-bottom-nav {
15
+ display: flex;
16
+ align-items: stretch;
17
+ gap: var(--gap-200);
18
+ box-sizing: border-box;
19
+ width: 100%;
20
+ /* The home-indicator area on notched phones; zero elsewhere. */
21
+ padding-bottom: env(safe-area-inset-bottom, 0px);
22
+ }
23
+
24
+ .ds-bottom-nav--fixed {
25
+ position: fixed;
26
+ left: 0;
27
+ right: 0;
28
+ bottom: 0;
29
+ z-index: 80;
30
+ }
31
+
32
+ .ds-bottom-nav__bar {
33
+ position: relative;
34
+ flex: 1;
35
+ min-width: 0;
36
+ }
37
+
38
+ .ds-bottom-nav__list {
39
+ position: relative;
40
+ display: flex;
41
+ height: 100%;
42
+ margin: 0;
43
+ padding: 0;
44
+ list-style: none;
45
+ }
46
+
47
+ .ds-bottom-nav__slot {
48
+ display: flex;
49
+ flex: 1 1 0;
50
+ min-width: 0;
51
+ }
52
+
53
+ .ds-bottom-nav__item {
54
+ display: flex;
55
+ flex: 1;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ justify-content: center;
59
+ gap: var(--gap-100);
60
+ min-width: 0;
61
+ padding: 0;
62
+ border: none;
63
+ background: transparent;
64
+ color: var(--color-text-secondary);
65
+ font: inherit;
66
+ text-decoration: none;
67
+ cursor: pointer;
68
+ -webkit-tap-highlight-color: transparent;
69
+ transition: color var(--motion-duration-fast) var(--motion-ease-standard);
70
+ }
71
+
72
+ .ds-bottom-nav__item:focus-visible,
73
+ .ds-bottom-nav__search:focus-visible {
74
+ outline: 2px solid var(--color-action-primary-bg);
75
+ outline-offset: -2px;
76
+ }
77
+
78
+ .ds-bottom-nav__indicator {
79
+ position: relative;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ }
84
+
85
+ .ds-bottom-nav__icon {
86
+ --icon-size: var(--icon-size-600);
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ color: inherit;
91
+ /* The icon font's hooks, never a raw font-variation-settings, so the
92
+ dark theme's grade and the default weight survive (design.md's icon
93
+ style hooks). */
94
+ --material-symbols-fill: 0;
95
+ transition: font-variation-settings var(--motion-duration-fast) var(--motion-ease-standard);
96
+ }
97
+
98
+ /* Selected icons draw filled, the convention on all three platforms. */
99
+ .ds-bottom-nav__item--active .ds-bottom-nav__icon {
100
+ --material-symbols-fill: 1;
101
+ }
102
+
103
+ .ds-bottom-nav__label {
104
+ max-width: 100%;
105
+ overflow: hidden;
106
+ text-overflow: ellipsis;
107
+ white-space: nowrap;
108
+ font-family: var(--font-caption-family);
109
+ font-size: var(--font-caption-size);
110
+ font-weight: var(--font-caption-weight);
111
+ line-height: var(--font-caption-line-height);
112
+ letter-spacing: var(--font-caption-letter-spacing);
113
+ }
114
+
115
+ /* Count badge on the icon's top-end corner. */
116
+ .ds-bottom-nav__badge {
117
+ position: absolute;
118
+ top: calc(var(--padding-100) * -1);
119
+ left: calc(50% + var(--padding-150));
120
+ box-sizing: border-box;
121
+ min-width: 16px;
122
+ height: 16px;
123
+ padding: 0 var(--padding-100);
124
+ border-radius: var(--radius-pill);
125
+ background-color: var(--color-status-error-icon);
126
+ color: var(--color-bg-page-primary);
127
+ font-family: var(--font-caption-family);
128
+ font-size: var(--font-caption-size);
129
+ font-weight: var(--font-paragraph-sm-emphasis-weight);
130
+ line-height: 16px;
131
+ text-align: center;
132
+ white-space: nowrap;
133
+ }
134
+
135
+ .ds-bottom-nav__badge--dot {
136
+ top: 0;
137
+ left: calc(50% + var(--padding-150));
138
+ min-width: 0;
139
+ width: 6px;
140
+ height: 6px;
141
+ padding: 0;
142
+ }
143
+
144
+ .ds-bottom-nav__sr {
145
+ position: absolute;
146
+ width: 1px;
147
+ height: 1px;
148
+ overflow: hidden;
149
+ clip-path: inset(50%);
150
+ white-space: nowrap;
151
+ }
152
+
153
+ .ds-bottom-nav__search {
154
+ display: none;
155
+ }
156
+
157
+ /* Icons take the colour of the text around them, whatever a consumer
158
+ stylesheet sets on the icon font class globally. */
159
+ .ds-bottom-nav .material-symbols-rounded {
160
+ color: inherit;
161
+ }
162
+
163
+ /* ============================================
164
+ DEFAULT — the Rift bar
165
+ A full-width bar on the page ground with a
166
+ hairline above; the selected destination is
167
+ the teal selection of a mutually exclusive set.
168
+ ============================================ */
169
+ .ds-bottom-nav--default {
170
+ background-color: var(--color-bg-page-primary);
171
+ border-top: var(--border-025) solid var(--color-bg-container-border);
172
+ }
173
+
174
+ .ds-bottom-nav--default .ds-bottom-nav__bar {
175
+ height: 64px;
176
+ }
177
+
178
+ .ds-bottom-nav--default .ds-bottom-nav__item:hover {
179
+ color: var(--color-text-primary);
180
+ }
181
+
182
+ .ds-bottom-nav--default .ds-bottom-nav__item--active,
183
+ .ds-bottom-nav--default .ds-bottom-nav__item--active:hover {
184
+ color: var(--color-action-primary-bg);
185
+ }
186
+
187
+ .ds-bottom-nav--default .ds-bottom-nav__item--active .ds-bottom-nav__label {
188
+ font-weight: var(--font-paragraph-sm-emphasis-weight);
189
+ }
190
+
191
+ /* ============================================
192
+ iOS — Liquid Glass (iOS 26)
193
+ ds-allow(platform): iOS 26 tab-bar geometry and material — the 62pt
194
+ capsule floating 21pt in from the screen edges, 10pt medium SF labels
195
+ in the system face, the glass fill, blur and saturation, the white
196
+ specular rim, and the white badge numeral — restated from the platform
197
+ so the mock reads as system chrome. The tint stays the action token.
198
+ ============================================ */
199
+ .ds-bottom-nav--ios {
200
+ --ds-bottom-nav-ios-height: 62px;
201
+ --ds-bottom-nav-ios-inset: 21px;
202
+ /* The glass: a thin tint of the container over a strong blur, with the
203
+ colour behind pushed up so the material reads as lit, not grey. */
204
+ --ds-bottom-nav-glass: color-mix(in srgb, var(--color-bg-container-primary) 58%, transparent);
205
+ --ds-bottom-nav-glass-filter: blur(16px) saturate(180%);
206
+ --ds-bottom-nav-glass-rim:
207
+ inset 0 1px 0 0 rgb(255 255 255 / 0.55),
208
+ inset 0 0 0 0.5px rgb(255 255 255 / 0.28),
209
+ 0 8px 32px rgb(0 0 0 / 0.14),
210
+ 0 1px 3px rgb(0 0 0 / 0.08);
211
+ padding: 0 var(--ds-bottom-nav-ios-inset) calc(env(safe-area-inset-bottom, 0px) + var(--ds-bottom-nav-ios-inset) - 8px);
212
+ font-family: system-ui, -apple-system, 'SF Pro Text', sans-serif;
213
+ }
214
+
215
+ .ds-bottom-nav--ios .ds-bottom-nav__bar {
216
+ height: var(--ds-bottom-nav-ios-height);
217
+ padding: 4px;
218
+ box-sizing: border-box;
219
+ border-radius: 999px;
220
+ background-color: var(--ds-bottom-nav-glass);
221
+ box-shadow: var(--ds-bottom-nav-glass-rim);
222
+ -webkit-backdrop-filter: var(--ds-bottom-nav-glass-filter);
223
+ backdrop-filter: var(--ds-bottom-nav-glass-filter);
224
+ transition: width var(--motion-duration-slow) var(--motion-ease-spring);
225
+ }
226
+
227
+ /* The lens: a lighter glass pill behind the selected slot, sliding
228
+ between slots on the platform's springy curve. */
229
+ .ds-bottom-nav--ios .ds-bottom-nav__lens {
230
+ position: absolute;
231
+ top: 4px;
232
+ bottom: 4px;
233
+ left: 4px;
234
+ width: calc((100% - 8px) / var(--ds-bottom-nav-count));
235
+ border-radius: 999px;
236
+ background-color: color-mix(in srgb, var(--color-text-primary) 9%, transparent);
237
+ box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.35);
238
+ transform: translateX(calc(100% * var(--ds-bottom-nav-index)));
239
+ transition:
240
+ transform var(--motion-duration-slow) var(--motion-ease-spring),
241
+ width var(--motion-duration-slow) var(--motion-ease-spring);
242
+ }
243
+
244
+ .ds-bottom-nav--ios .ds-bottom-nav__item {
245
+ gap: 1px;
246
+ color: var(--color-text-primary);
247
+ }
248
+
249
+ .ds-bottom-nav--ios .ds-bottom-nav__item--active {
250
+ color: var(--color-action-primary-bg);
251
+ }
252
+
253
+ .ds-bottom-nav--ios .ds-bottom-nav__icon {
254
+ --icon-size: var(--icon-size-600);
255
+ }
256
+
257
+ /* A medium stroke, as SF Symbols draw in a tab bar. */
258
+ .ds-bottom-nav--ios .ds-bottom-nav__icon {
259
+ --material-symbols-weight: 500;
260
+ }
261
+
262
+ .ds-bottom-nav--ios .ds-bottom-nav__label {
263
+ font-family: inherit;
264
+ font-size: 10px;
265
+ font-weight: 500;
266
+ line-height: 12px;
267
+ letter-spacing: 0.01em;
268
+ }
269
+
270
+ .ds-bottom-nav--ios .ds-bottom-nav__badge {
271
+ /* systemRed, light and dark alike on a badge. */
272
+ background-color: #ff3b30;
273
+ color: #fff;
274
+ font-family: inherit;
275
+ font-size: 11px;
276
+ }
277
+
278
+ /* The search destination: its own glass circle, the bar's height. */
279
+ .ds-bottom-nav--ios .ds-bottom-nav__search {
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ flex-shrink: 0;
284
+ width: var(--ds-bottom-nav-ios-height);
285
+ height: var(--ds-bottom-nav-ios-height);
286
+ padding: 0;
287
+ border: none;
288
+ border-radius: 999px;
289
+ background-color: var(--ds-bottom-nav-glass);
290
+ box-shadow: var(--ds-bottom-nav-glass-rim);
291
+ -webkit-backdrop-filter: var(--ds-bottom-nav-glass-filter);
292
+ backdrop-filter: var(--ds-bottom-nav-glass-filter);
293
+ color: var(--color-text-primary);
294
+ text-decoration: none;
295
+ cursor: pointer;
296
+ }
297
+
298
+ /* Minimised: the capsule closes to a circle holding only the selection;
299
+ the other slots fold to nothing and the lens fills what is left. */
300
+ .ds-bottom-nav--ios .ds-bottom-nav__slot {
301
+ transition:
302
+ flex-grow var(--motion-duration-slow) var(--motion-ease-spring),
303
+ opacity var(--motion-duration-fast) var(--motion-ease-standard);
304
+ }
305
+
306
+ .ds-bottom-nav--ios.ds-bottom-nav--minimized .ds-bottom-nav__bar {
307
+ flex: 0 0 var(--ds-bottom-nav-ios-height);
308
+ }
309
+
310
+ .ds-bottom-nav--ios.ds-bottom-nav--minimized .ds-bottom-nav__slot:not(:has(.ds-bottom-nav__item--active)) {
311
+ flex-grow: 0;
312
+ opacity: 0;
313
+ overflow: hidden;
314
+ }
315
+
316
+ .ds-bottom-nav--ios.ds-bottom-nav--minimized .ds-bottom-nav__lens {
317
+ width: calc(100% - 8px);
318
+ transform: none;
319
+ }
320
+
321
+ /* The label leaves the picture but stays the button's accessible name. */
322
+ .ds-bottom-nav--ios.ds-bottom-nav--minimized .ds-bottom-nav__label {
323
+ position: absolute;
324
+ width: 1px;
325
+ height: 1px;
326
+ overflow: hidden;
327
+ clip-path: inset(50%);
328
+ }
329
+
330
+ /* ============================================
331
+ ANDROID — Material 3 Expressive
332
+ ds-allow(platform): the M3 Expressive short navigation bar — a 64dp
333
+ bar on the surface-container role, a 56 x 32dp pill indicator behind
334
+ the selected icon, 12/16 medium labels tracked 0.5, the platform face
335
+ (Google Sans Flex, then Roboto), and the white badge numeral — restated
336
+ from the Material token set so the mock reads as system chrome.
337
+ ============================================ */
338
+ .ds-bottom-nav--android {
339
+ background-color: var(--color-bg-container-secondary);
340
+ font-family: 'Google Sans Flex', Roboto, system-ui, sans-serif;
341
+ }
342
+
343
+ .ds-bottom-nav--android .ds-bottom-nav__bar {
344
+ height: 64px;
345
+ }
346
+
347
+ .ds-bottom-nav--android .ds-bottom-nav__item {
348
+ justify-content: flex-start;
349
+ gap: 4px;
350
+ padding-top: 6px;
351
+ color: var(--color-text-secondary);
352
+ }
353
+
354
+ .ds-bottom-nav--android .ds-bottom-nav__item--active {
355
+ color: var(--color-text-primary);
356
+ }
357
+
358
+ .ds-bottom-nav--android .ds-bottom-nav__indicator {
359
+ width: 56px;
360
+ height: 32px;
361
+ flex-shrink: 0;
362
+ }
363
+
364
+ /* The pill grows out from the icon's centre on selection, M3's own
365
+ indicator motion, on the spring curve. */
366
+ .ds-bottom-nav--android .ds-bottom-nav__indicator::before {
367
+ content: '';
368
+ position: absolute;
369
+ inset: 0;
370
+ border-radius: 999px;
371
+ background-color: color-mix(in srgb, var(--color-action-primary-bg) 22%, var(--color-bg-container-secondary));
372
+ opacity: 0;
373
+ transform: scaleX(0.4);
374
+ transition:
375
+ opacity var(--motion-duration-fast) var(--motion-ease-standard),
376
+ transform var(--motion-duration-slow) var(--motion-ease-spring);
377
+ }
378
+
379
+ .ds-bottom-nav--android .ds-bottom-nav__item--active .ds-bottom-nav__indicator::before {
380
+ opacity: 1;
381
+ transform: none;
382
+ }
383
+
384
+ /* A pressed or hovered slot gets the state layer, the indicator's shape
385
+ at low strength. */
386
+ .ds-bottom-nav--android .ds-bottom-nav__item:hover:not(.ds-bottom-nav__item--active) .ds-bottom-nav__indicator::before {
387
+ opacity: 0.35;
388
+ transform: none;
389
+ background-color: var(--color-bg-container-tertiary);
390
+ }
391
+
392
+ .ds-bottom-nav--android .ds-bottom-nav__icon {
393
+ position: relative;
394
+ }
395
+
396
+ .ds-bottom-nav--android .ds-bottom-nav__label {
397
+ font-family: inherit;
398
+ font-size: 12px;
399
+ font-weight: 500;
400
+ line-height: 16px;
401
+ letter-spacing: 0.5px;
402
+ }
403
+
404
+ .ds-bottom-nav--android .ds-bottom-nav__badge {
405
+ top: 2px;
406
+ left: calc(50% + 4px);
407
+ /* The Material error role's baseline red. */
408
+ background-color: #b3261e;
409
+ color: #fff;
410
+ font-family: inherit;
411
+ font-size: 11px;
412
+ }
413
+
414
+ .ds-bottom-nav--android .ds-bottom-nav__badge--dot {
415
+ top: 4px;
416
+ left: calc(50% + 6px);
417
+ }
@@ -0,0 +1,73 @@
1
+ import { default as React } from 'react';
2
+ export interface BottomNavItem {
3
+ /** Unique key for this destination */
4
+ key: string;
5
+ /** Display label, always shown under the icon */
6
+ label: string;
7
+ /** Icon — Material Symbol name (string, drawn filled when selected) or custom element (ReactNode) */
8
+ icon: string | React.ReactNode;
9
+ /** Renders the destination as a real link to this URL instead of a button */
10
+ href?: string;
11
+ /** Click handler — also fires on a link destination, so a consumer can route client-side */
12
+ onClick?: () => void;
13
+ /** Count on the icon's corner; `true` shows a dot with no number */
14
+ badge?: number | string | true;
15
+ }
16
+ export interface BottomNavSearch {
17
+ /** The button's accessible name on iOS, and its visible tab label on the other platforms */
18
+ label: string;
19
+ /**
20
+ * Icon — Material Symbol name (string) or custom element (ReactNode).
21
+ * Defaults to `search`; set it when the set-apart slot launches something
22
+ * else, such as an assistant.
23
+ */
24
+ icon?: string | React.ReactNode;
25
+ /** Click handler */
26
+ onClick?: () => void;
27
+ /** Renders the button as a link to this URL instead */
28
+ href?: string;
29
+ }
30
+ type BottomNavOwnProps = {
31
+ /** Destinations, three to five of them */
32
+ items: BottomNavItem[];
33
+ /** Key of the destination for the current page */
34
+ activeKey?: string;
35
+ /** Called with a destination's key when it is pressed */
36
+ onValueChange?: (key: string) => void;
37
+ /**
38
+ * Which platform's tab bar to draw. `default` is the Rift bar; `ios` is the
39
+ * iOS 26 Liquid Glass capsule floating over content with a sliding lens
40
+ * behind the selection; `android` is the Material 3 Expressive navigation
41
+ * bar with a pill indicator behind the selected icon.
42
+ */
43
+ platform?: 'default' | 'ios' | 'android';
44
+ /**
45
+ * A destination set apart from the rest, search unless its `icon` says
46
+ * otherwise. On iOS it is its own glass circle beside the bar; elsewhere it
47
+ * joins the bar as the last destination.
48
+ */
49
+ search?: BottomNavSearch;
50
+ /**
51
+ * iOS only: shrinks the bar to the selected destination alone, the way the
52
+ * system bar minimises while the page scrolls down. Ignored on other platforms.
53
+ */
54
+ minimized?: boolean;
55
+ /** Pins the bar to the bottom of the viewport, above the home-indicator safe area */
56
+ fixed?: boolean;
57
+ /**
58
+ * Accessible name of the navigation landmark. Defaults to "Tabs", distinct
59
+ * from Nav's "Main", so a screen carrying both announces two different navs
60
+ */
61
+ 'aria-label'?: string;
62
+ /** Additional CSS classes */
63
+ className?: string;
64
+ };
65
+ export interface BottomNavProps extends BottomNavOwnProps, Omit<React.ComponentPropsWithoutRef<'nav'>, keyof BottomNavOwnProps> {
66
+ }
67
+ /**
68
+ * BottomNav — the tab bar of a mobile app: three to five top-level
69
+ * destinations along the bottom of the screen, in the Rift style or as a
70
+ * close match for iOS or Android.
71
+ */
72
+ export declare const BottomNav: React.ForwardRefExoticComponent<BottomNavProps & React.RefAttributes<HTMLElement>>;
73
+ export {};
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import "./BottomNav.css";
5
+ import "../../fonts/material-symbols.css";
6
+ const BottomNav = React.forwardRef(
7
+ ({
8
+ items,
9
+ activeKey,
10
+ onValueChange,
11
+ platform = "default",
12
+ search,
13
+ minimized = false,
14
+ fixed = false,
15
+ "aria-label": ariaLabel = "Tabs",
16
+ className = "",
17
+ style,
18
+ ...rest
19
+ }, ref) => {
20
+ const baseClass = "ds-bottom-nav";
21
+ const isIos = platform === "ios";
22
+ const destinations = search && !isIos ? [...items, { key: "__search", label: search.label, icon: search.icon ?? "search", href: search.href, onClick: search.onClick }] : items;
23
+ const activeIndex = destinations.findIndex((item) => item.key === activeKey);
24
+ const isMinimized = isIos && minimized && activeIndex >= 0;
25
+ const classes = [
26
+ baseClass,
27
+ `${baseClass}--${platform}`,
28
+ fixed ? `${baseClass}--fixed` : "",
29
+ isMinimized ? `${baseClass}--minimized` : "",
30
+ className
31
+ ].filter(Boolean).join(" ");
32
+ const lensStyle = {
33
+ ...style,
34
+ "--ds-bottom-nav-count": destinations.length,
35
+ "--ds-bottom-nav-index": Math.max(activeIndex, 0)
36
+ };
37
+ const renderIcon = (icon) => typeof icon === "string" ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: icon });
38
+ const renderBadge = (badge) => {
39
+ if (badge === void 0) return null;
40
+ return badge === true ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__badge ${baseClass}__badge--dot`, "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__badge`, "aria-hidden": "true", children: badge });
41
+ };
42
+ return /* @__PURE__ */ jsxs("nav", { ...rest, ref, className: classes, style: lensStyle, "aria-label": ariaLabel, children: [
43
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__bar`, children: [
44
+ isIos && activeIndex >= 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__lens`, "aria-hidden": "true" }),
45
+ /* @__PURE__ */ jsx("ul", { className: `${baseClass}__list`, children: destinations.map((item) => {
46
+ const isActive = item.key === activeKey;
47
+ const itemClasses = [
48
+ `${baseClass}__item`,
49
+ isActive ? `${baseClass}__item--active` : ""
50
+ ].filter(Boolean).join(" ");
51
+ const badgeText = item.badge === void 0 ? "" : item.badge === true ? ", new" : `, ${item.badge} new`;
52
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
53
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__indicator`, children: [
54
+ renderIcon(item.icon),
55
+ renderBadge(item.badge)
56
+ ] }),
57
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: item.label }),
58
+ badgeText && /* @__PURE__ */ jsx("span", { className: `${baseClass}__sr`, children: badgeText })
59
+ ] });
60
+ const handleClick = () => {
61
+ item.onClick?.();
62
+ if (item.key !== "__search") onValueChange?.(item.key);
63
+ };
64
+ return /* @__PURE__ */ jsx(
65
+ "li",
66
+ {
67
+ className: `${baseClass}__slot`,
68
+ inert: isMinimized && !isActive,
69
+ children: item.href ? /* @__PURE__ */ jsx(
70
+ "a",
71
+ {
72
+ className: itemClasses,
73
+ href: item.href,
74
+ onClick: handleClick,
75
+ "aria-current": isActive ? "page" : void 0,
76
+ children: content
77
+ }
78
+ ) : /* @__PURE__ */ jsx(
79
+ "button",
80
+ {
81
+ type: "button",
82
+ className: itemClasses,
83
+ onClick: handleClick,
84
+ "aria-current": isActive ? "page" : void 0,
85
+ children: content
86
+ }
87
+ )
88
+ },
89
+ item.key
90
+ );
91
+ }) })
92
+ ] }),
93
+ isIos && search && (search.href ? /* @__PURE__ */ jsx("a", { className: `${baseClass}__search`, href: search.href, onClick: search.onClick, "aria-label": search.label, children: renderIcon(search.icon ?? "search") }) : /* @__PURE__ */ jsx("button", { type: "button", className: `${baseClass}__search`, onClick: search.onClick, "aria-label": search.label, children: renderIcon(search.icon ?? "search") }))
94
+ ] });
95
+ }
96
+ );
97
+ BottomNav.displayName = "BottomNav";
98
+ export {
99
+ BottomNav
100
+ };