@nemigo/svelte 2.10.1 → 2.10.3

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/tree.d.ts CHANGED
@@ -34,8 +34,14 @@ export declare abstract class SvelteTree<R extends TreeRecord, Item extends Tree
34
34
  items: ISvelteState<Item[]>;
35
35
  map: Map<string, Item>;
36
36
  size: ISvelteState<number>;
37
+ root_parent_id: string | null;
38
+ constructor(root_parent_id?: string | null);
37
39
  abstract toTreeItem(record: R): Item;
38
40
  abstract sort(a: Item, b: Item): number;
41
+ /** true, если запись относится к нашему поддереву (корень или потомок) */
42
+ __inScope(record: R): boolean;
43
+ /** является ли этот parent_id "корнем" нашего поддерева */
44
+ __isRootParent(parent_id: string | null): boolean;
39
45
  hooks: {
40
46
  create: (record: R) => void;
41
47
  update: (record: R, mods: any) => void;
package/dist/tree.js CHANGED
@@ -5,8 +5,27 @@ export class SvelteTree extends Emitter {
5
5
  items = createSvelteRawState([]);
6
6
  map = new Map();
7
7
  size = createSvelteRawState(0);
8
+ root_parent_id;
9
+ constructor(root_parent_id = null) {
10
+ super();
11
+ this.root_parent_id = root_parent_id;
12
+ }
13
+ /** true, если запись относится к нашему поддереву (корень или потомок) */
14
+ __inScope(record) {
15
+ // корневой уровень нашего поддерева
16
+ if (record.parent_id === this.root_parent_id)
17
+ return true;
18
+ // потомок — значит его родитель уже в map
19
+ return record.parent_id !== null && this.map.has(record.parent_id);
20
+ }
21
+ /** является ли этот parent_id "корнем" нашего поддерева */
22
+ __isRootParent(parent_id) {
23
+ return parent_id === this.root_parent_id;
24
+ }
8
25
  hooks = {
9
26
  create: (record) => {
27
+ if (!this.__inScope(record))
28
+ return;
10
29
  const item = this.toTreeItem(record);
11
30
  this.dispatch("before-create", item);
12
31
  this.map.set(record.id, item);
@@ -18,40 +37,33 @@ export class SvelteTree extends Emitter {
18
37
  },
19
38
  update: (record, mods) => {
20
39
  const item = this.map.get(record.id);
21
- if (!item)
40
+ const current = item?.record.signal;
41
+ const preview = { ...(current ?? record), ...mods };
42
+ if (!item) {
43
+ if (this.__inScope(preview))
44
+ this.hooks.create(preview);
45
+ return;
46
+ }
47
+ const prev_parent_id = current.parent_id;
48
+ const next_parent_id = preview.parent_id;
49
+ const isParentChange = prev_parent_id !== next_parent_id;
50
+ if (isParentChange && !this.__inScope(preview)) {
51
+ this.hooks.delete(record.id);
22
52
  return;
23
- const preview = { ...item.record.signal, ...mods };
24
- if (isDataEqual(record, preview))
53
+ }
54
+ if (!isParentChange && isDataEqual(current, preview))
25
55
  return;
26
- const prev_parent_id = record.parent_id;
27
- const mod_parent_id = mods.parent_id;
28
- const isParentChange = mod_parent_id !== undefined && mod_parent_id !== prev_parent_id;
29
56
  if (isParentChange) {
30
- this.dispatch("before-parent-update", {
31
- prev: prev_parent_id,
32
- item,
33
- preview,
34
- mods,
35
- });
36
- this.__parent(item, (items) => {
37
- this.__delete(items, item);
38
- });
57
+ this.dispatch("before-parent-update", { prev: prev_parent_id, item, preview, mods });
58
+ this.__parent(item, (items) => this.__delete(items, item));
39
59
  item.record.signal = preview;
40
- this.__parent(item, (items) => {
41
- this.__push(items, item);
42
- });
43
- this.dispatch("after-parent-update", {
44
- prev: prev_parent_id,
45
- item,
46
- mods,
47
- });
60
+ this.__parent(item, (items) => this.__push(items, item));
61
+ this.dispatch("after-parent-update", { prev: prev_parent_id, item, mods });
48
62
  }
49
63
  else {
50
64
  this.dispatch("before-update", item);
51
65
  item.record.signal = preview;
52
- this.__parent(item, (items) => {
53
- this.__sort(items);
54
- });
66
+ this.__parent(item, (items) => this.__sort(items));
55
67
  this.dispatch("after-update", item);
56
68
  }
57
69
  this.size.signal = this.map.size;
@@ -77,15 +89,15 @@ export class SvelteTree extends Emitter {
77
89
  },
78
90
  };
79
91
  __sort(items) {
80
- items.signal = items.signal.toSorted((a, b) => this.sort(a, b)); // Триггерем эффекты
92
+ items.signal = items.signal.toSorted((a, b) => this.sort(a, b));
81
93
  }
82
94
  __push(items, item) {
83
95
  items.signal.push(item);
84
- this.__sort(items); // Триггерем эффекты
96
+ this.__sort(items);
85
97
  }
86
98
  __splice(items, idx) {
87
99
  items.signal.splice(idx, 1);
88
- items.signal = items.signal.slice(); // Триггерем эффекты
100
+ items.signal = items.signal.slice();
89
101
  }
90
102
  __delete(items, item) {
91
103
  const idx = items.signal.indexOf(item);
@@ -94,14 +106,14 @@ export class SvelteTree extends Emitter {
94
106
  }
95
107
  __parent(item, call) {
96
108
  const parent_id = item.record.signal.parent_id;
97
- if (parent_id) {
109
+ if (this.__isRootParent(parent_id)) {
110
+ call(this.items);
111
+ }
112
+ else if (parent_id) {
98
113
  const parent = this.map.get(parent_id);
99
114
  if (parent)
100
115
  call(parent.child);
101
116
  }
102
- else {
103
- call(this.items);
104
- }
105
117
  }
106
118
  clear() {
107
119
  this.items.signal = [];
@@ -109,20 +121,42 @@ export class SvelteTree extends Emitter {
109
121
  this.size.signal = 0;
110
122
  }
111
123
  __build(records) {
112
- // Первый проход: создаём все элементы и строим Map
124
+ // 1. индексируем все записи, чтобы корректно определять принадлежность к поддереву
125
+ const byId = new Map();
113
126
  for (const r of records)
127
+ byId.set(r.id, r);
128
+ // определяем, входит ли запись в наше поддерево (поднимаемся до корня)
129
+ const inScope = (r) => {
130
+ let cur = r;
131
+ const seen = new Set();
132
+ while (cur) {
133
+ if (cur.parent_id === this.root_parent_id)
134
+ return true;
135
+ if (cur.parent_id === null)
136
+ return false;
137
+ if (seen.has(cur.id))
138
+ return false; // защита от циклов
139
+ seen.add(cur.id);
140
+ cur = byId.get(cur.parent_id);
141
+ }
142
+ return false;
143
+ };
144
+ const scoped = records.filter(inScope);
145
+ // 2. создаём элементы
146
+ for (const r of scoped)
114
147
  this.map.set(r.id, this.toTreeItem(r));
115
- // Второй проход: строим дерево
116
- for (const r of records) {
148
+ // 3. строим дерево
149
+ for (const r of scoped) {
117
150
  const item = this.map.get(r.id);
118
- const parent_id = r.parent_id;
119
- if (parent_id)
120
- this.map.get(parent_id)?.child.signal.push(item);
121
- else
151
+ if (this.__isRootParent(r.parent_id)) {
122
152
  this.items.signal.push(item);
153
+ }
154
+ else if (r.parent_id) {
155
+ this.map.get(r.parent_id)?.child.signal.push(item);
156
+ }
123
157
  }
124
- // Третий проход: всё сортируем
125
- for (const r of records) {
158
+ // 4. сортируем
159
+ for (const r of scoped) {
126
160
  const item = this.map.get(r.id);
127
161
  this.__sort(item.child);
128
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nemigo/svelte",
3
- "version": "2.10.1",
3
+ "version": "2.10.3",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
6
  "author": {