@@ -6,16 +6,16 @@ import { Button } from 'primeng/button';
6
6
import { Checkbox } from 'primeng/checkbox' ;
7
7
import { DynamicDialogConfig , DynamicDialogRef } from 'primeng/dynamicdialog' ;
8
8
9
- import { ChangeDetectionStrategy , Component , effect , inject , OnInit , signal } from '@angular/core' ;
9
+ import { ChangeDetectionStrategy , Component , effect , inject , OnInit , signal , WritableSignal } from '@angular/core' ;
10
10
import { FormControl , FormsModule , ReactiveFormsModule } from '@angular/forms' ;
11
11
12
12
import { LoadingSpinnerComponent , TextInputComponent } from '@osf/shared/components' ;
13
13
import { InputLimits } from '@osf/shared/constants' ;
14
14
import { 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' ;
17
16
18
- import { ResourceInfoModel } from '../../models' ;
17
+ import { ResourceInfoModel , ViewOnlyLinkComponentItem } from '../../models' ;
18
+ import { ComponentCheckboxItemComponent } from '../component-checkbox-item/component-checkbox-item.component' ;
19
19
20
20
@Component ( {
21
21
selector : 'osf-create-view-link-dialog' ,
@@ -27,6 +27,7 @@ import { ResourceInfoModel } from '../../models';
27
27
Checkbox ,
28
28
TextInputComponent ,
29
29
LoadingSpinnerComponent ,
30
+ ComponentCheckboxItemComponent ,
30
31
] ,
31
32
templateUrl : './create-view-link-dialog.component.html' ,
32
33
styleUrl : './create-view-link-dialog.component.scss' ,
@@ -38,122 +39,126 @@ export class CreateViewLinkDialogComponent implements OnInit {
38
39
readonly inputLimits = InputLimits ;
39
40
40
41
linkName = new FormControl ( '' , { nonNullable : true , validators : [ CustomValidators . requiredTrimmed ( ) ] } ) ;
41
-
42
42
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 ( ) ;
56
43
57
- const result : ViewOnlyLinkChildren [ ] = [ ] ;
44
+ readonly components = select ( CurrentResourceSelectors . getResourceWithChildren ) ;
45
+ readonly isLoading = select ( CurrentResourceSelectors . isResourceWithChildrenLoading ) ;
46
+ readonly actions = createDispatchMap ( { getComponents : GetResourceWithChildren } ) ;
58
47
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 ( [ ] ) ;
77
49
78
50
constructor ( ) {
79
51
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 ) ;
84
70
} ) ;
85
71
}
86
72
87
73
ngOnInit ( ) : void {
88
- const projectId = this . currentResource . id ;
74
+ const currentResource = this . config . data as ResourceInfoModel ;
75
+ const { id, type } = currentResource ;
89
76
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 ) ;
94
79
}
95
80
}
96
81
97
- private initializeSelection ( ) : void {
98
- const initialState : Record < string , boolean > = { } ;
82
+ onCheckboxChange ( changedItem : ViewOnlyLinkComponentItem ) : void {
83
+ this . componentsList . update ( ( items ) => {
84
+ let updatedItems = [ ...items ] ;
99
85
100
- this . allComponents . forEach ( ( component ) => {
101
- initialState [ component . id ] = component . isCurrentResource ;
102
- } ) ;
86
+ if ( ! changedItem . checked ) {
87
+ updatedItems = this . uncheckChildren ( changedItem . id , updatedItems ) ;
88
+ }
103
89
104
- this . selectedComponents . set ( initialState ) ;
90
+ return updatedItems . map ( ( item ) => ( {
91
+ ...item ,
92
+ disabled : item . isCurrentResource ? item . disabled : ! this . isParentChecked ( item , updatedItems ) ,
93
+ } ) ) ;
94
+ } ) ;
105
95
}
106
96
107
97
addLink ( ) : void {
108
98
if ( this . linkName . invalid ) return ;
109
99
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 ) ;
113
104
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 ) ;
116
116
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' } ] : [ ] ;
117
144
const relationshipComponents = selectedIds
118
145
. filter ( ( id ) => id !== rootProjectId )
119
146
. map ( ( id ) => ( { id, type : 'nodes' } ) ) ;
120
147
121
148
const data : Record < string , unknown > = {
122
149
attributes : {
123
- name : this . linkName . value ,
124
- anonymous : this . anonymous ( ) ,
150
+ name : linkName ,
151
+ anonymous : isAnonymous ,
125
152
} ,
126
153
nodes : rootProject ,
127
154
} ;
128
155
129
156
if ( relationshipComponents . length ) {
130
157
data [ 'relationships' ] = {
131
- nodes : {
132
- data : relationshipComponents ,
133
- } ,
158
+ nodes : { data : relationshipComponents } ,
134
159
} ;
135
160
}
136
161
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 ;
158
163
}
159
164
}
0 commit comments