@camstack/ui-library 1.2.294 → 1.2.295
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.
|
@@ -86,8 +86,14 @@ export interface DeviceListGenericProps<T> {
|
|
|
86
86
|
* may include `PAGE_SIZE_ALL`. When set, `pageSize` is ignored.
|
|
87
87
|
*/
|
|
88
88
|
readonly pageSizeSelector?: GenericPageSizeSelector;
|
|
89
|
-
/**
|
|
90
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Optional control rendered next to the search box (e.g. a Refresh button).
|
|
91
|
+
* A function receives the rows the search and type filter currently leave
|
|
92
|
+
* (every page), for controls that act on "what is shown".
|
|
93
|
+
*/
|
|
94
|
+
readonly toolbar?: ReactNode | ((ctx: {
|
|
95
|
+
readonly filtered: readonly T[];
|
|
96
|
+
}) => ReactNode);
|
|
91
97
|
}
|
|
92
98
|
/**
|
|
93
99
|
* Generic render-prop list: search + pagination chrome shared with the device
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk selection for the device-export table (D875): choose by role and by
|
|
3
|
+
* provider, then select or deselect everything the filters leave on screen.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions, so the rules are pinned without a DOM. Nothing here hides a
|
|
6
|
+
* device by default: an empty filter keeps every row (the operator's table is
|
|
7
|
+
* the only filter, D869).
|
|
8
|
+
*/
|
|
9
|
+
import type { MultiSelectOption } from "./device-list/multi-select";
|
|
10
|
+
/** The slice of a device row the bulk filters read. */
|
|
11
|
+
export interface ExportFilterable {
|
|
12
|
+
readonly id: number;
|
|
13
|
+
readonly addonId: string;
|
|
14
|
+
readonly role?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface ExportBulkFilters {
|
|
17
|
+
/** Keep rows whose role is one of these; empty = no role filter. */
|
|
18
|
+
readonly roles: readonly string[];
|
|
19
|
+
/** Keep rows whose provider addon is one of these; empty = no provider filter. */
|
|
20
|
+
readonly providers: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** The role filter value of a row that has no role. */
|
|
23
|
+
export declare const NO_ROLE_FILTER_ID = "(none)";
|
|
24
|
+
export declare const NO_EXPORT_FILTERS: ExportBulkFilters;
|
|
25
|
+
export declare function roleFilterId(device: ExportFilterable): string;
|
|
26
|
+
/** The rows the role / provider filters leave. */
|
|
27
|
+
export declare function applyExportFilters<T extends ExportFilterable>(devices: readonly T[], filters: ExportBulkFilters): readonly T[];
|
|
28
|
+
/** Distinct roles present among the rows (a roleless row counts as "(none)"). */
|
|
29
|
+
export declare function roleFilterOptions(devices: readonly ExportFilterable[]): readonly MultiSelectOption[];
|
|
30
|
+
/** Distinct provider addons present among the rows. */
|
|
31
|
+
export declare function providerFilterOptions(devices: readonly ExportFilterable[]): readonly MultiSelectOption[];
|
|
32
|
+
/** `current` plus every shown row. */
|
|
33
|
+
export declare function selectShown(current: ReadonlySet<string>, shown: readonly ExportFilterable[]): ReadonlySet<string>;
|
|
34
|
+
/** `current` minus every shown row; selections outside the filter are kept. */
|
|
35
|
+
export declare function deselectShown(current: ReadonlySet<string>, shown: readonly ExportFilterable[]): ReadonlySet<string>;
|
package/dist/index.cjs
CHANGED
|
@@ -5368,6 +5368,11 @@ var DEVICE_ROLE_META = {
|
|
|
5368
5368
|
labelKey: "uiLibrary.deviceMeta.role.binaryHelper",
|
|
5369
5369
|
labelFallback: "Binary helper"
|
|
5370
5370
|
},
|
|
5371
|
+
[_camstack_types.DeviceRole.Outlet]: {
|
|
5372
|
+
icon: Plug,
|
|
5373
|
+
labelKey: "uiLibrary.deviceMeta.role.outlet",
|
|
5374
|
+
labelFallback: "Outlet"
|
|
5375
|
+
},
|
|
5371
5376
|
[_camstack_types.DeviceRole.MotionSensor]: {
|
|
5372
5377
|
icon: Footprints,
|
|
5373
5378
|
labelKey: "uiLibrary.deviceMeta.role.motionSensor",
|
|
@@ -55003,7 +55008,7 @@ function GenericModeList(props) {
|
|
|
55003
55008
|
"aria-label": searchPlaceholder,
|
|
55004
55009
|
className: "h-7 pl-7 pr-2.5 rounded-md border border-border bg-surface text-[11px] text-foreground placeholder:text-foreground-subtle/60 focus:outline-none focus:ring-1 focus:ring-primary/40 w-56"
|
|
55005
55010
|
})]
|
|
55006
|
-
}), toolbar]
|
|
55011
|
+
}), typeof toolbar === "function" ? toolbar({ filtered }) : toolbar]
|
|
55007
55012
|
})]
|
|
55008
55013
|
}),
|
|
55009
55014
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -55135,6 +55140,47 @@ function DeviceList(props) {
|
|
|
55135
55140
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceModeList, { ...props });
|
|
55136
55141
|
}
|
|
55137
55142
|
//#endregion
|
|
55143
|
+
//#region src/composites/export-bulk-selection.ts
|
|
55144
|
+
/** The role filter value of a row that has no role. */
|
|
55145
|
+
var NO_ROLE_FILTER_ID = "(none)";
|
|
55146
|
+
var NO_EXPORT_FILTERS = {
|
|
55147
|
+
roles: [],
|
|
55148
|
+
providers: []
|
|
55149
|
+
};
|
|
55150
|
+
function roleFilterId(device) {
|
|
55151
|
+
const role = device.role;
|
|
55152
|
+
return role === null || role === void 0 || role === "" ? NO_ROLE_FILTER_ID : role;
|
|
55153
|
+
}
|
|
55154
|
+
/** The rows the role / provider filters leave. */
|
|
55155
|
+
function applyExportFilters(devices, filters) {
|
|
55156
|
+
const roles = new Set(filters.roles);
|
|
55157
|
+
const providers = new Set(filters.providers);
|
|
55158
|
+
return devices.filter((d) => (roles.size === 0 || roles.has(roleFilterId(d))) && (providers.size === 0 || providers.has(d.addonId)));
|
|
55159
|
+
}
|
|
55160
|
+
function distinctOptions(values) {
|
|
55161
|
+
return [...new Set(values)].toSorted().map((id) => ({
|
|
55162
|
+
id,
|
|
55163
|
+
label: id
|
|
55164
|
+
}));
|
|
55165
|
+
}
|
|
55166
|
+
/** Distinct roles present among the rows (a roleless row counts as "(none)"). */
|
|
55167
|
+
function roleFilterOptions(devices) {
|
|
55168
|
+
return distinctOptions(devices.map(roleFilterId));
|
|
55169
|
+
}
|
|
55170
|
+
/** Distinct provider addons present among the rows. */
|
|
55171
|
+
function providerFilterOptions(devices) {
|
|
55172
|
+
return distinctOptions(devices.map((d) => d.addonId));
|
|
55173
|
+
}
|
|
55174
|
+
/** `current` plus every shown row. */
|
|
55175
|
+
function selectShown(current, shown) {
|
|
55176
|
+
return new Set([...current, ...shown.map((d) => String(d.id))]);
|
|
55177
|
+
}
|
|
55178
|
+
/** `current` minus every shown row; selections outside the filter are kept. */
|
|
55179
|
+
function deselectShown(current, shown) {
|
|
55180
|
+
const drop = new Set(shown.map((d) => String(d.id)));
|
|
55181
|
+
return new Set([...current].filter((id) => !drop.has(id)));
|
|
55182
|
+
}
|
|
55183
|
+
//#endregion
|
|
55138
55184
|
//#region src/composites/device-export-panel.tsx
|
|
55139
55185
|
/**
|
|
55140
55186
|
* DeviceExportPanel — generic, addon-agnostic surface for the
|
|
@@ -55541,6 +55587,18 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55541
55587
|
labelFor,
|
|
55542
55588
|
running
|
|
55543
55589
|
]);
|
|
55590
|
+
const [bulkFilters, setBulkFilters] = (0, react$1.useState)(NO_EXPORT_FILTERS);
|
|
55591
|
+
const roleOptions = (0, react$1.useMemo)(() => roleFilterOptions(candidateDevices), [candidateDevices]);
|
|
55592
|
+
const providerOptions = (0, react$1.useMemo)(() => providerFilterOptions(candidateDevices), [candidateDevices]);
|
|
55593
|
+
const shownDevices = (0, react$1.useMemo)(() => applyExportFilters(candidateDevices, bulkFilters), [candidateDevices, bulkFilters]);
|
|
55594
|
+
const setRoleFilter = (0, react$1.useCallback)((roles) => setBulkFilters((f) => ({
|
|
55595
|
+
...f,
|
|
55596
|
+
roles
|
|
55597
|
+
})), []);
|
|
55598
|
+
const setProviderFilter = (0, react$1.useCallback)((providers) => setBulkFilters((f) => ({
|
|
55599
|
+
...f,
|
|
55600
|
+
providers
|
|
55601
|
+
})), []);
|
|
55544
55602
|
const selectedList = (0, react$1.useMemo)(() => [...selectedIds], [selectedIds]);
|
|
55545
55603
|
const exposeSelected = (0, react$1.useCallback)(() => {
|
|
55546
55604
|
runExport("expose", selectedList);
|
|
@@ -55611,36 +55669,75 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55611
55669
|
toggleSelection,
|
|
55612
55670
|
t
|
|
55613
55671
|
]);
|
|
55614
|
-
const
|
|
55615
|
-
className: "inline-flex items-center gap-1.5",
|
|
55672
|
+
const renderToolbar = (0, react$1.useCallback)(({ filtered }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
55673
|
+
className: "inline-flex flex-wrap items-center gap-1.5",
|
|
55616
55674
|
children: [
|
|
55617
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
55618
|
-
"
|
|
55619
|
-
|
|
55620
|
-
|
|
55621
|
-
|
|
55622
|
-
|
|
55623
|
-
|
|
55624
|
-
|
|
55625
|
-
|
|
55626
|
-
|
|
55627
|
-
|
|
55675
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiSelectDropdown, {
|
|
55676
|
+
label: t("uiLibrary.deviceExport.filterRole", "Role"),
|
|
55677
|
+
options: roleOptions,
|
|
55678
|
+
selected: bulkFilters.roles,
|
|
55679
|
+
onChange: setRoleFilter
|
|
55680
|
+
}),
|
|
55681
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiSelectDropdown, {
|
|
55682
|
+
label: t("uiLibrary.deviceExport.filterProvider", "Provider"),
|
|
55683
|
+
options: providerOptions,
|
|
55684
|
+
selected: bulkFilters.providers,
|
|
55685
|
+
onChange: setProviderFilter
|
|
55628
55686
|
}),
|
|
55629
55687
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
55630
55688
|
size: "sm",
|
|
55631
55689
|
variant: "secondary",
|
|
55632
|
-
disabled:
|
|
55633
|
-
onClick:
|
|
55634
|
-
children: t("uiLibrary.deviceExport.
|
|
55690
|
+
disabled: filtered.length === 0,
|
|
55691
|
+
onClick: () => setSelectedIds((current) => selectShown(current, filtered)),
|
|
55692
|
+
children: t("uiLibrary.deviceExport.selectShown", "Select {{count}} shown", { count: filtered.length })
|
|
55635
55693
|
}),
|
|
55636
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
55637
|
-
|
|
55638
|
-
|
|
55639
|
-
|
|
55640
|
-
|
|
55641
|
-
|
|
55694
|
+
selectedIds.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
55695
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
55696
|
+
size: "sm",
|
|
55697
|
+
variant: "ghost",
|
|
55698
|
+
onClick: () => setSelectedIds((current) => deselectShown(current, filtered)),
|
|
55699
|
+
children: t("uiLibrary.deviceExport.deselectShown", "Deselect shown")
|
|
55700
|
+
}),
|
|
55701
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55702
|
+
"data-testid": "export-selection-count",
|
|
55703
|
+
className: "text-[11px] text-foreground-subtle",
|
|
55704
|
+
children: t("uiLibrary.deviceExport.selected", "{{count}} selected", { count: selectedIds.size })
|
|
55705
|
+
}),
|
|
55706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
55707
|
+
size: "sm",
|
|
55708
|
+
variant: "primary",
|
|
55709
|
+
disabled: busy,
|
|
55710
|
+
onClick: exposeSelected,
|
|
55711
|
+
children: t("uiLibrary.deviceExport.exposeN", "Expose {{count}}", { count: selectedIds.size })
|
|
55712
|
+
}),
|
|
55713
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
55714
|
+
size: "sm",
|
|
55715
|
+
variant: "secondary",
|
|
55716
|
+
disabled: busy,
|
|
55717
|
+
onClick: unexposeSelected,
|
|
55718
|
+
children: t("uiLibrary.deviceExport.unexposeN", "Unexpose {{count}}", { count: selectedIds.size })
|
|
55719
|
+
}),
|
|
55720
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
55721
|
+
size: "sm",
|
|
55722
|
+
variant: "ghost",
|
|
55723
|
+
onClick: clearSelection,
|
|
55724
|
+
children: t("uiLibrary.deviceExport.clear", "Clear")
|
|
55725
|
+
})
|
|
55726
|
+
] })
|
|
55642
55727
|
]
|
|
55643
|
-
})
|
|
55728
|
+
}), [
|
|
55729
|
+
t,
|
|
55730
|
+
roleOptions,
|
|
55731
|
+
providerOptions,
|
|
55732
|
+
bulkFilters,
|
|
55733
|
+
setRoleFilter,
|
|
55734
|
+
setProviderFilter,
|
|
55735
|
+
selectedIds.size,
|
|
55736
|
+
busy,
|
|
55737
|
+
exposeSelected,
|
|
55738
|
+
unexposeSelected,
|
|
55739
|
+
clearSelection
|
|
55740
|
+
]);
|
|
55644
55741
|
const hasSupportedKinds = supportedKindList.length > 0;
|
|
55645
55742
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55646
55743
|
className: "rounded-lg border border-border bg-surface overflow-hidden",
|
|
@@ -55682,14 +55779,14 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55682
55779
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }), t("uiLibrary.deviceExport.loading", "Loading exportable devices…")]
|
|
55683
55780
|
}) : hasSupportedKinds && candidateDevices.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceList, {
|
|
55684
55781
|
mode: "generic",
|
|
55685
|
-
items:
|
|
55782
|
+
items: shownDevices,
|
|
55686
55783
|
getKey,
|
|
55687
55784
|
getSearchText,
|
|
55688
55785
|
getFilterType,
|
|
55689
55786
|
filterOptions: supportedTypeOptions,
|
|
55690
55787
|
columns,
|
|
55691
55788
|
onRowClick: onOpenDevice ? onRowClick : void 0,
|
|
55692
|
-
toolbar:
|
|
55789
|
+
toolbar: renderToolbar,
|
|
55693
55790
|
pageSizeSelector: DEVICE_EXPORT_PAGE_SIZE,
|
|
55694
55791
|
searchPlaceholder: t("uiLibrary.deviceExport.search", "Search by name or stableId"),
|
|
55695
55792
|
emptyLabel: t("uiLibrary.deviceExport.noMatch", "No device matches this search.")
|
package/dist/index.js
CHANGED
|
@@ -5344,6 +5344,11 @@ var DEVICE_ROLE_META = {
|
|
|
5344
5344
|
labelKey: "uiLibrary.deviceMeta.role.binaryHelper",
|
|
5345
5345
|
labelFallback: "Binary helper"
|
|
5346
5346
|
},
|
|
5347
|
+
[DeviceRole.Outlet]: {
|
|
5348
|
+
icon: Plug,
|
|
5349
|
+
labelKey: "uiLibrary.deviceMeta.role.outlet",
|
|
5350
|
+
labelFallback: "Outlet"
|
|
5351
|
+
},
|
|
5347
5352
|
[DeviceRole.MotionSensor]: {
|
|
5348
5353
|
icon: Footprints,
|
|
5349
5354
|
labelKey: "uiLibrary.deviceMeta.role.motionSensor",
|
|
@@ -54979,7 +54984,7 @@ function GenericModeList(props) {
|
|
|
54979
54984
|
"aria-label": searchPlaceholder,
|
|
54980
54985
|
className: "h-7 pl-7 pr-2.5 rounded-md border border-border bg-surface text-[11px] text-foreground placeholder:text-foreground-subtle/60 focus:outline-none focus:ring-1 focus:ring-primary/40 w-56"
|
|
54981
54986
|
})]
|
|
54982
|
-
}), toolbar]
|
|
54987
|
+
}), typeof toolbar === "function" ? toolbar({ filtered }) : toolbar]
|
|
54983
54988
|
})]
|
|
54984
54989
|
}),
|
|
54985
54990
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -55111,6 +55116,47 @@ function DeviceList(props) {
|
|
|
55111
55116
|
return /* @__PURE__ */ jsx(DeviceModeList, { ...props });
|
|
55112
55117
|
}
|
|
55113
55118
|
//#endregion
|
|
55119
|
+
//#region src/composites/export-bulk-selection.ts
|
|
55120
|
+
/** The role filter value of a row that has no role. */
|
|
55121
|
+
var NO_ROLE_FILTER_ID = "(none)";
|
|
55122
|
+
var NO_EXPORT_FILTERS = {
|
|
55123
|
+
roles: [],
|
|
55124
|
+
providers: []
|
|
55125
|
+
};
|
|
55126
|
+
function roleFilterId(device) {
|
|
55127
|
+
const role = device.role;
|
|
55128
|
+
return role === null || role === void 0 || role === "" ? NO_ROLE_FILTER_ID : role;
|
|
55129
|
+
}
|
|
55130
|
+
/** The rows the role / provider filters leave. */
|
|
55131
|
+
function applyExportFilters(devices, filters) {
|
|
55132
|
+
const roles = new Set(filters.roles);
|
|
55133
|
+
const providers = new Set(filters.providers);
|
|
55134
|
+
return devices.filter((d) => (roles.size === 0 || roles.has(roleFilterId(d))) && (providers.size === 0 || providers.has(d.addonId)));
|
|
55135
|
+
}
|
|
55136
|
+
function distinctOptions(values) {
|
|
55137
|
+
return [...new Set(values)].toSorted().map((id) => ({
|
|
55138
|
+
id,
|
|
55139
|
+
label: id
|
|
55140
|
+
}));
|
|
55141
|
+
}
|
|
55142
|
+
/** Distinct roles present among the rows (a roleless row counts as "(none)"). */
|
|
55143
|
+
function roleFilterOptions(devices) {
|
|
55144
|
+
return distinctOptions(devices.map(roleFilterId));
|
|
55145
|
+
}
|
|
55146
|
+
/** Distinct provider addons present among the rows. */
|
|
55147
|
+
function providerFilterOptions(devices) {
|
|
55148
|
+
return distinctOptions(devices.map((d) => d.addonId));
|
|
55149
|
+
}
|
|
55150
|
+
/** `current` plus every shown row. */
|
|
55151
|
+
function selectShown(current, shown) {
|
|
55152
|
+
return new Set([...current, ...shown.map((d) => String(d.id))]);
|
|
55153
|
+
}
|
|
55154
|
+
/** `current` minus every shown row; selections outside the filter are kept. */
|
|
55155
|
+
function deselectShown(current, shown) {
|
|
55156
|
+
const drop = new Set(shown.map((d) => String(d.id)));
|
|
55157
|
+
return new Set([...current].filter((id) => !drop.has(id)));
|
|
55158
|
+
}
|
|
55159
|
+
//#endregion
|
|
55114
55160
|
//#region src/composites/device-export-panel.tsx
|
|
55115
55161
|
/**
|
|
55116
55162
|
* DeviceExportPanel — generic, addon-agnostic surface for the
|
|
@@ -55517,6 +55563,18 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55517
55563
|
labelFor,
|
|
55518
55564
|
running
|
|
55519
55565
|
]);
|
|
55566
|
+
const [bulkFilters, setBulkFilters] = useState(NO_EXPORT_FILTERS);
|
|
55567
|
+
const roleOptions = useMemo(() => roleFilterOptions(candidateDevices), [candidateDevices]);
|
|
55568
|
+
const providerOptions = useMemo(() => providerFilterOptions(candidateDevices), [candidateDevices]);
|
|
55569
|
+
const shownDevices = useMemo(() => applyExportFilters(candidateDevices, bulkFilters), [candidateDevices, bulkFilters]);
|
|
55570
|
+
const setRoleFilter = useCallback((roles) => setBulkFilters((f) => ({
|
|
55571
|
+
...f,
|
|
55572
|
+
roles
|
|
55573
|
+
})), []);
|
|
55574
|
+
const setProviderFilter = useCallback((providers) => setBulkFilters((f) => ({
|
|
55575
|
+
...f,
|
|
55576
|
+
providers
|
|
55577
|
+
})), []);
|
|
55520
55578
|
const selectedList = useMemo(() => [...selectedIds], [selectedIds]);
|
|
55521
55579
|
const exposeSelected = useCallback(() => {
|
|
55522
55580
|
runExport("expose", selectedList);
|
|
@@ -55587,36 +55645,75 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55587
55645
|
toggleSelection,
|
|
55588
55646
|
t
|
|
55589
55647
|
]);
|
|
55590
|
-
const
|
|
55591
|
-
className: "inline-flex items-center gap-1.5",
|
|
55648
|
+
const renderToolbar = useCallback(({ filtered }) => /* @__PURE__ */ jsxs("span", {
|
|
55649
|
+
className: "inline-flex flex-wrap items-center gap-1.5",
|
|
55592
55650
|
children: [
|
|
55593
|
-
/* @__PURE__ */ jsx(
|
|
55594
|
-
"
|
|
55595
|
-
|
|
55596
|
-
|
|
55597
|
-
|
|
55598
|
-
|
|
55599
|
-
|
|
55600
|
-
|
|
55601
|
-
|
|
55602
|
-
|
|
55603
|
-
|
|
55651
|
+
/* @__PURE__ */ jsx(MultiSelectDropdown, {
|
|
55652
|
+
label: t("uiLibrary.deviceExport.filterRole", "Role"),
|
|
55653
|
+
options: roleOptions,
|
|
55654
|
+
selected: bulkFilters.roles,
|
|
55655
|
+
onChange: setRoleFilter
|
|
55656
|
+
}),
|
|
55657
|
+
/* @__PURE__ */ jsx(MultiSelectDropdown, {
|
|
55658
|
+
label: t("uiLibrary.deviceExport.filterProvider", "Provider"),
|
|
55659
|
+
options: providerOptions,
|
|
55660
|
+
selected: bulkFilters.providers,
|
|
55661
|
+
onChange: setProviderFilter
|
|
55604
55662
|
}),
|
|
55605
55663
|
/* @__PURE__ */ jsx(Button, {
|
|
55606
55664
|
size: "sm",
|
|
55607
55665
|
variant: "secondary",
|
|
55608
|
-
disabled:
|
|
55609
|
-
onClick:
|
|
55610
|
-
children: t("uiLibrary.deviceExport.
|
|
55666
|
+
disabled: filtered.length === 0,
|
|
55667
|
+
onClick: () => setSelectedIds((current) => selectShown(current, filtered)),
|
|
55668
|
+
children: t("uiLibrary.deviceExport.selectShown", "Select {{count}} shown", { count: filtered.length })
|
|
55611
55669
|
}),
|
|
55612
|
-
/* @__PURE__ */
|
|
55613
|
-
|
|
55614
|
-
|
|
55615
|
-
|
|
55616
|
-
|
|
55617
|
-
|
|
55670
|
+
selectedIds.size > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
55671
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55672
|
+
size: "sm",
|
|
55673
|
+
variant: "ghost",
|
|
55674
|
+
onClick: () => setSelectedIds((current) => deselectShown(current, filtered)),
|
|
55675
|
+
children: t("uiLibrary.deviceExport.deselectShown", "Deselect shown")
|
|
55676
|
+
}),
|
|
55677
|
+
/* @__PURE__ */ jsx("span", {
|
|
55678
|
+
"data-testid": "export-selection-count",
|
|
55679
|
+
className: "text-[11px] text-foreground-subtle",
|
|
55680
|
+
children: t("uiLibrary.deviceExport.selected", "{{count}} selected", { count: selectedIds.size })
|
|
55681
|
+
}),
|
|
55682
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55683
|
+
size: "sm",
|
|
55684
|
+
variant: "primary",
|
|
55685
|
+
disabled: busy,
|
|
55686
|
+
onClick: exposeSelected,
|
|
55687
|
+
children: t("uiLibrary.deviceExport.exposeN", "Expose {{count}}", { count: selectedIds.size })
|
|
55688
|
+
}),
|
|
55689
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55690
|
+
size: "sm",
|
|
55691
|
+
variant: "secondary",
|
|
55692
|
+
disabled: busy,
|
|
55693
|
+
onClick: unexposeSelected,
|
|
55694
|
+
children: t("uiLibrary.deviceExport.unexposeN", "Unexpose {{count}}", { count: selectedIds.size })
|
|
55695
|
+
}),
|
|
55696
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55697
|
+
size: "sm",
|
|
55698
|
+
variant: "ghost",
|
|
55699
|
+
onClick: clearSelection,
|
|
55700
|
+
children: t("uiLibrary.deviceExport.clear", "Clear")
|
|
55701
|
+
})
|
|
55702
|
+
] })
|
|
55618
55703
|
]
|
|
55619
|
-
})
|
|
55704
|
+
}), [
|
|
55705
|
+
t,
|
|
55706
|
+
roleOptions,
|
|
55707
|
+
providerOptions,
|
|
55708
|
+
bulkFilters,
|
|
55709
|
+
setRoleFilter,
|
|
55710
|
+
setProviderFilter,
|
|
55711
|
+
selectedIds.size,
|
|
55712
|
+
busy,
|
|
55713
|
+
exposeSelected,
|
|
55714
|
+
unexposeSelected,
|
|
55715
|
+
clearSelection
|
|
55716
|
+
]);
|
|
55620
55717
|
const hasSupportedKinds = supportedKindList.length > 0;
|
|
55621
55718
|
return /* @__PURE__ */ jsxs("div", {
|
|
55622
55719
|
className: "rounded-lg border border-border bg-surface overflow-hidden",
|
|
@@ -55658,14 +55755,14 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
55658
55755
|
children: [/* @__PURE__ */ jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }), t("uiLibrary.deviceExport.loading", "Loading exportable devices…")]
|
|
55659
55756
|
}) : hasSupportedKinds && candidateDevices.length > 0 ? /* @__PURE__ */ jsx(DeviceList, {
|
|
55660
55757
|
mode: "generic",
|
|
55661
|
-
items:
|
|
55758
|
+
items: shownDevices,
|
|
55662
55759
|
getKey,
|
|
55663
55760
|
getSearchText,
|
|
55664
55761
|
getFilterType,
|
|
55665
55762
|
filterOptions: supportedTypeOptions,
|
|
55666
55763
|
columns,
|
|
55667
55764
|
onRowClick: onOpenDevice ? onRowClick : void 0,
|
|
55668
|
-
toolbar:
|
|
55765
|
+
toolbar: renderToolbar,
|
|
55669
55766
|
pageSizeSelector: DEVICE_EXPORT_PAGE_SIZE,
|
|
55670
55767
|
searchPlaceholder: t("uiLibrary.deviceExport.search", "Search by name or stableId"),
|
|
55671
55768
|
emptyLabel: t("uiLibrary.deviceExport.noMatch", "No device matches this search.")
|