@sukramj/fabric-ui 0.1.2 → 0.1.3
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sukramj/fabric-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The web interface of go-fabric nodes: Svelte 5 pages, UI kit, design tokens with a Home Assistant theme bridge, and the generated API client.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SukramJ",
|
|
@@ -681,7 +681,7 @@ export interface components {
|
|
|
681
681
|
label: string;
|
|
682
682
|
/** @description Offer a value filter built from the listed values. */
|
|
683
683
|
filter: boolean;
|
|
684
|
-
/** @description Default false. The field is
|
|
684
|
+
/** @description Default false. The field is list-valued: its values are the item's lists entry for the key (or, deprecated until v0.2.0, the comma split of its fields entry when lists has no such key); shown as chips, filtered by membership, the filter's options built from the individual values. */
|
|
685
685
|
multi?: boolean;
|
|
686
686
|
};
|
|
687
687
|
FilterOption: {
|
|
@@ -692,7 +692,7 @@ export interface components {
|
|
|
692
692
|
field: string;
|
|
693
693
|
label: string;
|
|
694
694
|
options: components["schemas"]["FilterOption"][];
|
|
695
|
-
/** @description Default false. Match by membership in the
|
|
695
|
+
/** @description Default false. Match by membership in the item's list for the field (its lists entry, or, deprecated until v0.2.0, the comma split of its fields entry, values trimmed; an empty value never matches) instead of equality. */
|
|
696
696
|
multi?: boolean;
|
|
697
697
|
};
|
|
698
698
|
ExposureSchema: {
|
|
@@ -715,6 +715,10 @@ export interface components {
|
|
|
715
715
|
fields: {
|
|
716
716
|
[key: string]: string;
|
|
717
717
|
};
|
|
718
|
+
/** @description Optional. The values of list-valued (multi) fields, by field key: one entry per value, no separator semantics, so a value may contain a comma. Wins over the comma-separated fields entry of the same key. */
|
|
719
|
+
lists?: {
|
|
720
|
+
[key: string]: string[];
|
|
721
|
+
};
|
|
718
722
|
verdict: components["schemas"]["Verdict"];
|
|
719
723
|
enabled: boolean;
|
|
720
724
|
/** @description Where the verdict comes from */
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import LoadingState from "../../kit/LoadingState.svelte";
|
|
28
28
|
import Select from "../../kit/Select.svelte";
|
|
29
29
|
import HostWarnings from "./HostWarnings.svelte";
|
|
30
|
-
import { distinctValues, fieldMatches,
|
|
30
|
+
import { distinctValues, fieldMatches, itemValues } from "./fields";
|
|
31
31
|
|
|
32
32
|
const { t, toast, node } = useFabricUI();
|
|
33
33
|
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
const out: ValueFilter[] = schema.filters.map((f) => ({ field: f.field, label: f.label, multi: !!f.multi, options: f.options }));
|
|
65
65
|
for (const col of schema.columns) {
|
|
66
66
|
if (!col.filter || out.some((f) => f.field === col.key)) continue;
|
|
67
|
-
const values = distinctValues(node.items
|
|
67
|
+
const values = distinctValues(node.items, col.key, !!col.multi);
|
|
68
68
|
out.push({ field: col.key, label: col.label, multi: !!col.multi, options: values.map((v) => ({ value: v, label: v })) });
|
|
69
69
|
}
|
|
70
70
|
return out;
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
const match = makeTextMatcher(search);
|
|
84
84
|
return node.items.filter((item) => {
|
|
85
85
|
for (const [field, value] of Object.entries(filterValues)) {
|
|
86
|
-
if (value !== ALL && !fieldMatches(item
|
|
86
|
+
if (value !== ALL && !fieldMatches(item, field, value, multiField.has(field))) return false;
|
|
87
87
|
}
|
|
88
88
|
if (!search.trim()) return true;
|
|
89
89
|
const it = current(item);
|
|
90
|
-
return [it.name, it.group, it.verdict.device_type_name, ...Object.values(it.fields)].some((v) => v && match(v));
|
|
90
|
+
return [it.name, it.group, it.verdict.device_type_name, ...Object.values(it.fields), ...Object.values(it.lists ?? {}).flat()].some((v) => v && match(v));
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -184,7 +184,12 @@
|
|
|
184
184
|
{ key: "select", label: t("node.selection.col_select") },
|
|
185
185
|
{ key: "state", label: t("node.selection.col_state"), align: "center", get: (i) => stateInfo(i).label },
|
|
186
186
|
{ key: "name", label: t("node.selection.col_name"), sortable: true, title: true, get: (i) => current(i).name },
|
|
187
|
-
...(schema?.columns ?? []).map((c) => ({
|
|
187
|
+
...(schema?.columns ?? []).map((c) => ({
|
|
188
|
+
key: "field:" + c.key,
|
|
189
|
+
label: c.label,
|
|
190
|
+
sortable: true,
|
|
191
|
+
get: (i: Item) => (c.multi ? itemValues(i, c.key).join(", ") : (i.fields[c.key] ?? "")),
|
|
192
|
+
})),
|
|
188
193
|
{ key: "device_type", label: t("node.selection.col_device_type"), sortable: true, get: (i) => i.verdict.device_type_name || "—" },
|
|
189
194
|
]);
|
|
190
195
|
</script>
|
|
@@ -228,7 +233,7 @@
|
|
|
228
233
|
{:else if col.key === "device_type"}
|
|
229
234
|
<span class="text-[var(--ha-secondary-text-color)]">{item.verdict.device_type_name || "—"}</span>
|
|
230
235
|
{:else if multiColumn.has(col.key)}
|
|
231
|
-
{@const values =
|
|
236
|
+
{@const values = itemValues(item, col.key.slice("field:".length))}
|
|
232
237
|
{#if values.length === 0}
|
|
233
238
|
<span class="text-[var(--ha-secondary-text-color)]">—</span>
|
|
234
239
|
{:else}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
// List-valued item fields (Column.multi, Filter.multi): the provider
|
|
2
|
-
// carries
|
|
2
|
+
// carries the values in `lists[key]`, one entry per value, so a value may
|
|
3
|
+
// contain a comma. The comma-separated `fields[key]` is the deprecated
|
|
4
|
+
// fallback (until v0.2.0), read only when `lists` has no such key.
|
|
3
5
|
|
|
4
|
-
/** The
|
|
6
|
+
/** The part of an item the list helpers read. */
|
|
7
|
+
export type ListSource = {
|
|
8
|
+
fields: Record<string, string>;
|
|
9
|
+
lists?: Record<string, string[]>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The values of a comma-separated field, trimmed, without empty ones.
|
|
14
|
+
*
|
|
15
|
+
* @deprecated The comma form is accepted until v0.2.0; read
|
|
16
|
+
* {@link itemValues}, which prefers `lists`.
|
|
17
|
+
*/
|
|
5
18
|
export function listValues(field: string | undefined): string[] {
|
|
6
19
|
if (!field) return [];
|
|
7
20
|
return field
|
|
@@ -11,22 +24,44 @@ export function listValues(field: string | undefined): string[] {
|
|
|
11
24
|
}
|
|
12
25
|
|
|
13
26
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
27
|
+
* The values of an item's list-valued field: `lists[key]` when the key is
|
|
28
|
+
* present there (empty entries dropped, the rest as given), otherwise the
|
|
29
|
+
* comma split of `fields[key]`.
|
|
30
|
+
*/
|
|
31
|
+
export function itemValues(item: ListSource, key: string): string[] {
|
|
32
|
+
const list = item.lists?.[key];
|
|
33
|
+
if (list) return list.filter((v) => v !== "");
|
|
34
|
+
return listValues(item.fields[key]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether an item matches a filter value on a field: by equality, or, for
|
|
39
|
+
* a list-valued field, by membership in its list. An empty value never
|
|
16
40
|
* matches a list.
|
|
17
41
|
*/
|
|
18
|
-
export function fieldMatches(
|
|
19
|
-
|
|
42
|
+
export function fieldMatches(
|
|
43
|
+
item: ListSource,
|
|
44
|
+
key: string,
|
|
45
|
+
value: string,
|
|
46
|
+
multi: boolean,
|
|
47
|
+
): boolean {
|
|
48
|
+
if (!multi) return item.fields[key] === value;
|
|
20
49
|
const v = value.trim();
|
|
21
|
-
return v !== "" &&
|
|
50
|
+
return v !== "" && itemValues(item, key).includes(v);
|
|
22
51
|
}
|
|
23
52
|
|
|
24
53
|
/** The distinct filter values of a field across items, sorted for display. */
|
|
25
|
-
export function distinctValues(
|
|
54
|
+
export function distinctValues(
|
|
55
|
+
items: ListSource[],
|
|
56
|
+
key: string,
|
|
57
|
+
multi: boolean,
|
|
58
|
+
): string[] {
|
|
26
59
|
const values = new Set<string>();
|
|
27
|
-
for (const
|
|
28
|
-
if (multi) for (const v of
|
|
29
|
-
else if (
|
|
60
|
+
for (const item of items) {
|
|
61
|
+
if (multi) for (const v of itemValues(item, key)) values.add(v);
|
|
62
|
+
else if (item.fields[key]) values.add(item.fields[key]);
|
|
30
63
|
}
|
|
31
|
-
return [...values].sort((a, b) =>
|
|
64
|
+
return [...values].sort((a, b) =>
|
|
65
|
+
a.localeCompare(b, undefined, { sensitivity: "base", numeric: true }),
|
|
66
|
+
);
|
|
32
67
|
}
|