qantler-flaz-dataview 1.0.0
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/package.json +11 -0
- package/src/FlazDataView.css +4423 -0
- package/src/FlazDataView.jsx +2943 -0
- package/src/FlazDataViewList.jsx +787 -0
- package/src/MdtActiveFilterBar.jsx +263 -0
- package/src/MdtBulkSelectionBar.jsx +91 -0
- package/src/MdtColumnPickerPopup.jsx +59 -0
- package/src/MdtColumnVisibilityList.jsx +38 -0
- package/src/MdtCreateViewModal.jsx +280 -0
- package/src/MdtDatetimeValueEditor.jsx +259 -0
- package/src/MdtDeleteViewConfirmModal.jsx +83 -0
- package/src/MdtDisplayPopup.jsx +170 -0
- package/src/MdtEmptyState.jsx +20 -0
- package/src/MdtFilterColumnPicker.jsx +66 -0
- package/src/MdtFilterOperatorDropdown.jsx +58 -0
- package/src/MdtFilterSection.jsx +73 -0
- package/src/MdtFilterToolbar.jsx +81 -0
- package/src/MdtInlineField.css +221 -0
- package/src/MdtInlineField.jsx +187 -0
- package/src/MdtInlineFieldRow.jsx +71 -0
- package/src/MdtNoDataIllustration.jsx +64 -0
- package/src/MdtPopupChecklist.jsx +70 -0
- package/src/MdtRowActionsMenu.jsx +281 -0
- package/src/MdtSidebarNav.jsx +26 -0
- package/src/MdtTabsRow.jsx +354 -0
- package/src/MdtTabsSection.jsx +35 -0
- package/src/MdtTruncateWithTooltip.jsx +82 -0
- package/src/MdtValueEditorPopup.jsx +950 -0
- package/src/filterDisplayUtils.jsx +96 -0
- package/src/mdtClientSideFilter.js +68 -0
- package/src/mdtColumnEditUtils.js +403 -0
- package/src/mdtDateFilterUtils.js +135 -0
- package/src/mdtExportUtils.js +234 -0
- package/src/mdtFilterUtils.js +56 -0
- package/src/mdtInlineFieldDisplay.js +66 -0
- package/src/mdtMobilePopupPosition.js +107 -0
- package/src/mdtMultiSelectDisplay.jsx +279 -0
- package/src/mdtSavedViewFilterMatch.js +154 -0
- package/src/mdtTabChangeUtils.js +12 -0
- package/src/mdtTabStatusRules.js +111 -0
- package/src/mdtTableConstants.js +2 -0
- package/src/mdtTooltipFormat.js +44 -0
- package/src/renderInlineFieldValue.jsx +183 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
ArrowDownWideNarrow,
|
|
4
|
+
ArrowUpNarrowWide,
|
|
5
|
+
ChevronDown,
|
|
6
|
+
} from "lucide-react";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Display popup — Display Properties, Order By, Group By (list only).
|
|
10
|
+
* Used in the toolbar and Create View modal.
|
|
11
|
+
*/
|
|
12
|
+
const MdtDisplayPopup = ({
|
|
13
|
+
viewMode = "table",
|
|
14
|
+
visibleColumns = [],
|
|
15
|
+
/** Columns shown as toggles in Display Properties (excludes locked e.g. Application ID). */
|
|
16
|
+
displayPropertyColumns = null,
|
|
17
|
+
hiddenColumns = [],
|
|
18
|
+
onToggleColumn,
|
|
19
|
+
groupByColumns = [],
|
|
20
|
+
groupByKey,
|
|
21
|
+
onGroupByChange,
|
|
22
|
+
sortState = { column: null, direction: null },
|
|
23
|
+
orderDirEffective = "asc",
|
|
24
|
+
onOrderFromDisplay,
|
|
25
|
+
displayPropsOpen,
|
|
26
|
+
onToggleDisplayPropsOpen,
|
|
27
|
+
orderByOpen,
|
|
28
|
+
onToggleOrderByOpen,
|
|
29
|
+
groupByOpen,
|
|
30
|
+
onToggleGroupByOpen,
|
|
31
|
+
className = "",
|
|
32
|
+
}) => {
|
|
33
|
+
const propertyColumns = displayPropertyColumns ?? visibleColumns;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className={`mdt-display-popup${className ? ` ${className}` : ""}`}>
|
|
37
|
+
<div className="mdt-dp-section">
|
|
38
|
+
<div className="mdt-dp-section-header">
|
|
39
|
+
<span>Display Properties</span>
|
|
40
|
+
<button
|
|
41
|
+
type="button"
|
|
42
|
+
className="mdt-dp-chevron-btn"
|
|
43
|
+
onClick={onToggleDisplayPropsOpen}
|
|
44
|
+
>
|
|
45
|
+
<ChevronDown
|
|
46
|
+
size={13}
|
|
47
|
+
className="mdt-dp-chevron"
|
|
48
|
+
style={{ transform: displayPropsOpen ? "rotate(180deg)" : "rotate(0deg)" }}
|
|
49
|
+
/>
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
{displayPropsOpen && (
|
|
53
|
+
<div className="mdt-dp-badges">
|
|
54
|
+
{propertyColumns.map((col) => {
|
|
55
|
+
const isVisible = !hiddenColumns.includes(col.accessorKey);
|
|
56
|
+
return (
|
|
57
|
+
<button
|
|
58
|
+
key={col.accessorKey}
|
|
59
|
+
type="button"
|
|
60
|
+
className={`mdt-dp-badge${isVisible ? " mdt-dp-badge-active" : ""}`}
|
|
61
|
+
onClick={() => onToggleColumn(col.accessorKey)}
|
|
62
|
+
>
|
|
63
|
+
{col.header}
|
|
64
|
+
</button>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div className="mdt-dp-section">
|
|
72
|
+
<div className="mdt-dp-section-header">
|
|
73
|
+
<span>Order By</span>
|
|
74
|
+
<div className="mdt-dp-header-icons">
|
|
75
|
+
{visibleColumns.some((col) => col.accessorKey === sortState.column) && (
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
className="mdt-dp-sort-icon-btn"
|
|
79
|
+
title={orderDirEffective === "asc" ? "Ascending" : "Descending"}
|
|
80
|
+
onClick={() =>
|
|
81
|
+
onOrderFromDisplay(
|
|
82
|
+
sortState.column,
|
|
83
|
+
orderDirEffective === "asc" ? "desc" : "asc",
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
{orderDirEffective === "asc" ? (
|
|
88
|
+
<ArrowUpNarrowWide size={12} />
|
|
89
|
+
) : (
|
|
90
|
+
<ArrowDownWideNarrow size={12} />
|
|
91
|
+
)}
|
|
92
|
+
</button>
|
|
93
|
+
)}
|
|
94
|
+
<button type="button" className="mdt-dp-chevron-btn" onClick={onToggleOrderByOpen}>
|
|
95
|
+
<ChevronDown
|
|
96
|
+
size={13}
|
|
97
|
+
className="mdt-dp-chevron"
|
|
98
|
+
style={{ transform: orderByOpen ? "rotate(180deg)" : "rotate(0deg)" }}
|
|
99
|
+
/>
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
{orderByOpen && (
|
|
104
|
+
<div className="mdt-dp-checklist">
|
|
105
|
+
{visibleColumns.map((col) => (
|
|
106
|
+
<label key={col.accessorKey} className="mdt-dp-check-row">
|
|
107
|
+
<input
|
|
108
|
+
type="checkbox"
|
|
109
|
+
className="mdt-dp-checkbox"
|
|
110
|
+
checked={sortState.column === col.accessorKey}
|
|
111
|
+
onChange={() => {
|
|
112
|
+
if (sortState.column === col.accessorKey) {
|
|
113
|
+
onOrderFromDisplay(null, null);
|
|
114
|
+
} else {
|
|
115
|
+
onOrderFromDisplay(col.accessorKey, "asc");
|
|
116
|
+
}
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
<span className="mdt-dp-check-label">{col.header}</span>
|
|
120
|
+
</label>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{viewMode === "list" && (
|
|
127
|
+
<div className="mdt-dp-section">
|
|
128
|
+
<div className="mdt-dp-section-header">
|
|
129
|
+
<span>Group By</span>
|
|
130
|
+
<button type="button" className="mdt-dp-chevron-btn" onClick={onToggleGroupByOpen}>
|
|
131
|
+
<ChevronDown
|
|
132
|
+
size={13}
|
|
133
|
+
className="mdt-dp-chevron"
|
|
134
|
+
style={{ transform: groupByOpen ? "rotate(180deg)" : "rotate(0deg)" }}
|
|
135
|
+
/>
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
{groupByOpen && (
|
|
139
|
+
<div className="mdt-dp-checklist">
|
|
140
|
+
{groupByColumns.map((col) => (
|
|
141
|
+
<label key={col.accessorKey} className="mdt-dp-check-row">
|
|
142
|
+
<input
|
|
143
|
+
type="checkbox"
|
|
144
|
+
className="mdt-dp-checkbox"
|
|
145
|
+
checked={groupByKey === col.accessorKey}
|
|
146
|
+
onChange={() =>
|
|
147
|
+
onGroupByChange(groupByKey === col.accessorKey ? null : col.accessorKey)
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
<span className="mdt-dp-check-label">{col.header}</span>
|
|
151
|
+
</label>
|
|
152
|
+
))}
|
|
153
|
+
<label className="mdt-dp-check-row mdt-dp-check-none">
|
|
154
|
+
<input
|
|
155
|
+
type="checkbox"
|
|
156
|
+
className="mdt-dp-checkbox"
|
|
157
|
+
checked={groupByKey === null}
|
|
158
|
+
onChange={() => onGroupByChange(null)}
|
|
159
|
+
/>
|
|
160
|
+
<span className="mdt-dp-check-label">None</span>
|
|
161
|
+
</label>
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export default MdtDisplayPopup;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import MdtNoDataIllustration from "./MdtNoDataIllustration";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Centered empty state for table/list when there is no data.
|
|
6
|
+
*/
|
|
7
|
+
const MdtEmptyState = ({
|
|
8
|
+
title = "No data found",
|
|
9
|
+
className = "",
|
|
10
|
+
}) => (
|
|
11
|
+
<div
|
|
12
|
+
className={`mdt-empty-state${className ? ` ${className}` : ""}`.trim()}
|
|
13
|
+
role="status"
|
|
14
|
+
>
|
|
15
|
+
<MdtNoDataIllustration className="mdt-empty-state-icon" />
|
|
16
|
+
<h3 className="mdt-empty-state-title">{title}</h3>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default MdtEmptyState;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Search } from "lucide-react";
|
|
3
|
+
import { renderColumnIcon } from "../../ui/tableColumnIcons";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reusable column picker dropdown (toolbar, chip replace, add filter).
|
|
7
|
+
*/
|
|
8
|
+
const MdtFilterColumnPicker = ({
|
|
9
|
+
columns = [],
|
|
10
|
+
search = "",
|
|
11
|
+
onSearchChange,
|
|
12
|
+
onSelectColumn,
|
|
13
|
+
searchPlaceholder = "Search",
|
|
14
|
+
emptyMessage = "No filters available",
|
|
15
|
+
className = "",
|
|
16
|
+
dropdownClassName = "mdt-filter-dropdown",
|
|
17
|
+
align = "right",
|
|
18
|
+
autoFocus = true,
|
|
19
|
+
}) => {
|
|
20
|
+
const alignClass =
|
|
21
|
+
align === "left" ? " mdt-fd-left" : align === "center" ? "" : "";
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={`${dropdownClassName}${alignClass}${className ? ` ${className}` : ""}`.trim()}
|
|
26
|
+
style={align === "right" ? { top: "calc(100% + 8px)" } : undefined}
|
|
27
|
+
>
|
|
28
|
+
<div className="mdt-fd-search">
|
|
29
|
+
<div className="mdt-fd-search-wrap">
|
|
30
|
+
<span className="mdt-fd-search-icon">
|
|
31
|
+
<Search size={12} />
|
|
32
|
+
</span>
|
|
33
|
+
<input
|
|
34
|
+
type="text"
|
|
35
|
+
placeholder={searchPlaceholder}
|
|
36
|
+
value={search}
|
|
37
|
+
onChange={(e) => onSearchChange?.(e.target.value)}
|
|
38
|
+
autoFocus={autoFocus}
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="mdt-fd-list">
|
|
43
|
+
{columns.map((col) => (
|
|
44
|
+
<button
|
|
45
|
+
key={col.accessorKey}
|
|
46
|
+
type="button"
|
|
47
|
+
className="mdt-fd-item"
|
|
48
|
+
onClick={() => onSelectColumn?.(col)}
|
|
49
|
+
>
|
|
50
|
+
<span className="mdt-fd-item-icon">
|
|
51
|
+
{renderColumnIcon(col.headerIcon || col.rowIcon || "default", {
|
|
52
|
+
className: "mdt-fd-svg",
|
|
53
|
+
})}
|
|
54
|
+
</span>
|
|
55
|
+
<span>{col.header}</span>
|
|
56
|
+
</button>
|
|
57
|
+
))}
|
|
58
|
+
{columns.length === 0 && (
|
|
59
|
+
<div className="mdt-fd-empty">{emptyMessage}</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default MdtFilterColumnPicker;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
import { Check } from "lucide-react";
|
|
3
|
+
import { DATE_FILTER_OPERATORS } from "./mdtDateFilterUtils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Date filter operator picker — "is" | "between".
|
|
7
|
+
*/
|
|
8
|
+
const MdtFilterOperatorDropdown = ({
|
|
9
|
+
operator,
|
|
10
|
+
onSelect,
|
|
11
|
+
onClose,
|
|
12
|
+
className = "",
|
|
13
|
+
}) => {
|
|
14
|
+
const ref = useRef(null);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleOutside = (e) => {
|
|
18
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
|
19
|
+
onClose?.();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
document.addEventListener("mousedown", handleOutside);
|
|
23
|
+
return () => document.removeEventListener("mousedown", handleOutside);
|
|
24
|
+
}, [onClose]);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={`mdt-filter-op-dropdown${className ? ` ${className}` : ""}`}
|
|
30
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
31
|
+
>
|
|
32
|
+
{DATE_FILTER_OPERATORS.map((opt) => {
|
|
33
|
+
const active = operator === opt.id;
|
|
34
|
+
return (
|
|
35
|
+
<button
|
|
36
|
+
key={opt.id}
|
|
37
|
+
type="button"
|
|
38
|
+
className={`mdt-fd-item mdt-filter-op-item${active ? " mdt-fd-item-checked" : ""}`}
|
|
39
|
+
onClick={() => {
|
|
40
|
+
onSelect(opt.id);
|
|
41
|
+
onClose?.();
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<span style={{ flex: 1 }}>{opt.label}</span>
|
|
45
|
+
{active && (
|
|
46
|
+
<Check
|
|
47
|
+
size={12}
|
|
48
|
+
style={{ color: "oklch(0.2378 0.0029 230.83)", flexShrink: 0 }}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
</button>
|
|
52
|
+
);
|
|
53
|
+
})}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default MdtFilterOperatorDropdown;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import MdtFilterToolbar from "./MdtFilterToolbar";
|
|
3
|
+
import MdtActiveFilterBar from "./MdtActiveFilterBar";
|
|
4
|
+
import { getFilterableColumns, resolveFilterConfig } from "./mdtFilterUtils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Full filter UI: optional toolbar button + active chips bar.
|
|
8
|
+
* State/handlers stay in the parent; this component wires layout + config.
|
|
9
|
+
*/
|
|
10
|
+
const MdtFilterSection = ({
|
|
11
|
+
filterConfig: filterConfigProp,
|
|
12
|
+
/** All table columns (filterable subset used internally). */
|
|
13
|
+
columns = [],
|
|
14
|
+
activeFilters = [],
|
|
15
|
+
isFilterBarOpen = false,
|
|
16
|
+
showToolbar = true,
|
|
17
|
+
showChipBar = true,
|
|
18
|
+
hideChipBar = false,
|
|
19
|
+
/** Toolbar */
|
|
20
|
+
isPickerOpen = false,
|
|
21
|
+
onPickerOpenChange,
|
|
22
|
+
onToggleFilterBar,
|
|
23
|
+
onSelectColumn,
|
|
24
|
+
pickerSearch = "",
|
|
25
|
+
onPickerSearchChange,
|
|
26
|
+
toolbarWrapRef,
|
|
27
|
+
/** Chips bar — spread remaining MdtActiveFilterBar props */
|
|
28
|
+
filterBarRef,
|
|
29
|
+
filterBarProps = {},
|
|
30
|
+
chipBarClassName = "mdt-filter-bar",
|
|
31
|
+
}) => {
|
|
32
|
+
const config = resolveFilterConfig(filterConfigProp);
|
|
33
|
+
const filterableColumns = getFilterableColumns(columns);
|
|
34
|
+
|
|
35
|
+
const showBar =
|
|
36
|
+
showChipBar &&
|
|
37
|
+
!hideChipBar &&
|
|
38
|
+
isFilterBarOpen &&
|
|
39
|
+
activeFilters.length > 0;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
{showToolbar && (
|
|
44
|
+
<MdtFilterToolbar
|
|
45
|
+
filterConfig={config}
|
|
46
|
+
columns={filterableColumns}
|
|
47
|
+
activeFilters={activeFilters}
|
|
48
|
+
isFilterBarOpen={isFilterBarOpen}
|
|
49
|
+
isPickerOpen={isPickerOpen}
|
|
50
|
+
onPickerOpenChange={onPickerOpenChange}
|
|
51
|
+
onToggleFilterBar={onToggleFilterBar}
|
|
52
|
+
onSelectColumn={onSelectColumn}
|
|
53
|
+
pickerSearch={pickerSearch}
|
|
54
|
+
onPickerSearchChange={onPickerSearchChange}
|
|
55
|
+
wrapRef={toolbarWrapRef}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
{showBar && (
|
|
60
|
+
<div className={chipBarClassName} ref={filterBarRef}>
|
|
61
|
+
<MdtActiveFilterBar
|
|
62
|
+
filterConfig={config}
|
|
63
|
+
filterableColumns={filterableColumns}
|
|
64
|
+
activeFilters={activeFilters}
|
|
65
|
+
{...filterBarProps}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default MdtFilterSection;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
import { ListFilter } from "lucide-react";
|
|
3
|
+
import MdtFilterColumnPicker from "./MdtFilterColumnPicker";
|
|
4
|
+
import { resolveFilterConfig } from "./mdtFilterUtils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Header filter button + column picker dropdown.
|
|
8
|
+
*/
|
|
9
|
+
const MdtFilterToolbar = ({
|
|
10
|
+
filterConfig: filterConfigProp,
|
|
11
|
+
columns = [],
|
|
12
|
+
activeFilters = [],
|
|
13
|
+
isFilterBarOpen = false,
|
|
14
|
+
isPickerOpen = false,
|
|
15
|
+
onPickerOpenChange,
|
|
16
|
+
onToggleFilterBar,
|
|
17
|
+
onSelectColumn,
|
|
18
|
+
pickerSearch = "",
|
|
19
|
+
onPickerSearchChange,
|
|
20
|
+
wrapRef,
|
|
21
|
+
iconSize = 16,
|
|
22
|
+
}) => {
|
|
23
|
+
const config = resolveFilterConfig(filterConfigProp);
|
|
24
|
+
const innerRef = useRef(null);
|
|
25
|
+
const setRef = (el) => {
|
|
26
|
+
innerRef.current = el;
|
|
27
|
+
if (typeof wrapRef === "function") wrapRef(el);
|
|
28
|
+
else if (wrapRef) wrapRef.current = el;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const hasFilters = activeFilters.length > 0;
|
|
32
|
+
const isActive = hasFilters || isFilterBarOpen;
|
|
33
|
+
|
|
34
|
+
const handleButtonClick = () => {
|
|
35
|
+
if (isFilterBarOpen && hasFilters) {
|
|
36
|
+
onToggleFilterBar?.(false);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (hasFilters) {
|
|
40
|
+
onToggleFilterBar?.(true);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
onPickerOpenChange?.(!isPickerOpen);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const availableColumns = columns.filter((col) => {
|
|
47
|
+
const q = pickerSearch.toLowerCase();
|
|
48
|
+
if (!col.header?.toLowerCase().includes(q)) return false;
|
|
49
|
+
return !activeFilters.find((f) => f.key === col.accessorKey);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className="mdt-filter-button-wrap" ref={setRef}>
|
|
54
|
+
<button
|
|
55
|
+
className={`mdt-ghost icon${isActive ? " mdt-filter-active" : ""}`}
|
|
56
|
+
type="button"
|
|
57
|
+
title={config.toolbarButtonTitle}
|
|
58
|
+
onClick={handleButtonClick}
|
|
59
|
+
>
|
|
60
|
+
<ListFilter size={iconSize} />
|
|
61
|
+
</button>
|
|
62
|
+
|
|
63
|
+
{isPickerOpen && (
|
|
64
|
+
<MdtFilterColumnPicker
|
|
65
|
+
columns={availableColumns}
|
|
66
|
+
search={pickerSearch}
|
|
67
|
+
onSearchChange={onPickerSearchChange}
|
|
68
|
+
onSelectColumn={(col) => {
|
|
69
|
+
onSelectColumn?.(col);
|
|
70
|
+
onPickerOpenChange?.(false);
|
|
71
|
+
}}
|
|
72
|
+
searchPlaceholder={config.searchPlaceholder}
|
|
73
|
+
emptyMessage={config.emptyColumnsMessage}
|
|
74
|
+
align={config.dropdownAlign}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default MdtFilterToolbar;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
.mdt-inline-field {
|
|
2
|
+
position: relative;
|
|
3
|
+
flex-shrink: 0;
|
|
4
|
+
max-width: 100%;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.mdt-inline-field-chip.mdt-filter-chip {
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
border: none;
|
|
10
|
+
box-shadow: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.mdt-inline-field-chip-btn {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: 8px;
|
|
17
|
+
width: 100%;
|
|
18
|
+
min-width: 0;
|
|
19
|
+
height: 28px;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
font: inherit;
|
|
24
|
+
text-align: left;
|
|
25
|
+
color: inherit;
|
|
26
|
+
background: #ffffff;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.mdt-inline-field-chip-btn:hover,
|
|
30
|
+
.mdt-inline-field-chip-btn.mdt-inline-field-chip--open {
|
|
31
|
+
background: #f4f4f5;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.mdt-inline-field-chip-btn .mdt-chip-label {
|
|
35
|
+
flex-shrink: 0;
|
|
36
|
+
padding: 0 0 0 6px;
|
|
37
|
+
max-width: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.mdt-inline-field-chip-value {
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
flex: 1 1 auto;
|
|
44
|
+
min-width: 0;
|
|
45
|
+
height: 100%;
|
|
46
|
+
padding: 0 6px 0 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Selected / entered value text in badges */
|
|
50
|
+
.mdt-inline-field-chip-value:not(.mdt-inline-field-value--empty),
|
|
51
|
+
.mdt-inline-field-chip-value:not(.mdt-inline-field-value--empty) .mdt-chip-value-text,
|
|
52
|
+
.mdt-inline-field-chip-value:not(.mdt-inline-field-value--empty) .mdt-chip-value-entry-label,
|
|
53
|
+
.mdt-inline-field-chip-value:not(.mdt-inline-field-value--empty) .mdt-chip-value-sep,
|
|
54
|
+
.mdt-inline-field-chip-value:not(.mdt-inline-field-value--empty) .mdt-chip-value-more {
|
|
55
|
+
font-size: 13px;
|
|
56
|
+
line-height: 18px;
|
|
57
|
+
font-weight: 400;
|
|
58
|
+
color: oklch(0.4377 0.0066 230.87);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.mdt-inline-field-chip-value .mdt-chip-value-text {
|
|
62
|
+
min-width: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.mdt-inline-field-value-with-icon {
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 4px;
|
|
69
|
+
min-width: 0;
|
|
70
|
+
max-width: 100%;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.mdt-inline-field-chip .mdt-chip-value-icon {
|
|
74
|
+
display: inline-flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
width: 16px;
|
|
79
|
+
height: 16px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.mdt-inline-field-chip .mdt-chip-value-icon svg {
|
|
83
|
+
width: 16px;
|
|
84
|
+
height: 16px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.mdt-inline-field-chip .mdt-chip-value-entry {
|
|
88
|
+
gap: 4px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.mdt-inline-field-chip-value.mdt-inline-field-value--empty {
|
|
92
|
+
min-width: 12px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.mdt-inline-field-chip--error.mdt-filter-chip {
|
|
96
|
+
border: none;
|
|
97
|
+
box-shadow: inset 0 0 0 1px #dc2626;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.mdt-inline-field-chip--error.mdt-inline-field-chip-btn:hover,
|
|
101
|
+
.mdt-inline-field-chip--error.mdt-inline-field-chip-btn.mdt-inline-field-chip--open {
|
|
102
|
+
background: #f4f4f5;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.mdt-inline-field--open {
|
|
106
|
+
z-index: 10002;
|
|
107
|
+
overflow: visible;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Same popup positioning as filter bar (.mdt-chip-value-wrap) */
|
|
111
|
+
.mdt-inline-field .mdt-inline-field-popup-anchor {
|
|
112
|
+
position: absolute;
|
|
113
|
+
top: 100%;
|
|
114
|
+
left: 0;
|
|
115
|
+
right: auto;
|
|
116
|
+
flex: none;
|
|
117
|
+
height: 0;
|
|
118
|
+
min-width: 0;
|
|
119
|
+
max-width: none;
|
|
120
|
+
width: 0;
|
|
121
|
+
border-left: none;
|
|
122
|
+
overflow: visible;
|
|
123
|
+
z-index: 10002;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.mdt-inline-field-popup-anchor--align-end {
|
|
127
|
+
left: auto;
|
|
128
|
+
right: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.mdt-inline-field-popup-anchor--align-end .mdt-value-popup {
|
|
132
|
+
left: auto;
|
|
133
|
+
right: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.mdt-inline-field-popup-anchor .mdt-fd-item-icon {
|
|
137
|
+
width: 16px;
|
|
138
|
+
height: 16px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.mdt-inline-field-popup-anchor .mdt-fd-item-icon svg {
|
|
142
|
+
width: 16px;
|
|
143
|
+
height: 16px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.mdt-inline-field-row {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-wrap: wrap;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
padding: 12px 21.6px;
|
|
152
|
+
border-bottom: none;
|
|
153
|
+
background: #ffffff;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Responsive badge grid — column count follows available width (~4 on wide, fewer on small) */
|
|
157
|
+
.mdt-inline-field-row--responsive,
|
|
158
|
+
.mdt-inline-field-row--4-cols {
|
|
159
|
+
display: grid;
|
|
160
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, 13.5rem), 1fr));
|
|
161
|
+
gap: 1rem;
|
|
162
|
+
align-items: stretch;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.mdt-inline-field-row--responsive .mdt-inline-field,
|
|
166
|
+
.mdt-inline-field-row--4-cols .mdt-inline-field {
|
|
167
|
+
min-width: 0;
|
|
168
|
+
max-width: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Badge bar: full-width grid cells; vertical rule at column end; 4px inset from line */
|
|
172
|
+
.mdt-inline-field-row--responsive.mdt-inline-field-row--badge-bar,
|
|
173
|
+
.mdt-inline-field-row--4-cols.mdt-inline-field-row--badge-bar {
|
|
174
|
+
column-gap: 0;
|
|
175
|
+
row-gap: 1rem;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.mdt-inline-field-row--badge-bar .mdt-inline-field {
|
|
179
|
+
box-sizing: border-box;
|
|
180
|
+
min-width: 0;
|
|
181
|
+
padding-left: 4px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.mdt-inline-field-row--badge-bar .mdt-inline-field:not(.mdt-inline-field--row-end) {
|
|
185
|
+
padding-right: 4px;
|
|
186
|
+
border-right: 1px solid #e4e4e7;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.mdt-inline-field-row--badge-bar .mdt-inline-field-chip-btn {
|
|
190
|
+
width: 100%;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@media (max-width: 599px) {
|
|
194
|
+
.mdt-inline-field-row--responsive,
|
|
195
|
+
.mdt-inline-field-row--4-cols {
|
|
196
|
+
grid-template-columns: minmax(0, 1fr);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.mdt-inline-field-row--open {
|
|
201
|
+
position: relative;
|
|
202
|
+
z-index: 60;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Flex-only bar (no responsive grid) */
|
|
206
|
+
.mdt-inline-field-row--badge-bar:not(.mdt-inline-field-row--responsive):not(
|
|
207
|
+
.mdt-inline-field-row--4-cols
|
|
208
|
+
) {
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-wrap: wrap;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.mdt-inline-field-row--badge-bar:not(.mdt-inline-field-row--responsive):not(
|
|
216
|
+
.mdt-inline-field-row--4-cols
|
|
217
|
+
)
|
|
218
|
+
.mdt-inline-field {
|
|
219
|
+
flex-shrink: 0;
|
|
220
|
+
max-width: 100%;
|
|
221
|
+
}
|