@flamingo-stack/openframe-frontend-core 0.0.400 → 0.0.401

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-4HAVKWTJ.js → chunk-2B5FIJE5.js} +2 -2
  2. package/dist/{chunk-MFRP2UDK.cjs → chunk-3GXZPZHR.cjs} +26 -26
  3. package/dist/{chunk-MFRP2UDK.cjs.map → chunk-3GXZPZHR.cjs.map} +1 -1
  4. package/dist/{chunk-6VR7BJF6.js → chunk-5P5WMTVW.js} +4 -4
  5. package/dist/{chunk-556BG7SX.cjs → chunk-7YKHVAUU.cjs} +31 -31
  6. package/dist/{chunk-556BG7SX.cjs.map → chunk-7YKHVAUU.cjs.map} +1 -1
  7. package/dist/{chunk-PD5MXNTJ.js → chunk-B3IDZ6JJ.js} +4 -4
  8. package/dist/{chunk-RSFHAFZN.cjs → chunk-BI23LOMC.cjs} +37 -37
  9. package/dist/{chunk-RSFHAFZN.cjs.map → chunk-BI23LOMC.cjs.map} +1 -1
  10. package/dist/{chunk-VTSEL7CJ.cjs → chunk-CG7LIPSA.cjs} +59 -59
  11. package/dist/{chunk-VTSEL7CJ.cjs.map → chunk-CG7LIPSA.cjs.map} +1 -1
  12. package/dist/{chunk-UYBNOHTC.js → chunk-EDR55RQJ.js} +4707 -4714
  13. package/dist/chunk-EDR55RQJ.js.map +1 -0
  14. package/dist/{chunk-J6IQQJJQ.cjs → chunk-F2LZ67KM.cjs} +1165 -1172
  15. package/dist/chunk-F2LZ67KM.cjs.map +1 -0
  16. package/dist/{chunk-I4XG5PJA.js → chunk-FLDYBM25.js} +2 -2
  17. package/dist/{chunk-ABRDOCVN.cjs → chunk-FRLLKYMO.cjs} +7 -7
  18. package/dist/{chunk-ABRDOCVN.cjs.map → chunk-FRLLKYMO.cjs.map} +1 -1
  19. package/dist/{chunk-LAL63W5J.js → chunk-MZPEN27A.js} +4 -4
  20. package/dist/{chunk-OPD5V57R.cjs → chunk-PLB6Z3E4.cjs} +12 -12
  21. package/dist/{chunk-OPD5V57R.cjs.map → chunk-PLB6Z3E4.cjs.map} +1 -1
  22. package/dist/{chunk-FHEIG7QO.js → chunk-PMR74LEE.js} +2 -2
  23. package/dist/{chunk-6XQJSN7G.cjs → chunk-Q4HI3DGY.cjs} +15 -15
  24. package/dist/{chunk-6XQJSN7G.cjs.map → chunk-Q4HI3DGY.cjs.map} +1 -1
  25. package/dist/{chunk-ILWSJCWD.cjs → chunk-QDERWRT4.cjs} +26 -26
  26. package/dist/{chunk-ILWSJCWD.cjs.map → chunk-QDERWRT4.cjs.map} +1 -1
  27. package/dist/{chunk-EFTKJFEF.cjs → chunk-QOOWYEPL.cjs} +5 -5
  28. package/dist/{chunk-EFTKJFEF.cjs.map → chunk-QOOWYEPL.cjs.map} +1 -1
  29. package/dist/{chunk-463X3AW2.js → chunk-RWKVTABY.js} +2 -2
  30. package/dist/{chunk-KIC4ZEZ7.cjs → chunk-TGWHUEPD.cjs} +62 -62
  31. package/dist/{chunk-KIC4ZEZ7.cjs.map → chunk-TGWHUEPD.cjs.map} +1 -1
  32. package/dist/{chunk-UFP45JLE.js → chunk-TZDROKLI.js} +2 -2
  33. package/dist/{chunk-RYPCNUXA.js → chunk-V3NY65ET.js} +2 -2
  34. package/dist/{chunk-MQRTT3DO.js → chunk-V4YFWSB2.js} +4 -4
  35. package/dist/{chunk-QGQ76SSR.cjs → chunk-VIEWUAFE.cjs} +9 -9
  36. package/dist/{chunk-QGQ76SSR.cjs.map → chunk-VIEWUAFE.cjs.map} +1 -1
  37. package/dist/{chunk-56XCRZ7I.js → chunk-YON2QYWK.js} +2 -2
  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 +16 -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 +57 -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-2B5FIJE5.js.map} +0 -0
  103. /package/dist/{chunk-6VR7BJF6.js.map → chunk-5P5WMTVW.js.map} +0 -0
  104. /package/dist/{chunk-PD5MXNTJ.js.map → chunk-B3IDZ6JJ.js.map} +0 -0
  105. /package/dist/{chunk-I4XG5PJA.js.map → chunk-FLDYBM25.js.map} +0 -0
  106. /package/dist/{chunk-LAL63W5J.js.map → chunk-MZPEN27A.js.map} +0 -0
  107. /package/dist/{chunk-FHEIG7QO.js.map → chunk-PMR74LEE.js.map} +0 -0
  108. /package/dist/{chunk-463X3AW2.js.map → chunk-RWKVTABY.js.map} +0 -0
  109. /package/dist/{chunk-UFP45JLE.js.map → chunk-TZDROKLI.js.map} +0 -0
  110. /package/dist/{chunk-RYPCNUXA.js.map → chunk-V3NY65ET.js.map} +0 -0
  111. /package/dist/{chunk-MQRTT3DO.js.map → chunk-V4YFWSB2.js.map} +0 -0
  112. /package/dist/{chunk-56XCRZ7I.js.map → chunk-YON2QYWK.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,20 @@ 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
+ /** Fired on Apply with the drafted values, and on Reset with the defaults. */
68
+ onChange: (result: DateFilterResult) => void
69
+ }
54
70
  /** Text shown in empty state title */
55
71
  emptyStateTitle?: string
56
72
  /** Text shown in empty state description */
@@ -74,6 +90,7 @@ export function FilterModal({
74
90
  selectedTags,
75
91
  onTagsChange,
76
92
  tagFilterTitle,
93
+ dateFilter,
77
94
  isLoading = false,
78
95
  emptyStateTitle = 'No filters available',
79
96
  emptyStateDescription = 'There are no filter options to display at the moment',
@@ -83,10 +100,20 @@ export function FilterModal({
83
100
  })
84
101
  const [pendingTags, setPendingTags] = useState<string[]>(selectedTags ?? [])
85
102
 
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
+
86
111
  useEffect(() => {
87
112
  if (isOpen) {
88
113
  setSelectedFilters({ ...currentFilters })
89
114
  setPendingTags(selectedTags ?? [])
115
+ setDraftDateSort(dateFilter?.sort ?? dateDefaultSort)
116
+ setDraftDateRange(dateFilter?.range)
90
117
  }
91
118
  }, [isOpen, JSON.stringify(currentFilters), JSON.stringify(selectedTags)])
92
119
 
@@ -109,12 +136,14 @@ export function FilterModal({
109
136
  const handleReset = () => {
110
137
  onFilterChange({})
111
138
  onTagsChange?.([])
139
+ dateFilter?.onChange({ sort: dateDefaultSort, range: undefined })
112
140
  onClose()
113
141
  }
114
142
 
115
143
  const handleApply = () => {
116
144
  onFilterChange(selectedFilters)
117
145
  onTagsChange?.(pendingTags)
146
+ dateFilter?.onChange({ sort: draftDateSort, range: draftDateRange })
118
147
  onClose()
119
148
  }
120
149
 
@@ -125,7 +154,7 @@ export function FilterModal({
125
154
  const hasSort = !!sortConfig && sortConfig.columns.length > 0
126
155
  const hasFilterGroups = filterGroups.length > 0
127
156
  const hasTagFilter = !!tagFilterKeys && tagFilterKeys.length > 0 && !!onTagsChange
128
- const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter
157
+ const isEmpty = !isLoading && !hasSort && !hasFilterGroups && !hasTagFilter && !dateFilter
129
158
 
130
159
  return (
131
160
  <ModalV2
@@ -143,7 +172,12 @@ export function FilterModal({
143
172
  !isEmpty && 'md:bg-ods-bg md:border md:border-ods-border md:rounded-md md:px-3',
144
173
  )}
145
174
  >
146
- <div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 md:py-3">
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
+ >
147
181
  {isLoading ? (
148
182
  <>
149
183
  {[0, 1].map((group) => (
@@ -229,10 +263,31 @@ export function FilterModal({
229
263
  onTagsChange={setPendingTags}
230
264
  keysTitle={tagFilterTitle}
231
265
  />
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>
232
282
  )}
233
283
  </>
234
284
  )}
235
285
  </div>
286
+
287
+ {/* Scroll shadows over the filter list edges */}
288
+ <ScrollFadeOverlay edge="top" visible={fadeTop} />
289
+ <ScrollFadeOverlay edge="bottom" visible={fadeBottom} />
290
+ </div>
236
291
  </ModalV2Content>
237
292
 
238
293
  <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
+ }