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,15 +1,15 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { Fragment } from "react"
|
|
4
|
-
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
|
5
|
-
import { Button } from "../Button"
|
|
6
|
-
import { Badge } from "../Badge"
|
|
7
|
-
import { X } from "lucide-react"
|
|
8
|
-
import { Checkbox } from "../Checkbox"
|
|
9
|
-
import { Divider } from "../Divider"
|
|
10
|
-
import { Label } from "../Label"
|
|
11
|
-
import { DataViewRadio } from "./DataViewRadio"
|
|
12
|
-
import { cn } from "../../utils/cn"
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Fragment } from "react";
|
|
4
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
5
|
+
import { Button } from "../Button";
|
|
6
|
+
import { Badge } from "../Badge";
|
|
7
|
+
import { X } from "lucide-react";
|
|
8
|
+
import { Checkbox } from "../Checkbox";
|
|
9
|
+
import { Divider } from "../Divider";
|
|
10
|
+
import { Label } from "../Label";
|
|
11
|
+
import { DataViewRadio } from "./DataViewRadio";
|
|
12
|
+
import { cn } from "../../utils/cn";
|
|
13
13
|
import type {
|
|
14
14
|
DynamicRecord,
|
|
15
15
|
DynamicFilterConfig,
|
|
@@ -19,8 +19,8 @@ import type {
|
|
|
19
19
|
FilterValue,
|
|
20
20
|
NumericRangeFilter,
|
|
21
21
|
DateRangeFilter,
|
|
22
|
-
} from "./types"
|
|
23
|
-
import { getByPath, formatPathLabel } from "../../utils/dataViews/pathUtils"
|
|
22
|
+
} from "./types";
|
|
23
|
+
import { getByPath, formatPathLabel } from "../../utils/dataViews/pathUtils";
|
|
24
24
|
import {
|
|
25
25
|
computeNumericExtremes,
|
|
26
26
|
countActiveFilters,
|
|
@@ -28,29 +28,29 @@ import {
|
|
|
28
28
|
isDateRange,
|
|
29
29
|
isNumericRange,
|
|
30
30
|
resolvePresets,
|
|
31
|
-
} from "../../utils/dataViews/rangeUtils"
|
|
32
|
-
import { RangeSliderWithInputs } from "./filters/RangeSliderWithInputs"
|
|
33
|
-
import { DatePickerRangeFilter } from "./filters/DatePickerRangeFilter"
|
|
34
|
-
import { PresetChips } from "./filters/PresetChips"
|
|
35
|
-
import { resolveBadgeVariant } from "./badgeAdapter"
|
|
36
|
-
import { SearchableSelect } from "../SearchableSelect"
|
|
37
|
-
import type { SearchableSelectOption } from "../SearchableSelect"
|
|
31
|
+
} from "../../utils/dataViews/rangeUtils";
|
|
32
|
+
import { RangeSliderWithInputs } from "./filters/RangeSliderWithInputs";
|
|
33
|
+
import { DatePickerRangeFilter } from "./filters/DatePickerRangeFilter";
|
|
34
|
+
import { PresetChips } from "./filters/PresetChips";
|
|
35
|
+
import { resolveBadgeVariant } from "./badgeAdapter";
|
|
36
|
+
import { SearchableSelect } from "../SearchableSelect";
|
|
37
|
+
import type { SearchableSelectOption } from "../SearchableSelect";
|
|
38
38
|
|
|
39
39
|
type FilterPanelProps = {
|
|
40
|
-
data: DynamicRecord[]
|
|
41
|
-
fields: FieldConfig[]
|
|
42
|
-
filters: FilterState
|
|
43
|
-
onFilterChange: (path: string, value: FilterValue) => void
|
|
44
|
-
onClearAll: () => void
|
|
45
|
-
filterConfig?: DynamicFilterConfig[]
|
|
40
|
+
data: DynamicRecord[];
|
|
41
|
+
fields: FieldConfig[];
|
|
42
|
+
filters: FilterState;
|
|
43
|
+
onFilterChange: (path: string, value: FilterValue) => void;
|
|
44
|
+
onClearAll: () => void;
|
|
45
|
+
filterConfig?: DynamicFilterConfig[];
|
|
46
46
|
/**
|
|
47
47
|
* "default": standalone left-rail style (border, padding, light surface).
|
|
48
48
|
* "panel": matches the Config tab inside DataViewsConfigPanel — no outer
|
|
49
49
|
* chrome, white section headers, categorical options inside a #1C1D1F
|
|
50
50
|
* rounded container, sections separated by #2C2D2E dividers.
|
|
51
51
|
*/
|
|
52
|
-
variant?: "default" | "panel"
|
|
53
|
-
}
|
|
52
|
+
variant?: "default" | "panel";
|
|
53
|
+
};
|
|
54
54
|
|
|
55
55
|
const NUMERIC_TYPES: FieldType[] = [
|
|
56
56
|
"number",
|
|
@@ -58,19 +58,19 @@ const NUMERIC_TYPES: FieldType[] = [
|
|
|
58
58
|
"currency",
|
|
59
59
|
"progress-bar",
|
|
60
60
|
"star-rating",
|
|
61
|
-
]
|
|
61
|
+
];
|
|
62
62
|
|
|
63
|
-
const DATE_TYPES: FieldType[] = ["date", "date-format"]
|
|
63
|
+
const DATE_TYPES: FieldType[] = ["date", "date-format"];
|
|
64
64
|
|
|
65
|
-
type FilterKind = "categorical" | "numeric-range" | "date-range"
|
|
65
|
+
type FilterKind = "categorical" | "numeric-range" | "date-range";
|
|
66
66
|
|
|
67
67
|
type Entry = {
|
|
68
|
-
path: string
|
|
69
|
-
label: string
|
|
70
|
-
kind: FilterKind
|
|
71
|
-
field?: FieldConfig
|
|
72
|
-
legacy?: DynamicFilterConfig
|
|
73
|
-
}
|
|
68
|
+
path: string;
|
|
69
|
+
label: string;
|
|
70
|
+
kind: FilterKind;
|
|
71
|
+
field?: FieldConfig;
|
|
72
|
+
legacy?: DynamicFilterConfig;
|
|
73
|
+
};
|
|
74
74
|
|
|
75
75
|
function buildFilterableEntries(
|
|
76
76
|
data: DynamicRecord[],
|
|
@@ -79,41 +79,41 @@ function buildFilterableEntries(
|
|
|
79
79
|
): Entry[] {
|
|
80
80
|
const legacyByPath = new Map(
|
|
81
81
|
(legacy ?? []).filter((f) => f.enabled !== false).map((f) => [f.id, f]),
|
|
82
|
-
)
|
|
82
|
+
);
|
|
83
83
|
|
|
84
|
-
const entries: Entry[] = []
|
|
85
|
-
const seen = new Set<string>()
|
|
84
|
+
const entries: Entry[] = [];
|
|
85
|
+
const seen = new Set<string>();
|
|
86
86
|
|
|
87
87
|
for (const f of fields) {
|
|
88
|
-
if (f.type === "hidden") continue
|
|
89
|
-
if (f.filterable === false) continue
|
|
88
|
+
if (f.type === "hidden") continue;
|
|
89
|
+
if (f.filterable === false) continue;
|
|
90
90
|
|
|
91
|
-
const isExplicit = f.filterable === true
|
|
91
|
+
const isExplicit = f.filterable === true;
|
|
92
92
|
const isCategoricalAuto =
|
|
93
93
|
f.type === "enum-badge" ||
|
|
94
94
|
f.type === "boolean" ||
|
|
95
95
|
f.type === "badge-array" ||
|
|
96
|
-
f.type === "icon-text"
|
|
97
|
-
const isNumeric = f.type != null && NUMERIC_TYPES.includes(f.type)
|
|
98
|
-
const isDate = f.type != null && DATE_TYPES.includes(f.type)
|
|
96
|
+
f.type === "icon-text";
|
|
97
|
+
const isNumeric = f.type != null && NUMERIC_TYPES.includes(f.type);
|
|
98
|
+
const isDate = f.type != null && DATE_TYPES.includes(f.type);
|
|
99
99
|
|
|
100
|
-
let include = isExplicit || isCategoricalAuto
|
|
100
|
+
let include = isExplicit || isCategoricalAuto;
|
|
101
101
|
|
|
102
102
|
if (!include) {
|
|
103
|
-
if (f.type !== "text" && f.type !== undefined) continue
|
|
104
|
-
const unique = new Set<string>()
|
|
103
|
+
if (f.type !== "text" && f.type !== undefined) continue;
|
|
104
|
+
const unique = new Set<string>();
|
|
105
105
|
for (const item of data) {
|
|
106
|
-
const v = getByPath(item, f.path)
|
|
107
|
-
if (v == null) continue
|
|
108
|
-
unique.add(String(v))
|
|
109
|
-
if (unique.size > 10) break
|
|
106
|
+
const v = getByPath(item, f.path);
|
|
107
|
+
if (v == null) continue;
|
|
108
|
+
unique.add(String(v));
|
|
109
|
+
if (unique.size > 10) break;
|
|
110
110
|
}
|
|
111
|
-
include = unique.size > 0 && unique.size <= 10
|
|
111
|
+
include = unique.size > 0 && unique.size <= 10;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
if (!include) continue
|
|
114
|
+
if (!include) continue;
|
|
115
115
|
|
|
116
|
-
const kind: FilterKind = isNumeric ? "numeric-range" : isDate ? "date-range" : "categorical"
|
|
116
|
+
const kind: FilterKind = isNumeric ? "numeric-range" : isDate ? "date-range" : "categorical";
|
|
117
117
|
|
|
118
118
|
entries.push({
|
|
119
119
|
path: f.path,
|
|
@@ -121,21 +121,21 @@ function buildFilterableEntries(
|
|
|
121
121
|
kind,
|
|
122
122
|
field: f,
|
|
123
123
|
legacy: legacyByPath.get(f.path),
|
|
124
|
-
})
|
|
125
|
-
seen.add(f.path)
|
|
124
|
+
});
|
|
125
|
+
seen.add(f.path);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
for (const lf of legacyByPath.values()) {
|
|
129
|
-
if (seen.has(lf.id)) continue
|
|
129
|
+
if (seen.has(lf.id)) continue;
|
|
130
130
|
entries.push({
|
|
131
131
|
path: lf.id,
|
|
132
132
|
label: lf.label ?? formatPathLabel(lf.id),
|
|
133
133
|
kind: "categorical",
|
|
134
134
|
legacy: lf,
|
|
135
|
-
})
|
|
135
|
+
});
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
return entries
|
|
138
|
+
return entries;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
function getCategoricalOptions(
|
|
@@ -145,37 +145,37 @@ function getCategoricalOptions(
|
|
|
145
145
|
legacy?: DynamicFilterConfig,
|
|
146
146
|
): string[] {
|
|
147
147
|
if (field?.filterOptions && field.filterOptions.length > 0) {
|
|
148
|
-
return normalizeOptions(field.filterOptions)
|
|
148
|
+
return normalizeOptions(field.filterOptions);
|
|
149
149
|
}
|
|
150
150
|
if (legacy?.options && legacy.options.length > 0) {
|
|
151
|
-
return normalizeOptions(legacy.options)
|
|
151
|
+
return normalizeOptions(legacy.options);
|
|
152
152
|
}
|
|
153
153
|
if (field?.variants) {
|
|
154
|
-
const fromMap = Object.keys(field.variants)
|
|
155
|
-
const fromData = collectUnique(data, path)
|
|
156
|
-
return Array.from(new Set([...fromMap, ...fromData]))
|
|
154
|
+
const fromMap = Object.keys(field.variants);
|
|
155
|
+
const fromData = collectUnique(data, path);
|
|
156
|
+
return Array.from(new Set([...fromMap, ...fromData]));
|
|
157
157
|
}
|
|
158
|
-
return collectUnique(data, path)
|
|
158
|
+
return collectUnique(data, path);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
function normalizeOptions(opts: NonNullable<FieldConfig["filterOptions"]>): string[] {
|
|
162
|
-
if (opts.length === 0) return []
|
|
163
|
-
if (typeof opts[0] === "string") return opts as string[]
|
|
164
|
-
return (opts as { label: string; value: string }[]).map((o) => o.value)
|
|
162
|
+
if (opts.length === 0) return [];
|
|
163
|
+
if (typeof opts[0] === "string") return opts as string[];
|
|
164
|
+
return (opts as { label: string; value: string }[]).map((o) => o.value);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
function collectUnique(data: DynamicRecord[], path: string): string[] {
|
|
168
|
-
const set = new Set<string>()
|
|
168
|
+
const set = new Set<string>();
|
|
169
169
|
for (const item of data) {
|
|
170
|
-
const v = getByPath(item, path)
|
|
171
|
-
if (v == null) continue
|
|
170
|
+
const v = getByPath(item, path);
|
|
171
|
+
if (v == null) continue;
|
|
172
172
|
if (Array.isArray(v)) {
|
|
173
|
-
for (const x of v) set.add(String(x))
|
|
173
|
+
for (const x of v) set.add(String(x));
|
|
174
174
|
} else {
|
|
175
|
-
set.add(String(v))
|
|
175
|
+
set.add(String(v));
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
return Array.from(set).sort()
|
|
178
|
+
return Array.from(set).sort();
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
export function FilterPanel({
|
|
@@ -187,30 +187,30 @@ export function FilterPanel({
|
|
|
187
187
|
filterConfig,
|
|
188
188
|
variant = "default",
|
|
189
189
|
}: FilterPanelProps) {
|
|
190
|
-
const entries = buildFilterableEntries(data, fields, filterConfig)
|
|
190
|
+
const entries = buildFilterableEntries(data, fields, filterConfig);
|
|
191
191
|
|
|
192
192
|
const setFilter = (path: string, value: FilterValue) => {
|
|
193
|
-
onFilterChange(path, value)
|
|
194
|
-
const field = fields.find((f) => f.path === path)
|
|
195
|
-
field?.onFilterChange?.(value)
|
|
193
|
+
onFilterChange(path, value);
|
|
194
|
+
const field = fields.find((f) => f.path === path);
|
|
195
|
+
field?.onFilterChange?.(value);
|
|
196
196
|
if (Array.isArray(value)) {
|
|
197
|
-
const legacy = filterConfig?.find((f) => f.id === path)
|
|
198
|
-
legacy?.onChange?.(value)
|
|
197
|
+
const legacy = filterConfig?.find((f) => f.id === path);
|
|
198
|
+
legacy?.onChange?.(value);
|
|
199
199
|
}
|
|
200
|
-
}
|
|
200
|
+
};
|
|
201
201
|
|
|
202
202
|
const toggleCategorical = (path: string, option: string) => {
|
|
203
|
-
const current = filters[path]
|
|
204
|
-
const arr = Array.isArray(current) ? current : []
|
|
205
|
-
const next = arr.includes(option) ? arr.filter((v) => v !== option) : [...arr, option]
|
|
206
|
-
setFilter(path, next)
|
|
207
|
-
}
|
|
203
|
+
const current = filters[path];
|
|
204
|
+
const arr = Array.isArray(current) ? current : [];
|
|
205
|
+
const next = arr.includes(option) ? arr.filter((v) => v !== option) : [...arr, option];
|
|
206
|
+
setFilter(path, next);
|
|
207
|
+
};
|
|
208
208
|
|
|
209
|
-
const totalFilters = countActiveFilters(filters)
|
|
209
|
+
const totalFilters = countActiveFilters(filters);
|
|
210
210
|
|
|
211
|
-
if (entries.length === 0) return null
|
|
211
|
+
if (entries.length === 0) return null;
|
|
212
212
|
|
|
213
|
-
const countBadge = resolveBadgeVariant("gray")
|
|
213
|
+
const countBadge = resolveBadgeVariant("gray");
|
|
214
214
|
|
|
215
215
|
if (variant === "panel") {
|
|
216
216
|
return (
|
|
@@ -261,7 +261,7 @@ export function FilterPanel({
|
|
|
261
261
|
</Fragment>
|
|
262
262
|
))}
|
|
263
263
|
</div>
|
|
264
|
-
)
|
|
264
|
+
);
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
return (
|
|
@@ -305,7 +305,7 @@ export function FilterPanel({
|
|
|
305
305
|
))}
|
|
306
306
|
</div>
|
|
307
307
|
</div>
|
|
308
|
-
)
|
|
308
|
+
);
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
function FilterBody({
|
|
@@ -316,21 +316,23 @@ function FilterBody({
|
|
|
316
316
|
onSetFilter,
|
|
317
317
|
variant = "default",
|
|
318
318
|
}: {
|
|
319
|
-
entry: Entry
|
|
320
|
-
data: DynamicRecord[]
|
|
321
|
-
value: FilterValue | undefined
|
|
322
|
-
onCategoricalToggle: (option: string) => void
|
|
323
|
-
onSetFilter: (next: FilterValue) => void
|
|
324
|
-
variant?: "default" | "panel"
|
|
319
|
+
entry: Entry;
|
|
320
|
+
data: DynamicRecord[];
|
|
321
|
+
value: FilterValue | undefined;
|
|
322
|
+
onCategoricalToggle: (option: string) => void;
|
|
323
|
+
onSetFilter: (next: FilterValue) => void;
|
|
324
|
+
variant?: "default" | "panel";
|
|
325
325
|
}) {
|
|
326
326
|
if (entry.kind === "numeric-range" && entry.field) {
|
|
327
|
-
const extremes = computeNumericExtremes(data, entry.path)
|
|
327
|
+
const extremes = computeNumericExtremes(data, entry.path);
|
|
328
328
|
if (!extremes || extremes.min === extremes.max) {
|
|
329
|
-
return
|
|
329
|
+
return (
|
|
330
|
+
<div className="text-xs text-content-presentation-global-tertiary">No range to filter.</div>
|
|
331
|
+
);
|
|
330
332
|
}
|
|
331
|
-
const step = inferStep(entry.field, extremes)
|
|
332
|
-
const presets = resolvePresets(entry.field)
|
|
333
|
-
const numericValue: NumericRangeFilter | undefined = isNumericRange(value) ? value : undefined
|
|
333
|
+
const step = inferStep(entry.field, extremes);
|
|
334
|
+
const presets = resolvePresets(entry.field);
|
|
335
|
+
const numericValue: NumericRangeFilter | undefined = isNumericRange(value) ? value : undefined;
|
|
334
336
|
return (
|
|
335
337
|
<div className="space-y-3">
|
|
336
338
|
{presets.length > 0 && (
|
|
@@ -344,19 +346,19 @@ function FilterBody({
|
|
|
344
346
|
onChange={onSetFilter}
|
|
345
347
|
/>
|
|
346
348
|
</div>
|
|
347
|
-
)
|
|
349
|
+
);
|
|
348
350
|
}
|
|
349
351
|
|
|
350
352
|
if (entry.kind === "date-range" && entry.field) {
|
|
351
|
-
const dateValue: DateRangeFilter | undefined = isDateRange(value) ? value : undefined
|
|
353
|
+
const dateValue: DateRangeFilter | undefined = isDateRange(value) ? value : undefined;
|
|
352
354
|
// Glare DatePicker in range mode — pick a from→to span. (No quick-preset
|
|
353
355
|
// chips: the calendar range selection is the single intended interaction.)
|
|
354
|
-
return <DatePickerRangeFilter value={dateValue} onChange={onSetFilter}
|
|
356
|
+
return <DatePickerRangeFilter value={dateValue} onChange={onSetFilter} />;
|
|
355
357
|
}
|
|
356
358
|
|
|
357
|
-
const opts = getCategoricalOptions(data, entry.path, entry.field, entry.legacy)
|
|
358
|
-
const selected = Array.isArray(value) ? value : []
|
|
359
|
-
const isSingle = entry.field?.filterMode === "single"
|
|
359
|
+
const opts = getCategoricalOptions(data, entry.path, entry.field, entry.legacy);
|
|
360
|
+
const selected = Array.isArray(value) ? value : [];
|
|
361
|
+
const isSingle = entry.field?.filterMode === "single";
|
|
360
362
|
|
|
361
363
|
// Searchable single-select dropdown — for fields with many options. Stores
|
|
362
364
|
// the chosen value as a 1-element array (empty when cleared) to stay
|
|
@@ -365,8 +367,8 @@ function FilterBody({
|
|
|
365
367
|
const selectOptions: SearchableSelectOption[] = opts.map((opt) => ({
|
|
366
368
|
value: opt,
|
|
367
369
|
label: opt,
|
|
368
|
-
}))
|
|
369
|
-
const current = selected[0] ?? null
|
|
370
|
+
}));
|
|
371
|
+
const current = selected[0] ?? null;
|
|
370
372
|
return (
|
|
371
373
|
<SearchableSelect
|
|
372
374
|
options={selectOptions}
|
|
@@ -375,12 +377,12 @@ function FilterBody({
|
|
|
375
377
|
placeholder={`Select ${entry.label}…`}
|
|
376
378
|
icon={<i className="ri-search-line" />}
|
|
377
379
|
/>
|
|
378
|
-
)
|
|
380
|
+
);
|
|
379
381
|
}
|
|
380
382
|
|
|
381
383
|
if (isSingle) {
|
|
382
|
-
const current = selected[0] ?? ""
|
|
383
|
-
const onSingleChange = (next: string) => onSetFilter(next ? [next] : [])
|
|
384
|
+
const current = selected[0] ?? "";
|
|
385
|
+
const onSingleChange = (next: string) => onSetFilter(next ? [next] : []);
|
|
384
386
|
return (
|
|
385
387
|
<RadioGroupPrimitive.Root
|
|
386
388
|
value={current}
|
|
@@ -393,26 +395,26 @@ function FilterBody({
|
|
|
393
395
|
)}
|
|
394
396
|
>
|
|
395
397
|
{opts.map((opt, i) => {
|
|
396
|
-
const isSelected = current === opt
|
|
397
|
-
const badgeVariant = entry.field?.variants?.[opt]
|
|
398
|
-
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null
|
|
398
|
+
const isSelected = current === opt;
|
|
399
|
+
const badgeVariant = entry.field?.variants?.[opt];
|
|
400
|
+
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null;
|
|
399
401
|
return (
|
|
400
402
|
<div key={opt}>
|
|
401
|
-
{i > 0 &&
|
|
402
|
-
<div className="dv-divider h-px bg-[#2C2D2E]" />
|
|
403
|
-
)}
|
|
403
|
+
{i > 0 && <div className="dv-divider h-px bg-[#2C2D2E]" />}
|
|
404
404
|
<DataViewRadio value={opt}>
|
|
405
|
-
{entry.legacy?.render
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
405
|
+
{entry.legacy?.render ? (
|
|
406
|
+
entry.legacy.render(opt, isSelected)
|
|
407
|
+
) : badgeProps ? (
|
|
408
|
+
<Badge {...badgeProps} label={opt} size="XS" />
|
|
409
|
+
) : (
|
|
410
|
+
opt
|
|
411
|
+
)}
|
|
410
412
|
</DataViewRadio>
|
|
411
413
|
</div>
|
|
412
|
-
)
|
|
414
|
+
);
|
|
413
415
|
})}
|
|
414
416
|
</RadioGroupPrimitive.Root>
|
|
415
|
-
)
|
|
417
|
+
);
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
if (variant === "panel") {
|
|
@@ -426,9 +428,9 @@ function FilterBody({
|
|
|
426
428
|
)}
|
|
427
429
|
>
|
|
428
430
|
{opts.map((opt, i) => {
|
|
429
|
-
const isSelected = selected.includes(opt)
|
|
430
|
-
const badgeVariant = entry.field?.variants?.[opt]
|
|
431
|
-
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null
|
|
431
|
+
const isSelected = selected.includes(opt);
|
|
432
|
+
const badgeVariant = entry.field?.variants?.[opt];
|
|
433
|
+
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null;
|
|
432
434
|
return (
|
|
433
435
|
<div key={opt}>
|
|
434
436
|
{i > 0 && <div className="dv-divider h-px bg-[#2C2D2E]" />}
|
|
@@ -442,26 +444,28 @@ function FilterBody({
|
|
|
442
444
|
onCheckedChange={() => onCategoricalToggle(opt)}
|
|
443
445
|
/>
|
|
444
446
|
<span className="flex-1 leading-none">
|
|
445
|
-
{entry.legacy?.render
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
447
|
+
{entry.legacy?.render ? (
|
|
448
|
+
entry.legacy.render(opt, isSelected)
|
|
449
|
+
) : badgeProps ? (
|
|
450
|
+
<Badge {...badgeProps} label={opt} size="XS" />
|
|
451
|
+
) : (
|
|
452
|
+
opt
|
|
453
|
+
)}
|
|
450
454
|
</span>
|
|
451
455
|
</label>
|
|
452
456
|
</div>
|
|
453
|
-
)
|
|
457
|
+
);
|
|
454
458
|
})}
|
|
455
459
|
</div>
|
|
456
|
-
)
|
|
460
|
+
);
|
|
457
461
|
}
|
|
458
462
|
|
|
459
463
|
return (
|
|
460
464
|
<div className="space-y-2">
|
|
461
465
|
{opts.map((opt) => {
|
|
462
|
-
const isSelected = selected.includes(opt)
|
|
463
|
-
const badgeVariant = entry.field?.variants?.[opt]
|
|
464
|
-
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null
|
|
466
|
+
const isSelected = selected.includes(opt);
|
|
467
|
+
const badgeVariant = entry.field?.variants?.[opt];
|
|
468
|
+
const badgeProps = badgeVariant ? resolveBadgeVariant(badgeVariant) : null;
|
|
465
469
|
return (
|
|
466
470
|
<div key={opt} className="flex items-center space-x-2">
|
|
467
471
|
<Checkbox
|
|
@@ -473,15 +477,17 @@ function FilterBody({
|
|
|
473
477
|
htmlFor={`${entry.path}-${opt}`}
|
|
474
478
|
className="text-sm text-content-presentation-global-primary cursor-pointer leading-none flex-1"
|
|
475
479
|
>
|
|
476
|
-
{entry.legacy?.render
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
480
|
+
{entry.legacy?.render ? (
|
|
481
|
+
entry.legacy.render(opt, isSelected)
|
|
482
|
+
) : badgeProps ? (
|
|
483
|
+
<Badge {...badgeProps} label={opt} size="XS" />
|
|
484
|
+
) : (
|
|
485
|
+
opt
|
|
486
|
+
)}
|
|
481
487
|
</label>
|
|
482
488
|
</div>
|
|
483
|
-
)
|
|
489
|
+
);
|
|
484
490
|
})}
|
|
485
491
|
</div>
|
|
486
|
-
)
|
|
492
|
+
);
|
|
487
493
|
}
|
|
@@ -10,11 +10,7 @@ export type HeaderSearchProps = {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export function HeaderSearch({
|
|
14
|
-
value,
|
|
15
|
-
onChange,
|
|
16
|
-
placeholder = "Search...",
|
|
17
|
-
}: HeaderSearchProps) {
|
|
13
|
+
export function HeaderSearch({ value, onChange, placeholder = "Search..." }: HeaderSearchProps) {
|
|
18
14
|
const [open, setOpen] = useState(false);
|
|
19
15
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
20
16
|
const wrapRef = useRef<HTMLDivElement>(null);
|