@flamingo-stack/openframe-frontend-core 0.0.324-snapshot.20260625182754 → 0.0.324
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.
- package/dist/{chunk-XTV2EZHO.cjs → chunk-2YESJYW6.cjs} +31 -31
- package/dist/{chunk-XTV2EZHO.cjs.map → chunk-2YESJYW6.cjs.map} +1 -1
- package/dist/{chunk-WAYJ7VBD.cjs → chunk-3MVY3CT6.cjs} +12 -12
- package/dist/{chunk-WAYJ7VBD.cjs.map → chunk-3MVY3CT6.cjs.map} +1 -1
- package/dist/{chunk-5CWJGCOL.js → chunk-7343ZUA2.js} +4 -4
- package/dist/{chunk-AYBSXRRC.cjs → chunk-ALXY56MH.cjs} +527 -659
- package/dist/chunk-ALXY56MH.cjs.map +1 -0
- package/dist/{chunk-VUE2FSVD.js → chunk-BXBRH4VK.js} +2 -2
- package/dist/{chunk-P3IIAUCL.js → chunk-F3DK3ETT.js} +179 -311
- package/dist/chunk-F3DK3ETT.js.map +1 -0
- package/dist/{chunk-XQBK7IVF.cjs → chunk-FAJRYL3A.cjs} +15 -15
- package/dist/{chunk-XQBK7IVF.cjs.map → chunk-FAJRYL3A.cjs.map} +1 -1
- package/dist/{chunk-5YGKK636.js → chunk-FM4KGO75.js} +4 -4
- package/dist/{chunk-U74OC6PM.cjs → chunk-G5NW2RHD.cjs} +26 -26
- package/dist/{chunk-U74OC6PM.cjs.map → chunk-G5NW2RHD.cjs.map} +1 -1
- package/dist/{chunk-UJIDDEXD.js → chunk-M2ZHKEZG.js} +2 -2
- package/dist/{chunk-4USOR7KK.cjs → chunk-NPM3PSRW.cjs} +25 -25
- package/dist/{chunk-4USOR7KK.cjs.map → chunk-NPM3PSRW.cjs.map} +1 -1
- package/dist/{chunk-GYWMN4AK.js → chunk-O4SGWVZK.js} +4 -4
- package/dist/{chunk-5HRCKYFX.cjs → chunk-OZORBZ23.cjs} +9 -9
- package/dist/{chunk-5HRCKYFX.cjs.map → chunk-OZORBZ23.cjs.map} +1 -1
- package/dist/{chunk-VSUBZBG7.js → chunk-PBHJEI4L.js} +2 -2
- package/dist/{chunk-XFRQUV5M.js → chunk-PD2G6OXQ.js} +2 -2
- package/dist/{chunk-K2PS5MJH.cjs → chunk-POMQVPWA.cjs} +5 -5
- package/dist/{chunk-K2PS5MJH.cjs.map → chunk-POMQVPWA.cjs.map} +1 -1
- package/dist/{chunk-QLSUOQMU.cjs → chunk-QVWKPHD3.cjs} +7 -7
- package/dist/{chunk-QLSUOQMU.cjs.map → chunk-QVWKPHD3.cjs.map} +1 -1
- package/dist/{chunk-6GSM6K5W.js → chunk-RTPVAKRQ.js} +2 -2
- package/dist/{chunk-HS2YU2NF.js → chunk-SIQWYHZF.js} +2 -2
- package/dist/{chunk-MIVP6B6P.js → chunk-TQX5O6OU.js} +4 -4
- package/dist/{chunk-5DXWGCTW.cjs → chunk-UNFD5VKD.cjs} +56 -56
- package/dist/{chunk-5DXWGCTW.cjs.map → chunk-UNFD5VKD.cjs.map} +1 -1
- package/dist/{chunk-5GXMQPG7.cjs → chunk-VD2JHSBW.cjs} +37 -37
- package/dist/{chunk-5GXMQPG7.cjs.map → chunk-VD2JHSBW.cjs.map} +1 -1
- package/dist/{chunk-EQSGC5WT.js → chunk-WRRZKIFU.js} +2 -2
- package/dist/{chunk-5COLEZW6.cjs → chunk-X6NDSU3W.cjs} +62 -62
- package/dist/{chunk-5COLEZW6.cjs.map → chunk-X6NDSU3W.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +120 -122
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +9 -11
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/date-picker.d.ts +0 -47
- package/dist/components/ui/date-picker.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -4
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +1 -3
- package/dist/index.cjs +2 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -3
- package/package.json +1 -1
- package/src/components/ui/date-picker.tsx +9 -214
- package/src/stories/DatePicker.stories.tsx +0 -141
- package/dist/chunk-AYBSXRRC.cjs.map +0 -1
- package/dist/chunk-P3IIAUCL.js.map +0 -1
- /package/dist/{chunk-5CWJGCOL.js.map → chunk-7343ZUA2.js.map} +0 -0
- /package/dist/{chunk-VUE2FSVD.js.map → chunk-BXBRH4VK.js.map} +0 -0
- /package/dist/{chunk-5YGKK636.js.map → chunk-FM4KGO75.js.map} +0 -0
- /package/dist/{chunk-UJIDDEXD.js.map → chunk-M2ZHKEZG.js.map} +0 -0
- /package/dist/{chunk-GYWMN4AK.js.map → chunk-O4SGWVZK.js.map} +0 -0
- /package/dist/{chunk-VSUBZBG7.js.map → chunk-PBHJEI4L.js.map} +0 -0
- /package/dist/{chunk-XFRQUV5M.js.map → chunk-PD2G6OXQ.js.map} +0 -0
- /package/dist/{chunk-6GSM6K5W.js.map → chunk-RTPVAKRQ.js.map} +0 -0
- /package/dist/{chunk-HS2YU2NF.js.map → chunk-SIQWYHZF.js.map} +0 -0
- /package/dist/{chunk-MIVP6B6P.js.map → chunk-TQX5O6OU.js.map} +0 -0
- /package/dist/{chunk-EQSGC5WT.js.map → chunk-WRRZKIFU.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as Popover from "@radix-ui/react-popover";
|
|
4
|
-
import {
|
|
4
|
+
import { Calendar, ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import {
|
|
7
7
|
DayPicker,
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
SelectTrigger,
|
|
20
20
|
SelectValue,
|
|
21
21
|
} from "./select";
|
|
22
|
-
import type { SortDirection } from "./sort-column-item";
|
|
23
22
|
|
|
24
23
|
// ============================================================================
|
|
25
24
|
// Types
|
|
@@ -122,12 +121,6 @@ interface DatePickerCalendarProps {
|
|
|
122
121
|
fromDate?: Date;
|
|
123
122
|
toDate?: Date;
|
|
124
123
|
locale?: DayPickerProps["locale"];
|
|
125
|
-
/**
|
|
126
|
-
* When true the calendar fills its container width and day cells flex to
|
|
127
|
-
* fit (instead of the fixed 40px cells). Used by DateFilterMenu so the grid
|
|
128
|
-
* lines up with the surrounding controls per the Figma filter-menu.
|
|
129
|
-
*/
|
|
130
|
-
fluid?: boolean;
|
|
131
124
|
}
|
|
132
125
|
|
|
133
126
|
function DatePickerCalendar({
|
|
@@ -138,7 +131,6 @@ function DatePickerCalendar({
|
|
|
138
131
|
fromDate,
|
|
139
132
|
toDate,
|
|
140
133
|
locale,
|
|
141
|
-
fluid = false,
|
|
142
134
|
}: DatePickerCalendarProps) {
|
|
143
135
|
const today = new Date();
|
|
144
136
|
|
|
@@ -182,35 +174,28 @@ function DatePickerCalendar({
|
|
|
182
174
|
onSelect(completed);
|
|
183
175
|
};
|
|
184
176
|
|
|
185
|
-
// Fixed 40px cells by default; in fluid mode cells flex to fill the width.
|
|
186
|
-
const cellOuter = fluid ? "flex-1 aspect-square min-w-0" : "size-10";
|
|
187
|
-
const cellInner = fluid ? "size-full" : "size-10";
|
|
188
|
-
|
|
189
177
|
const classNames: DayPickerProps["classNames"] = {
|
|
190
|
-
root:
|
|
178
|
+
root: "p-4 date-picker-calendar",
|
|
191
179
|
months: "flex gap-8",
|
|
192
|
-
month:
|
|
180
|
+
month: "flex flex-col gap-2",
|
|
193
181
|
month_caption: "hidden",
|
|
194
182
|
nav: "hidden",
|
|
195
|
-
month_grid:
|
|
183
|
+
month_grid: "border-collapse",
|
|
196
184
|
weekdays: "flex",
|
|
197
185
|
weekday: cn(
|
|
198
|
-
|
|
199
|
-
"flex items-center justify-center",
|
|
186
|
+
"size-10 flex items-center justify-center",
|
|
200
187
|
"text-[14px] font-medium leading-5 text-ods-text-secondary"
|
|
201
188
|
),
|
|
202
189
|
week: "flex",
|
|
203
190
|
day: cn(
|
|
204
|
-
|
|
205
|
-
"flex items-center justify-center",
|
|
191
|
+
"size-10 flex items-center justify-center",
|
|
206
192
|
"text-h4 text-ods-text-primary",
|
|
207
193
|
"cursor-pointer",
|
|
208
194
|
"transition-colors duration-150",
|
|
209
195
|
"hover:bg-ods-bg-surface hover:rounded-[6px]"
|
|
210
196
|
),
|
|
211
197
|
day_button: cn(
|
|
212
|
-
|
|
213
|
-
"flex items-center justify-center",
|
|
198
|
+
"size-10 flex items-center justify-center",
|
|
214
199
|
"cursor-pointer bg-transparent border-none outline-none",
|
|
215
200
|
"text-inherit font-inherit"
|
|
216
201
|
),
|
|
@@ -306,7 +291,7 @@ function DatePickerCalendar({
|
|
|
306
291
|
|
|
307
292
|
if (mode === "single") {
|
|
308
293
|
return (
|
|
309
|
-
<div className=
|
|
294
|
+
<div className="bg-ods-card border border-ods-border rounded-[6px] overflow-hidden">
|
|
310
295
|
<div className="flex items-center justify-between px-4 pt-4">
|
|
311
296
|
<CalendarNavButton
|
|
312
297
|
direction="left"
|
|
@@ -343,7 +328,7 @@ function DatePickerCalendar({
|
|
|
343
328
|
// Range mode
|
|
344
329
|
return (
|
|
345
330
|
<div
|
|
346
|
-
className=
|
|
331
|
+
className="bg-ods-card border border-ods-border rounded-md overflow-hidden"
|
|
347
332
|
onMouseLeave={() => setHoveredDate(undefined)}
|
|
348
333
|
>
|
|
349
334
|
<style>{rangeStyles}</style>
|
|
@@ -981,196 +966,6 @@ export function DatePickerInputSimple({
|
|
|
981
966
|
);
|
|
982
967
|
}
|
|
983
968
|
|
|
984
|
-
// ============================================================================
|
|
985
|
-
// DateFilterMenu Component (sort + calendar filter popover from Figma)
|
|
986
|
-
// ============================================================================
|
|
987
|
-
|
|
988
|
-
export interface DateFilterResult {
|
|
989
|
-
/** Selected sort direction */
|
|
990
|
-
sort: SortDirection;
|
|
991
|
-
/** Selected single date (mode === "single") */
|
|
992
|
-
date?: Date;
|
|
993
|
-
/** Selected date range (mode === "range") */
|
|
994
|
-
range?: DateRange;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
export interface DateFilterMenuProps {
|
|
998
|
-
/** Selection mode for the calendar. Defaults to "range". */
|
|
999
|
-
mode?: DatePickerMode;
|
|
1000
|
-
/** Current (applied) sort direction. Defaults to "desc". */
|
|
1001
|
-
sort?: SortDirection;
|
|
1002
|
-
/** Current (applied) single date — used when mode === "single". */
|
|
1003
|
-
date?: Date;
|
|
1004
|
-
/** Current (applied) range — used when mode === "range". */
|
|
1005
|
-
range?: DateRange;
|
|
1006
|
-
/** Fired when the user presses Apply with the drafted selection. */
|
|
1007
|
-
onApply?: (result: DateFilterResult) => void;
|
|
1008
|
-
/** Fired when the menu closes (Close button, outside click, Esc). */
|
|
1009
|
-
onClose?: () => void;
|
|
1010
|
-
/** Disable the trigger. */
|
|
1011
|
-
disabled?: boolean;
|
|
1012
|
-
/** Minimum selectable date. */
|
|
1013
|
-
fromDate?: Date;
|
|
1014
|
-
/** Maximum selectable date. */
|
|
1015
|
-
toDate?: Date;
|
|
1016
|
-
/** Locale for the calendar. */
|
|
1017
|
-
locale?: DayPickerProps["locale"];
|
|
1018
|
-
/** Popover alignment relative to the trigger. */
|
|
1019
|
-
align?: "start" | "center" | "end";
|
|
1020
|
-
/** Label for the ascending sort option. */
|
|
1021
|
-
ascLabel?: string;
|
|
1022
|
-
/** Label for the descending sort option. */
|
|
1023
|
-
descLabel?: string;
|
|
1024
|
-
/** Additional class name for the trigger button. */
|
|
1025
|
-
className?: string;
|
|
1026
|
-
/** Accessible label for the trigger. */
|
|
1027
|
-
"aria-label"?: string;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
/**
|
|
1031
|
-
* DateFilterMenu — a calendar-icon-triggered popover combining a sort-direction
|
|
1032
|
-
* selector and a date / date-range calendar, with Close and Apply actions.
|
|
1033
|
-
* Selections are drafted internally and only committed via `onApply`.
|
|
1034
|
-
*/
|
|
1035
|
-
export function DateFilterMenu({
|
|
1036
|
-
mode = "range",
|
|
1037
|
-
sort = "desc",
|
|
1038
|
-
date,
|
|
1039
|
-
range,
|
|
1040
|
-
onApply,
|
|
1041
|
-
onClose,
|
|
1042
|
-
disabled = false,
|
|
1043
|
-
fromDate,
|
|
1044
|
-
toDate,
|
|
1045
|
-
locale,
|
|
1046
|
-
align = "start",
|
|
1047
|
-
ascLabel = "Sort by Ascending",
|
|
1048
|
-
descLabel = "Sort by Descending",
|
|
1049
|
-
className,
|
|
1050
|
-
"aria-label": ariaLabel = "Open date filter",
|
|
1051
|
-
}: DateFilterMenuProps) {
|
|
1052
|
-
const [open, setOpen] = React.useState(false);
|
|
1053
|
-
|
|
1054
|
-
// Drafted selection — initialized from props each time the menu opens.
|
|
1055
|
-
const [draftSort, setDraftSort] = React.useState<SortDirection>(sort);
|
|
1056
|
-
const [draftSelected, setDraftSelected] = React.useState<
|
|
1057
|
-
Date | DateRange | undefined
|
|
1058
|
-
>(mode === "single" ? date : range);
|
|
1059
|
-
|
|
1060
|
-
const resetDraft = React.useCallback(() => {
|
|
1061
|
-
setDraftSort(sort);
|
|
1062
|
-
setDraftSelected(mode === "single" ? date : range);
|
|
1063
|
-
}, [sort, date, range, mode]);
|
|
1064
|
-
|
|
1065
|
-
const handleOpenChange = (next: boolean) => {
|
|
1066
|
-
if (next) {
|
|
1067
|
-
resetDraft();
|
|
1068
|
-
} else {
|
|
1069
|
-
onClose?.();
|
|
1070
|
-
}
|
|
1071
|
-
setOpen(next);
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
const handleApply = () => {
|
|
1075
|
-
const result: DateFilterResult =
|
|
1076
|
-
mode === "single"
|
|
1077
|
-
? { sort: draftSort, date: draftSelected as Date | undefined }
|
|
1078
|
-
: { sort: draftSort, range: draftSelected as DateRange | undefined };
|
|
1079
|
-
onApply?.(result);
|
|
1080
|
-
setOpen(false);
|
|
1081
|
-
};
|
|
1082
|
-
|
|
1083
|
-
const handleClose = () => {
|
|
1084
|
-
onClose?.();
|
|
1085
|
-
setOpen(false);
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
return (
|
|
1089
|
-
<Popover.Root open={open} onOpenChange={handleOpenChange}>
|
|
1090
|
-
<Popover.Trigger asChild>
|
|
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
|
-
/>
|
|
1100
|
-
</Popover.Trigger>
|
|
1101
|
-
|
|
1102
|
-
<Popover.Portal>
|
|
1103
|
-
<Popover.Content
|
|
1104
|
-
className={cn(
|
|
1105
|
-
"z-[9999] w-80 max-w-[calc(100vw-2rem)]",
|
|
1106
|
-
"flex flex-col gap-4 rounded-[6px] border border-ods-border bg-ods-bg p-4",
|
|
1107
|
-
"animate-in fade-in-0 zoom-in-95",
|
|
1108
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
1109
|
-
"data-[side=bottom]:slide-in-from-top-2",
|
|
1110
|
-
"data-[side=top]:slide-in-from-bottom-2"
|
|
1111
|
-
)}
|
|
1112
|
-
sideOffset={8}
|
|
1113
|
-
align={align}
|
|
1114
|
-
>
|
|
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
|
|
1139
|
-
mode={mode}
|
|
1140
|
-
selected={draftSelected}
|
|
1141
|
-
onSelect={setDraftSelected}
|
|
1142
|
-
numberOfMonths={1}
|
|
1143
|
-
fromDate={fromDate}
|
|
1144
|
-
toDate={toDate}
|
|
1145
|
-
locale={locale}
|
|
1146
|
-
fluid
|
|
1147
|
-
/>
|
|
1148
|
-
|
|
1149
|
-
{/* Actions */}
|
|
1150
|
-
<div className="flex w-full items-stretch gap-4">
|
|
1151
|
-
<Button
|
|
1152
|
-
type="button"
|
|
1153
|
-
variant="outline"
|
|
1154
|
-
fullWidth
|
|
1155
|
-
onClick={handleClose}
|
|
1156
|
-
>
|
|
1157
|
-
Close
|
|
1158
|
-
</Button>
|
|
1159
|
-
<Button
|
|
1160
|
-
type="button"
|
|
1161
|
-
variant="accent"
|
|
1162
|
-
fullWidth
|
|
1163
|
-
onClick={handleApply}
|
|
1164
|
-
>
|
|
1165
|
-
Apply
|
|
1166
|
-
</Button>
|
|
1167
|
-
</div>
|
|
1168
|
-
</Popover.Content>
|
|
1169
|
-
</Popover.Portal>
|
|
1170
|
-
</Popover.Root>
|
|
1171
|
-
);
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
969
|
// ============================================================================
|
|
1175
970
|
// Exports
|
|
1176
971
|
// ============================================================================
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import {
|
|
4
|
-
DateFilterMenu,
|
|
5
|
-
type DateFilterResult,
|
|
6
4
|
DatePicker,
|
|
7
5
|
DatePickerInput,
|
|
8
6
|
DatePickerInputSimple,
|
|
9
7
|
type DatePickerBaseProps,
|
|
10
8
|
type DateRange,
|
|
11
9
|
} from "../components/ui/date-picker";
|
|
12
|
-
import type { SortDirection } from "../components/ui/sort-column-item";
|
|
13
10
|
|
|
14
11
|
// Using a simplified type for Storybook since DatePicker uses discriminated union
|
|
15
12
|
type DatePickerStoryProps = DatePickerBaseProps & {
|
|
@@ -762,141 +759,3 @@ export const SimpleInputDateOnly: Story = {
|
|
|
762
759
|
),
|
|
763
760
|
],
|
|
764
761
|
};
|
|
765
|
-
|
|
766
|
-
// ============================================================================
|
|
767
|
-
// DateFilterMenu Stories (sort + calendar filter popover from Figma)
|
|
768
|
-
// ============================================================================
|
|
769
|
-
|
|
770
|
-
const fmtSort = (sort: SortDirection) =>
|
|
771
|
-
sort === "asc" ? "Ascending" : "Descending";
|
|
772
|
-
|
|
773
|
-
const fmtDay = (d: Date) =>
|
|
774
|
-
d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
775
|
-
|
|
776
|
-
/**
|
|
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.
|
|
780
|
-
*/
|
|
781
|
-
export const FilterMenuRange: Story = {
|
|
782
|
-
render: function Render() {
|
|
783
|
-
const [applied, setApplied] = useState<DateFilterResult | null>(null);
|
|
784
|
-
|
|
785
|
-
const summary = () => {
|
|
786
|
-
if (!applied) return "Nothing applied yet";
|
|
787
|
-
const { sort, range } = applied;
|
|
788
|
-
const r = range?.from
|
|
789
|
-
? range.to
|
|
790
|
-
? `${fmtDay(range.from)} - ${fmtDay(range.to)}`
|
|
791
|
-
: fmtDay(range.from)
|
|
792
|
-
: "all dates";
|
|
793
|
-
return `Sort ${fmtSort(sort)} · ${r}`;
|
|
794
|
-
};
|
|
795
|
-
|
|
796
|
-
return (
|
|
797
|
-
<div className="flex flex-col gap-4">
|
|
798
|
-
<div className="flex items-center gap-3">
|
|
799
|
-
<span className="text-h4 text-ods-text-primary">Last activity</span>
|
|
800
|
-
<DateFilterMenu mode="range" onApply={setApplied} />
|
|
801
|
-
</div>
|
|
802
|
-
<div className="text-[14px] text-ods-text-secondary p-3 bg-ods-bg rounded-lg border border-ods-border">
|
|
803
|
-
<span className="text-ods-text-primary">Applied:</span> {summary()}
|
|
804
|
-
</div>
|
|
805
|
-
</div>
|
|
806
|
-
);
|
|
807
|
-
},
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
/**
|
|
811
|
-
* DateFilterMenu in single-date mode.
|
|
812
|
-
*/
|
|
813
|
-
export const FilterMenuSingle: Story = {
|
|
814
|
-
render: function Render() {
|
|
815
|
-
const [applied, setApplied] = useState<DateFilterResult | null>(null);
|
|
816
|
-
|
|
817
|
-
return (
|
|
818
|
-
<div className="flex flex-col gap-4">
|
|
819
|
-
<div className="flex items-center gap-3">
|
|
820
|
-
<span className="text-h4 text-ods-text-primary">Date & time</span>
|
|
821
|
-
<DateFilterMenu mode="single" sort="asc" onApply={setApplied} />
|
|
822
|
-
</div>
|
|
823
|
-
<div className="text-[14px] text-ods-text-secondary p-3 bg-ods-bg rounded-lg border border-ods-border">
|
|
824
|
-
<span className="text-ods-text-primary">Applied:</span>{" "}
|
|
825
|
-
{applied
|
|
826
|
-
? `Sort ${fmtSort(applied.sort)} · ${
|
|
827
|
-
applied.date ? fmtDay(applied.date) : "all dates"
|
|
828
|
-
}`
|
|
829
|
-
: "Nothing applied yet"}
|
|
830
|
-
</div>
|
|
831
|
-
</div>
|
|
832
|
-
);
|
|
833
|
-
},
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
/**
|
|
837
|
-
* DateFilterMenu wired to filter + sort a small table — mirrors the intended
|
|
838
|
-
* usage in table columns (organizations last-activity, scripts schedules,
|
|
839
|
-
* logs). Apply re-filters and re-sorts the rows by date.
|
|
840
|
-
*/
|
|
841
|
-
export const FilterMenuWithTable: Story = {
|
|
842
|
-
render: function Render() {
|
|
843
|
-
type Row = { id: string; activity: Date };
|
|
844
|
-
const rows: Row[] = [
|
|
845
|
-
{ id: "Acme Corp", activity: new Date(2026, 4, 2) },
|
|
846
|
-
{ id: "Globex", activity: new Date(2026, 4, 6) },
|
|
847
|
-
{ id: "Initech", activity: new Date(2026, 4, 11) },
|
|
848
|
-
{ id: "Umbrella", activity: new Date(2026, 4, 18) },
|
|
849
|
-
{ id: "Stark Ind.", activity: new Date(2026, 4, 24) },
|
|
850
|
-
];
|
|
851
|
-
|
|
852
|
-
const [filter, setFilter] = useState<DateFilterResult>({
|
|
853
|
-
sort: "desc",
|
|
854
|
-
range: undefined,
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
const visible = rows
|
|
858
|
-
.filter((row) => {
|
|
859
|
-
const { from, to } = filter.range ?? {};
|
|
860
|
-
if (from && row.activity < from) return false;
|
|
861
|
-
if (to && row.activity > to) return false;
|
|
862
|
-
return true;
|
|
863
|
-
})
|
|
864
|
-
.sort((a, b) =>
|
|
865
|
-
filter.sort === "asc"
|
|
866
|
-
? a.activity.getTime() - b.activity.getTime()
|
|
867
|
-
: b.activity.getTime() - a.activity.getTime()
|
|
868
|
-
);
|
|
869
|
-
|
|
870
|
-
return (
|
|
871
|
-
<div className="flex flex-col gap-3" style={{ width: 360 }}>
|
|
872
|
-
<div className="flex items-center justify-between">
|
|
873
|
-
<span className="text-h4 text-ods-text-primary">Organizations</span>
|
|
874
|
-
<DateFilterMenu
|
|
875
|
-
mode="range"
|
|
876
|
-
sort={filter.sort}
|
|
877
|
-
range={filter.range}
|
|
878
|
-
onApply={setFilter}
|
|
879
|
-
/>
|
|
880
|
-
</div>
|
|
881
|
-
<div className="rounded-lg border border-ods-border overflow-hidden">
|
|
882
|
-
{visible.map((row) => (
|
|
883
|
-
<div
|
|
884
|
-
key={row.id}
|
|
885
|
-
className="flex items-center justify-between px-3 py-2 border-b border-ods-border last:border-b-0"
|
|
886
|
-
>
|
|
887
|
-
<span className="text-ods-text-primary text-[14px]">{row.id}</span>
|
|
888
|
-
<span className="text-ods-text-secondary text-[14px]">
|
|
889
|
-
{fmtDay(row.activity)}
|
|
890
|
-
</span>
|
|
891
|
-
</div>
|
|
892
|
-
))}
|
|
893
|
-
{visible.length === 0 && (
|
|
894
|
-
<div className="px-3 py-4 text-center text-ods-text-secondary text-[14px]">
|
|
895
|
-
No results in range
|
|
896
|
-
</div>
|
|
897
|
-
)}
|
|
898
|
-
</div>
|
|
899
|
-
</div>
|
|
900
|
-
);
|
|
901
|
-
},
|
|
902
|
-
};
|