@quenty/observablecollection 5.14.0 → 5.15.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.15.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.14.0...@quenty/observablecollection@5.15.0) (2023-05-08)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Fix removal firing events to fire correctly ([10f98c3](https://github.com/Quenty/NevermoreEngine/commit/10f98c329e7bab203ffc056d1ffb0c2c715392a8))
12
+
13
+
14
+ ### Features
15
+
16
+ * Add ObservableSet:ObserveContains(item) ([df5beba](https://github.com/Quenty/NevermoreEngine/commit/df5beba6b196933653365719c26e6ed9fa21d4cc))
17
+ * Add ObservableSortedList:ObserveAtIndex(indexToObserve) and ObservableSortedList:FindFirstKey(content) ([4d9f4d8](https://github.com/Quenty/NevermoreEngine/commit/4d9f4d8d34eafa0899157e4960ea87c9c812cf48))
18
+
19
+
20
+
21
+
22
+
6
23
  # [5.14.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.13.0...@quenty/observablecollection@5.14.0) (2023-04-20)
7
24
 
8
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/observablecollection",
3
- "version": "5.14.0",
3
+ "version": "5.15.0",
4
4
  "description": "A set of observable collections, such as sets, maps, sorted lists, and more.",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,17 +27,17 @@
27
27
  "Quenty"
28
28
  ],
29
29
  "dependencies": {
30
- "@quenty/brio": "^8.11.0",
30
+ "@quenty/brio": "^8.12.0",
31
31
  "@quenty/loader": "^6.2.1",
32
32
  "@quenty/maid": "^2.5.0",
33
33
  "@quenty/promise": "^6.5.0",
34
34
  "@quenty/rx": "^7.10.0",
35
35
  "@quenty/signal": "^2.3.0",
36
36
  "@quenty/symbol": "^2.2.0",
37
- "@quenty/valuebaseutils": "^7.12.0"
37
+ "@quenty/valuebaseutils": "^7.13.0"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "15d1274fffdaef706f849fd5dca2f14364c1264e"
42
+ "gitHead": "2ad8cea7dd3ad79a39afd7d7b785b489b90553fd"
43
43
  }
@@ -214,7 +214,7 @@ function ObservableCountingMap:Set(key, amount)
214
214
  end
215
215
 
216
216
  if current < amount then
217
- self:Remove(amount - current)
217
+ self:Add(-(amount - current))
218
218
  return
219
219
  elseif current == amount then
220
220
  return
@@ -237,29 +237,50 @@ function ObservableCountingMap:Add(key, amount)
237
237
 
238
238
  if amount == 0 then
239
239
  return
240
- elseif amount < 0 then
241
- self:Remove(key, -amount)
242
- return
243
240
  end
244
241
 
245
- if not self._map[key] then
242
+ if self._map[key] then
243
+ local newValue = self._map[key] + amount
244
+ if newValue == 0 then
245
+ -- Remove item
246
+ self._map[key] = nil
247
+
248
+ -- Fire events
249
+ self._totalKeyCountValue.Value = self._totalKeyCountValue.Value - 1
250
+
251
+ if self.Destroy then
252
+ self.KeyRemoved:Fire(key)
253
+ end
254
+
255
+ if self.Destroy then
256
+ self.KeyChanged:Fire(key, 0)
257
+ end
258
+ else
259
+ -- Update item
260
+ self._map[key] = newValue
261
+ self.KeyChanged:Fire(key, newValue)
262
+ end
263
+ else
264
+ -- Add item
246
265
  self._map[key] = amount
266
+
267
+ -- Fire events
247
268
  self._totalKeyCountValue.Value = self._totalKeyCountValue.Value + 1
248
269
 
249
270
  if self.Destroy then
250
271
  self.KeyAdded:Fire(key)
251
272
  end
252
- else
253
- local newValue = self._map[key] + amount
254
- self._map[key] = newValue
255
- self.KeyChanged:Fire(key, newValue)
273
+
274
+ if self.Destroy then
275
+ self.KeyChanged:Fire(key, amount)
276
+ end
256
277
  end
257
278
 
258
279
  local removed = false
259
280
  return function()
260
281
  if self.Destroy and not removed then
261
282
  removed = true
262
- self:RemoveCount(key, amount)
283
+ self:Add(key, -amount)
263
284
  end
264
285
  end
265
286
  end
@@ -270,40 +291,12 @@ end
270
291
  @param amount number?
271
292
  @return callback
272
293
  ]=]
273
- function ObservableCountingMap:RemoveCount(key, amount)
294
+ function ObservableCountingMap:Remove(key, amount)
274
295
  assert(key ~= nil, "Bad key")
275
296
  assert(type(amount) == "number" or amount == nil, "Bad amount")
276
297
  amount = amount or 1
277
298
 
278
- if amount == 0 then
279
- return
280
- elseif amount < 0 then
281
- self:Add(key, -amount)
282
- return
283
- end
284
-
285
- local current = self._map[key]
286
- if not current then
287
- return
288
- end
289
-
290
- local newValue = current - amount
291
- if newValue > 0 then
292
- self._map[key] = newValue
293
- self.KeyChanged:Fire(key, newValue)
294
- else
295
- self._map[key] = nil
296
-
297
- self._totalKeyCountValue.Value = self._totalKeyCountValue.Value - 1
298
-
299
- if self.Destroy then
300
- self.KeyRemoved:Fire(key)
301
- end
302
-
303
- if self.Destroy then
304
- self.KeyChanged:Fire(key, 0)
305
- end
306
- end
299
+ self:Add(key, -amount)
307
300
  end
308
301
 
309
302
  --[=[
@@ -10,6 +10,7 @@ local Observable = require("Observable")
10
10
  local Maid = require("Maid")
11
11
  local Brio = require("Brio")
12
12
  local RxValueBaseUtils = require("RxValueBaseUtils")
13
+ local ObservableSubscriptionTable = require("ObservableSubscriptionTable")
13
14
 
14
15
  local ObservableSet = {}
15
16
  ObservableSet.ClassName = "ObservableSet"
@@ -25,6 +26,9 @@ function ObservableSet.new()
25
26
  self._maid = Maid.new()
26
27
  self._set = {}
27
28
 
29
+ self._containsObservables = ObservableSubscriptionTable.new()
30
+ self._maid:GiveTask(self._containsObservables)
31
+
28
32
  self._countValue = Instance.new("IntValue")
29
33
  self._countValue.Value = 0
30
34
  self._maid:GiveTask(self._countValue)
@@ -99,6 +103,39 @@ function ObservableSet:ObserveItemsBrio()
99
103
  end)
100
104
  end
101
105
 
106
+ --[=[
107
+ Observes the current value at a given index. This can be useful for observing
108
+ the first entry, or matching stuff up to a given slot.
109
+
110
+ @param item T
111
+ @return Observable<boolean>
112
+ ]=]
113
+ function ObservableSet:ObserveContains(item)
114
+ assert(item ~= nil, "Bad item")
115
+
116
+ return Observable.new(function(sub)
117
+ local maid = Maid.new()
118
+
119
+ if self._set[item] then
120
+ sub:Fire(true)
121
+ else
122
+ sub:Fire(false)
123
+ end
124
+
125
+ maid:GiveTask(self._containsObservables:Observe(item):Subscribe(function(doesContain)
126
+ sub:Fire(doesContain)
127
+ end))
128
+
129
+ self._maid[sub] = maid
130
+ maid:GiveTask(function()
131
+ self._maid[sub] = nil
132
+ sub:Complete()
133
+ end)
134
+
135
+ return maid
136
+ end)
137
+ end
138
+
102
139
  --[=[
103
140
  Returns whether the set contains the item
104
141
  @param item T
@@ -135,9 +172,12 @@ function ObservableSet:Add(item)
135
172
  assert(item ~= nil, "Bad item")
136
173
 
137
174
  if not self._set[item] then
138
- self._countValue.Value = self._countValue.Value + 1
139
175
  self._set[item] = true
176
+
177
+ -- Fire events
178
+ self._countValue.Value = self._countValue.Value + 1
140
179
  self.ItemAdded:Fire(item)
180
+ self._containsObservables:Fire(item, true)
141
181
  end
142
182
 
143
183
  return function()
@@ -155,12 +195,14 @@ function ObservableSet:Remove(item)
155
195
  assert(item ~= nil, "Bad item")
156
196
 
157
197
  if self._set[item] then
158
- self._countValue.Value = self._countValue.Value - 1
159
198
  self._set[item] = nil
160
199
 
200
+ -- Fire in reverse order
201
+ self._containsObservables:Fire(item, false)
161
202
  if self.Destroy then
162
203
  self.ItemRemoved:Fire(item)
163
204
  end
205
+ self._countValue.Value = self._countValue.Value - 1
164
206
  end
165
207
  end
166
208
 
@@ -20,10 +20,11 @@ local Maid = require("Maid")
20
20
  local Brio = require("Brio")
21
21
  local RxValueBaseUtils = require("RxValueBaseUtils")
22
22
  local Symbol = require("Symbol")
23
+ local ObservableSubscriptionTable = require("ObservableSubscriptionTable")
23
24
 
24
- -- Higher numbers last
25
+ -- Higher numbers last. Using <= ensures insertion at end on ties.
25
26
  local function defaultCompare(a, b)
26
- return a < b
27
+ return a <= b
27
28
  end
28
29
 
29
30
  local ObservableSortedList = {}
@@ -42,6 +43,12 @@ function ObservableSortedList.new(compare)
42
43
 
43
44
  self._keyList = {} -- { [number]: Symbol } -- immutable
44
45
 
46
+ self._indexObservers = ObservableSubscriptionTable.new()
47
+ self._maid:GiveTask(self._indexObservers)
48
+
49
+ self._contentIndexObservers = ObservableSubscriptionTable.new()
50
+ self._maid:GiveTask(self._contentIndexObservers)
51
+
45
52
  self._sortValue = {} -- { [Symbol]: number }
46
53
  self._contents = {} -- { [Symbol]: T }
47
54
  self._indexes = {} -- { [Symbol]: number }
@@ -123,6 +130,22 @@ function ObservableSortedList:ObserveItemsBrio()
123
130
  end)
124
131
  end
125
132
 
133
+ --[=[
134
+ Gets the first key for a given symbol
135
+
136
+ @param content T
137
+ @return Symbol
138
+ ]=]
139
+ function ObservableSortedList:FindFirstKey(content)
140
+ for key, item in pairs(self._contents) do
141
+ if item == content then
142
+ return key
143
+ end
144
+ end
145
+
146
+ return nil
147
+ end
148
+
126
149
  --[=[
127
150
  Observes the index as it changes, until the entry at the existing
128
151
  index is removed.
@@ -141,6 +164,34 @@ function ObservableSortedList:ObserveIndex(indexToObserve)
141
164
  return self:ObserveIndexByKey(key)
142
165
  end
143
166
 
167
+ --[=[
168
+ Observes the current value at a given index. This can be useful for observing
169
+ the first entry, or matching stuff up to a given slot.
170
+
171
+ @param indexToObserve number
172
+ @return Observable<T>
173
+ ]=]
174
+ function ObservableSortedList:ObserveAtIndex(indexToObserve)
175
+ assert(type(indexToObserve) == "number", "Bad indexToObserve")
176
+
177
+ return self._indexObservers:Observe(indexToObserve)
178
+ :Pipe({
179
+ function(source)
180
+ return Observable.new(function(sub)
181
+ local key = self._keyList[indexToObserve]
182
+
183
+ if key then
184
+ sub:Fire(self._contents[key]) -- Look up the content
185
+ else
186
+ sub:Fire(nil)
187
+ end
188
+
189
+ return source:Subscribe(sub:GetFireFailComplete())
190
+ end)
191
+ end
192
+ })
193
+ end
194
+
144
195
  --[=[
145
196
  Observes the index as it changes, until the entry at the existing
146
197
  key is removed.
@@ -410,6 +461,7 @@ function ObservableSortedList:_removeItemByKey(key, item)
410
461
  local itemRemoved = {
411
462
  key = key;
412
463
  item = item;
464
+ previousIndex = index;
413
465
  }
414
466
 
415
467
  -- TODO: Defer item removed as a changed event?
@@ -453,14 +505,26 @@ function ObservableSortedList:_queueDeferredChange()
453
505
 
454
506
  self._countValue.Value = self._countValue.Value + snapshot.countChange
455
507
 
456
- if self.Destroy then
457
- -- Fire off last adds
458
- for _, lastAdded in pairs(snapshot.itemsAdded) do
459
- self.ItemAdded:Fire(lastAdded.item, lastAdded.newIndex, lastAdded.key)
508
+ -- Fire off last adds
509
+ for _, lastAdded in pairs(snapshot.itemsAdded) do
510
+ if not self.ItemAdded.Destroy then
511
+ break
460
512
  end
513
+ self.ItemAdded:Fire(lastAdded.item, lastAdded.newIndex, lastAdded.key)
514
+
515
+ -- Item adds are included in indexChanges.
516
+ end
461
517
 
462
- for _, lastRemoved in pairs(snapshot.itemsRemoved) do
463
- self.ItemRemoved:Fire(lastRemoved.item, lastRemoved.key)
518
+ for _, lastRemoved in pairs(snapshot.itemsRemoved) do
519
+ if not self.ItemRemoved.Destroy then
520
+ break
521
+ end
522
+
523
+ self.ItemRemoved:Fire(lastRemoved.item, lastRemoved.key)
524
+
525
+ -- Fire only if we aren't handled by an index change.
526
+ if self._keyList[lastRemoved.previousIndex] == nil then
527
+ self._indexObservers:Fire(lastRemoved.previousIndex, nil)
464
528
  end
465
529
  end
466
530
 
@@ -471,6 +535,8 @@ function ObservableSortedList:_queueDeferredChange()
471
535
  if subs then
472
536
  self:_fireSubs(subs, lastChange.newIndex)
473
537
  end
538
+
539
+ self._indexObservers:Fire(lastChange.newIndex, self._contents[lastChange.key])
474
540
  end
475
541
  end
476
542
  end)