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.
Files changed (43) hide show
  1. package/package.json +11 -0
  2. package/src/FlazDataView.css +4423 -0
  3. package/src/FlazDataView.jsx +2943 -0
  4. package/src/FlazDataViewList.jsx +787 -0
  5. package/src/MdtActiveFilterBar.jsx +263 -0
  6. package/src/MdtBulkSelectionBar.jsx +91 -0
  7. package/src/MdtColumnPickerPopup.jsx +59 -0
  8. package/src/MdtColumnVisibilityList.jsx +38 -0
  9. package/src/MdtCreateViewModal.jsx +280 -0
  10. package/src/MdtDatetimeValueEditor.jsx +259 -0
  11. package/src/MdtDeleteViewConfirmModal.jsx +83 -0
  12. package/src/MdtDisplayPopup.jsx +170 -0
  13. package/src/MdtEmptyState.jsx +20 -0
  14. package/src/MdtFilterColumnPicker.jsx +66 -0
  15. package/src/MdtFilterOperatorDropdown.jsx +58 -0
  16. package/src/MdtFilterSection.jsx +73 -0
  17. package/src/MdtFilterToolbar.jsx +81 -0
  18. package/src/MdtInlineField.css +221 -0
  19. package/src/MdtInlineField.jsx +187 -0
  20. package/src/MdtInlineFieldRow.jsx +71 -0
  21. package/src/MdtNoDataIllustration.jsx +64 -0
  22. package/src/MdtPopupChecklist.jsx +70 -0
  23. package/src/MdtRowActionsMenu.jsx +281 -0
  24. package/src/MdtSidebarNav.jsx +26 -0
  25. package/src/MdtTabsRow.jsx +354 -0
  26. package/src/MdtTabsSection.jsx +35 -0
  27. package/src/MdtTruncateWithTooltip.jsx +82 -0
  28. package/src/MdtValueEditorPopup.jsx +950 -0
  29. package/src/filterDisplayUtils.jsx +96 -0
  30. package/src/mdtClientSideFilter.js +68 -0
  31. package/src/mdtColumnEditUtils.js +403 -0
  32. package/src/mdtDateFilterUtils.js +135 -0
  33. package/src/mdtExportUtils.js +234 -0
  34. package/src/mdtFilterUtils.js +56 -0
  35. package/src/mdtInlineFieldDisplay.js +66 -0
  36. package/src/mdtMobilePopupPosition.js +107 -0
  37. package/src/mdtMultiSelectDisplay.jsx +279 -0
  38. package/src/mdtSavedViewFilterMatch.js +154 -0
  39. package/src/mdtTabChangeUtils.js +12 -0
  40. package/src/mdtTabStatusRules.js +111 -0
  41. package/src/mdtTableConstants.js +2 -0
  42. package/src/mdtTooltipFormat.js +44 -0
  43. 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
+ }