Skip to content

Commit

Permalink
feat: backbone removal initial stage [dev] (#2386)
Browse files Browse the repository at this point in the history
  • Loading branch information
zbynekstara committed Nov 20, 2023
1 parent 014a53b commit 0fc2abf
Show file tree
Hide file tree
Showing 98 changed files with 5,523 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<p>
Collections are ordered sets of models. You can bind <code>"change"</code> events to be notified when any model in the collection has been
modified, and listen for <code>"add"</code> and <code>"remove"</code> events.
</p>

<p>
Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience. This allows you to
listen for changes to specific attributes in any model in a collection.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<pre class="docs-method-signature"><code>collection.add(models, [options])</code></pre>

<p>
Add a model (or an array of models) to the collection, firing an <code>"add"</code> event for each model, and an <code>"update"</code> event
afterwards. This is a variant of <a href="#mvc.Collection.prototype.set"><code>set()</code></a> with the same options and return value, but it
always adds and never removes. If you're adding models to the collection that are already in the collection, they'll be ignored, unless you
pass <code>{ merge: true }</code>, in which case their attributes will be merged into the corresponding models, firing any appropriate
<code>"change"</code> events.
</p>

<pre><code>const shapes = new mvc.Collection;

shapes.on('add', function(shape) {
console.log(shape.get('name'));
// A
// B
});

shapes.add([
{ name: 'A' },
{ name: 'B' }
]);
</code></pre>

<p>
Note that adding the same model (a model with the same <code>id</code>) to a collection more than once is a no-op.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.at(index)</code></pre>

<p>
Get a model from a collection, specified by index. Useful if your collection is sorted, and if your collection isn't sorted, <code>at</code>
will still retrieve models in insertion order. When passed a negative index, it will retrieve the model from the back of the collection.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="docs-method-signature"><code>collection.clone()</code></pre>

<p>
Returns a new instance of the collection with an identical list of models.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<pre class="docs-method-signature"><code>collection.comparator</code></pre>

<p>
By default there is no comparator for a collection. If you define a comparator, it will be used to sort the collection any time a model is
added. A comparator can be defined as a <a href="https://underscorejs.org/#sortBy" target="_blank"><code>sortBy</code></a> (pass a function
that takes a single argument), as a
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort" target="_blank"><code>sort</code></a>
(pass a comparator function that expects two arguments), or as a string indicating the attribute to sort by.
</p>

<p>
"sortBy" comparator functions take a model and return a numeric or string value by which the model should be ordered relative to others.
"sort" comparator functions take two models, and return <code>-1</code> if the first model should come before the second, <code>0</code> if
they are of the same rank and <code>1</code> if the first model should come after. Note that JointJS depends on the arity of your comparator
function to determine between the two styles, so be careful if your comparator function is bound.
</p>

<pre><code>const Shape = new mvc.Model;
const shapes = new mvc.Collection;

shapes.comparator = 'order';

shapes.add(new Shape({ order: 3, letter: "C" }));
shapes.add(new Shape({ order: 2, letter: "B" }));
shapes.add(new Shape({ order: 1, letter: "A" }));

console.log(shapes.pluck('letter')); // A, B, C
</code></pre>

<p>
Note: Collections with a comparator will not automatically re-sort if you later change model attributes, so you may wish to call
<code>sort</code> after changing model attributes that would affect the order.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>mvc.Collection.extend(properties, [classProperties])</code></pre>

<p>
To create a Collection class of your own, extend <code>mvc.Collection</code>. Provide instance properties, and optional classProperties
to be attached directly to the constructor function.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.get(id)</code></pre>

<p>
Get a model from a collection, specified by an <a href="#mvc.Model.prototype.id"><code>id</code></a>, a
<a href="#mvc.Model.prototype.cid"><code>cid</code></a>, or by passing in a model.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<p>
When creating a Collection, you may choose to pass in the initial array of models. The collection's
<a href="#mvc.Collection.prototype.comparator"><code>comparator</code></a> may be included as an option. Passing <code>false</code> as the
<code>comparator</code> option will prevent sorting. If you define an initialize function, it will be invoked when the collection is created.
Initialize is an empty function by default. Override it with your own initialization logic.
</p>

<p>
There are a couple of options that, if provided, are attached to the collection directly: <code>model</code> and <code>comparator</code>.
Pass <code>null</code> for models to create an empty Collection with <code>options</code>.
</p>

<pre><code>const shapes = new mvc.Collection(null, {
model: Shape
});
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="docs-method-signature"><code>collection.length</code></pre>

<p>
Like an array, a Collection maintains a <code>length</code> property, counting the number of models it contains.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<pre class="docs-method-signature"><code>collection.model([attrs], [options])</code></pre>

<p>
Override this property to specify the model class that the collection contains. If defined, you can pass raw attributes objects (and arrays)
and options to <a href="#mvc.Collection.prototype.add"><code>add()</code></a>, and
<a href="#mvc.Collection.prototype.reset"><code>reset()</code></a>, and the attributes will be converted into a model of the proper type using
the provided options, if any.
</p>

<pre><code>const Shapes = mvc.Collection.extend({
model: Shape
});
</code></pre>

<p>
A collection can also contain polymorphic models by overriding this property with a constructor that returns a model.
</p>

<pre><code>const Shapes = mvc.Collection.extend({

model: function(attrs, options) {
if (condition) {
return new ShapeA(attrs, options);
} else {
return new ShapeB(attrs, options);
}
}
});
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<pre class="docs-method-signature"><code>collection.modelId(attrs, idAttribute)</code></pre>

<p>
Override this method to return the value the collection will use to identify a model given its attributes. Useful for combining models from
multiple tables with different <a href=#mvc.Model.prototype.idAttribute""><code>idAttribute</code></a> values into a single collection.
</p>

<p>
By default returns the value of the given <code>idAttribute</code> within the <code>attrs</code>, or failing that, <code>id</code>. If your
collection uses a <a href="#mvc.Collection.prototype.model">model factory</a> and the id ranges of those models might collide, you must override
this method.
</p>

<pre><code>const Shapes = mvc.Collection.extend({
modelId: function(attrs) {
return attrs.type + attrs.id;
}
});

const shapes = new Shapes([
{ type: 'a', id: 1 },
{ type: 'b', id: 1 }
]);

console.log(shapes.get('a1').id); // 1
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.models</code></pre>

<p>
Raw access to the JavaScript array of models inside of the collection. Usually you'll want to use <code>get()</code> or <code>at()</code> to
access model objects, but occasionally a direct reference to the array is desired.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<pre class="docs-method-signature"><code>collection.pluck(attribute)</code></pre>

<p>
Pluck an attribute from each model in the collection. Equivalent to calling
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" target="_blank"><code>map</code></a> and
returning a single attribute from the iterator.
</p>

<pre><code>const shapes = new mvc.Collection([
{name: 'A'},
{name: 'B'},
{name: 'C'}
]);

const names = shapes.pluck('name');
console.log(names); // ['A', 'B', 'C']
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.pop([options])</code></pre>

<p>
Remove and return the last model from a collection. Takes the same options as
<a href="#mvc.Collection.prototype.remove"><code>remove()</code></a>.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<p>
For use with collections as ES classes. If you define a <code>preinitialize</code> method, it will be invoked when the Collection is first
created and before any instantiation logic is run for the Collection.
</p>

<pre><code>class Shapes extends mvc.Collection {
preinitialize() {
this.on('add', function() {
console.log('Add model event got fired!');
});
}
}
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="docs-method-signature"><code>collection.push(model, [options])</code></pre>

<p>
Like <a href="#mvc.Collection.prototype.add"><code>add()</code></a>, but always adds a model at the end of the collection and never sorts.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<pre class="docs-method-signature"><code>collection.remove(models, [options])</code></pre>

<p>
Remove a model (or an array of models) from the collection, and return them. Each model can be a Model instance, an <code>id</code> string or a
JS object, any value acceptable as the <code>id</code> argument of <a href="#mvc.Collection.prototype.get"><code>collection.get</code></a>.
Fires a <code>"remove"</code> event for each model, and a single <code>"update"</code> event afterwards, unless <code>{ silent: true }</code>
is passed. The model's index before removal is available to listeners as <code>options.index</code>.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<pre class="docs-method-signature"><code>collection.reset([models], [options])</code></pre>

<p>
Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single <code>"reset"</code> event on completion,
and without triggering any <code>"add"</code> or <code>"remove"</code> events on any models. Returns the newly-set models. For convenience,
within a <code>"reset"</code> event, the list of any previous models is available as <code>options.previousModels</code>.
Pass <code>null</code> for <code>models</code> to empty your Collection with <code>options</code>.
</p>

<p>
Calling <code>collection.reset()</code> without passing any models as arguments will empty the entire collection.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<pre class="docs-method-signature"><code>collection.set(models, [options])</code></pre>

<p>
The set method performs a "smart" update of the collection with the passed list of models. If a model in the list isn't yet in the collection
it will be added; if the model is already in the collection its attributes will be merged; and if the collection contains any models that aren't
present in the list, they'll be removed. All of the appropriate <code>"add"</code>, <code>"remove"</code>, and <code>"change"</code> events are
fired as this happens, with a single <code>"update"</code> event at the end. Returns the touched models in the collection. If you'd like to
customize this behavior, you can change it with options: <code>{ add: false }</code>, <code>{ remove: false }</code>, or
<code>{ merge: false }</code>.
</p>

<p>
If a <a href="#mvc.Collection.prototype.model"><code>model</code></a> property is defined, you may also pass raw attributes objects and options,
and have them be vivified as instances of the model using the provided options. If you set a
<a href="#mvc.Collection.prototype.comparator"><code>comparator</code></a>, the collection will automatically sort itself and trigger a
<code>"sort"</code> event, unless you pass <code>{ sort: false }</code> or use the <code>{ at: index }</code> option. Pass
<code>{ at: index }</code> to splice the model(s) into the collection at the specified <code>index</code>.
</p>

<pre><code>const players = new mvc.Collection([ carlsen, nakamura, caruana, liren ]);

players.set([ carlsen, nakamura, caruana, firouzja ]);

// Fires a "remove" event for "liren", and an "add" event for "firouzja".
// Updates any of "caruana", "nakamura", and carlsen's attributes that may have changed.
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.shift([options])</code></pre>

<p>
Remove and return the first model from a collection. Takes the same options as
<a href="#mvc.Collection.prototype.remove"><code>remove()</code></a>.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.slice(begin, end)</code></pre>

<p>
Return a shallow copy of this collection's models, using the same options as native
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice" target="_blank">Array.prototype.slice</a>.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<pre class="docs-method-signature"><code>collection.sort([options])</code></pre>

<p>
Force a collection to re-sort itself. Note that a collection with a <a href="#mvc.Collection.prototype.comparator"><code>comparator</code></a>
will sort itself automatically whenever a model is added. To disable sorting when adding a model, pass <code>{ sort: false }</code> to
<code>add()</code>. Calling sort triggers a <code>"sort"</code> event on the collection.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<pre class="docs-method-signature"><code>collection.toJSON([options])</code></pre>

<p>
Return an array containing the attributes hash of each model (via <a href="#mvc.Model.prototype.toJSON"><code>toJSON</code></a>) in the
collection. This can be used to serialize and persist the collection as a whole.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="docs-method-signature"><code>collection.unshift(model, [options])</code></pre>

<p>
Like <a href="#mvc.Collection.prototype.add"><code>add()</code></a>, but always adds a model at the beginning of the collection and never sorts.
</p>
24 changes: 24 additions & 0 deletions packages/joint-core/docs/src/joint/api/mvc/Events/events.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<p>
Here's the complete list of built-in JointJS events, with arguments. You're also free to trigger your own events on Models, Collections and
Views as you see fit.
</p>

<ul>
<li><code>"add" (model, collection, options)</code> - when a model is added to a collection.</li>
<li><code>"remove" (model, collection, options)</code> - when a model is removed from a collection.</li>
<li><code>"update" (collection, options)</code> - single event triggered after any number of models have been added, removed or changed in a collection.</li>
<li><code>"reset" (collection, options)</code> - when the collection's entire contents have been reset.</li>
<li><code>"sort" (collection, options)</code> - when the collection has been re-sorted.</li>
<li><code>"change" (model, options)</code> - when a model's attributes have changed.</li>
<li><code>"changeId" (model, previousId, options)</code> - when the model's id has been updated.</li>
<li><code>"change:[attribute]" (model, value, options)</code> - when a specific attribute has been updated.</li>
<li><code>"invalid" (model, error, options)</code> - when a model's validation fails.</li>
<li><code>"all"</code> - this special event fires for any triggered event, passing the event name as the first argument followed by all trigger arguments.</li>
</ul>

<p>
Generally speaking, when calling a function that emits an event (<code>model.set</code>, <code>collection.add</code>, and so on...), if you'd
like to prevent the event from being triggered, you may pass <code>{silent: true}</code> as an option. Note that this is rarely, perhaps even
never, a good idea. Passing through a specific flag in the options for your event callback to look at, and choose to ignore,
will usually work out better.
</p>
11 changes: 11 additions & 0 deletions packages/joint-core/docs/src/joint/api/mvc/Events/intro.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<p>
A module that can be mixed in to any object in order to provide it with a custom event channel. You may bind a callback to an event with
<code>on</code> or remove with <code>off</code>; <code>trigger</code>-ing an event fires all callbacks in succession. Events do not have to be
declared before they are bound, and may take passed arguments.
</p>

<pre><code>const object = {};
joint.util.assign(object, joint.mvc.Events);
object.on('expand', function(msg){ alert('expanded' + msg); });
object.trigger('expand', 'the example');
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<pre class="docs-method-signature"><code>object.listenTo(other, event, callback)</code></pre>

<p>
Tell an object to listen to a particular event on an 'other' object. <code>listenTo</code> allows the object to keep track of the events, and
they can be removed all at once later on. The callback will always be called with object as context.
</p>

<pre><code>view.listenTo(model, 'change', view.render);
</code></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="docs-method-signature"><code>object.listenToOnce(other, event, callback)</code></pre>

<p>
Just like <a href="#mvc.Events.listenTo"><code>listenTo</code></a>, but causes the bound callback to fire only once before being removed.
</p>
28 changes: 28 additions & 0 deletions packages/joint-core/docs/src/joint/api/mvc/Events/off.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<pre class="docs-method-signature"><code>object.off([event], [callback], [context])</code></pre>

<p>
Remove a previously-bound callback function from an object. If no context is specified, all of the versions of the callback with
different contexts will be removed. If no callback is specified, all callbacks for the event will be removed. If no event is
specified, callbacks for all events will be removed.
</p>

<pre><code>// Removes just the `onChange` callback.
object.off('change', onChange);

// Removes all "change" callbacks.
object.off('change');

// Removes the `onChange` callback for all events.
object.off(null, onChange);

// Removes all callbacks for `context` for all events.
object.off(null, null, context);

// Removes all callbacks on `model`(including internal JointJS events).
model.off();
</code></pre>

<p>
Note that calling <code>model.off()</code>, for example, will indeed remove all events on the model — including events that JointJS uses for
internal bookkeeping.
</p>
Loading

0 comments on commit 0fc2abf

Please sign in to comment.