Browse Source

Fix schema filtering.

pull/786/head
Sebastian 4 years ago
parent
commit
20574d0b6d
  1. 3
      frontend/app/features/content/pages/schemas/schemas-page.component.html
  2. 1
      frontend/app/features/schemas/pages/schema/schema-page.component.scss
  3. 4
      frontend/app/features/schemas/pages/schemas/schemas-page.component.scss
  4. 19
      frontend/app/shared/components/schema-category.component.html
  5. 35
      frontend/app/shared/components/schema-category.component.scss
  6. 16
      frontend/app/shared/components/schema-category.component.ts
  7. 7
      frontend/app/shared/services/schemas.service.spec.ts
  8. 333
      frontend/app/shared/state/schemas.state.spec.ts
  9. 112
      frontend/app/shared/state/schemas.state.ts
  10. 2
      frontend/app/theme/_panels2.scss

3
frontend/app/features/content/pages/schemas/schemas-page.component.html

@ -20,7 +20,8 @@
<ng-container> <ng-container>
<sqx-schema-category *ngFor="let category of categories | async; trackBy: trackByCategory" <sqx-schema-category *ngFor="let category of categories | async; trackBy: trackByCategory"
[schemaCategory]="category" [forContent]="true"> [schemaCategory]="category"
[schemaTarget]="'Contents'">
</sqx-schema-category> </sqx-schema-category>
</ng-container> </ng-container>
</ng-container> </ng-container>

1
frontend/app/features/schemas/pages/schema/schema-page.component.scss

@ -6,6 +6,7 @@
} }
.cards { .cards {
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: $panel-padding; padding: $panel-padding;
} }

4
frontend/app/features/schemas/pages/schemas/schemas-page.component.scss

@ -1,4 +0,0 @@
::ng-deep sqx-schemas-page .panel2-main-inner.padded {
padding: .5rem;
padding-left: 0;
}

19
frontend/app/shared/components/schema-category.component.html

@ -1,6 +1,5 @@
<ul [hidden]="forContent && visibleCount == 0" id="cat_{{schemaCategory.name}}" class="nav nav-light flex-column"> <ul [hidden]="forContent && schemaCategory.countSchemasInSubtree === 0" class="nav nav-light flex-column">
<div class="droppable nav-category"
<div class="droppable category"
cdkDropList cdkDropList
cdkDropListSortingDisabled cdkDropListSortingDisabled
[cdkDropListData]="schemaCategory.name" [cdkDropListData]="schemaCategory.name"
@ -19,8 +18,8 @@
</div> </div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<ng-container *ngIf="visibleCount > 0; else noSchemas"> <ng-container *ngIf="schemaCategory.countSchemasInSubtree > 0; else noSchemas">
<span class="badge rounded-pill badge-secondary">{{visibleCount}}</span> <span class="badge rounded-pill badge-secondary">{{schemaCategory.countSchemasInSubtreeFiltered}}</span>
</ng-container> </ng-container>
<ng-template #noSchemas> <ng-template #noSchemas>
<button type="button" class="btn btn-sm btn-text-secondary btn-remove" (click)="remove.emit(schemaCategory.name)" *ngIf="schemaCategory.name"> <button type="button" class="btn btn-sm btn-text-secondary btn-remove" (click)="remove.emit(schemaCategory.name)" *ngIf="schemaCategory.name">
@ -31,7 +30,7 @@
</div> </div>
</li> </li>
<div [hidden]="isCollapsed" @fade [style.height]="getContainerHeight()" class="nav-collapsed"> <div [hidden]="isCollapsed" [style.height]="getContainerHeight()" class="nav-collapsed">
<ng-container *ngIf="!forContent; else simpleMode"> <ng-container *ngIf="!forContent; else simpleMode">
<li *ngFor="let schema of schemas; trackBy: trackBySchema" class="nav-item truncate" [style.height]="getItemHeight()" <li *ngFor="let schema of schemas; trackBy: trackBySchema" class="nav-item truncate" [style.height]="getItemHeight()"
cdkDrag cdkDrag
@ -52,8 +51,7 @@
<li *ngFor="let schema of schemas; trackBy: trackBySchema" class="nav-item truncate"> <li *ngFor="let schema of schemas; trackBy: trackBySchema" class="nav-item truncate">
<a class="nav-link truncate drag-none" [routerLink]="schemaRoute(schema)" routerLinkActive="active" sqxStopDrag <a class="nav-link truncate drag-none" [routerLink]="schemaRoute(schema)" routerLinkActive="active" sqxStopDrag
title="{{schema.displayName}}" title="{{schema.displayName}}"
titlePosition="top-left" titlePosition="top-left">
id="schema_{{schema.name}}">
{{schema.displayName}} {{schema.displayName}}
</a> </a>
</li> </li>
@ -61,13 +59,12 @@
</div> </div>
<div class="drop-indicator"></div> <div class="drop-indicator"></div>
</div> </div>
<div class="nav nav-panel nav-dark nav-dark-bordered flex-column" [hidden]="isCollapsed" @fade> <div class="categories" [hidden]="isCollapsed">
<sqx-schema-category *ngFor="let category of schemaCategory.categories; trackBy: trackByCategory" <sqx-schema-category *ngFor="let category of schemaCategory.categories; trackBy: trackByCategory"
[schemaCategory]="category" [schemaCategory]="category"
[forContent]="forContent" [schemaTarget]="schemaTarget"
(remove)="remove.emit($event)"> (remove)="remove.emit($event)">
</sqx-schema-category> </sqx-schema-category>
</div> </div>

35
frontend/app/shared/components/schema-category.component.scss

@ -74,26 +74,31 @@ $drag-margin: -8px;
} }
} }
:host {
&:first-child {
.nav-light {
margin-top: 0;
}
}
}
.item-published { .item-published {
margin-bottom: 1px; margin-bottom: 1px;
} }
ul.nav-light { .nav-category,
margin-left: 1rem;
margin-right: 0;
font-size: 100%;
}
.category,
.nav-panel, .nav-panel,
sqx-schema-category { sqx-schema-category {
max-width: 100%; max-width: 100%;
}
.categories {
padding: 0;
padding-left: 1rem;
font-size: 1rem;
.nav-light {
margin-left: 0;
margin-right: 0;
}
}
:host {
&:first-child {
.nav-light {
margin-top: 0;
}
}
} }

16
frontend/app/shared/components/schema-category.component.ts

@ -28,14 +28,16 @@ export class SchemaCategoryComponent implements OnChanges {
public schemaCategory: SchemaCategory; public schemaCategory: SchemaCategory;
@Input() @Input()
public forContent?: boolean | null; public schemaTarget?: 'Schema' | 'Contents';
public isCollapsed = false; public isCollapsed = false;
public visibleCount = 0; public get forContent() {
return this.schemaTarget === 'Contents';
}
public get schemas() { public get schemas() {
return this.schemaCategory.schemas; return this.schemaCategory.schemasFiltered;
} }
constructor( constructor(
@ -51,19 +53,13 @@ export class SchemaCategoryComponent implements OnChanges {
} }
public ngOnChanges() { public ngOnChanges() {
this.visibleCount = this.getCount(this.schemaCategory); if (this.schemaCategory.countSchemasInSubtreeFiltered < this.schemaCategory.countSchemasInSubtree) {
if (this.schemaCategory.schemas.length < this.schemaCategory.schemaTotalCount) {
this.isCollapsed = false; this.isCollapsed = false;
} else { } else {
this.isCollapsed = this.localStore.getBoolean(this.configKey()); this.isCollapsed = this.localStore.getBoolean(this.configKey());
} }
} }
private getCount(category: SchemaCategory): number {
const childCount = category.categories.reduce((total, child) => total + this.getCount(child), 0);
return childCount + category.schemas.length;
}
public schemaRoute(schema: SchemaDto) { public schemaRoute(schema: SchemaDto) {
if (schema.type === 'Singleton' && this.forContent) { if (schema.type === 'Singleton' && this.forContent) {
return [schema.name, schema.id, 'history']; return [schema.name, schema.id, 'history'];

7
frontend/app/shared/services/schemas.service.spec.ts

@ -818,15 +818,12 @@ function createSchemaProperties(id: number, suffix = '') {
); );
} }
export function createSchema(id: number, suffix = '', category = '') { export function createSchema(id: number, suffix = '') {
const links: ResourceLinks = { const links: ResourceLinks = {
update: { method: 'PUT', href: `/schemas/${id}` }, update: { method: 'PUT', href: `/schemas/${id}` },
}; };
const key = `${id}${suffix}`; const key = `${id}${suffix}`;
if (category === '') {
category = `schema-category${key}`;
}
return new SchemaDto(links, return new SchemaDto(links,
`id${id}`, `id${id}`,
@ -834,7 +831,7 @@ export function createSchema(id: number, suffix = '', category = '') {
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`, DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key), new Version(key),
`schema-name${key}`, `schema-name${key}`,
category, `schema-category${key}`,
id % 2 === 0 ? 'Default' : 'Singleton', id % 2 === 0 ? 'Default' : 'Singleton',
id % 3 === 0, id % 3 === 0,
createSchemaProperties(id, suffix), createSchemaProperties(id, suffix),

333
frontend/app/shared/state/schemas.state.spec.ts

@ -494,14 +494,74 @@ describe('SchemasState', () => {
}); });
describe('Categories', () => { describe('Categories', () => {
it('should be build from schemas', () => { it('should be build from schemas with undefined categories', () => {
const schemaDefault = createSchema(6);
const schemaComponent = createSchema(7);
(schemaDefault as any)['category'] = '';
(schemaComponent as any)['category'] = '';
(schemaComponent as any)['type'] = 'Component';
const result = getCategoryTree([schemaDefault, schemaComponent], new Set<string>());
expect(result).toEqual([
{
displayName: 'i18n:common.components',
schemas: [schemaComponent],
schemasFiltered: [schemaComponent],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
{
displayName: 'i18n:common.schemas',
schemas: [schemaDefault],
schemasFiltered: [schemaDefault],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
]);
});
it('should be build from schemas with defined categories', () => {
const result = getCategoryTree([schema1, schema2], new Set<string>()); const result = getCategoryTree([schema1, schema2], new Set<string>());
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [schema1], schemaTotalCount: 1, categories: [] }, schemas: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [schema2], schemaTotalCount: 1, categories: [] }, schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [schema1],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
{
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [schema2],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
]); ]);
}); });
@ -509,11 +569,49 @@ describe('SchemasState', () => {
const result = getCategoryTree([schema1, schema2], new Set<string>(['schema-category3'])); const result = getCategoryTree([schema1, schema2], new Set<string>(['schema-category3']));
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [schema1], schemaTotalCount: 1, categories: [] }, schemas: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [schema2], schemaTotalCount: 1, categories: [] }, schemasFiltered: [],
{ displayName: 'schema-category3', name: 'schema-category3', schemas: [], schemaTotalCount: 0, categories: [] }, countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [schema1],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
{
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [schema2],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
{
displayName: 'schema-category3',
name: 'schema-category3',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
]); ]);
}); });
@ -521,24 +619,100 @@ describe('SchemasState', () => {
const result = getCategoryTree([schema1, schema2], new Set<string>(), '1'); const result = getCategoryTree([schema1, schema2], new Set<string>(), '1');
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [schema1], schemaTotalCount: 1, categories: [] }, schemas: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [], schemaTotalCount: 1, categories: [] }, // Filtered out schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [schema1],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
{
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
]); ]);
}); });
it('should be build from schemas with nested categories', () => { it('should be build from schemas with nested categories', () => {
const schema3 = createSchema(3, '', 'A'); const schemaA = createSchema(3);
const schema4 = createSchema(4, '', 'A/B'); const schemaAB = createSchema(4);
const result = getCategoryTree([schema1, schema2, schema3, schema4], new Set<string>());
(schemaA as any)['category'] = 'A';
(schemaAB as any)['category'] = 'A/B';
const result = getCategoryTree([schema1, schema2, schemaA, schemaAB], new Set<string>());
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'A', name: 'A', schemas: [schema3], schemaTotalCount: 2, categories: [{ displayName: 'B', name: 'A/B', schemas: [schema4], schemaTotalCount: 1, categories: [] }] }, schemas: [],
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [schema1], schemaTotalCount: 1, categories: [] }, schemasFiltered: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [schema2], schemaTotalCount: 1, categories: [] }, countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'A',
name: 'A',
schemas: [schemaA],
schemasFiltered: [schemaA],
countSchemasInSubtree: 2,
countSchemasInSubtreeFiltered: 2,
categories: [{
displayName: 'B',
name: 'A/B',
schemas: [schemaAB],
schemasFiltered: [schemaAB],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
}],
}, {
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [schema1],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
}, {
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [schema2],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
]); ]);
}); });
@ -546,26 +720,115 @@ describe('SchemasState', () => {
const result = getCategoryTree([schema1, schema2], new Set<string>(['A/B'])); const result = getCategoryTree([schema1, schema2], new Set<string>(['A/B']));
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'A', name: 'A', schemas: [], schemaTotalCount: 0, categories: [{ displayName: 'B', name: 'A/B', schemas: [], schemaTotalCount: 0, categories: [] }] }, schemas: [],
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [schema1], schemaTotalCount: 1, categories: [] }, schemasFiltered: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [schema2], schemaTotalCount: 1, categories: [] }, countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'A',
name: 'A',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [{
displayName: 'B',
name: 'A/B',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}],
}, {
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [schema1],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
}, {
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [schema2],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
},
]); ]);
}); });
}); });
it('should be build from schemas with nested categories and filter', () => { it('should be build from schemas with nested categories and filter', () => {
const schema3 = createSchema(3, '', 'A'); const schemaA = createSchema(3);
const schema4 = createSchema(4, '', 'A/B'); const schemaAB = createSchema(4);
const result = getCategoryTree([schema1, schema2, schema3, schema4], new Set<string>(), '4');
(schemaA as any)['category'] = 'A';
(schemaAB as any)['category'] = 'A/B';
const result = getCategoryTree([schema1, schema2, schemaA, schemaAB], new Set<string>(), '4');
expect(result).toEqual([ expect(result).toEqual([
{ displayName: 'i18n:common.components', schemas: [], schemaTotalCount: 0, categories: [] }, {
{ displayName: 'i18n:common.schemas', schemas: [], schemaTotalCount: 0, categories: [] }, displayName: 'i18n:common.components',
{ displayName: 'A', name: 'A', schemas: [], schemaTotalCount: 2, categories: [{ displayName: 'B', name: 'A/B', schemas: [schema4], schemaTotalCount: 1, categories: [] }] }, schemas: [],
{ displayName: 'schema-category1', name: 'schema-category1', schemas: [], schemaTotalCount: 1, categories: [] }, schemasFiltered: [],
{ displayName: 'schema-category2', name: 'schema-category2', schemas: [], schemaTotalCount: 1, categories: [] }, countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'i18n:common.schemas',
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
{
displayName: 'A',
name: 'A',
schemas: [schemaA],
schemasFiltered: [],
countSchemasInSubtree: 2,
countSchemasInSubtreeFiltered: 1,
categories: [{
displayName: 'B',
name: 'A/B',
schemas: [schemaAB],
schemasFiltered: [schemaAB],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 1,
categories: [],
}],
}, {
displayName: 'schema-category1',
name: 'schema-category1',
schemas: [schema1],
schemasFiltered: [],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 0,
categories: [],
}, {
displayName: 'schema-category2',
name: 'schema-category2',
schemas: [schema2],
schemasFiltered: [],
countSchemasInSubtree: 1,
countSchemasInSubtreeFiltered: 0,
categories: [],
},
]); ]);
}); });
}); });

112
frontend/app/shared/state/schemas.state.ts

@ -367,7 +367,9 @@ export type SchemaCategory = {
displayName: string; displayName: string;
name?: string; name?: string;
schemas: SchemaDto[]; schemas: SchemaDto[];
schemaTotalCount: number; schemasFiltered: SchemaDto[];
countSchemasInSubtree: number;
countSchemasInSubtreeFiltered: number;
categories: SchemaCategory[]; categories: SchemaCategory[];
}; };
@ -394,52 +396,72 @@ export function getCategoryTree(allSchemas: ReadonlyArray<SchemaDto>, categories
const schemas: SchemaCategory = { const schemas: SchemaCategory = {
displayName: SPECIAL_SCHEMAS, displayName: SPECIAL_SCHEMAS,
schemas: [], schemas: [],
schemaTotalCount: 0, schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [], categories: [],
}; };
const components: SchemaCategory = { const components: SchemaCategory = {
displayName: SPECIAL_COMPONENTS, displayName: SPECIAL_COMPONENTS,
schemas: [], schemas: [],
schemaTotalCount: 0, schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [], categories: [],
}; };
const flatCategoryList: SchemaCategory[] = [schemas, components]; const categoryCache: { [name: string]: SchemaCategory } = {};
const categoryRoots: SchemaCategory[] = [];
// Sort the categories so that components and schemas stay top.
for (const name of categories) { for (const name of categories) {
getOrCreateCategory(name); getOrCreateCategory(name);
} }
function addSchemaToCategory(schema: SchemaDto, category: SchemaCategory) { function addSchemaToCategory(schema: SchemaDto, category: SchemaCategory) {
category.schemaTotalCount++; category.schemas.push(schema);
if (match(schema)) { if (match(schema)) {
category.schemas.push(schema); category.schemasFiltered.push(schema);
} }
} }
function getOrCreateCategory(name: string): SchemaCategory { function getOrCreateCategory(name: string): SchemaCategory {
let category = flatCategoryList.find(x => x.name === name); let category = categoryCache[name];
const displayName = (name.indexOf(NESTED_CATEGORY_SEPARATOR) === -1) ? name : name.substr(name.lastIndexOf(NESTED_CATEGORY_SEPARATOR) + 1); if (category) {
return category;
}
if (!category) { let displayName = name;
category = {
displayName,
name,
schemas: [],
schemaTotalCount: 0,
categories: [],
};
flatCategoryList.push(category); const lastSeparatorIndex = name.lastIndexOf(NESTED_CATEGORY_SEPARATOR);
if (name.indexOf(NESTED_CATEGORY_SEPARATOR) !== -1) { if (lastSeparatorIndex >= 0) {
// Recurse back creating all the parents of this category displayName = displayName.substr(lastSeparatorIndex + 1);
const parentName = name.substr(0, name.lastIndexOf(NESTED_CATEGORY_SEPARATOR)); }
getOrCreateCategory(parentName);
} category = {
displayName,
name,
schemas: [],
schemasFiltered: [],
countSchemasInSubtree: 0,
countSchemasInSubtreeFiltered: 0,
categories: [],
};
categoryCache[name] = category;
if (lastSeparatorIndex >= 0) {
// Recurse back creating all the parents of this category
const parentName = name.substr(0, lastSeparatorIndex);
const parentCategory = getOrCreateCategory(parentName);
parentCategory.categories.push(category);
} else {
categoryRoots.push(category);
} }
return category; return category;
@ -449,8 +471,7 @@ export function getCategoryTree(allSchemas: ReadonlyArray<SchemaDto>, categories
const name = schema.category; const name = schema.category;
if (name) { if (name) {
const category = getOrCreateCategory(name); addSchemaToCategory(schema, getOrCreateCategory(name));
addSchemaToCategory(schema, category);
} else if (schema.type === 'Component') { } else if (schema.type === 'Component') {
addSchemaToCategory(schema, components); addSchemaToCategory(schema, components);
} else { } else {
@ -458,28 +479,29 @@ export function getCategoryTree(allSchemas: ReadonlyArray<SchemaDto>, categories
} }
} }
// Sort by name and than DisplayName so that children get correctly sorted under their parents but component and schema still sort correctly function update(category: SchemaCategory) {
flatCategoryList.sortByString(x => `${x.name ?? ''} - ${x.displayName}`); category.countSchemasInSubtree = category.schemas.length;
category.countSchemasInSubtreeFiltered = category.schemasFiltered.length;
const result: SchemaCategory[] = [];
// Child categories by necessity come after their parents alphabetically so processing in reverse lets us roll up all categories into their parents nicely. // Add up the total count of the whole tree.
// Because we're processing in reverse we unshift rather than push to the results array to get everything in the right order at the end. for (const child of category.categories) {
for (const category of flatCategoryList.reverse()) { update(child);
if (category.name) {
if (category.name?.indexOf(NESTED_CATEGORY_SEPARATOR) !== -1) { category.countSchemasInSubtree += child.countSchemasInSubtree;
const parentName = category.name?.substr(0, category.name.lastIndexOf(NESTED_CATEGORY_SEPARATOR)); category.countSchemasInSubtreeFiltered += child.countSchemasInSubtreeFiltered;
const parentCategory = flatCategoryList.find(x => x.name === parentName);
if (parentCategory) {
parentCategory.categories.unshift(category);
parentCategory.schemaTotalCount += category.schemaTotalCount;
}
} else {
result.unshift(category);
}
} else {
result.unshift(category);
} }
// Sort each category category individually because sorting is O(log N).
category.categories.sortByString(x => x.displayName);
}
// Sort by name and then add components and schemas, so that both categories are on top.
categoryRoots.sortByString(x => x.displayName);
categoryRoots.unshift(components, schemas);
for (const child of categoryRoots) {
update(child);
} }
return result; return categoryRoots;
} }

2
frontend/app/theme/_panels2.scss

@ -304,7 +304,7 @@
.nav-heading { .nav-heading {
color: $color-text-decent; color: $color-text-decent;
margin-bottom: .5rem; margin-bottom: 0;
margin-top: 1rem; margin-top: 1rem;
} }
} }
Loading…
Cancel
Save