Implement storing sorting and grouping for all tables (#20594)

This commit is contained in:
Bram Kragten 2024-04-24 11:06:00 +02:00 committed by GitHub
parent d9b71e754d
commit 62de16bb8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 352 additions and 53 deletions

View File

@ -529,11 +529,7 @@ export class HaDataTable extends LitElement {
}
if (this.appendRow || this.hasFab || this.groupColumn) {
const items = [...data];
if (this.appendRow) {
items.push({ append: true, content: this.appendRow });
}
let items = [...data];
if (this.groupColumn) {
const grouped = groupBy(items, (item) => item[this.groupColumn!]);
@ -599,14 +595,18 @@ export class HaDataTable extends LitElement {
}
});
this._items = groupedItems;
} else {
this._items = items;
items = groupedItems;
}
if (this.appendRow) {
items.push({ append: true, content: this.appendRow });
}
if (this.hasFab) {
this._items = [...this._items, { empty: true }];
items.push({ empty: true });
}
this._items = items;
} else {
this._items = data;
}

View File

@ -37,15 +37,21 @@ import { computeCssColor } from "../../../common/color/compute-color";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { storage } from "../../../common/decorators/storage";
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import "../../../components/chips/ha-assist-chip";
import type {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/entity/ha-entity-toggle";
@ -105,10 +111,6 @@ import { showCategoryRegistryDetailDialog } from "../category/show-dialog-catego
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { showNewAutomationDialog } from "./show-dialog-new-automation";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type AutomationItem = AutomationEntity & {
name: string;
@ -156,6 +158,19 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
@state() private _overflowAutomation?: AutomationItem;
@storage({ key: "automation-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "automation-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "automation-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
@query("#overflow-menu") private _overflowMenu!: HaMenu;
private _sizeController = new ResizeController(this, {
@ -470,7 +485,12 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
this.hass.localize,
this.hass.locale
)}
initialGroupColumn="category"
.initialGroupColumn=${this._activeGrouping || "category"}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
.data=${automations}
.empty=${!this.automations.length}
@row-click=${this._handleRowClicked}
@ -1238,6 +1258,18 @@ ${rejected
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -24,6 +24,7 @@ import { extractSearchParam } from "../../../common/url/search-params";
import {
DataTableColumnContainer,
RowClickedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-button";
@ -54,6 +55,7 @@ import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config";
import { showAddBlueprintDialog } from "./show-dialog-import-blueprint";
import { storage } from "../../../common/decorators/storage";
type BlueprintMetaDataPath = BlueprintMetaData & {
path: string;
@ -92,8 +94,24 @@ class HaBlueprintOverview extends LitElement {
Blueprints
>;
@storage({ key: "blueprint-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "blueprint-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "blueprint-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
private _processedBlueprints = memoizeOne(
(blueprints: Record<string, Blueprints>): BlueprintMetaDataPath[] => {
(
blueprints: Record<string, Blueprints>,
localize: LocalizeFunc
): BlueprintMetaDataPath[] => {
const result: any[] = [];
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
@ -101,6 +119,9 @@ class HaBlueprintOverview extends LitElement {
result.push({
name: blueprint.error,
type,
translated_type: localize(
`ui.panel.config.blueprint.overview.types.${type as "automation" | "script"}`
),
error: true,
path,
fullpath: `${type}/${path}`,
@ -109,6 +130,9 @@ class HaBlueprintOverview extends LitElement {
result.push({
...blueprint.metadata,
type,
translated_type: localize(
`ui.panel.config.blueprint.overview.types.${type as "automation" | "script"}`
),
error: false,
path,
fullpath: `${type}/${path}`,
@ -140,14 +164,11 @@ class HaBlueprintOverview extends LitElement {
`
: undefined,
},
type: {
translated_type: {
title: localize("ui.panel.config.blueprint.overview.headers.type"),
template: (blueprint) =>
html`${this.hass.localize(
`ui.panel.config.blueprint.overview.types.${blueprint.type}`
)}`,
sortable: true,
filterable: true,
groupable: true,
hidden: narrow,
direction: "asc",
width: "10%",
@ -256,7 +277,7 @@ class HaBlueprintOverview extends LitElement {
this.hass.language,
this.hass.localize
)}
.data=${this._processedBlueprints(this.blueprints)}
.data=${this._processedBlueprints(this.blueprints, this.hass.localize)}
id="fullpath"
.noDataText=${this.hass.localize(
"ui.panel.config.blueprint.overview.no_blueprints"
@ -281,6 +302,12 @@ class HaBlueprintOverview extends LitElement {
>
</a>
</div>`}
.initialGroupColumn=${this._activeGrouping}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
>
<ha-icon-button
slot="toolbar-icon"
@ -341,9 +368,10 @@ class HaBlueprintOverview extends LitElement {
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const blueprint = this._processedBlueprints(this.blueprints).find(
(b) => b.fullpath === ev.detail.id
)!;
const blueprint = this._processedBlueprints(
this.blueprints,
this.hass.localize
).find((b) => b.fullpath === ev.detail.id)!;
if (blueprint.error) {
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.blueprint.overview.error", {
@ -502,6 +530,18 @@ class HaBlueprintOverview extends LitElement {
fireEvent(this, "reload-blueprints");
};
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return haStyle;
}

View File

@ -29,6 +29,7 @@ import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import memoize from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { storage } from "../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
@ -37,10 +38,15 @@ import {
protocolIntegrationPicked,
} from "../../../common/integrations/protocolIntegrationPicked";
import { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import type {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-alert";
@ -66,6 +72,11 @@ import {
removeEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import {
EntitySources,
fetchEntitySourcesWithCache,
} from "../../../data/entity_sources";
import { domainToName } from "../../../data/integration";
import {
LabelRegistryEntry,
createLabelRegistryEntry,
@ -86,15 +97,6 @@ import { configSections } from "../ha-panel-config";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
EntitySources,
fetchEntitySourcesWithCache,
} from "../../../data/entity_sources";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import { domainToName } from "../../../data/integration";
export interface StateEntity
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
@ -151,6 +153,19 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
@state() private _entitySources?: EntitySources;
@storage({ key: "entities-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "entities-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "entities-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
@ -265,7 +280,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
},
domain: {
title: localize("ui.panel.config.entities.picker.headers.domain"),
sortable: true,
sortable: false,
hidden: true,
filterable: true,
groupable: true,
@ -603,6 +618,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
.filter=${this._filter}
selectable
.selected=${this._selected.length}
.initialGroupColumn=${this._activeGrouping}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
@selection-changed=${this._handleSelectionChanged}
clickable
@clear-filter=${this._clearFilter}
@ -1205,6 +1226,18 @@ ${rejected
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -24,6 +24,7 @@ import {
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { storage } from "../../../common/decorators/storage";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { navigate } from "../../../common/navigate";
@ -40,6 +41,7 @@ import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-fab";
@ -139,6 +141,19 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public route!: Route;
@storage({ key: "helpers-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "helpers-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "helpers-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
@state() private _stateItems: HassEntity[] = [];
@state() private _entityEntries?: Record<string, EntityRegistryEntry>;
@ -525,7 +540,12 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
).length}
.columns=${this._columns(this.narrow, this.hass.localize)}
.data=${helpers}
initialGroupColumn="category"
.initialGroupColumn=${this._activeGrouping || "category"}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
.activeFilters=${this._activeFilters}
@clear-filter=${this._clearFilter}
@row-click=${this._openEditDialog}
@ -1020,6 +1040,18 @@ ${rejected
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -10,15 +10,17 @@ import { LitElement, PropertyValues, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import {
DataTableColumnContainer,
RowClickedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-relative-time";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-relative-time";
import {
LabelRegistryEntry,
LabelRegistryEntryMutableParams,
@ -35,7 +37,7 @@ import "../../../layouts/hass-tabs-subpage-data-table";
import { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "./show-dialog-label-detail";
import { navigate } from "../../../common/navigate";
import { storage } from "../../../common/decorators/storage";
@customElement("ha-config-labels")
export class HaConfigLabels extends LitElement {
@ -49,6 +51,13 @@ export class HaConfigLabels extends LitElement {
@state() private _labels: LabelRegistryEntry[] = [];
@storage({
key: "labels-table-sort",
state: false,
subscribe: false,
})
private _activeSorting?: SortingChangedEvent;
private _columns = memoizeOne((localize: LocalizeFunc) => {
const columns: DataTableColumnContainer<LabelRegistryEntry> = {
icon: {
@ -149,6 +158,8 @@ export class HaConfigLabels extends LitElement {
.data=${this._data(this._labels)}
.noDataText=${this.hass.localize("ui.panel.config.labels.no_labels")}
hasFab
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@row-click=${this._editLabel}
clickable
id="label_id"
@ -268,6 +279,10 @@ export class HaConfigLabels extends LitElement {
`/config/automation/dashboard?historyBack=1&label=${label.label_id}`
);
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
}
declare global {

View File

@ -16,6 +16,7 @@ import { stringCompare } from "../../../../common/string/compare";
import {
DataTableColumnContainer,
RowClickedEvent,
SortingChangedEvent,
} from "../../../../components/data-table/ha-data-table";
import "../../../../components/ha-clickable-list-item";
import "../../../../components/ha-fab";
@ -46,6 +47,7 @@ import { showNewDashboardDialog } from "../../dashboard/show-dialog-new-dashboar
import { lovelaceTabs } from "../ha-config-lovelace";
import { showDashboardConfigureStrategyDialog } from "./show-dialog-lovelace-dashboard-configure-strategy";
import { showDashboardDetailDialog } from "./show-dialog-lovelace-dashboard-detail";
import { storage } from "../../../../common/decorators/storage";
type DataTableItem = Pick<
LovelaceDashboard,
@ -68,6 +70,13 @@ export class HaConfigLovelaceDashboards extends LitElement {
@state() private _dashboards: LovelaceDashboard[] = [];
@storage({
key: "lovelace-dashboards-table-sort",
state: false,
subscribe: false,
})
private _activeSorting?: SortingChangedEvent;
public willUpdate() {
if (!this.hasUpdated) {
this.hass.loadFragmentTranslation("lovelace");
@ -293,6 +302,8 @@ export class HaConfigLovelaceDashboards extends LitElement {
this.hass.localize
)}
.data=${this._getItems(this._dashboards)}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@row-click=${this._editDashboard}
id="url_path"
hasFab
@ -440,6 +451,10 @@ export class HaConfigLovelaceDashboards extends LitElement {
},
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
}
declare global {

View File

@ -10,9 +10,11 @@ import {
import { customElement, property, state } from "lit/decorators";
import memoize from "memoize-one";
import { stringCompare } from "../../../../common/string/compare";
import { LocalizeFunc } from "../../../../common/translations/localize";
import {
DataTableColumnContainer,
RowClickedEvent,
SortingChangedEvent,
} from "../../../../components/data-table/ha-data-table";
import "../../../../components/ha-card";
import "../../../../components/ha-fab";
@ -33,10 +35,10 @@ import "../../../../layouts/hass-subpage";
import "../../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../../resources/styles";
import { HomeAssistant, Route } from "../../../../types";
import { LocalizeFunc } from "../../../../common/translations/localize";
import { loadLovelaceResources } from "../../../lovelace/common/load-resources";
import { lovelaceResourcesTabs } from "../ha-config-lovelace";
import { showResourceDetailDialog } from "./show-dialog-lovelace-resource-detail";
import { storage } from "../../../../common/decorators/storage";
@customElement("ha-config-lovelace-resources")
export class HaConfigLovelaceRescources extends LitElement {
@ -50,6 +52,13 @@ export class HaConfigLovelaceRescources extends LitElement {
@state() private _resources: LovelaceResource[] = [];
@storage({
key: "lovelace-resources-table-sort",
state: false,
subscribe: false,
})
private _activeSorting?: SortingChangedEvent;
private _columns = memoize(
(
_language,
@ -127,6 +136,8 @@ export class HaConfigLovelaceRescources extends LitElement {
.noDataText=${this.hass.localize(
"ui.panel.config.lovelace.resources.picker.no_resources"
)}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@row-click=${this._editResource}
hasFab
clickable
@ -237,6 +248,10 @@ export class HaConfigLovelaceRescources extends LitElement {
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -32,14 +32,20 @@ import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { storage } from "../../../common/decorators/storage";
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-button";
@ -95,10 +101,6 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type SceneItem = SceneEntity & {
name: string;
@ -144,6 +146,19 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
@consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[];
@storage({ key: "scene-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "scene-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "scene-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
private _sizeController = new ResizeController(this, {
callback: (entries) => entries[0]?.contentRect.width,
});
@ -463,7 +478,12 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
).length}
.columns=${this._columns(this.narrow, this.hass.localize)}
id="entity_id"
initialGroupColumn="category"
.initialGroupColumn=${this._activeGrouping || "category"}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
.data=${scenes}
.empty=${!this.scenes.length}
.activeFilters=${this._activeFilters}
@ -975,6 +995,18 @@ ${rejected
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -33,14 +33,20 @@ import { computeCssColor } from "../../../common/color/compute-color";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { storage } from "../../../common/decorators/storage";
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-fab";
@ -97,10 +103,6 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type ScriptItem = ScriptEntity & {
name: string;
@ -148,6 +150,19 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
@consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[];
@storage({ key: "script-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "script-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "script-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
private _sizeController = new ResizeController(this, {
callback: (entries) => entries[0]?.contentRect.width,
});
@ -462,7 +477,12 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
{ number: scripts.length }
)}
hasFilters
initialGroupColumn="category"
.initialGroupColumn=${this._activeGrouping || "category"}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
selectable
.selected=${this._selected.length}
@selection-changed=${this._handleSelectionChanged}
@ -1091,6 +1111,18 @@ ${rejected
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -7,6 +7,7 @@ import { LocalizeFunc } from "../../../common/translations/localize";
import {
DataTableColumnContainer,
RowClickedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-icon";
import "../../../components/ha-fab";
@ -25,6 +26,7 @@ import { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
import { showAddUserDialog } from "./show-dialog-add-user";
import { showUserDetailDialog } from "./show-dialog-user-detail";
import { storage } from "../../../common/decorators/storage";
@customElement("ha-config-users")
export class HaConfigUsers extends LitElement {
@ -38,6 +40,19 @@ export class HaConfigUsers extends LitElement {
@state() private _users: User[] = [];
@storage({ key: "users-table-sort", state: false, subscribe: false })
private _activeSorting?: SortingChangedEvent;
@storage({ key: "users-table-grouping", state: false, subscribe: false })
private _activeGrouping?: string;
@storage({
key: "users-table-collapsed",
state: false,
subscribe: false,
})
private _activeCollapsed?: string;
private _columns = memoizeOne(
(narrow: boolean, localize: LocalizeFunc): DataTableColumnContainer => {
const columns: DataTableColumnContainer<User> = {
@ -70,16 +85,14 @@ export class HaConfigUsers extends LitElement {
hidden: narrow,
template: (user) => html`${user.username || "—"}`,
},
group_ids: {
group: {
title: localize("ui.panel.config.users.picker.headers.group"),
sortable: true,
filterable: true,
groupable: true,
width: "20%",
direction: "asc",
hidden: narrow,
template: (user) => html`
${localize(`groups.${user.group_ids[0]}`)}
`,
},
is_active: {
title: this.hass.localize(
@ -164,7 +177,13 @@ export class HaConfigUsers extends LitElement {
backPath="/config"
.tabs=${configSections.persons}
.columns=${this._columns(this.narrow, this.hass.localize)}
.data=${this._users}
.data=${this._userData(this._users, this.hass.localize)}
.initialGroupColumn=${this._activeGrouping}
.initialCollapsedGroups=${this._activeCollapsed}
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@grouping-changed=${this._handleGroupingChanged}
@collapsed-changed=${this._handleCollapseChanged}
@row-click=${this._editUser}
hasFab
clickable
@ -181,6 +200,13 @@ export class HaConfigUsers extends LitElement {
`;
}
private _userData = memoizeOne((users: User[], localize: LocalizeFunc) =>
users.map((user) => ({
...user,
group: localize(`groups.${user.group_ids[0]}`),
}))
);
private async _fetchUsers() {
this._users = await fetchUsers(this.hass);
@ -245,6 +271,18 @@ export class HaConfigUsers extends LitElement {
},
});
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
private _handleGroupingChanged(ev: CustomEvent) {
this._activeGrouping = ev.detail.value;
}
private _handleCollapseChanged(ev: CustomEvent) {
this._activeCollapsed = ev.detail.value;
}
}
declare global {

View File

@ -23,6 +23,7 @@ import {
DataTableRowData,
RowClickedEvent,
SelectionChangedEvent,
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-fab";
import { AlexaEntity, fetchCloudAlexaEntities } from "../../../data/alexa";
@ -52,6 +53,7 @@ import "./expose/expose-assistant-icon";
import { voiceAssistantTabs } from "./ha-config-voice-assistants";
import { showExposeEntityDialog } from "./show-dialog-expose-entity";
import { showVoiceSettingsDialog } from "./show-dialog-voice-settings";
import { storage } from "../../../common/decorators/storage";
@customElement("ha-config-voice-assistants-expose")
export class VoiceAssistantsExpose extends LitElement {
@ -87,6 +89,13 @@ export class VoiceAssistantsExpose extends LitElement {
string[] | undefined
>;
@storage({
key: "voice-expose-table-sort",
state: false,
subscribe: false,
})
private _activeSorting?: SortingChangedEvent;
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
@ -505,6 +514,8 @@ export class VoiceAssistantsExpose extends LitElement {
selectable
.selected=${this._selectedEntities.length}
clickable
.initialSorting=${this._activeSorting}
@sorting-changed=${this._handleSortingChanged}
@selection-changed=${this._handleSelectionChanged}
@clear-filter=${this._clearFilter}
@search-changed=${this._handleSearchChange}
@ -696,6 +707,10 @@ export class VoiceAssistantsExpose extends LitElement {
navigate(window.location.pathname, { replace: true });
}
private _handleSortingChanged(ev: CustomEvent) {
this._activeSorting = ev.detail;
}
static get styles(): CSSResultGroup {
return [
haStyle,