@flamingo-stack/openframe-frontend-core 0.0.400-snapshot.20260710144905 → 0.0.400

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 (112) hide show
  1. package/dist/{chunk-RWKVTABY.js → chunk-463X3AW2.js} +2 -2
  2. package/dist/{chunk-2B5FIJE5.js → chunk-4HAVKWTJ.js} +2 -2
  3. package/dist/{chunk-7YKHVAUU.cjs → chunk-556BG7SX.cjs} +31 -31
  4. package/dist/{chunk-7YKHVAUU.cjs.map → chunk-556BG7SX.cjs.map} +1 -1
  5. package/dist/{chunk-YON2QYWK.js → chunk-56XCRZ7I.js} +2 -2
  6. package/dist/{chunk-5P5WMTVW.js → chunk-6VR7BJF6.js} +4 -4
  7. package/dist/{chunk-Q4HI3DGY.cjs → chunk-6XQJSN7G.cjs} +15 -15
  8. package/dist/{chunk-Q4HI3DGY.cjs.map → chunk-6XQJSN7G.cjs.map} +1 -1
  9. package/dist/{chunk-FRLLKYMO.cjs → chunk-ABRDOCVN.cjs} +7 -7
  10. package/dist/{chunk-FRLLKYMO.cjs.map → chunk-ABRDOCVN.cjs.map} +1 -1
  11. package/dist/{chunk-QOOWYEPL.cjs → chunk-EFTKJFEF.cjs} +5 -5
  12. package/dist/{chunk-QOOWYEPL.cjs.map → chunk-EFTKJFEF.cjs.map} +1 -1
  13. package/dist/{chunk-PMR74LEE.js → chunk-FHEIG7QO.js} +2 -2
  14. package/dist/{chunk-FLDYBM25.js → chunk-I4XG5PJA.js} +2 -2
  15. package/dist/{chunk-QDERWRT4.cjs → chunk-ILWSJCWD.cjs} +26 -26
  16. package/dist/{chunk-QDERWRT4.cjs.map → chunk-ILWSJCWD.cjs.map} +1 -1
  17. package/dist/{chunk-F2LZ67KM.cjs → chunk-J6IQQJJQ.cjs} +1172 -1165
  18. package/dist/chunk-J6IQQJJQ.cjs.map +1 -0
  19. package/dist/{chunk-TGWHUEPD.cjs → chunk-KIC4ZEZ7.cjs} +62 -62
  20. package/dist/{chunk-TGWHUEPD.cjs.map → chunk-KIC4ZEZ7.cjs.map} +1 -1
  21. package/dist/{chunk-MZPEN27A.js → chunk-LAL63W5J.js} +4 -4
  22. package/dist/{chunk-3GXZPZHR.cjs → chunk-MFRP2UDK.cjs} +26 -26
  23. package/dist/{chunk-3GXZPZHR.cjs.map → chunk-MFRP2UDK.cjs.map} +1 -1
  24. package/dist/{chunk-V4YFWSB2.js → chunk-MQRTT3DO.js} +4 -4
  25. package/dist/{chunk-PLB6Z3E4.cjs → chunk-OPD5V57R.cjs} +12 -12
  26. package/dist/{chunk-PLB6Z3E4.cjs.map → chunk-OPD5V57R.cjs.map} +1 -1
  27. package/dist/{chunk-B3IDZ6JJ.js → chunk-PD5MXNTJ.js} +4 -4
  28. package/dist/{chunk-VIEWUAFE.cjs → chunk-QGQ76SSR.cjs} +9 -9
  29. package/dist/{chunk-VIEWUAFE.cjs.map → chunk-QGQ76SSR.cjs.map} +1 -1
  30. package/dist/{chunk-BI23LOMC.cjs → chunk-RSFHAFZN.cjs} +37 -37
  31. package/dist/{chunk-BI23LOMC.cjs.map → chunk-RSFHAFZN.cjs.map} +1 -1
  32. package/dist/{chunk-V3NY65ET.js → chunk-RYPCNUXA.js} +2 -2
  33. package/dist/{chunk-TZDROKLI.js → chunk-UFP45JLE.js} +2 -2
  34. package/dist/{chunk-EDR55RQJ.js → chunk-UYBNOHTC.js} +4714 -4707
  35. package/dist/chunk-UYBNOHTC.js.map +1 -0
  36. package/dist/{chunk-CG7LIPSA.cjs → chunk-VTSEL7CJ.cjs} +59 -59
  37. package/dist/{chunk-CG7LIPSA.cjs.map → chunk-VTSEL7CJ.cjs.map} +1 -1
  38. package/dist/components/case-studies/index.cjs +8 -8
  39. package/dist/components/case-studies/index.js +2 -2
  40. package/dist/components/chat/chat-ticket-list.d.ts.map +1 -1
  41. package/dist/components/chat/guide-welcome.d.ts.map +1 -1
  42. package/dist/components/chat/index.cjs +2 -2
  43. package/dist/components/chat/index.js +1 -1
  44. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  45. package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
  46. package/dist/components/contact/index.cjs +3 -3
  47. package/dist/components/contact/index.js +2 -2
  48. package/dist/components/docs/index.cjs +5 -5
  49. package/dist/components/docs/index.js +4 -4
  50. package/dist/components/embeds/index.cjs +3 -3
  51. package/dist/components/embeds/index.js +2 -2
  52. package/dist/components/faq/index.cjs +3 -3
  53. package/dist/components/faq/index.js +2 -2
  54. package/dist/components/features/index.cjs +2 -2
  55. package/dist/components/features/index.js +1 -1
  56. package/dist/components/help-center-pages/index.cjs +20 -20
  57. package/dist/components/help-center-pages/index.js +11 -11
  58. package/dist/components/index.cjs +120 -126
  59. package/dist/components/index.cjs.map +1 -1
  60. package/dist/components/index.js +9 -15
  61. package/dist/components/index.js.map +1 -1
  62. package/dist/components/navigation/index.cjs +2 -2
  63. package/dist/components/navigation/index.js +1 -1
  64. package/dist/components/onboarding-guides/index.cjs +5 -5
  65. package/dist/components/onboarding-guides/index.js +4 -4
  66. package/dist/components/related-content/index.cjs +3 -3
  67. package/dist/components/related-content/index.js +2 -2
  68. package/dist/components/tickets/index.cjs +5 -5
  69. package/dist/components/tickets/index.js +4 -4
  70. package/dist/components/ui/data-table/data-table-header.d.ts.map +1 -1
  71. package/dist/components/ui/data-table/types.d.ts +0 -6
  72. package/dist/components/ui/data-table/types.d.ts.map +1 -1
  73. package/dist/components/ui/date-picker.d.ts +4 -41
  74. package/dist/components/ui/date-picker.d.ts.map +1 -1
  75. package/dist/components/ui/filter-modal.d.ts +1 -16
  76. package/dist/components/ui/filter-modal.d.ts.map +1 -1
  77. package/dist/components/ui/index.cjs +2 -8
  78. package/dist/components/ui/index.cjs.map +1 -1
  79. package/dist/components/ui/index.d.ts +0 -1
  80. package/dist/components/ui/index.d.ts.map +1 -1
  81. package/dist/components/ui/index.js +1 -7
  82. package/dist/index.cjs +2 -8
  83. package/dist/index.cjs.map +1 -1
  84. package/dist/index.js +1 -7
  85. package/package.json +1 -1
  86. package/src/components/chat/chat-ticket-list.tsx +36 -8
  87. package/src/components/chat/guide-welcome.tsx +43 -5
  88. package/src/components/chat/mingo-chat-history.tsx +40 -6
  89. package/src/components/chat/mingo-welcome.tsx +47 -7
  90. package/src/components/ui/data-table/data-table-header.tsx +3 -6
  91. package/src/components/ui/data-table/types.ts +0 -6
  92. package/src/components/ui/date-picker.tsx +41 -149
  93. package/src/components/ui/filter-modal.tsx +2 -57
  94. package/src/components/ui/index.ts +0 -1
  95. package/src/stories/DatePicker.stories.tsx +2 -4
  96. package/src/stories/FilterModal.stories.tsx +0 -33
  97. package/dist/chunk-EDR55RQJ.js.map +0 -1
  98. package/dist/chunk-F2LZ67KM.cjs.map +0 -1
  99. package/dist/components/ui/scroll-fade.d.ts +0 -33
  100. package/dist/components/ui/scroll-fade.d.ts.map +0 -1
  101. package/src/components/ui/scroll-fade.tsx +0 -90
  102. /package/dist/{chunk-RWKVTABY.js.map → chunk-463X3AW2.js.map} +0 -0
  103. /package/dist/{chunk-2B5FIJE5.js.map → chunk-4HAVKWTJ.js.map} +0 -0
  104. /package/dist/{chunk-YON2QYWK.js.map → chunk-56XCRZ7I.js.map} +0 -0
  105. /package/dist/{chunk-5P5WMTVW.js.map → chunk-6VR7BJF6.js.map} +0 -0
  106. /package/dist/{chunk-PMR74LEE.js.map → chunk-FHEIG7QO.js.map} +0 -0
  107. /package/dist/{chunk-FLDYBM25.js.map → chunk-I4XG5PJA.js.map} +0 -0
  108. /package/dist/{chunk-MZPEN27A.js.map → chunk-LAL63W5J.js.map} +0 -0
  109. /package/dist/{chunk-V4YFWSB2.js.map → chunk-MQRTT3DO.js.map} +0 -0
  110. /package/dist/{chunk-B3IDZ6JJ.js.map → chunk-PD5MXNTJ.js.map} +0 -0
  111. /package/dist/{chunk-V3NY65ET.js.map → chunk-RYPCNUXA.js.map} +0 -0
  112. /package/dist/{chunk-TZDROKLI.js.map → chunk-UFP45JLE.js.map} +0 -0
@@ -3,7 +3,6 @@
3
3
  import * as React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
5
  import { MingoIcon } from '../icons'
6
- import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
7
6
  import { Skeleton } from '../ui/skeleton'
8
7
  import { ChatQuickActionRow } from './chat-quick-action-row'
9
8
  import { EntityIcon } from '../icon-display'
@@ -101,7 +100,25 @@ export function GuideWelcome({
101
100
  // Scroll-fade affordances: a 48px gradient at the top/bottom edge of the
102
101
  // scroll region, shown only while content is actually hidden in that
103
102
  // direction. (Same behaviour as MingoWelcome.)
104
- const { scrollRef, fadeTop, fadeBottom, update: updateScrollFade } = useScrollFade<HTMLDivElement>()
103
+ const scrollRef = React.useRef<HTMLDivElement>(null)
104
+ const [scrollFade, setScrollFade] = React.useState({ top: false, bottom: false })
105
+ const updateScrollFade = React.useCallback(() => {
106
+ const el = scrollRef.current
107
+ if (!el) return
108
+ const top = el.scrollTop > 1
109
+ const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1
110
+ setScrollFade((prev) =>
111
+ prev.top === top && prev.bottom === bottom ? prev : { top, bottom },
112
+ )
113
+ }, [])
114
+ React.useEffect(() => {
115
+ updateScrollFade()
116
+ const el = scrollRef.current
117
+ if (!el || typeof ResizeObserver === 'undefined') return
118
+ const ro = new ResizeObserver(updateScrollFade)
119
+ ro.observe(el)
120
+ return () => ro.disconnect()
121
+ }, [updateScrollFade])
105
122
 
106
123
  // Map to the shared `ChatQuickActionRow` chip shape. In `wrap` mode every chip
107
124
  // renders (no overflow), and `onHoverStart`/`onHoverEnd` drive the composer
@@ -187,9 +204,30 @@ export function GuideWelcome({
187
204
  {children}
188
205
  </div>
189
206
 
190
- {/* Edge scroll-fades — visible only when content is hidden beyond them. */}
191
- <ScrollFadeOverlay edge="top" visible={fadeTop} className="h-12" />
192
- <ScrollFadeOverlay edge="bottom" visible={fadeBottom} className="h-12" />
207
+ {/* Top scroll-fade — visible only when content is hidden above. */}
208
+ <div
209
+ aria-hidden
210
+ className={cn(
211
+ 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
212
+ scrollFade.top ? 'opacity-100' : 'opacity-0',
213
+ )}
214
+ style={{
215
+ background:
216
+ 'linear-gradient(0deg, transparent 0%, var(--color-bg) 100%)',
217
+ }}
218
+ />
219
+ {/* Bottom scroll-fade — visible only when content is hidden below. */}
220
+ <div
221
+ aria-hidden
222
+ className={cn(
223
+ 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
224
+ scrollFade.bottom ? 'opacity-100' : 'opacity-0',
225
+ )}
226
+ style={{
227
+ background:
228
+ 'linear-gradient(180deg, transparent 0%, var(--color-bg) 100%)',
229
+ }}
230
+ />
193
231
  </div>
194
232
 
195
233
  {/* Pinned quick-action chips above the composer — the shared
@@ -6,7 +6,6 @@ import { useDebounce } from '../../hooks/ui/use-debounce'
6
6
  import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
7
7
  import { Button } from '../ui/button'
8
8
  import { Input } from '../ui/input'
9
- import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
10
9
  import { Ellipsis01Icon, SearchIcon } from '../icons-v2-generated'
11
10
  import type { DialogItem } from './types/component.types'
12
11
 
@@ -358,9 +357,24 @@ export function MingoChatHistory({
358
357
  const groups = React.useMemo(() => groupDialogs(dialogs), [dialogs])
359
358
  const noSearchResults = groups.length === 0 && !!searchQuery?.trim()
360
359
 
361
- // Scroll-fade affordances shared ui/scroll-fade (re-measures on resize
362
- // and content mutations, so no manual `dialogs` dependency is needed).
363
- const { scrollRef, fadeTop, fadeBottom, update: updateFade } = useScrollFade<HTMLDivElement>()
360
+ // Scroll-fade affordances (same pattern as MingoWelcome's greeting region).
361
+ const scrollRef = React.useRef<HTMLDivElement>(null)
362
+ const [fade, setFade] = React.useState({ top: false, bottom: false })
363
+ const updateFade = React.useCallback(() => {
364
+ const el = scrollRef.current
365
+ if (!el) return
366
+ const top = el.scrollTop > 1
367
+ const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1
368
+ setFade((p) => (p.top === top && p.bottom === bottom ? p : { top, bottom }))
369
+ }, [])
370
+ React.useEffect(() => {
371
+ updateFade()
372
+ const el = scrollRef.current
373
+ if (!el || typeof ResizeObserver === 'undefined') return
374
+ const ro = new ResizeObserver(updateFade)
375
+ ro.observe(el)
376
+ return () => ro.disconnect()
377
+ }, [updateFade, dialogs])
364
378
 
365
379
  // Infinite scroll — load the next page when the sentinel enters the
366
380
  // scroll viewport.
@@ -431,8 +445,28 @@ export function MingoChatHistory({
431
445
  </div>
432
446
 
433
447
  {/* Scroll-fade — only while content is hidden in that direction. */}
434
- <ScrollFadeOverlay edge="top" visible={fadeTop} color="var(--color-bg-card)" className="h-12" />
435
- <ScrollFadeOverlay edge="bottom" visible={fadeBottom} color="var(--color-bg-card)" className="h-12" />
448
+ <div
449
+ aria-hidden
450
+ className={cn(
451
+ 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
452
+ fade.top ? 'opacity-100' : 'opacity-0',
453
+ )}
454
+ style={{
455
+ background:
456
+ 'linear-gradient(0deg, transparent 0%, var(--color-bg-card) 100%)',
457
+ }}
458
+ />
459
+ <div
460
+ aria-hidden
461
+ className={cn(
462
+ 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
463
+ fade.bottom ? 'opacity-100' : 'opacity-0',
464
+ )}
465
+ style={{
466
+ background:
467
+ 'linear-gradient(180deg, transparent 0%, var(--color-bg-card) 100%)',
468
+ }}
469
+ />
436
470
  </div>
437
471
  </div>
438
472
  )
@@ -7,7 +7,6 @@ import { MingoChatHistorySkeleton } from './mingo-chat-history'
7
7
  import { ChatQuickActionRow } from './chat-quick-action-row'
8
8
  import { QuickActionChipButton } from './quick-action-chip'
9
9
  import { Button } from '../ui/button'
10
- import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
11
10
  import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
12
11
  import {
13
12
  Rocket01Icon,
@@ -232,9 +231,29 @@ export function MingoWelcome({
232
231
  }
233
232
  }, [promoStorage, promoStorageKey])
234
233
 
235
- // Scroll-fade affordances shared ui/scroll-fade (48px edge gradients shown
236
- // only while content is hidden in that direction).
237
- const { scrollRef, fadeTop, fadeBottom, update: updateScrollFade } = useScrollFade<HTMLDivElement>()
234
+ // Scroll-fade affordances: show a 48px gradient at the top/bottom edge of the
235
+ // scroll region only while content is actually hidden in that direction.
236
+ const scrollRef = React.useRef<HTMLDivElement>(null)
237
+ const [scrollFade, setScrollFade] = React.useState({ top: false, bottom: false })
238
+ const updateScrollFade = React.useCallback(() => {
239
+ const el = scrollRef.current
240
+ if (!el) return
241
+ const top = el.scrollTop > 1
242
+ const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1
243
+ setScrollFade((prev) =>
244
+ prev.top === top && prev.bottom === bottom ? prev : { top, bottom },
245
+ )
246
+ }, [])
247
+ React.useEffect(() => {
248
+ updateScrollFade()
249
+ const el = scrollRef.current
250
+ if (!el || typeof ResizeObserver === 'undefined') return
251
+ // Recompute when the panel resizes — a taller panel may remove the need
252
+ // to scroll entirely, a shorter one introduces it.
253
+ const ro = new ResizeObserver(updateScrollFade)
254
+ ro.observe(el)
255
+ return () => ro.disconnect()
256
+ }, [updateScrollFade])
238
257
 
239
258
  const cellCount = featureCards.length
240
259
  // Last row's first index — used to drop the bottom divider on the final row.
@@ -346,9 +365,30 @@ export function MingoWelcome({
346
365
  )}
347
366
  </div>
348
367
 
349
- {/* Edge scroll-fades — visible only when content is hidden beyond them. */}
350
- <ScrollFadeOverlay edge="top" visible={fadeTop} color="var(--color-bg-card)" className="h-12" />
351
- <ScrollFadeOverlay edge="bottom" visible={fadeBottom} color="var(--color-bg-card)" className="h-12" />
368
+ {/* Top scroll-fade — visible only when content is hidden above. */}
369
+ <div
370
+ aria-hidden
371
+ className={cn(
372
+ 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
373
+ scrollFade.top ? 'opacity-100' : 'opacity-0',
374
+ )}
375
+ style={{
376
+ background:
377
+ 'linear-gradient(0deg, transparent 0%, var(--color-bg-card) 100%)',
378
+ }}
379
+ />
380
+ {/* Bottom scroll-fade — visible only when content is hidden below. */}
381
+ <div
382
+ aria-hidden
383
+ className={cn(
384
+ 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
385
+ scrollFade.bottom ? 'opacity-100' : 'opacity-0',
386
+ )}
387
+ style={{
388
+ background:
389
+ 'linear-gradient(180deg, transparent 0%, var(--color-bg-card) 100%)',
390
+ }}
391
+ />
352
392
  </div>
353
393
  </>
354
394
  ))}
@@ -69,8 +69,7 @@ export function DataTableHeader({
69
69
  const hasVisibleHeaderCell = headerGroup.headers.some(header => {
70
70
  if (header.isPlaceholder) return false
71
71
  if (isLgUp) return true
72
- const meta = header.column.columnDef.meta
73
- return Boolean(meta?.filter) || meta?.alwaysShowHeader === true
72
+ return Boolean(header.column.columnDef.meta?.filter)
74
73
  })
75
74
 
76
75
  return (
@@ -123,8 +122,6 @@ function HeaderCell({ header, isLgUp, sort, onSortChange }: HeaderCellProps) {
123
122
  const column = header.column
124
123
  const meta = column.columnDef.meta
125
124
  const hasFilter = Boolean(meta?.filter)
126
- // Tablet visibility: filterable columns and explicit opt-ins stay rendered.
127
- const keepOnTablet = hasFilter || meta?.alwaysShowHeader === true
128
125
  const align = meta?.align ?? 'left'
129
126
  // Sort is opt-in via `meta.sortable`. Direction is fully consumer-driven via
130
127
  // the `sort` prop; we do not consult TanStack's sort APIs here.
@@ -133,7 +130,7 @@ function HeaderCell({ header, isLgUp, sort, onSortChange }: HeaderCellProps) {
133
130
  sort?.id === column.id ? (sort.desc ? 'desc' : 'asc') : false
134
131
 
135
132
  // Mobile (md, below lg): hide non-filter columns so only filters are accessible.
136
- if (!isLgUp && !keepOnTablet) return null
133
+ if (!isLgUp && !hasFilter) return null
137
134
 
138
135
  return (
139
136
  <div
@@ -142,7 +139,7 @@ function HeaderCell({ header, isLgUp, sort, onSortChange }: HeaderCellProps) {
142
139
  isLgUp && (meta?.width || 'flex-1 min-w-0'),
143
140
  meta?.headerClassName,
144
141
  // Don't apply hide classes if column is filterable on tablet (keep filter accessible)
145
- !(keepOnTablet && !isLgUp) && getHideClasses(meta?.hideAt),
142
+ !(hasFilter && !isLgUp) && getHideClasses(meta?.hideAt),
146
143
  )}
147
144
  >
148
145
  {hasFilter ? (
@@ -16,12 +16,6 @@ declare module '@tanstack/react-table' {
16
16
  align?: 'left' | 'center' | 'right'
17
17
  /** Hide column at/below these Tailwind breakpoints. */
18
18
  hideAt?: TailwindBreakpoint | TailwindBreakpoint[]
19
- /**
20
- * Keep this header visible on tablet (md, below lg) even without
21
- * `meta.filter` — for custom headers hosting their own filter UI
22
- * (e.g. the date filter calendar trigger).
23
- */
24
- alwaysShowHeader?: boolean
25
19
  /** If present, header renders a filter dropdown with these options. */
26
20
  filter?: {
27
21
  options: DataTableFilterOption[]
@@ -150,14 +150,6 @@ function DatePickerCalendar({
150
150
  );
151
151
  const [hoveredDate, setHoveredDate] = React.useState<Date | undefined>(undefined);
152
152
 
153
- // Keep the internal range in sync when the consumer changes `selected`
154
- // externally (e.g. DateFilterMenu Reset while the popover stays open).
155
- React.useEffect(() => {
156
- if (mode === "range") {
157
- setDraftRange(selected as DateRange | undefined);
158
- }
159
- }, [mode, selected]);
160
-
161
153
  const rangeSelected = draftRange;
162
154
  const hasCompleteRange =
163
155
  mode === "range" &&
@@ -176,11 +168,7 @@ function DatePickerCalendar({
176
168
  if (!triggerDate) return;
177
169
 
178
170
  if (!draftRange?.from || draftRange.to) {
179
- // First click starts the range — propagate it so consumers can already
180
- // act on a single-day selection (e.g. DateFilterMenu Apply/Reset).
181
- const started: DateRange = { from: triggerDate, to: undefined };
182
- setDraftRange(started);
183
- onSelect(started);
171
+ setDraftRange({ from: triggerDate, to: undefined });
184
172
  return;
185
173
  }
186
174
  // Second click closes the range, ordering the two ends.
@@ -993,89 +981,6 @@ export function DatePickerInputSimple({
993
981
  );
994
982
  }
995
983
 
996
- // ============================================================================
997
- // DateFilterPanel Component (sort select + fluid calendar)
998
- // ============================================================================
999
-
1000
- export interface DateFilterPanelProps {
1001
- /** Selection mode for the calendar. Defaults to "range". */
1002
- mode?: DatePickerMode;
1003
- /** Current sort direction shown in the select. */
1004
- sort: SortDirection;
1005
- onSortChange: (sort: SortDirection) => void;
1006
- /** Current calendar selection. */
1007
- selected: Date | DateRange | undefined;
1008
- onSelect: (value: Date | DateRange | undefined) => void;
1009
- /** Minimum selectable date. */
1010
- fromDate?: Date;
1011
- /** Maximum selectable date. */
1012
- toDate?: Date;
1013
- /** Locale for the calendar. */
1014
- locale?: DayPickerProps["locale"];
1015
- /** Label for the ascending sort option. */
1016
- ascLabel?: string;
1017
- /** Label for the descending sort option. */
1018
- descLabel?: string;
1019
- className?: string;
1020
- }
1021
-
1022
- /**
1023
- * DateFilterPanel — the controlled sort-direction select + fluid calendar
1024
- * block shared by DateFilterMenu (popover) and FilterModal (mobile "Sort and
1025
- * Filter"). Owns no state: the consumer drafts and commits the values.
1026
- */
1027
- export function DateFilterPanel({
1028
- mode = "range",
1029
- sort,
1030
- onSortChange,
1031
- selected,
1032
- onSelect,
1033
- fromDate,
1034
- toDate,
1035
- locale,
1036
- ascLabel = "Sort by Ascending",
1037
- descLabel = "Sort by Descending",
1038
- className,
1039
- }: DateFilterPanelProps) {
1040
- return (
1041
- <div className={cn("flex flex-col gap-4", className)}>
1042
- {/* Sort direction selector */}
1043
- <Select
1044
- value={sort}
1045
- onValueChange={(value) => onSortChange(value as SortDirection)}
1046
- >
1047
- <SelectTrigger className="gap-2" aria-label="Sort direction">
1048
- {/* Wrapper is a <div> (not <span>) so SelectTrigger's
1049
- `[&>span]:line-clamp-1` rule doesn't force it to a vertical
1050
- -webkit-box and stack the icon/label into a column. */}
1051
- <div className="flex min-w-0 flex-1 items-center gap-2">
1052
- <ArrowUpDown className="size-6 shrink-0 text-ods-text-secondary" />
1053
- <span className="truncate">
1054
- <SelectValue />
1055
- </span>
1056
- </div>
1057
- </SelectTrigger>
1058
- <SelectContent>
1059
- <SelectItem value="asc">{ascLabel}</SelectItem>
1060
- <SelectItem value="desc">{descLabel}</SelectItem>
1061
- </SelectContent>
1062
- </Select>
1063
-
1064
- {/* Calendar */}
1065
- <DatePickerCalendar
1066
- mode={mode}
1067
- selected={selected}
1068
- onSelect={onSelect}
1069
- numberOfMonths={1}
1070
- fromDate={fromDate}
1071
- toDate={toDate}
1072
- locale={locale}
1073
- fluid
1074
- />
1075
- </div>
1076
- );
1077
- }
1078
-
1079
984
  // ============================================================================
1080
985
  // DateFilterMenu Component (sort + calendar filter popover from Figma)
1081
986
  // ============================================================================
@@ -1094,21 +999,14 @@ export interface DateFilterMenuProps {
1094
999
  mode?: DatePickerMode;
1095
1000
  /** Current (applied) sort direction. Defaults to "desc". */
1096
1001
  sort?: SortDirection;
1097
- /** Baseline sort direction — Reset restores it, and a draft that differs
1098
- * from it counts as an active change (shows Reset). Defaults to "desc". */
1099
- defaultSort?: SortDirection;
1100
1002
  /** Current (applied) single date — used when mode === "single". */
1101
1003
  date?: Date;
1102
1004
  /** Current (applied) range — used when mode === "range". */
1103
1005
  range?: DateRange;
1104
- /** Fired when the user presses Apply with the drafted selection. Also fired
1105
- * by Reset with a cleared selection so the consumer refetches unfiltered data. */
1006
+ /** Fired when the user presses Apply with the drafted selection. */
1106
1007
  onApply?: (result: DateFilterResult) => void;
1107
1008
  /** Fired when the menu closes (Close button, outside click, Esc). */
1108
1009
  onClose?: () => void;
1109
- /** Custom trigger element (rendered via Radix `asChild` — must accept a ref,
1110
- * e.g. a native button). Defaults to the outline calendar icon Button. */
1111
- trigger?: React.ReactNode;
1112
1010
  /** Disable the trigger. */
1113
1011
  disabled?: boolean;
1114
1012
  /** Minimum selectable date. */
@@ -1131,21 +1029,16 @@ export interface DateFilterMenuProps {
1131
1029
 
1132
1030
  /**
1133
1031
  * DateFilterMenu — a calendar-icon-triggered popover combining a sort-direction
1134
- * selector and a date / date-range calendar, with Close/Reset and Apply actions.
1135
- * The sort and date selection are drafted internally and committed via
1136
- * `onApply`. While a date is selected, Close is replaced by Reset, which
1137
- * clears and commits the empty selection (fires `onApply`) so the consumer
1138
- * drops the filter.
1032
+ * selector and a date / date-range calendar, with Close and Apply actions.
1033
+ * Selections are drafted internally and only committed via `onApply`.
1139
1034
  */
1140
1035
  export function DateFilterMenu({
1141
1036
  mode = "range",
1142
1037
  sort = "desc",
1143
- defaultSort = "desc",
1144
1038
  date,
1145
1039
  range,
1146
1040
  onApply,
1147
1041
  onClose,
1148
- trigger,
1149
1042
  disabled = false,
1150
1043
  fromDate,
1151
1044
  toDate,
@@ -1192,39 +1085,18 @@ export function DateFilterMenu({
1192
1085
  setOpen(false);
1193
1086
  };
1194
1087
 
1195
- // Anything to reset? A calendar selection or a non-default sort — drives Close vs Reset.
1196
- const hasSelection =
1197
- mode === "single"
1198
- ? Boolean(draftSelected)
1199
- : Boolean((draftSelected as DateRange | undefined)?.from);
1200
- const hasChanges = hasSelection || draftSort !== defaultSort;
1201
-
1202
- // Reset restores the defaults and commits them so the consumer drops the
1203
- // filter and refetches; the menu stays open with the button back to Close.
1204
- const handleReset = () => {
1205
- setDraftSort(defaultSort);
1206
- setDraftSelected(undefined);
1207
- onApply?.(
1208
- mode === "single"
1209
- ? { sort: defaultSort, date: undefined }
1210
- : { sort: defaultSort, range: undefined }
1211
- );
1212
- };
1213
-
1214
1088
  return (
1215
1089
  <Popover.Root open={open} onOpenChange={handleOpenChange}>
1216
1090
  <Popover.Trigger asChild>
1217
- {trigger ?? (
1218
- <Button
1219
- type="button"
1220
- variant="outline"
1221
- size="icon"
1222
- disabled={disabled}
1223
- aria-label={ariaLabel}
1224
- className={className}
1225
- leftIcon={<Calendar className="size-6" />}
1226
- />
1227
- )}
1091
+ <Button
1092
+ type="button"
1093
+ variant="outline"
1094
+ size="icon"
1095
+ disabled={disabled}
1096
+ aria-label={ariaLabel}
1097
+ className={className}
1098
+ leftIcon={<Calendar className="size-6" />}
1099
+ />
1228
1100
  </Popover.Trigger>
1229
1101
 
1230
1102
  <Popover.Portal>
@@ -1240,18 +1112,38 @@ export function DateFilterMenu({
1240
1112
  sideOffset={8}
1241
1113
  align={align}
1242
1114
  >
1243
- {/* Sort + calendar (shared panel) */}
1244
- <DateFilterPanel
1115
+ {/* Sort direction selector */}
1116
+ <Select
1117
+ value={draftSort}
1118
+ onValueChange={(value) => setDraftSort(value as SortDirection)}
1119
+ >
1120
+ <SelectTrigger className="gap-2" aria-label="Sort direction">
1121
+ {/* Wrapper is a <div> (not <span>) so SelectTrigger's
1122
+ `[&>span]:line-clamp-1` rule doesn't force it to a vertical
1123
+ -webkit-box and stack the icon/label into a column. */}
1124
+ <div className="flex min-w-0 flex-1 items-center gap-2">
1125
+ <ArrowUpDown className="size-6 shrink-0 text-ods-text-secondary" />
1126
+ <span className="truncate">
1127
+ <SelectValue />
1128
+ </span>
1129
+ </div>
1130
+ </SelectTrigger>
1131
+ <SelectContent>
1132
+ <SelectItem value="asc">{ascLabel}</SelectItem>
1133
+ <SelectItem value="desc">{descLabel}</SelectItem>
1134
+ </SelectContent>
1135
+ </Select>
1136
+
1137
+ {/* Calendar */}
1138
+ <DatePickerCalendar
1245
1139
  mode={mode}
1246
- sort={draftSort}
1247
- onSortChange={setDraftSort}
1248
1140
  selected={draftSelected}
1249
1141
  onSelect={setDraftSelected}
1142
+ numberOfMonths={1}
1250
1143
  fromDate={fromDate}
1251
1144
  toDate={toDate}
1252
1145
  locale={locale}
1253
- ascLabel={ascLabel}
1254
- descLabel={descLabel}
1146
+ fluid
1255
1147
  />
1256
1148
 
1257
1149
  {/* Actions */}
@@ -1260,9 +1152,9 @@ export function DateFilterMenu({
1260
1152
  type="button"
1261
1153
  variant="outline"
1262
1154
  fullWidth
1263
- onClick={hasChanges ? handleReset : handleClose}
1155
+ onClick={handleClose}
1264
1156
  >
1265
- {hasChanges ? "Reset" : "Close"}
1157
+ Close
1266
1158
  </Button>
1267
1159
  <Button
1268
1160
  type="button"
@@ -6,9 +6,7 @@ import { cn } from '../../utils/cn'
6
6
  import { Filter02Icon } from '../icons-v2-generated/sort-and-filter/filter-02-icon'
7
7
  import { Button } from './button'
8
8
  import { FilterCheckboxItem } from './filter-checkbox-item'
9
- import { DateFilterPanel, type DateFilterResult, type DateRange } from './date-picker'
10
9
  import { ModalV2, ModalV2Content, ModalV2Footer, ModalV2Header, ModalV2Title } from './modal-v2'
11
- import { ScrollFadeOverlay, useScrollFade } from './scroll-fade'
12
10
  import { Skeleton } from './skeleton'
13
11
  import { SortColumnItem, type SortConfig, type SortDirection } from './sort-column-item'
14
12
  import { TagKeyValueFilter, type TagKeyConfig } from './tag-key-value-filter'
@@ -53,20 +51,6 @@ export interface FilterModalProps {
53
51
  tagFilterTitle?: string
54
52
  /** Show skeleton loading state */
55
53
  isLoading?: boolean
56
- /** Date sort + range filter rendered as the last section. Drafted with the
57
- * other filters and committed via `onChange` on Apply (defaults on Reset). */
58
- dateFilter?: {
59
- /** Section title. Defaults to 'Date'. */
60
- title?: string
61
- /** Applied sort direction. Defaults to `defaultSort`. */
62
- sort?: SortDirection
63
- /** Baseline sort direction restored by Reset. Defaults to 'desc'. */
64
- defaultSort?: SortDirection
65
- /** Applied date range. */
66
- range?: DateRange
67
- /** Fired on Apply with the drafted values, and on Reset with the defaults. */
68
- onChange: (result: DateFilterResult) => void
69
- }
70
54
  /** Text shown in empty state title */
71
55
  emptyStateTitle?: string
72
56
  /** Text shown in empty state description */
@@ -90,7 +74,6 @@ export function FilterModal({
90
74
  selectedTags,
91
75
  onTagsChange,
92
76
  tagFilterTitle,
93
- dateFilter,
94
77
  isLoading = false,
95
78
  emptyStateTitle = 'No filters available',
96
79
  emptyStateDescription = 'There are no filter options to display at the moment',
@@ -100,20 +83,10 @@ export function FilterModal({
100
83
  })
101
84
  const [pendingTags, setPendingTags] = useState<string[]>(selectedTags ?? [])
102
85
 
103
- const dateDefaultSort: SortDirection = dateFilter?.defaultSort ?? 'desc'
104
- const [draftDateSort, setDraftDateSort] = useState<SortDirection>(dateFilter?.sort ?? dateDefaultSort)
105
- const [draftDateRange, setDraftDateRange] = useState<DateRange | undefined>(dateFilter?.range)
106
-
107
- // Scroll shadows: bottom fade by default while content continues below,
108
- // top fade once scrolled (same pattern as the chat lists).
109
- const { scrollRef, fadeTop, fadeBottom, update: updateFade } = useScrollFade<HTMLDivElement>()
110
-
111
86
  useEffect(() => {
112
87
  if (isOpen) {
113
88
  setSelectedFilters({ ...currentFilters })
114
89
  setPendingTags(selectedTags ?? [])
115
- setDraftDateSort(dateFilter?.sort ?? dateDefaultSort)
116
- setDraftDateRange(dateFilter?.range)
117
90
  }
118
91
  }, [isOpen, JSON.stringify(currentFilters), JSON.stringify(selectedTags)])
119
92
 
@@ -136,14 +109,12 @@ export function FilterModal({
136
109
  const handleReset = () => {
137
110
  onFilterChange({})
138
111
  onTagsChange?.([])
139
- dateFilter?.onChange({ sort: dateDefaultSort, range: undefined })
140
112
  onClose()
141
113
  }
142
114
 
143
115
  const handleApply = () => {
144
116
  onFilterChange(selectedFilters)
145
117
  onTagsChange?.(pendingTags)
146
- dateFilter?.onChange({ sort: draftDateSort, range: draftDateRange })
147
118
  onClose()
148
119
  }
149
120
 
@@ -154,7 +125,7 @@ export function FilterModal({
154
125
  const hasSort = !!sortConfig && sortConfig.columns.length > 0
155
126
  const hasFilterGroups = filterGroups.length > 0
156
127
  const hasTagFilter = !!tagFilterKeys && tagFilterKeys.length > 0 && !!onTagsChange
157
- const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter && !dateFilter
128
+ const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter
158
129
 
159
130
  return (
160
131
  <ModalV2
@@ -172,12 +143,7 @@ export function FilterModal({
172
143
  !isEmpty && 'md:bg-ods-bg md:border md:border-ods-border md:rounded-md md:px-3',
173
144
  )}
174
145
  >
175
- <div className="relative flex-1 min-h-0 flex flex-col">
176
- <div
177
- ref={scrollRef}
178
- onScroll={updateFade}
179
- className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 md:py-3"
180
- >
146
+ <div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 md:py-3">
181
147
  {isLoading ? (
182
148
  <>
183
149
  {[0, 1].map((group) => (
@@ -263,31 +229,10 @@ export function FilterModal({
263
229
  onTagsChange={setPendingTags}
264
230
  keysTitle={tagFilterTitle}
265
231
  />
266
- )}
267
- {/* Date sort + range filter */}
268
- {dateFilter && (
269
- <div className="flex flex-col gap-1">
270
- <span className="text-h5 text-ods-text-secondary">
271
- {dateFilter.title ?? 'Date'}
272
- </span>
273
- <DateFilterPanel
274
- mode="range"
275
- sort={draftDateSort}
276
- onSortChange={setDraftDateSort}
277
- selected={draftDateRange}
278
- onSelect={(value) => setDraftDateRange(value as DateRange | undefined)}
279
- className="gap-[var(--spacing-system-xxs)]"
280
- />
281
- </div>
282
232
  )}
283
233
  </>
284
234
  )}
285
235
  </div>
286
-
287
- {/* Scroll shadows over the filter list edges */}
288
- <ScrollFadeOverlay edge="top" visible={fadeTop} />
289
- <ScrollFadeOverlay edge="bottom" visible={fadeBottom} />
290
- </div>
291
236
  </ModalV2Content>
292
237
 
293
238
  <ModalV2Footer>
@@ -24,7 +24,6 @@ export * from './no-data'
24
24
  export * from './page-loader'
25
25
  export * from './progress-bar'
26
26
  export * from './radio-group'
27
- export * from './scroll-fade'
28
27
  export * from './select'
29
28
  export * from './switch'
30
29
  export * from './tag-select-dropdown'