@@ -6,16 +6,16 @@ import { Button } from 'primeng/button';
66import { Checkbox } from 'primeng/checkbox' ;
77import { DynamicDialogConfig , DynamicDialogRef } from 'primeng/dynamicdialog' ;
88
9- import { ChangeDetectionStrategy , Component , effect , inject , OnInit , signal } from '@angular/core' ;
9+ import { ChangeDetectionStrategy , Component , effect , inject , OnInit , signal , WritableSignal } from '@angular/core' ;
1010import { FormControl , FormsModule , ReactiveFormsModule } from '@angular/forms' ;
1111
1212import { LoadingSpinnerComponent , TextInputComponent } from '@osf/shared/components' ;
1313import { InputLimits } from '@osf/shared/constants' ;
1414import { CustomValidators } from '@osf/shared/helpers' ;
15- import { CurrentResourceSelectors , GetResourceChildren } from '@osf/shared/stores' ;
16- import { ViewOnlyLinkChildren } from '@shared/models' ;
15+ import { CurrentResourceSelectors , GetResourceWithChildren } from '@osf/shared/stores' ;
1716
18- import { ResourceInfoModel } from '../../models' ;
17+ import { ResourceInfoModel , ViewOnlyLinkComponentItem } from '../../models' ;
18+ import { ComponentCheckboxItemComponent } from '../component-checkbox-item/component-checkbox-item.component' ;
1919
2020@Component ( {
2121 selector : 'osf-create-view-link-dialog' ,
@@ -27,6 +27,7 @@ import { ResourceInfoModel } from '../../models';
2727 Checkbox ,
2828 TextInputComponent ,
2929 LoadingSpinnerComponent ,
30+ ComponentCheckboxItemComponent ,
3031 ] ,
3132 templateUrl : './create-view-link-dialog.component.html' ,
3233 styleUrl : './create-view-link-dialog.component.scss' ,
@@ -38,122 +39,126 @@ export class CreateViewLinkDialogComponent implements OnInit {
3839 readonly inputLimits = InputLimits ;
3940
4041 linkName = new FormControl ( '' , { nonNullable : true , validators : [ CustomValidators . requiredTrimmed ( ) ] } ) ;
41-
4242 anonymous = signal ( true ) ;
43- selectedComponents = signal < Record < string , boolean > > ( { } ) ;
44- components = select ( CurrentResourceSelectors . getResourceChildren ) ;
45- isLoading = select ( CurrentResourceSelectors . isResourceChildrenLoading ) ;
46-
47- actions = createDispatchMap ( { getComponents : GetResourceChildren } ) ;
48-
49- get currentResource ( ) {
50- return this . config . data as ResourceInfoModel ;
51- }
52-
53- get allComponents ( ) : ViewOnlyLinkChildren [ ] {
54- const currentResourceData = this . currentResource ;
55- const components = this . components ( ) ;
5643
57- const result : ViewOnlyLinkChildren [ ] = [ ] ;
44+ readonly components = select ( CurrentResourceSelectors . getResourceWithChildren ) ;
45+ readonly isLoading = select ( CurrentResourceSelectors . isResourceWithChildrenLoading ) ;
46+ readonly actions = createDispatchMap ( { getComponents : GetResourceWithChildren } ) ;
5847
59- if ( currentResourceData ) {
60- result . push ( {
61- id : currentResourceData . id ,
62- title : currentResourceData . title ,
63- isCurrentResource : true ,
64- } ) ;
65- }
66-
67- components . forEach ( ( comp ) => {
68- result . push ( {
69- id : comp . id ,
70- title : comp . title ,
71- isCurrentResource : false ,
72- } ) ;
73- } ) ;
74-
75- return result ;
76- }
48+ componentsList : WritableSignal < ViewOnlyLinkComponentItem [ ] > = signal ( [ ] ) ;
7749
7850 constructor ( ) {
7951 effect ( ( ) => {
80- const components = this . allComponents ;
81- if ( components . length ) {
82- this . initializeSelection ( ) ;
83- }
52+ const currentResource = this . config . data as ResourceInfoModel ;
53+ const components = this . components ( ) ;
54+
55+ const items : ViewOnlyLinkComponentItem [ ] = components . map ( ( item ) => ( {
56+ id : item . id ,
57+ title : item . title ,
58+ isCurrentResource : currentResource . id === item . id ,
59+ parentId : item . parentId ,
60+ checked : currentResource . id === item . id ,
61+ disabled : currentResource . id === item . id ,
62+ } ) ) ;
63+
64+ const updatedItems = items . map ( ( item ) => ( {
65+ ...item ,
66+ disabled : item . isCurrentResource ? item . disabled : ! this . isParentChecked ( item , items ) ,
67+ } ) ) ;
68+
69+ this . componentsList . set ( updatedItems ) ;
8470 } ) ;
8571 }
8672
8773 ngOnInit ( ) : void {
88- const projectId = this . currentResource . id ;
74+ const currentResource = this . config . data as ResourceInfoModel ;
75+ const { id, type } = currentResource ;
8976
90- if ( projectId ) {
91- this . actions . getComponents ( projectId , this . currentResource . type ) ;
92- } else {
93- this . initializeSelection ( ) ;
77+ if ( id ) {
78+ this . actions . getComponents ( id , type ) ;
9479 }
9580 }
9681
97- private initializeSelection ( ) : void {
98- const initialState : Record < string , boolean > = { } ;
82+ onCheckboxChange ( changedItem : ViewOnlyLinkComponentItem ) : void {
83+ this . componentsList . update ( ( items ) => {
84+ let updatedItems = [ ...items ] ;
9985
100- this . allComponents . forEach ( ( component ) => {
101- initialState [ component . id ] = component . isCurrentResource ;
102- } ) ;
86+ if ( ! changedItem . checked ) {
87+ updatedItems = this . uncheckChildren ( changedItem . id , updatedItems ) ;
88+ }
10389
104- this . selectedComponents . set ( initialState ) ;
90+ return updatedItems . map ( ( item ) => ( {
91+ ...item ,
92+ disabled : item . isCurrentResource ? item . disabled : ! this . isParentChecked ( item , updatedItems ) ,
93+ } ) ) ;
94+ } ) ;
10595 }
10696
10797 addLink ( ) : void {
10898 if ( this . linkName . invalid ) return ;
10999
110- const selectedIds = Object . entries ( this . selectedComponents ( ) )
111- . filter ( ( [ , checked ] ) => checked )
112- . map ( ( [ id ] ) => id ) ;
100+ const currentResource = this . config . data as ResourceInfoModel ;
101+ const selectedIds = this . componentsList ( )
102+ . filter ( ( x ) => x . checked )
103+ . map ( ( x ) => x . id ) ;
113104
114- const rootProjectId = this . currentResource . id ;
115- const rootProject = selectedIds . includes ( rootProjectId ) ? [ { id : rootProjectId , type : 'nodes' } ] : [ ] ;
105+ const data = this . buildLinkData ( selectedIds , currentResource . id , this . linkName . value , this . anonymous ( ) ) ;
106+
107+ this . dialogRef . close ( data ) ;
108+ }
109+
110+ private isParentChecked ( item : ViewOnlyLinkComponentItem , items : ViewOnlyLinkComponentItem [ ] ) : boolean {
111+ if ( ! item . parentId ) {
112+ return true ;
113+ }
114+
115+ const parent = items . find ( ( x ) => x . id === item . parentId ) ;
116116
117+ return parent ?. checked ?? true ;
118+ }
119+
120+ private uncheckChildren ( parentId : string , items : ViewOnlyLinkComponentItem [ ] ) : ViewOnlyLinkComponentItem [ ] {
121+ let updatedItems = items . map ( ( item ) => {
122+ if ( item . parentId === parentId ) {
123+ return { ...item , checked : false } ;
124+ }
125+ return item ;
126+ } ) ;
127+
128+ const directChildren = updatedItems . filter ( ( item ) => item . parentId === parentId ) ;
129+
130+ for ( const child of directChildren ) {
131+ updatedItems = this . uncheckChildren ( child . id , updatedItems ) ;
132+ }
133+
134+ return updatedItems ;
135+ }
136+
137+ private buildLinkData (
138+ selectedIds : string [ ] ,
139+ rootProjectId : string ,
140+ linkName : string ,
141+ isAnonymous : boolean
142+ ) : Record < string , unknown > {
143+ const rootProject = selectedIds . includes ( rootProjectId ) ? [ { id : rootProjectId , type : 'nodes' } ] : [ ] ;
117144 const relationshipComponents = selectedIds
118145 . filter ( ( id ) => id !== rootProjectId )
119146 . map ( ( id ) => ( { id, type : 'nodes' } ) ) ;
120147
121148 const data : Record < string , unknown > = {
122149 attributes : {
123- name : this . linkName . value ,
124- anonymous : this . anonymous ( ) ,
150+ name : linkName ,
151+ anonymous : isAnonymous ,
125152 } ,
126153 nodes : rootProject ,
127154 } ;
128155
129156 if ( relationshipComponents . length ) {
130157 data [ 'relationships' ] = {
131- nodes : {
132- data : relationshipComponents ,
133- } ,
158+ nodes : { data : relationshipComponents } ,
134159 } ;
135160 }
136161
137- this . dialogRef . close ( data ) ;
138- }
139-
140- onCheckboxToggle ( id : string , checked : boolean ) : void {
141- this . selectedComponents . update ( ( prev ) => ( { ...prev , [ id ] : checked } ) ) ;
142- }
143-
144- selectAllComponents ( ) : void {
145- const allIds : Record < string , boolean > = { } ;
146- this . allComponents . forEach ( ( component ) => {
147- allIds [ component . id ] = true ;
148- } ) ;
149- this . selectedComponents . set ( allIds ) ;
150- }
151-
152- deselectAllComponents ( ) : void {
153- const allIds : Record < string , boolean > = { } ;
154- this . allComponents . forEach ( ( component ) => {
155- allIds [ component . id ] = component . isCurrentResource ;
156- } ) ;
157- this . selectedComponents . set ( allIds ) ;
162+ return data ;
158163 }
159164}
0 commit comments