@happyvertical/smrt-svelte 0.51.39 → 0.53.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/AGENTS.md +3 -0
  2. package/README.md +214 -1
  3. package/agents/components.md +8 -1
  4. package/agents/workspace.md +24 -0
  5. package/dist/Provider.svelte +19 -15
  6. package/dist/Provider.svelte.d.ts +2 -2
  7. package/dist/Provider.svelte.d.ts.map +1 -1
  8. package/dist/__tests__/provider-mode-harness.svelte +14 -0
  9. package/dist/__tests__/provider-mode-harness.svelte.d.ts +11 -0
  10. package/dist/__tests__/provider-mode-harness.svelte.d.ts.map +1 -0
  11. package/dist/__tests__/provider-mode-probe.svelte +13 -0
  12. package/dist/__tests__/provider-mode-probe.svelte.d.ts +8 -0
  13. package/dist/__tests__/provider-mode-probe.svelte.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/browser-speech.d.ts +11 -0
  15. package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -1
  16. package/dist/browser-ai/adapters/stt/browser-speech.js +20 -4
  17. package/dist/browser-ai/adapters/stt/browser-speech.test.js +113 -0
  18. package/dist/browser-ai/adapters/stt/dictation-source.d.ts +3 -0
  19. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/dictation-source.js +38 -0
  21. package/dist/browser-ai/adapters/stt/dictation-source.test.js +52 -0
  22. package/dist/browser-ai/adapters/stt/index.d.ts +2 -1
  23. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  24. package/dist/browser-ai/adapters/stt/index.js +2 -1
  25. package/dist/components/forms/FileUpload.svelte +120 -16
  26. package/dist/components/forms/FileUpload.svelte.d.ts +7 -1
  27. package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -1
  28. package/dist/components/forms/Form.svelte +118 -8
  29. package/dist/components/forms/Form.svelte.d.ts +18 -2
  30. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  31. package/dist/components/forms/FormScope.svelte +202 -0
  32. package/dist/components/forms/FormScope.svelte.d.ts +22 -0
  33. package/dist/components/forms/FormScope.svelte.d.ts.map +1 -0
  34. package/dist/components/forms/__tests__/FileUpload.native-form.test.js +249 -0
  35. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte +24 -0
  36. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts +10 -0
  37. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts.map +1 -0
  38. package/dist/components/forms/__tests__/Form.ui-controls.test.js +119 -0
  39. package/dist/components/forms/__tests__/Form.webmcp.test.js +5 -3
  40. package/dist/components/forms/__tests__/FormScope.test.js +39 -0
  41. package/dist/components/forms/__tests__/form-scope.fixture.svelte +30 -0
  42. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts +10 -0
  43. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts.map +1 -0
  44. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte +84 -0
  45. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts +14 -0
  46. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts.map +1 -0
  47. package/dist/components/forms/index.d.ts +1 -0
  48. package/dist/components/forms/index.d.ts.map +1 -1
  49. package/dist/components/forms/index.js +1 -0
  50. package/dist/components/workspace/README.md +11 -0
  51. package/dist/components/workspace/__tests__/AdminShell.responsive.test.js +597 -0
  52. package/dist/components/workspace/__tests__/AdminShell.test.js +257 -1
  53. package/dist/components/workspace/__tests__/TenantNav.test.js +115 -0
  54. package/dist/components/workspace/__tests__/WorkspaceAccountMenu.test.js +6 -0
  55. package/dist/components/workspace/__tests__/admin-shell-header-overflow.test.js +25 -0
  56. package/dist/components/workspace/__tests__/admin-shell-mobile.test.js +217 -0
  57. package/dist/components/workspace/__tests__/admin-shell-page-contracts.test.js +30 -0
  58. package/dist/components/workspace/__tests__/admin-shell-responsive-state.test.js +184 -0
  59. package/dist/components/workspace/__tests__/admin-shell-state.test.js +25 -0
  60. package/dist/components/workspace/__tests__/admin-shell-theme-hooks.test.js +36 -0
  61. package/dist/components/workspace/__tests__/shell-chrome-components.test.js +108 -0
  62. package/dist/components/workspace/admin-shell/AdminShell.svelte +1099 -59
  63. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +44 -1
  64. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  65. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte +170 -0
  66. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts +11 -0
  67. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts.map +1 -0
  68. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte +93 -0
  69. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts +11 -0
  70. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts.map +1 -0
  71. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte +88 -0
  72. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts +14 -0
  73. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts.map +1 -0
  74. package/dist/components/workspace/admin-shell/ShellTitle.svelte +42 -0
  75. package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts +11 -0
  76. package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts.map +1 -0
  77. package/dist/components/workspace/admin-shell/TenantNav.svelte +116 -13
  78. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -2
  79. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  80. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +16 -2
  81. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +2 -0
  82. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
  83. package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts +37 -0
  84. package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts.map +1 -0
  85. package/dist/components/workspace/admin-shell/mobile-shell-dom.js +150 -0
  86. package/dist/components/workspace/admin-shell/mobile-shell.d.ts +191 -0
  87. package/dist/components/workspace/admin-shell/mobile-shell.d.ts.map +1 -0
  88. package/dist/components/workspace/admin-shell/mobile-shell.js +165 -0
  89. package/dist/components/workspace/admin-shell/settings.d.ts +27 -2
  90. package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
  91. package/dist/components/workspace/admin-shell/settings.js +84 -2
  92. package/dist/components/workspace/admin-shell/state.svelte.d.ts +57 -1
  93. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  94. package/dist/components/workspace/admin-shell/state.svelte.js +156 -6
  95. package/dist/components/workspace/admin-shell/types.d.ts +115 -1
  96. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  97. package/dist/components/workspace/admin-shell/types.js +13 -0
  98. package/dist/components/workspace/index.d.ts +11 -4
  99. package/dist/components/workspace/index.d.ts.map +1 -1
  100. package/dist/components/workspace/index.js +12 -3
  101. package/dist/i18n/strings.forms.d.ts +3 -0
  102. package/dist/i18n/strings.forms.d.ts.map +1 -1
  103. package/dist/i18n/strings.forms.js +3 -0
  104. package/dist/i18n/strings.workspace.d.ts +14 -0
  105. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  106. package/dist/i18n/strings.workspace.js +14 -0
  107. package/dist/index.d.ts +1 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +1 -1
  110. package/dist/provider-mode.test.js +86 -0
  111. package/dist/state/app-state.svelte.d.ts.map +1 -1
  112. package/dist/state/app-state.svelte.js +8 -8
  113. package/dist/web/__tests__/assistant-dock.integration.svelte.test.js +2 -2
  114. package/dist/web/__tests__/list-sort-surface.fixture.svelte +41 -0
  115. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts +15 -0
  116. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts.map +1 -0
  117. package/dist/web/__tests__/list-sort-surface.svelte.test.js +59 -0
  118. package/dist/web/__tests__/surfaces.child.fixture.svelte +69 -0
  119. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts +16 -0
  120. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts.map +1 -0
  121. package/dist/web/__tests__/surfaces.fixture.svelte +26 -0
  122. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts +18 -0
  123. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts.map +1 -0
  124. package/dist/web/__tests__/surfaces.svelte.test.js +140 -0
  125. package/dist/web/__tests__/webmcp-ui.test.js +18 -0
  126. package/dist/web/index.d.ts +4 -1
  127. package/dist/web/index.d.ts.map +1 -1
  128. package/dist/web/index.js +4 -1
  129. package/dist/web/link-surface.svelte.d.ts +14 -0
  130. package/dist/web/link-surface.svelte.d.ts.map +1 -0
  131. package/dist/web/link-surface.svelte.js +49 -0
  132. package/dist/web/list-surface.svelte.d.ts +80 -0
  133. package/dist/web/list-surface.svelte.d.ts.map +1 -0
  134. package/dist/web/list-surface.svelte.js +309 -0
  135. package/dist/web/step-surface.svelte.d.ts +17 -0
  136. package/dist/web/step-surface.svelte.d.ts.map +1 -0
  137. package/dist/web/step-surface.svelte.js +56 -0
  138. package/dist/web/webmcp-ui-context.d.ts +7 -0
  139. package/dist/web/webmcp-ui-context.d.ts.map +1 -1
  140. package/dist/web/webmcp-ui-context.js +10 -0
  141. package/dist/web/webmcp-ui.d.ts.map +1 -1
  142. package/dist/web/webmcp-ui.js +16 -3
  143. package/package.json +17 -17
@@ -2,11 +2,17 @@
2
2
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
3
  import type { Component } from 'svelte';
4
4
  import { M } from '../../../i18n/strings.workspace.js';
5
- import type { ShellNavItem } from './types.js';
5
+ import type { ShellNavGroup, ShellNavItem } from './types.js';
6
6
 
7
7
  interface Props {
8
8
  /** Navigation items with href, label, icon, and optional children. */
9
- items: ShellNavItem[];
9
+ items?: ShellNavItem[];
10
+ /** Labelled, collapsible navigation groups, rendered after flat items. */
11
+ groups?: ShellNavGroup[];
12
+ /** Accessible name for this navigation landmark. */
13
+ 'aria-label'?: string;
14
+ /** Target density; omit to inherit the theme density. */
15
+ density?: 'comfortable' | 'touch';
10
16
  /** Current URL to highlight the active navigation link. */
11
17
  currentHref?: string;
12
18
  /** Component used to render icon names as SVG or custom icons. */
@@ -18,7 +24,10 @@ interface Props {
18
24
  }
19
25
 
20
26
  let {
21
- items,
27
+ items = [],
28
+ groups = [],
29
+ 'aria-label': ariaLabel,
30
+ density,
22
31
  currentHref = '',
23
32
  iconComponent: IconComponent,
24
33
  collapsed = false,
@@ -35,17 +44,33 @@ function isVisibleActive(item: ShellNavItem): boolean {
35
44
  return collapsed && !!item.children?.some((child) => isActive(child));
36
45
  }
37
46
 
47
+ /** Accessible label of an item's attention dot, or null when it has none. */
48
+ function attentionLabel(item: ShellNavItem): string | null {
49
+ if (!item.attention) return null;
50
+ return typeof item.attention === 'string' && item.attention.trim()
51
+ ? item.attention.trim()
52
+ : t(M['ui.tenant_nav.needs_attention']);
53
+ }
54
+
38
55
  function fallbackIcon(label: string): string {
39
56
  return label.trim().charAt(0).toLocaleUpperCase() || '?';
40
57
  }
41
58
  </script>
42
59
 
43
- <nav
44
- class="smrt-tenant-nav"
45
- class:smrt-tenant-nav--collapsed={collapsed}
46
- aria-label={t(M['ui.tenant_nav.tenant_navigation'])}
47
- >
48
- {#each items as item (item.href)}
60
+ {#snippet attention(item: ShellNavItem)}
61
+ {@const label = attentionLabel(item)}
62
+ {#if label}
63
+ <span
64
+ class="smrt-tenant-nav__attention"
65
+ data-attention
66
+ aria-hidden="true"
67
+ ></span>
68
+ <span class="smrt-tenant-nav__sr-only">({label})</span>
69
+ {/if}
70
+ {/snippet}
71
+
72
+ {#snippet navItems(entries: ShellNavItem[])}
73
+ {#each entries as item (item.href)}
49
74
  <div class="smrt-tenant-nav__section">
50
75
  <a
51
76
  href={item.href}
@@ -78,6 +103,7 @@ function fallbackIcon(label: string): string {
78
103
  <small>{item.badge}</small>
79
104
  {/if}
80
105
  {/if}
106
+ {@render attention(item)}
81
107
  </a>
82
108
  {#if item.children?.length && !collapsed}
83
109
  <div class="smrt-tenant-nav__children">
@@ -100,25 +126,82 @@ function fallbackIcon(label: string): string {
100
126
  {#if child.badge !== null && child.badge !== undefined}
101
127
  <small>{child.badge}</small>
102
128
  {/if}
129
+ {@render attention(child)}
103
130
  </a>
104
131
  {/each}
105
132
  </div>
106
133
  {/if}
107
134
  </div>
108
135
  {/each}
136
+ {/snippet}
137
+
138
+ <nav
139
+ class="smrt-tenant-nav"
140
+ data-density={density}
141
+ class:smrt-tenant-nav--collapsed={collapsed}
142
+ aria-label={ariaLabel || t(M['ui.tenant_nav.tenant_navigation'])}
143
+ >
144
+
145
+ {@render navItems(items)}
146
+ {#each groups as group}
147
+ <details class="smrt-tenant-nav__group" aria-label={group.heading} open>
148
+ <summary class="smrt-tenant-nav__heading">
149
+ {#if collapsed}
150
+ <span aria-hidden="true">{fallbackIcon(group.heading)}</span>
151
+ <span class="smrt-tenant-nav__sr-only">{group.heading}</span>
152
+ {:else}
153
+ <span>{group.heading}</span>
154
+ {/if}
155
+ </summary>
156
+ <div class="smrt-tenant-nav__group-items">{@render navItems(group.items)}</div>
157
+ </details>
158
+ {/each}
109
159
  </nav>
110
160
 
111
161
  <style>
112
162
  .smrt-tenant-nav,
113
163
  .smrt-tenant-nav__section,
114
- .smrt-tenant-nav__children {
164
+ .smrt-tenant-nav__children,
165
+ .smrt-tenant-nav__group-items {
115
166
  display: grid;
116
167
  gap: var(--smrt-spacing-1);
117
168
  }
118
169
 
170
+ .smrt-tenant-nav__heading {
171
+ box-sizing: border-box;
172
+ min-block-size: var(--smrt-control-target-min, 0px);
173
+ padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
174
+ color: var(--smrt-color-on-surface-variant);
175
+ font-size: var(--smrt-typography-label-large-size);
176
+ font-weight: var(--smrt-typography-weight-semibold);
177
+ border-radius: var(--smrt-radius-medium);
178
+ cursor: pointer;
179
+ }
180
+ .smrt-tenant-nav__heading:focus-visible {
181
+ outline: 2px solid var(--smrt-color-primary);
182
+ outline-offset: 2px;
183
+ }
184
+ .smrt-tenant-nav__group-items { margin-block-start: var(--smrt-spacing-1); }
185
+ .smrt-tenant-nav--collapsed .smrt-tenant-nav__heading {
186
+ display: grid;
187
+ place-items: center;
188
+ list-style: none;
189
+ inline-size: max(2.25rem, var(--smrt-control-target-min, 0px));
190
+ block-size: max(2.25rem, var(--smrt-control-target-min, 0px));
191
+ padding: 0;
192
+ }
193
+ .smrt-tenant-nav--collapsed .smrt-tenant-nav__heading::-webkit-details-marker { display: none; }
194
+
195
+ .smrt-tenant-nav[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
196
+ .smrt-tenant-nav[data-density='comfortable'] { --smrt-control-target-min: 0px; }
119
197
  .smrt-tenant-nav a {
198
+ box-sizing: border-box;
199
+ min-block-size: var(--smrt-control-target-min, 0px);
120
200
  display: grid;
121
- grid-template-columns: auto minmax(0, 1fr) auto;
201
+ /* Icon, label, then one auto column per trailing mark (badge, dot). */
202
+ grid-template-columns: auto minmax(0, 1fr);
203
+ grid-auto-flow: column;
204
+ grid-auto-columns: auto;
122
205
  align-items: center;
123
206
  gap: var(--smrt-spacing-2);
124
207
  min-inline-size: 0;
@@ -136,8 +219,8 @@ function fallbackIcon(label: string): string {
136
219
  .smrt-tenant-nav--collapsed a {
137
220
  grid-template-columns: minmax(0, 1fr);
138
221
  place-items: center;
139
- inline-size: 2.25rem;
140
- block-size: 2.25rem;
222
+ inline-size: max(2.25rem, var(--smrt-control-target-min, 0px));
223
+ block-size: max(2.25rem, var(--smrt-control-target-min, 0px));
141
224
  padding: 0;
142
225
  }
143
226
 
@@ -178,6 +261,26 @@ function fallbackIcon(label: string): string {
178
261
  padding-inline-start: var(--smrt-spacing-4);
179
262
  }
180
263
 
264
+ .smrt-tenant-nav a {
265
+ position: relative;
266
+ }
267
+
268
+ .smrt-tenant-nav__attention {
269
+ inline-size: 0.5rem;
270
+ block-size: 0.5rem;
271
+ border-radius: var(--smrt-radius-full);
272
+ background: var(--smrt-color-error);
273
+ justify-self: end;
274
+ }
275
+
276
+ /* Collapsed: the dot sits on the icon's corner. */
277
+ .smrt-tenant-nav--collapsed .smrt-tenant-nav__attention {
278
+ position: absolute;
279
+ inset-block-start: 0.125rem;
280
+ inset-inline-end: 0.125rem;
281
+ box-shadow: 0 0 0 2px var(--smrt-color-surface);
282
+ }
283
+
181
284
  .smrt-tenant-nav__sr-only {
182
285
  position: absolute;
183
286
  inline-size: 1px;
@@ -1,8 +1,14 @@
1
1
  import type { Component } from 'svelte';
2
- import type { ShellNavItem } from './types.js';
2
+ import type { ShellNavGroup, ShellNavItem } from './types.js';
3
3
  interface Props {
4
4
  /** Navigation items with href, label, icon, and optional children. */
5
- items: ShellNavItem[];
5
+ items?: ShellNavItem[];
6
+ /** Labelled, collapsible navigation groups, rendered after flat items. */
7
+ groups?: ShellNavGroup[];
8
+ /** Accessible name for this navigation landmark. */
9
+ 'aria-label'?: string;
10
+ /** Target density; omit to inherit the theme density. */
11
+ density?: 'comfortable' | 'touch';
6
12
  /** Current URL to highlight the active navigation link. */
7
13
  currentHref?: string;
8
14
  /** Component used to render icon names as SVG or custom icons. */
@@ -1 +1 @@
1
- {"version":3,"file":"TenantNav.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/TenantNav.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,UAAU,KAAK;IACb,sEAAsE;IACtE,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAsFD,QAAA,MAAM,SAAS,0BAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"TenantNav.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/TenantNav.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG9D,UAAU,KAAK;IACb,sEAAsE;IACtE,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA6HD,QAAA,MAAM,SAAS,0BAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -11,6 +11,8 @@ const TENANT_PREFIX = 'workspace-account:tenant:';
11
11
  export interface Props {
12
12
  /** Username or identifier displayed in the account menu. */
13
13
  userName: string;
14
+ /** Target density; omit to inherit the theme density. */
15
+ density?: 'comfortable' | 'touch';
14
16
  /** Custom display name for the user, falls back to userName. */
15
17
  userLabel?: string;
16
18
  /** URL for the user's avatar image. */
@@ -35,6 +37,7 @@ export interface Props {
35
37
 
36
38
  let {
37
39
  userName,
40
+ density,
38
41
  userLabel = '',
39
42
  avatarUrl,
40
43
  tenantLabel = '',
@@ -100,7 +103,7 @@ function handleSelect(id: string): void {
100
103
  }
101
104
  </script>
102
105
 
103
- <div class="smrt-workspace-account-menu" title={summaryTitle}>
106
+ <div data-density={density} class="smrt-workspace-account-menu" title={summaryTitle}>
104
107
  <Dropdown
105
108
  {items}
106
109
  {placement}
@@ -129,6 +132,17 @@ function handleSelect(id: string): void {
129
132
  width: 100%;
130
133
  }
131
134
 
135
+ .smrt-workspace-account-menu[data-density='touch'] {
136
+ --smrt-control-target-min: var(--smrt-touch-target-min, 48px);
137
+ }
138
+ .smrt-workspace-account-menu[data-density='comfortable'] {
139
+ --smrt-control-target-min: 0px;
140
+ }
141
+ .smrt-workspace-account-menu :global(.dropdown__item) {
142
+ min-block-size: var(--smrt-control-target-min, 0px);
143
+ box-sizing: border-box;
144
+ }
145
+
132
146
  .smrt-workspace-account-menu :global(.dropdown) {
133
147
  display: flex;
134
148
  width: 100%;
@@ -141,7 +155,7 @@ function handleSelect(id: string): void {
141
155
  gap: var(--smrt-spacing-2);
142
156
  width: 100%;
143
157
  min-width: 0;
144
- min-height: 2.75rem;
158
+ min-height: max(2.75rem, var(--smrt-control-target-min, 0px));
145
159
  padding: var(--smrt-spacing-2);
146
160
  border-radius: var(--smrt-radius-large);
147
161
  text-align: start;
@@ -3,6 +3,8 @@ import type { WorkspaceAccountTenant } from './types.js';
3
3
  export interface Props {
4
4
  /** Username or identifier displayed in the account menu. */
5
5
  userName: string;
6
+ /** Target density; omit to inherit the theme density. */
7
+ density?: 'comfortable' | 'touch';
6
8
  /** Custom display name for the user, falls back to userName. */
7
9
  userLabel?: string;
8
10
  /** URL for the user's avatar image. */
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceAccountMenu.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,wBAAwB,CAAC;AAI1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACnC,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAqGD,QAAA,MAAM,oBAAoB,2CAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"WorkspaceAccountMenu.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,wBAAwB,CAAC;AAI1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACnC,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAsGD,QAAA,MAAM,oBAAoB,2CAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type ScrollChromeOptions } from './mobile-shell.js';
2
+ import { type ShellState } from './state.svelte.js';
3
+ /**
4
+ * Keep `shell.viewport` in step with the phone/desktop media queries (which
5
+ * applies each edge's `viewportDefaults` when the class changes).
6
+ */
7
+ export declare function installShellViewport(shell: ShellState): () => void;
8
+ /**
9
+ * Keep `shell.overlayMatches` in step with each side edge's `overlayMedia`
10
+ * query (edges without one are skipped).
11
+ */
12
+ export declare function installShellOverlayMedia(shell: ShellState): () => void;
13
+ /**
14
+ * Hide-on-scroll for one scroller (AdminShell's main region). Samples once
15
+ * per animation frame and reports changes of the hidden state.
16
+ */
17
+ export declare function installScrollChrome(scroller: HTMLElement, options: {
18
+ onChange: (hidden: boolean) => void;
19
+ isPinned: () => boolean;
20
+ tuning?: ScrollChromeOptions;
21
+ }): {
22
+ recheck: () => void;
23
+ reset: () => void;
24
+ destroy: () => void;
25
+ };
26
+ /**
27
+ * Watch for the on-screen keyboard. Sets `data-keyboard-open` on `<html>`
28
+ * (phone bars and `FormActionBar` hide on it) and keeps the focused field in
29
+ * view when the keyboard opens.
30
+ */
31
+ export declare function installKeyboardWatcher(onChange: (open: boolean) => void): () => void;
32
+ /**
33
+ * Report whether `container` holds a form action bar
34
+ * (`[data-form-action-bar]`), now and whenever its subtree changes.
35
+ */
36
+ export declare function watchFormActionBar(container: HTMLElement, onChange: (present: boolean) => void): () => void;
37
+ //# sourceMappingURL=mobile-shell-dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-shell-dom.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/mobile-shell-dom.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,IAAI,CAoBlE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,IAAI,CAoBtE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE;IACP,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B,GACA;IAAE,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAqCjE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAChC,MAAM,IAAI,CA6BZ;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,WAAW,EACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GACnC,MAAM,IAAI,CAYZ"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * DOM wiring for AdminShell's responsive chrome. Every installer returns a
3
+ * disposer and is a no-op without the browser API it needs, so callers can
4
+ * run them from `onMount` without guards. The decisions live in
5
+ * `./mobile-shell.ts`.
6
+ */
7
+ import { isEditableTarget } from './hotkeys.js';
8
+ import { FORM_ACTION_BAR_SELECTOR, keyboardLikelyOpen, ScrollChrome, viewportFor, } from './mobile-shell.js';
9
+ import { ADMIN_SHELL_DESKTOP_QUERY, ADMIN_SHELL_PHONE_QUERY, } from './state.svelte.js';
10
+ /**
11
+ * Keep `shell.viewport` in step with the phone/desktop media queries (which
12
+ * applies each edge's `viewportDefaults` when the class changes).
13
+ */
14
+ export function installShellViewport(shell) {
15
+ if (typeof window === 'undefined' ||
16
+ typeof window.matchMedia !== 'function') {
17
+ return () => { };
18
+ }
19
+ const phone = window.matchMedia(ADMIN_SHELL_PHONE_QUERY);
20
+ const desktop = window.matchMedia(ADMIN_SHELL_DESKTOP_QUERY);
21
+ const sync = () => shell.setViewport(viewportFor({ phone: phone.matches, desktop: desktop.matches }));
22
+ sync();
23
+ phone.addEventListener('change', sync);
24
+ desktop.addEventListener('change', sync);
25
+ return () => {
26
+ phone.removeEventListener('change', sync);
27
+ desktop.removeEventListener('change', sync);
28
+ };
29
+ }
30
+ /**
31
+ * Keep `shell.overlayMatches` in step with each side edge's `overlayMedia`
32
+ * query (edges without one are skipped).
33
+ */
34
+ export function installShellOverlayMedia(shell) {
35
+ if (typeof window === 'undefined' ||
36
+ typeof window.matchMedia !== 'function') {
37
+ return () => { };
38
+ }
39
+ const disposers = [];
40
+ for (const edge of ['left', 'right']) {
41
+ const query = shell.config.panels[edge].overlayMedia;
42
+ if (!query)
43
+ continue;
44
+ const media = window.matchMedia(query);
45
+ const sync = () => shell.setOverlayMatch(edge, media.matches);
46
+ sync();
47
+ media.addEventListener('change', sync);
48
+ disposers.push(() => media.removeEventListener('change', sync));
49
+ }
50
+ return () => {
51
+ for (const dispose of disposers)
52
+ dispose();
53
+ };
54
+ }
55
+ /**
56
+ * Hide-on-scroll for one scroller (AdminShell's main region). Samples once
57
+ * per animation frame and reports changes of the hidden state.
58
+ */
59
+ export function installScrollChrome(scroller, options) {
60
+ const chrome = new ScrollChrome(options.tuning);
61
+ let frame = 0;
62
+ let last = false;
63
+ function sample() {
64
+ frame = 0;
65
+ const hidden = chrome.update({
66
+ y: scroller.scrollTop,
67
+ maxY: scroller.scrollHeight - scroller.clientHeight,
68
+ pinned: options.isPinned(),
69
+ });
70
+ if (hidden !== last) {
71
+ last = hidden;
72
+ options.onChange(hidden);
73
+ }
74
+ }
75
+ function onScroll() {
76
+ if (!frame)
77
+ frame = requestAnimationFrame(sample);
78
+ }
79
+ scroller.addEventListener('scroll', onScroll, { passive: true });
80
+ return {
81
+ recheck: sample,
82
+ reset() {
83
+ chrome.reset();
84
+ if (last) {
85
+ last = false;
86
+ options.onChange(false);
87
+ }
88
+ },
89
+ destroy() {
90
+ if (frame)
91
+ cancelAnimationFrame(frame);
92
+ scroller.removeEventListener('scroll', onScroll);
93
+ },
94
+ };
95
+ }
96
+ /**
97
+ * Watch for the on-screen keyboard. Sets `data-keyboard-open` on `<html>`
98
+ * (phone bars and `FormActionBar` hide on it) and keeps the focused field in
99
+ * view when the keyboard opens.
100
+ */
101
+ export function installKeyboardWatcher(onChange) {
102
+ if (typeof window === 'undefined' || !window.visualViewport) {
103
+ return () => { };
104
+ }
105
+ const viewport = window.visualViewport;
106
+ let open = false;
107
+ function sync() {
108
+ const next = keyboardLikelyOpen({
109
+ layoutHeight: window.innerHeight,
110
+ visualHeight: viewport.height,
111
+ scale: viewport.scale,
112
+ });
113
+ if (next === open)
114
+ return;
115
+ open = next;
116
+ if (open)
117
+ document.documentElement.dataset.keyboardOpen = '';
118
+ else
119
+ delete document.documentElement.dataset.keyboardOpen;
120
+ onChange(open);
121
+ const active = document.activeElement;
122
+ if (open && active instanceof HTMLElement && isEditableTarget(active)) {
123
+ active.scrollIntoView({ block: 'center' });
124
+ }
125
+ }
126
+ viewport.addEventListener('resize', sync);
127
+ return () => {
128
+ viewport.removeEventListener('resize', sync);
129
+ delete document.documentElement.dataset.keyboardOpen;
130
+ };
131
+ }
132
+ /**
133
+ * Report whether `container` holds a form action bar
134
+ * (`[data-form-action-bar]`), now and whenever its subtree changes.
135
+ */
136
+ export function watchFormActionBar(container, onChange) {
137
+ let present = Boolean(container.querySelector(FORM_ACTION_BAR_SELECTOR));
138
+ onChange(present);
139
+ if (typeof MutationObserver === 'undefined')
140
+ return () => { };
141
+ const observer = new MutationObserver(() => {
142
+ const next = Boolean(container.querySelector(FORM_ACTION_BAR_SELECTOR));
143
+ if (next === present)
144
+ return;
145
+ present = next;
146
+ onChange(present);
147
+ });
148
+ observer.observe(container, { childList: true, subtree: true });
149
+ return () => observer.disconnect();
150
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Pure logic behind AdminShell's phone chrome: the hide-on-scroll state
3
+ * machine, the on-screen keyboard heuristic, the phone top-bar model, and the
4
+ * bottom-bar mode. DOM-free and SSR-safe so it can be unit tested; the DOM
5
+ * wiring lives in `./mobile-shell-dom.ts`.
6
+ */
7
+ import type { Component, ComponentType, SvelteComponent } from 'svelte';
8
+ import type { ShellViewport } from './types.js';
9
+ /** Classify a viewport from the phone and desktop media-query matches. */
10
+ export declare function viewportFor(matches: {
11
+ phone: boolean;
12
+ desktop: boolean;
13
+ }): ShellViewport;
14
+ /** Tuning for {@link ScrollChrome}. */
15
+ export interface ScrollChromeOptions {
16
+ /** Always show the chrome within this many px of the top (default 56). */
17
+ revealWithin?: number;
18
+ /** Downward travel in px before the chrome hides (default 12). */
19
+ hideAfter?: number;
20
+ /** Upward travel in px before it comes back (default 4, "first scroll up"). */
21
+ showAfter?: number;
22
+ }
23
+ /** One scroll position fed to {@link ScrollChrome.update}. */
24
+ export interface ScrollSample {
25
+ /** Current scrollTop. */
26
+ y: number;
27
+ /** Largest reachable scrollTop, to ignore iOS overscroll bounce. */
28
+ maxY?: number;
29
+ /** Keep the chrome shown (a banner is visible, a drawer is open, …). */
30
+ pinned?: boolean;
31
+ }
32
+ /**
33
+ * Hide-on-scroll state machine for the phone top bar (and anything that
34
+ * slides with it). Feed it every scroll position; read `hidden`. Travel
35
+ * accumulates in one direction and resets when the direction reverses.
36
+ */
37
+ export declare class ScrollChrome {
38
+ #private;
39
+ hidden: boolean;
40
+ constructor(options?: ScrollChromeOptions);
41
+ /** Feed a scroll sample; returns whether the chrome is now hidden. */
42
+ update(sample: ScrollSample): boolean;
43
+ /** Show the chrome and forget the scroll history (e.g. after navigating). */
44
+ reset(): void;
45
+ }
46
+ /**
47
+ * `visualViewport` heuristic: the keyboard is open when the visible viewport
48
+ * is much shorter than the layout viewport. `scale` undoes pinch zoom, which
49
+ * also shrinks `visualViewport.height`.
50
+ */
51
+ export declare function keyboardLikelyOpen(input: {
52
+ layoutHeight: number;
53
+ visualHeight: number;
54
+ scale?: number;
55
+ threshold?: number;
56
+ }): boolean;
57
+ /** A nav entry as far as path matching is concerned. */
58
+ export interface ShellNavPathItem {
59
+ href: string;
60
+ exact?: boolean;
61
+ children?: ShellNavPathItem[];
62
+ }
63
+ /** Drop trailing slashes (`/` stays `/`). */
64
+ export declare function normalizeShellPath(path: string): string;
65
+ /** Whether `path` is the item's page or (unless `exact`) below it. */
66
+ export declare function shellNavItemMatches(item: ShellNavPathItem, path: string): boolean;
67
+ /** The chain of nav items from the top level down to the one matching `path`. */
68
+ export declare function findShellNavTrail<T extends ShellNavPathItem>(items: T[], path: string): T[];
69
+ /** One ancestor of the current page: a breadcrumb and a possible back target. */
70
+ export interface ShellCrumb {
71
+ label: string;
72
+ href: string;
73
+ }
74
+ /** Input for {@link shellPageTrailFor}. */
75
+ export interface ShellPageTrailInput<T extends ShellNavPathItem & {
76
+ label: string;
77
+ }> {
78
+ /** Current pathname. */
79
+ path: string;
80
+ /** The workspace home (overview) page. */
81
+ homeHref: string;
82
+ /** Workspace name: the first ancestor of every non-home page. */
83
+ homeTitle: string;
84
+ /** The workspace nav. */
85
+ navItems: T[];
86
+ /**
87
+ * Ancestors deeper than the nav knows (an article above its video, a
88
+ * council above its meeting), nearest last. They follow the nav trail.
89
+ */
90
+ parents?: readonly ShellCrumb[] | null;
91
+ }
92
+ /** Where a page sits: a section home, or a page with ancestors. */
93
+ export interface ShellPageTrail {
94
+ /** The home page or a top-level nav page (and no page-given parents). */
95
+ sectionHome: boolean;
96
+ /** Ancestors only, never the current page; empty on section homes. */
97
+ crumbs: ShellCrumb[];
98
+ /** The nav item for the current page itself, if it is one. */
99
+ current: {
100
+ label: string;
101
+ href: string;
102
+ } | null;
103
+ }
104
+ /**
105
+ * The single source for a page's place in the workspace: breadcrumbs on
106
+ * wider screens and the phone top bar's back target both come from here.
107
+ *
108
+ * Section homes (the home page and top-level nav pages) have no ancestors.
109
+ * Anything else lists the workspace home, the nav items above it (a nested
110
+ * nav page's parent item counts, even when its URL is not below it), then the
111
+ * page-given `parents`. The current page is never a crumb.
112
+ */
113
+ export declare function shellPageTrailFor<T extends ShellNavPathItem & {
114
+ label: string;
115
+ }>(input: ShellPageTrailInput<T>): ShellPageTrail;
116
+ /**
117
+ * What the phone top bar shows: the workspace name on section home pages, or
118
+ * a back arrow (to the parent, never `history.back()`) plus the page's
119
+ * title on every other page.
120
+ */
121
+ export type PhoneTopBarModel = {
122
+ kind: 'home';
123
+ title: string;
124
+ } | {
125
+ kind: 'detail';
126
+ title: string;
127
+ backHref: string;
128
+ backLabel: string;
129
+ };
130
+ /** Input for {@link phoneTopBarFor}. */
131
+ export interface PhoneTopBarInput<T extends ShellNavPathItem & {
132
+ label: string;
133
+ }> extends ShellPageTrailInput<T> {
134
+ /** Title the page provided (article headline, person's name, …). */
135
+ pageTitle?: string | null;
136
+ /**
137
+ * Explicit back target. Prefer `parents` (the same ancestors the page's
138
+ * breadcrumbs show); this overrides the last one.
139
+ */
140
+ backHref?: string | null;
141
+ /** Back label used with an explicit `backHref` (default "Back"). */
142
+ backLabel?: string;
143
+ }
144
+ /**
145
+ * Section homes (see {@link shellPageTrailFor}) show the workspace name.
146
+ * Every other page shows a back arrow to its nearest ancestor (the last
147
+ * breadcrumb) and the page's title.
148
+ */
149
+ export declare function phoneTopBarFor<T extends ShellNavPathItem & {
150
+ label: string;
151
+ }>(input: PhoneTopBarInput<T>): PhoneTopBarModel;
152
+ /**
153
+ * What sits at the bottom of a phone screen: the nav bar, a form's own
154
+ * action bar (which replaces it), or nothing while the keyboard is open.
155
+ */
156
+ export type BottomBarMode = 'nav' | 'form' | 'none';
157
+ /** Decide the {@link BottomBarMode}. */
158
+ export declare function bottomBarMode(input: {
159
+ formActions: boolean;
160
+ keyboardOpen: boolean;
161
+ }): BottomBarMode;
162
+ /** Selector a form's action row carries to replace the phone bottom bar. */
163
+ export declare const FORM_ACTION_BAR_SELECTOR = "[data-form-action-bar]";
164
+ /**
165
+ * An icon component: a Svelte 5 function component or a legacy class
166
+ * component (e.g. lucide-svelte icons).
167
+ */
168
+ export type ShellIcon = Component<{
169
+ size?: number | string;
170
+ }> | ComponentType<SvelteComponent<{
171
+ size?: number | string;
172
+ }>>;
173
+ /** One phone bottom bar entry: a link (`href`; `onclick` also runs) or a button. */
174
+ export interface PhoneBottomBarItem {
175
+ id: string;
176
+ label: string;
177
+ icon: ShellIcon;
178
+ href?: string;
179
+ onclick?: () => void;
180
+ active?: boolean;
181
+ /** Count badge (hidden at 0). */
182
+ badge?: number;
183
+ /** Small dot, e.g. an unread reply. */
184
+ dot?: boolean;
185
+ /** Accessible description of the dot (default "new"). */
186
+ dotLabel?: string;
187
+ /** For items that open a panel: its expanded state and id. */
188
+ expanded?: boolean;
189
+ controls?: string;
190
+ }
191
+ //# sourceMappingURL=mobile-shell.d.ts.map