diff --git a/.editorconfig b/.editorconfig index 59d9a3a..148d21d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,6 @@ quote_type = single [*.md] max_line_length = off trim_trailing_whitespace = false + +[*.scss] +indent_size = 4 diff --git a/.gitignore b/.gitignore index 0711527..ee69532 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ testem.log # System files .DS_Store Thumbs.db +.nx/ diff --git a/.nvmrc b/.nvmrc index 60495ee..42e31a0 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.19.1 +v20.14.0 diff --git a/angular.json b/angular.json index 25f79e3..8d4f63a 100644 --- a/angular.json +++ b/angular.json @@ -15,7 +15,7 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:browser-esbuild", "options": { "outputPath": "docs", "index": "projects/demo-showcase/src/index.html", diff --git a/projects/demo-showcase/src/app/page-features-viewer/page-features-viewer.component.html b/projects/demo-showcase/src/app/page-features-viewer/page-features-viewer.component.html index 18a597d..6e9e0b6 100644 --- a/projects/demo-showcase/src/app/page-features-viewer/page-features-viewer.component.html +++ b/projects/demo-showcase/src/app/page-features-viewer/page-features-viewer.component.html @@ -36,17 +36,24 @@

Feature viewer

- + +
{{ trace.label }}  @if (trace.expanded) { - + } @else { - + }
+ + +
+ Right label! +
+
@@ -72,4 +79,4 @@

Feature viewer

Suspendisse potenti. Duis imperdiet aliquam hendrerit.

- \ No newline at end of file + diff --git a/projects/demo-showcase/src/app/page-structure-viewer/page-structure-viewer.component.html b/projects/demo-showcase/src/app/page-structure-viewer/page-structure-viewer.component.html index ecafd3b..5fb777f 100644 --- a/projects/demo-showcase/src/app/page-structure-viewer/page-structure-viewer.component.html +++ b/projects/demo-showcase/src/app/page-structure-viewer/page-structure-viewer.component.html @@ -13,7 +13,7 @@

Structure viewer

- + - + \ No newline at end of file diff --git a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.html b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.html index c14363a..9226296 100644 --- a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.html +++ b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.html @@ -1,33 +1,33 @@ @if (chains$ | async; as chains) { - -
- -
-
- - -
+ +
+ +
+
+ +
- -
-

Color chain

-
- - @for (chain of chains; track $index) { -
-
- -
- -
- - -
{{ chain | json }}
-
+
+ +
+

Color chain

+
+ + @for (chain of chains; track $index) { +
+
+ +
+
+ + +
{{ chain | json }}
+
- }
+ }
+
} \ No newline at end of file diff --git a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.ts b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.ts index e90555a..9a3de5c 100644 --- a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.ts +++ b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-chains.component.ts @@ -1,4 +1,4 @@ -import { NgxStructureViewerComponent, Locus, Settings, Source } from '@ngx-structure-viewer'; +import { NgxStructureViewerComponent, Locus, Settings, Source, StructureService, PluginService } from '@ngx-structure-viewer'; import { Observable, interval, map, shareReplay, startWith } from 'rxjs'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @@ -9,6 +9,10 @@ import { CommonModule } from '@angular/common'; NgxStructureViewerComponent, CommonModule, ], + providers: [ + StructureService, + PluginService, + ], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './section-chains.component.html', @@ -22,7 +26,10 @@ export class SectionChainsComponent { readonly chains$: Observable; - constructor() { + constructor( + public structureService: StructureService, + public pluginService: PluginService, + ) { // Define settings this.settings = { 'background-color': '#2b3035ff', @@ -62,6 +69,11 @@ export class SectionChainsComponent { // Cache result shareReplay(1), ); + + // TODO Check that service has been imported + structureService.structure$.subscribe(() => { + console.log('Hello, world!'); + }); } } diff --git a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-sources.component.ts b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-sources.component.ts index c296018..9ba75ee 100644 --- a/projects/demo-showcase/src/app/page-structure-viewer/sections/section-sources.component.ts +++ b/projects/demo-showcase/src/app/page-structure-viewer/sections/section-sources.component.ts @@ -2,7 +2,7 @@ import { NgxStructureViewerComponent, Settings, Source } from '@ngx-structure-vi import { HttpClient, HttpClientModule } from '@angular/common/http'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { Observable, map, of } from 'rxjs'; +import { Observable, map, of, tap } from 'rxjs'; @Component({ selector: 'app-section-sources', @@ -51,6 +51,8 @@ export class SectionSourcesComponent { map((data: string) => new Blob([data], { type: 'text/plain' })), // Provide local source map((data: Blob) => ({ ...this.local, data })), + // TODO Remove this + tap((source) => console.log('Local source:', source)), ); this.remote$ = of({ ...this.remote, link: 'https://files.rcsb.org/view/8VAP.cif' }); diff --git a/projects/demo-showcase/src/styles.scss b/projects/demo-showcase/src/styles.scss index 1003c29..f237ba2 100644 --- a/projects/demo-showcase/src/styles.scss +++ b/projects/demo-showcase/src/styles.scss @@ -35,7 +35,8 @@ @import "../../../node_modules/bootstrap/scss/utilities/api"; // 8. Add additional custom code here -@import "../../../node_modules/bootstrap-icons/font/bootstrap-icons.scss"; +//@import "../../../node_modules/bootstrap-icons/font/bootstrap-icons.scss"; @import "./styles/background"; @import "./styles/scaffold"; @import "./styles/utils"; + diff --git a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.html b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.html index 313daea..ef88ef9 100644 --- a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.html +++ b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.html @@ -1,12 +1,22 @@
- - @if (label) { + + @if (labelLeft) { @for(kv of featuresService.traces | keyvalue; track $index) { -
- +
+
} } -
\ No newline at end of file + + @if(labelRight) { + + @for(kv of featuresService.traces | keyvalue; track $index) { + +
+ +
+ } + } +
diff --git a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.ts b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.ts index 86f5dc1..2e58195 100644 --- a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.ts +++ b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.component.ts @@ -1,18 +1,32 @@ -// prettier-ignore -import { AfterViewInit, ChangeDetectionStrategy, Component, ContentChild, ElementRef, HostListener, Input, OnChanges, OnDestroy, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; -import { Observable, Subscription, tap, switchMap } from 'rxjs'; -import { CommonModule } from '@angular/common'; +import { + AfterContentInit, + AfterViewInit, + ChangeDetectionStrategy, + Component, + ContentChildren, + ElementRef, + HostListener, + Input, + OnChanges, + OnDestroy, + QueryList, + SimpleChanges, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import {Observable, Subscription, switchMap, tap} from 'rxjs'; +import {CommonModule} from '@angular/common'; // Custom components -import { NgxFeaturesViewerLabelDirective } from './ngx-features-viewer.directive'; +import {NgxFeaturesViewerLabelDirective} from './ngx-features-viewer.directive'; // Custom providers -import { InitializeService } from './services/initialize.service'; -import { FeaturesService } from './services/features.service'; -import { ResizeService } from './services/resize.service'; -import { ZoomService } from './services/zoom.service'; -import { DrawService } from './services/draw.service'; +import {InitializeService} from './services/initialize.service'; +import {FeaturesService} from './services/features.service'; +import {ResizeService} from './services/resize.service'; +import {ZoomService} from './services/zoom.service'; +import {DrawService} from './services/draw.service'; // Custom data types -import { Hierarchy } from './hierarchy'; -import { Settings } from './settings'; +import {Hierarchy} from './hierarchy'; +import {Settings} from './settings'; // TODO Define sequence type @@ -38,13 +52,17 @@ export type Sequence = Array; changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, }) -export class NgxFeaturesViewerComponent implements AfterViewInit, OnChanges, OnDestroy { - +export class NgxFeaturesViewerComponent implements AfterViewInit, AfterContentInit, OnChanges, OnDestroy { + @ViewChild('root') public _root!: ElementRef; - @ContentChild(NgxFeaturesViewerLabelDirective) - public label?: NgxFeaturesViewerLabelDirective; + @ContentChildren(NgxFeaturesViewerLabelDirective) + public labels?: QueryList; + + public labelLeft?: NgxFeaturesViewerLabelDirective; + + public labelRight?: NgxFeaturesViewerLabelDirective; @Input() public set settings(settings: Settings) { @@ -90,7 +108,7 @@ export class NgxFeaturesViewerComponent implements AfterViewInit, OnChanges, OnD // Initialize zoom scale tap(() => { // const { width, height } = this.resizeService; - const { left: ms, right: me, bottom: mb } = this.resizeService.margin; + const {left: ms, right: me, bottom: mb} = this.resizeService.margin; const h = this.resizeService.height; const w = this.resizeService.width; // // Define number of residues in sequnce @@ -123,7 +141,7 @@ export class NgxFeaturesViewerComponent implements AfterViewInit, OnChanges, OnD this._update = this.update$.subscribe(); } - ngOnChanges(changes: SimpleChanges): void { + public ngOnChanges(changes: SimpleChanges): void { // Case input sequence changes if (changes && changes['sequence']) { // Emit sequence @@ -131,12 +149,33 @@ export class NgxFeaturesViewerComponent implements AfterViewInit, OnChanges, OnD } } - ngAfterViewInit(): void { + public ngAfterContentInit(): void { + // Case label templates are defined + if (this.labels) { + // Loop thorugh each label template + this.labels.forEach((label) => { + // Case both labels are defined, then throw error + if (this.labelLeft && this.labelRight) { + throw new Error('Only one label can be defined'); + } + // Case label is left + if (label.where === 'left') { + this.labelLeft = label; + } + // Case label is right + if (label.where === 'right') { + this.labelRight = label; + } + }); + } + } + + public ngAfterViewInit(): void { // Emit root element this.initService.initialize$.next(this._root); } - ngOnDestroy(): void { + public ngOnDestroy(): void { // Unsubscribe from update emission this._update.unsubscribe(); } diff --git a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.directive.ts b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.directive.ts index a1c058d..66e74d2 100644 --- a/projects/ngx-features-viewer/src/lib/ngx-features-viewer.directive.ts +++ b/projects/ngx-features-viewer/src/lib/ngx-features-viewer.directive.ts @@ -1,4 +1,4 @@ -import { Directive, TemplateRef } from '@angular/core'; +import { Directive, Input, TemplateRef } from '@angular/core'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector @@ -7,6 +7,16 @@ import { Directive, TemplateRef } from '@angular/core'; }) export class NgxFeaturesViewerLabelDirective { + where: 'left' | 'right' = 'left'; + + @Input('ngx-features-viewer-label') set _where(value: 'left' | 'right' | '' | undefined) { + if (value === 'left' || value === 'right') { + this.where = value; + } else { + this.where = 'left'; + } + } + constructor(public templateRef: TemplateRef) { } } diff --git a/projects/ngx-structure-viewer/package.json b/projects/ngx-structure-viewer/package.json index 2b96bd8..ce3032c 100644 --- a/projects/ngx-structure-viewer/package.json +++ b/projects/ngx-structure-viewer/package.json @@ -1,6 +1,6 @@ { "name": "ngx-structure-viewer", - "version": "0.0.13", + "version": "0.0.15", "license": "MIT", "author": { "name": "Damiano Clementel", @@ -9,7 +9,7 @@ "peerDependencies": { "@angular/common": "^17.0.0", "@angular/core": "^17.0.0", - "molstar": "^3.44.0", + "molstar": "^4.0.0", "rxjs": "~7.8.1" }, "dependencies": { diff --git a/projects/ngx-structure-viewer/src/lib/ngx-structure-viewer.component.ts b/projects/ngx-structure-viewer/src/lib/ngx-structure-viewer.component.ts index 029608b..a2dc5cf 100644 --- a/projects/ngx-structure-viewer/src/lib/ngx-structure-viewer.component.ts +++ b/projects/ngx-structure-viewer/src/lib/ngx-structure-viewer.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, Output, ViewChild } from '@angular/core'; +import { Component, ElementRef, Injector, Input, Optional, Output, SkipSelf, ViewChild } from '@angular/core'; import { ViewEncapsulation } from '@angular/core'; import { CommonModule } from '@angular/common'; // Custom dependencies @@ -21,12 +21,49 @@ import { Locus } from './interfaces/locus'; CommonModule, ], providers: [ + // Mandatory providers RepresentationService, HighlightService, - StructureService, SettingsService, - PluginService, CanvasService, + // Optional providers + // NOTE Those are created for the instance only if they are not provided by the parent component + { + provide: StructureService, + deps: [Injector, PluginService, [new Optional(), new SkipSelf(), StructureService],], + useFactory: (parentInjector: Injector, pluginService: PluginService, structureService?: StructureService) => { + // Case structure service is not provided, it must be created + if (!structureService) { + // Define injector for current component + const childInjector = Injector.create({ + providers: [StructureService, { provide: PluginService, useValue: pluginService }], + parent: parentInjector + }); + // get injected service + structureService = childInjector.get(StructureService); + } + // Return structure service + return structureService; + } + }, + { + provide: PluginService, + deps: [Injector, [new Optional(), new SkipSelf(), PluginService]], + useFactory: (parentInjector: Injector, pluginService?: PluginService) => { + // Case plugin service is not provided, it must be created + if (!pluginService) { + // Define injector for current component + const childInjector = Injector.create({ + providers: [PluginService], + parent: parentInjector + }); + // get injected service + pluginService = childInjector.get(PluginService); + } + // Return plugin service + return pluginService; + } + } ], standalone: true, // Handle representation diff --git a/projects/ngx-structure-viewer/src/lib/services/camera.service.ts b/projects/ngx-structure-viewer/src/lib/services/camera.service.ts index 73fa4b5..5de6924 100644 --- a/projects/ngx-structure-viewer/src/lib/services/camera.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/camera.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; -@Injectable({ - providedIn: 'root' -}) +@Injectable() export class CameraService { constructor() { } diff --git a/projects/ngx-structure-viewer/src/lib/services/canvas.service.ts b/projects/ngx-structure-viewer/src/lib/services/canvas.service.ts index 8384c06..b9b5056 100644 --- a/projects/ngx-structure-viewer/src/lib/services/canvas.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/canvas.service.ts @@ -9,10 +9,10 @@ import { PluginService } from './plugin.service'; import { fromHexString } from '../colors'; // React dependencies -import { createElement } from 'react'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; +import { createElement } from "react"; -@Injectable({ providedIn: 'platform' }) +@Injectable() export class CanvasService { readonly initialize$ = new ReplaySubject(1); @@ -38,56 +38,32 @@ export class CanvasService { // Get HTML div container const div = container.nativeElement as HTMLDivElement; // Get first child of div container - const canvas = div.firstElementChild as HTMLCanvasElement; + const canvas = div.firstElementChild as HTMLCanvasElement; // Return both elements return { div, canvas }; }), // Combine with plugin initialization combineLatestWith(plugin$), + // Combine with settings retrieval + combineLatestWith(settings$), // Create plugin's React UI - map(([{ div: container }, plugin]) => { - // // const { spec, target, onBeforeUIRender, render } = options; - // const ctx = new PluginUIContext(spec || DefaultPluginUISpec()); - // await ctx.init(); - // if (onBeforeUIRender) { - // await onBeforeUIRender(ctx); - // } - // Render - render(createElement(Plugin, { plugin }), container); - // try { - // await plugin.canvas3dInitialized; - // } catch { - // // Error reported in UI/console elsewhere. - // } - // Return initialize plugin + map(([[{ div: container }, plugin], settings]) => { + // Get background color + const [color, alpha] = fromHexString(settings['background-color']); + // Update canvas specs before rendering + this.pluginService.specs.canvas3d = { + renderer: { + backgroundColor: color, + pickingAlphaThreshold: alpha, + } + }; + // Render using React + createRoot(container).render(createElement(Plugin, { plugin })); + // Return initialized plugin return plugin; }), - // // Bind plugin to HTML elements - // tap(([{ div: container, canvas }, plugin]) => plugin.initViewer(canvas, container)), - // // Return initialized plugin - // map(([, plugin]) => plugin), // Cache result shareReplay(1), - // Combine with settings retrieval - combineLatestWith(settings$), - // Update plugin settings - map(([plugin, settings]) => { - // Case canvas3d is available - if (plugin.canvas3d) { - // Get background color - const [ color, alpha ] = fromHexString(settings['background-color']); - // Set background color - plugin.canvas3d.setProps({ - // Change background color - renderer: { - backgroundColor: color, - pickingAlphaThreshold: alpha, - } - }); - } - // Return updated plugin - return plugin; - }), ); } } diff --git a/projects/ngx-structure-viewer/src/lib/services/highlight.service.ts b/projects/ngx-structure-viewer/src/lib/services/highlight.service.ts index 19d1806..9f34bb3 100644 --- a/projects/ngx-structure-viewer/src/lib/services/highlight.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/highlight.service.ts @@ -10,7 +10,7 @@ import { Injectable, OnDestroy } from '@angular/core'; export type Highlights = Locus | undefined; -@Injectable({ providedIn: 'platform' }) +@Injectable() export class HighlightService implements OnDestroy { readonly input$ = new ReplaySubject(1); diff --git a/projects/ngx-structure-viewer/src/lib/services/plugin.service.ts b/projects/ngx-structure-viewer/src/lib/services/plugin.service.ts index 8a45b0b..005c276 100644 --- a/projects/ngx-structure-viewer/src/lib/services/plugin.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/plugin.service.ts @@ -1,25 +1,34 @@ -import { DefaultPluginUISpec } from 'molstar/lib/mol-plugin-ui/spec'; +import { DefaultPluginUISpec, PluginUISpec } from 'molstar/lib/mol-plugin-ui/spec'; import { PluginUIContext } from 'molstar/lib/mol-plugin-ui/context'; import { PluginConfig } from 'molstar/lib/mol-plugin/config'; // import { PluginContext } from 'molstar/lib/mol-plugin/context'; import { Observable, from, map, shareReplay } from 'rxjs'; import { Injectable } from '@angular/core'; +import { Color } from 'molstar/lib/mol-util/color'; // TODO Remove this -@Injectable({ providedIn: 'platform' }) +@Injectable() export class PluginService { readonly plugin!: PluginUIContext; + readonly specs: PluginUISpec = { ...DefaultPluginUISpec(), + // Show commands + config: [ + [PluginConfig.VolumeStreaming.Enabled, false] + ], + // TODO Remove this + canvas3d: { + renderer: { + backgroundColor: Color(0x000000), + } + } + }; + readonly plugin$: Observable; constructor() { // Initialize plugin context - this.plugin = new PluginUIContext({ - ...DefaultPluginUISpec(), - config: [ - [PluginConfig.VolumeStreaming.Enabled, false] - ] - }); + this.plugin = new PluginUIContext(this.specs); // Define plugin initialization pipeline this.plugin$ = from(this.plugin.init()).pipe( // Get current plugin instance @@ -27,43 +36,5 @@ export class PluginService { // Cache result shareReplay(1), ); - // this.container$.pipe( - // // Get outer HTML element - // map((container) => { - // // Get HTML div container - // const div = container.nativeElement as HTMLDivElement; - // // Get first child of div container - // const canvas = div.firstElementChild as HTMLCanvasElement; - // // Return both elements - // return { div, canvas }; - // }), - // // Bind plugin to HTML elements - // tap(({ div: container, canvas }) => this.plugin.initViewer(canvas, container)), - // // Initialize plugin - // switchMap(() => ), - // // Return initialized plugin - // map(() => this.plugin), - // // Cache result - // shareReplay(1), - // // Combine with settings retrieval - // combineLatestWith(settings$), - // // Update plugin settings - // map(([plugin, settings]) => { - // // Case canvas3d is available - // if (plugin.canvas3d) { - // // Get background color - // const [ color, alpha ] = fromHexString(settings['background-color']); - // // Set background color - // plugin.canvas3d.setProps({ - // // Change background color - // renderer: { - // backgroundColor: color, - // pickingAlphaThreshold: alpha, - // } - // }); - // } - // // Return updated plugin - // return plugin; - // }), } } diff --git a/projects/ngx-structure-viewer/src/lib/services/representation.service.ts b/projects/ngx-structure-viewer/src/lib/services/representation.service.ts index abf52df..6c8f25e 100644 --- a/projects/ngx-structure-viewer/src/lib/services/representation.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/representation.service.ts @@ -52,7 +52,7 @@ export function getFilteredBundle(layers: Overpaint.BundleLayer[], structure: St return Overpaint.filter(merged, structure); } -@Injectable({ providedIn: 'platform' }) +@Injectable() export class RepresentationService implements OnDestroy { readonly loci$ = new ReplaySubject(1); diff --git a/projects/ngx-structure-viewer/src/lib/services/select.service.ts b/projects/ngx-structure-viewer/src/lib/services/select.service.ts index 195fac9..884f418 100644 --- a/projects/ngx-structure-viewer/src/lib/services/select.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/select.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; -@Injectable({ - providedIn: 'root' -}) +@Injectable() export class SelectService { constructor() { } diff --git a/projects/ngx-structure-viewer/src/lib/services/settings.service.ts b/projects/ngx-structure-viewer/src/lib/services/settings.service.ts index a8d936a..7b5efc7 100644 --- a/projects/ngx-structure-viewer/src/lib/services/settings.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/settings.service.ts @@ -2,7 +2,7 @@ import { Settings } from '../interfaces/settings'; import { Injectable } from '@angular/core'; import { ReplaySubject } from 'rxjs'; -@Injectable({ providedIn: 'platform' }) +@Injectable() export class SettingsService { readonly settings$ = new ReplaySubject(); diff --git a/projects/ngx-structure-viewer/src/lib/services/structure.service.ts b/projects/ngx-structure-viewer/src/lib/services/structure.service.ts index 49a99c1..e68f820 100644 --- a/projects/ngx-structure-viewer/src/lib/services/structure.service.ts +++ b/projects/ngx-structure-viewer/src/lib/services/structure.service.ts @@ -9,7 +9,7 @@ import { PluginService } from './plugin.service'; import { Residue, threeToOne } from '../interfaces/residue'; import { Source } from '../interfaces/source'; -@Injectable({ providedIn: 'platform' }) +@Injectable() export class StructureService { // eslint-disable-next-line @typescript-eslint/no-explicit-any