@quenty/blend 2.2.0 → 2.3.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,26 @@
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
+ # [2.3.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.2.1...@quenty/blend@2.3.0) (2022-01-17)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add a SpringObject to blend, which is a much heavier object than a spring, but simplifies API usage significantly ([cb59db0](https://github.com/Quenty/NevermoreEngine/commit/cb59db0ed4297ecec842b0820a485e1aa0c8ad70))
12
+ * Add Blend.Attached and fix children not being unsubscribed correctly (memory leak fix) ([f4fa4c2](https://github.com/Quenty/NevermoreEngine/commit/f4fa4c2ebce9be6e16a7ab1492afaf87fe81b8aa))
13
+
14
+
15
+
16
+
17
+
18
+ ## [2.2.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.2.0...@quenty/blend@2.2.1) (2022-01-16)
19
+
20
+ **Note:** Version bump only for package @quenty/blend
21
+
22
+
23
+
24
+
25
+
6
26
  # [2.2.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.1.1...@quenty/blend@2.2.0) (2022-01-07)
7
27
 
8
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/blend",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Declarative UI system.",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,23 +27,23 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@quenty/acceltween": "^2.0.1",
31
- "@quenty/brio": "^3.7.0",
32
- "@quenty/instanceutils": "^3.7.0",
33
- "@quenty/loader": "^3.3.0",
34
- "@quenty/maid": "^2.0.2",
35
- "@quenty/promise": "^3.5.0",
36
- "@quenty/rx": "^3.7.0",
30
+ "@quenty/acceltween": "^2.1.0",
31
+ "@quenty/brio": "^3.8.0",
32
+ "@quenty/instanceutils": "^3.8.0",
33
+ "@quenty/loader": "^3.4.0",
34
+ "@quenty/maid": "^2.1.0",
35
+ "@quenty/promise": "^3.6.0",
36
+ "@quenty/rx": "^3.8.0",
37
37
  "@quenty/signal": "^2.1.0",
38
- "@quenty/spring": "^3.2.0",
38
+ "@quenty/spring": "^3.3.0",
39
39
  "@quenty/steputils": "^3.0.1",
40
40
  "@quenty/string": "^2.2.1",
41
- "@quenty/valuebaseutils": "^3.7.0",
42
- "@quenty/valueobject": "^3.7.0"
41
+ "@quenty/valuebaseutils": "^3.8.0",
42
+ "@quenty/valueobject": "^3.8.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@quenty/contentproviderutils": "^3.5.0",
46
- "@quenty/playerthumbnailutils": "^3.6.0"
45
+ "@quenty/contentproviderutils": "^3.6.0",
46
+ "@quenty/playerthumbnailutils": "^3.7.0"
47
47
  },
48
- "gitHead": "5a3f3fb6c908fd3874f5ceacc70b404780275119"
48
+ "gitHead": "c094ba8f4e128cdff08919d89de226d3d65247ce"
49
49
  }
@@ -9,9 +9,11 @@ local AccelTween = require("AccelTween")
9
9
  local BlendDefaultProps = require("BlendDefaultProps")
10
10
  local Brio = require("Brio")
11
11
  local Maid = require("Maid")
12
+ local MaidTaskUtils = require("MaidTaskUtils")
12
13
  local Observable = require("Observable")
13
14
  local Promise = require("Promise")
14
15
  local Rx = require("Rx")
16
+ local BrioUtils = require("BrioUtils")
15
17
  local RxInstanceUtils = require("RxInstanceUtils")
16
18
  local RxValueBaseUtils = require("RxValueBaseUtils")
17
19
  local Signal = require("Signal")
@@ -21,8 +23,6 @@ local StepUtils = require("StepUtils")
21
23
  local ValueBaseUtils = require("ValueBaseUtils")
22
24
  local ValueObject = require("ValueObject")
23
25
  local ValueObjectUtils = require("ValueObjectUtils")
24
- local MaidTaskUtils = require("MaidTaskUtils")
25
- local RxBrioUtils = require("RxBrioUtils")
26
26
 
27
27
  local Blend = {}
28
28
 
@@ -114,9 +114,9 @@ end
114
114
  return verb .. " " .. name
115
115
  end)
116
116
 
117
- computed:Subscribe(function(sentence)
117
+ maid:GiveTask(computed:Subscribe(function(sentence)
118
118
  print(sentence)
119
- end) --> "hi alice"
119
+ end)) --> "hi alice"
120
120
 
121
121
  nameState.Value = "bob" --> "hi bob"
122
122
  verbState.Value = "bye" --> "bye bob"
@@ -212,6 +212,30 @@ function Blend.OnEvent(eventName)
212
212
  end
213
213
  end
214
214
 
215
+ --[=[
216
+ Uses the constructor to attach a class or resource to the actual object
217
+ for the lifetime of the subscription of that object.
218
+ @param constructor T
219
+ @return (parent: Instance) -> Observable<T>
220
+ ]=]
221
+ function Blend.Attached(constructor)
222
+ return function(parent)
223
+ return Observable.new(function(sub)
224
+ local maid = Maid.new()
225
+
226
+ local resource = constructor(parent)
227
+
228
+ if MaidTaskUtils.isValidTask(resource) then
229
+ maid:GiveTask(resource)
230
+ end
231
+
232
+ sub:Fire(resource)
233
+
234
+ return maid
235
+ end)
236
+ end;
237
+ end
238
+
215
239
  --[=[
216
240
  Similiar to Fusion's ComputedPairs, where the changes are cached, and the lifetime limited.
217
241
  @param source Observable<T> | any
@@ -427,7 +451,7 @@ end
427
451
  function Blend.toEventObservable(value)
428
452
  if Observable.isObservable(value) then
429
453
  return value
430
- elseif typeof(value) == "RBXScriptSignal" then
454
+ elseif typeof(value) == "RBXScriptSignal" or Signal.isSignal(value) then
431
455
  return Rx.fromSignal(value)
432
456
  else
433
457
  return nil
@@ -451,7 +475,11 @@ function Blend.toEventHandler(value)
451
475
  end
452
476
  end
453
477
  elseif type(value) == "table" then
454
- if value.ClassName == "ValueObject" then
478
+ if Signal.isSignal(value) then
479
+ return function(...)
480
+ value:Fire(...)
481
+ end
482
+ elseif value.ClassName == "ValueObject" then
455
483
  return function(result)
456
484
  value.Value = result
457
485
  end
@@ -520,6 +548,48 @@ function Blend.Children(parent, value)
520
548
  end
521
549
  end
522
550
 
551
+ --[=[
552
+ An event emitter that emits the instance that was actually created. This is
553
+ useful for a variety of things.
554
+
555
+ Using this to track an instance
556
+
557
+ ```lua
558
+ local currentCamera = Blend.State()
559
+
560
+ return Blend.New "ViewportFrame" {
561
+ CurrentCamera = currentCamera;
562
+ [Blend.Children] = {
563
+ self._current;
564
+ Blend.New "Camera" {
565
+ [Blend.Instance] = currentCamera;
566
+ };
567
+ };
568
+ };
569
+ ```
570
+
571
+ You can also use this to execute code against an instance.
572
+
573
+ ```lua
574
+ return Blend.New "Frame" {
575
+ [Blend.Instance] = function(frame)
576
+ print("We got a new frame!")
577
+ end;
578
+ };
579
+ ```
580
+
581
+ Note that if you subscribe twice to the resulting observable, the internal function
582
+ will execute twice.
583
+
584
+ @param parent Instance
585
+ @return Observable<Instance>
586
+ ]=]
587
+ function Blend.Instance(parent)
588
+ return Observable.new(function(sub)
589
+ sub:Fire(parent)
590
+ end)
591
+ end
592
+
523
593
  --[=[
524
594
  Ensures the computed version of a value is limited by lifetime instead
525
595
  of multiple. Used in conjunction with [Blend.Children] and [Blend.Computed].
@@ -532,7 +602,7 @@ end
532
602
  Blend.New "ScreenGui" {
533
603
  Parent = game.Players.LocalPlayer.PlayerGui;
534
604
  [Blend.Children] = {
535
- Blend.Single(percentVisible, Blend.Computed(function()
605
+ Blend.Single(Blend.Computed(percentVisible, function()
536
606
  -- you generally would not want to do this anyway because this reconstructs a new frame
537
607
  -- every frame.
538
608
 
@@ -550,7 +620,28 @@ end
550
620
  @return Observable<Brio<Instance>>
551
621
  @within Blend
552
622
  ]=]
553
- Blend.Single = RxBrioUtils.switchToBrio
623
+ function Blend.Single(observable)
624
+ return Observable.new(function(sub)
625
+ local maid = Maid.new()
626
+
627
+ maid:GiveTask(observable:Subscribe(function(result)
628
+ if Brio.isBrio(result) then
629
+ local copy = BrioUtils.clone(result)
630
+ maid._current = copy
631
+ sub:Fire(copy)
632
+ return copy
633
+ end
634
+
635
+ local current = Brio.new(result)
636
+ maid._current = current
637
+ sub:Fire(current)
638
+
639
+ return current
640
+ end))
641
+
642
+ return maid
643
+ end)
644
+ end
554
645
 
555
646
  --[=[
556
647
  Observes children and ensures that the value is cleaned up
@@ -676,7 +767,7 @@ function Blend._observeChildren(value)
676
767
  return Observable.new(function(sub)
677
768
  local maid = Maid.new()
678
769
 
679
- value:Subscribe(function(result)
770
+ maid:GiveTask(value:Subscribe(function(result)
680
771
  if typeof(result) == "Instance" then
681
772
  -- lifetime of subscription
682
773
  maid:GiveTask(result)
@@ -710,7 +801,7 @@ function Blend._observeChildren(value)
710
801
  sub:Fire(...)
711
802
  end, function()
712
803
  -- Drop completion, other inner components may have completed.
713
- end)
804
+ end))
714
805
 
715
806
  return maid
716
807
  end)
@@ -29,109 +29,103 @@ SOFTWARE.
29
29
 
30
30
  ]]
31
31
 
32
- local ENABLE_SENSIBLE_DEFAULTS = true
33
-
34
- if ENABLE_SENSIBLE_DEFAULTS then
35
- return {
36
- ScreenGui = {
37
- ResetOnSpawn = false,
38
- ZIndexBehavior = "Sibling"
39
- },
40
-
41
- BillboardGui = {
42
- ResetOnSpawn = false,
43
- ZIndexBehavior = "Sibling"
44
- },
45
-
46
- SurfaceGui = {
47
- ResetOnSpawn = false,
48
- ZIndexBehavior = "Sibling",
49
-
50
- SizingMode = "PixelsPerStud",
51
- PixelsPerStud = 50
52
- },
53
-
54
- Frame = {
55
- BackgroundColor3 = Color3.new(1, 1, 1),
56
- BorderColor3 = Color3.new(0, 0, 0),
57
- BorderSizePixel = 0
58
- },
59
-
60
- ScrollingFrame = {
61
- BackgroundColor3 = Color3.new(1, 1, 1),
62
- BorderColor3 = Color3.new(0, 0, 0),
63
- BorderSizePixel = 0,
64
-
65
- ScrollBarImageColor3 = Color3.new(0, 0, 0)
66
- },
67
-
68
- TextLabel = {
69
- BackgroundColor3 = Color3.new(1, 1, 1),
70
- BorderColor3 = Color3.new(0, 0, 0),
71
- BorderSizePixel = 0,
72
-
73
- Font = "SourceSans",
74
- Text = "",
75
- TextColor3 = Color3.new(0, 0, 0),
76
- TextSize = 14
77
- },
78
-
79
- TextButton = {
80
- BackgroundColor3 = Color3.new(1, 1, 1),
81
- BorderColor3 = Color3.new(0, 0, 0),
82
- BorderSizePixel = 0,
83
-
84
- AutoButtonColor = false,
85
-
86
- Font = "SourceSans",
87
- Text = "",
88
- TextColor3 = Color3.new(0, 0, 0),
89
- TextSize = 14
90
- },
91
-
92
- TextBox = {
93
- BackgroundColor3 = Color3.new(1, 1, 1),
94
- BorderColor3 = Color3.new(0, 0, 0),
95
- BorderSizePixel = 0,
96
-
97
- ClearTextOnFocus = false,
98
-
99
- Font = "SourceSans",
100
- Text = "",
101
- TextColor3 = Color3.new(0, 0, 0),
102
- TextSize = 14
103
- },
104
-
105
- ImageLabel = {
106
- BackgroundColor3 = Color3.new(1, 1, 1),
107
- BorderColor3 = Color3.new(0, 0, 0),
108
- BorderSizePixel = 0
109
- },
110
-
111
- ImageButton = {
112
- BackgroundColor3 = Color3.new(1, 1, 1),
113
- BorderColor3 = Color3.new(0, 0, 0),
114
- BorderSizePixel = 0,
115
-
116
- AutoButtonColor = false
117
- },
118
-
119
- ViewportFrame = {
120
- BackgroundColor3 = Color3.new(1, 1, 1),
121
- BorderColor3 = Color3.new(0, 0, 0),
122
- BorderSizePixel = 0
123
- },
124
-
125
- VideoFrame = {
126
- BackgroundColor3 = Color3.new(1, 1, 1),
127
- BorderColor3 = Color3.new(0, 0, 0),
128
- BorderSizePixel = 0
129
- },
130
-
131
- UIListLayout = {
132
- SortOrder = Enum.SortOrder.LayoutOrder;
133
- },
134
- }
135
- else
136
- return {}
137
- end
32
+ return {
33
+ ScreenGui = {
34
+ ResetOnSpawn = false,
35
+ ZIndexBehavior = "Sibling"
36
+ },
37
+
38
+ BillboardGui = {
39
+ ResetOnSpawn = false,
40
+ ZIndexBehavior = "Sibling"
41
+ },
42
+
43
+ SurfaceGui = {
44
+ ResetOnSpawn = false,
45
+ ZIndexBehavior = "Sibling",
46
+
47
+ SizingMode = "PixelsPerStud",
48
+ PixelsPerStud = 50
49
+ },
50
+
51
+ Frame = {
52
+ BackgroundColor3 = Color3.new(1, 1, 1),
53
+ BorderColor3 = Color3.new(0, 0, 0),
54
+ BorderSizePixel = 0
55
+ },
56
+
57
+ ScrollingFrame = {
58
+ BackgroundColor3 = Color3.new(1, 1, 1),
59
+ BorderColor3 = Color3.new(0, 0, 0),
60
+ BorderSizePixel = 0,
61
+
62
+ ScrollBarImageColor3 = Color3.new(0, 0, 0)
63
+ },
64
+
65
+ TextLabel = {
66
+ BackgroundColor3 = Color3.new(1, 1, 1),
67
+ BorderColor3 = Color3.new(0, 0, 0),
68
+ BorderSizePixel = 0,
69
+
70
+ Font = "SourceSans",
71
+ Text = "",
72
+ TextColor3 = Color3.new(0, 0, 0),
73
+ TextSize = 14
74
+ },
75
+
76
+ TextButton = {
77
+ BackgroundColor3 = Color3.new(1, 1, 1),
78
+ BorderColor3 = Color3.new(0, 0, 0),
79
+ BorderSizePixel = 0,
80
+
81
+ AutoButtonColor = false,
82
+
83
+ Font = "SourceSans",
84
+ Text = "",
85
+ TextColor3 = Color3.new(0, 0, 0),
86
+ TextSize = 14
87
+ },
88
+
89
+ TextBox = {
90
+ BackgroundColor3 = Color3.new(1, 1, 1),
91
+ BorderColor3 = Color3.new(0, 0, 0),
92
+ BorderSizePixel = 0,
93
+
94
+ ClearTextOnFocus = false,
95
+
96
+ Font = "SourceSans",
97
+ Text = "",
98
+ TextColor3 = Color3.new(0, 0, 0),
99
+ TextSize = 14
100
+ },
101
+
102
+ ImageLabel = {
103
+ BackgroundColor3 = Color3.new(1, 1, 1),
104
+ BorderColor3 = Color3.new(0, 0, 0),
105
+ BorderSizePixel = 0
106
+ },
107
+
108
+ ImageButton = {
109
+ BackgroundColor3 = Color3.new(1, 1, 1),
110
+ BorderColor3 = Color3.new(0, 0, 0),
111
+ BorderSizePixel = 0,
112
+
113
+ AutoButtonColor = false
114
+ },
115
+
116
+ ViewportFrame = {
117
+ BackgroundColor3 = Color3.new(1, 1, 1),
118
+ BorderColor3 = Color3.new(0, 0, 0),
119
+ BorderSizePixel = 0
120
+ },
121
+
122
+ VideoFrame = {
123
+ BackgroundColor3 = Color3.new(1, 1, 1),
124
+ BorderColor3 = Color3.new(0, 0, 0),
125
+ BorderSizePixel = 0
126
+ },
127
+
128
+ UIListLayout = {
129
+ SortOrder = Enum.SortOrder.LayoutOrder;
130
+ },
131
+ }
@@ -0,0 +1,227 @@
1
+ --[=[
2
+ @class SpringObject
3
+ ]=]
4
+
5
+ local require = require(script.Parent.loader).load(script)
6
+
7
+ local RunService= game:GetService("RunService")
8
+
9
+ local Spring = require("Spring")
10
+ local Maid = require("Maid")
11
+ local Signal = require("Signal")
12
+ local StepUtils = require("StepUtils")
13
+ local Observable = require("Observable")
14
+ local SpringUtils = require("SpringUtils")
15
+ local Blend = require("Blend")
16
+ local Rx = require("Rx")
17
+
18
+ local SpringObject = {}
19
+ SpringObject.ClassName = "SpringObject"
20
+ SpringObject.__index = SpringObject
21
+
22
+ --[=[
23
+ Constructs a new SpringObject.
24
+ @param target T
25
+ @param speed number | Observable<number> | ValueObject<number> | NumberValue | any
26
+ @param damper number | Observable<number> | NumberValue | any
27
+ @return Spring<T>
28
+ ]=]
29
+ function SpringObject.new(target, speed, damper)
30
+ local self = setmetatable({
31
+ _maid = Maid.new();
32
+ Changed = Signal.new();
33
+ }, SpringObject)
34
+
35
+ --[=[
36
+ Event fires when the spring value changes
37
+ @prop Changed Signal<()> -- Fires whenever the spring initially changes state
38
+ @within ValueObject
39
+ ]=]
40
+ self._maid:GiveTask(self.Changed)
41
+
42
+ if target then
43
+ self.Target = target
44
+ else
45
+ self:_getSpringForType(0)
46
+ end
47
+
48
+ if speed then
49
+ self.Speed = speed
50
+ end
51
+
52
+ if damper then
53
+ self.Damper = damper
54
+ end
55
+
56
+ return self
57
+ end
58
+
59
+ --[=[
60
+ Returns whether an object is a SpringObject.
61
+ @param value any
62
+ @return boolean
63
+ ]=]
64
+ function SpringObject.isSpringObject(value)
65
+ return type(value) == "table" and getmetatable(value) == SpringObject
66
+ end
67
+
68
+ --[=[
69
+ Observes the spring animating
70
+ @return Observable<T>
71
+ ]=]
72
+ function SpringObject:ObserveRenderStepped()
73
+ return self:ObserveOnSignal(RunService.RenderStepped)
74
+ end
75
+
76
+ --[=[
77
+ Observes the spring animating
78
+ @param signal RBXScriptSignal
79
+ @return Observable<T>
80
+ ]=]
81
+ function SpringObject:ObserveOnSignal(signal)
82
+ return Observable.new(function(sub)
83
+ local maid = Maid.new()
84
+
85
+ local startAnimate, stopAnimate = StepUtils.bindToSignal(signal, function()
86
+ local animating, position = SpringUtils.animating(self._currentSpring)
87
+ sub:Fire(SpringUtils.fromLinearIfNeeded(position))
88
+ return animating
89
+ end)
90
+
91
+ maid:GiveTask(stopAnimate)
92
+ maid:GiveTask(self.Changed:Connect(startAnimate))
93
+ startAnimate()
94
+
95
+ return maid
96
+ end)
97
+
98
+ end
99
+
100
+ --[=[
101
+ Impulses the spring, increasing velocity by the amount given. This is useful to make something shake,
102
+ like a Mac password box failing.
103
+
104
+ @param velocity T -- The velocity to impulse with
105
+ @return ()
106
+ ]=]
107
+ function SpringObject:Impulse(velocity)
108
+ self._currentSpring:Impulse(SpringUtils.toLinearIfNeeded(velocity))
109
+ self.Changed:Fire()
110
+ end
111
+
112
+ --[=[
113
+ Instantly skips the spring forwards by that amount time
114
+ @param delta number -- Time to skip forwards
115
+ @return ()
116
+ ]=]
117
+ function SpringObject:TimeSkip(delta)
118
+ assert(type(delta) == "number", "Bad delta")
119
+
120
+ self._currentSpring:TimeSkip(delta)
121
+ self.Changed:Fire()
122
+ end
123
+
124
+ function SpringObject:__index(index)
125
+ if index == "Value" or index == "Position" or index == "p" then
126
+ return SpringUtils.fromLinearIfNeeded(self._currentSpring.Value)
127
+ elseif index == "Velocity" or index == "v" then
128
+ return SpringUtils.fromLinearIfNeeded(self._currentSpring.Velocity)
129
+ elseif index == "Target" or index == "t" then
130
+ return SpringUtils.fromLinearIfNeeded(self._currentSpring.Target)
131
+ elseif index == "Damper" or index == "d" then
132
+ return self._currentSpring.Damper
133
+ elseif index == "Speed" or index == "s" then
134
+ return self._currentSpring.Speed
135
+ elseif index == "Clock" then
136
+ return self._currentSpring.Clock
137
+ elseif SpringObject[index] then
138
+ return SpringObject[index]
139
+ else
140
+ error(("%q is not a member of SpringObject"):format(tostring(index)))
141
+ end
142
+ end
143
+
144
+ function SpringObject:__newindex(index, value)
145
+ if index == "Value" or index == "Position" or index == "p" then
146
+ local observable = Blend.toPropertyObservable(value) or Rx.of(value)
147
+
148
+ self._maid._valueSub = observable:Subscribe(function(unconverted)
149
+ local converted = SpringUtils.toLinearIfNeeded(unconverted)
150
+ self:_getSpringForType(converted).Value = converted
151
+ self.Changed:Fire()
152
+ end)
153
+ elseif index == "Velocity" or index == "v" then
154
+ local observable = Blend.toPropertyObservable(value) or Rx.of(value)
155
+
156
+ self._maid._velocitySub = observable:Subscribe(function(unconverted)
157
+ local converted = SpringUtils.toLinearIfNeeded(unconverted)
158
+
159
+ self:_getSpringForType(0*converted).Velocity = converted
160
+ self.Changed:Fire()
161
+ end)
162
+ elseif index == "Target" or index == "t" then
163
+ local observable = Blend.toPropertyObservable(value) or Rx.of(value)
164
+
165
+ self._maid._targetSub = observable:Subscribe(function(unconverted)
166
+ local converted = SpringUtils.toLinearIfNeeded(unconverted)
167
+ self:_getSpringForType(converted).Target = converted
168
+
169
+ self.Changed:Fire()
170
+ end)
171
+ elseif index == "Damper" or index == "d" then
172
+ local observable = assert(Blend.toNumberObservable(value), "Invalid damper")
173
+
174
+ self._maid._damperSub = observable:Subscribe(function(unconverted)
175
+ assert(type(unconverted) == "number", "Bad damper")
176
+
177
+ self._currentSpring.Damper = unconverted
178
+ self.Changed:Fire()
179
+ end)
180
+ elseif index == "Speed" or index == "s" then
181
+ local observable = assert(Blend.toNumberObservable(value), "Invalid speed")
182
+
183
+ self._maid._speedSub = observable:Subscribe(function(unconverted)
184
+ assert(type(unconverted) == "number", "Bad damper")
185
+
186
+ self._currentSpring.Speed = unconverted
187
+ self.Changed:Fire()
188
+ end)
189
+ elseif index == "Clock" then
190
+ assert(type(value) == "function", "Bad clock value")
191
+ self._currentSpring.Clock = value
192
+ self.Changed:Fire()
193
+ else
194
+ error(("%q is not a member of SpringObject"):format(tostring(index)))
195
+ end
196
+ end
197
+
198
+ function SpringObject:_getSpringForType(converted)
199
+ if rawget(self, "_currentSpring") == nil then
200
+ -- only happens on init
201
+ rawset(self, "_currentSpring", Spring.new(converted))
202
+ return self._currentSpring
203
+ else
204
+ local currentType = typeof(SpringUtils.fromLinearIfNeeded(self._currentSpring.Value))
205
+ if currentType == typeof(converted) then
206
+ return self._currentSpring
207
+ else
208
+ local oldDamper = self._currentSpring.d
209
+ local oldSpeed = self._currentSpring.s
210
+
211
+ self._currentSpring = Spring.new(converted)
212
+ self._currentSpring.Speed = oldSpeed
213
+ self._currentSpring.Damper = oldDamper
214
+ return self._currentSpring
215
+ end
216
+ end
217
+ end
218
+
219
+ --[=[
220
+ Cleans up the BaseObject and sets the metatable to nil
221
+ ]=]
222
+ function SpringObject:Destroy()
223
+ self._maid:DoCleaning()
224
+ setmetatable(self, nil)
225
+ end
226
+
227
+ return SpringObject
@@ -0,0 +1,45 @@
1
+ --[[
2
+ @class BlendSingle.story
3
+ ]]
4
+
5
+ local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
6
+
7
+ local Maid = require("Maid")
8
+ local Observable = require("Observable")
9
+ local Blend = require("Blend")
10
+
11
+ return function(target)
12
+ local maid = Maid.new()
13
+
14
+ local state = Blend.State("a")
15
+
16
+ local result = Blend.Single(Blend.Dynamic(state, function(text)
17
+ return Blend.New "TextLabel" {
18
+ Parent = target;
19
+ Text = text;
20
+ Size = UDim2.new(1, 0, 1, 0);
21
+ BackgroundTransparency = 0.5;
22
+ [function()
23
+ return Observable.new(function()
24
+ local internal = Maid.new()
25
+
26
+ print("Made for", text)
27
+ internal:GiveTask(function()
28
+ print("Cleaning up", text)
29
+ end)
30
+
31
+ return internal
32
+ end)
33
+ end] = true;
34
+ }
35
+ end))
36
+
37
+
38
+ maid:GiveTask(result:Subscribe())
39
+
40
+ state.Value = "b"
41
+
42
+ return function()
43
+ maid:DoCleaning()
44
+ end
45
+ end
@@ -1,5 +1,6 @@
1
- -- Main injection point
2
- -- @script ServerMain
1
+ --[[
2
+ @class ServerMain
3
+ ]]
3
4
 
4
5
  local ServerScriptService = game:GetService("ServerScriptService")
5
6