Skip to content

Commit d4f2e6d

Browse files
committed
Enonic UI: Add Filter Panel component #4185
1 parent 60bde18 commit d4f2e6d

File tree

10 files changed

+65
-326
lines changed

10 files changed

+65
-326
lines changed

modules/app/pnpm-lock.yaml

Lines changed: 3 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/app/src/main/resources/admin/tools/main/main.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<!-- Styles -->
3333
<link rel="stylesheet" type="text/css" href="{{assetsUri}}/admin/common/styles/lib.css">
3434
<link rel="stylesheet" type="text/css" href="{{assetsUri}}/styles/contentlib.css">
35+
<link rel="stylesheet" type="text/css" href="{{assetsUri}}/admin/common/styles/tailwind.css">
3536
<link rel="stylesheet" type="text/css" href="{{assetsUri}}/styles/tailwind.css">
3637
<script type="text/javascript" src="{{assetsUri}}/admin/common/js/lib.js"></script>
3738

modules/lib/pnpm-lock.yaml

Lines changed: 3 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/lib/src/main/resources/assets/js/app/browse/ContentBrowsePanel.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ export class ContentBrowsePanel
233233
}
234234

235235
protected disableSelectionMode() {
236-
this.filterPanel.resetConstraints();
237236
this.hideFilterPanel();
238237
super.disableSelectionMode();
239238
this.treeListBox.setFilterQuery(null);

modules/lib/src/main/resources/assets/js/app/browse/filter/AggregationsDisplayNamesResolver.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Aggregation} from '@enonic/lib-admin-ui/aggregation/Aggregation';
22
import {Bucket} from '@enonic/lib-admin-ui/aggregation/Bucket';
33
import {BucketAggregation} from '@enonic/lib-admin-ui/aggregation/BucketAggregation';
4+
import {AuthContext} from '@enonic/lib-admin-ui/auth/AuthContext';
45
import {i18n} from '@enonic/lib-admin-ui/util/Messages';
56
import {ContentAggregation} from './ContentAggregation';
67
import {GetPrincipalsByKeysRequest} from '../../security/GetPrincipalsByKeysRequest';
@@ -20,17 +21,19 @@ export class AggregationsDisplayNamesResolver {
2021
private contentTypes: Map<string, string>;
2122
private readonly currentUserId?: string;
2223

23-
constructor(currentUserId?: string) {
24-
this.currentUserId = currentUserId;
24+
constructor() {
25+
this.currentUserId = AuthContext.get().getUser().getKey().toString();
2526
}
2627

27-
updateAggregationsDisplayNames(aggregations: Aggregation[], userId: string): Q.Promise<void> {
28+
updateAggregationsDisplayNames(aggregations: BucketAggregation[]): Q.Promise<void> {
2829
this.updateWorkflowAggregations(aggregations);
2930

3031
const updatePromises: Q.Promise<void>[] = [];
3132
updatePromises.push(this.updateLanguageAggregations(aggregations));
3233
updatePromises.push(this.updateContentTypeAggregations(aggregations));
33-
34+
aggregations.filter((aggr) => this.isPrincipalAggregation(aggr.getName())).forEach((principalAggr: BucketAggregation) => {
35+
updatePromises.push(this.updateUnknownPrincipals(principalAggr).then(() => this.updateKnownPrincipals(principalAggr)));
36+
});
3437

3538
return Q.all(updatePromises).thenResolve(null);
3639
}
@@ -142,4 +145,8 @@ export class AggregationsDisplayNamesResolver {
142145
return this.updateContentTypeAggregation(aggregation);
143146
});
144147
}
148+
149+
private isPrincipalAggregation(name: string): boolean {
150+
return name === ContentAggregation.OWNER.toString() || name === ContentAggregation.MODIFIED_BY.toString();
151+
}
145152
}

modules/lib/src/main/resources/assets/js/app/browse/filter/ContentAggregationsFetcher.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ export class ContentAggregationsFetcher {
134134
const aggregationSelect: AggregationSelection[] = this.searchInputValues.aggregationSelections.filter(
135135
(aggrSelection: AggregationSelection) => aggrSelection.getName() !== aggregation.getName());
136136

137-
const emptyAggregationSelection: AggregationSelection = new AggregationSelection(aggregation.getName());
138-
emptyAggregationSelection.setValues([]);
137+
const emptyAggregationSelection: AggregationSelection = new AggregationSelection(aggregation.getName(), []);
139138
aggregationSelect.push(emptyAggregationSelection);
140139

141140
return aggregationSelect;

modules/lib/src/main/resources/assets/js/app/browse/filter/ContentBrowseFilterPanel.ts

Lines changed: 31 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {DefaultErrorHandler} from '@enonic/lib-admin-ui/DefaultErrorHandler';
12
import Q from 'q';
23
import {i18n} from '@enonic/lib-admin-ui/util/Messages';
34
import {Router} from '../../Router';
@@ -9,7 +10,6 @@ import {Aggregation} from '@enonic/lib-admin-ui/aggregation/Aggregation';
910
import {BrowseFilterPanel} from '@enonic/lib-admin-ui/app/browse/filter/BrowseFilterPanel';
1011
import {BucketAggregation} from '@enonic/lib-admin-ui/aggregation/BucketAggregation';
1112
import {Bucket} from '@enonic/lib-admin-ui/aggregation/Bucket';
12-
import {BucketAggregationView} from '@enonic/lib-admin-ui/aggregation/BucketAggregationView';
1313
import {ContentServerChangeItem} from '../../event/ContentServerChangeItem';
1414
import {ProjectContext} from '../../project/ProjectContext';
1515
import {ContentSummary} from '../../content/ContentSummary';
@@ -45,10 +45,12 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
4545
constructor() {
4646
super();
4747

48-
this.addClass(cn('content-browse-filter-panel bg-surface-primary text-main'));
48+
this.addClass(cn('content-browse-filter-panel bg-surface-neutral text-main'));
4949
this.aggregationsFetcher = this.createAggregationFetcher();
50+
this.displayNamesResolver = new AggregationsDisplayNamesResolver();
51+
this.dependenciesSection = new DependenciesSection();
5052

51-
this.getAndUpdateAggregations();
53+
this.getAndUpdateAggregations().catch(DefaultErrorHandler.handle);
5254
this.initElementsAndListeners();
5355
}
5456

@@ -127,55 +129,24 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
127129
});
128130
}
129131

130-
protected createHitsCountContainer(): DivEl {
131-
const hitsCounterAndClearButtonWrapper = super.createHitsCountContainer();
132-
if (this.exportElement) {
133-
hitsCounterAndClearButtonWrapper.appendChild(this.exportElement);
134-
}
135-
136-
return hitsCounterAndClearButtonWrapper;
137-
}
138-
139-
protected getAggregationEnum(): Record<string, string> {
140-
return ContentAggregation;
141-
}
142-
143-
protected getGroupViews(): AggregationGroupView[] {
144-
this.aggregations = new Map<string, AggregationGroupView>();
145-
146-
const aggregationEnum = this.getAggregationEnum();
147-
for (let aggrEnum in aggregationEnum) {
148-
const name: string = aggregationEnum[aggrEnum];
149-
this.aggregations.set(name, this.createGroupView(name));
150-
}
132+
protected getFilterableAggregations(): { name: string; idsToKeepOnTop?: string[] }[] {
133+
const currentUserKey = AuthContext.get().getUser().getKey().toString();
151134

152-
return Array.from(this.aggregations.values());
135+
return [
136+
{
137+
name: ContentAggregation.OWNER.toString(),
138+
idsToKeepOnTop: [currentUserKey]
139+
},
140+
{
141+
name: ContentAggregation.MODIFIED_BY.toString(),
142+
idsToKeepOnTop: [currentUserKey]
143+
},
144+
];
153145
}
154146

155-
protected isPrincipalAggregation(name: string): boolean {
156-
return name === ContentAggregation.OWNER.toString() || name === ContentAggregation.MODIFIED_BY.toString();
157-
}
158-
159-
protected createGroupView(name: string): AggregationGroupView {
160-
if (this.isPrincipalAggregation(name)) {
161-
const currentUserKey = this.getCurrentUserKeyAsString();
162-
if (!this.displayNamesResolver) {
163-
this.displayNamesResolver = new AggregationsDisplayNamesResolver(currentUserKey);
164-
}
165-
const aggregationGroupView = new FilterableAggregationGroupView(name, i18n(`field.${name}`));
166-
aggregationGroupView.setIdsToKeepOnToTop([currentUserKey]);
167-
aggregationGroupView.setResolver(this.displayNamesResolver);
168-
169-
return aggregationGroupView;
170-
}
171-
172-
return new AggregationGroupView(name, i18n(`field.${name}`));
173-
}
174-
175-
protected appendExtraSections() {
176-
super.appendExtraSections();
177-
this.dependenciesSection = new DependenciesSection();
178-
this.appendChild(this.dependenciesSection);
147+
protected isFilterableAggregation(aggregation: BucketAggregation): boolean {
148+
return aggregation.getName() === ContentAggregation.OWNER.toString() || aggregation.getName() ===
149+
ContentAggregation.MODIFIED_BY.toString();
179150
}
180151

181152
protected isExportAllowed(): boolean {
@@ -185,7 +156,6 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
185156

186157
private removeDependencyItem() {
187158
this.dependenciesSection.reset();
188-
this.resetConstraints();
189159
this.search();
190160
Router.get().back();
191161
}
@@ -197,7 +167,6 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
197167
this.selectBucketByTypeOnLoad(type);
198168
}
199169

200-
this.setConstraintItems(this.dependenciesSection, [item.getId()]);
201170
this.dependenciesSection.setDependencyItem(item);
202171
}
203172

@@ -220,7 +189,7 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
220189
}
221190

222191
private selectContentTypeBucket(key: string): void {
223-
(this.aggregations.get(ContentAggregation.CONTENT_TYPE).getAggregationViews()[0] as BucketAggregationView)?.selectBucketViewByKey(key);
192+
(this.aggregations.get(ContentAggregation.CONTENT_TYPE).getAggregationViews()[0])?.selectBucketViewByKey(key);
224193
}
225194

226195
searchItemById(id: ContentId): void {
@@ -262,7 +231,6 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
262231

263232
setSelectedItems(itemsIds: string[]) {
264233
this.dependenciesSection.reset();
265-
super.setSelectedItems(itemsIds);
266234
}
267235

268236
protected isFilteredOrConstrained() {
@@ -276,7 +244,7 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
276244
this.updateHitsCounter(aggregationsQueryResult.getMetadata().getTotalHits());
277245
this.updateExportState(aggregationsQueryResult);
278246

279-
return this.processAggregations(aggregationsQueryResult.getAggregations()).then(() => {
247+
return this.processAggregations(aggregationsQueryResult.getAggregations() as BucketAggregation[]).then(() => {
280248
return aggregationsQueryResult;
281249
});
282250
});
@@ -289,25 +257,27 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
289257
this.exportElement.setTotal(aggregationsQueryResult.getMetadata().getTotalHits());
290258
this.exportElement.setSearchInputValues(this.getSearchInputValues());
291259
this.exportElement.setDependency(this.getDependency());
292-
this.exportElement.setConstraintIds(this.hasConstraint() ? this.getSelectionItems().slice() : null);
293260
this.exportElement.setEnabled(aggregationsQueryResult.getMetadata().getTotalHits() > 0);
294261
}
295262

296-
private processAggregations(aggregations: Aggregation[]): Q.Promise<void> {
297-
this.toggleAggregationsVisibility(aggregations);
263+
private processAggregations(aggregations: BucketAggregation[]): Q.Promise<void> {
264+
this.sortAggregations(aggregations);
298265

299-
return this.displayNamesResolver.updateAggregationsDisplayNames(aggregations, this.getCurrentUserKeyAsString()).then(() => {
266+
return this.displayNamesResolver.updateAggregationsDisplayNames(aggregations).then(() => {
300267
this.updateAggregations(aggregations);
301268
});
302269
}
303270

304-
private getCurrentUserKeyAsString(): string {
305-
return AuthContext.get().getUser().getKey().toString();
271+
private sortAggregations(aggregations: BucketAggregation[]): void {
272+
const order = Object.values(ContentAggregation).filter(value => typeof value === 'string') as string[];
273+
274+
aggregations.sort(
275+
(a, b) => order.indexOf(a.getName()) - order.indexOf(b.getName())
276+
);
306277
}
307278

308279
private getAggregations(): Q.Promise<AggregationsQueryResult> {
309280
this.aggregationsFetcher.setSearchInputValues(this.getSearchInputValues());
310-
this.aggregationsFetcher.setConstraintItemsIds(this.hasConstraint() ? this.getSelectionItems().slice() : null);
311281
this.aggregationsFetcher.setDependency(this.getDependency());
312282

313283
return this.aggregationsFetcher.getAggregations();
@@ -335,14 +305,6 @@ export class ContentBrowseFilterPanel<T extends ContentSummaryAndCompareStatus =
335305
return null;
336306
}
337307

338-
private toggleAggregationsVisibility(aggregations: Aggregation[]) {
339-
aggregations.forEach((aggregation: BucketAggregation) => {
340-
const isAggregationNotEmpty: boolean = aggregation.getBuckets().some((bucket: Bucket) => bucket.docCount > 0);
341-
this.aggregations.get(aggregation.getName()).setVisible(isAggregationNotEmpty);
342-
});
343-
}
344-
345-
346308
// doing a trick to avoid changing lib-admin-ui, adding all children except export button to a wrapper
347309
appendChild(child: Element, lazyRender?: boolean): Element {
348310
if (!this.elementsContainer) {

modules/lib/src/main/resources/assets/js/app/browse/filter/ContentExportElement.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class ContentExportElement extends SpanEl {
103103
}
104104

105105
this.searchInputValues.aggregationSelections.filter((value) => value.getSelectedBuckets().length).forEach((selected) => {
106-
params[selected.name] = this.aggregationSelectionValueToString(selected);
106+
params[selected.getName()] = this.aggregationSelectionValueToString(selected);
107107
});
108108

109109
return UriHelper.appendUrlParams(this.getReportServicePath(), params);
@@ -115,7 +115,7 @@ export class ContentExportElement extends SpanEl {
115115
(bucket: DateRangeBucket) => ValueExpr.dateTime(bucket.getFrom()).getValue().getString()).join();
116116
}
117117

118-
return selected.selectedBuckets.map((bucket) => bucket.getKey()).join();
118+
return selected.getSelectedBuckets().map((bucket) => bucket.getKey()).join();
119119
}
120120

121121

0 commit comments

Comments
 (0)