@flamingo-stack/openframe-frontend-core 0.0.839 → 0.0.840

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 (108) hide show
  1. package/dist/build/export-map.json +1 -1
  2. package/dist/{chunk-37XKZ4JM.cjs → chunk-275AQ6LC.cjs} +6 -6
  3. package/dist/{chunk-5BM7X4GA.cjs → chunk-5GBLXY5F.cjs} +2 -2
  4. package/dist/{chunk-5L3UKDC6.cjs → chunk-6LDL3UZE.cjs} +9 -9
  5. package/dist/{chunk-SWMUW3RR.cjs → chunk-744JXTDP.cjs} +19 -19
  6. package/dist/{chunk-46LATWQR.cjs → chunk-7WKRMJI6.cjs} +5 -5
  7. package/dist/{chunk-XJ4AB24Y.cjs → chunk-AT6D7BAZ.cjs} +5 -5
  8. package/dist/{chunk-C4OQIYGW.cjs → chunk-CCRXBFNL.cjs} +8 -8
  9. package/dist/{chunk-LC65AC6M.cjs → chunk-COWMXUHN.cjs} +2 -0
  10. package/dist/{chunk-5E4YEHHO.cjs → chunk-DGY5KEPY.cjs} +55 -55
  11. package/dist/{chunk-N2TCVIPR.cjs → chunk-DYDQLS2R.cjs} +539 -539
  12. package/dist/{chunk-LWMWY3JO.cjs → chunk-E7M54JWW.cjs} +14 -14
  13. package/dist/{chunk-Z7A7IZWL.cjs → chunk-EV6BPF47.cjs} +236 -210
  14. package/dist/{chunk-7XRFAHJF.cjs → chunk-FHI4I3TQ.cjs} +9 -9
  15. package/dist/{chunk-ZABZIM2Y.cjs → chunk-FRL2ZN4O.cjs} +3 -3
  16. package/dist/{chunk-7Z536SPR.cjs → chunk-FW7RKWPY.cjs} +15 -1
  17. package/dist/{chunk-3MBKEF7Z.cjs → chunk-GMK4JGC2.cjs} +46 -46
  18. package/dist/{chunk-SZVPRP5E.cjs → chunk-GUKFMMDZ.cjs} +3 -3
  19. package/dist/{chunk-GN6LEJWY.cjs → chunk-HXTZZE3R.cjs} +253 -131
  20. package/dist/{chunk-2XZZHRRU.cjs → chunk-IELPVUPR.cjs} +18 -18
  21. package/dist/{chunk-64PF3UC7.cjs → chunk-INBFKUZA.cjs} +17 -17
  22. package/dist/{chunk-APOMNC7H.cjs → chunk-IUDBG3HJ.cjs} +3 -3
  23. package/dist/{chunk-4JHO2NVI.cjs → chunk-J2IXYCEH.cjs} +26 -10
  24. package/dist/{chunk-C4RYIDPU.cjs → chunk-JM2XA7VY.cjs} +28 -28
  25. package/dist/{chunk-GPXKG34G.cjs → chunk-JUEAZCD6.cjs} +46 -46
  26. package/dist/{chunk-2QAUQPGC.cjs → chunk-MYAPG3FQ.cjs} +21 -21
  27. package/dist/{chunk-GRHF5SWL.cjs → chunk-NFNSPLGC.cjs} +3 -3
  28. package/dist/{chunk-UDRVVCOC.cjs → chunk-Q6WUHYOG.cjs} +72 -72
  29. package/dist/{chunk-H3WTYAVR.cjs → chunk-RSJYTQEA.cjs} +30 -30
  30. package/dist/{chunk-MG3HJIGG.cjs → chunk-UAI4VMEF.cjs} +4 -4
  31. package/dist/{chunk-S5RMRQKT.cjs → chunk-UZPYYHF5.cjs} +18 -18
  32. package/dist/{chunk-DFTXZE7B.cjs → chunk-VBH2RCFM.cjs} +73 -73
  33. package/dist/{chunk-FNNDGOR3.cjs → chunk-WCK5T3UD.cjs} +43 -43
  34. package/dist/{chunk-TRZEFT2N.cjs → chunk-WI7EXAZT.cjs} +9 -5
  35. package/dist/{chunk-TKS25VKE.cjs → chunk-Z35PL3UR.cjs} +32 -32
  36. package/dist/{chunk-ENK75WN4.cjs → chunk-ZAO5SYBJ.cjs} +4 -4
  37. package/dist/{cloud-tenants-WE3H3VAW.cjs → cloud-tenants-PV63RZ73.cjs} +19 -19
  38. package/dist/components/case-studies/index.cjs +33 -33
  39. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
  40. package/dist/components/chat/chat-nav-sidebar.js +17 -3
  41. package/dist/components/chat/index.cjs +13 -13
  42. package/dist/components/contact/index.cjs +20 -20
  43. package/dist/components/docs/index.cjs +23 -23
  44. package/dist/components/embeds/index.cjs +20 -20
  45. package/dist/components/faq/index.cjs +21 -21
  46. package/dist/components/features/index.cjs +19 -19
  47. package/dist/components/help-center-pages/index.cjs +120 -120
  48. package/dist/components/index.cjs +76 -76
  49. package/dist/components/meeting-scheduler/index.cjs +82 -82
  50. package/dist/components/navigation/account-popover.d.ts.map +1 -1
  51. package/dist/components/navigation/account-popover.js +26 -2
  52. package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -1
  53. package/dist/components/navigation/app-layout-side-panel.js +2 -9
  54. package/dist/components/navigation/index.cjs +22 -22
  55. package/dist/components/navigation/navigation-sidebar-grouped.d.ts.map +1 -1
  56. package/dist/components/navigation/navigation-sidebar-grouped.js +30 -6
  57. package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
  58. package/dist/components/navigation/navigation-sidebar-item.js +14 -1
  59. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  60. package/dist/components/navigation/navigation-sidebar.js +72 -3
  61. package/dist/components/onboarding-guides/index.cjs +22 -22
  62. package/dist/components/product-demo/index.cjs +30 -30
  63. package/dist/components/related-content/index.cjs +8 -8
  64. package/dist/components/tickets/index.cjs +24 -24
  65. package/dist/components/ui/index.cjs +19 -19
  66. package/dist/components/ui/panel-resize-handle.d.ts +27 -6
  67. package/dist/components/ui/panel-resize-handle.d.ts.map +1 -1
  68. package/dist/components/ui/panel-resize-handle.js +27 -3
  69. package/dist/{devices-ZBZN2RRG.cjs → devices-VECG5JNC.cjs} +20 -20
  70. package/dist/{embeddable-chat-RBC7F4TL.cjs → embeddable-chat-VRYAQQF6.cjs} +90 -90
  71. package/dist/hooks/index.cjs +11 -7
  72. package/dist/hooks/ui/index.d.ts +1 -0
  73. package/dist/hooks/ui/index.d.ts.map +1 -1
  74. package/dist/hooks/ui/index.js +1 -0
  75. package/dist/hooks/ui/use-clipped-label-tooltip.d.ts +14 -0
  76. package/dist/hooks/ui/use-clipped-label-tooltip.d.ts.map +1 -0
  77. package/dist/hooks/ui/use-clipped-label-tooltip.js +13 -0
  78. package/dist/hooks/ui/use-is-truncated.d.ts.map +1 -1
  79. package/dist/hooks/ui/use-is-truncated.js +1 -0
  80. package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
  81. package/dist/hooks/ui/use-media-query.js +1 -0
  82. package/dist/hooks/ui/use-resizable-panel-size.d.ts +1 -0
  83. package/dist/hooks/ui/use-resizable-panel-size.d.ts.map +1 -1
  84. package/dist/hooks/ui/use-resizable-panel-size.js +5 -1
  85. package/dist/index.cjs +28 -24
  86. package/dist/{knowledge-2QNZGDFO.cjs → knowledge-YJSDJIXT.cjs} +20 -20
  87. package/dist/{logs-YPWTJVIH.cjs → logs-42JG3NZ5.cjs} +26 -26
  88. package/dist/{policies-BK7TUKAF.cjs → policies-D4QNVJ26.cjs} +23 -23
  89. package/dist/{remote-session-CN3G7JNR.cjs → remote-session-EE6477O3.cjs} +10 -10
  90. package/dist/{scripts-K7AOYWZE.cjs → scripts-4VFYBXBE.cjs} +26 -26
  91. package/dist/{software-update-UFN4SBMY.cjs → software-update-OSDZQ5CP.cjs} +23 -23
  92. package/dist/{tickets-board-PLYD2NSY.cjs → tickets-board-44HO3LR4.cjs} +10 -10
  93. package/dist/types/navigation.d.ts +36 -0
  94. package/dist/types/navigation.d.ts.map +1 -1
  95. package/package.json +1 -1
  96. package/src/components/chat/chat-nav-sidebar.tsx +17 -4
  97. package/src/components/navigation/account-popover.tsx +31 -2
  98. package/src/components/navigation/app-layout-side-panel.tsx +2 -7
  99. package/src/components/navigation/navigation-sidebar-grouped.tsx +37 -6
  100. package/src/components/navigation/navigation-sidebar-item.tsx +18 -1
  101. package/src/components/navigation/navigation-sidebar.tsx +116 -7
  102. package/src/components/ui/panel-resize-handle.tsx +50 -8
  103. package/src/hooks/ui/index.ts +1 -0
  104. package/src/hooks/ui/use-clipped-label-tooltip.ts +20 -0
  105. package/src/hooks/ui/use-is-truncated.ts +2 -0
  106. package/src/hooks/ui/use-media-query.ts +2 -0
  107. package/src/hooks/ui/use-resizable-panel-size.ts +7 -1
  108. package/src/types/navigation.ts +37 -0
@@ -10,9 +10,9 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
10
10
 
11
11
 
12
12
 
13
- var _chunkSWMUW3RRcjs = require('./chunk-SWMUW3RR.cjs');
14
- require('./chunk-APOMNC7H.cjs');
15
- require('./chunk-5L3UKDC6.cjs');
13
+ var _chunk744JXTDPcjs = require('./chunk-744JXTDP.cjs');
14
+ require('./chunk-IUDBG3HJ.cjs');
15
+ require('./chunk-6LDL3UZE.cjs');
16
16
  require('./chunk-YJZ7R3W5.cjs');
17
17
  require('./chunk-46547WQW.cjs');
18
18
  require('./chunk-LNUFH4E2.cjs');
@@ -1806,7 +1806,7 @@ require('./chunk-XIBCMGBW.cjs');
1806
1806
  require('./chunk-BUEEQD3G.cjs');
1807
1807
  require('./chunk-WWUSFOJ7.cjs');
1808
1808
  require('./chunk-SKI6NKOE.cjs');
1809
- require('./chunk-GRHF5SWL.cjs');
1809
+ require('./chunk-NFNSPLGC.cjs');
1810
1810
  require('./chunk-JBTH3O2C.cjs');
1811
1811
  require('./chunk-S5C3OZCO.cjs');
1812
1812
  require('./chunk-32ASZMTV.cjs');
@@ -1815,7 +1815,7 @@ require('./chunk-4IYNDV3G.cjs');
1815
1815
  require('./chunk-6VOOPMP5.cjs');
1816
1816
  require('./chunk-ORO54HDF.cjs');
1817
1817
  require('./chunk-RUHKTWGB.cjs');
1818
- require('./chunk-LC65AC6M.cjs');
1818
+ require('./chunk-COWMXUHN.cjs');
1819
1819
  require('./chunk-YSDB6HGQ.cjs');
1820
1820
  require('./chunk-FX35L4Y6.cjs');
1821
1821
  require('./chunk-JQCQCE6U.cjs');
@@ -1841,7 +1841,7 @@ function buildTicketsBoardFixture(cast) {
1841
1841
  const dana = assignee(cast.person("dana"));
1842
1842
  const alex = assignee(cast.person("alex"));
1843
1843
  return [
1844
- _chunkSWMUW3RRcjs.columnFromTicketStatus.call(void 0, "ACTIVE", [
1844
+ _chunk744JXTDPcjs.columnFromTicketStatus.call(void 0, "ACTIVE", [
1845
1845
  ticket("tk-1051", "Printer offline at the front desk", "ACTIVE", {
1846
1846
  ticketNumber: "1051",
1847
1847
  agent: "fae",
@@ -1859,7 +1859,7 @@ function buildTicketsBoardFixture(cast) {
1859
1859
  tags: ["onboarding"]
1860
1860
  })
1861
1861
  ]),
1862
- _chunkSWMUW3RRcjs.columnFromTicketStatus.call(void 0, "TECH_REQUIRED", [
1862
+ _chunk744JXTDPcjs.columnFromTicketStatus.call(void 0, "TECH_REQUIRED", [
1863
1863
  ticket("tk-1049", "Acrobat Pro for Maya", "TECH_REQUIRED", {
1864
1864
  ticketNumber: "1049",
1865
1865
  agent: "fae",
@@ -1883,7 +1883,7 @@ function buildTicketsBoardFixture(cast) {
1883
1883
  escalatedByUser: true
1884
1884
  })
1885
1885
  ]),
1886
- _chunkSWMUW3RRcjs.columnFromTicketStatus.call(void 0, "ON_HOLD", [
1886
+ _chunk744JXTDPcjs.columnFromTicketStatus.call(void 0, "ON_HOLD", [
1887
1887
  ticket("tk-1046", "Replace the conference room display", "ON_HOLD", {
1888
1888
  ticketNumber: "1046",
1889
1889
  organizationName: _chunkFDZOE5TLcjs.DEMO_ORGANIZATIONS.acme.name,
@@ -1892,7 +1892,7 @@ function buildTicketsBoardFixture(cast) {
1892
1892
  activity: { kind: "waiting-external" }
1893
1893
  })
1894
1894
  ]),
1895
- _chunkSWMUW3RRcjs.columnFromTicketStatus.call(void 0,
1895
+ _chunk744JXTDPcjs.columnFromTicketStatus.call(void 0,
1896
1896
  "RESOLVED",
1897
1897
  [
1898
1898
  ticket("tk-1048", "Slow laptop", "RESOLVED", {
@@ -1928,7 +1928,7 @@ function TicketsBoardScreen({ compact = false }) {
1928
1928
  const cast = _chunkLHV2R6QUcjs.useProductDemoCast.call(void 0, );
1929
1929
  const board = _react.useMemo.call(void 0, () => buildTicketsBoardFixture(cast), [cast]);
1930
1930
  const columns = compact ? board.filter((column) => column.id !== "ON_HOLD") : board;
1931
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg p-[var(--spacing-system-m)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkSWMUW3RRcjs.Board, { columns, onChange: noop, initialCollapsed: compact ? void 0 : FOLDED }) });
1931
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg p-[var(--spacing-system-m)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk744JXTDPcjs.Board, { columns, onChange: noop, initialCollapsed: compact ? void 0 : FOLDED }) });
1932
1932
  }
1933
1933
 
1934
1934
 
@@ -126,6 +126,34 @@ export interface SlidingSidebarConfig {
126
126
  position?: 'left' | 'right';
127
127
  className?: string;
128
128
  }
129
+ /**
130
+ * `NavigationSidebarConfig.resizable`: the user drags the expanded sidebar's
131
+ * right edge on desktop (`lg` and up). The 56px rail is not resizable, nor are
132
+ * the tablet overlay and the burger menu; the overlay opens to the chosen width.
133
+ */
134
+ export interface NavigationSidebarResizeConfig {
135
+ /**
136
+ * Narrowest expanded width, in px. Raised to 141px when lower: the rows read
137
+ * 140px and up as expanded (`@[140px]/of-nav-sidebar`).
138
+ */
139
+ minWidth: number;
140
+ /** Widest expanded width, in px. Also kept to the room there is (`minContentWidth`). */
141
+ maxWidth: number;
142
+ /**
143
+ * Px of the row the content beside the sidebar always keeps, so a wide sidebar
144
+ * cannot crowd the page out of a narrow desktop window. Default 720, the lib's
145
+ * `content-md` breakpoint.
146
+ */
147
+ minContentWidth?: number;
148
+ /**
149
+ * `localStorage` key for the width the user chose: `{ size }` under
150
+ * `<storageKey>:chosen` (see `useResizablePanelSize`). Only a drag or a key
151
+ * press on the handle writes it, never the default or a clamped width; a double
152
+ * click removes it. Omit to keep the choice for the mount only. A host that seeds
153
+ * `NAVIGATION_SIDEBAR_WIDTH_VAR` before first paint reads the same entry.
154
+ */
155
+ storageKey?: string;
156
+ }
129
157
  /**
130
158
  * Configuration for the navigation sidebar component
131
159
  */
@@ -253,6 +281,14 @@ export interface NavigationSidebarConfig {
253
281
  * chat search in `sectionSlot`.
254
282
  */
255
283
  revealSignal?: number;
284
+ /**
285
+ * Let the user resize the expanded sidebar on desktop: a drag handle astride
286
+ * its right edge (the grip the Mingo chat drawer had), arrow keys, Home and
287
+ * End on it, a double click back to the default. The default stays
288
+ * `expandedWidth` (grouped) or 224px (flat); both variants. Omitted: the fixed
289
+ * widths, as before.
290
+ */
291
+ resizable?: NavigationSidebarResizeConfig;
256
292
  }
257
293
  /** What a slot render function is told about the place it is drawn in. */
258
294
  export interface NavigationSidebarSlotState {
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/types/navigation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/types/navigation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.839",
3
+ "version": "0.0.840",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -1,9 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import { forwardRef, type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react';
4
+ import { useClippedLabelTooltip } from '../../hooks/ui/use-clipped-label-tooltip';
4
5
  import { cn } from '../../utils/cn';
5
6
  import { MediaSkeleton, TextLineSkeleton } from '../loading/unified-skeleton';
6
7
  import { BUTTON_KIND_CLASS, Button } from '../ui/button';
8
+ import { FloatingTooltip } from '../ui/floating-tooltip';
7
9
  import { LoadingItems, loadingItemProps, useLoadingScope } from '../ui/loading-scope';
8
10
  import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade';
9
11
 
@@ -157,6 +159,8 @@ const ChatListItem = forwardRef<HTMLButtonElement, ChatListItemProps>(
157
159
  ref,
158
160
  ) => {
159
161
  const loading = useLoadingScope() || loadingProp;
162
+ // A title the list clips: its full text in a tooltip over it, only while clipped and where a pointer can hover.
163
+ const { ref: titleRef, enabled: titleTooltip } = useClippedLabelTooltip<HTMLSpanElement>(title);
160
164
  const content = (
161
165
  <>
162
166
  <span className="flex min-w-0 flex-1 flex-col">
@@ -165,11 +169,20 @@ const ChatListItem = forwardRef<HTMLButtonElement, ChatListItemProps>(
165
169
  {loading ? (
166
170
  <TextLineSkeleton type="h4" className={SKELETON_TITLE_WIDTHS[index % SKELETON_TITLE_WIDTHS.length]} />
167
171
  ) : (
168
- <span
169
- className={cn('min-w-0 flex-1 truncate text-h4', active ? 'text-ods-accent' : 'text-ods-text-primary')}
172
+ <FloatingTooltip
173
+ content={title}
174
+ side="top"
175
+ as="span"
176
+ disabled={!titleTooltip}
177
+ triggerClassName="min-w-0 flex-1"
170
178
  >
171
- {title}
172
- </span>
179
+ <span
180
+ ref={titleRef}
181
+ className={cn('block min-w-0 truncate text-h4', active ? 'text-ods-accent' : 'text-ods-text-primary')}
182
+ >
183
+ {title}
184
+ </span>
185
+ </FloatingTooltip>
173
186
  )}
174
187
  </span>
175
188
  {subtitle != null &&
@@ -2,9 +2,11 @@
2
2
 
3
3
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
4
4
  import type { ReactNode } from 'react';
5
+ import { useClippedLabelTooltip } from '../../hooks/ui/use-clipped-label-tooltip';
5
6
  import { cn } from '../../utils';
6
7
  import { Chevron02DownIcon } from '../icons-v2-generated';
7
8
  import { ActionsMenuDropdown, type ActionsMenuGroup, type ActionsMenuItem } from '../ui/actions-menu';
9
+ import { FloatingTooltip } from '../ui/floating-tooltip';
8
10
  import { SquareAvatar } from '../ui/square-avatar';
9
11
  import { UnreadCountBadge, UnreadDot } from './unread-dot';
10
12
 
@@ -124,6 +126,11 @@ export function AccountPopover({
124
126
  className,
125
127
  }: AccountPopoverProps) {
126
128
  const unread = items.some(entry => entry.type !== 'divider' && (entry.count ?? 0) > 0);
129
+ // The name and subtitle a narrow sidebar clips: their full text in a tooltip
130
+ // over them, only while clipped and where a pointer can hover. The rail's
131
+ // tooltip is the trigger's `title`.
132
+ const { ref: nameRef, enabled: nameTooltip } = useClippedLabelTooltip<HTMLSpanElement>(name);
133
+ const { ref: subtitleRef, enabled: subtitleTooltip } = useClippedLabelTooltip<HTMLSpanElement>(subtitle);
127
134
  const [open = false, setOpen] = useControllableState({
128
135
  prop: openProp,
129
136
  defaultProp: false,
@@ -182,14 +189,36 @@ export function AccountPopover({
182
189
  {/* The chevron follows the name, so it reads as this row's menu and not
183
190
  as one more of the buttons a host sets at the row's end. */}
184
191
  <span className="flex min-w-0 items-center gap-[var(--spacing-system-xxs)]">
185
- <span className="truncate text-ods-text-primary text-h4">{name}</span>
192
+ <FloatingTooltip
193
+ content={name}
194
+ side="top"
195
+ as="span"
196
+ disabled={collapsed || !nameTooltip}
197
+ triggerClassName="min-w-0"
198
+ >
199
+ <span ref={nameRef} className="block truncate text-ods-text-primary text-h4">
200
+ {name}
201
+ </span>
202
+ </FloatingTooltip>
186
203
  <Chevron02DownIcon
187
204
  size={16}
188
205
  className="shrink-0 text-ods-text-secondary transition-transform duration-200 group-data-[state=open]/account:rotate-180"
189
206
  aria-hidden="true"
190
207
  />
191
208
  </span>
192
- {subtitle && <span className="truncate text-ods-text-secondary text-code">{subtitle}</span>}
209
+ {subtitle && (
210
+ <FloatingTooltip
211
+ content={subtitle}
212
+ side="top"
213
+ as="span"
214
+ disabled={collapsed || !subtitleTooltip}
215
+ triggerClassName="min-w-0"
216
+ >
217
+ <span ref={subtitleRef} className="block truncate text-ods-text-secondary text-code">
218
+ {subtitle}
219
+ </span>
220
+ </FloatingTooltip>
221
+ )}
193
222
  </span>
194
223
  </button>
195
224
  }
@@ -13,7 +13,7 @@ import {
13
13
  import { useIsomorphicLayoutEffect } from '../../hooks/ui/use-isomorphic-layout-effect';
14
14
  import { breakpoints, useMediaQuery } from '../../hooks/ui/use-media-query';
15
15
  import { cn } from '../../utils/cn';
16
- import { Menu01Icon } from '../icons-v2-generated';
16
+ import { PanelResizeGrip } from '../ui/panel-resize-handle';
17
17
 
18
18
  /**
19
19
  * - `docked`: a column beside the content, resizable.
@@ -523,12 +523,7 @@ function SidePanelResizeHandle({ state, label, controls }: SidePanelResizeHandle
523
523
  onKeyDown={handleKeyDown}
524
524
  className="group absolute inset-y-0 left-0 z-10 flex w-4 -translate-x-1/2 cursor-col-resize touch-none select-none items-center justify-center outline-none"
525
525
  >
526
- <span
527
- aria-hidden
528
- className="flex h-8 w-4 items-center justify-center rounded-sm border border-ods-border bg-ods-card text-ods-text-secondary transition-colors group-hover:border-ods-border-hover group-focus-visible:border-ods-accent group-focus-visible:text-ods-accent"
529
- >
530
- <Menu01Icon size={12} />
531
- </span>
526
+ <PanelResizeGrip />
532
527
  </div>
533
528
  );
534
529
  }
@@ -2,9 +2,11 @@
2
2
 
3
3
  import { cloneElement, isValidElement, type MouseEvent, type ReactNode, useId, useState } from 'react';
4
4
  import Link from '../../embed-shims/next-link';
5
+ import { useClippedLabelTooltip } from '../../hooks/ui/use-clipped-label-tooltip';
5
6
  import type { NavigationSidebarItem } from '../../types/navigation';
6
7
  import { cn } from '../../utils';
7
8
  import { UnifiedSkeleton } from '../loading/unified-skeleton';
9
+ import { FloatingTooltip } from '../ui/floating-tooltip';
8
10
  import { NavigationItemBadge } from './navigation-item-badge';
9
11
  import { UnreadDot } from './unread-dot';
10
12
 
@@ -150,8 +152,15 @@ function RowEnd({ count }: { count: number }) {
150
152
  return count > 0 ? <CountPill count={count} className="ml-auto" /> : null;
151
153
  }
152
154
 
153
- function RowLabel({ item }: { item: NavigationSidebarItem }) {
155
+ /**
156
+ * The row's label column. A label the expanded sidebar clips (a narrow width,
157
+ * a long label) shows its full text in a tooltip over it, as `TruncateText`
158
+ * offers it — only while it IS clipped, and only where a pointer can hover.
159
+ * The rail (`showLabel` false) has its own: the row's `title`.
160
+ */
161
+ function RowLabel({ item, showLabel = true }: { item: NavigationSidebarItem; showLabel?: boolean }) {
154
162
  const unreadCount = item.unreadCount ?? 0;
163
+ const { ref: labelRef, enabled: labelTooltip } = useClippedLabelTooltip<HTMLSpanElement>(item.label);
155
164
  return (
156
165
  <span
157
166
  className={cn(
@@ -159,7 +168,17 @@ function RowLabel({ item }: { item: NavigationSidebarItem }) {
159
168
  EXPANDED_ONLY,
160
169
  )}
161
170
  >
162
- <span className="min-w-0 truncate text-h4">{item.label}</span>
171
+ <FloatingTooltip
172
+ content={item.label}
173
+ side="top"
174
+ as="span"
175
+ disabled={!showLabel || !labelTooltip}
176
+ triggerClassName="min-w-0"
177
+ >
178
+ <span ref={labelRef} className="block min-w-0 truncate text-h4">
179
+ {item.label}
180
+ </span>
181
+ </FloatingTooltip>
163
182
  {item.badge && <NavigationItemBadge label={item.badge} compact />}
164
183
  <RowEnd count={unreadCount} />
165
184
  </span>
@@ -204,7 +223,7 @@ function GroupedLeafRow({
204
223
  const content = (
205
224
  <>
206
225
  <RowIcon item={item} size={20} className={lit ? 'text-ods-accent' : idleIcon('row')} />
207
- <RowLabel item={item} />
226
+ <RowLabel item={item} showLabel={showLabel} />
208
227
  </>
209
228
  );
210
229
  const shared = {
@@ -248,6 +267,8 @@ function GroupedChildRow({
248
267
  }) {
249
268
  const name = accessibleName(item);
250
269
  const lit = isActive && !disabled;
270
+ // The clipped label's tooltip, as `RowLabel` offers it.
271
+ const { ref: labelRef, enabled: labelTooltip } = useClippedLabelTooltip<HTMLSpanElement>(item.label);
251
272
  const className = cn(
252
273
  // The icon column flush, under the parent's icon at both widths.
253
274
  'group/nav-child relative flex h-11 w-full items-stretch gap-[var(--spacing-system-xxs)] rounded-md text-left',
@@ -276,7 +297,17 @@ function GroupedChildRow({
276
297
  !lit && !disabled && isPending && 'after:opacity-100',
277
298
  )}
278
299
  >
279
- <span className="min-w-0 truncate text-h4">{item.label}</span>
300
+ <FloatingTooltip
301
+ content={item.label}
302
+ side="top"
303
+ as="span"
304
+ disabled={!showLabel || !labelTooltip}
305
+ triggerClassName="min-w-0"
306
+ >
307
+ <span ref={labelRef} className="block min-w-0 truncate text-h4">
308
+ {item.label}
309
+ </span>
310
+ </FloatingTooltip>
280
311
  {item.badge && <NavigationItemBadge label={item.badge} compact />}
281
312
  <RowEnd count={item.unreadCount ?? 0} />
282
313
  </span>
@@ -365,7 +396,7 @@ function GroupedGroup({
365
396
  )}
366
397
  >
367
398
  <RowIcon item={item} size={20} className={lit ? 'text-ods-accent' : idleIcon('row')} />
368
- <RowLabel item={item} />
399
+ <RowLabel item={item} showLabel={showLabel} />
369
400
  </button>
370
401
  <div
371
402
  id={groupDomId}
@@ -517,7 +548,7 @@ export function NavigationSidebarActionRow({
517
548
  const content = (
518
549
  <>
519
550
  <RowIcon item={entry} size={20} className={lit || (outline && !disabled) ? 'text-ods-accent' : idleIcon('row')} />
520
- <RowLabel item={entry} />
551
+ <RowLabel item={entry} showLabel={!minimized} />
521
552
  </>
522
553
  );
523
554
  const shared = { className: classes, title: minimized ? name : undefined, 'aria-label': name };
@@ -2,10 +2,12 @@
2
2
 
3
3
  import { cloneElement, memo, type ReactElement, type ReactNode, type MouseEvent } from 'react';
4
4
  import Link from '../../embed-shims/next-link';
5
+ import { useClippedLabelTooltip } from '../../hooks/ui/use-clipped-label-tooltip';
5
6
  import type { NavigationSidebarItem } from '../../types/navigation';
6
7
  import { cn } from '../../utils';
7
8
  import { UnifiedSkeleton } from '../loading/unified-skeleton';
8
9
  import { BUTTON_KIND_CLASS, Button } from '../ui/button';
10
+ import { FloatingTooltip } from '../ui/floating-tooltip';
9
11
  import { useLoadingScope } from '../ui/loading-scope';
10
12
  import { NavigationItemBadge } from './navigation-item-badge';
11
13
  import { UnreadDot } from './unread-dot';
@@ -76,6 +78,11 @@ function LoadedNavigationSidebarItemButton({
76
78
  // The accessible name carries the stamp — "Devices (Beta)" — because `aria-label`
77
79
  // replaces the content; the rail's tooltip is the same string.
78
80
  const name = item.badge ? `${item.label} (${item.badge})` : item.label;
81
+ // A label the expanded sidebar clips (a narrow width, a long label): its full
82
+ // text in a tooltip over it, as `TruncateText` offers it — only while it IS
83
+ // clipped, since a label that fits would only repeat itself, and only where a
84
+ // pointer can hover. The rail's tooltip is the row's `title` below.
85
+ const { ref: labelRef, enabled: labelTooltip } = useClippedLabelTooltip<HTMLSpanElement>(item.label);
79
86
 
80
87
  const content: ReactNode = (
81
88
  <>
@@ -98,7 +105,17 @@ function LoadedNavigationSidebarItemButton({
98
105
  )}
99
106
  aria-hidden={!showLabel}
100
107
  >
101
- <span className="min-w-0 truncate text-h4">{item.label}</span>
108
+ <FloatingTooltip
109
+ content={item.label}
110
+ side="top"
111
+ as="span"
112
+ disabled={!showLabel || !labelTooltip}
113
+ triggerClassName="min-w-0"
114
+ >
115
+ <span ref={labelRef} className="block min-w-0 truncate text-h4">
116
+ {item.label}
117
+ </span>
118
+ </FloatingTooltip>
102
119
  {item.badge && <NavigationItemBadge label={item.badge} className="ml-[var(--spacing-system-xs)] shrink-0" />}
103
120
  </span>
104
121
 
@@ -1,13 +1,15 @@
1
1
  'use client';
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
- import type { CSSProperties, MouseEvent, ReactNode } from 'react';
4
+ import type { CSSProperties, MouseEvent, ReactNode, RefObject } from 'react';
5
5
  import { useIsHydrated } from '../../hooks/ui/use-is-hydrated';
6
6
  import { useIsomorphicLayoutEffect } from '../../hooks/ui/use-isomorphic-layout-effect';
7
7
  import { useLocalStorage } from '../../hooks/ui/use-local-storage';
8
8
  import { useLgUp, useMdUp } from '../../hooks/ui/use-media-query';
9
+ import { useResizablePanelSize } from '../../hooks/ui/use-resizable-panel-size';
9
10
  import type { NavigationSidebarConfig, NavigationSidebarItem } from '../../types/navigation';
10
11
  import { cn } from '../../utils';
12
+ import { PanelResizeGrip, PanelResizeHandle } from '../ui/panel-resize-handle';
11
13
  import { NAVIGATION_SIDEBAR_ATTRIBUTE } from './app-layout-context';
12
14
  import {
13
15
  NavigationSidebarGroupedEntries,
@@ -24,6 +26,12 @@ const MINIMIZED_WIDTH = 56; // 3.5rem = 56px
24
26
  const EXPANDED_WIDTH = 224; // 14rem = 224px — the default; a grouped config may set its own
25
27
  /** The narrowest custom `expandedWidth`: the rows read 140px and up as expanded. */
26
28
  const MIN_CUSTOM_EXPANDED_WIDTH = 141;
29
+ /**
30
+ * Px of its row a resizable sidebar leaves to the content beside it
31
+ * (`NavigationSidebarResizeConfig.minContentWidth`): the `content-md`
32
+ * breakpoint, under which page content lays out for a phone.
33
+ */
34
+ const DEFAULT_MIN_CONTENT_WIDTH = 720;
27
35
  const STORAGE_KEY = 'of.navigationSidebar.minimized';
28
36
 
29
37
  /**
@@ -146,6 +154,27 @@ const SIDEBAR_TABLET_WIDTH = {
146
154
  customOverlay: 'md:w-[var(--of-navigation-sidebar-expanded-width)]',
147
155
  } as const;
148
156
 
157
+ /**
158
+ * The width of the sidebar's row — its parent, the room it shares with the
159
+ * content — for the resize clamp. 0 until measured, and when there is nothing to
160
+ * measure (not resizable, no parent, the server).
161
+ */
162
+ function useRowWidth(asideRef: RefObject<HTMLElement | null>, enabled: boolean): number {
163
+ const [width, setWidth] = useState(0);
164
+ useEffect(() => {
165
+ const row = enabled ? asideRef.current?.parentElement : null;
166
+ if (!row) return undefined;
167
+ const update = () => setWidth(row.clientWidth);
168
+ update();
169
+ // An environment without ResizeObserver (jsdom in a consumer's tests) measures once.
170
+ if (typeof ResizeObserver === 'undefined') return undefined;
171
+ const observer = new ResizeObserver(update);
172
+ observer.observe(row);
173
+ return () => observer.disconnect();
174
+ }, [asideRef, enabled]);
175
+ return width;
176
+ }
177
+
149
178
  /** A click the browser will resolve itself — new tab, new window, middle button. */
150
179
  export const isModifiedClick = (event?: MouseEvent): boolean =>
151
180
  !!event && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0);
@@ -255,18 +284,74 @@ export function NavigationSidebar({
255
284
 
256
285
  // A grouped config's own expanded width (`expandedWidth`); `null` = the
257
286
  // default 224px through the literal classes, exactly as before.
258
- const customExpandedWidth =
287
+ const configExpandedWidth =
259
288
  config.variant === 'grouped' && config.expandedWidth !== undefined
260
289
  ? Math.max(config.expandedWidth, MIN_CUSTOM_EXPANDED_WIDTH)
261
290
  : null;
262
- const expandedWidth = customExpandedWidth ?? EXPANDED_WIDTH;
291
+
292
+ // `config.resizable`: the expanded width is the user's — dragged or stepped on
293
+ // the handle below, kept under the config's storage key — and the config width
294
+ // is where it starts. Clamped to the config's bounds and to the row, so the
295
+ // content beside the rail keeps `minContentWidth`. Not resizable: the config
296
+ // width, and the hook reads and writes nothing.
297
+ const resize = config.resizable;
298
+ const asideRef = useRef<HTMLElement>(null);
299
+ const rowWidth = useRowWidth(asideRef, resize !== undefined);
300
+ const resizeMinWidth = Math.max(resize?.minWidth ?? MIN_CUSTOM_EXPANDED_WIDTH, MIN_CUSTOM_EXPANDED_WIDTH);
301
+ const {
302
+ size: chosenExpandedWidth,
303
+ setSize: setChosenExpandedWidth,
304
+ resetSize: resetChosenExpandedWidth,
305
+ clampSize: clampExpandedWidth,
306
+ } = useResizablePanelSize({
307
+ enabled: resize !== undefined,
308
+ minSize: resizeMinWidth,
309
+ maxSize: resize?.maxWidth ?? Number.POSITIVE_INFINITY,
310
+ defaultSize: configExpandedWidth ?? EXPANDED_WIDTH,
311
+ available: rowWidth,
312
+ reserve: resize?.minContentWidth ?? DEFAULT_MIN_CONTENT_WIDTH,
313
+ storageKey: resize?.storageKey,
314
+ });
315
+
316
+ // The desktop width — what the effect below publishes. The chosen width may
317
+ // be used here as soon as it is known: an effect never runs into the SSR HTML.
318
+ const expandedWidth = resize !== undefined ? chosenExpandedWidth : (configExpandedWidth ?? EXPANDED_WIDTH);
319
+ // The width in the MARKUP — the inline custom property on the rail and its
320
+ // tablet slot, which the tablet overlay opens to and the desktop rule falls
321
+ // back on, and the handle's `aria-valuenow`: the chosen width only once
322
+ // hydrated. The server could not read it, and a first client render that
323
+ // differs is a hydration mismatch, which React reports and leaves in the DOM.
324
+ // Until then the config width stands, as for a sidebar that is not resizable;
325
+ // on desktop the seeded width shows throughout, so nothing moves.
326
+ const markupExpandedWidth = hydrated ? chosenExpandedWidth : (configExpandedWidth ?? EXPANDED_WIDTH);
327
+ const customExpandedWidth = resize !== undefined ? markupExpandedWidth : configExpandedWidth;
263
328
  const expandedWidthStyle =
264
329
  customExpandedWidth === null ? undefined : ({ [EXPANDED_WIDTH_VAR]: `${customExpandedWidth}px` } as CSSProperties);
265
330
 
331
+ // A drag moves the rail's own width property on every pointer move and marks
332
+ // the rail (`data-resizing`, read by a class on it) so its width transition
333
+ // does not trail the pointer; the choice is committed once, on release, which
334
+ // also lifts the mark. Neither write concerns hydration: both run on the
335
+ // client only, once the user has the handle.
336
+ const dragExpandedWidth = useCallback((next: number) => {
337
+ const aside = asideRef.current;
338
+ if (!aside) return;
339
+ aside.setAttribute('data-resizing', '');
340
+ aside.style.setProperty(NAVIGATION_SIDEBAR_WIDTH_VAR, `${Math.round(next)}px`);
341
+ }, []);
342
+ const commitExpandedWidth = useCallback(
343
+ (next: number) => {
344
+ asideRef.current?.removeAttribute('data-resizing');
345
+ setChosenExpandedWidth(next);
346
+ },
347
+ [setChosenExpandedWidth],
348
+ );
349
+
266
350
  // A new expanded width (the host swapped configs — e.g. a placeholder for
267
- // the grouped tree) is a different sidebar, not a toggle: it snaps instead of
268
- // animating. The transition is dropped in the same commit that moves the
269
- // width and comes back a frame later (the effect below).
351
+ // the grouped tree; a resize committed, stepped or reset on the handle) is a
352
+ // different sidebar, not a toggle: it snaps instead of animating. The
353
+ // transition is dropped in the same commit that moves the width and comes
354
+ // back a frame later (the effect below).
270
355
  const [widthFor, setWidthFor] = useState(expandedWidth);
271
356
  if (widthFor !== expandedWidth) {
272
357
  setWidthFor(expandedWidth);
@@ -447,7 +532,6 @@ export function NavigationSidebar({
447
532
  // On the rail, not on `<html>` where the seed sits: the rail is its only reader,
448
533
  // and an inherited custom property on the root made WebKit restyle the whole
449
534
  // document on every toggle. Its own value overrides the inherited seed.
450
- const asideRef = useRef<HTMLElement>(null);
451
535
  useIsomorphicLayoutEffect(() => {
452
536
  asideRef.current?.style.setProperty(NAVIGATION_SIDEBAR_WIDTH_VAR, sidebarWidth);
453
537
  }, [sidebarWidth]);
@@ -555,6 +639,9 @@ export function NavigationSidebar({
555
639
  ? SIDEBAR_TABLET_WIDTH.overlay
556
640
  : SIDEBAR_TABLET_WIDTH.customOverlay,
557
641
  transitionsEnabled && widthTransition,
642
+ // A drag on the resize handle in progress: the width follows the
643
+ // pointer, with no transition trailing it (`dragExpandedWidth`).
644
+ 'data-[resizing]:transition-none',
558
645
  // Snaps where its width moves the content: the desktop rail, and the
559
646
  // tablet overlay too when it pushes (its slot above snaps with it).
560
647
  !animateWidth && (pushOnTablet ? 'transition-none' : 'lg:transition-none'),
@@ -632,6 +719,28 @@ export function NavigationSidebar({
632
719
  onToggle={handleToggle}
633
720
  compact={grouped}
634
721
  />
722
+
723
+ {/* The resize handle (`config.resizable`), astride the rail's right edge —
724
+ the grip the Mingo chat drawer had — on desktop while expanded: the
725
+ 56px rail has nothing to resize, and below `lg` the width is the
726
+ tablet's. The 24px track straddles the border, half of it over the
727
+ content's edge; the grip marks the border as draggable. */}
728
+ {resize !== undefined && !minimized && (
729
+ <PanelResizeHandle
730
+ side="left"
731
+ variant="inLayout"
732
+ size={markupExpandedWidth}
733
+ minSize={resizeMinWidth}
734
+ maxSize={clampExpandedWidth(Number.POSITIVE_INFINITY)}
735
+ clampSize={clampExpandedWidth}
736
+ onSize={commitExpandedWidth}
737
+ onDrag={dragExpandedWidth}
738
+ onReset={resetChosenExpandedWidth}
739
+ ariaLabel="Resize navigation sidebar"
740
+ className="inset-y-0 hidden w-6 -translate-x-1/2 justify-center pl-0 lg:flex"
741
+ grip={<PanelResizeGrip />}
742
+ />
743
+ )}
635
744
  </aside>
636
745
  </>
637
746
  );