Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced the Collection.addMany method and Collection.change event #7644

Merged
merged 11 commits into from
Jul 20, 2020
Merged
20 changes: 5 additions & 15 deletions packages/ckeditor5-font/src/documentcolorcollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export default class DocumentColorCollection extends Collection {
* @member {Boolean} #isEmpty
*/
this.set( 'isEmpty', true );

this.on( 'change', () => {
this.set( 'isEmpty', this.length === 0 );
} );
}

/**
Expand All @@ -44,6 +48,7 @@ export default class DocumentColorCollection extends Collection {
* @param {Number} [index] The position of the item in the collection. The item
* is pushed to the collection when `index` is not specified.
* @fires add
* @fires change
*/
add( item, index ) {
if ( this.find( element => element.color === item.color ) ) {
Expand All @@ -52,21 +57,6 @@ export default class DocumentColorCollection extends Collection {
}

super.add( item, index );

this.set( 'isEmpty', false );
}

/**
* @inheritdoc
*/
remove( subject ) {
const ret = super.remove( subject );

if ( this.length === 0 ) {
this.set( 'isEmpty', true );
}

return ret;
}

/**
Expand Down
165 changes: 116 additions & 49 deletions packages/ckeditor5-utils/src/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,24 @@ export default class Collection {
* @param {Number} [index] The position of the item in the collection. The item
* is pushed to the collection when `index` not specified.
* @fires add
* @fires change
*/
add( item, index ) {
const itemId = this._getItemIdBeforeAdding( item );
return this.addMany( [ item ], index );
}

// TODO: Use ES6 default function argument.
/**
* Adds multiple items into the collection.
*
* Any item not containing an id will get an automatically generated one.
*
* @chainable
* @param {Iterable.<Object>} item
* @param {Number} [index] The position of the insertion. Items will be appended if no `index` is specified.
* @fires add
* @fires change
*/
addMany( items, index ) {
if ( index === undefined ) {
index = this._items.length;
} else if ( index > this._items.length || index < 0 ) {
Expand All @@ -195,11 +208,22 @@ export default class Collection {
throw new CKEditorError( 'collection-add-item-invalid-index', this );
}

this._items.splice( index, 0, item );
for ( let offset = 0; offset < items.length; offset++ ) {
const item = items[ offset ];
const itemId = this._getItemIdBeforeAdding( item );
const currentItemIndex = index + offset;

this._itemMap.set( itemId, item );
this._items.splice( currentItemIndex, 0, item );
this._itemMap.set( itemId, item );

this.fire( 'add', item, index );
this.fire( 'add', item, currentItemIndex );
}

this.fire( 'change', {
added: items,
removed: [],
index
} );

return this;
}
Expand Down Expand Up @@ -271,52 +295,16 @@ export default class Collection {
* @param {Object|Number|String} subject The item to remove, its id or index in the collection.
* @returns {Object} The removed item.
* @fires remove
* @fires change
*/
remove( subject ) {
let index, id, item;
let itemDoesNotExist = false;
const idProperty = this._idProperty;

if ( typeof subject == 'string' ) {
id = subject;
item = this._itemMap.get( id );
itemDoesNotExist = !item;

if ( item ) {
index = this._items.indexOf( item );
}
} else if ( typeof subject == 'number' ) {
index = subject;
item = this._items[ index ];
itemDoesNotExist = !item;

if ( item ) {
id = item[ idProperty ];
}
} else {
item = subject;
id = item[ idProperty ];
index = this._items.indexOf( item );
itemDoesNotExist = ( index == -1 || !this._itemMap.get( id ) );
}
const [ item, index ] = this._remove( subject );

if ( itemDoesNotExist ) {
/**
* Item not found.
*
* @error collection-remove-404
*/
throw new CKEditorError( 'collection-remove-404: Item not found.', this );
}

this._items.splice( index, 1 );
this._itemMap.delete( id );

const externalItem = this._bindToInternalToExternalMap.get( item );
this._bindToInternalToExternalMap.delete( item );
this._bindToExternalToInternalMap.delete( externalItem );

this.fire( 'remove', item, index );
this.fire( 'change', {
added: [],
removed: [ item ],
index
} );

return item;
}
Expand Down Expand Up @@ -363,16 +351,27 @@ export default class Collection {
/**
* Removes all items from the collection and destroys the binding created using
* {@link #bindTo}.
*
* @fires remove
* @fires change
*/
clear() {
if ( this._bindToCollection ) {
this.stopListening( this._bindToCollection );
this._bindToCollection = null;
}

const removedItems = Array.from( this._items );

while ( this.length ) {
this.remove( 0 );
this._remove( 0 );
}

this.fire( 'change', {
added: [],
removed: removedItems,
index: 0
} );
}

/**
Expand Down Expand Up @@ -664,6 +663,65 @@ export default class Collection {
return itemId;
}

/**
* Core {@link #remove} method implementation shared in other functions.
*
* In contrast this method **does not** fire the {@link #event:change} event.
*
* @private
* @param {Object} subject The item to remove, its id or index in the collection.
* @returns {Array} Returns an array with the removed item and its index.
* @fires remove
*/
_remove( subject ) {
let index, id, item;
let itemDoesNotExist = false;
const idProperty = this._idProperty;

if ( typeof subject == 'string' ) {
id = subject;
item = this._itemMap.get( id );
itemDoesNotExist = !item;

if ( item ) {
index = this._items.indexOf( item );
}
} else if ( typeof subject == 'number' ) {
index = subject;
item = this._items[ index ];
itemDoesNotExist = !item;

if ( item ) {
id = item[ idProperty ];
}
} else {
item = subject;
id = item[ idProperty ];
index = this._items.indexOf( item );
itemDoesNotExist = ( index == -1 || !this._itemMap.get( id ) );
}

if ( itemDoesNotExist ) {
/**
* Item not found.
*
* @error collection-remove-404
*/
throw new CKEditorError( 'collection-remove-404: Item not found.', this );
}

this._items.splice( index, 1 );
this._itemMap.delete( id );

const externalItem = this._bindToInternalToExternalMap.get( item );
this._bindToInternalToExternalMap.delete( item );
this._bindToExternalToInternalMap.delete( externalItem );

this.fire( 'remove', item, index );

return [ item, index ];
}

/**
* Iterable interface.
*
Expand All @@ -680,6 +738,15 @@ export default class Collection {
* @param {Object} item The added item.
*/

/**
* Fired when the collection was changed due to adding or removing items.
*
* @event change
* @param {Iterable.<Object>} added A list of added items.
* @param {Iterable.<Object>} removed A list of removed items.
* @param {Number} index An index where the addition or removal occurred.
*/

/**
* Fired when an item is removed from the collection.
*
Expand Down
Loading