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

chore(TS): Collection #8433

Merged
merged 23 commits into from
Nov 20, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- chore(TS): migrate Collection [#8433](https://github.com/fabricjs/fabric.js/pull/8433)
- ci(): Simplify filestats even more [#8449](https://github.com/fabricjs/fabric.js/pull/8449)
- chore(TS): migrate filter backends [#8403](https://github.com/fabricjs/fabric.js/pull/8403)
- chore(TS): migrate Text classes/mixins [#8408](https://github.com/fabricjs/fabric.js/pull/8408)
Expand Down
5 changes: 2 additions & 3 deletions src/mixins/canvas_serialization.mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,10 @@
* @param {Array} enlivenedObjects canvas objects
*/
__setupCanvas: function (serialized, enlivenedObjects) {
var _this = this;
enlivenedObjects.forEach(function (obj, index) {
enlivenedObjects.forEach((obj, index) => {
// we splice the array just in case some custom classes restored from JSON
// will add more object to canvas at canvas init.
_this.insertAt(obj, index);
this.insertAt(index, obj);
});
// remove parts i cannot set as options
delete serialized.objects;
Expand Down
164 changes: 77 additions & 87 deletions src/mixins/collection.mixin.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,67 @@
//@ts-nocheck
(function (global) {
var fabric = global.fabric;
/**
* @namespace fabric.Collection
*/
fabric.Collection = {
import { fabric } from '../../HEADER';
import type { FabricObject } from '../shapes/fabricObject.class';

export function createCollectionMixin(Klass: { new (...args: any[]): any }) {
return class Collection extends Klass {
/**
* @type {fabric.Object[]}
* @type {FabricObject[]}
*/
_objects: [],
_objects: FabricObject[] = [];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
_onObjectAdded(object: FabricObject) {
// subclasses should override this method
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
_onObjectRemoved(object: FabricObject) {
// subclasses should override this method
}

/**
* Adds objects to collection, Canvas or Group, then renders canvas
* (if `renderOnAddRemove` is not `false`).
* Objects should be instances of (or inherit from) fabric.Object
* @private
* @param {fabric.Object[]} objects to add
* @param {(object:fabric.Object) => any} [callback]
* Adds objects to collection
* Objects should be instances of (or inherit from) FabricObject
* @param {...FabricObject[]} objects to add
* @returns {number} new array length
*/
add: function (objects, callback) {
var size = this._objects.push.apply(this._objects, objects);
if (callback) {
for (var i = 0; i < objects.length; i++) {
callback.call(this, objects[i]);
}
}
add(...objects: FabricObject[]) {
const size = this._objects.push(...objects);
objects.forEach((object) => this._onObjectAdded(object));
return size;
},
}

/**
* Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
* An object should be an instance of (or inherit from) fabric.Object
* @private
* @param {fabric.Object|fabric.Object[]} objects Object(s) to insert
* @param {Number} index Index to insert object at
* @param {(object:fabric.Object) => any} [callback]
* Inserts an object into collection at specified index
* @param {number} index Index to insert object at
* @param {...FabricObject[]} objects Object(s) to insert
* @returns {number} new array length
*/
insertAt: function (objects, index, callback) {
var args = [index, 0].concat(objects);
this._objects.splice.apply(this._objects, args);
if (callback) {
for (var i = 2; i < args.length; i++) {
callback.call(this, args[i]);
}
}
insertAt(index: number, ...objects: FabricObject[]) {
this._objects.splice(index, 0, ...objects);
objects.forEach((object) => this._onObjectAdded(object));
return this._objects.length;
},
}

/**
* Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
* @private
* @param {fabric.Object[]} objectsToRemove objects to remove
* @param {(object:fabric.Object) => any} [callback] function to call for each object removed
* @returns {fabric.Object[]} removed objects
* @param {...FabricObject[]} objects objects to remove
* @returns {FabricObject[]} removed objects
*/
remove: function (objectsToRemove, callback) {
var objects = this._objects,
removed = [];
for (var i = 0, object, index; i < objectsToRemove.length; i++) {
object = objectsToRemove[i];
index = objects.indexOf(object);
remove(...objects: FabricObject[]) {
const array = this._objects,
removed: FabricObject[] = [];
objects.forEach((object) => {
const index = array.indexOf(object);
// only call onObjectRemoved if an object was actually removed
if (index !== -1) {
objects.splice(index, 1);
array.splice(index, 1);
removed.push(object);
callback && callback.call(this, object);
this._onObjectRemoved(object);
}
}
});
return removed;
},
}

/**
* Executes given function for each object in this group
Expand All @@ -81,87 +72,86 @@
* when no `context` argument is given
*
* @param {Object} context Context (aka thisObject)
* @return {Self} thisArg
* @chainable
*/
forEachObject: function (callback, context) {
var objects = this.getObjects();
for (var i = 0; i < objects.length; i++) {
callback.call(context, objects[i], i, objects);
}
return this;
},
forEachObject<T>(
callback: (
this: T | undefined,
object: FabricObject,
index: number,
array: FabricObject[]
) => any,
context?: T
) {
this.getObjects().forEach(callback.bind(context));
ShaMan123 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Returns an array of children objects of this instance
* @param {...String} [types] When specified, only objects of these types are returned
* @return {Array}
*/
getObjects: function () {
if (arguments.length === 0) {
return this._objects.concat();
getObjects(...types: string[]) {
if (types.length === 0) {
return [...this._objects];
}
var types = Array.from(arguments);
return this._objects.filter(function (o) {
return types.indexOf(o.type) > -1;
});
},
return this._objects.filter((o) => types.includes(o.type));
}

/**
* Returns object at specified index
* @param {Number} index
* @return {Object} object at index
*/
item: function (index) {
item(index: number) {
return this._objects[index];
},
}

/**
* Returns true if collection contains no objects
* @return {Boolean} true if collection is empty
*/
isEmpty: function () {
isEmpty() {
return this._objects.length === 0;
},
}

/**
* Returns a size of a collection (i.e: length of an array containing its objects)
* @return {Number} Collection size
*/
size: function () {
size() {
return this._objects.length;
},
}

/**
* Returns true if collection contains an object.\
* **Prefer using {@link `fabric.Object#isDescendantOf`} for performance reasons**
* **Prefer using {@link `FabricObject#isDescendantOf`} for performance reasons**
* instead of a.contains(b) use b.isDescendantOf(a)
* @param {Object} object Object to check against
* @param {Boolean} [deep=false] `true` to check all descendants, `false` to check only `_objects`
* @return {Boolean} `true` if collection contains an object
*/
contains: function (object, deep) {
if (this._objects.indexOf(object) > -1) {
contains(object: FabricObject, deep?: boolean): boolean {
if (this._objects.includes(object)) {
return true;
} else if (deep) {
return this._objects.some(function (obj) {
return (
typeof obj.contains === 'function' && obj.contains(object, true)
);
});
return this._objects.some(
(obj) => obj instanceof Collection && obj.contains(object, true)
);
}
return false;
},
}

/**
* Returns number representation of a collection complexity
* @return {Number} complexity
*/
complexity: function () {
return this._objects.reduce(function (memo, current) {
complexity() {
return this._objects.reduce((memo, current) => {
memo += current.complexity ? current.complexity() : 0;
return memo;
}, 0);
},
}
};
})(typeof exports !== 'undefined' ? exports : window);
}

fabric.createCollectionMixin = createCollectionMixin;
2 changes: 1 addition & 1 deletion src/mixins/object_ancestry.mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { FabricObject } from '../shapes/fabricObject.class';
/** @lends FabricObject.prototype */ {
/**
* Checks if object is decendant of target
* Should be used instead of @link {fabric.Collection.contains} for performance reasons
* Should be used instead of @link {Group.contains} or @link {StaticCanvas.contains} for performance reasons
* @param {fabric.Object|fabric.StaticCanvas} target
* @returns {boolean}
*/
Expand Down
Loading