torch-glare 2.4.0 → 2.4.1
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/README.md +8 -0
- package/apps/lib/components/ActionButton.tsx +7 -12
- package/apps/lib/components/ActionsGroup.tsx +4 -15
- package/apps/lib/components/AlertDialog.tsx +173 -191
- package/apps/lib/components/Avatar.tsx +13 -16
- package/apps/lib/components/Badge.tsx +1 -3
- package/apps/lib/components/BadgeField.tsx +76 -71
- package/apps/lib/components/Breadcrumb.tsx +49 -85
- package/apps/lib/components/Button.tsx +20 -19
- package/apps/lib/components/ButtonGroup.tsx +31 -41
- package/apps/lib/components/Calendar.tsx +51 -29
- package/apps/lib/components/Card.tsx +2 -13
- package/apps/lib/components/ChartBlockTool.ts +17 -52
- package/apps/lib/components/Checkbox.tsx +9 -16
- package/apps/lib/components/ContextMenu.tsx +97 -96
- package/apps/lib/components/CountBadge.tsx +4 -6
- package/apps/lib/components/DataTable.tsx +196 -216
- package/apps/lib/components/DataViews/DataViewRadio.tsx +1 -6
- package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +13 -47
- package/apps/lib/components/DataViews/DataViewsHeader.tsx +1 -3
- package/apps/lib/components/DataViews/DataViewsLayout.tsx +5 -12
- package/apps/lib/components/DataViews/FilterPanel.tsx +155 -149
- package/apps/lib/components/DataViews/HeaderSearch.tsx +1 -5
- package/apps/lib/components/DataViews/InboxView.tsx +22 -68
- package/apps/lib/components/DataViews/InboxViewCard.tsx +68 -77
- package/apps/lib/components/DataViews/KanbanView.tsx +8 -25
- package/apps/lib/components/DataViews/PanelControls.tsx +3 -13
- package/apps/lib/components/DataViews/SettingsPanel.tsx +83 -89
- package/apps/lib/components/DataViews/TableView.tsx +8 -28
- package/apps/lib/components/DataViews/TreeView.tsx +12 -40
- package/apps/lib/components/DataViews/badgeAdapter.ts +32 -28
- package/apps/lib/components/DataViews/fieldRenderers.tsx +106 -141
- package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +30 -23
- package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +39 -32
- package/apps/lib/components/DataViews/filters/PresetChips.tsx +15 -15
- package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +69 -58
- package/apps/lib/components/DataViews/index.ts +26 -26
- package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +12 -16
- package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +28 -31
- package/apps/lib/components/DataViews/types.ts +104 -115
- package/apps/lib/components/DatePicker.tsx +198 -196
- package/apps/lib/components/Dialog.tsx +92 -105
- package/apps/lib/components/Divider.tsx +21 -24
- package/apps/lib/components/Drawer.tsx +79 -139
- package/apps/lib/components/DropdownMenu.tsx +131 -171
- package/apps/lib/components/FieldHint.tsx +4 -11
- package/apps/lib/components/Form.tsx +146 -155
- package/apps/lib/components/FormStepper.tsx +9 -39
- package/apps/lib/components/HeaderBar.tsx +1 -9
- package/apps/lib/components/ImageAttachment.tsx +111 -73
- package/apps/lib/components/InnerLabelField.tsx +20 -16
- package/apps/lib/components/Input.tsx +171 -172
- package/apps/lib/components/InputField.tsx +44 -45
- package/apps/lib/components/InputOTP.tsx +68 -69
- package/apps/lib/components/Label.tsx +31 -35
- package/apps/lib/components/LabelField.tsx +4 -5
- package/apps/lib/components/LabeledCheckBox.tsx +8 -12
- package/apps/lib/components/LabeledRadio.tsx +7 -5
- package/apps/lib/components/LinkButton.tsx +2 -9
- package/apps/lib/components/LoginButton.tsx +4 -9
- package/apps/lib/components/PasswordLevel.tsx +5 -5
- package/apps/lib/components/Popover.tsx +15 -33
- package/apps/lib/components/ProfileMenu.tsx +67 -72
- package/apps/lib/components/Radio.tsx +1 -5
- package/apps/lib/components/RadioCard.tsx +9 -28
- package/apps/lib/components/ScrollArea.tsx +45 -43
- package/apps/lib/components/SearchField.tsx +49 -39
- package/apps/lib/components/SearchableSelect.tsx +8 -14
- package/apps/lib/components/SearchableTable.tsx +108 -118
- package/apps/lib/components/SearchableTree.tsx +469 -0
- package/apps/lib/components/SearchableTreeDialog.tsx +493 -0
- package/apps/lib/components/SectionBlock.tsx +4 -16
- package/apps/lib/components/Select.tsx +53 -80
- package/apps/lib/components/SimpleSelect.tsx +196 -187
- package/apps/lib/components/Skeleton.tsx +9 -12
- package/apps/lib/components/SlideDatePicker.tsx +215 -166
- package/apps/lib/components/SpinLoading.tsx +33 -49
- package/apps/lib/components/Stepper.tsx +13 -56
- package/apps/lib/components/Switch.tsx +8 -10
- package/apps/lib/components/TabFormItem.tsx +3 -12
- package/apps/lib/components/TabSwitch.tsx +7 -34
- package/apps/lib/components/Table.tsx +60 -75
- package/apps/lib/components/TableDnDWrapper.ts +15 -48
- package/apps/lib/components/TextEditor.tsx +93 -104
- package/apps/lib/components/Textarea.tsx +8 -12
- package/apps/lib/components/Timeline.tsx +21 -41
- package/apps/lib/components/Toast.tsx +83 -83
- package/apps/lib/components/Toggle.tsx +112 -114
- package/apps/lib/components/ToggleButton.tsx +1 -1
- package/apps/lib/components/Tooltip.tsx +24 -19
- package/apps/lib/components/TransparentLabel.tsx +61 -62
- package/apps/lib/components/TreeDropDown.tsx +86 -52
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +15 -48
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +16 -21
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +9 -20
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +3 -3
- package/apps/lib/components/TreeFolder/icons.tsx +14 -17
- package/apps/lib/components/TreeFolder/index.ts +15 -8
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +47 -52
- package/apps/lib/components/TreeFolder/types.ts +32 -32
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +117 -123
- package/apps/lib/hooks/useActiveTreeItem.ts +52 -55
- package/apps/lib/hooks/useClickOutside.ts +21 -20
- package/apps/lib/hooks/useDataViewsState.ts +86 -80
- package/apps/lib/hooks/useIsMobile.ts +12 -12
- package/apps/lib/hooks/useResize.ts +54 -65
- package/apps/lib/hooks/useTagSelection.ts +201 -201
- package/apps/lib/layouts/CNLayout.tsx +181 -140
- package/apps/lib/layouts/DataViewCard.tsx +4 -18
- package/apps/lib/layouts/FieldSection.tsx +2 -6
- package/apps/lib/layouts/TreeSubLayout.tsx +24 -38
- package/apps/lib/providers/ThemeProvider.tsx +8 -23
- package/apps/lib/registry.json +1004 -0
- package/apps/lib/tsconfig.json +9 -0
- package/apps/lib/utils/cn.ts +1 -1
- package/apps/lib/utils/dataViews/columnUtils.ts +46 -44
- package/apps/lib/utils/dataViews/fieldUtils.ts +70 -71
- package/apps/lib/utils/dataViews/nestedDataUtils.tsx +129 -122
- package/apps/lib/utils/dataViews/pathUtils.ts +74 -67
- package/apps/lib/utils/dataViews/rangeUtils.ts +121 -112
- package/apps/lib/utils/dataViews/treeUtils.ts +204 -211
- package/apps/lib/utils/dateFormat.ts +64 -54
- package/apps/lib/utils/markdownParser.ts +2 -7
- package/apps/lib/utils/resize.ts +21 -25
- package/apps/lib/utils/types.ts +13 -13
- package/dist/bin/index.js +8 -2
- package/dist/bin/index.js.map +1 -1
- package/dist/src/shared/getAvailableFiles.d.ts.map +1 -1
- package/dist/src/shared/getAvailableFiles.js +6 -1
- package/dist/src/shared/getAvailableFiles.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +7 -0
- package/dist/src/shared/loadRegistry.d.ts.map +1 -0
- package/dist/src/shared/loadRegistry.js +31 -0
- package/dist/src/shared/loadRegistry.js.map +1 -0
- package/dist/src/shared/resolveInstallPlan.d.ts +12 -0
- package/dist/src/shared/resolveInstallPlan.d.ts.map +1 -0
- package/dist/src/shared/resolveInstallPlan.js +39 -0
- package/dist/src/shared/resolveInstallPlan.js.map +1 -0
- package/dist/src/types/main.d.ts +24 -0
- package/dist/src/types/main.d.ts.map +1 -1
- package/docs/README.md +10 -2
- package/docs/components/action-button.md +30 -24
- package/docs/components/actions-group.md +26 -20
- package/docs/components/alert-dialog.md +45 -45
- package/docs/components/avatar.md +6 -2
- package/docs/components/badge-field.md +6 -2
- package/docs/components/badge.md +7 -1
- package/docs/components/breadcrumb.md +15 -40
- package/docs/components/button-group.md +13 -10
- package/docs/components/button.md +58 -33
- package/docs/components/calendar.md +26 -19
- package/docs/components/card.md +19 -16
- package/docs/components/chart-block-tool.md +128 -0
- package/docs/components/checkbox.md +16 -8
- package/docs/components/cn-layout.md +7 -1
- package/docs/components/context-menu.md +18 -25
- package/docs/components/count-badge.md +7 -1
- package/docs/components/data-table.md +45 -33
- package/docs/components/data-views-config-panel.md +15 -11
- package/docs/components/data-views-layout.md +13 -16
- package/docs/components/date-picker.md +30 -23
- package/docs/components/dialog.md +43 -42
- package/docs/components/divider.md +10 -4
- package/docs/components/drawer.md +8 -33
- package/docs/components/dropdown-menu.md +25 -34
- package/docs/components/field-hint.md +37 -25
- package/docs/components/field-section.md +9 -3
- package/docs/components/form-stepper.md +6 -0
- package/docs/components/form.md +17 -20
- package/docs/components/header-bar.md +6 -0
- package/docs/components/image-attachment.md +33 -41
- package/docs/components/inbox-view.md +16 -5
- package/docs/components/inner-label-field.md +34 -24
- package/docs/components/input-field.md +18 -10
- package/docs/components/input-otp.md +14 -11
- package/docs/components/input.md +22 -16
- package/docs/components/kanban-view.md +15 -5
- package/docs/components/label-field.md +32 -22
- package/docs/components/label.md +43 -32
- package/docs/components/labeled-check-box.md +16 -8
- package/docs/components/labeled-radio.md +18 -11
- package/docs/components/link-button.md +7 -1
- package/docs/components/login-button.md +8 -2
- package/docs/components/password-level.md +37 -24
- package/docs/components/popover.md +26 -23
- package/docs/components/profile-menu.md +38 -27
- package/docs/components/radio-card.md +16 -9
- package/docs/components/radio.md +16 -8
- package/docs/components/scroll-area.md +6 -2
- package/docs/components/search-field.md +14 -6
- package/docs/components/searchable-select.md +19 -14
- package/docs/components/searchable-table.md +18 -13
- package/docs/components/searchable-tree-dialog.md +190 -0
- package/docs/components/searchable-tree.md +200 -0
- package/docs/components/section-block.md +12 -4
- package/docs/components/select.md +23 -18
- package/docs/components/simple-select.md +14 -10
- package/docs/components/skeleton.md +43 -33
- package/docs/components/slide-date-picker.md +37 -29
- package/docs/components/spin-loading.md +32 -23
- package/docs/components/stepper.md +6 -0
- package/docs/components/switch.md +14 -6
- package/docs/components/tab-form-item.md +7 -1
- package/docs/components/tab-switch.md +13 -6
- package/docs/components/table-dnd-wrapper.md +105 -0
- package/docs/components/table-view.md +15 -5
- package/docs/components/table.md +28 -28
- package/docs/components/text-editor.md +19 -13
- package/docs/components/textarea.md +15 -7
- package/docs/components/timeline.md +6 -0
- package/docs/components/toast.md +35 -26
- package/docs/components/toggle-button.md +24 -21
- package/docs/components/toggle.md +23 -15
- package/docs/components/tooltip.md +42 -30
- package/docs/components/transparent-label.md +29 -20
- package/docs/components/tree-drop-down.md +31 -23
- package/docs/components/tree-sub-layout.md +6 -2
- package/docs/components/tree-view.md +16 -5
- package/docs/explanation/architecture.md +54 -0
- package/docs/explanation/design-system.md +52 -0
- package/docs/how-to/data-views-from-backend-response.md +6 -4
- package/docs/how-to/guides.md +24 -18
- package/docs/migration/changelog.md +38 -0
- package/docs/reference/cli.md +87 -0
- package/docs/reference/components.md +11 -12
- package/docs/reference/hooks.md +8 -8
- package/docs/reference/providers.md +22 -22
- package/docs/reference/tailwind-plugins.md +2 -2
- package/docs/reference/theme.md +85 -0
- package/docs/reference/types.md +4 -4
- package/docs/reference/utilities.md +19 -26
- package/docs/tutorials/building-first-form.md +20 -13
- package/docs/tutorials/component-composition.md +34 -50
- package/docs/tutorials/getting-started.md +2 -2
- package/docs/tutorials/theming-basics.md +13 -10
- package/package.json +31 -3
- package/dist/src/commands/block.d.ts +0 -5
- package/dist/src/commands/block.d.ts.map +0 -1
- package/dist/src/commands/block.js +0 -255
- package/dist/src/commands/block.js.map +0 -1
- package/dist/src/shared/configureFonts.d.ts +0 -6
- package/dist/src/shared/configureFonts.d.ts.map +0 -1
- package/dist/src/shared/configureFonts.js +0 -106
- package/dist/src/shared/configureFonts.js.map +0 -1
- package/dist/src/shared/configureGlobalCss.d.ts +0 -6
- package/dist/src/shared/configureGlobalCss.d.ts.map +0 -1
- package/dist/src/shared/configureGlobalCss.js +0 -154
- package/dist/src/shared/configureGlobalCss.js.map +0 -1
- package/dist/src/shared/configureTailwind.d.ts +0 -7
- package/dist/src/shared/configureTailwind.d.ts.map +0 -1
- package/dist/src/shared/configureTailwind.js +0 -163
- package/dist/src/shared/configureTailwind.js.map +0 -1
- package/dist/src/shared/detectFramework.d.ts +0 -23
- package/dist/src/shared/detectFramework.d.ts.map +0 -1
- package/dist/src/shared/detectFramework.js +0 -119
- package/dist/src/shared/detectFramework.js.map +0 -1
- package/dist/src/shared/installBaseUtils.d.ts +0 -5
- package/dist/src/shared/installBaseUtils.d.ts.map +0 -1
- package/dist/src/shared/installBaseUtils.js +0 -79
- package/dist/src/shared/installBaseUtils.js.map +0 -1
- package/dist/src/shared/resolveAliases.d.ts +0 -24
- package/dist/src/shared/resolveAliases.d.ts.map +0 -1
- package/dist/src/shared/resolveAliases.js +0 -98
- package/dist/src/shared/resolveAliases.js.map +0 -1
|
@@ -1,50 +1,37 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from "react"
|
|
4
|
-
import { Badge } from "../Badge"
|
|
5
|
-
import { Avatar, AvatarFallback, AvatarImage } from "../Avatar"
|
|
6
|
-
import { cn } from "../../utils/cn"
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
FieldConfig,
|
|
11
|
-
FieldType,
|
|
12
|
-
} from "./types"
|
|
13
|
-
import { getByPath } from "../../utils/dataViews/pathUtils"
|
|
14
|
-
import { resolveBadgeVariant } from "./badgeAdapter"
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { Badge } from "../Badge";
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../Avatar";
|
|
6
|
+
import { cn } from "../../utils/cn";
|
|
7
|
+
import type { CurrencyOptions, DynamicRecord, FieldConfig, FieldType } from "./types";
|
|
8
|
+
import { getByPath } from "../../utils/dataViews/pathUtils";
|
|
9
|
+
import { resolveBadgeVariant } from "./badgeAdapter";
|
|
15
10
|
|
|
16
11
|
type RenderArgs = {
|
|
17
|
-
value:
|
|
18
|
-
field: FieldConfig
|
|
19
|
-
row: DynamicRecord
|
|
20
|
-
}
|
|
12
|
+
value: unknown;
|
|
13
|
+
field: FieldConfig;
|
|
14
|
+
row: DynamicRecord;
|
|
15
|
+
};
|
|
21
16
|
|
|
22
|
-
const NULL_PLACEHOLDER =
|
|
23
|
-
<span className="text-content-presentation-global-tertiary">-</span>
|
|
24
|
-
)
|
|
17
|
+
const NULL_PLACEHOLDER = <span className="text-content-presentation-global-tertiary">-</span>;
|
|
25
18
|
|
|
26
|
-
export function renderField(
|
|
27
|
-
value
|
|
28
|
-
field: FieldConfig,
|
|
29
|
-
row: DynamicRecord,
|
|
30
|
-
): ReactNode {
|
|
31
|
-
if (field.render) return field.render(value, row)
|
|
19
|
+
export function renderField(value: unknown, field: FieldConfig, row: DynamicRecord): ReactNode {
|
|
20
|
+
if (field.render) return field.render(value, row);
|
|
32
21
|
|
|
33
|
-
const type = field.type ?? "text"
|
|
34
|
-
if (type === "hidden") return null
|
|
22
|
+
const type = field.type ?? "text";
|
|
23
|
+
if (type === "hidden") return null;
|
|
35
24
|
|
|
36
25
|
if (value == null && type !== "boolean" && type !== "progress-bar") {
|
|
37
|
-
return NULL_PLACEHOLDER
|
|
26
|
+
return NULL_PLACEHOLDER;
|
|
38
27
|
}
|
|
39
28
|
|
|
40
|
-
const renderer = RENDERERS[type] ?? renderText
|
|
41
|
-
return renderer({ value, field, row })
|
|
29
|
+
const renderer = RENDERERS[type] ?? renderText;
|
|
30
|
+
return renderer({ value, field, row });
|
|
42
31
|
}
|
|
43
32
|
|
|
44
33
|
function renderText({ value }: RenderArgs): ReactNode {
|
|
45
|
-
return (
|
|
46
|
-
<span className="text-content-presentation-global-primary">{String(value)}</span>
|
|
47
|
-
)
|
|
34
|
+
return <span className="text-content-presentation-global-primary">{String(value)}</span>;
|
|
48
35
|
}
|
|
49
36
|
|
|
50
37
|
function renderNumber({ value }: RenderArgs): ReactNode {
|
|
@@ -52,107 +39,96 @@ function renderNumber({ value }: RenderArgs): ReactNode {
|
|
|
52
39
|
<span className="font-mono text-content-presentation-global-primary">
|
|
53
40
|
{typeof value === "number" ? value.toLocaleString() : String(value)}
|
|
54
41
|
</span>
|
|
55
|
-
)
|
|
42
|
+
);
|
|
56
43
|
}
|
|
57
44
|
|
|
58
45
|
function renderDate({ value }: RenderArgs): ReactNode {
|
|
59
|
-
return <span className="text-content-presentation-global-primary">{String(value)}</span
|
|
46
|
+
return <span className="text-content-presentation-global-primary">{String(value)}</span>;
|
|
60
47
|
}
|
|
61
48
|
|
|
62
49
|
function renderDateFormat({ value, field }: RenderArgs): ReactNode {
|
|
63
|
-
const opts = field.dateFormat
|
|
64
|
-
let formatted = String(value)
|
|
50
|
+
const opts = field.dateFormat;
|
|
51
|
+
let formatted = String(value);
|
|
65
52
|
try {
|
|
66
|
-
const d = value instanceof Date ? value : new Date(value)
|
|
53
|
+
const d = value instanceof Date ? value : new Date(value as string | number);
|
|
67
54
|
if (!isNaN(d.getTime())) {
|
|
68
55
|
if (typeof opts === "object" && opts) {
|
|
69
|
-
formatted = new Intl.DateTimeFormat(undefined, opts).format(d)
|
|
56
|
+
formatted = new Intl.DateTimeFormat(undefined, opts).format(d);
|
|
70
57
|
} else if (typeof opts === "string") {
|
|
71
|
-
formatted = formatWithToken(d, opts)
|
|
58
|
+
formatted = formatWithToken(d, opts);
|
|
72
59
|
} else {
|
|
73
60
|
formatted = new Intl.DateTimeFormat(undefined, {
|
|
74
61
|
year: "numeric",
|
|
75
62
|
month: "short",
|
|
76
63
|
day: "numeric",
|
|
77
|
-
}).format(d)
|
|
64
|
+
}).format(d);
|
|
78
65
|
}
|
|
79
66
|
}
|
|
80
67
|
} catch {
|
|
81
68
|
/* fall through to raw string */
|
|
82
69
|
}
|
|
83
|
-
return <span className="text-content-presentation-global-primary">{formatted}</span
|
|
70
|
+
return <span className="text-content-presentation-global-primary">{formatted}</span>;
|
|
84
71
|
}
|
|
85
72
|
|
|
86
73
|
function formatWithToken(d: Date, token: string): string {
|
|
87
|
-
const pad = (n: number) => String(n).padStart(2, "0")
|
|
74
|
+
const pad = (n: number) => String(n).padStart(2, "0");
|
|
88
75
|
return token
|
|
89
76
|
.replace(/YYYY/g, String(d.getFullYear()))
|
|
90
77
|
.replace(/MM/g, pad(d.getMonth() + 1))
|
|
91
78
|
.replace(/DD/g, pad(d.getDate()))
|
|
92
79
|
.replace(/HH/g, pad(d.getHours()))
|
|
93
80
|
.replace(/mm/g, pad(d.getMinutes()))
|
|
94
|
-
.replace(/ss/g, pad(d.getSeconds()))
|
|
81
|
+
.replace(/ss/g, pad(d.getSeconds()));
|
|
95
82
|
}
|
|
96
83
|
|
|
97
84
|
function renderBoolean({ value, field }: RenderArgs): ReactNode {
|
|
98
|
-
const isTrue = !!value
|
|
99
|
-
const variant = isTrue ? field.trueVariant ?? "green" : field.falseVariant ?? "gray"
|
|
100
|
-
const badgeProps = resolveBadgeVariant(variant)
|
|
85
|
+
const isTrue = !!value;
|
|
86
|
+
const variant = isTrue ? (field.trueVariant ?? "green") : (field.falseVariant ?? "gray");
|
|
87
|
+
const badgeProps = resolveBadgeVariant(variant);
|
|
101
88
|
return (
|
|
102
89
|
<Badge
|
|
103
90
|
{...badgeProps}
|
|
104
|
-
label={isTrue ? field.trueLabel ?? "Yes" : field.falseLabel ?? "No"}
|
|
91
|
+
label={isTrue ? (field.trueLabel ?? "Yes") : (field.falseLabel ?? "No")}
|
|
105
92
|
size="S"
|
|
106
|
-
|
|
107
93
|
/>
|
|
108
|
-
)
|
|
94
|
+
);
|
|
109
95
|
}
|
|
110
96
|
|
|
111
97
|
function renderEnumBadge({ value, field }: RenderArgs): ReactNode {
|
|
112
|
-
const key = String(value)
|
|
113
|
-
const variant = field.variants?.[key] ?? field.defaultVariant ?? "gray"
|
|
114
|
-
const badgeProps = resolveBadgeVariant(variant)
|
|
98
|
+
const key = String(value);
|
|
99
|
+
const variant = field.variants?.[key] ?? field.defaultVariant ?? "gray";
|
|
100
|
+
const badgeProps = resolveBadgeVariant(variant);
|
|
115
101
|
// Use medium size so the glare badge dot indicator is clearly visible
|
|
116
|
-
return <Badge {...badgeProps} label={key} size="S"
|
|
102
|
+
return <Badge {...badgeProps} label={key} size="S" />;
|
|
117
103
|
}
|
|
118
104
|
|
|
119
105
|
function renderBadgeArray({ value, field }: RenderArgs): ReactNode {
|
|
120
|
-
const badgeProps = resolveBadgeVariant(field.variant ?? "blue")
|
|
106
|
+
const badgeProps = resolveBadgeVariant(field.variant ?? "blue");
|
|
121
107
|
if (!Array.isArray(value)) {
|
|
122
|
-
return <Badge {...badgeProps} label={String(value)} size="XS"
|
|
108
|
+
return <Badge {...badgeProps} label={String(value)} size="XS" />;
|
|
123
109
|
}
|
|
124
|
-
const limit = field.limit ?? value.length
|
|
125
|
-
const head = value.slice(0, limit)
|
|
126
|
-
const overflow = value.length - head.length
|
|
127
|
-
const overflowProps = resolveBadgeVariant("gray")
|
|
110
|
+
const limit = field.limit ?? value.length;
|
|
111
|
+
const head = value.slice(0, limit);
|
|
112
|
+
const overflow = value.length - head.length;
|
|
113
|
+
const overflowProps = resolveBadgeVariant("gray");
|
|
128
114
|
return (
|
|
129
115
|
<div className="flex flex-wrap gap-1">
|
|
130
116
|
{head.map((v, i) => (
|
|
131
|
-
<Badge
|
|
132
|
-
key={i}
|
|
133
|
-
{...badgeProps}
|
|
134
|
-
label={String(v)}
|
|
135
|
-
size="XS"
|
|
136
|
-
|
|
137
|
-
/>
|
|
117
|
+
<Badge key={i} {...badgeProps} label={String(v)} size="XS" />
|
|
138
118
|
))}
|
|
139
|
-
{overflow > 0 &&
|
|
140
|
-
<Badge {...overflowProps} label={`+${overflow}`} size="XS" />
|
|
141
|
-
)}
|
|
119
|
+
{overflow > 0 && <Badge {...overflowProps} label={`+${overflow}`} size="XS" />}
|
|
142
120
|
</div>
|
|
143
|
-
)
|
|
121
|
+
);
|
|
144
122
|
}
|
|
145
123
|
|
|
146
124
|
function renderCurrency({ value, field }: RenderArgs): ReactNode {
|
|
147
|
-
const num = Number(value)
|
|
148
|
-
if (Number.isNaN(num)) return renderText({ value, field, row: {} as
|
|
125
|
+
const num = Number(value);
|
|
126
|
+
if (Number.isNaN(num)) return renderText({ value, field, row: {} as DynamicRecord });
|
|
149
127
|
|
|
150
128
|
const opts: CurrencyOptions =
|
|
151
|
-
typeof field.currency === "string"
|
|
152
|
-
? { code: field.currency }
|
|
153
|
-
: field.currency ?? {}
|
|
129
|
+
typeof field.currency === "string" ? { code: field.currency } : (field.currency ?? {});
|
|
154
130
|
|
|
155
|
-
let formatted: string
|
|
131
|
+
let formatted: string;
|
|
156
132
|
if (opts.code) {
|
|
157
133
|
try {
|
|
158
134
|
formatted = new Intl.NumberFormat(opts.locale, {
|
|
@@ -160,39 +136,36 @@ function renderCurrency({ value, field }: RenderArgs): ReactNode {
|
|
|
160
136
|
currency: opts.code,
|
|
161
137
|
minimumFractionDigits: opts.decimals,
|
|
162
138
|
maximumFractionDigits: opts.decimals,
|
|
163
|
-
}).format(num)
|
|
139
|
+
}).format(num);
|
|
164
140
|
} catch {
|
|
165
141
|
formatted = `${opts.symbol ?? "$"}${num.toLocaleString(opts.locale, {
|
|
166
142
|
minimumFractionDigits: opts.decimals,
|
|
167
143
|
maximumFractionDigits: opts.decimals,
|
|
168
|
-
})}
|
|
144
|
+
})}`;
|
|
169
145
|
}
|
|
170
146
|
} else {
|
|
171
147
|
formatted = `${opts.symbol ?? "$"}${num.toLocaleString(opts.locale, {
|
|
172
148
|
minimumFractionDigits: opts.decimals,
|
|
173
149
|
maximumFractionDigits: opts.decimals,
|
|
174
|
-
})}
|
|
150
|
+
})}`;
|
|
175
151
|
}
|
|
176
152
|
|
|
177
|
-
return <span className="font-semibold text-green-600">{formatted}</span
|
|
153
|
+
return <span className="font-semibold text-green-600">{formatted}</span>;
|
|
178
154
|
}
|
|
179
155
|
|
|
180
156
|
function renderNumberFormat({ value, field }: RenderArgs): ReactNode {
|
|
181
|
-
const num = Number(value)
|
|
182
|
-
if (Number.isNaN(num)) return renderText({ value, field, row: {} as
|
|
183
|
-
const formatted = new Intl.NumberFormat(undefined, field.format).format(num)
|
|
184
|
-
return <span className="font-mono text-content-presentation-global-primary">{formatted}</span
|
|
157
|
+
const num = Number(value);
|
|
158
|
+
if (Number.isNaN(num)) return renderText({ value, field, row: {} as DynamicRecord });
|
|
159
|
+
const formatted = new Intl.NumberFormat(undefined, field.format).format(num);
|
|
160
|
+
return <span className="font-mono text-content-presentation-global-primary">{formatted}</span>;
|
|
185
161
|
}
|
|
186
162
|
|
|
187
163
|
function renderProgressBar({ value, field }: RenderArgs): ReactNode {
|
|
188
|
-
const raw = Number(value)
|
|
189
|
-
const num = Number.isFinite(raw) ? raw : 0
|
|
190
|
-
const pct = Math.max(0, Math.min(100, num))
|
|
191
|
-
const [warn, ok] = field.thresholds ?? [40, 70]
|
|
192
|
-
const color =
|
|
193
|
-
num >= ok ? "bg-green-500"
|
|
194
|
-
: num >= warn ? "bg-yellow-500"
|
|
195
|
-
: "bg-red-500"
|
|
164
|
+
const raw = Number(value);
|
|
165
|
+
const num = Number.isFinite(raw) ? raw : 0;
|
|
166
|
+
const pct = Math.max(0, Math.min(100, num));
|
|
167
|
+
const [warn, ok] = field.thresholds ?? [40, 70];
|
|
168
|
+
const color = num >= ok ? "bg-green-500" : num >= warn ? "bg-yellow-500" : "bg-red-500";
|
|
196
169
|
|
|
197
170
|
return (
|
|
198
171
|
<div className="flex items-center gap-2 min-w-[120px]">
|
|
@@ -203,91 +176,83 @@ function renderProgressBar({ value, field }: RenderArgs): ReactNode {
|
|
|
203
176
|
{Math.round(num)}%
|
|
204
177
|
</span>
|
|
205
178
|
</div>
|
|
206
|
-
)
|
|
179
|
+
);
|
|
207
180
|
}
|
|
208
181
|
|
|
209
182
|
function renderStarRating({ value, field }: RenderArgs): ReactNode {
|
|
210
|
-
const num = Number(value)
|
|
211
|
-
const max = field.max ?? 5
|
|
212
|
-
const filled = Math.max(0, Math.min(max, Math.floor(num)))
|
|
183
|
+
const num = Number(value);
|
|
184
|
+
const max = field.max ?? 5;
|
|
185
|
+
const filled = Math.max(0, Math.min(max, Math.floor(num)));
|
|
213
186
|
|
|
214
187
|
return (
|
|
215
188
|
<div className="flex items-center gap-2">
|
|
216
189
|
<div className="flex">
|
|
217
190
|
{Array.from({ length: max }).map((_, i) => (
|
|
218
|
-
<span
|
|
219
|
-
key={i}
|
|
220
|
-
className={i < filled ? "text-yellow-500" : "text-gray-300"}
|
|
221
|
-
aria-hidden
|
|
222
|
-
>
|
|
191
|
+
<span key={i} className={i < filled ? "text-yellow-500" : "text-gray-300"} aria-hidden>
|
|
223
192
|
★
|
|
224
193
|
</span>
|
|
225
194
|
))}
|
|
226
195
|
</div>
|
|
227
196
|
<span className="text-sm font-semibold tabular-nums">{Number.isFinite(num) ? num : "-"}</span>
|
|
228
197
|
</div>
|
|
229
|
-
)
|
|
198
|
+
);
|
|
230
199
|
}
|
|
231
200
|
|
|
232
201
|
function renderIconText({ value, field }: RenderArgs): ReactNode {
|
|
233
|
-
const icon = field.icon ?? ""
|
|
234
|
-
const after = field.iconPosition === "after"
|
|
202
|
+
const icon = field.icon ?? "";
|
|
203
|
+
const after = field.iconPosition === "after";
|
|
235
204
|
return (
|
|
236
205
|
<span className="inline-flex items-center gap-1.5 text-content-presentation-global-primary">
|
|
237
206
|
{!after && icon && <IconNode icon={icon} />}
|
|
238
207
|
<span>{String(value)}</span>
|
|
239
208
|
{after && icon && <IconNode icon={icon} />}
|
|
240
209
|
</span>
|
|
241
|
-
)
|
|
210
|
+
);
|
|
242
211
|
}
|
|
243
212
|
|
|
244
213
|
function IconNode({ icon }: { icon: string }) {
|
|
245
|
-
if (/^ri-/.test(icon)) return <i className={icon}
|
|
246
|
-
return <span aria-hidden>{icon}</span
|
|
214
|
+
if (/^ri-/.test(icon)) return <i className={icon} />;
|
|
215
|
+
return <span aria-hidden>{icon}</span>;
|
|
247
216
|
}
|
|
248
217
|
|
|
249
218
|
function renderTwoLine({ value, field, row }: RenderArgs): ReactNode {
|
|
250
|
-
const secondary =
|
|
251
|
-
field.secondaryPath != null ? getByPath(row, field.secondaryPath) : null
|
|
219
|
+
const secondary = field.secondaryPath != null ? getByPath(row, field.secondaryPath) : null;
|
|
252
220
|
return (
|
|
253
221
|
<div className="leading-tight">
|
|
254
|
-
<div className="font-semibold text-content-presentation-global-primary">
|
|
255
|
-
{String(value)}
|
|
256
|
-
</div>
|
|
222
|
+
<div className="font-semibold text-content-presentation-global-primary">{String(value)}</div>
|
|
257
223
|
{secondary != null && (
|
|
258
224
|
<div className="text-xs text-content-presentation-global-secondary">
|
|
259
225
|
{String(secondary)}
|
|
260
226
|
</div>
|
|
261
227
|
)}
|
|
262
228
|
</div>
|
|
263
|
-
)
|
|
229
|
+
);
|
|
264
230
|
}
|
|
265
231
|
|
|
266
232
|
function renderAvatar({ value, field, row }: RenderArgs): ReactNode {
|
|
267
|
-
const src = typeof value === "string" ? value : null
|
|
268
|
-
const fallbackSource =
|
|
269
|
-
|
|
270
|
-
const initials = toInitials(fallbackSource ?? src ?? "?")
|
|
233
|
+
const src = typeof value === "string" ? value : null;
|
|
234
|
+
const fallbackSource = field.fallbackPath != null ? getByPath(row, field.fallbackPath) : null;
|
|
235
|
+
const initials = toInitials(fallbackSource ?? src ?? "?");
|
|
271
236
|
|
|
272
237
|
return (
|
|
273
238
|
<Avatar>
|
|
274
239
|
{src && <AvatarImage src={src} alt={String(initials)} />}
|
|
275
240
|
<AvatarFallback>{initials}</AvatarFallback>
|
|
276
241
|
</Avatar>
|
|
277
|
-
)
|
|
242
|
+
);
|
|
278
243
|
}
|
|
279
244
|
|
|
280
245
|
function toInitials(s: string): string {
|
|
281
|
-
if (!s) return "?"
|
|
282
|
-
const parts = String(s).trim().split(/\s+/).slice(0, 2)
|
|
283
|
-
return parts.map((p) => p[0]?.toUpperCase() ?? "").join("") || "?"
|
|
246
|
+
if (!s) return "?";
|
|
247
|
+
const parts = String(s).trim().split(/\s+/).slice(0, 2);
|
|
248
|
+
return parts.map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
|
|
284
249
|
}
|
|
285
250
|
|
|
286
251
|
function renderLink({ value, field }: RenderArgs): ReactNode {
|
|
287
|
-
const v = String(value)
|
|
288
|
-
let href = v
|
|
289
|
-
if (field.linkType === "mailto" && !v.startsWith("mailto:")) href = `mailto:${v}
|
|
290
|
-
else if (field.linkType === "tel" && !v.startsWith("tel:")) href = `tel:${v}
|
|
252
|
+
const v = String(value);
|
|
253
|
+
let href = v;
|
|
254
|
+
if (field.linkType === "mailto" && !v.startsWith("mailto:")) href = `mailto:${v}`;
|
|
255
|
+
else if (field.linkType === "tel" && !v.startsWith("tel:")) href = `tel:${v}`;
|
|
291
256
|
|
|
292
257
|
return (
|
|
293
258
|
<a
|
|
@@ -299,36 +264,36 @@ function renderLink({ value, field }: RenderArgs): ReactNode {
|
|
|
299
264
|
>
|
|
300
265
|
{v}
|
|
301
266
|
</a>
|
|
302
|
-
)
|
|
267
|
+
);
|
|
303
268
|
}
|
|
304
269
|
|
|
305
270
|
function renderImage({ value }: RenderArgs): ReactNode {
|
|
306
|
-
if (typeof value !== "string" || !value) return NULL_PLACEHOLDER
|
|
271
|
+
if (typeof value !== "string" || !value) return NULL_PLACEHOLDER;
|
|
307
272
|
return (
|
|
308
273
|
<img
|
|
309
274
|
src={value}
|
|
310
275
|
alt=""
|
|
311
276
|
className="h-10 w-10 rounded object-cover border border-border-presentation-global-primary"
|
|
312
277
|
/>
|
|
313
|
-
)
|
|
278
|
+
);
|
|
314
279
|
}
|
|
315
280
|
|
|
316
281
|
const RENDERERS: Record<FieldType, (a: RenderArgs) => ReactNode> = {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
282
|
+
text: renderText,
|
|
283
|
+
number: renderNumber,
|
|
284
|
+
date: renderDate,
|
|
285
|
+
boolean: renderBoolean,
|
|
286
|
+
hidden: () => null,
|
|
322
287
|
"enum-badge": renderEnumBadge,
|
|
323
288
|
"badge-array": renderBadgeArray,
|
|
324
|
-
|
|
289
|
+
currency: renderCurrency,
|
|
325
290
|
"number-format": renderNumberFormat,
|
|
326
291
|
"progress-bar": renderProgressBar,
|
|
327
292
|
"star-rating": renderStarRating,
|
|
328
293
|
"icon-text": renderIconText,
|
|
329
294
|
"two-line": renderTwoLine,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
295
|
+
avatar: renderAvatar,
|
|
296
|
+
link: renderLink,
|
|
297
|
+
image: renderImage,
|
|
333
298
|
"date-format": renderDateFormat,
|
|
334
|
-
}
|
|
299
|
+
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { type ChangeEventHandler } from "react"
|
|
4
|
-
import { DatePicker } from "../../DatePicker"
|
|
5
|
-
import { InputField } from "../../InputField"
|
|
6
|
-
import { ActionButton } from "../../ActionButton"
|
|
7
|
-
import type { DateRangeFilter } from "../types"
|
|
8
|
-
import { toIsoDate } from "../../../utils/dataViews/rangeUtils"
|
|
3
|
+
import { type ChangeEventHandler } from "react";
|
|
4
|
+
import { DatePicker } from "../../DatePicker";
|
|
5
|
+
import { InputField } from "../../InputField";
|
|
6
|
+
import { ActionButton } from "../../ActionButton";
|
|
7
|
+
import type { DateRangeFilter } from "../types";
|
|
8
|
+
import { toIsoDate } from "../../../utils/dataViews/rangeUtils";
|
|
9
9
|
|
|
10
10
|
type Props = {
|
|
11
|
-
value: DateRangeFilter | undefined
|
|
12
|
-
onChange: (next: DateRangeFilter) => void
|
|
13
|
-
}
|
|
11
|
+
value: DateRangeFilter | undefined;
|
|
12
|
+
onChange: (next: DateRangeFilter) => void;
|
|
13
|
+
};
|
|
14
14
|
|
|
15
15
|
/** Parse an ISO `yyyy-MM-dd` string to a local Date (noon-safe, no TZ drift). */
|
|
16
16
|
function parseIso(iso?: string): Date | undefined {
|
|
17
|
-
return iso ? new Date(iso + "T00:00:00") : undefined
|
|
17
|
+
return iso ? new Date(iso + "T00:00:00") : undefined;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** One labeled single-date Glare DatePicker bound to one end of the range. */
|
|
@@ -24,10 +24,10 @@ function DateBound({
|
|
|
24
24
|
placeholder,
|
|
25
25
|
onPick,
|
|
26
26
|
}: {
|
|
27
|
-
caption: string
|
|
28
|
-
iso?: string
|
|
29
|
-
placeholder: string
|
|
30
|
-
onPick: (next?: string) => void
|
|
27
|
+
caption: string;
|
|
28
|
+
iso?: string;
|
|
29
|
+
placeholder: string;
|
|
30
|
+
onPick: (next?: string) => void;
|
|
31
31
|
}) {
|
|
32
32
|
return (
|
|
33
33
|
<label className="flex items-center gap-2">
|
|
@@ -41,10 +41,12 @@ function DateBound({
|
|
|
41
41
|
value={parseIso(iso) ?? (undefined as never)}
|
|
42
42
|
dateFormat="yyyy-MM-dd"
|
|
43
43
|
// Single mode reports `{ target: { value: Date } }` (it casts internally).
|
|
44
|
-
onChange={
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
onChange={
|
|
45
|
+
((e: { target: { value: Date | undefined } }) => {
|
|
46
|
+
const d = e?.target?.value;
|
|
47
|
+
onPick(d instanceof Date ? toIsoDate(d) : undefined);
|
|
48
|
+
}) as unknown as ChangeEventHandler<HTMLInputElement>
|
|
49
|
+
}
|
|
48
50
|
>
|
|
49
51
|
<InputField
|
|
50
52
|
readOnly
|
|
@@ -59,7 +61,7 @@ function DateBound({
|
|
|
59
61
|
</DatePicker>
|
|
60
62
|
</div>
|
|
61
63
|
</label>
|
|
62
|
-
)
|
|
64
|
+
);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/**
|
|
@@ -69,12 +71,17 @@ function DateBound({
|
|
|
69
71
|
*/
|
|
70
72
|
export function DatePickerRangeFilter({ value, onChange }: Props) {
|
|
71
73
|
const setBound = (key: "from" | "to") => (next?: string) =>
|
|
72
|
-
onChange({ kind: "date", from: value?.from, to: value?.to, [key]: next })
|
|
74
|
+
onChange({ kind: "date", from: value?.from, to: value?.to, [key]: next });
|
|
73
75
|
|
|
74
76
|
return (
|
|
75
77
|
<div className="space-y-2">
|
|
76
|
-
<DateBound
|
|
78
|
+
<DateBound
|
|
79
|
+
caption="From"
|
|
80
|
+
iso={value?.from}
|
|
81
|
+
placeholder="Start date"
|
|
82
|
+
onPick={setBound("from")}
|
|
83
|
+
/>
|
|
77
84
|
<DateBound caption="To" iso={value?.to} placeholder="End date" onPick={setBound("to")} />
|
|
78
85
|
</div>
|
|
79
|
-
)
|
|
86
|
+
);
|
|
80
87
|
}
|
|
@@ -1,47 +1,50 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react"
|
|
4
|
-
import { DayPicker, type DateRange } from "react-day-picker"
|
|
5
|
-
import "react-day-picker/style.css"
|
|
6
|
-
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
|
7
|
-
import { Calendar, X } from "lucide-react"
|
|
8
|
-
import { cn } from "../../../utils/cn"
|
|
9
|
-
import type { DateRangeFilter, FieldConfig } from "../types"
|
|
10
|
-
import { toIsoDate } from "../../../utils/dataViews/rangeUtils"
|
|
11
|
-
import { PresetChips } from "./PresetChips"
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { DayPicker, type DateRange } from "react-day-picker";
|
|
5
|
+
import "react-day-picker/style.css";
|
|
6
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
7
|
+
import { Calendar, X } from "lucide-react";
|
|
8
|
+
import { cn } from "../../../utils/cn";
|
|
9
|
+
import type { DateRangeFilter, FieldConfig, FilterValue } from "../types";
|
|
10
|
+
import { toIsoDate } from "../../../utils/dataViews/rangeUtils";
|
|
11
|
+
import { PresetChips } from "./PresetChips";
|
|
12
12
|
|
|
13
13
|
type Props = {
|
|
14
|
-
value: DateRangeFilter | undefined
|
|
15
|
-
onChange: (next: DateRangeFilter) => void
|
|
16
|
-
presets: FieldConfig["presets"]
|
|
17
|
-
}
|
|
14
|
+
value: DateRangeFilter | undefined;
|
|
15
|
+
onChange: (next: DateRangeFilter) => void;
|
|
16
|
+
presets: FieldConfig["presets"];
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
export function DateRangePopover({ value, onChange, presets }: Props) {
|
|
20
|
-
const [open, setOpen] = useState(false)
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
21
|
|
|
22
22
|
const range: DateRange | undefined =
|
|
23
23
|
value && (value.from || value.to)
|
|
24
24
|
? {
|
|
25
25
|
from: value.from ? new Date(value.from + "T00:00:00") : undefined,
|
|
26
|
-
to:
|
|
26
|
+
to: value.to ? new Date(value.to + "T00:00:00") : undefined,
|
|
27
27
|
}
|
|
28
|
-
: undefined
|
|
28
|
+
: undefined;
|
|
29
29
|
|
|
30
30
|
const label =
|
|
31
|
-
value?.from && value?.to
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
value?.from && value?.to
|
|
32
|
+
? `${value.from} → ${value.to}`
|
|
33
|
+
: value?.from
|
|
34
|
+
? `from ${value.from}`
|
|
35
|
+
: value?.to
|
|
36
|
+
? `until ${value.to}`
|
|
37
|
+
: "Any date";
|
|
35
38
|
|
|
36
39
|
const handleSelect = (next: DateRange | undefined) => {
|
|
37
40
|
onChange({
|
|
38
41
|
kind: "date",
|
|
39
42
|
from: next?.from ? toIsoDate(next.from) : undefined,
|
|
40
|
-
to:
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
+
to: next?.to ? toIsoDate(next.to) : undefined,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
43
46
|
|
|
44
|
-
const isActive = !!(value?.from || value?.to)
|
|
47
|
+
const isActive = !!(value?.from || value?.to);
|
|
45
48
|
|
|
46
49
|
return (
|
|
47
50
|
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
|
|
@@ -63,14 +66,14 @@ export function DateRangePopover({ value, onChange, presets }: Props) {
|
|
|
63
66
|
tabIndex={0}
|
|
64
67
|
aria-label="Clear date filter"
|
|
65
68
|
onClick={(e) => {
|
|
66
|
-
e.stopPropagation()
|
|
67
|
-
onChange({ kind: "date" })
|
|
69
|
+
e.stopPropagation();
|
|
70
|
+
onChange({ kind: "date" });
|
|
68
71
|
}}
|
|
69
72
|
onKeyDown={(e) => {
|
|
70
73
|
if (e.key === "Enter" || e.key === " ") {
|
|
71
|
-
e.stopPropagation()
|
|
72
|
-
e.preventDefault()
|
|
73
|
-
onChange({ kind: "date" })
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
onChange({ kind: "date" });
|
|
74
77
|
}
|
|
75
78
|
}}
|
|
76
79
|
className="text-content-presentation-global-tertiary hover:text-content-presentation-global-primary cursor-pointer"
|
|
@@ -87,7 +90,11 @@ export function DateRangePopover({ value, onChange, presets }: Props) {
|
|
|
87
90
|
className="z-50 bg-background-presentation-body-primary border border-border-presentation-global-primary rounded-lg shadow-lg p-3 space-y-3"
|
|
88
91
|
>
|
|
89
92
|
{presets && presets.length > 0 && (
|
|
90
|
-
<PresetChips
|
|
93
|
+
<PresetChips
|
|
94
|
+
presets={presets}
|
|
95
|
+
current={value}
|
|
96
|
+
onSelect={onChange as (value: FilterValue) => void}
|
|
97
|
+
/>
|
|
91
98
|
)}
|
|
92
99
|
<DayPicker
|
|
93
100
|
mode="range"
|
|
@@ -109,5 +116,5 @@ export function DateRangePopover({ value, onChange, presets }: Props) {
|
|
|
109
116
|
</PopoverPrimitive.Content>
|
|
110
117
|
</PopoverPrimitive.Portal>
|
|
111
118
|
</PopoverPrimitive.Root>
|
|
112
|
-
)
|
|
119
|
+
);
|
|
113
120
|
}
|