@@ -11,10 +11,12 @@ import {
1111 SimpleChanges ,
1212 ViewEncapsulation ,
1313 booleanAttribute ,
14+ forwardRef ,
1415} from '@angular/core' ;
16+ import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
1517
1618import { DiffConfig , MergeView } from '@codemirror/merge' ;
17- import { Extension } from '@codemirror/state' ;
19+ import { Compartment , Extension } from '@codemirror/state' ;
1820import { EditorView } from '@codemirror/view' ;
1921import { basicSetup , minimalSetup } from 'codemirror' ;
2022
@@ -24,6 +26,11 @@ export type Orientation = 'a-b' | 'b-a';
2426export type RevertControls = 'a-to-b' | 'b-to-a' ;
2527export type RenderRevertControl = ( ) => HTMLElement ;
2628
29+ export interface DiffEditorModel {
30+ original : string ;
31+ modified : string ;
32+ }
33+
2734@Component ( {
2835 selector : 'diff-editor' ,
2936 standalone : true ,
@@ -48,8 +55,15 @@ export type RenderRevertControl = () => HTMLElement;
4855 } ,
4956 encapsulation : ViewEncapsulation . None ,
5057 changeDetection : ChangeDetectionStrategy . OnPush ,
58+ providers : [
59+ {
60+ provide : NG_VALUE_ACCESSOR ,
61+ useExisting : forwardRef ( ( ) => DiffEditor ) ,
62+ multi : true ,
63+ } ,
64+ ] ,
5165} )
52- export class DiffEditor implements OnChanges , OnInit , OnDestroy {
66+ export class DiffEditor implements OnChanges , OnInit , OnDestroy , ControlValueAccessor {
5367 /**
5468 * The editor's built-in setup. The value can be set to
5569 * [`basic`](https://codemirror.net/docs/ref/#codemirror.basicSetup),
@@ -60,7 +74,7 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
6074 @Input ( ) setup : Setup = 'basic' ;
6175
6276 /** The diff-editor's original value. */
63- @Input ( ) originalValue = '' ;
77+ @Input ( ) originalValue : string = '' ;
6478
6579 /**
6680 * The MergeView original config's
@@ -71,7 +85,7 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
7185 @Input ( ) originalExtensions : Extension [ ] = [ ] ;
7286
7387 /** The diff-editor's modified value. */
74- @Input ( ) modifiedValue = '' ;
88+ @Input ( ) modifiedValue : string = '' ;
7589
7690 /**
7791 * The MergeView modified config's
@@ -99,6 +113,9 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
99113 /** Controls whether a gutter marker is shown next to changed lines. */
100114 @Input ( { transform : booleanAttribute } ) gutter = true ;
101115
116+ /** Whether the diff-editor is disabled. */
117+ @Input ( { transform : booleanAttribute } ) disabled = false ;
118+
102119 /**
103120 * When given, long stretches of unchanged text are collapsed.
104121 * `margin` gives the number of lines to leave visible after/before
@@ -116,26 +133,39 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
116133 /** Event emitted when the editor's modified value changes. */
117134 @Output ( ) modifiedValueChange = new EventEmitter < string > ( ) ;
118135
136+ private _onChange : ( value : DiffEditorModel ) => void = ( ) => { } ;
137+ private _onTouched : ( ) => void = ( ) => { } ;
138+
119139 constructor ( private _elementRef : ElementRef < Element > ) { }
120140
121141 /** The merge view instance. */
122142 mergeView ?: MergeView ;
123143
124- private _updateListener = ( valueChange : EventEmitter < string > ) => {
144+ private _updateListener = ( editor : 'a' | 'b' ) => {
125145 return EditorView . updateListener . of ( vu => {
126146 if ( vu . docChanged && ! vu . transactions . some ( tr => tr . annotation ( External ) ) ) {
127147 const value = vu . state . doc . toString ( ) ;
128- valueChange . emit ( value ) ;
148+ if ( editor == 'a' ) {
149+ this . _onChange ( { original : value , modified : this . modifiedValue } ) ;
150+ this . originalValue = value ;
151+ this . originalValueChange . emit ( value ) ;
152+ } else if ( editor == 'b' ) {
153+ this . _onChange ( { original : this . originalValue , modified : value } ) ;
154+ this . modifiedValue = value ;
155+ this . modifiedValueChange . emit ( value ) ;
156+ }
129157 }
130158 } ) ;
131159 } ;
132160
161+ private _editableConf = new Compartment ( ) ;
162+
133163 ngOnChanges ( changes : SimpleChanges ) : void {
134164 if ( changes [ 'originalValue' ] ) {
135- this . setOriginalValue ( this . originalValue ) ;
165+ this . setValue ( 'a' , this . originalValue ) ;
136166 }
137167 if ( changes [ 'modifiedValue' ] ) {
138- this . setModifiedValue ( this . modifiedValue ) ;
168+ this . setValue ( 'b' , this . modifiedValue ) ;
139169 }
140170 if ( changes [ 'orientation' ] ) {
141171 this . mergeView ?. reconfigure ( { orientation : this . orientation } ) ;
@@ -166,15 +196,17 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
166196 a : {
167197 doc : this . originalValue ,
168198 extensions : [
169- this . _updateListener ( this . originalValueChange ) ,
199+ this . _updateListener ( 'a' ) ,
200+ this . _editableConf . of ( [ ] ) ,
170201 this . setup === 'basic' ? basicSetup : this . setup === 'minimal' ? minimalSetup : [ ] ,
171202 ...this . originalExtensions ,
172203 ] ,
173204 } ,
174205 b : {
175206 doc : this . modifiedValue ,
176207 extensions : [
177- this . _updateListener ( this . modifiedValueChange ) ,
208+ this . _updateListener ( 'b' ) ,
209+ this . _editableConf . of ( [ ] ) ,
178210 this . setup === 'basic' ? basicSetup : this . setup === 'minimal' ? minimalSetup : [ ] ,
179211 ...this . modifiedExtensions ,
180212 ] ,
@@ -187,23 +219,49 @@ export class DiffEditor implements OnChanges, OnInit, OnDestroy {
187219 collapseUnchanged : this . collapseUnchanged ,
188220 diffConfig : this . diffConfig ,
189221 } ) ;
222+
223+ this . setEditable ( 'a' , ! this . disabled ) ;
224+ this . setEditable ( 'b' , ! this . disabled ) ;
190225 }
191226
192227 ngOnDestroy ( ) : void {
193228 this . mergeView ?. destroy ( ) ;
194229 }
195230
196- /** Sets diff-editor's original value. */
197- setOriginalValue ( value : string ) {
198- this . mergeView ?. a . dispatch ( {
199- changes : { from : 0 , to : this . mergeView . a . state . doc . length , insert : value } ,
231+ writeValue ( value : DiffEditorModel ) : void {
232+ if ( this . mergeView && value != null && typeof value === 'object' ) {
233+ this . originalValue = value . original ;
234+ this . modifiedValue = value . modified ;
235+ this . setValue ( 'a' , value . original ) ;
236+ this . setValue ( 'b' , value . modified ) ;
237+ }
238+ }
239+
240+ registerOnChange ( fn : ( value : DiffEditorModel ) => void ) {
241+ this . _onChange = fn ;
242+ }
243+
244+ registerOnTouched ( fn : ( ) => void ) {
245+ this . _onTouched = fn ;
246+ }
247+
248+ setDisabledState ( isDisabled : boolean ) {
249+ this . disabled = isDisabled ;
250+ this . setEditable ( 'a' , ! isDisabled ) ;
251+ this . setEditable ( 'b' , ! isDisabled ) ;
252+ }
253+
254+ /** Sets diff-editor's value. */
255+ setValue ( editor : 'a' | 'b' , value : string ) {
256+ this . mergeView ?. [ editor ] . dispatch ( {
257+ changes : { from : 0 , to : this . mergeView [ editor ] . state . doc . length , insert : value } ,
200258 } ) ;
201259 }
202260
203- /** Sets diff-editor's modified value . */
204- setModifiedValue ( value : string ) {
205- this . mergeView ?. b . dispatch ( {
206- changes : { from : 0 , to : this . mergeView . b . state . doc . length , insert : value } ,
261+ /** Sets diff-editor's editable state . */
262+ setEditable ( editor : 'a' | 'b' , value : boolean ) {
263+ this . mergeView ?. [ editor ] . dispatch ( {
264+ effects : this . _editableConf . reconfigure ( EditorView . editable . of ( value ) ) ,
207265 } ) ;
208266 }
209267}
0 commit comments