@mintjamsinc/ichigojs 0.1.83 → 0.1.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ichigo.cjs CHANGED
@@ -11319,6 +11319,25 @@
11319
11319
  * Previous iterations to detect changes
11320
11320
  */
11321
11321
  #previousIterations = [];
11322
+ /**
11323
+ * The source array watched for writes made directly to it, and the function
11324
+ * that stops watching it.
11325
+ *
11326
+ * A write such as `items[1] = x` is reported as a change of "items[1]", which
11327
+ * does not match this directive's dependency on "items", and the row's loop
11328
+ * variable holds the value it was rendered with. The directive therefore
11329
+ * watches the array itself and refreshes the rows that were written.
11330
+ */
11331
+ #watchedSource;
11332
+ #unwatchSource;
11333
+ /**
11334
+ * Positions written since the last refresh, and whether a write (such as one
11335
+ * to `length`) needs the whole list rendered again.
11336
+ */
11337
+ #writtenIndexes = new Set();
11338
+ #writtenWhole = false;
11339
+ #refreshScheduled = false;
11340
+ #destroyed = false;
11322
11341
  /**
11323
11342
  * @param context The context for parsing the directive.
11324
11343
  */
@@ -11435,6 +11454,10 @@
11435
11454
  * @inheritdoc
11436
11455
  */
11437
11456
  destroy() {
11457
+ this.#destroyed = true;
11458
+ this.#unwatchSource?.();
11459
+ this.#unwatchSource = undefined;
11460
+ this.#watchedSource = undefined;
11438
11461
  // Clean up all rendered items
11439
11462
  // First destroy all VNodes (calls @unmount hooks), then remove from DOM
11440
11463
  for (const { vNode } of this.#renderedItems) {
@@ -11466,6 +11489,10 @@
11466
11489
  }
11467
11490
  // Evaluate the source expression to get the data
11468
11491
  const sourceData = this.#sourceEvaluator.evaluate();
11492
+ // Every row is rendered from the source below, including those written in place
11493
+ this.#watchSource(sourceData);
11494
+ this.#writtenIndexes.clear();
11495
+ this.#writtenWhole = false;
11469
11496
  // Get iterations from the source data
11470
11497
  let iterations = this.#getIterations(sourceData);
11471
11498
  // If we don't have a key evaluator yet, try to create it
@@ -11859,6 +11886,89 @@
11859
11886
  }
11860
11887
  vNode.update();
11861
11888
  }
11889
+ /**
11890
+ * Watches the source for writes made directly to its elements. Only a
11891
+ * reactive array can be written that way; anything else is not watched.
11892
+ */
11893
+ #watchSource(source) {
11894
+ if (source === this.#watchedSource) {
11895
+ return;
11896
+ }
11897
+ this.#unwatchSource?.();
11898
+ this.#unwatchSource = undefined;
11899
+ this.#watchedSource = source;
11900
+ const path = Array.isArray(source) ? ReactiveProxy.getPath(source) : undefined;
11901
+ if (!path) {
11902
+ return;
11903
+ }
11904
+ this.#unwatchSource = ReactiveProxy.subscribe(source, (changedPath) => {
11905
+ this.#onSourceWritten(path, changedPath);
11906
+ });
11907
+ }
11908
+ /**
11909
+ * Notes a write made directly to the source array. A change below an
11910
+ * element (e.g. "items[1].name") is followed by the row's own bindings, and
11911
+ * a change of the whole array (e.g. by splice) by the usual dependency on it.
11912
+ */
11913
+ #onSourceWritten(path, changedPath) {
11914
+ if (!changedPath || !changedPath.startsWith(path + '[') || !changedPath.endsWith(']')) {
11915
+ return;
11916
+ }
11917
+ const property = changedPath.substring(path.length + 1, changedPath.length - 1);
11918
+ if (property.includes(']')) {
11919
+ return;
11920
+ }
11921
+ if (/^\d+$/.test(property)) {
11922
+ this.#writtenIndexes.add(Number(property));
11923
+ }
11924
+ else {
11925
+ // e.g. `items.length = 0`
11926
+ this.#writtenWhole = true;
11927
+ }
11928
+ // Writes made together are refreshed together, after the application's update
11929
+ if (!this.#refreshScheduled) {
11930
+ this.#refreshScheduled = true;
11931
+ queueMicrotask(() => {
11932
+ this.#refreshScheduled = false;
11933
+ this.#refreshWritten();
11934
+ });
11935
+ }
11936
+ }
11937
+ /**
11938
+ * Shows the elements written since the last refresh. A row whose position or
11939
+ * key no longer holds is not patched; the whole list is rendered again.
11940
+ */
11941
+ #refreshWritten() {
11942
+ if (this.#destroyed || (!this.#writtenWhole && this.#writtenIndexes.size === 0)) {
11943
+ return;
11944
+ }
11945
+ const indexes = [...this.#writtenIndexes];
11946
+ const whole = this.#writtenWhole;
11947
+ this.#writtenIndexes.clear();
11948
+ this.#writtenWhole = false;
11949
+ const source = this.#sourceEvaluator?.evaluate();
11950
+ if (whole || source !== this.#watchedSource || !Array.isArray(source) || source.length !== this.#renderedItems.length) {
11951
+ this.#render();
11952
+ return;
11953
+ }
11954
+ for (const index of indexes) {
11955
+ const rendered = this.#renderedItems[index];
11956
+ let context = { key: index, item: source[index], index };
11957
+ if (this.#evaluateKey && (this.#itemName || this.#itemDestructure)) {
11958
+ const itemBindings = new VBindings({
11959
+ parent: this.#vNode.bindings
11960
+ });
11961
+ this.#setItemBindings(itemBindings, context);
11962
+ context = { ...context, key: this.#evaluateKey(itemBindings) };
11963
+ }
11964
+ if (!rendered || context.key !== rendered.key) {
11965
+ this.#render();
11966
+ return;
11967
+ }
11968
+ this.#updateItemBindings(rendered.vNode, context);
11969
+ this.#previousIterations[index] = context;
11970
+ }
11971
+ }
11862
11972
  /**
11863
11973
  * Get iterations from various data types
11864
11974
  * Supports: Arrays, Objects, Sets, Maps, Iterables, Numbers, Strings
@@ -11868,7 +11978,8 @@
11868
11978
  return [];
11869
11979
  // Array
11870
11980
  if (Array.isArray(data)) {
11871
- return data.map((item, index) => ({
11981
+ // Array.from rather than map: a hole (e.g. after `items[5] = x` past the end) is a row of undefined
11982
+ return Array.from(data, (item, index) => ({
11872
11983
  item,
11873
11984
  index,
11874
11985
  key: index