@quenty/observablecollection 6.0.0-canary.367.e9fdcbc.0 → 6.0.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 +117 -1
- package/README.md +1 -1
- package/package.json +15 -10
- package/src/Shared/FilteredObservableListView.lua +2 -2
- package/src/Shared/ObservableCountingMap.lua +5 -9
- package/src/Shared/ObservableList.lua +90 -99
- package/src/Shared/ObservableList.spec.lua +48 -0
- package/src/Shared/ObservableMap.lua +38 -40
- package/src/Shared/ObservableMapList.lua +303 -0
- package/src/Shared/ObservableMapList.spec.lua +24 -0
- package/src/Shared/ObservableMapSet.lua +58 -5
- package/src/Shared/ObservableSet.lua +8 -0
- package/src/Shared/ObservableSortedList.lua +163 -56
- package/src/Shared/ObservableSortedList.spec.lua +8 -1
- package/src/Shared/ObservableSortedList.story.lua +152 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,123 @@
|
|
|
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
|
-
# [6.0.0
|
|
6
|
+
# [6.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.26.0...@quenty/observablecollection@6.0.0) (2023-10-11)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Make observable sorted list stable and use binary search ([6ca9dc7](https://github.com/Quenty/NevermoreEngine/commit/6ca9dc74724e8446aceac432dca95a647c3ef0c2))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [5.26.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.25.0...@quenty/observablecollection@5.26.0) (2023-09-21)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# [5.25.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.24.0...@quenty/observablecollection@5.25.0) (2023-09-04)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* Add ObservableMapList.new() and fix unit tests ([84e16ed](https://github.com/Quenty/NevermoreEngine/commit/84e16ed86af9bbc1ad19486f0298a9f7f3484b3e))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# [5.24.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.23.0...@quenty/observablecollection@5.24.0) (2023-08-23)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Bug Fixes
|
|
40
|
+
|
|
41
|
+
* Handle items after connecting to events ([800364b](https://github.com/Quenty/NevermoreEngine/commit/800364b77ce0b7fc42ad141846265657876512f8))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### Features
|
|
45
|
+
|
|
46
|
+
* ObservableMap uses subscription table ([fa39e53](https://github.com/Quenty/NevermoreEngine/commit/fa39e539c809e77f7d9539442727c48505f98ea1))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# [5.23.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.22.0...@quenty/observablecollection@5.23.0) (2023-08-01)
|
|
53
|
+
|
|
54
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# [5.22.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.21.0...@quenty/observablecollection@5.22.0) (2023-07-28)
|
|
61
|
+
|
|
62
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# [5.21.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.20.0...@quenty/observablecollection@5.21.0) (2023-07-23)
|
|
69
|
+
|
|
70
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# [5.20.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.19.1...@quenty/observablecollection@5.20.0) (2023-07-15)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
### Features
|
|
80
|
+
|
|
81
|
+
* Add :Observe() API calls to a variety of systems and allow Blend to :Observe() stuff ([ca29c68](https://github.com/Quenty/NevermoreEngine/commit/ca29c68164dfdaf136e9168faf48f487bed26088))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## [5.19.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.19.0...@quenty/observablecollection@5.19.1) (2023-07-11)
|
|
88
|
+
|
|
89
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# [5.19.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.18.1...@quenty/observablecollection@5.19.0) (2023-07-10)
|
|
96
|
+
|
|
97
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## [5.18.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.18.0...@quenty/observablecollection@5.18.1) (2023-07-03)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### Bug Fixes
|
|
107
|
+
|
|
108
|
+
* Fix missing dependency in ObservableCollection ([f6e7834](https://github.com/Quenty/NevermoreEngine/commit/f6e7834c02142dbe6c941e7cf4226cfd723ed0b1))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# [5.18.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.17.0...@quenty/observablecollection@5.18.0) (2023-06-17)
|
|
115
|
+
|
|
116
|
+
**Note:** Version bump only for package @quenty/observablecollection
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# [5.17.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@5.16.0...@quenty/observablecollection@5.17.0) (2023-06-05)
|
|
7
123
|
|
|
8
124
|
**Note:** Version bump only for package @quenty/observablecollection
|
|
9
125
|
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/observablecollection",
|
|
3
|
-
"version": "6.0.0
|
|
3
|
+
"version": "6.0.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,22 @@
|
|
|
27
27
|
"Quenty"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@quenty/
|
|
31
|
-
"@quenty/
|
|
32
|
-
"@quenty/
|
|
33
|
-
"@quenty/
|
|
34
|
-
"@quenty/
|
|
35
|
-
"@quenty/
|
|
36
|
-
"@quenty/
|
|
37
|
-
"@quenty/
|
|
30
|
+
"@quenty/baseobject": "^7.0.0",
|
|
31
|
+
"@quenty/brio": "^9.0.0",
|
|
32
|
+
"@quenty/loader": "^7.0.0",
|
|
33
|
+
"@quenty/maid": "^2.6.0",
|
|
34
|
+
"@quenty/promise": "^7.0.0",
|
|
35
|
+
"@quenty/rx": "^8.0.0",
|
|
36
|
+
"@quenty/signal": "^3.0.0",
|
|
37
|
+
"@quenty/steputils": "^3.3.0",
|
|
38
|
+
"@quenty/symbol": "^2.2.0",
|
|
39
|
+
"@quenty/valueobject": "^8.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@quenty/blend": "^7.0.0"
|
|
38
43
|
},
|
|
39
44
|
"publishConfig": {
|
|
40
45
|
"access": "public"
|
|
41
46
|
},
|
|
42
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "fdeae46099587019ec5fc15317dc673aed379400"
|
|
43
48
|
}
|
|
@@ -25,10 +25,10 @@ function FilteredObservableListView.new(observableList, observeScoreCallback, co
|
|
|
25
25
|
self._baseList = assert(observableList, "No observableList")
|
|
26
26
|
self._observeScoreCallback = assert(observeScoreCallback, "No observeScoreCallback")
|
|
27
27
|
|
|
28
|
-
self._scoredList = ObservableSortedList.new(function(a, b)
|
|
28
|
+
self._scoredList = ObservableSortedList.new(false, function(a, b)
|
|
29
29
|
-- Preserve index when scoring does not
|
|
30
30
|
if a.score == b.score then
|
|
31
|
-
return a.index
|
|
31
|
+
return a.index - b.index
|
|
32
32
|
else
|
|
33
33
|
return self._compare(a.score, b.score)
|
|
34
34
|
end
|
|
@@ -25,8 +25,7 @@ function ObservableCountingMap.new()
|
|
|
25
25
|
self._maid = Maid.new()
|
|
26
26
|
self._map = {}
|
|
27
27
|
|
|
28
|
-
self._totalKeyCountValue = ValueObject.new(0, "number")
|
|
29
|
-
self._maid:GiveTask(self._totalKeyCountValue)
|
|
28
|
+
self._totalKeyCountValue = self._maid:Add(ValueObject.new(0, "number"))
|
|
30
29
|
|
|
31
30
|
--[=[
|
|
32
31
|
Fires when an key is added
|
|
@@ -34,8 +33,7 @@ function ObservableCountingMap.new()
|
|
|
34
33
|
@prop KeyAdded Signal<T>
|
|
35
34
|
@within ObservableCountingMap
|
|
36
35
|
]=]
|
|
37
|
-
self.KeyAdded = Signal.new()
|
|
38
|
-
self._maid:GiveTask(self.KeyAdded)
|
|
36
|
+
self.KeyAdded = self._maid:Add(Signal.new())
|
|
39
37
|
|
|
40
38
|
--[=[
|
|
41
39
|
Fires when an key is removed.
|
|
@@ -43,8 +41,7 @@ function ObservableCountingMap.new()
|
|
|
43
41
|
@prop KeyRemoved Signal<T>
|
|
44
42
|
@within ObservableCountingMap
|
|
45
43
|
]=]
|
|
46
|
-
self.KeyRemoved = Signal.new()
|
|
47
|
-
self._maid:GiveTask(self.KeyRemoved)
|
|
44
|
+
self.KeyRemoved = self._maid:Add(Signal.new())
|
|
48
45
|
|
|
49
46
|
--[=[
|
|
50
47
|
Fires when an item count changes
|
|
@@ -52,8 +49,7 @@ function ObservableCountingMap.new()
|
|
|
52
49
|
@prop KeyChanged Signal<T>
|
|
53
50
|
@within ObservableCountingMap
|
|
54
51
|
]=]
|
|
55
|
-
self.KeyChanged = Signal.new()
|
|
56
|
-
self._maid:GiveTask(self.KeyChanged)
|
|
52
|
+
self.KeyChanged = self._maid:Add(Signal.new())
|
|
57
53
|
|
|
58
54
|
--[=[
|
|
59
55
|
Fires when the total count changes.
|
|
@@ -311,7 +307,7 @@ end
|
|
|
311
307
|
Gets a list of all keys.
|
|
312
308
|
@return { T }
|
|
313
309
|
]=]
|
|
314
|
-
function ObservableCountingMap:
|
|
310
|
+
function ObservableCountingMap:GetKeyList()
|
|
315
311
|
local list = {}
|
|
316
312
|
for key, _ in pairs(self._map) do
|
|
317
313
|
table.insert(list, key)
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
local require = require(script.Parent.loader).load(script)
|
|
7
7
|
|
|
8
|
-
local Signal = require("Signal")
|
|
9
|
-
local Observable = require("Observable")
|
|
10
|
-
local Maid = require("Maid")
|
|
11
8
|
local Brio = require("Brio")
|
|
9
|
+
local Maid = require("Maid")
|
|
10
|
+
local Observable = require("Observable")
|
|
11
|
+
local ObservableSubscriptionTable = require("ObservableSubscriptionTable")
|
|
12
|
+
local Rx = require("Rx")
|
|
13
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
14
|
+
local Signal = require("Signal")
|
|
12
15
|
local Symbol = require("Symbol")
|
|
13
16
|
local ValueObject = require("ValueObject")
|
|
14
|
-
local Rx = require("Rx")
|
|
15
17
|
|
|
16
18
|
local ObservableList = {}
|
|
17
19
|
ObservableList.ClassName = "ObservableList"
|
|
@@ -30,10 +32,9 @@ function ObservableList.new()
|
|
|
30
32
|
self._contents = {} -- { [Symbol]: T }
|
|
31
33
|
self._indexes = {} -- { [Symbol]: number }
|
|
32
34
|
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
self._countValue = ValueObject.new(0, "number")
|
|
36
|
-
self._maid:GiveTask(self._countValue)
|
|
35
|
+
self._indexObservers = self._maid:Add(ObservableSubscriptionTable.new())
|
|
36
|
+
self._keyIndexObservables = self._maid:Add(ObservableSubscriptionTable.new())
|
|
37
|
+
self._countValue = self._maid:Add(ValueObject.new(0, "number"))
|
|
37
38
|
|
|
38
39
|
--[=[
|
|
39
40
|
Fires when an item is added
|
|
@@ -41,8 +42,7 @@ function ObservableList.new()
|
|
|
41
42
|
@prop ItemAdded Signal<T, number, Symbol>
|
|
42
43
|
@within ObservableList
|
|
43
44
|
]=]
|
|
44
|
-
self.ItemAdded = Signal.new()
|
|
45
|
-
self._maid:GiveTask(self.ItemAdded)
|
|
45
|
+
self.ItemAdded = self._maid:Add(Signal.new())
|
|
46
46
|
|
|
47
47
|
--[=[
|
|
48
48
|
Fires when an item is removed.
|
|
@@ -50,8 +50,7 @@ function ObservableList.new()
|
|
|
50
50
|
@prop ItemRemoved Signal<T, Symbol>
|
|
51
51
|
@within ObservableList
|
|
52
52
|
]=]
|
|
53
|
-
self.ItemRemoved = Signal.new()
|
|
54
|
-
self._maid:GiveTask(self.ItemRemoved)
|
|
53
|
+
self.ItemRemoved = self._maid:Add(Signal.new())
|
|
55
54
|
|
|
56
55
|
--[=[
|
|
57
56
|
Fires when the count changes.
|
|
@@ -103,15 +102,15 @@ function ObservableList:ObserveItemsBrio()
|
|
|
103
102
|
sub:Fire(brio)
|
|
104
103
|
end
|
|
105
104
|
|
|
106
|
-
for index, key in pairs(self._keyList) do
|
|
107
|
-
handleItem(self._contents[key], index, key)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
105
|
maid:GiveTask(self.ItemAdded:Connect(handleItem))
|
|
111
106
|
maid:GiveTask(self.ItemRemoved:Connect(function(_item, includeKey)
|
|
112
107
|
maid[includeKey] = nil
|
|
113
108
|
end))
|
|
114
109
|
|
|
110
|
+
for index, key in pairs(self._keyList) do
|
|
111
|
+
handleItem(self._contents[key], index, key)
|
|
112
|
+
end
|
|
113
|
+
|
|
115
114
|
self._maid[sub] = maid
|
|
116
115
|
maid:GiveTask(function()
|
|
117
116
|
self._maid[sub] = nil
|
|
@@ -134,12 +133,43 @@ function ObservableList:ObserveIndex(indexToObserve)
|
|
|
134
133
|
|
|
135
134
|
local key = self._keyList[indexToObserve]
|
|
136
135
|
if not key then
|
|
137
|
-
error(("No entry at index %q, cannot observe changes"
|
|
136
|
+
error(string.format("No entry at index %q, cannot observe changes", indexToObserve))
|
|
138
137
|
end
|
|
139
138
|
|
|
140
139
|
return self:ObserveIndexByKey(key)
|
|
141
140
|
end
|
|
142
141
|
|
|
142
|
+
--[=[
|
|
143
|
+
Observes the current value at a given index. This can be useful for observing
|
|
144
|
+
the first entry, or matching stuff up to a given slot.
|
|
145
|
+
|
|
146
|
+
@param indexToObserve number
|
|
147
|
+
@return Observable<T?>
|
|
148
|
+
]=]
|
|
149
|
+
function ObservableList:ObserveAtIndex(indexToObserve)
|
|
150
|
+
assert(type(indexToObserve) == "number", "Bad indexToObserve")
|
|
151
|
+
|
|
152
|
+
return self._indexObservers:Observe(indexToObserve, function(sub)
|
|
153
|
+
sub:Fire(self:Get(indexToObserve))
|
|
154
|
+
end)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
--[=[
|
|
158
|
+
Observes the current value at a given index. This can be useful for observing
|
|
159
|
+
the first entry, or matching stuff up to a given slot.
|
|
160
|
+
|
|
161
|
+
@param indexToObserve number
|
|
162
|
+
@return Observable<Brio<T>>
|
|
163
|
+
]=]
|
|
164
|
+
function ObservableList:ObserveAtIndexBrio(indexToObserve)
|
|
165
|
+
assert(type(indexToObserve) == "number", "Bad indexToObserve")
|
|
166
|
+
|
|
167
|
+
return self:ObserveAtIndex(indexToObserve):Pipe({
|
|
168
|
+
RxBrioUtils.toBrio();
|
|
169
|
+
RxBrioUtils.onlyLastBrioSurvives();
|
|
170
|
+
})
|
|
171
|
+
end
|
|
172
|
+
|
|
143
173
|
--[=[
|
|
144
174
|
Removes the first instance found in contents
|
|
145
175
|
|
|
@@ -176,35 +206,8 @@ end
|
|
|
176
206
|
function ObservableList:ObserveIndexByKey(key)
|
|
177
207
|
assert(type(key) == "userdata", "Bad key")
|
|
178
208
|
|
|
179
|
-
return
|
|
180
|
-
|
|
181
|
-
if not currentIndex then
|
|
182
|
-
sub:Complete()
|
|
183
|
-
return
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
local maid = Maid.new()
|
|
187
|
-
self._keyObservables[key] = self._keyObservables[key] or {}
|
|
188
|
-
table.insert(self._keyObservables[key], sub)
|
|
189
|
-
|
|
190
|
-
sub:Fire(currentIndex)
|
|
191
|
-
|
|
192
|
-
maid:GiveTask(function()
|
|
193
|
-
local list = self._keyObservables[key]
|
|
194
|
-
if not list then
|
|
195
|
-
return
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
local index = table.find(list, sub)
|
|
199
|
-
if index then
|
|
200
|
-
table.remove(list, index)
|
|
201
|
-
if #list == 0 then
|
|
202
|
-
self._keyObservables[key] = nil
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
end)
|
|
206
|
-
|
|
207
|
-
return maid
|
|
209
|
+
return self._keyIndexObservables:Observe(key, function(sub)
|
|
210
|
+
sub:Fire(self:GetIndexByKey(key))
|
|
208
211
|
end)
|
|
209
212
|
end
|
|
210
213
|
|
|
@@ -256,6 +259,8 @@ end
|
|
|
256
259
|
function ObservableList:Get(index)
|
|
257
260
|
assert(type(index) == "number", "Bad index")
|
|
258
261
|
|
|
262
|
+
index = self:_toPositiveIndex(index)
|
|
263
|
+
|
|
259
264
|
local key = self._keyList[index]
|
|
260
265
|
if not key then
|
|
261
266
|
return nil
|
|
@@ -289,17 +294,14 @@ function ObservableList:InsertAt(item, index)
|
|
|
289
294
|
self._indexes[nextKey] = i + 1
|
|
290
295
|
self._keyList[i + 1] = nextKey
|
|
291
296
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
newIndex = i + 1;
|
|
297
|
-
subs = subs;
|
|
298
|
-
})
|
|
299
|
-
end
|
|
297
|
+
table.insert(changed, {
|
|
298
|
+
key = nextKey;
|
|
299
|
+
newIndex = i + 1;
|
|
300
|
+
})
|
|
300
301
|
end
|
|
301
302
|
|
|
302
303
|
self._keyList[index] = key
|
|
304
|
+
local listLength = #self._keyList
|
|
303
305
|
|
|
304
306
|
-- Fire off count
|
|
305
307
|
self._countValue.Value = self._countValue.Value + 1
|
|
@@ -307,23 +309,17 @@ function ObservableList:InsertAt(item, index)
|
|
|
307
309
|
-- Fire off add
|
|
308
310
|
self.ItemAdded:Fire(item, index, key)
|
|
309
311
|
|
|
310
|
-
-- Fire off the index change on the value
|
|
311
|
-
do
|
|
312
|
-
local subs = self._keyObservables[key]
|
|
313
|
-
if subs then
|
|
314
|
-
table.insert(changed, {
|
|
315
|
-
key = key;
|
|
316
|
-
newIndex = index;
|
|
317
|
-
subs = subs;
|
|
318
|
-
})
|
|
319
|
-
end
|
|
320
|
-
end
|
|
321
312
|
|
|
313
|
+
-- Fire off the index change on the value
|
|
314
|
+
self._keyIndexObservables:Fire(key, index)
|
|
315
|
+
self._indexObservers:Fire(index, item)
|
|
316
|
+
self._indexObservers:Fire(self:_toNegativeIndex(listLength, index), item)
|
|
322
317
|
|
|
323
|
-
-- Fire off index change on each key list (if the data isn't stale)
|
|
324
318
|
for _, data in pairs(changed) do
|
|
325
319
|
if self._indexes[data.key] == data.newIndex then
|
|
326
|
-
self:
|
|
320
|
+
self._indexObservers:Fire(data.newIndex, self._contents[data.key])
|
|
321
|
+
self._indexObservers:Fire(self:_toNegativeIndex(listLength, index), self._contents[data.key])
|
|
322
|
+
self._keyIndexObservables:Fire(data.key, data.newIndex)
|
|
327
323
|
end
|
|
328
324
|
end
|
|
329
325
|
|
|
@@ -368,8 +364,6 @@ function ObservableList:RemoveByKey(key)
|
|
|
368
364
|
return nil
|
|
369
365
|
end
|
|
370
366
|
|
|
371
|
-
local observableSubs = self._keyObservables[key]
|
|
372
|
-
self._keyObservables[key] = nil
|
|
373
367
|
self._indexes[key] = nil
|
|
374
368
|
self._contents[key] = nil
|
|
375
369
|
|
|
@@ -382,16 +376,13 @@ function ObservableList:RemoveByKey(key)
|
|
|
382
376
|
self._indexes[nextKey] = i
|
|
383
377
|
self._keyList[i] = nextKey
|
|
384
378
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
newIndex = i;
|
|
390
|
-
subs = subs;
|
|
391
|
-
})
|
|
392
|
-
end
|
|
379
|
+
table.insert(changed, {
|
|
380
|
+
key = nextKey;
|
|
381
|
+
newIndex = i;
|
|
382
|
+
})
|
|
393
383
|
end
|
|
394
384
|
self._keyList[n] = nil
|
|
385
|
+
local listLength = #self._keyList
|
|
395
386
|
|
|
396
387
|
-- Fire off that count changed
|
|
397
388
|
self._countValue.Value = self._countValue.Value - 1
|
|
@@ -401,39 +392,25 @@ function ObservableList:RemoveByKey(key)
|
|
|
401
392
|
end
|
|
402
393
|
|
|
403
394
|
-- Fire off the index change on the value
|
|
404
|
-
|
|
405
|
-
|
|
395
|
+
self._keyIndexObservables:Complete(key)
|
|
396
|
+
self._indexObservers:Fire(listLength, nil)
|
|
397
|
+
|
|
398
|
+
if listLength == 0 then
|
|
399
|
+
self._indexObservers:Fire(-1, nil)
|
|
406
400
|
end
|
|
407
401
|
|
|
408
402
|
-- Fire off index change on each key list (if the data isn't stale)
|
|
409
403
|
for _, data in pairs(changed) do
|
|
410
404
|
if self._indexes[data.key] == data.newIndex then
|
|
411
|
-
self:
|
|
405
|
+
self._indexObservers:Fire(data.newIndex, self._contents[data.key])
|
|
406
|
+
self._indexObservers:Fire(self:_toNegativeIndex(listLength, index), self._contents[data.key])
|
|
407
|
+
self._keyIndexObservables:Fire(data.key, data.newIndex)
|
|
412
408
|
end
|
|
413
409
|
end
|
|
414
410
|
|
|
415
411
|
return item
|
|
416
412
|
end
|
|
417
413
|
|
|
418
|
-
function ObservableList:_fireSubs(list, index)
|
|
419
|
-
for _, sub in pairs(list) do
|
|
420
|
-
if sub:IsPending() then
|
|
421
|
-
task.spawn(function()
|
|
422
|
-
sub:Fire(index)
|
|
423
|
-
end)
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
function ObservableList:_completeSubs(list)
|
|
429
|
-
for _, sub in pairs(list) do
|
|
430
|
-
if sub:IsPending() then
|
|
431
|
-
sub:Fire(nil)
|
|
432
|
-
sub:Complete()
|
|
433
|
-
end
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
414
|
--[=[
|
|
438
415
|
Gets a list of all entries.
|
|
439
416
|
@return { T }
|
|
@@ -446,6 +423,20 @@ function ObservableList:GetList()
|
|
|
446
423
|
return list
|
|
447
424
|
end
|
|
448
425
|
|
|
426
|
+
function ObservableList:_toPositiveIndex(index)
|
|
427
|
+
if index > 0 then
|
|
428
|
+
return index
|
|
429
|
+
elseif index < 0 then
|
|
430
|
+
return #self._keyList + index + 1
|
|
431
|
+
else
|
|
432
|
+
error(string.format("[ObservableList._toPositiveIndex] - Bad index %d", index))
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
function ObservableList:_toNegativeIndex(listLength, index)
|
|
437
|
+
return -listLength + index - 1
|
|
438
|
+
end
|
|
439
|
+
|
|
449
440
|
--[=[
|
|
450
441
|
Cleans up the ObservableList and sets the metatable to nil.
|
|
451
442
|
]=]
|
|
@@ -23,6 +23,10 @@ return function()
|
|
|
23
23
|
expect(observableList:GetCount()).to.equal(1)
|
|
24
24
|
end)
|
|
25
25
|
|
|
26
|
+
it("should allow negative queries", function()
|
|
27
|
+
expect(observableList:Get(-1)).to.equal("a")
|
|
28
|
+
expect(observableList:Get(-2)).to.equal(nil)
|
|
29
|
+
end)
|
|
26
30
|
|
|
27
31
|
it("should allow false as a value", function()
|
|
28
32
|
expect(observableList:Get(2)).to.equal(nil)
|
|
@@ -30,6 +34,14 @@ return function()
|
|
|
30
34
|
expect(observableList:Get(2)).to.equal(false)
|
|
31
35
|
end)
|
|
32
36
|
|
|
37
|
+
it("should allow negative queries after false", function()
|
|
38
|
+
expect(observableList:Get(1)).to.equal("a")
|
|
39
|
+
expect(observableList:Get(2)).to.equal(false)
|
|
40
|
+
|
|
41
|
+
expect(observableList:Get(-1)).to.equal(false)
|
|
42
|
+
expect(observableList:Get(-2)).to.equal("a")
|
|
43
|
+
end)
|
|
44
|
+
|
|
33
45
|
it("should fire off events for a specific key", function()
|
|
34
46
|
local seen = {}
|
|
35
47
|
local sub = observableList:ObserveIndex(1):Subscribe(function(value)
|
|
@@ -61,6 +73,42 @@ return function()
|
|
|
61
73
|
expect(seen[4]:IsDead()).to.equal(true)
|
|
62
74
|
end)
|
|
63
75
|
|
|
76
|
+
it("it should be able to observe a specific key", function()
|
|
77
|
+
local seen = {}
|
|
78
|
+
local sub = observableList:ObserveAtIndex(1):Subscribe(function(value)
|
|
79
|
+
table.insert(seen, value)
|
|
80
|
+
end)
|
|
81
|
+
|
|
82
|
+
local originalList = observableList:GetList()
|
|
83
|
+
expect(originalList[1]).to.equal("c")
|
|
84
|
+
|
|
85
|
+
observableList:InsertAt("dragon", 1)
|
|
86
|
+
|
|
87
|
+
sub:Destroy()
|
|
88
|
+
|
|
89
|
+
expect(#seen).to.equal(2)
|
|
90
|
+
expect(seen[1]).to.equal("c")
|
|
91
|
+
expect(seen[2]).to.equal("dragon")
|
|
92
|
+
end)
|
|
93
|
+
|
|
94
|
+
it("it should be able to observe a specific negative key", function()
|
|
95
|
+
local seen = {}
|
|
96
|
+
local sub = observableList:ObserveAtIndex(-1):Subscribe(function(value)
|
|
97
|
+
table.insert(seen, value)
|
|
98
|
+
end)
|
|
99
|
+
|
|
100
|
+
local originalList = observableList:GetList()
|
|
101
|
+
expect(originalList[#originalList]).to.equal("a")
|
|
102
|
+
|
|
103
|
+
observableList:Add("fire")
|
|
104
|
+
|
|
105
|
+
sub:Destroy()
|
|
106
|
+
|
|
107
|
+
expect(#seen).to.equal(2)
|
|
108
|
+
expect(seen[1]).to.equal("a")
|
|
109
|
+
expect(seen[2]).to.equal("fire")
|
|
110
|
+
end)
|
|
111
|
+
|
|
64
112
|
it("should fire off events on removal", function()
|
|
65
113
|
local seen = {}
|
|
66
114
|
local sub = observableList:ObserveIndex(2):Subscribe(function(value)
|