@flamingo-stack/openframe-frontend-core 0.0.222 → 0.0.223

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 (183) hide show
  1. package/dist/{chunk-AIKPR6BE.js → chunk-4VHWSAQR.js} +6069 -5235
  2. package/dist/chunk-4VHWSAQR.js.map +1 -0
  3. package/dist/{chunk-GMNE7P7V.cjs → chunk-4WBVMIKQ.cjs} +26 -26
  4. package/dist/{chunk-GMNE7P7V.cjs.map → chunk-4WBVMIKQ.cjs.map} +1 -1
  5. package/dist/{chunk-QYRV6MKX.js → chunk-53EA4QPZ.js} +13 -7
  6. package/dist/chunk-53EA4QPZ.js.map +1 -0
  7. package/dist/{chunk-4JOWZLEL.cjs → chunk-7TKYFZ3L.cjs} +7 -7
  8. package/dist/{chunk-4JOWZLEL.cjs.map → chunk-7TKYFZ3L.cjs.map} +1 -1
  9. package/dist/{chunk-IZA5WF44.cjs → chunk-ABBU7QOM.cjs} +12 -12
  10. package/dist/{chunk-IZA5WF44.cjs.map → chunk-ABBU7QOM.cjs.map} +1 -1
  11. package/dist/{chunk-KM4KK2A6.cjs → chunk-ENDUMC7Z.cjs} +2558 -1724
  12. package/dist/chunk-ENDUMC7Z.cjs.map +1 -0
  13. package/dist/{chunk-CCSEXVI4.js → chunk-GYYGA24C.js} +2 -2
  14. package/dist/{chunk-RMOHKAHA.js → chunk-ISWWP2BH.js} +3 -3
  15. package/dist/{chunk-QNYH3WUU.cjs → chunk-JUWPBMQM.cjs} +13 -7
  16. package/dist/chunk-JUWPBMQM.cjs.map +1 -0
  17. package/dist/{chunk-A2SFHYVF.cjs → chunk-KXLTWANQ.cjs} +10 -10
  18. package/dist/{chunk-A2SFHYVF.cjs.map → chunk-KXLTWANQ.cjs.map} +1 -1
  19. package/dist/{chunk-ND24XSVT.js → chunk-TA6Z2AKD.js} +3 -3
  20. package/dist/chunk-TA6Z2AKD.js.map +1 -0
  21. package/dist/{chunk-HRIAZQ5D.js → chunk-WOKB6RJ7.js} +3 -3
  22. package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
  23. package/dist/components/chat/chat-composer.d.ts.map +1 -1
  24. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  25. package/dist/components/chat/chat-message-skeleton.d.ts +19 -4
  26. package/dist/components/chat/chat-message-skeleton.d.ts.map +1 -1
  27. package/dist/components/chat/chat-panel-header-mobile.d.ts +24 -0
  28. package/dist/components/chat/chat-panel-header-mobile.d.ts.map +1 -0
  29. package/dist/components/chat/chat-panel-header.d.ts +6 -0
  30. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  31. package/dist/components/chat/chat-quick-action-row.d.ts +31 -0
  32. package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -0
  33. package/dist/components/chat/embeddable-chat.d.ts +27 -0
  34. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  35. package/dist/components/chat/entity-cards/dispatch.d.ts +14 -0
  36. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  37. package/dist/components/chat/entity-cards/github-activity-card.d.ts +10 -1
  38. package/dist/components/chat/entity-cards/github-activity-card.d.ts.map +1 -1
  39. package/dist/components/chat/guide-welcome.d.ts.map +1 -1
  40. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +9 -0
  41. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  42. package/dist/components/chat/hooks/use-slash-commands.d.ts +26 -0
  43. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
  44. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +12 -1
  45. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  46. package/dist/components/chat/hooks/use-unified-chat.d.ts +12 -0
  47. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  48. package/dist/components/chat/index.cjs +9 -3
  49. package/dist/components/chat/index.cjs.map +1 -1
  50. package/dist/components/chat/index.js +8 -2
  51. package/dist/components/chat/mingo-chat-history.d.ts +10 -0
  52. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  53. package/dist/components/chat/mingo-info-card.d.ts +63 -0
  54. package/dist/components/chat/mingo-info-card.d.ts.map +1 -0
  55. package/dist/components/chat/mingo-welcome.d.ts +14 -1
  56. package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
  57. package/dist/components/chat/types/chat.types.d.ts +6 -0
  58. package/dist/components/chat/types/chat.types.d.ts.map +1 -1
  59. package/dist/components/chat/types/unified-chat-state.types.d.ts +30 -0
  60. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  61. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  62. package/dist/components/contact/index.cjs +4 -4
  63. package/dist/components/contact/index.js +3 -3
  64. package/dist/components/features/index.cjs +3 -3
  65. package/dist/components/features/index.js +2 -2
  66. package/dist/components/index.cjs +110 -104
  67. package/dist/components/index.cjs.map +1 -1
  68. package/dist/components/index.js +36 -30
  69. package/dist/components/index.js.map +1 -1
  70. package/dist/components/navigation/app-layout-drawer.d.ts +7 -0
  71. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  72. package/dist/components/navigation/index.cjs +3 -3
  73. package/dist/components/navigation/index.js +2 -2
  74. package/dist/components/onboarding-guides/index.cjs +26 -26
  75. package/dist/components/onboarding-guides/index.js +4 -4
  76. package/dist/components/tickets/index.cjs +67 -67
  77. package/dist/components/tickets/index.js +5 -5
  78. package/dist/components/ui/actions-menu.d.ts +9 -0
  79. package/dist/components/ui/actions-menu.d.ts.map +1 -1
  80. package/dist/components/ui/dropdown-button.d.ts +2 -2
  81. package/dist/components/ui/dropdown-button.d.ts.map +1 -1
  82. package/dist/components/ui/index.cjs +9 -3
  83. package/dist/components/ui/index.cjs.map +1 -1
  84. package/dist/components/ui/index.js +8 -2
  85. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  86. package/dist/components/ui/more-actions-menu.d.ts +13 -0
  87. package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
  88. package/dist/components/ui/square-avatar.d.ts +4 -0
  89. package/dist/components/ui/square-avatar.d.ts.map +1 -1
  90. package/dist/components/ui/textarea.d.ts.map +1 -1
  91. package/dist/hooks/index.cjs +2 -2
  92. package/dist/hooks/index.js +1 -1
  93. package/dist/hooks/ui/use-auto-limit-tags.d.ts +7 -1
  94. package/dist/hooks/ui/use-auto-limit-tags.d.ts.map +1 -1
  95. package/dist/index.cjs +9 -3
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.js +8 -2
  98. package/package.json +1 -1
  99. package/src/components/chat/chat-archive-page.tsx +32 -7
  100. package/src/components/chat/chat-composer.tsx +6 -2
  101. package/src/components/chat/chat-message-enhanced.tsx +24 -7
  102. package/src/components/chat/chat-message-list.tsx +1 -1
  103. package/src/components/chat/chat-message-skeleton.tsx +60 -60
  104. package/src/components/chat/chat-panel-header-mobile.tsx +94 -0
  105. package/src/components/chat/chat-panel-header.tsx +78 -50
  106. package/src/components/chat/chat-quick-action-row.tsx +146 -0
  107. package/src/components/chat/embeddable-chat.tsx +140 -45
  108. package/src/components/chat/entity-cards/dispatch.tsx +770 -232
  109. package/src/components/chat/entity-cards/github-activity-card.tsx +9 -3
  110. package/src/components/chat/guide-welcome.tsx +11 -8
  111. package/src/components/chat/hooks/use-nats-chat-adapter.ts +80 -7
  112. package/src/components/chat/hooks/use-slash-commands.ts +39 -0
  113. package/src/components/chat/hooks/use-sse-chat-adapter.ts +49 -45
  114. package/src/components/chat/hooks/use-unified-chat.ts +31 -8
  115. package/src/components/chat/mingo-chat-history.tsx +118 -36
  116. package/src/components/chat/mingo-info-card.tsx +214 -0
  117. package/src/components/chat/mingo-welcome.tsx +89 -36
  118. package/src/components/chat/types/chat.types.ts +4 -0
  119. package/src/components/chat/types/unified-chat-state.types.ts +35 -0
  120. package/src/components/chat/utils/process-historical-messages.ts +8 -0
  121. package/src/components/content-loading-container.tsx +1 -1
  122. package/src/components/empty-state.tsx +3 -3
  123. package/src/components/faq-accordion.tsx +2 -2
  124. package/src/components/features/changelog-manager.tsx +2 -2
  125. package/src/components/features/highlight-config-section.tsx +2 -2
  126. package/src/components/features/og-editor-preview.tsx +3 -3
  127. package/src/components/features/release-media-manager.tsx +2 -2
  128. package/src/components/loading/card-skeleton.tsx +3 -3
  129. package/src/components/loading/device-card-skeleton.tsx +1 -1
  130. package/src/components/loading/margin-report-skeleton.tsx +2 -2
  131. package/src/components/loading/organization-card-skeleton.tsx +1 -1
  132. package/src/components/loading/organization-icon-skeleton.tsx +1 -1
  133. package/src/components/loading/page-layout-skeleton.tsx +9 -9
  134. package/src/components/loading/users-grid-skeleton.tsx +2 -2
  135. package/src/components/logs-list.tsx +1 -1
  136. package/src/components/media-carousel.tsx +1 -1
  137. package/src/components/navigation/app-layout-drawer.tsx +70 -6
  138. package/src/components/open-source-features.tsx +2 -2
  139. package/src/components/ui/actions-menu.tsx +36 -13
  140. package/src/components/ui/allowed-domains-input.tsx +3 -3
  141. package/src/components/ui/cursor-pagination.tsx +1 -1
  142. package/src/components/ui/dropdown-button.tsx +6 -6
  143. package/src/components/ui/info-row.tsx +2 -2
  144. package/src/components/ui/modal-v2.tsx +8 -1
  145. package/src/components/ui/more-actions-menu.tsx +13 -0
  146. package/src/components/ui/square-avatar.tsx +6 -1
  147. package/src/components/ui/tags-input.tsx +1 -1
  148. package/src/components/ui/textarea.tsx +18 -4
  149. package/src/components/vendor-icon.tsx +1 -1
  150. package/src/components/vendor-tag.tsx +1 -1
  151. package/src/components/why-it-matters.tsx +1 -1
  152. package/src/hooks/ui/use-auto-limit-tags.ts +22 -8
  153. package/src/stories/ActionsMenu.stories.tsx +256 -0
  154. package/src/stories/AppLayoutSidebar.stories.tsx +6 -0
  155. package/src/stories/Board.stories.tsx +1 -1
  156. package/src/stories/ChatMingoInfoCards.stories.tsx +748 -0
  157. package/src/stories/ChatTicketList.stories.tsx +1 -1
  158. package/src/stories/DataTable.stories.tsx +17 -13
  159. package/src/stories/EmbeddableChat.stories.tsx +233 -1
  160. package/src/stories/MingoInfoCard.stories.tsx +193 -0
  161. package/src/stories/MingoOnboardingCard.stories.tsx +1 -1
  162. package/src/stories/Modal.stories.tsx +1 -0
  163. package/src/stories/NotificationDrawer.stories.tsx +14 -3
  164. package/src/stories/PageActions.stories.tsx +4 -4
  165. package/src/stories/PageContainer.stories.tsx +5 -5
  166. package/src/stories/PageLayout.stories.tsx +1 -1
  167. package/src/stories/TabSelector.stories.tsx +2 -2
  168. package/src/stories/Tag.stories.tsx +10 -5
  169. package/src/stories/TicketInfoSection.stories.tsx +23 -11
  170. package/src/stories/Toaster.stories.tsx +5 -5
  171. package/src/stories/__fixtures__/chat-card-decorator.tsx +44 -0
  172. package/dist/chunk-AIKPR6BE.js.map +0 -1
  173. package/dist/chunk-KM4KK2A6.cjs.map +0 -1
  174. package/dist/chunk-ND24XSVT.js.map +0 -1
  175. package/dist/chunk-QNYH3WUU.cjs.map +0 -1
  176. package/dist/chunk-QYRV6MKX.js.map +0 -1
  177. package/src/stories/.TicketDescriptionViewer.stories.md +0 -39
  178. package/src/stories/.TicketKnowledgeBaseList.stories.md +0 -36
  179. package/src/stories/TicketDescriptionViewer.stories.tsx +0 -70
  180. package/src/stories/TicketKnowledgeBaseList.stories.tsx +0 -51
  181. /package/dist/{chunk-CCSEXVI4.js.map → chunk-GYYGA24C.js.map} +0 -0
  182. /package/dist/{chunk-RMOHKAHA.js.map → chunk-ISWWP2BH.js.map} +0 -0
  183. /package/dist/{chunk-HRIAZQ5D.js.map → chunk-WOKB6RJ7.js.map} +0 -0
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
 
3
3
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
4
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
4
5
  import { Check } from "lucide-react";
5
6
  import Link from "../../embed-shims/next-link";
6
- import React, { useCallback, useState } from "react";
7
+ import React, { useCallback } from "react";
7
8
  import { Chevron02RightIcon, Ellipsis01Icon } from "../icons-v2-generated";
8
9
  import { cn } from "../../utils/cn";
9
10
  import { Button } from "./button";
@@ -31,6 +32,8 @@ export interface ActionsMenuItem {
31
32
  type?: "item" | "checkbox" | "submenu" | "separator";
32
33
  checked?: boolean;
33
34
  submenu?: ActionsMenuItem[];
35
+ /** Render the row in the error/destructive color (label + icon). */
36
+ danger?: boolean;
34
37
  /** Optional URL for navigation items */
35
38
  href?: string;
36
39
  /**
@@ -59,12 +62,12 @@ interface MenuItemProps {
59
62
  }
60
63
 
61
64
  const ROW_CLASSES =
62
- "flex flex-1 min-w-0 items-center gap-2 px-3 py-3 cursor-pointer transition-colors bg-ods-bg outline-none";
65
+ "flex flex-1 min-w-0 items-center gap-[var(--spacing-system-xsf)] p-[var(--spacing-system-s)] cursor-pointer transition-colors bg-ods-bg outline-none";
63
66
  const WRAPPER_CLASSES =
64
67
  "relative flex items-stretch border-b border-ods-border last:border-b-0";
65
68
 
66
69
  const SECONDARY_ACTION_CLASSES =
67
- "flex w-10 shrink-0 items-center justify-center self-stretch border-l border-ods-border transition-colors hover:bg-ods-bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus";
70
+ "flex p-[var(--spacing-system-s)] shrink-0 items-center justify-center self-stretch border-l border-ods-border transition-colors hover:bg-ods-bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus [&_svg]:w-4 [&_svg]:h-4 md:[&_svg]:w-6 md:[&_svg]:h-6";
68
71
 
69
72
  const SecondaryAction: React.FC<{ action: ActionsMenuItemIconAction }> = ({ action }) => {
70
73
  const handleClick = useCallback(
@@ -173,7 +176,7 @@ const MenuItem: React.FC<MenuItemProps> = ({ item, onItemClick }) => {
173
176
 
174
177
  const subTriggerClasses = cn(
175
178
  itemClasses,
176
- "data-[state=open]:bg-[#2b2b2b] focus:bg-ods-bg-hover",
179
+ "data-[state=open]:bg-ods-bg-active focus:bg-ods-bg-hover",
177
180
  );
178
181
 
179
182
  const renderAsLink =
@@ -184,7 +187,8 @@ const MenuItem: React.FC<MenuItemProps> = ({ item, onItemClick }) => {
184
187
  {item.icon && (
185
188
  <div
186
189
  className={cn(
187
- "w-6 h-6 flex-shrink-0 flex items-center justify-center",
190
+ "w-4 h-4 md:w-6 md:h-6 flex-shrink-0 flex items-center justify-center",
191
+ item.danger && "text-ods-error",
188
192
  item.disabled && "opacity-50",
189
193
  )}
190
194
  >
@@ -194,8 +198,12 @@ const MenuItem: React.FC<MenuItemProps> = ({ item, onItemClick }) => {
194
198
 
195
199
  <span
196
200
  className={cn(
197
- "flex-1 text-[18px] font-medium leading-6",
198
- item.disabled ? "text-ods-text-secondary" : "text-ods-text-primary",
201
+ "flex-1 text-h4 font-medium leading-6",
202
+ item.disabled
203
+ ? "text-ods-text-secondary"
204
+ : item.danger
205
+ ? "text-ods-error"
206
+ : "text-ods-text-primary",
199
207
  )}
200
208
  >
201
209
  {item.label}
@@ -204,20 +212,20 @@ const MenuItem: React.FC<MenuItemProps> = ({ item, onItemClick }) => {
204
212
  {item.type === "checkbox" && (
205
213
  <div
206
214
  className={cn(
207
- "w-6 h-6 flex items-center justify-center rounded-md transition-colors",
215
+ "w-4 h-4 md:w-6 md:h-6 flex items-center justify-center rounded-md transition-colors",
208
216
  item.checked
209
- ? "bg-[#ffc008]"
217
+ ? "bg-ods-accent"
210
218
  : "border-2 border-ods-border bg-transparent",
211
219
  )}
212
220
  >
213
221
  {item.checked && (
214
- <Check className="w-4 h-4 text-black" strokeWidth={3} />
222
+ <Check className="w-3 h-3 md:w-4 md:h-4 text-ods-text-on-accent" strokeWidth={3} />
215
223
  )}
216
224
  </div>
217
225
  )}
218
226
 
219
227
  {item.type === "submenu" && (
220
- <Chevron02RightIcon className="w-6 h-6 text-ods-text-secondary" />
228
+ <Chevron02RightIcon className="w-4 h-4 md:w-6 md:h-6 text-ods-text-secondary" />
221
229
  )}
222
230
  </>
223
231
  );
@@ -331,6 +339,13 @@ export interface ActionsMenuDropdownProps extends ActionsMenuProps {
331
339
  align?: "start" | "center" | "end";
332
340
  side?: "top" | "right" | "bottom" | "left";
333
341
  sideOffset?: number;
342
+ /** Controlled open state. Pair with `onOpenChange`. Uncontrolled by default. */
343
+ open?: boolean;
344
+ /** Open-state change handler (also fires when an item closes the menu). */
345
+ onOpenChange?: (open: boolean) => void;
346
+ /** Forwarded to the dropdown content — e.g. `e.preventDefault()` to stop
347
+ * Radix returning focus (and its focus ring) to the trigger on close. */
348
+ onCloseAutoFocus?: (event: Event) => void;
334
349
  }
335
350
 
336
351
  export const ActionsMenuDropdown: React.FC<ActionsMenuDropdownProps> = ({
@@ -345,8 +360,15 @@ export const ActionsMenuDropdown: React.FC<ActionsMenuDropdownProps> = ({
345
360
  align = "end",
346
361
  side = "bottom",
347
362
  sideOffset = 6,
363
+ open: openProp,
364
+ onOpenChange,
365
+ onCloseAutoFocus,
348
366
  }) => {
349
- const [open, setOpen] = useState(false);
367
+ const [open = false, setOpen] = useControllableState({
368
+ prop: openProp,
369
+ defaultProp: false,
370
+ onChange: onOpenChange,
371
+ });
350
372
 
351
373
  const handleItemClick = useCallback(
352
374
  (item: ActionsMenuItem) => {
@@ -355,7 +377,7 @@ export const ActionsMenuDropdown: React.FC<ActionsMenuDropdownProps> = ({
355
377
  setOpen(false);
356
378
  }
357
379
  },
358
- [onItemClick],
380
+ [onItemClick, setOpen],
359
381
  );
360
382
 
361
383
  return (
@@ -382,6 +404,7 @@ export const ActionsMenuDropdown: React.FC<ActionsMenuDropdownProps> = ({
382
404
  align={align}
383
405
  side={side}
384
406
  sideOffset={sideOffset}
407
+ onCloseAutoFocus={onCloseAutoFocus}
385
408
  className={cn(
386
409
  "p-0 border-0 bg-transparent shadow-none overflow-visible",
387
410
  contentClassName,
@@ -88,7 +88,7 @@ const AllowedDomainsInput = React.forwardRef<HTMLDivElement, AllowedDomainsInput
88
88
  <Input
89
89
  value={domain}
90
90
  disabled
91
- className="bg-[#212121] border-[#3a3a3a] rounded-[6px] flex-1"
91
+ className="bg-ods-card border-ods-border rounded-[6px] flex-1"
92
92
  />
93
93
  <Button
94
94
  type="button"
@@ -114,7 +114,7 @@ const AllowedDomainsInput = React.forwardRef<HTMLDivElement, AllowedDomainsInput
114
114
  onKeyDown={handleKeyDown}
115
115
  placeholder={placeholder}
116
116
  disabled={disabled}
117
- className="bg-[#212121] border-[#3a3a3a] rounded-[6px] flex-1"
117
+ className="bg-ods-card border-ods-border rounded-[6px] flex-1"
118
118
  />
119
119
  </div>
120
120
 
@@ -128,7 +128,7 @@ const AllowedDomainsInput = React.forwardRef<HTMLDivElement, AllowedDomainsInput
128
128
  onClick={addDomain}
129
129
  disabled={disabled}
130
130
  >
131
- <PlusCircle className="h-5 w-5 text-[#888888]" />
131
+ <PlusCircle className="h-5 w-5 text-ods-text-secondary" />
132
132
  <span className="font-['DM_Sans'] font-bold text-base">Add Domain</span>
133
133
  </button>
134
134
 
@@ -92,7 +92,7 @@ export function CursorPagination({
92
92
  >
93
93
  {/* Info text */}
94
94
  {showInfo && (
95
- <div className="text-sm text-[#888888]">
95
+ <div className="text-sm text-ods-text-secondary">
96
96
  {displayText || <span>&nbsp;</span>}
97
97
  </div>
98
98
  )}
@@ -3,7 +3,7 @@
3
3
  import React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
5
  import { Chevron02DownIcon } from '../icons-v2-generated'
6
- import { MoreActionsMenu, type MoreActionsItem } from './more-actions-menu'
6
+ import { ActionsMenuDropdown, type ActionsMenuItem } from './actions-menu'
7
7
 
8
8
  export interface DropdownButtonProps {
9
9
  /** Button label rendered next to the chevron. */
@@ -11,7 +11,7 @@ export interface DropdownButtonProps {
11
11
  /** Optional leading icon rendered before the label. */
12
12
  icon?: React.ReactNode
13
13
  /** Items shown in the dropdown. */
14
- items: MoreActionsItem[]
14
+ items: ActionsMenuItem[]
15
15
  disabled?: boolean
16
16
  className?: string
17
17
  ariaLabel?: string
@@ -49,14 +49,14 @@ export function DropdownButton({
49
49
  )
50
50
 
51
51
  return (
52
- <MoreActionsMenu
53
- items={items}
52
+ <ActionsMenuDropdown
53
+ groups={[{ items }]}
54
54
  open={open}
55
55
  onOpenChange={handleOpenChange}
56
56
  align={align}
57
57
  side={side}
58
- ariaLabel={ariaLabel || label}
59
- trigger={
58
+ triggerAriaLabel={ariaLabel || label}
59
+ customTrigger={
60
60
  <button
61
61
  type="button"
62
62
  disabled={disabled}
@@ -9,11 +9,11 @@ interface InfoRowProps {
9
9
  export function InfoRow({ label, value, icon }: InfoRowProps) {
10
10
  return (
11
11
  <div className="flex gap-2 items-center w-full">
12
- <div className="text-h4 text-[#fafafa] overflow-hidden text-ellipsis whitespace-nowrap">
12
+ <div className="text-h4 text-ods-text-primary overflow-hidden text-ellipsis whitespace-nowrap">
13
13
  {label}
14
14
  </div>
15
15
  <div className="flex-1 bg-[#3a3a3a] h-px min-h-px min-w-px" />
16
- <div className="text-h4 text-[#fafafa] overflow-hidden text-ellipsis whitespace-nowrap flex items-center gap-1">
16
+ <div className="text-h4 text-ods-text-primary overflow-hidden text-ellipsis whitespace-nowrap flex items-center gap-1">
17
17
  {value}
18
18
  {icon}
19
19
  </div>
@@ -82,7 +82,11 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
82
82
  "absolute inset-0 bg-black/50 backdrop-blur-[2px] md:backdrop-blur-none",
83
83
  "duration-200",
84
84
  "data-[state=open]:animate-in data-[state=open]:fade-in-0",
85
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0"
85
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
86
+ // Hold the hidden end-state until unmount so the backdrop doesn't
87
+ // flash back to full opacity when the animation finishes a frame
88
+ // before the unmount timeout fires.
89
+ "data-[state=closed]:fill-mode-forwards"
86
90
  )}
87
91
  onClick={onClose}
88
92
  aria-hidden="true"
@@ -102,6 +106,9 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
102
106
  "data-[state=open]:slide-in-from-bottom-4 md:data-[state=open]:slide-in-from-bottom-0 md:data-[state=open]:zoom-in-95",
103
107
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
104
108
  "data-[state=closed]:slide-out-to-bottom-4 md:data-[state=closed]:slide-out-to-bottom-0 md:data-[state=closed]:zoom-out-95",
109
+ // Hold the hidden end-state until unmount so the panel doesn't snap
110
+ // back to opacity/scale 1 for a frame before it unmounts.
111
+ "data-[state=closed]:fill-mode-forwards",
105
112
  className
106
113
  )}
107
114
  role="dialog"
@@ -12,6 +12,11 @@ import {
12
12
  DropdownMenuTrigger
13
13
  } from './dropdown-menu'
14
14
 
15
+ /**
16
+ * @deprecated Use `ActionsMenuItem` from `./actions-menu` with
17
+ * `ActionsMenuDropdown` instead. `MoreActionsMenu` is being phased out in favor
18
+ * of the `ActionsMenu*` family.
19
+ */
15
20
  export type MoreActionsItem = {
16
21
  label: string
17
22
  /** Click handler. Optional when `href` is provided. */
@@ -25,6 +30,9 @@ export type MoreActionsItem = {
25
30
  danger?: boolean
26
31
  }
27
32
 
33
+ /**
34
+ * @deprecated Use `ActionsMenuDropdownProps` from `./actions-menu` instead.
35
+ */
28
36
  export interface MoreActionsMenuProps {
29
37
  items: MoreActionsItem[]
30
38
  align?: 'start' | 'center' | 'end'
@@ -50,6 +58,11 @@ export interface MoreActionsMenuProps {
50
58
  /**
51
59
  * Compact, reusable menu triggered by an ellipsis icon button.
52
60
  * Built on top of Radix DropdownMenu used in the UI Kit.
61
+ *
62
+ * @deprecated Use `ActionsMenuDropdown` from `./actions-menu` instead — it
63
+ * supports the same trigger override (`customTrigger`), controlled `open` /
64
+ * `onOpenChange`, `onCloseAutoFocus`, and `danger` items, plus grouped items,
65
+ * checkboxes, and submenus. This component will be removed in a future release.
53
66
  */
54
67
  export function MoreActionsMenu({
55
68
  items,
@@ -11,10 +11,14 @@ interface SquareAvatarProps extends React.HTMLAttributes<HTMLDivElement> {
11
11
  size?: 'sm' | 'md' | 'lg' | 'xl';
12
12
  fallback?: string;
13
13
  variant?: 'square' | 'round';
14
+ /** Override the initials-fallback styling (font size/color). Merged over the
15
+ * defaults (`text-xs font-medium text-ods-text-primary`) via tailwind-merge,
16
+ * so callers can shrink/recolor the initials for compact avatars. */
17
+ initialsClassName?: string;
14
18
  }
15
19
 
16
20
  const SquareAvatar = React.memo(React.forwardRef<HTMLDivElement, SquareAvatarProps>(
17
- ({ className, src, alt, size = 'md', fallback, variant = 'square', ...props }, ref) => {
21
+ ({ className, src, alt, size = 'md', fallback, variant = 'square', initialsClassName, ...props }, ref) => {
18
22
  const sizeClasses = {
19
23
  sm: 'h-8 w-8',
20
24
  md: 'h-10 w-10',
@@ -55,6 +59,7 @@ const SquareAvatar = React.memo(React.forwardRef<HTMLDivElement, SquareAvatarPro
55
59
  // (#fafafa) on the same pink yields ~3.5:1 (AA Large) and on the
56
60
  // dark default bg yields ~15:1.
57
61
  'flex items-center justify-center text-xs font-medium text-ods-text-primary',
62
+ initialsClassName,
58
63
  src && 'hidden'
59
64
  )}>
60
65
  {getFirstLastInitials(fallback || alt) || '?'}
@@ -79,7 +79,7 @@ export function TagsInput({
79
79
  placeholder={placeholder}
80
80
  disabled={disabled || (maxTags ? value.length >= maxTags : false)}
81
81
  className={cn(
82
- "flex-1 bg-[#161616] border-ods-border text-ods-text-primary",
82
+ "flex-1 bg-ods-bg border-ods-border text-ods-text-primary",
83
83
  inputClassName
84
84
  )}
85
85
  />
@@ -69,7 +69,12 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
69
69
  // to the bottom-right when the textarea grows multi-line. Vertical
70
70
  // padding lives here (not on the textarea) so both children share
71
71
  // the same bottom baseline at any height.
72
- "flex w-full items-end gap-2 rounded-[6px] border px-3 py-2.5 md:py-3 min-h-11 md:min-h-12 cursor-text",
72
+ //
73
+ // Padding is sized so the single-line height matches `Input` exactly
74
+ // (44px mobile / 48px desktop): line `leading-6` (24px) + 2× padding
75
+ // + 1px border top/bottom. `min-h-11 md:min-h-12` is the matching
76
+ // floor; with symmetric padding the single line stays centered.
77
+ "flex w-full items-end gap-2 rounded-[6px] border px-3 py-[9px] md:py-[11px] min-h-11 md:min-h-12 cursor-text",
73
78
  "has-[:focus-visible]:outline-none",
74
79
  "group",
75
80
  "transition-colors duration-200",
@@ -88,7 +93,12 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
88
93
  // JS needed to size to content. Falls back to `rows={1}` natural
89
94
  // height on older browsers.
90
95
  "[field-sizing:content]",
91
- "text-h4 text-ods-text-primary placeholder:text-ods-text-secondary leading-6",
96
+ // `!leading-6` (not plain `leading-6`): on mobile `text-h4` sets a
97
+ // 20px line-height of its own that otherwise overrides the leading,
98
+ // leaving the 20px text box short of the 24px icon box and dropping
99
+ // the text below center under `items-end`. Forcing 24px keeps text
100
+ // and icon co-centered and the single-line height at 44px.
101
+ "text-h4 text-ods-text-primary placeholder:text-ods-text-secondary !leading-6",
92
102
  // Hard cap on growth: beyond `max-h` the textarea scrolls
93
103
  // internally instead of pushing the icon out of frame.
94
104
  "max-h-[160px] overflow-y-auto",
@@ -105,7 +115,11 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
105
115
  aria-label={endIconButtonProps?.['aria-label'] ?? 'Submit'}
106
116
  {...endIconButtonProps}
107
117
  className={cn(
108
- "flex shrink-0 items-center text-ods-text-secondary transition-colors duration-200",
118
+ // `h-6` matches the textarea's `leading-6` line box so the icon's
119
+ // visual center lines up with the text on the first line. Without
120
+ // it, `items-end` bottom-aligns the smaller mobile icon (size-4)
121
+ // against the 24px text line and drops it below the text center.
122
+ "flex h-6 shrink-0 items-center text-ods-text-secondary transition-colors duration-200",
109
123
  "group-has-[:focus]:text-ods-accent group-data-[invalid]:text-ods-error",
110
124
  "[&_svg]:size-4 md:[&_svg]:size-6",
111
125
  "cursor-pointer hover:text-ods-text-primary",
@@ -119,7 +133,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
119
133
  ) : (
120
134
  <span
121
135
  className={cn(
122
- "flex shrink-0 items-center text-ods-text-secondary transition-colors duration-200",
136
+ "flex h-6 shrink-0 items-center text-ods-text-secondary transition-colors duration-200",
123
137
  "group-has-[:focus]:text-ods-accent group-data-[invalid]:text-ods-error",
124
138
  "[&_svg]:size-4 md:[&_svg]:size-6",
125
139
  )}
@@ -38,7 +38,7 @@ const imageSizeMap = {
38
38
  }
39
39
 
40
40
  const backgroundClasses = {
41
- dark: 'bg-[#161616] border border-ods-border',
41
+ dark: 'bg-ods-bg border border-ods-border',
42
42
  light: 'bg-ods-card border border-ods-border',
43
43
  white: 'bg-white border border-[#E5E5E5]'
44
44
  }
@@ -24,7 +24,7 @@ export function VendorTag({
24
24
  }: VendorTagProps) {
25
25
  // Base classes for the tag container
26
26
  const baseClasses = cn(
27
- "flex items-center gap-1.5 bg-[#161616] border border-ods-border rounded whitespace-nowrap",
27
+ "flex items-center gap-1.5 bg-ods-bg border border-ods-border rounded whitespace-nowrap",
28
28
  size === 'sm' ? "px-2 py-1" : "px-2.5 py-1.5"
29
29
  )
30
30
 
@@ -59,7 +59,7 @@ const WhyItMatters = () => {
59
59
  ];
60
60
 
61
61
  return (
62
- <section className="bg-[#161616]">
62
+ <section className="bg-ods-bg">
63
63
  <div className="w-full max-w-[1920px] mx-auto px-6 md:px-20">
64
64
  <h2 className="text-h1 text-center text-ods-text-primary tracking-[-0.02em] mb-6">
65
65
  Why It Matters
@@ -9,6 +9,12 @@ export interface UseAutoLimitTagsOptions {
9
9
  limitTags?: number | "auto"
10
10
  /** Placeholder text used to reserve input width (only used in "auto" mode) */
11
11
  placeholder?: string
12
+ /**
13
+ * Reserve trailing space for a text input (the autocomplete combobox). Set
14
+ * `false` for a pure chip row that has no input — then only the chips and the
15
+ * "+N" / "⋯" overflow badge compete for the available width. Default `true`.
16
+ */
17
+ reserveInputWidth?: boolean
12
18
  }
13
19
 
14
20
  export interface UseAutoLimitTagsReturn {
@@ -41,6 +47,7 @@ export function useAutoLimitTags({
41
47
  count,
42
48
  limitTags = "auto",
43
49
  placeholder = "",
50
+ reserveInputWidth = true,
44
51
  }: UseAutoLimitTagsOptions): UseAutoLimitTagsReturn {
45
52
  const middleRef = useRef<HTMLDivElement>(null)
46
53
  const measureRef = useRef<HTMLDivElement>(null)
@@ -74,15 +81,22 @@ export function useAutoLimitTags({
74
81
  const gap = parseFloat(cs.gap) || 0
75
82
  const middleW = middle.clientWidth
76
83
 
77
- // Reserve space for the input based on placeholder width
78
- const textW = textMeasureRef.current?.offsetWidth ?? 60
79
- const inputMinW = inputRef.current
80
- ? parseFloat(getComputedStyle(inputRef.current).minWidth) || 60
81
- : 60
82
- const inputReservedW = Math.max(textW + 8, inputMinW)
84
+ // Reserve trailing space for the input (autocomplete only). A pure chip row
85
+ // (`reserveInputWidth: false`) has no input, so nothing — and no gap before
86
+ // it is reserved.
87
+ let inputReservedW = 0
88
+ let trailingGap = 0
89
+ if (reserveInputWidth) {
90
+ const textW = textMeasureRef.current?.offsetWidth ?? 60
91
+ const inputMinW = inputRef.current
92
+ ? parseFloat(getComputedStyle(inputRef.current).minWidth) || 60
93
+ : 60
94
+ inputReservedW = Math.max(textW + 8, inputMinW)
95
+ trailingGap = gap
96
+ }
83
97
 
84
98
  // Available = middle zone − padding − input reserved − gap before input
85
- const available = middleW - padL - padR - inputReservedW - gap
99
+ const available = middleW - padL - padR - inputReservedW - trailingGap
86
100
 
87
101
  // Measure every tag from the off-screen container
88
102
  const tagEls = Array.from(measure.children) as HTMLElement[]
@@ -112,7 +126,7 @@ export function useAutoLimitTags({
112
126
  }
113
127
 
114
128
  setVisibleCount(Math.max(0, fitCount))
115
- }, [count, limitTags, placeholder])
129
+ }, [count, limitTags, placeholder, reserveInputWidth])
116
130
 
117
131
  // Recalculate when inputs change
118
132
  useEffect(() => {