Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/wabe/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,6 @@ export class AppModule {
addIcon('feedback_outline');
addIcon('assistant');
addIcon('semicorrect');
addIcon('light');
}
}
28 changes: 28 additions & 0 deletions apps/wabe/src/assets/svg/light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions libs/solid/core/src/lib/solid-core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { AudioToolbarComponent } from './components/audio-toolbar/audio-toolbar.
import { AudioIconComponent } from './components/audio-icon/audio-icon.component';
import { MEDIA_DIALOG_TOKEN } from './media-dialog-token';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTabsModule } from '@angular/material/tabs';

@NgModule({
declarations: [
Expand All @@ -48,6 +49,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
ScrollingModule,
MatSliderModule,
MatTooltipModule,
MatTabsModule,
],
exports: [
CommonModule,
Expand All @@ -58,6 +60,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
MediaComponent,
ScrollingModule,
MatTooltipModule,
MatTabsModule,
],
providers: [MarkdownService, TitleService],
})
Expand Down
121 changes: 89 additions & 32 deletions libs/solid/glossary/src/lib/components/glossary.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,94 @@
<mat-icon>{{ Filter.value === '' ? 'search' : 'close' }}</mat-icon>
</button>
</mat-form-field>
<div class="scrollable-list">
<mat-list *ngIf="GlossaryEntries | async as state">
<ng-container *ngFor="let kvp of state.sections">
<h3 mat-subeader>{{ kvp[0] }}</h3>
<mat-list-item
*ngFor="let entryId of kvp[1]"
solidGlossaryEntry
[refId]="entryId"
>
<span class="mat-body-strong entry-header" mat-line>{{
state.entries[entryId].term
}}</span>
<div
[data]="state.entries[entryId].text"
markdown
[inline]="true"
mat-line
></div>
<span
class="links"
*ngIf="state.entries[entryId].links.length > 0"
mat-line
>&rarr;<a
(click)="followRef(refId)"
*ngFor="let refId of state.entries[entryId].links; let i = index"

<div class="scrollable-list" *ngIf="GlossaryEntries | async as state">
<!-- Single element case - no tabs needed -->
<ng-container *ngIf="Object.keys(state).length === 1">
<mat-list>
<ng-container *ngFor="let kvp of state['Glossar'].sections">
<h3 mat-subeader>{{ kvp[0] }}</h3>
<mat-list-item
*ngFor="let entryId of kvp[1]"
solidGlossaryEntry
[refId]="entryId"
>
<span class="mat-body-strong entry-header" mat-line>{{
state['Glossar'].entries[entryId].term
}}</span>
<div
[data]="state['Glossar'].entries[entryId].text"
markdown
[inline]="true"
mat-line
></div>
<span
class="links"
*ngIf="state['Glossar'].entries[entryId].links.length > 0"
mat-line
>&rarr;<a
(click)="followRef(refId)"
*ngFor="
let refId of state['Glossar'].entries[entryId].links;
let i = index
"
>
{{ state['Glossar'].entries[refId].term
}}{{
i < state['Glossar'].entries[entryId].links.length - 1
? ','
: ''
}}</a
>
</span>
</mat-list-item>
</ng-container>
</mat-list>
</ng-container>

<!-- Multiple elements case - use tabs -->
<mat-tab-group *ngIf="Object.keys(state).length > 1">
<mat-tab *ngFor="let tab of Object.keys(state)" [label]="tab">
<mat-list>
<ng-container *ngFor="let kvp of state[tab].sections">
<h3 mat-subeader>{{ kvp[0] }}</h3>
<mat-list-item
*ngFor="let entryId of kvp[1]"
solidGlossaryEntry
[refId]="entryId"
>
{{ state.entries[refId].term
}}{{ i < state.entries[entryId].links.length - 1 ? ',' : '' }}</a
>
</span>
</mat-list-item>
</ng-container>
</mat-list>
<span class="mat-body-strong entry-header" mat-line>{{
state[tab].entries[entryId].term
}}</span>
<div
[data]="state[tab].entries[entryId].text"
markdown
[inline]="true"
mat-line
></div>
<span
class="links"
*ngIf="
state[tab].entries[entryId].links.length > 0 &&
tab === 'Glossar'
"
mat-line
>&rarr;<a
(click)="followRef(refId)"
*ngFor="
let refId of state[tab].entries[entryId].links;
let i = index
"
>
{{ state[tab].entries[refId].term
}}{{
i < state[tab].entries[entryId].links.length - 1 ? ',' : ''
}}
</a>
</span>
</mat-list-item>
</ng-container>
</mat-list>
</mat-tab>
</mat-tab-group>
</div>
27 changes: 21 additions & 6 deletions libs/solid/glossary/src/lib/components/glossary.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import {
GlossaryEntryModel,
ExtendedGlossaryEntryModel,
GlossaryState,
GlossaryStateModel,
} from '../glossary.state';
Expand All @@ -22,8 +23,10 @@ export class GlossaryComponent implements OnDestroy {
@ViewChildren(RefDirective, { read: RefDirective })
public refElements!: QueryList<RefDirective>;
@Select(GlossaryState.state)
public State!: Observable<GlossaryStateModel>;
public GlossaryEntries: Observable<GlossaryStateModel>;
public State!: Observable<ExtendedGlossaryEntryModel>;
public GlossaryEntries: Observable<ExtendedGlossaryEntryModel>;

protected Object = Object;

constructor(store: Store) {
store.dispatch(new LoadGLossary());
Expand All @@ -33,19 +36,24 @@ export class GlossaryComponent implements OnDestroy {
]).pipe(
map((val) => {
const filterStr: string = (val[0] as string).toLowerCase();
const state = val[1];
const state = { ...val[1] }; // Create a copy of the state to modify

if (filterStr === '') {
return state;
}
const validEntryIds = Object.values(state.entries)

// Only filter the glossary section
const glossaryState = state['Glossar'];
const validEntryIds = Object.values(glossaryState.entries)
.filter((entry: GlossaryEntryModel) => {
return (
entry.term.toLowerCase().includes(filterStr) ||
entry.text.toLowerCase().includes(filterStr)
);
})
.map((entry) => entry.id);
const filteredSections = state.sections

const filteredSections = glossaryState.sections
.filter((section) => {
return section[1].some((id) => validEntryIds.includes(id));
})
Expand All @@ -55,7 +63,14 @@ export class GlossaryComponent implements OnDestroy {
section[1].filter((id) => validEntryIds.includes(id)),
] as [string, number[]];
});
return { sections: filteredSections, entries: state.entries };

// Update only the glossary section in the state copy
state['Glossar'] = {
sections: filteredSections,
entries: glossaryState.entries,
};

return state;
}),
takeUntil(this.$destroyed),
);
Expand Down
72 changes: 49 additions & 23 deletions libs/solid/glossary/src/lib/glossary.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,21 @@ export interface GlossaryStateModel {
sections: [string, number[]][];
}

@State<GlossaryStateModel>({
export interface ExtendedGlossaryEntryModel {
[key: string]: GlossaryStateModel;
}

export interface GlossaryApiResponse {
[key: string]: GlossaryEntryModel[];
}

@State<ExtendedGlossaryEntryModel>({
name: 'glossary',
defaults: {
entries: {},
sections: [],
Glossar: {
entries: {},
sections: [],
},
},
})
@Injectable()
Expand All @@ -39,34 +49,50 @@ export class GlossaryState {
) {}

@Selector()
static state(state: GlossaryStateModel) {
static state(state: ExtendedGlossaryEntryModel) {
return { ...state };
}

@Action(LoadGLossary)
public load(ctx: StateContext<GlossaryStateModel>) {
public load(ctx: StateContext<ExtendedGlossaryEntryModel>) {
return this._http
.get<GlossaryEntryModel[]>(`${this._config.apiUrl}/glossaryentries`)
.get<GlossaryApiResponse>(`${this._config.apiUrl}/glossaryentries`)
.pipe(
map((result) => {
const entries: GlossaryEntries = {};
const sections: { [key: string]: number[] } = {};
result.forEach((entry) => {
entries[entry.id] = entry;
const firstChar = entry.term[0].toUpperCase();
if (sections[firstChar] === undefined) {
sections[firstChar] = [];
}
sections[firstChar].push(entry.id);
const extendedGlossary: ExtendedGlossaryEntryModel = {};

// Iterate over each tab content
Object.entries(result).forEach(([tabName, entryArray]) => {
const entries: GlossaryEntries = {};
const sections: { [key: string]: number[] } = {};

// Process each entry
entryArray.forEach((entry) => {
entries[entry.id] = entry;
const firstChar = entry.term[0].toUpperCase();
if (sections[firstChar] === undefined) {
sections[firstChar] = [];
}
sections[firstChar].push(entry.id);
});

// Sort entries within each section by term
Object.keys(sections).forEach((sectionKey) =>
sections[sectionKey].sort((a, b) =>
entries[a].term.localeCompare(entries[b].term),
),
);

// Convert sections to array format and sort by section key
const sectionArr = Object.entries(sections);
sectionArr.sort((a, b) => a[0].localeCompare(b[0]));

extendedGlossary[tabName] = {
entries,
sections: sectionArr,
};
});
Object.keys(sections).forEach((sectionKey) =>
sections[sectionKey].sort((a, b) =>
entries[a].term.localeCompare(entries[b].term),
),
);
const sectionArr = Object.entries(sections);
sectionArr.sort((a, b) => a[0].localeCompare(b[0]));
return { entries, sections: sectionArr };
return extendedGlossary;
}),
tap((v) => {
ctx.patchState(v);
Expand Down
Loading