1- import React , { KeyboardEvent } from "react" ;
1+ import React , { KeyboardEvent , RefObject } from "react" ;
22import ReactDOM from "react-dom" ;
33import Align from "rc-align" ;
44import { randomIntInRange } from "../../../../common/utils" ;
@@ -31,9 +31,9 @@ export interface ITagInputProps {
3131 /** Function to call on clicking individual tag while holding CTRL key */
3232 onCtrlTagClick ?: ( tag : ITag ) => void ;
3333 /** Function to call when tag is renamed */
34- onTagRenamed ?: ( oldTag : string , newTag : string ) => void ;
34+ onTagRenamed ?: ( tagName : string , newTagName : string ) => void ;
3535 /** Function to call when tag is deleted */
36- onTagDeleted ?: ( tag : ITag ) => void ;
36+ onTagDeleted ?: ( tagName : string ) => void ;
3737 /** Always show tag input box */
3838 showTagInputBox ?: boolean ;
3939 /** Always show tag search box */
@@ -72,7 +72,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
7272 portalElement : defaultDOMNode ( ) ,
7373 } ;
7474
75- private tagItemRefs : { [ id : string ] : TagInputItem } = { } ;
75+ private tagItemRefs : Map < string , RefObject < TagInputItem > > = new Map < string , RefObject < TagInputItem > > ( ) ;
7676 private portalDiv = document . createElement ( "div" ) ;
7777
7878 public render ( ) {
@@ -109,7 +109,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
109109 }
110110 { this . getColorPickerPortal ( ) }
111111 < div className = "tag-input-items" >
112- { this . getTagItems ( ) }
112+ { this . renderTagItems ( ) }
113113 </ div >
114114 {
115115 this . state . addTags &&
@@ -154,11 +154,13 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
154154 }
155155 }
156156
157- private getTagNode = ( tag : ITag ) => {
157+ private getTagNode = ( tag : ITag ) : Element => {
158158 if ( ! tag ) {
159159 return defaultDOMNode ( ) ;
160160 }
161- return ReactDOM . findDOMNode ( this . tagItemRefs [ tag . name ] ) as Element ;
161+
162+ const itemRef = this . tagItemRefs . get ( tag . name ) ;
163+ return ( itemRef ? ReactDOM . findDOMNode ( itemRef . current ) : defaultDOMNode ( ) ) as Element ;
162164 }
163165
164166 private onEditTag = ( tag : ITag ) => {
@@ -219,20 +221,25 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
219221 } , ( ) => this . props . onChange ( tags ) ) ;
220222 }
221223
222- private updateTag = ( oldTag : ITag , newTag : ITag ) => {
223- if ( oldTag === newTag ) {
224+ private updateTag = ( tag : ITag , newTag : ITag ) => {
225+ if ( tag === newTag ) {
224226 return ;
225227 }
226228 if ( ! newTag . name . length ) {
227229 toast . warn ( strings . tags . warnings . emptyName ) ;
228230 return ;
229231 }
230- if ( newTag . name !== oldTag . name && this . state . tags . some ( ( t ) => t . name === newTag . name ) ) {
232+ const nameChange = tag . name !== newTag . name ;
233+ if ( nameChange && this . state . tags . some ( ( t ) => t . name === newTag . name ) ) {
231234 toast . warn ( strings . tags . warnings . existingName ) ;
232235 return ;
233236 }
237+ if ( nameChange && this . props . onTagRenamed ) {
238+ this . props . onTagRenamed ( tag . name , newTag . name ) ;
239+ return ;
240+ }
234241 const tags = this . state . tags . map ( ( t ) => {
235- return ( t . name === oldTag . name ) ? newTag : t ;
242+ return ( t . name === tag . name ) ? newTag : t ;
236243 } ) ;
237244 this . setState ( {
238245 tags,
@@ -293,12 +300,15 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
293300 return this . state . editingTagNode || document ;
294301 }
295302
296- private getTagItems = ( ) => {
297- let props = this . getTagItemProps ( ) ;
303+ private renderTagItems = ( ) => {
304+ let props = this . createTagItemProps ( ) ;
298305 const query = this . state . searchQuery ;
306+ this . tagItemRefs . clear ( ) ;
307+
299308 if ( query . length ) {
300309 props = props . filter ( ( prop ) => prop . tag . name . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ) ;
301310 }
311+
302312 return props . map ( ( prop ) =>
303313 < TagInputItem
304314 key = { prop . tag . name }
@@ -308,16 +318,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
308318 }
309319
310320 private setTagItemRef = ( item , tag ) => {
311- if ( item ) {
312- this . tagItemRefs [ tag . name ] = item ;
313- }
321+ this . tagItemRefs . set ( tag . name , item ) ;
322+ return item ;
314323 }
315324
316- private getTagItemProps = ( ) : ITagInputItemProps [ ] => {
325+ private createTagItemProps = ( ) : ITagInputItemProps [ ] => {
317326 const tags = this . state . tags ;
318327 const selectedRegionTagSet = this . getSelectedRegionTagSet ( ) ;
319- return tags . map ( ( tag ) => {
320- const item : ITagInputItemProps = {
328+
329+ return tags . map ( ( tag ) => (
330+ {
321331 tag,
322332 index : tags . findIndex ( ( t ) => t . name === tag . name ) ,
323333 isLocked : this . props . lockedTags && this . props . lockedTags . findIndex ( ( t ) => t === tag . name ) > - 1 ,
@@ -326,9 +336,8 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
326336 appliedToSelectedRegions : selectedRegionTagSet . has ( tag . name ) ,
327337 onClick : this . handleClick ,
328338 onChange : this . updateTag ,
329- } ;
330- return item ;
331- } ) ;
339+ } as ITagInputItemProps
340+ ) ) ;
332341 }
333342
334343 private getSelectedRegionTagSet = ( ) : Set < string > => {
@@ -346,6 +355,7 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
346355 private onAltClick = ( tag : ITag , clickedColor : boolean ) => {
347356 const { editingTag } = this . state ;
348357 const newEditingTag = editingTag && editingTag . name === tag . name ? null : tag ;
358+
349359 this . setState ( {
350360 editingTag : newEditingTag ,
351361 editingTagNode : this . getTagNode ( newEditingTag ) ,
@@ -355,16 +365,16 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
355365 }
356366
357367 private handleClick = ( tag : ITag , props : ITagClickProps ) => {
368+ // Lock tags
358369 if ( props . ctrlKey && this . props . onCtrlTagClick ) {
359370 this . props . onCtrlTagClick ( tag ) ;
360371 this . setState ( { clickedColor : props . clickedColor } ) ;
361- } else if ( props . altKey ) {
372+ } else if ( props . altKey ) { // Edit tag
362373 this . onAltClick ( tag , props . clickedColor ) ;
363- } else {
374+ } else { // Select tag
364375 const { editingTag, selectedTag } = this . state ;
365376 const inEditMode = editingTag && tag . name === editingTag . name ;
366377 const alreadySelected = selectedTag && selectedTag . name === tag . name ;
367-
368378 const newEditingTag = inEditMode ? null : editingTag ;
369379
370380 this . setState ( {
@@ -389,12 +399,19 @@ export class TagInput extends React.Component<ITagInputProps, ITagInputState> {
389399 if ( ! tag ) {
390400 return ;
391401 }
402+ if ( this . props . onTagDeleted ) {
403+ this . props . onTagDeleted ( tag . name ) ;
404+ return ;
405+ }
406+
392407 const index = this . state . tags . indexOf ( tag ) ;
393408 const tags = this . state . tags . filter ( ( t ) => t . name !== tag . name ) ;
409+
394410 this . setState ( {
395411 tags,
396412 selectedTag : this . getNewSelectedTag ( tags , index ) ,
397413 } , ( ) => this . props . onChange ( tags ) ) ;
414+
398415 if ( this . props . lockedTags . find ( ( l ) => l === tag . name ) ) {
399416 this . props . onLockedTagsChange (
400417 this . props . lockedTags . filter ( ( lockedTag ) => lockedTag !== tag . name ) ,
0 commit comments