@quenty/blend 6.14.0 → 6.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 +11 -0
- package/package.json +7 -7
- package/src/Shared/Blend/Blend.lua +138 -10
- package/src/Shared/Test/BlendFind.story.lua +78 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.15.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@6.14.0...@quenty/blend@6.15.0) (2023-04-06)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add Blend.Find which allows mounting in existing frames, as well as allowing [Blend.Children] to be optional ([9af4998](https://github.com/Quenty/NevermoreEngine/commit/9af4998f5de3287c90f0e6eb279b95d10019bfd2))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [6.14.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@6.13.0...@quenty/blend@6.14.0) (2023-04-03)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @quenty/blend
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/blend",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.0",
|
|
4
4
|
"description": "Declarative UI system.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@quenty/acceltween": "^2.2.1",
|
|
31
|
-
"@quenty/brio": "^8.
|
|
32
|
-
"@quenty/instanceutils": "^7.
|
|
31
|
+
"@quenty/brio": "^8.10.0",
|
|
32
|
+
"@quenty/instanceutils": "^7.11.0",
|
|
33
33
|
"@quenty/loader": "^6.2.0",
|
|
34
34
|
"@quenty/maid": "^2.5.0",
|
|
35
35
|
"@quenty/promise": "^6.4.0",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"@quenty/spring": "^6.2.0",
|
|
39
39
|
"@quenty/steputils": "^3.2.0",
|
|
40
40
|
"@quenty/string": "^3.1.0",
|
|
41
|
-
"@quenty/valuebaseutils": "^7.
|
|
42
|
-
"@quenty/valueobject": "^7.
|
|
41
|
+
"@quenty/valuebaseutils": "^7.11.0",
|
|
42
|
+
"@quenty/valueobject": "^7.11.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@quenty/contentproviderutils": "^6.
|
|
45
|
+
"@quenty/contentproviderutils": "^6.7.0",
|
|
46
46
|
"@quenty/playerthumbnailutils": "^6.4.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "d26d804d1f96e9a0f3f5687f8e024ec9d476172d"
|
|
49
49
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
--[=[
|
|
2
|
-
Declarative UI system inspired by Fusion
|
|
2
|
+
Declarative UI system inspired by Fusion.
|
|
3
3
|
@class Blend
|
|
4
4
|
]=]
|
|
5
5
|
|
|
@@ -562,9 +562,23 @@ end
|
|
|
562
562
|
};
|
|
563
563
|
```
|
|
564
564
|
|
|
565
|
+
Note since 6.14 you don't need to be explicit about [Blend.Children]. Any number-based
|
|
566
|
+
index in the mounting process will be automatically inferred as children to mount.
|
|
567
|
+
|
|
568
|
+
```lua
|
|
569
|
+
Blend.New "ScreenGui" {
|
|
570
|
+
Parent = game.Players.LocalPlayer.PlayerGui;
|
|
571
|
+
|
|
572
|
+
Blend.New "Frame" {
|
|
573
|
+
Size = UDim2.new(1, 0, 1, 0);
|
|
574
|
+
BackgroundTransparency = 0.5;
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
```
|
|
578
|
+
|
|
565
579
|
Rules:
|
|
566
580
|
|
|
567
|
-
* `{ Instance }` -Tables of instances are all parented to the parent
|
|
581
|
+
* `{ Instance }` - Tables of instances are all parented to the parent
|
|
568
582
|
* Brio<Instance> will last for the lifetime of the brio
|
|
569
583
|
* Brio<Observable<Instance>> will last for the lifetime of the brio
|
|
570
584
|
* Brio<Signal<Instance>> will also act as above
|
|
@@ -579,18 +593,20 @@ end
|
|
|
579
593
|
* Observable<Brio<Instance>> will last for the lifetime of the brio, and parent the instance.
|
|
580
594
|
* Observable<Observable<Instance>> occurs when computed returns a value.
|
|
581
595
|
* ValueObject<Instance> will switch to the current value
|
|
596
|
+
* function - Will be invoked as `func(parent)` and then the standard scheme will be applied
|
|
582
597
|
|
|
583
598
|
Cleanup:
|
|
584
599
|
* Instances will be cleaned up on unsubscribe
|
|
585
600
|
|
|
586
601
|
@param parent Instance
|
|
587
602
|
@param value any
|
|
588
|
-
@return
|
|
603
|
+
@return Observable
|
|
589
604
|
]=]
|
|
590
605
|
function Blend.Children(parent, value)
|
|
591
606
|
assert(typeof(parent) == "Instance", "Bad parent")
|
|
592
607
|
|
|
593
|
-
local observe = Blend._observeChildren(value)
|
|
608
|
+
local observe = Blend._observeChildren(value, parent)
|
|
609
|
+
|
|
594
610
|
if observe then
|
|
595
611
|
return observe:Pipe({
|
|
596
612
|
Rx.tap(function(child)
|
|
@@ -602,6 +618,79 @@ function Blend.Children(parent, value)
|
|
|
602
618
|
end
|
|
603
619
|
end
|
|
604
620
|
|
|
621
|
+
--[=[
|
|
622
|
+
Mounts Blend objects into an existing instance.
|
|
623
|
+
|
|
624
|
+
:::tip
|
|
625
|
+
Normally specifying ClassName as a property breaks mounting, since you
|
|
626
|
+
can't write to ClassName. However, if you specify ClassName here, it will only
|
|
627
|
+
listen to changes on children with that class name.
|
|
628
|
+
:::
|
|
629
|
+
|
|
630
|
+
If multiple instances are named the same thing, then this will
|
|
631
|
+
bind to both.
|
|
632
|
+
|
|
633
|
+
:::tip
|
|
634
|
+
This explicitly listens for any children underneath the mounted
|
|
635
|
+
instance with the name passed in here. This is fine for small amounts
|
|
636
|
+
of instances, like in most Gui hierarchies. However, it will be way less
|
|
637
|
+
performance friendly for large class hierarchies.
|
|
638
|
+
:::
|
|
639
|
+
|
|
640
|
+
```lua
|
|
641
|
+
maid:GiveTask(Blend.mount(frame, {
|
|
642
|
+
Size = UDim2.new(0.5, 0, 0.5, 0);
|
|
643
|
+
|
|
644
|
+
Blend.Find "MyUIScaleName" {
|
|
645
|
+
Scale = 2;
|
|
646
|
+
};
|
|
647
|
+
}))
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
@param name string
|
|
651
|
+
@return function
|
|
652
|
+
]=]
|
|
653
|
+
function Blend.Find(name)
|
|
654
|
+
assert(type(name) == "string", "Bad name")
|
|
655
|
+
|
|
656
|
+
return function(props)
|
|
657
|
+
assert(type(props) == "table", "Bad props")
|
|
658
|
+
|
|
659
|
+
local mountProps = props
|
|
660
|
+
local className
|
|
661
|
+
if props.ClassName then
|
|
662
|
+
className = props.ClassName
|
|
663
|
+
mountProps = table.clone(props)
|
|
664
|
+
mountProps.ClassName = nil
|
|
665
|
+
else
|
|
666
|
+
className = "Instance"
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
return function(parent)
|
|
670
|
+
return RxInstanceUtils.observeChildrenOfNameBrio(parent, className, name):Pipe({
|
|
671
|
+
Rx.flatMap(function(brio)
|
|
672
|
+
if brio:IsDead() then
|
|
673
|
+
return
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
local maid = brio:ToMaid()
|
|
677
|
+
local instance = brio:GetValue()
|
|
678
|
+
maid:GiveTask(Blend.mount(instance, mountProps))
|
|
679
|
+
|
|
680
|
+
-- Dead after mounting? Clean up...
|
|
681
|
+
-- Probably caused by name change.
|
|
682
|
+
if brio:IsDead() then
|
|
683
|
+
maid:DoCleaning()
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
-- Avoid emitting anything else so we don't get cleaned up
|
|
687
|
+
return Rx.EMPTY
|
|
688
|
+
end);
|
|
689
|
+
})
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
|
|
605
694
|
--[=[
|
|
606
695
|
An event emitter that emits the instance that was actually created. This is
|
|
607
696
|
useful for a variety of things.
|
|
@@ -622,6 +711,20 @@ end
|
|
|
622
711
|
};
|
|
623
712
|
```
|
|
624
713
|
|
|
714
|
+
Note that since 6.14 you should also be able to just use the reification scheme of
|
|
715
|
+
[Blend.Children] implicitly in [Blend.mount] to get somewhat equivalent behavior.
|
|
716
|
+
|
|
717
|
+
```lua
|
|
718
|
+
Blend.mount(frame, {
|
|
719
|
+
-- Array indexed methods get treated as children-constructors, which get the parent
|
|
720
|
+
-- in them;
|
|
721
|
+
|
|
722
|
+
function(parent)
|
|
723
|
+
print("Got parent!", parent)
|
|
724
|
+
end;
|
|
725
|
+
})
|
|
726
|
+
```
|
|
727
|
+
|
|
625
728
|
You can also use this to execute code against an instance.
|
|
626
729
|
|
|
627
730
|
```lua
|
|
@@ -701,9 +804,10 @@ end
|
|
|
701
804
|
Observes children and ensures that the value is cleaned up
|
|
702
805
|
afterwards.
|
|
703
806
|
@param value any
|
|
807
|
+
@param parent Instance
|
|
704
808
|
@return Observable<Instance>
|
|
705
809
|
]=]
|
|
706
|
-
function Blend._observeChildren(value)
|
|
810
|
+
function Blend._observeChildren(value, parent)
|
|
707
811
|
if typeof(value) == "Instance" then
|
|
708
812
|
-- Should be uncommon
|
|
709
813
|
return Observable.new(function(sub)
|
|
@@ -713,6 +817,10 @@ function Blend._observeChildren(value)
|
|
|
713
817
|
end)
|
|
714
818
|
end
|
|
715
819
|
|
|
820
|
+
if type(value) == "function" then
|
|
821
|
+
value = Blend._observeChildren(value(parent), parent)
|
|
822
|
+
end
|
|
823
|
+
|
|
716
824
|
if ValueObject.isValueObject(value) then
|
|
717
825
|
return Observable.new(function(sub)
|
|
718
826
|
local maid = Maid.new()
|
|
@@ -726,7 +834,7 @@ function Blend._observeChildren(value)
|
|
|
726
834
|
return
|
|
727
835
|
end
|
|
728
836
|
|
|
729
|
-
local observe = Blend._observeChildren(result)
|
|
837
|
+
local observe = Blend._observeChildren(result, parent)
|
|
730
838
|
if observe then
|
|
731
839
|
maid._current = nil
|
|
732
840
|
|
|
@@ -783,7 +891,7 @@ function Blend._observeChildren(value)
|
|
|
783
891
|
return maid
|
|
784
892
|
end
|
|
785
893
|
|
|
786
|
-
local observe = Blend._observeChildren(result)
|
|
894
|
+
local observe = Blend._observeChildren(result, parent)
|
|
787
895
|
if observe then
|
|
788
896
|
local maid = value:ToMaid()
|
|
789
897
|
|
|
@@ -829,7 +937,7 @@ function Blend._observeChildren(value)
|
|
|
829
937
|
return
|
|
830
938
|
end
|
|
831
939
|
|
|
832
|
-
local observe = Blend._observeChildren(result)
|
|
940
|
+
local observe = Blend._observeChildren(result, parent)
|
|
833
941
|
|
|
834
942
|
if observe then
|
|
835
943
|
local innerMaid = Maid.new()
|
|
@@ -864,7 +972,7 @@ function Blend._observeChildren(value)
|
|
|
864
972
|
if type(value) == "table" and not getmetatable(value) then
|
|
865
973
|
local observables = {}
|
|
866
974
|
for key, item in pairs(value) do
|
|
867
|
-
local observe = Blend._observeChildren(item)
|
|
975
|
+
local observe = Blend._observeChildren(item, parent)
|
|
868
976
|
if observe then
|
|
869
977
|
table.insert(observables, observe)
|
|
870
978
|
else
|
|
@@ -893,7 +1001,23 @@ end
|
|
|
893
1001
|
* Keys of functions are invoked on the instance in question
|
|
894
1002
|
* `(instance, value) -> Observable
|
|
895
1003
|
* If this returns an observable (or can be turned into one), we subscribe the event immediately
|
|
896
|
-
*
|
|
1004
|
+
* Keys of numbers (array components) are treated as implicit children
|
|
1005
|
+
* If the key is [Blend.Children] then we invoke mountChildren on it.
|
|
1006
|
+
|
|
1007
|
+
```lua
|
|
1008
|
+
Blend.mount(frame, {
|
|
1009
|
+
BackgroundTransparency = 1;
|
|
1010
|
+
|
|
1011
|
+
-- All items named InventoryItem
|
|
1012
|
+
Blend.Find "InventoryItem" {
|
|
1013
|
+
|
|
1014
|
+
-- Apply the following properties
|
|
1015
|
+
Blend.New "UIScale" {
|
|
1016
|
+
Scale = 0.5;
|
|
1017
|
+
};
|
|
1018
|
+
};
|
|
1019
|
+
})
|
|
1020
|
+
```
|
|
897
1021
|
|
|
898
1022
|
@param instance Instance
|
|
899
1023
|
@param props table
|
|
@@ -931,6 +1055,10 @@ function Blend.mount(instance, props)
|
|
|
931
1055
|
else
|
|
932
1056
|
warn(("Unable to apply event listener %q"):format(tostring(key)))
|
|
933
1057
|
end
|
|
1058
|
+
elseif type(key) == "number" then
|
|
1059
|
+
-- Treat this as an implicit children contract
|
|
1060
|
+
-- Thus, we don't need an explicit [Blend.Children] call.
|
|
1061
|
+
table.insert(dependentObservables, { Blend.Children(instance, value), value })
|
|
934
1062
|
else
|
|
935
1063
|
warn(("Unable to apply property %q"):format(tostring(key)))
|
|
936
1064
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--[[
|
|
2
|
+
@class Blend.story
|
|
3
|
+
]]
|
|
4
|
+
|
|
5
|
+
local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
|
|
6
|
+
|
|
7
|
+
local RunService = game:GetService("RunService")
|
|
8
|
+
|
|
9
|
+
local Blend = require("Blend")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
|
|
12
|
+
return function(target)
|
|
13
|
+
local maid = Maid.new()
|
|
14
|
+
|
|
15
|
+
local isVisible = Instance.new("BoolValue")
|
|
16
|
+
isVisible.Value = false
|
|
17
|
+
|
|
18
|
+
local percentVisible = Blend.Spring(Blend.Computed(isVisible, function(visible)
|
|
19
|
+
return visible and 1 or 0
|
|
20
|
+
end), 35)
|
|
21
|
+
|
|
22
|
+
local transparency = Blend.Computed(percentVisible, function(percent)
|
|
23
|
+
return 1 - percent
|
|
24
|
+
end)
|
|
25
|
+
|
|
26
|
+
local frame = Instance.new("Frame")
|
|
27
|
+
frame.Size = UDim2.new(0.5, 0, 0.5, 0)
|
|
28
|
+
frame.BackgroundColor3 = Color3.new(0.9, 0.9, 0.9)
|
|
29
|
+
frame.AnchorPoint = Vector2.new(0.5, 0.5)
|
|
30
|
+
frame.Position = UDim2.new(0.5, 0, 0.5, 0)
|
|
31
|
+
frame.BackgroundTransparency = transparency
|
|
32
|
+
frame.Parent = target
|
|
33
|
+
maid:GiveTask(frame)
|
|
34
|
+
|
|
35
|
+
local subFrame = Instance.new("Frame")
|
|
36
|
+
subFrame.Name = "CenterFrame";
|
|
37
|
+
subFrame.Size = UDim2.new(0.5, 0, 0.5, 0)
|
|
38
|
+
subFrame.BackgroundColor3 = Color3.new(0.5, 0.5, 0.5)
|
|
39
|
+
subFrame.AnchorPoint = Vector2.new(0.5, 0.5)
|
|
40
|
+
subFrame.Position = UDim2.new(0.5, 0, 0.5, 0)
|
|
41
|
+
subFrame.BackgroundTransparency = transparency
|
|
42
|
+
subFrame.Parent = frame
|
|
43
|
+
|
|
44
|
+
local uiScale = Instance.new("UIScale")
|
|
45
|
+
uiScale.Name = "MyUIScale"
|
|
46
|
+
uiScale.Parent = subFrame
|
|
47
|
+
|
|
48
|
+
maid:GiveTask(Blend.mount(frame, {
|
|
49
|
+
Size = UDim2.new(0.5, 0, 0.5, 0);
|
|
50
|
+
|
|
51
|
+
Blend.New "UICorner" {
|
|
52
|
+
CornerRadius = UDim.new(0.05, 0);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
Blend.Find "CenterFrame" {
|
|
56
|
+
Blend.Find "MyUIScale" {
|
|
57
|
+
ClassName = "UIScale";
|
|
58
|
+
|
|
59
|
+
Scale = Blend.Computed(percentVisible, function(percent)
|
|
60
|
+
return 0.8 + 0.2*percent
|
|
61
|
+
end);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Blend.New "UICorner" {
|
|
65
|
+
CornerRadius = UDim.new(0.05, 0);
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
local PERIOD = 2
|
|
71
|
+
maid:GiveTask(RunService.RenderStepped:Connect(function()
|
|
72
|
+
isVisible.Value = os.clock()/PERIOD % 1 < 0.5
|
|
73
|
+
end))
|
|
74
|
+
|
|
75
|
+
return function()
|
|
76
|
+
maid:DoCleaning()
|
|
77
|
+
end
|
|
78
|
+
end
|