@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 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.14.0",
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.9.0",
32
- "@quenty/instanceutils": "^7.10.0",
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.10.0",
42
- "@quenty/valueobject": "^7.10.0"
41
+ "@quenty/valuebaseutils": "^7.11.0",
42
+ "@quenty/valueobject": "^7.11.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@quenty/contentproviderutils": "^6.6.0",
45
+ "@quenty/contentproviderutils": "^6.7.0",
46
46
  "@quenty/playerthumbnailutils": "^6.4.0"
47
47
  },
48
- "gitHead": "fa7c7edfd754cf18fd808466121310f9bfff6271"
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 MaidTask
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
- * If the key is [Blend.Children] then we invoke mountChildren on it
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