Skip to content

Commit

Permalink
Add ability to configure the graph
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippMDoerner committed Dec 12, 2024
1 parent 7b349b9 commit 007cb32
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 98 deletions.
2 changes: 1 addition & 1 deletion src/app/_models/formly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface FormlyOverviewDisabledSelectConfig<Model, Option>
warningMessage: string;
}

export type InputKind = 'NUMBER' | 'STRING' | 'NAME';
export type InputKind = 'NUMBER' | 'STRING' | 'NAME' | 'NUMBER_FRACTION';

export interface FormlyInputConfig<T> extends FormlyInterface<T> {
placeholder?: string;
Expand Down
4 changes: 4 additions & 0 deletions src/app/_modules/formly_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
integerValidator,
invalidTimeMessage,
notIntegerMessage,
notNumberMesage,
numberValidator,
requiredIconMessage,
requiredIconValidator,
requiredMessage,
Expand Down Expand Up @@ -53,6 +55,7 @@ export const FORMLY_CONFIG: ConfigOption = {
hasSpecialCharactersMessage,
fieldsDontMatchMessage,
sessionAlreadyHasAuthor,
notNumberMesage,
],
validators: [
timeValidator,
Expand All @@ -63,6 +66,7 @@ export const FORMLY_CONFIG: ConfigOption = {
integerValidator,
specialCharacterValidator,
fieldMatchValidator,
numberValidator,
],
};

Expand Down
17 changes: 11 additions & 6 deletions src/app/_services/formly/formly-service.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,17 @@ export class FormlyService {

buildInputConfig<T>(config: FormlyInputConfig<T>): FormlyFieldConfig {
const validators = this.getValidators(config);
if (config.inputKind === 'NUMBER') {
validators.push('notInteger');
}
if (config.inputKind === 'NAME') {
//Why 'hasSpecialCharacters' validation? Names are used in URLs, they mustn't have special characters
validators.push('hasSpecialCharacters');
switch (config.inputKind) {
case 'NUMBER':
validators.push('notInteger');
break;
case 'NAME':
//Why 'hasSpecialCharacters' validation? Names are used in URLs, they mustn't have special characters
validators.push('hasSpecialCharacters');
break;
case 'NUMBER_FRACTION':
validators.push('notNumber');
break;
}

let innerInputType: 'string' | 'number';
Expand Down
16 changes: 16 additions & 0 deletions src/app/_services/formly/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export const notIntegerMessage = {
message:
'Your input is not an integer. This field requires an integer number. No amount of revolution can overcome this.',
};
export const notNumberMesage = {
name: 'notNumber',
message: 'Your input is not a number.',
};
export const hasSpecialCharactersMessage = {
name: 'hasSpecialCharacters',
message:
Expand Down Expand Up @@ -116,6 +120,18 @@ export const integerValidator: ValidatorOption = {
validation: isIntegerValidation,
};

function isNumberValidation(control: AbstractControl): ValidationErrors | null {
const isNumberType = typeof control.value === 'number';
const isNumberString =
typeof control.value === 'string' && !isNaN(control.value as any);
const isNumber = isNumberType || isNumberString;
return isNumber ? null : { notNumber: !isNumber };
}
export const numberValidator: ValidatorOption = {
name: 'notNumber',
validation: isNumberValidation,
};

function hasNoSpecialCharactersValidation(
control: AbstractControl,
): ValidationErrors | null {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<button
class="rounded-pill"
aria-label="Shows modal to modify settings used to create the graph"
btn
[kind]="'NONE'"
[icon]="'gear'"
[text]="'settings'"
(click)="openModal(settingsModal)"
></button>

<ng-template #settingsModal let-modal>
<div class="modal-header">
<h2
class="modal-title w-100 d-flex justify-content-between"
id="modal-title"
>
<span>Graph Settings</span>
<button type="button" aria-label="Close" (click)="modal.dismiss()">
<app-icon [icon]="'times'"></app-icon>
</button>
</h2>
</div>

<div class="modal-body">
<app-form
[model]="settings()"
[fields]="formlyFields()"
(formlySubmit)="onSettingsSubmit($event)"
(formlyCancel)="modal.dismiss()"
></app-form>
</div>
</ng-template>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
output,
TemplateRef,
} from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { FormlyService } from 'src/app/_services/formly/formly-service.service';
import { ButtonComponent } from 'src/design/atoms/button/button.component';
import { GRAPH_SETTINGS } from 'src/design/organisms/_model/graph';
import { IconComponent } from '../../../../design/atoms/icon/icon.component';
import { FormComponent } from '../../../../design/molecules/form/form.component';

@Component({
selector: 'app-graph-settings-modal',
standalone: true,
imports: [ButtonComponent, FormComponent, IconComponent],
templateUrl: './graph-settings-modal.component.html',
styleUrl: './graph-settings-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GraphSettingsModalComponent {
modalService = inject(NgbModal);
formlyService = inject(FormlyService);

settings = input(GRAPH_SETTINGS);

newSettings = output<typeof GRAPH_SETTINGS>();

formlyFields = computed<FormlyFieldConfig[]>(() => [
this.formlyService.buildInputConfig<typeof GRAPH_SETTINGS>({
key: 'linkAttractingForce',
inputKind: 'NUMBER_FRACTION',
label: 'Strength of links pulling nodes together',
}),
this.formlyService.buildInputConfig<typeof GRAPH_SETTINGS>({
key: 'nodeRepellingForce',
inputKind: 'NUMBER_FRACTION',
label: 'Strength of nodes repelling each other',
}),
this.formlyService.buildInputConfig<typeof GRAPH_SETTINGS>({
key: 'xForce',
inputKind: 'NUMBER_FRACTION',
label: 'Horizontal force',
}),
this.formlyService.buildInputConfig<typeof GRAPH_SETTINGS>({
key: 'yForce',
inputKind: 'NUMBER_FRACTION',
label: 'Vertical force',
}),
]);

onSettingsSubmit(model: any) {
const newSettings: typeof GRAPH_SETTINGS = {
...model,
xForce: parseFloat(model.xForce),
yForce: parseFloat(model.yForce),
linkAttractingForce: parseFloat(model.linkAttractingForce),
nodeRepellingForce: parseFloat(model.nodeRepellingForce),
};

this.newSettings.emit(newSettings);

this.modalService.dismissAll();
}

openModal(content: TemplateRef<any>) {
this.modalService.open(content, {
ariaLabelledBy: 'modal-title',
modalDialogClass: 'border border-info border-3 rounded mymodal',
});
}
}
6 changes: 5 additions & 1 deletion src/app/campaign/pages/graph-page/graph-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@ <h1 class="text-center mb-3">Wiki Overview</h1>
<app-graph
class="col-12 mb-2"
[data]="graph"
[graphSettings]="graphSettings()"
[activeNodesData]="selectedNodes() ?? []"
/>
<div class="col-12 d-flex justify-content-end">
<div class="col-12 d-flex justify-content-between">
<app-graph-settings-modal
(newSettings)="onSettingsChange($event)"
></app-graph-settings-modal>
<app-graph-help-modal></app-graph-help-modal>
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions src/app/campaign/pages/graph-page/graph-page.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
CategoryLabel,
GRAPH_CATEGORIES,
} from 'src/design/molecules/_models/search-preferences';
import { GRAPH_SETTINGS } from 'src/design/organisms/_model/graph';
import { GraphMenuService } from 'src/design/organisms/graph/graph-menu.service';
import { GraphService } from 'src/design/organisms/graph/graph.service';
import { filterNil } from 'src/utils/rxjs-operators';
Expand All @@ -50,6 +51,7 @@ import { SearchFieldComponent } from '../../../../design/molecules/search-field/
import { GraphComponent } from '../../../../design/organisms/graph/graph.component';
import { PageContainerComponent } from '../../../../design/organisms/page-container/page-container.component';
import { GraphHelpModalComponent } from '../../components/graph-help-modal/graph-help-modal.component';
import { GraphSettingsModalComponent } from '../../components/graph-settings-modal/graph-settings-modal.component';
import { GraphPageStore } from './graph-page.store';

@Component({
Expand All @@ -69,6 +71,7 @@ import { GraphPageStore } from './graph-page.store';
ConfirmationToggleButtonComponent,
SearchFieldComponent,
GraphHelpModalComponent,
GraphSettingsModalComponent,
],
templateUrl: './graph-page.component.html',
styleUrl: './graph-page.component.scss',
Expand Down Expand Up @@ -102,6 +105,7 @@ export class GraphPageComponent {
),
shareReplay(1),
);
graphSettings = signal(GRAPH_SETTINGS);

pageState = signal<'DISPLAY' | 'CREATE'>('DISPLAY');
isPanelOpen = signal<boolean>(false);
Expand Down Expand Up @@ -202,6 +206,11 @@ export class GraphPageComponent {
this.store.deleteConnection(linkId);
}

onSettingsChange(newSettings: typeof GRAPH_SETTINGS) {
console.log('New settings: ', newSettings);
this.graphSettings.set(newSettings);
}

private filterGraphData(
graphData: NodeMap | undefined,
filterCategories: Set<string>,
Expand Down
1 change: 1 addition & 0 deletions src/design/atoms/_models/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const ALL_SOLID_ICONS = [
'file-audio',
'file-import',
'file',
'gear',
'gavel',
'globe-americas',
'hammer',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ export const SELECTORS = {
deleteLinkSelector: '#delete-link',
};

export const GRAPH_SETTINGS = {
width: 1080,
minZoom: 0.5,
maxZoom: 12,
minHeight: 300,
linkAttractingForce: 0.5,
nodeRepellingForce: 50,
undirectedForce: 0.025,
circleSize: 6,
xForce: 1,
yForce: 1,
centeringTransitionTime: 1000,
hoverTransitionTime: 200,
strokeWidth: 0.5,
};

export type NodeClickEvent = {
event: MouseEvent;
clickedNode: ArticleNode | undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/design/organisms/graph/graph-menu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
} from 'src/app/_models/nodeMap';
import { ArticleService } from 'src/app/_services/article/article.service';
import { ellipsize } from 'src/utils/string';
import { LinkClickEvent, SELECTORS } from '../_model/graph';
import { SIDEBAR_ENTRIES } from '../_model/sidebar';
import { LinkClickEvent, SELECTORS } from './data';

export type NodeMenuData = {
title: string | undefined;
Expand Down
6 changes: 3 additions & 3 deletions src/design/organisms/graph/graph.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
#zoomControl
type="range"
aria-label="'Zoom level of graph'"
[min]="settings.minZoom"
[max]="settings.maxZoom"
[value]="(zoomLevel$ | async) ?? settings.minZoom"
[min]="graphSettings().minZoom"
[max]="graphSettings().maxZoom"
[value]="(zoomLevel$ | async) ?? graphSettings().minZoom"
[step]="0.1"
(input)="zoomSliderEvents$.next(zoomControl.value)"
class="controls__slider"
Expand Down
32 changes: 12 additions & 20 deletions src/design/organisms/graph/graph.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,9 @@ import { filter, map, Subject, take } from 'rxjs';
import { NodeMap, NodeSelection } from 'src/app/_models/nodeMap';
import { ArticleService } from 'src/app/_services/article/article.service';
import { ButtonComponent } from 'src/design/atoms/button/button.component';
import { GRAPH_SETTINGS } from '../_model/graph';
import { GraphService } from './graph.service';

const GRAPH_SETTINGS = {
width: 1080,
minZoom: 0.5,
maxZoom: 12,
minHeight: 300,
linkAttractingForce: 0.5,
nodeRepellingForce: 50,
circleSize: 6,
xForce: 1,
yForce: 1,
centeringTransitionTime: 1000,
hoverTransitionTime: 200,
strokeWidth: 0.5,
};

@Component({
selector: 'app-graph',
standalone: true,
Expand All @@ -43,6 +29,7 @@ const GRAPH_SETTINGS = {
export class GraphComponent {
data = input.required<NodeMap>();
activeNodesData = input.required<NodeSelection>();
graphSettings = input.required<typeof GRAPH_SETTINGS>();

articleService = inject(ArticleService);
graphService = inject(GraphService); //Accessible as the parent, GraphPageComponent, provides an instance
Expand All @@ -52,14 +39,19 @@ export class GraphComponent {
elements = toSignal(this.graphService.elements$);
zoomLevel$ = this.graphService.zoomLevelChangedEvent$;

settings = GRAPH_SETTINGS;

zoomSliderEvents$ = new Subject<string>();

constructor() {
effect(() => this.graphService.createGraphEvents$.next(this.data()), {
allowSignalWrites: true,
});
effect(
() =>
this.graphService.createGraphEvents$.next({
data: this.data(),
settings: this.graphSettings(),
}),
{
allowSignalWrites: true,
},
);

// Replace graph in HTML if graph changes
effect(() => {
Expand Down
Loading

0 comments on commit 007cb32

Please sign in to comment.