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

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-ABRDOCVN.cjs → chunk-32K5CRL2.cjs} +7 -7
  2. package/dist/{chunk-ABRDOCVN.cjs.map → chunk-32K5CRL2.cjs.map} +1 -1
  3. package/dist/{chunk-4HAVKWTJ.js → chunk-3TCCW7B4.js} +2 -2
  4. package/dist/{chunk-I4XG5PJA.js → chunk-6FIVVZO2.js} +2 -2
  5. package/dist/{chunk-56XCRZ7I.js → chunk-6VAJDGZK.js} +2 -2
  6. package/dist/{chunk-KIC4ZEZ7.cjs → chunk-A4RDD2PS.cjs} +62 -62
  7. package/dist/{chunk-KIC4ZEZ7.cjs.map → chunk-A4RDD2PS.cjs.map} +1 -1
  8. package/dist/{chunk-MQRTT3DO.js → chunk-COGPHD7D.js} +4 -4
  9. package/dist/{chunk-EFTKJFEF.cjs → chunk-CWLKGOU4.cjs} +5 -5
  10. package/dist/{chunk-EFTKJFEF.cjs.map → chunk-CWLKGOU4.cjs.map} +1 -1
  11. package/dist/{chunk-VTSEL7CJ.cjs → chunk-D3HPV6YE.cjs} +59 -59
  12. package/dist/{chunk-VTSEL7CJ.cjs.map → chunk-D3HPV6YE.cjs.map} +1 -1
  13. package/dist/{chunk-MFRP2UDK.cjs → chunk-DYV47UA6.cjs} +26 -26
  14. package/dist/{chunk-MFRP2UDK.cjs.map → chunk-DYV47UA6.cjs.map} +1 -1
  15. package/dist/{chunk-ILWSJCWD.cjs → chunk-EDOLCPBS.cjs} +26 -26
  16. package/dist/{chunk-ILWSJCWD.cjs.map → chunk-EDOLCPBS.cjs.map} +1 -1
  17. package/dist/{chunk-UFP45JLE.js → chunk-F63VMX4S.js} +2 -2
  18. package/dist/{chunk-6XQJSN7G.cjs → chunk-HU5HWEQ5.cjs} +15 -15
  19. package/dist/{chunk-6XQJSN7G.cjs.map → chunk-HU5HWEQ5.cjs.map} +1 -1
  20. package/dist/{chunk-RSFHAFZN.cjs → chunk-JXSWDXUA.cjs} +37 -37
  21. package/dist/{chunk-RSFHAFZN.cjs.map → chunk-JXSWDXUA.cjs.map} +1 -1
  22. package/dist/{chunk-FHEIG7QO.js → chunk-LAFLYVZ4.js} +2 -2
  23. package/dist/{chunk-J6IQQJJQ.cjs → chunk-MEU2JEXL.cjs} +1167 -1172
  24. package/dist/chunk-MEU2JEXL.cjs.map +1 -0
  25. package/dist/{chunk-463X3AW2.js → chunk-NHFQ2LCC.js} +2 -2
  26. package/dist/{chunk-RYPCNUXA.js → chunk-O5QU5KAC.js} +2 -2
  27. package/dist/{chunk-556BG7SX.cjs → chunk-ONAWSVFK.cjs} +31 -31
  28. package/dist/{chunk-556BG7SX.cjs.map → chunk-ONAWSVFK.cjs.map} +1 -1
  29. package/dist/{chunk-OPD5V57R.cjs → chunk-OPCGDJU7.cjs} +12 -12
  30. package/dist/{chunk-OPD5V57R.cjs.map → chunk-OPCGDJU7.cjs.map} +1 -1
  31. package/dist/{chunk-UYBNOHTC.js → chunk-RZIFF62P.js} +4709 -4714
  32. package/dist/chunk-RZIFF62P.js.map +1 -0
  33. package/dist/{chunk-LAL63W5J.js → chunk-VMJTNXOH.js} +4 -4
  34. package/dist/{chunk-6VR7BJF6.js → chunk-WM72XFOD.js} +4 -4
  35. package/dist/{chunk-QGQ76SSR.cjs → chunk-YIJOSOSA.cjs} +9 -9
  36. package/dist/{chunk-QGQ76SSR.cjs.map → chunk-YIJOSOSA.cjs.map} +1 -1
  37. package/dist/{chunk-PD5MXNTJ.js → chunk-YPNI74ZP.js} +4 -4
  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 +126 -120
  59. package/dist/components/index.cjs.map +1 -1
  60. package/dist/components/index.js +15 -9
  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 +6 -0
  72. package/dist/components/ui/data-table/types.d.ts.map +1 -1
  73. package/dist/components/ui/date-picker.d.ts +41 -4
  74. package/dist/components/ui/date-picker.d.ts.map +1 -1
  75. package/dist/components/ui/filter-modal.d.ts +20 -1
  76. package/dist/components/ui/filter-modal.d.ts.map +1 -1
  77. package/dist/components/ui/index.cjs +8 -2
  78. package/dist/components/ui/index.cjs.map +1 -1
  79. package/dist/components/ui/index.d.ts +1 -0
  80. package/dist/components/ui/index.d.ts.map +1 -1
  81. package/dist/components/ui/index.js +7 -1
  82. package/dist/components/ui/scroll-fade.d.ts +33 -0
  83. package/dist/components/ui/scroll-fade.d.ts.map +1 -0
  84. package/dist/index.cjs +8 -2
  85. package/dist/index.cjs.map +1 -1
  86. package/dist/index.js +7 -1
  87. package/package.json +1 -1
  88. package/src/components/chat/chat-ticket-list.tsx +8 -36
  89. package/src/components/chat/guide-welcome.tsx +5 -43
  90. package/src/components/chat/mingo-chat-history.tsx +6 -40
  91. package/src/components/chat/mingo-welcome.tsx +7 -47
  92. package/src/components/ui/data-table/data-table-header.tsx +6 -3
  93. package/src/components/ui/data-table/types.ts +6 -0
  94. package/src/components/ui/date-picker.tsx +149 -41
  95. package/src/components/ui/filter-modal.tsx +63 -2
  96. package/src/components/ui/index.ts +1 -0
  97. package/src/components/ui/scroll-fade.tsx +90 -0
  98. package/src/stories/DatePicker.stories.tsx +4 -2
  99. package/src/stories/FilterModal.stories.tsx +33 -0
  100. package/dist/chunk-J6IQQJJQ.cjs.map +0 -1
  101. package/dist/chunk-UYBNOHTC.js.map +0 -1
  102. /package/dist/{chunk-4HAVKWTJ.js.map → chunk-3TCCW7B4.js.map} +0 -0
  103. /package/dist/{chunk-I4XG5PJA.js.map → chunk-6FIVVZO2.js.map} +0 -0
  104. /package/dist/{chunk-56XCRZ7I.js.map → chunk-6VAJDGZK.js.map} +0 -0
  105. /package/dist/{chunk-MQRTT3DO.js.map → chunk-COGPHD7D.js.map} +0 -0
  106. /package/dist/{chunk-UFP45JLE.js.map → chunk-F63VMX4S.js.map} +0 -0
  107. /package/dist/{chunk-FHEIG7QO.js.map → chunk-LAFLYVZ4.js.map} +0 -0
  108. /package/dist/{chunk-463X3AW2.js.map → chunk-NHFQ2LCC.js.map} +0 -0
  109. /package/dist/{chunk-RYPCNUXA.js.map → chunk-O5QU5KAC.js.map} +0 -0
  110. /package/dist/{chunk-LAL63W5J.js.map → chunk-VMJTNXOH.js.map} +0 -0
  111. /package/dist/{chunk-6VR7BJF6.js.map → chunk-WM72XFOD.js.map} +0 -0
  112. /package/dist/{chunk-PD5MXNTJ.js.map → chunk-YPNI74ZP.js.map} +0 -0
@@ -6,7 +6,9 @@ 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'
9
10
  import { ModalV2, ModalV2Content, ModalV2Footer, ModalV2Header, ModalV2Title } from './modal-v2'
11
+ import { ScrollFadeOverlay, useScrollFade } from './scroll-fade'
10
12
  import { Skeleton } from './skeleton'
11
13
  import { SortColumnItem, type SortConfig, type SortDirection } from './sort-column-item'
12
14
  import { TagKeyValueFilter, type TagKeyConfig } from './tag-key-value-filter'
@@ -51,6 +53,24 @@ export interface FilterModalProps {
51
53
  tagFilterTitle?: string
52
54
  /** Show skeleton loading state */
53
55
  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
+ /** Minimum selectable date. */
68
+ fromDate?: Date
69
+ /** Maximum selectable date (e.g. today, to block future dates). */
70
+ toDate?: Date
71
+ /** Fired on Apply with the drafted values, and on Reset with the defaults. */
72
+ onChange: (result: DateFilterResult) => void
73
+ }
54
74
  /** Text shown in empty state title */
55
75
  emptyStateTitle?: string
56
76
  /** Text shown in empty state description */
@@ -74,6 +94,7 @@ export function FilterModal({
74
94
  selectedTags,
75
95
  onTagsChange,
76
96
  tagFilterTitle,
97
+ dateFilter,
77
98
  isLoading = false,
78
99
  emptyStateTitle = 'No filters available',
79
100
  emptyStateDescription = 'There are no filter options to display at the moment',
@@ -83,10 +104,20 @@ export function FilterModal({
83
104
  })
84
105
  const [pendingTags, setPendingTags] = useState<string[]>(selectedTags ?? [])
85
106
 
107
+ const dateDefaultSort: SortDirection = dateFilter?.defaultSort ?? 'desc'
108
+ const [draftDateSort, setDraftDateSort] = useState<SortDirection>(dateFilter?.sort ?? dateDefaultSort)
109
+ const [draftDateRange, setDraftDateRange] = useState<DateRange | undefined>(dateFilter?.range)
110
+
111
+ // Scroll shadows: bottom fade by default while content continues below,
112
+ // top fade once scrolled (same pattern as the chat lists).
113
+ const { scrollRef, fadeTop, fadeBottom, update: updateFade } = useScrollFade<HTMLDivElement>()
114
+
86
115
  useEffect(() => {
87
116
  if (isOpen) {
88
117
  setSelectedFilters({ ...currentFilters })
89
118
  setPendingTags(selectedTags ?? [])
119
+ setDraftDateSort(dateFilter?.sort ?? dateDefaultSort)
120
+ setDraftDateRange(dateFilter?.range)
90
121
  }
91
122
  }, [isOpen, JSON.stringify(currentFilters), JSON.stringify(selectedTags)])
92
123
 
@@ -109,12 +140,14 @@ export function FilterModal({
109
140
  const handleReset = () => {
110
141
  onFilterChange({})
111
142
  onTagsChange?.([])
143
+ dateFilter?.onChange({ sort: dateDefaultSort, range: undefined })
112
144
  onClose()
113
145
  }
114
146
 
115
147
  const handleApply = () => {
116
148
  onFilterChange(selectedFilters)
117
149
  onTagsChange?.(pendingTags)
150
+ dateFilter?.onChange({ sort: draftDateSort, range: draftDateRange })
118
151
  onClose()
119
152
  }
120
153
 
@@ -125,7 +158,7 @@ export function FilterModal({
125
158
  const hasSort = !!sortConfig && sortConfig.columns.length > 0
126
159
  const hasFilterGroups = filterGroups.length > 0
127
160
  const hasTagFilter = !!tagFilterKeys && tagFilterKeys.length > 0 && !!onTagsChange
128
- const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter
161
+ const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter && !dateFilter
129
162
 
130
163
  return (
131
164
  <ModalV2
@@ -143,7 +176,12 @@ export function FilterModal({
143
176
  !isEmpty && 'md:bg-ods-bg md:border md:border-ods-border md:rounded-md md:px-3',
144
177
  )}
145
178
  >
146
- <div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 md:py-3">
179
+ <div className="relative flex-1 min-h-0 flex flex-col">
180
+ <div
181
+ ref={scrollRef}
182
+ onScroll={updateFade}
183
+ className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 md:py-3"
184
+ >
147
185
  {isLoading ? (
148
186
  <>
149
187
  {[0, 1].map((group) => (
@@ -229,10 +267,33 @@ export function FilterModal({
229
267
  onTagsChange={setPendingTags}
230
268
  keysTitle={tagFilterTitle}
231
269
  />
270
+ )}
271
+ {/* Date sort + range filter */}
272
+ {dateFilter && (
273
+ <div className="flex flex-col gap-1">
274
+ <span className="text-h5 text-ods-text-secondary">
275
+ {dateFilter.title ?? 'Date'}
276
+ </span>
277
+ <DateFilterPanel
278
+ mode="range"
279
+ sort={draftDateSort}
280
+ onSortChange={setDraftDateSort}
281
+ selected={draftDateRange}
282
+ onSelect={(value) => setDraftDateRange(value as DateRange | undefined)}
283
+ fromDate={dateFilter.fromDate}
284
+ toDate={dateFilter.toDate}
285
+ className="gap-[var(--spacing-system-xxs)]"
286
+ />
287
+ </div>
232
288
  )}
233
289
  </>
234
290
  )}
235
291
  </div>
292
+
293
+ {/* Scroll shadows over the filter list edges */}
294
+ <ScrollFadeOverlay edge="top" visible={fadeTop} />
295
+ <ScrollFadeOverlay edge="bottom" visible={fadeBottom} />
296
+ </div>
236
297
  </ModalV2Content>
237
298
 
238
299
  <ModalV2Footer>
@@ -24,6 +24,7 @@ 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'
27
28
  export * from './select'
28
29
  export * from './switch'
29
30
  export * from './tag-select-dropdown'
@@ -0,0 +1,90 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../../utils/cn'
5
+
6
+ /**
7
+ * Tracks whether a scrollable element is scrolled away from its top/bottom
8
+ * edge, so edge fades ("scroll shadows") can be shown only where content
9
+ * continues. Attach `scrollRef` to the scrollable element and `update` to its
10
+ * `onScroll`; content/size changes are re-measured via ResizeObserver.
11
+ *
12
+ * Canonical fade pattern shared by chat lists and FilterModal.
13
+ */
14
+ export function useScrollFade<T extends HTMLElement = HTMLDivElement>() {
15
+ const scrollRef = React.useRef<T | null>(null)
16
+ const [fade, setFade] = React.useState({ top: false, bottom: false })
17
+
18
+ const update = React.useCallback(() => {
19
+ const el = scrollRef.current
20
+ if (!el) return
21
+ setFade((prev) => {
22
+ const top = el.scrollTop > 0
23
+ const bottom = el.scrollHeight - el.scrollTop - el.clientHeight > 1
24
+ return prev.top === top && prev.bottom === bottom ? prev : { top, bottom }
25
+ })
26
+ }, [])
27
+
28
+ React.useLayoutEffect(() => {
29
+ update()
30
+ const el = scrollRef.current
31
+ if (!el) return
32
+ const cleanups: Array<() => void> = []
33
+ if (typeof ResizeObserver !== 'undefined') {
34
+ const resizeObserver = new ResizeObserver(update)
35
+ resizeObserver.observe(el)
36
+ cleanups.push(() => resizeObserver.disconnect())
37
+ }
38
+ // ResizeObserver only sees the container box; content growth (appended
39
+ // list items) changes scrollHeight without resizing it — watch mutations.
40
+ if (typeof MutationObserver !== 'undefined') {
41
+ const mutationObserver = new MutationObserver(update)
42
+ mutationObserver.observe(el, { childList: true, subtree: true, characterData: true })
43
+ cleanups.push(() => mutationObserver.disconnect())
44
+ }
45
+ return () => {
46
+ for (const cleanup of cleanups) cleanup()
47
+ }
48
+ }, [update])
49
+
50
+ return { scrollRef, fadeTop: fade.top, fadeBottom: fade.bottom, update }
51
+ }
52
+
53
+ export interface ScrollFadeOverlayProps {
54
+ /** Which edge of the scroll container the fade sits on. */
55
+ edge: 'top' | 'bottom'
56
+ /** Whether the fade is currently shown (content continues past this edge). */
57
+ visible: boolean
58
+ /**
59
+ * CSS color the content fades into — should match the surface behind the
60
+ * list. Defaults to the page background token.
61
+ */
62
+ color?: string
63
+ className?: string
64
+ }
65
+
66
+ /**
67
+ * Edge overlay that fades scrollable content into the surface behind it.
68
+ * Render inside a `relative` wrapper around the scrollable element.
69
+ */
70
+ export function ScrollFadeOverlay({
71
+ edge,
72
+ visible,
73
+ color = 'var(--color-bg)',
74
+ className,
75
+ }: ScrollFadeOverlayProps) {
76
+ return (
77
+ <div
78
+ aria-hidden
79
+ className={cn(
80
+ 'pointer-events-none absolute inset-x-0 h-16 transition-opacity duration-150',
81
+ edge === 'top' ? 'top-0' : 'bottom-0',
82
+ visible ? 'opacity-100' : 'opacity-0',
83
+ className,
84
+ )}
85
+ style={{
86
+ background: `linear-gradient(${edge === 'top' ? '0deg' : '180deg'}, transparent 0%, ${color} 100%)`,
87
+ }}
88
+ />
89
+ )
90
+ }
@@ -775,8 +775,10 @@ const fmtDay = (d: Date) =>
775
775
 
776
776
  /**
777
777
  * DateFilterMenu — calendar-icon trigger opens a popover with a sort selector,
778
- * a date-range calendar, and Close / Apply actions. Matches the Figma
779
- * `filter-menu`. Selections are drafted internally and committed on Apply.
778
+ * a date-range calendar, and Close/Reset + Apply actions. Matches the Figma
779
+ * `filter-menu`. The sort and date selection are drafted and committed on
780
+ * Apply. With a selection present, Close becomes Reset, which clears and
781
+ * commits the empty selection.
780
782
  */
781
783
  export const FilterMenuRange: Story = {
782
784
  render: function Render() {
@@ -517,3 +517,36 @@ export const InteractiveWithTags: Story = {
517
517
  onClose: fn(),
518
518
  },
519
519
  }
520
+
521
+ /**
522
+ * Mobile "Sort and Filter" with the date sort + range filter as the last
523
+ * section (per the logs Figma). Scroll shadows: bottom fade by default while
524
+ * content continues, top fade once scrolled.
525
+ */
526
+ export const WithDateFilter: Story = {
527
+ args: {
528
+ isOpen: true,
529
+ title: 'Sort and Filter',
530
+ filterGroups: [
531
+ ...defaultFilterGroups,
532
+ {
533
+ id: 'tools',
534
+ title: 'Tool',
535
+ options: [
536
+ { id: 'fleet', label: 'Fleet MDM', count: 10 },
537
+ { id: 'mesh', label: 'MeshCentral', count: 30 },
538
+ { id: 'tactical', label: 'TacticalRMM', count: 20 },
539
+ { id: 'windows', label: 'Windows', count: 30 },
540
+ ],
541
+ },
542
+ ],
543
+ currentFilters: defaultCurrentFilters,
544
+ onFilterChange: fn(),
545
+ onClose: fn(),
546
+ dateFilter: {
547
+ title: 'Date',
548
+ sort: 'desc',
549
+ onChange: fn(),
550
+ },
551
+ },
552
+ }