diff --git a/Package.swift b/Package.swift
index 7c5c93d..5579cca 100644
--- a/Package.swift
+++ b/Package.swift
@@ -10,7 +10,7 @@ let package = Package(
.library(name: "Autolayout", type: .static, targets: ["Autolayout"])
],
dependencies: [
- .package(url: "https://github.com/swifweb/web", from: "1.0.0-beta.1.20.0")
+ .package(url: "https://github.com/swifweb/web", from: "1.0.0-beta.1.22.0")
],
targets: [
.target(name: "Autolayout", dependencies: [
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..438eeba
--- /dev/null
+++ b/README.md
@@ -0,0 +1,780 @@
+[![SwifWeb](https://user-images.githubusercontent.com/1272610/208269515-82c38e3b-530c-4611-9638-6eaac2cf87e3.png)](http://swifweb.com)
+
+
+
+
+
+
+
+
+
+
+
+
+
+This library gives you the powerful autolayout which works on pure CSS3 ❤️
+
+It will help you to easily build your awesome reactive web app in beloved Swift ❤️
+
+# Installation
+
+Add package to your SwifWeb app's `Package.swift`
+
+In dependencies section
+```swift
+ dependencies: [
+ .package(url: "https://github.com/swifweb/autolayout", from: "1.0.0")
+]
+```
+In target section
+```swift
+targets: [
+ .executableTarget(name: "App", dependencies: [
+ .product(name: "Web", package: "web"),
+ .product(name: "Autolayout", package: "autolayout")
+ ]
+]
+```
+
+# Usage
+
+Start using it at any view by simply declaring methods listed below.
+
+## Breakpoints
+
+Breakpoints can be added in the end of any autolayout-method. It is direct full power of CSS3 `@media` rule.
+
+Classic way to set `@media` rules is like this
+
+```swift
+.all.minWidth(576.px).maxWidth(767.px)
+```
+or like this if you need to declare several
+```swift
+.all.maxWidth(575.px), .all.minWidth(576.px).maxWidth(767.px), .all.minWidth(768.px).maxWidth(991.px)
+```
+
+But you can predefine needed breakpoints easily like this
+
+```swift
+extension MediaRule.MediaType {
+ static var extraSmall: MediaRule.MediaType { .init(.all.maxWidth(575.px), label: "xs") }
+ static var small: MediaRule.MediaType { .init(.all.minWidth(576.px).maxWidth(767.px), label: "s") }
+ static var medium: MediaRule.MediaType { .init(.all.minWidth(768.px).maxWidth(991.px), label: "m") }
+}
+```
+
+So you will be able to use them simply like this
+
+```swift
+.top(100.px, breakpoints: .extraSmall, .small, .medium)
+```
+
+To save your time there are already predefined breakpoints
+
+```swift
+.extraSmall // <576px
+.small // ≥576px and <768px
+.medium // ≥768px and <992px
+.large // ≥992px and <1200px
+.extraLarge // ≥1200px and <1400px
+.extraExtraLarge // ≥1400px
+```
+
+## Methods
+
+> You can declare multiple same methods but with different breakpoints.
+
+> Each method also accept `@State` value
+
+### Top
+
+Specifies `top` position to the first parent element with relative position
+
+#### Top to top
+
+```swift
+// will set top to 0px
+.top()
+
+// will set top to 100px
+.top(100.px)
+
+// will set top to 50px
+.top(100.px, multiplier: 0.5)
+
+// will set top to 0px only for extra-small, small and medium screens
+.top(breakpoints: .xs, .s, m)
+
+// will set top to 50px only for extra-small, small and medium screens
+.top(50.px, breakpoints: .xs, .s, m)
+
+// will set top to 25px only for extra-small, small and medium screens
+.top(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Top to center
+
+Simply add `side: .center` as a second argument and `top` side will stick to `center` of the first parent with relative position
+
+```swift
+// will set top to 0px from the center
+.top(side: .center)
+
+// will set top to 100px from the center
+.top(100.px, side: .center)
+
+// will set top to 50px from the center
+.top(100.px, side: .center, multiplier: 0.5)
+
+// will set top to 0px from the center only for extra-small, small and medium screens
+.top(side: .center, breakpoints: .xs, .s, m)
+
+// will set top to 50px from the center only for extra-small, small and medium screens
+.top(50.px, side: .center, breakpoints: .xs, .s, m)
+
+// will set top to 25px from the center only for extra-small, small and medium screens
+.top(50.px, side: .center, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Top to bottom
+
+Simply add `side: .bottom` as a second argument and `top` side will stick to `bottom` of the first parent with relative position
+
+```swift
+// will set top to 0px from the bottom
+.top(side: .bottom)
+
+// will set top to 100px from the bottom
+.top(100.px, side: .bottom)
+
+// will set top to 50px from the bottom
+.top(100.px, side: .bottom, multiplier: 0.5)
+
+// will set top to 0px from the bottom only for extra-small, small and medium screens
+.top(side: .bottom, breakpoints: .xs, .s, m)
+
+// will set top to 50px from the bottom only for extra-small, small and medium screens
+.top(50.px, side: .bottom, breakpoints: .xs, .s, m)
+
+// will set top to 25px from the bottom only for extra-small, small and medium screens
+.top(50.px, side: .bottom, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Bottom
+
+Specifies `bottom` position to the first parent element with relative position
+
+#### Bottom to bottom
+
+```swift
+// will set bottom to 0px
+.bottom()
+
+// will set bottom to 100px
+.bottom(100.px)
+
+// will set bottom to 50px
+.bottom(100.px, multiplier: 0.5)
+
+// will set bottom to 0px only for extra-small, small and medium screens
+.bottom(breakpoints: .xs, .s, m)
+
+// will set bottom to 50px only for extra-small, small and medium screens
+.bottom(50.px, breakpoints: .xs, .s, m)
+
+// will set bottom to 25px only for extra-small, small and medium screens
+.bottom(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Bottom to center
+
+Simply add `side: .center` as a second argument and `bottom` side will stick to `center` of the first parent with relative position
+
+```swift
+// will set bottom to 0px from the center
+.bottom(side: .center)
+
+// will set bottom to 100px from the center
+.bottom(100.px, side: .center)
+
+// will set bottom to 50px from the center
+.bottom(100.px, side: .center, multiplier: 0.5)
+
+// will set bottom to 0px from the center only for extra-small, small and medium screens
+.bottom(side: .center, breakpoints: .xs, .s, m)
+
+// will set bottom to 50px from the center only for extra-small, small and medium screens
+.bottom(50.px, side: .center, breakpoints: .xs, .s, m)
+
+// will set bottom to 25px from the center only for extra-small, small and medium screens
+.bottom(50.px, side: .center, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Bottom to top
+
+Simply add `side: .top` as a second argument and `bottom` side will stick to `top` of the first parent with relative position
+
+```swift
+// will set bottom to 0px from the top
+.bottom(side: .top)
+
+// will set bottom to 100px from the top
+.bottom(100.px, side: .top)
+
+// will set bottom to 50px from the top
+.bottom(100.px, side: .top, multiplier: 0.5)
+
+// will set bottom to 0px from the top only for extra-small, small and medium screens
+.bottom(side: .top, breakpoints: .xs, .s, m)
+
+// will set bottom to 50px from the top only for extra-small, small and medium screens
+.bottom(50.px, side: .top, breakpoints: .xs, .s, m)
+
+// will set bottom to 25px from the top only for extra-small, small and medium screens
+.bottom(50.px, side: .top, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Left
+
+Specifies `left` position to the first parent element with relative position
+
+#### Left to left
+
+```swift
+// will set left to 0px
+.left()
+
+// will set left to 100px
+.left(100.px)
+
+// will set left to 50px
+.left(100.px, multiplier: 0.5)
+
+// will set left to 0px only for extra-small, small and medium screens
+.left(breakpoints: .xs, .s, m)
+
+// will set left to 50px only for extra-small, small and medium screens
+.left(50.px, breakpoints: .xs, .s, m)
+
+// will set left to 25px only for extra-small, small and medium screens
+.left(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Left to center
+
+Simply add `side: .center` as a second argument and `left` side will stick to `center` of the first parent with relative position
+
+```swift
+// will set left to 0px from the center
+.left(side: .center)
+
+// will set left to 100px from the center
+.left(100.px, side: .center)
+
+// will set left to 50px from the center
+.left(100.px, side: .center, multiplier: 0.5)
+
+// will set left to 0px from the center only for extra-small, small and medium screens
+.left(side: .center, breakpoints: .xs, .s, m)
+
+// will set left to 50px from the center only for extra-small, small and medium screens
+.left(50.px, side: .center, breakpoints: .xs, .s, m)
+
+// will set left to 25px from the center only for extra-small, small and medium screens
+.left(50.px, side: .center, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Left to right
+
+Simply add `side: .right` as a second argument and `left` side will stick to `right` of the first parent with relative position
+
+```swift
+// will set left to 0px from the right
+.left(side: .right)
+
+// will set left to 100px from the right
+.left(100.px, side: .right)
+
+// will set left to 50px from the right
+.left(100.px, side: .right, multiplier: 0.5)
+
+// will set left to 0px from the right only for extra-small, small and medium screens
+.left(side: .right, breakpoints: .xs, .s, m)
+
+// will set left to 50px from the right only for extra-small, small and medium screens
+.left(50.px, side: .right, breakpoints: .xs, .s, m)
+
+// will set left to 25px from the right only for extra-small, small and medium screens
+.left(50.px, side: .right, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Right
+
+Specifies `right` position to the first parent element with relative position
+
+#### Right to right
+
+```swift
+// will set right to 0px
+.right()
+
+// will set right to 100px
+.right(100.px)
+
+// will set right to 50px
+.right(100.px, multiplier: 0.5)
+
+// will set right to 0px only for extra-small, small and medium screens
+.right(breakpoints: .xs, .s, m)
+
+// will set right to 50px only for extra-small, small and medium screens
+.right(50.px, breakpoints: .xs, .s, m)
+
+// will set right to 25px only for extra-small, small and medium screens
+.right(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Right to center
+
+Simply add `side: .center` as a second argument and `right` side will stick to `center` of the first parent with relative position
+
+```swift
+// will set right to 0px from the center
+.right(side: .center)
+
+// will set right to 100px from the center
+.right(100.px, side: .center)
+
+// will set right to 50px from the center
+.right(100.px, side: .center, multiplier: 0.5)
+
+// will set right to 0px from the center only for extra-small, small and medium screens
+.right(side: .center, breakpoints: .xs, .s, m)
+
+// will set right to 50px from the center only for extra-small, small and medium screens
+.right(50.px, side: .center, breakpoints: .xs, .s, m)
+
+// will set right to 25px from the center only for extra-small, small and medium screens
+.right(50.px, side: .center, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Right to left
+
+Simply add `side: .left` as a second argument and `right` side will stick to `left` of the first parent with relative position
+
+```swift
+// will set right to 0px from the left
+.right(side: .left)
+
+// will set right to 100px from the left
+.right(100.px, side: .left)
+
+// will set right to 50px from the left
+.right(100.px, side: .left, multiplier: 0.5)
+
+// will set right to 0px from the left only for extra-small, small and medium screens
+.right(side: .left, breakpoints: .xs, .s, m)
+
+// will set right to 50px from the left only for extra-small, small and medium screens
+.right(50.px, side: .left, breakpoints: .xs, .s, m)
+
+// will set right to 25px from the left only for extra-small, small and medium screens
+.right(50.px, side: .left, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Edges
+
+Convenience setter for all sides: top, right, bottom, left
+
+#### All edges
+
+```swift
+// Will set top, right, bottom, and left to 0px
+.edges()
+
+// Will set top, right, bottom, and left to 10px
+.edges(10.px)
+
+// Will set top, right, bottom, and left to 5px only for extra-small, small and medium screens
+.edges(5.px, breakpoints: .xs, .s, m)
+```
+
+#### Horizontal edges
+
+```swift
+// Will set left and right to 0px
+.edges(h: 0.px)
+
+// Will set left and right to 10px
+.edges(h: 10.px)
+
+// Will set left and right to 5px only for extra-small, small and medium screens
+.edges(h: 5.px, breakpoints: .xs, .s, m)
+```
+
+#### Vertical edges
+
+```swift
+// Will set top and bottom to 0px
+.edges(v: 0.px)
+
+// Will set top and bottom to 10px
+.edges(v: 10.px)
+
+// Will set top and bottom to 5px only for extra-small, small and medium screens
+.edges(v: 5.px, breakpoints: .xs, .s, m)
+```
+
+#### Horizontal and vertical edges
+
+```swift
+// Will set left and right to 0px, and top and bottom to 0px
+.edges(h: 0.px, v: 0.px)
+
+// Will set left and right to 0px, and top and bottom to 10px
+.edges(h: 0.px, v: 10.px)
+
+// Will set left and right to 2px, and top and bottom to 4px only for extra-small, small and medium screens
+.edges(h: 2.px, v: 4.px, breakpoints: .xs, .s, m)
+```
+
+### Center X
+
+Specifies the horizontal center position to the first parent element with relative position
+
+#### Center to center
+
+```swift
+// will set centerX to 0px
+.centerX()
+
+// will set centerX to 100px
+.centerX(100.px)
+
+// will set centerX to 50px
+.centerX(100.px, multiplier: 0.5)
+
+// will set centerX to 0px only for extra-small, small and medium screens
+.centerX(breakpoints: .xs, .s, m)
+
+// will set centerX to 50px only for extra-small, small and medium screens
+.centerX(50.px, breakpoints: .xs, .s, m)
+
+// will set centerX to 25px only for extra-small, small and medium screens
+.centerX(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Center to left
+
+Simply add `side: .left` as a second argument and `centerX` side will stick to `left` of the first parent with relative position
+
+```swift
+// will set centerX to 0px of the left
+.centerX(side: .left)
+
+// will set centerX to 100px of the left
+.centerX(100.px, side: .left)
+
+// will set centerX to 50px of the left
+.centerX(100.px, side: .left, multiplier: 0.5)
+
+// will set centerX to 0px of the left only for extra-small, small and medium screens
+.centerX(side: .left, breakpoints: .xs, .s, m)
+
+// will set centerX to 50px of the left only for extra-small, small and medium screens
+.centerX(50.px, side: .left, breakpoints: .xs, .s, m)
+
+// will set centerX to 25px of the left only for extra-small, small and medium screens
+.centerX(50.px, side: .left, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Center to right
+
+Simply add `side: .right` as a second argument and `centerX ` side will stick to `right` of the first parent with relative position
+
+```swift
+// will set centerX to 0px of the right
+.centerX(side: .right)
+
+// will set centerX to 100px of the right
+.centerX(100.px, side: .right)
+
+// will set centerX to 50px of the right
+.centerX(100.px, side: .right, multiplier: 0.5)
+
+// will set centerX to 0px of the right only for extra-small, small and medium screens
+.centerX(side: .right, breakpoints: .xs, .s, m)
+
+// will set centerX to 50px of the right only for extra-small, small and medium screens
+.centerX(50.px, side: .right, breakpoints: .xs, .s, m)
+
+// will set centerX to 25px of the right only for extra-small, small and medium screens
+.centerX(50.px, side: .right, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Center Y
+
+Specifies the vertical center position to the first parent element with relative position
+
+#### Center to center
+
+```swift
+// will set centerY to 0px
+.centerY()
+
+// will set centerY to 100px
+.centerY(100.px)
+
+// will set centerY to 50px
+.centerY(100.px, multiplier: 0.5)
+
+// will set centerY to 0px only for extra-small, small and medium screens
+.centerY(breakpoints: .xs, .s, m)
+
+// will set centerY to 50px only for extra-small, small and medium screens
+.centerY(50.px, breakpoints: .xs, .s, m)
+
+// will set centerY to 25px only for extra-small, small and medium screens
+.centerY(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Center to top
+
+Simply add `side: .top` as a second argument and `centerY` side will stick to `top` of the first parent with relative position
+
+```swift
+// will set centerY to 0px of the top
+.centerY(side: .top)
+
+// will set centerY to 100px of the top
+.centerY(100.px, side: .top)
+
+// will set centerY to 50px of the top
+.centerY(100.px, side: .top, multiplier: 0.5)
+
+// will set centerY to 0px of the top only for extra-small, small and medium screens
+.centerY(side: .top, breakpoints: .xs, .s, m)
+
+// will set centerY to 50px of the top only for extra-small, small and medium screens
+.centerY(50.px, side: .top, breakpoints: .xs, .s, m)
+
+// will set centerY to 25px of the top only for extra-small, small and medium screens
+.centerY(50.px, side: .top, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+#### Center to bottom
+
+Simply add `side: .bottom` as a second argument and `centerY ` side will stick to `bottom` of the first parent with relative position
+
+```swift
+// will set centerY to 0px of the bottom
+.centerY(side: .bottom)
+
+// will set centerY to 100px of the bottom
+.centerY(100.px, side: .bottom)
+
+// will set centerY to 50px of the bottom
+.centerY(100.px, side: .bottom, multiplier: 0.5)
+
+// will set centerY to 0px of the bottom only for extra-small, small and medium screens
+.centerY(side: .bottom, breakpoints: .xs, .s, m)
+
+// will set centerY to 50px of the bottom only for extra-small, small and medium screens
+.centerY(50.px, side: .bottom, breakpoints: .xs, .s, m)
+
+// will set centerY to 25px of the bottom only for extra-small, small and medium screens
+.centerY(50.px, side: .bottom, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Center X+Y
+
+Specifies both vertical and horizontal center position to the first parent element with relative position
+
+```swift
+// will set centerX and centerY to 0px
+.center()
+
+// will set centerX and centerY to 100px
+.center(100.px)
+
+// will set centerX and centerY to 50px
+.center(100.px, multiplier: 0.5)
+
+// will set centerX and centerY to 0px only for extra-small, small and medium screens
+.center(breakpoints: .xs, .s, m)
+
+// will set centerX and centerY to 50px only for extra-small, small and medium screens
+.center(50.px, breakpoints: .xs, .s, m)
+
+// will set centerX and centerY to 25px only for extra-small, small and medium screens
+.center(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Width
+
+Sets width of an element
+
+```swift
+// will set width to 0px
+.width()
+
+// will set width to 100px
+.width(100.px)
+
+// will set width to 100%
+.width(100.percent)
+
+// will set width to 50px
+.width(100.px, multiplier: 0.5)
+
+// will set width to 0px only for extra-small, small and medium screens
+.width(breakpoints: .xs, .s, m)
+
+// will set width to 50px only for extra-small, small and medium screens
+.width(50.px, breakpoints: .xs, .s, m)
+
+// will set width to 25px only for extra-small, small and medium screens
+.width(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Width to parent
+
+Sets width of an element to fit first parent element with relative position
+
+```swift
+// will set width to 100% of first parent element with relative position
+.widthToParent()
+
+// will set width to 100% of first parent element with relative position only for extra-small, small and medium screens
+.widthToParent(breakpoints: .xs, .s, m)
+
+// will set width to 100% + 100px of first parent element with relative position
+.widthToParent(extra: 100.px)
+
+// will set width to 100% + 100px of first parent element with relative position
+// only for extra-small, small and medium screens
+.widthToParent(extra: 100.px, breakpoints: .xs, .s, m)
+
+// will set width to (100% + 100px) * 0.5 of first parent element with relative position
+.widthToParent(extra: 100.px, multiplier: 0.5)
+
+// will set width to (100% + 100px) * 0.5 of first parent element with relative position
+// only for extra-small, small and medium screens
+.widthToParent(extra: 100.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+
+// will set width to 50% of first parent element with relative position
+.widthToParent(multiplier: 0.5)
+
+// will set width to 50% of first parent element with relative position
+// only for extra-small, small and medium screens
+.widthToParent(multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Height
+
+Sets height of an element
+
+```swift
+// will set height to 0px
+.height()
+
+// will set height to 100px
+.height(100.px)
+
+// will set height to 100%
+.height(100.percent)
+
+// will set height to 50px
+.height(100.px, multiplier: 0.5)
+
+// will set height to 0px only for extra-small, small and medium screens
+.height(breakpoints: .xs, .s, m)
+
+// will set height to 50px only for extra-small, small and medium screens
+.height(50.px, breakpoints: .xs, .s, m)
+
+// will set height to 25px only for extra-small, small and medium screens
+.height(50.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Height to parent
+
+Sets height of an element to fit first parent element with relative position
+
+```swift
+// will set height to 100% of first parent element with relative position
+.heightToParent()
+
+// will set height to 100% of first parent element with relative position only for extra-small, small and medium screens
+.heightToParent(breakpoints: .xs, .s, m)
+
+// will set height to 100% + 100px of first parent element with relative position
+.heightToParent(extra: 100.px)
+
+// will set height to 100% + 100px of first parent element with relative position
+// only for extra-small, small and medium screens
+.heightToParent(extra: 100.px, breakpoints: .xs, .s, m)
+
+// will set height to (100% + 100px) * 0.5 of first parent element with relative position
+.heightToParent(extra: 100.px, multiplier: 0.5)
+
+// will set height to (100% + 100px) * 0.5 of first parent element with relative position
+// only for extra-small, small and medium screens
+.heightToParent(extra: 100.px, multiplier: 0.5, breakpoints: .xs, .s, m)
+
+// will set height to 50% of first parent element with relative position
+.heightToParent(multiplier: 0.5)
+
+// will set height to 50% of first parent element with relative position
+// only for extra-small, small and medium screens
+.heightToParent(multiplier: 0.5, breakpoints: .xs, .s, m)
+```
+
+### Position
+
+Specifies the type of positioning method used for an element `static, relative, absolute or fixed`
+
+```swift
+// will set position to absolute
+.position(.absolute)
+
+// will set position to absolute only for extra-small, small and medium screens
+.position(.absolute, breakpoints: .xs, .s, m)
+```
+
+### Display
+
+Specifies how a certain HTML element should be displayed
+
+```swift
+// will set display to block
+.display(.block)
+
+// will set display to block only for extra-small, small and medium screens
+.display(.block, breakpoints: .xs, .s, m)
+```
+
+### Visibility
+
+Specifies whether or not an element is visible
+
+```swift
+// will set visibility to visible
+.visibility(.visible)
+
+// will set visibility to hidden only for extra-small, small and medium screens
+.visibility(.hidden, breakpoints: .xs, .s, m)
+```
+
+### Opacity
+
+Sets the opacity level for an element
+
+```swift
+// will set opacity to 0.8
+.opacity(0.8)
+
+// will set opacity to 0.5 only for extra-small, small and medium screens
+.opacity(0.5, breakpoints: .xs, .s, m)
+```
diff --git a/Sources/Autolayout/Autolayout.swift b/Sources/Autolayout/Autolayout.swift
index 198b5b8..24e6ecd 100644
--- a/Sources/Autolayout/Autolayout.swift
+++ b/Sources/Autolayout/Autolayout.swift
@@ -8,92 +8,23 @@
import Web
import ResizeObserverAPI
+let _autolayout = Autolayout()
+
public class Autolayout {
- // MARK: Storage keys
+ let stylesheet = Stylesheet().id("autolayout_styles")
+ var ruleIndexCache: [String: Int] = [:]
- struct ResizeObserverStorageKey: StorageKey {
- typealias Value = ResizeObserver
- }
-
- struct StoredConstraintStorageKey: StorageKey {
- typealias Value = Set
- }
-
- class StoredConstraint: Equatable, Hashable, CustomStringConvertible {
- typealias Handler = (Rect) -> Void
-
- let destinationView: BaseElement
- let constraint: Constraint
- var handlers: [Handler]
-
- init (destinationView: BaseElement, constraint: Constraint, handlers: [Handler]) {
- self.destinationView = destinationView
- self.constraint = constraint
- self.handlers = handlers
- }
-
- // Equatable
-
- static func == (lhs: Autolayout.StoredConstraint, rhs: Autolayout.StoredConstraint) -> Bool {
- lhs.destinationView == rhs.destinationView && lhs.constraint == rhs.constraint
- }
-
- // Hashable
-
- public func hash(into hasher: inout Hasher) {
- hasher.combine(destinationView)
- hasher.combine(constraint)
- }
-
- public var description: String {
- "#\(destinationView.properties._id)/\(constraint.rawValue)"
- }
+ fileprivate init () {
+ WebApp.shared.document.head.appendChild(stylesheet)
}
// MARK: Constraints
- enum Constraint: String {
- case widthToWidthOfView
- case widthToHeightOfView
- case heightToHeightOfView
- case heightToWidthOfView
- case leftToLeftOfView
- case leftToRightOfView
- case leftToCenterOfView
- case rightToRightOfView
- case rightToLeftOfView
- case rightToCenterOfView
- case topToTopOfView
- case topToBottomOfView
- case topToCenterOfView
- case bottomToBottomOfView
- case bottomToTopOfView
- case bottomToCenterOfView
- case widthToSuperview
- case heightToSuperview
- case topToSuperview
- case bottomToSuperview
- case leadingToSuperview
- case trailingToSuperview
- case leftToSuperview
- case rightToSuperview
- case centerXInSuperview
- case centerXToLeftOfView
- case centerXToRightOfView
- case centerXToCenterXOfView
- case centerYInSuperview
- case centerYToTopOfView
- case centerYToBottomOfView
- case centerYToCenterYOfView
- }
-
enum ConstraintAttribute: String {
case left
case right
case top
case bottom
- case leading
- case trailing
case width
case height
case centerX
@@ -103,32 +34,23 @@ public class Autolayout {
// MARK: Constraint sides
public enum ConstraintCXSide {
- case x
- case leading, left
- case trailing, right
- case centerX
+ case left, right, center
var side: ConstraintAttribute {
switch self {
- case .x: return .centerX
- case .leading: return .leading
case .left: return .left
- case .trailing: return .trailing
case .right: return .right
- case .centerX: return .centerX
+ case .center: return .centerX
}
}
}
public enum ConstraintCYSide {
- case y
- case top, bottom
- case centerY
+ case top, bottom, center
var side: ConstraintAttribute {
switch self {
- case .y: return .centerY
case .top: return .top
case .bottom: return .bottom
- case .centerY: return .centerY
+ case .center: return .centerY
}
}
}
@@ -144,1558 +66,1314 @@ public class Autolayout {
}
public enum ConstraintXSide {
- case leading, left, trailing, right, centerX
+ case left, right, center
var side: ConstraintAttribute {
switch self {
- case .leading: return .leading
case .left: return .left
- case .trailing: return .trailing
case .right: return .right
- case .centerX: return .centerX
+ case .center: return .centerX
}
}
}
public enum ConstraintYSide {
- case top, bottom, centerY
+ case top, bottom, center
var side: ConstraintAttribute {
switch self {
case .top: return .top
case .bottom: return .bottom
- case .centerY: return .centerY
+ case .center: return .centerY
}
}
}
}
-extension BaseElement {
- var resizeObserver: ResizeObserver? {
- get { self.storage.get(Autolayout.ResizeObserverStorageKey.self) }
- set { self.storage.set(Autolayout.ResizeObserverStorageKey.self, to: newValue) }
- }
- var storedConstraints: Set {
- get { self.storage.get(Autolayout.StoredConstraintStorageKey.self) ?? .init() }
- set { self.storage.set(Autolayout.StoredConstraintStorageKey.self, to: newValue) }
- }
+extension MediaRule.MediaType {
+ /// Extra small `<576px`
+ public static var extraSmall: MediaRule.MediaType { .init(.all.maxWidth(575.px), label: "xs") }
- // MARK: Relative
+ /// Small `≥576px` and `<768px`
+ public static var small: MediaRule.MediaType { .init(.all.minWidth(576.px).maxWidth(767.px), label: "s") }
- private func setupConstraint(_ constraint: Autolayout.Constraint, for view: BaseElement, _ handler: @escaping (Rect) -> Void) {
- let resizeObserver = view.resizeObserver ?? ResizeObserver().observe(view)
-
- if let member = view.storedConstraints.first(where: { $0.destinationView == view && $0.constraint == constraint }) {
-// print("❗️storedConstraints already contains member #\(view.properties._id) set handlersForView #\(self.properties._id)")
- member.handlers.append(handler)
- } else {
- let member = Autolayout.StoredConstraint(destinationView: view, constraint: constraint, handlers: [handler])
- view.storedConstraints.insert(member)
- }
-
-// print("#\(view.properties._id) set handlersForView #\(self.properties._id)")
-
- resizeObserver.setObserver(for: self) { entries, observer in
- let rect: Rect = entries.first?.contentRect ?? .zero
- entries.forEach { entrie in
- print("entrie(#\(entrie.target.id.string ?? "nnn")) entrie.contentRect: \(entrie.contentRect)")
- }
-// print("#\(self?.properties._id ?? "nnn") #\(view.properties._id).resizeObserver fired, storedConstraints.count: \(view.storedConstraints.count), values: \(view.storedConstraints.map { $0.description })")
- view.storedConstraints.forEach {
-// print("#\(self?.properties._id ?? "nnn") calling #\($0.destinationView.properties._id) handler for \($0.constraint)")
- $0.handlers.forEach { $0(rect) }
- }
+ /// Medium `≥768px` and `<992px`
+ public static var medium: MediaRule.MediaType { .init(.all.minWidth(768.px).maxWidth(991.px), label: "m") }
+
+ /// Large `≥992px` and `<1200px`
+ public static var large: MediaRule.MediaType { .init(.all.minWidth(992.px).maxWidth(1199.px), label: "l") }
+
+ /// Large `≥1200px` and `<1400px`
+ public static var extraLarge: MediaRule.MediaType { .init(.all.minWidth(1200.px).maxWidth(1399.px), label: "xl") }
+
+ /// Large `≥1400px`
+ public static var extraExtraLarge: MediaRule.MediaType { .init(.all.minWidth(1400.px), label: "xxl") }
+}
+
+extension BaseElement {
+ private func _getClassName(_ methodName: String, breakpoints: [MediaRule.MediaType]) -> String {
+ let media = breakpoints.map {
+ String($0.description.map { [" ", ",", "(", ")", "-", ":", "."].contains($0) ? "_" : $0 })
+ }.joined(separator: "_")
+ return properties._id + "_" + methodName + (media.isEmpty ? "" : "_" + media)
+ }
+
+ private func _setRule(
+ _ className: String,
+ breakpoints: [MediaRule.MediaType],
+ _ rulesHandler: @escaping (CSSRule) -> CSSRule
+ ) {
+ if let indexToDelete = _autolayout.ruleIndexCache[className] {
+ _autolayout.stylesheet.deleteRule(indexToDelete)
}
- view.resizeObserver = resizeObserver
- if isInDOM {
- handler(view.boundingClientRect)
+ let index: Int
+ if breakpoints.count == 0 {
+ let rule = rulesHandler(CSSRule(Class(stringLiteral: className).pointer))
+ index = _autolayout.stylesheet.addRule(rule)
} else {
- onDidAddToDOM { handler(view.boundingClientRect) }
+ let cssRule = CSSRule(Class(stringLiteral: className).pointer)
+ let mediaRule = MediaRule(breakpoints) { cssRule }
+ let rule = rulesHandler(cssRule)
+ index = _autolayout.stylesheet.addMediaRule(mediaRule)
}
- view.onDidAddToDOM { handler(view.boundingClientRect) }
- }
-
- // MARK: - Relative
-
- private func _createRelative(
- value: State,
- multiplier: Double,
- attribute1: Autolayout.ConstraintAttribute,
- attribute2: Autolayout.ConstraintAttribute,
- destinationView: BaseElement
- ) -> Self {
- var printedWarning: [Autolayout.Constraint: Bool] = [:]
- func _setupSides(
- currentAbsolute: @escaping () -> Double?,
- currentOffset: @escaping () -> Double?,
- destinationAbsolute: @escaping () -> Double?,
- constraint: Autolayout.Constraint,
- trackSelf: Bool = false,
- newValueHandler: @escaping (UnitValue) -> Void
- ) {
- func _setup(_ value: UnitValue) {
-// print("_setupSides #\(self.properties._id) -> #\(destinationView.properties._id) \(constraint)")
- let updateHandler: (Rect) -> Void = { [weak self] rect in
- guard let self = self else { return }
- let _position = Window.shared.getComputedStyle(self, for: CSS.PropertyType.position.rawValue)
- guard let p = _position, p != "", p != "static" else {
- if printedWarning[constraint] != true {
- printedWarning[constraint] = true
- #if DEBUG
- Console.warning("⚠️🎨 \(attribute1.rawValue.capitalized) to \(attribute2.rawValue) constraint doesn't work with static position (#\(self.properties._id).\(attribute1.rawValue) to #\(destinationView.properties._id).\(attribute2.rawValue))")
- #endif
- }
- return
- }
-// if (p != "absolute" || p != "fixed"), [
-// .topToSuperview, .topToTopOfView, .topToBottomOfView, .topToCenterOfView,
-// .bottomToSuperview, .bottomToTopOfView, .bottomToBottomOfView, .bottomToCenterOfView,
-// .leftToSuperview, .leftToLeftOfView, .leftToRightOfView, .leftToCenterOfView,
-// .rightToSuperview, .rightToLeftOfView, .rightToRightOfView, .rightToCenterOfView,
-// .leadingToSuperview,
-// .trailingToSuperview
-// ].contains(constraint) {
-// self.position(.absolute)
-// #if DEBUG
-// Console.warning("⚠️🎨 \(attribute1.rawValue.capitalized) to \(attribute2.rawValue) automatically fixed position to absolute (#\(self.properties._id).\(attribute1.rawValue) to #\(destinationView.properties._id).\(attribute2.rawValue))")
-// #endif
-// }
- let currentAbsolute = 0.0//currentAbsolute() ?? 0
- let currentOffset = 0.0//currentOffset() ?? 0
- let destinationAbsolute = 0.0//destinationAbsolute() ?? 0
- printedWarning[constraint] = false
- var diff: Double = 0
- let newValue: Double
- if currentAbsolute > destinationAbsolute {
- diff = currentAbsolute - destinationAbsolute
- newValue = currentOffset - diff
- } else if currentAbsolute < destinationAbsolute {
- diff = destinationAbsolute - currentAbsolute
- newValue = currentOffset + diff
- } else {
- newValue = currentOffset
- }
-// print("#\(self.properties._id) -> #\(destinationView.properties._id) cAbsolute: \(currentAbsolute) cOffset: \(currentOffset) dAbsolute: \(destinationAbsolute)")
- newValueHandler(.init(newValue * multiplier + value.value, .px))
- }
- setupConstraint(constraint, for: destinationView, updateHandler)
- if trackSelf {
- setupConstraint(constraint, for: self, updateHandler)
- }
- }
- value.listen {
- _setup(.init($0.value.doubleValue, $0.unit))
- }
- _setup(.init(value.wrappedValue.value.doubleValue, value.wrappedValue.unit))
- }
- switch attribute1 {
- case .left, .leading:
- switch attribute2 {
- case .left, .leading:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .leftToLeftOfView
- ) { [weak self] in
-// print("left to left called")
- self?.left($0)
- }
- case .right, .trailing:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteRight },
- constraint: .leftToRightOfView
- ) { [weak self] in
-// print("left to right called")
- self?.left($0)
- }
-// case .leading: // TODO
-// break
-// case .trailing: // TODO
-// break
- case .centerX:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .leftToCenterOfView
- ) { [weak self] in
-// print("left to centerX called")
- guard let self = self else { return }
- self.left(($0.value + (destinationView.clientWidth / 2)).px)
- }
- default:
- break
- }
- case .right, .trailing:
- switch attribute2 {
- case .left, .leading:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .rightToLeftOfView,
- trackSelf: true
- ) { [weak self] in
-// print("right to left called")
- guard let self = self else { return }
- self.left(($0.value - self.clientWidth).px)
- }
- case .right, .trailing:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .rightToRightOfView,
- trackSelf: true
- ) { [weak self] in
-// print("right to right called")
- guard let self = self else { return }
- if destinationView.clientWidth > self.clientWidth {
- self.left(($0.value + (destinationView.clientWidth - self.clientWidth)).px)
- } else if self.clientWidth > destinationView.clientWidth {
- self.left(($0.value + (self.clientWidth - destinationView.clientWidth)).px)
- } else {
- self.left($0)
- }
- }
-// case .leading: // TODO
-// break
-// case .trailing: // TODO
-// break
- case .centerX:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .rightToCenterOfView,
- trackSelf: true
- ) { [weak self] in
-// print("right to centerX called")
- guard let self = self else { return }
- let destinationCenterPoint = ($0.value + (destinationView.clientWidth / 2))
- let currentRightPoint = $0.value + self.clientWidth
- let diff = currentRightPoint - destinationCenterPoint
- if destinationCenterPoint > currentRightPoint {
- self.left(($0.value + (destinationCenterPoint - currentRightPoint)).px)
- } else if destinationCenterPoint < currentRightPoint {
- self.left(($0.value - (currentRightPoint - destinationCenterPoint)).px)
- } else {
- self.left($0)
- }
- }
- default:
- break
- }
- case .top:
- switch attribute2 {
- case .top:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .topToTopOfView
- ) { [weak self] in
-// print("top to top called")
- self?.top($0)
- }
- case .bottom:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteBottom },
- constraint: .topToBottomOfView
- ) { [weak self] in
-// print("top to bottom called")
- self?.top($0)
- }
- case .centerY:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .topToCenterOfView
- ) { [weak self] in
-// print("top to centerY called")
- guard let self = self else { return }
- self.top(($0.value + (destinationView.clientHeight / 2)).px)
- }
- default:
- break
- }
- case .bottom:
- switch attribute2 {
- case .top:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .bottomToTopOfView,
- trackSelf: true
- ) { [weak self] in
-// print("bottom to top called")
- guard let self = self else { return }
- self.top(($0.value - self.clientHeight).px)
- }
- case .bottom:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .bottomToBottomOfView,
- trackSelf: true
- ) { [weak self] in
-// print("bottom to bottom called")
- guard let self = self else { return }
- if destinationView.clientHeight > self.clientHeight {
- self.top(($0.value + (destinationView.clientHeight - self.clientHeight)).px)
- } else if self.clientHeight > destinationView.clientHeight {
- self.top(($0.value + (self.clientHeight - destinationView.clientHeight)).px)
- } else {
- self.top($0)
- }
- }
- case .centerY:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .bottomToCenterOfView,
- trackSelf: true
- ) { [weak self] in
-// print("bottom to centerY called")
- guard let self = self else { return }
- let destinationCenterPoint = ($0.value + (destinationView.clientHeight / 2))
- let currentRightPoint = $0.value + self.clientHeight
- let diff = currentRightPoint - destinationCenterPoint
- if destinationCenterPoint > currentRightPoint {
- self.top(($0.value + (destinationCenterPoint - currentRightPoint)).px)
- } else if destinationCenterPoint < currentRightPoint {
- self.top(($0.value - (currentRightPoint - destinationCenterPoint)).px)
- } else {
- self.top($0)
- }
- }
- default:
- break
- }
-// case .leading: // TODO
-// switch attribute2 {
-// case .left:
-// break
-// case .right:
-// break
-// case .leading:
-// break
-// case .trailing:
-// break
-// case .centerX:
-// break
-// default:
-// break
-// }
-// case .trailing: // TODO
-// switch attribute2 {
-// case .left:
-// break
-// case .right:
-// break
-// case .leading:
-// break
-// case .trailing:
-// break
-// case .centerX:
-// break
-// default:
-// break
-// }
- case .width:
- switch attribute2 {
- case .width:
- func setup(_ value: UnitValue) {
- let updateHandler: (Rect) -> Void = { [weak self] rect in
-// print("height to width called")
- self?.width(UnitValue(rect.width * multiplier + value.value, .px))
- }
- setupConstraint(.widthToWidthOfView, for: destinationView, updateHandler)
- }
- value.listen {
- setup(.init($0.value.doubleValue, $0.unit))
- }
- setup(.init(value.wrappedValue.value.doubleValue, value.wrappedValue.unit))
- case .height:
- func setup(_ value: UnitValue) {
- let updateHandler: (Rect) -> Void = { [weak self] rect in
-// print("width to height called")
- self?.width(UnitValue(rect.height * multiplier + value.value, .px))
- }
- setupConstraint(.widthToHeightOfView, for: destinationView, updateHandler)
- }
- value.listen {
- setup(.init($0.value.doubleValue, $0.unit))
- }
- setup(.init(value.wrappedValue.value.doubleValue, value.wrappedValue.unit))
- default:
- break
- }
- case .height:
- switch attribute2 {
- case .width:
- func setup(_ value: UnitValue) {
- let updateHandler: (Rect) -> Void = { [weak self] rect in
-// print("height to width called")
- self?.height(UnitValue(rect.width * multiplier + value.value, .px))
- }
- setupConstraint(.heightToWidthOfView, for: destinationView, updateHandler)
- }
- value.listen {
- setup(.init($0.value.doubleValue, $0.unit))
- }
- setup(.init(value.wrappedValue.value.doubleValue, value.wrappedValue.unit))
- case .height:
- func setup(_ value: UnitValue) {
- let updateHandler: (Rect) -> Void = { [weak self] rect in
-// print("height to height called")
- self?.visibility(.hidden)
- self?.height(UnitValue(rect.height * multiplier + value.value, .px))
- self?.visibility(.visible)
- }
- setupConstraint(.heightToHeightOfView, for: destinationView, updateHandler)
- }
- value.listen {
- setup(.init($0.value.doubleValue, $0.unit))
- }
- setup(.init(value.wrappedValue.value.doubleValue, value.wrappedValue.unit))
- default:
- break
- }
- case .centerX:
-// print("create centerX 1")
- switch attribute2 {
- case .left, .leading:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .centerXToLeftOfView,
- trackSelf: true
- ) { [weak self] in
-// print("centerX to left called")
- guard let self = self else { return }
- self.left(($0.value - (self.clientWidth / 2)).px)
- }
- case .right, .trailing:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteRight },
- constraint: .centerXToRightOfView,
- trackSelf: true
- ) { [weak self] in
-// print("centerX to right called")
- guard let self = self else { return }
- self.left(($0.value - (self.clientWidth / 2)).px)
- }
-// case .leading: // TODO
-// break
-// case .trailing: // TODO
-// break
- case .centerX:
-// print("centerX(#\(self.properties._id)) to centerX(#\(destinationView.properties._id)) case 1")
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteLeft },
- currentOffset: { [weak self] in self?.offsetLeft },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteLeft },
- constraint: .centerXToCenterXOfView,
- trackSelf: true
- ) { [weak self] in
-// print("centerX to centerX called")
-// print("centerX(#\(self?.properties._id ?? "nnn")) to centerX(#\(destinationView.properties._id)) case 2")
- guard let self = self else { return }
-// print("centerX(#\(self.properties._id)) to centerX(#\(destinationView.properties._id)) case 3")
- let currentCenter = (self.clientWidth / 2)
- let destinationCenter = (destinationView.clientWidth / 2)
- if currentCenter > destinationCenter {
-// print("centerX(#\(self.properties._id)) to centerX(#\(destinationView.properties._id)) case 4.1")
- self.left(($0.value + destinationCenter - currentCenter).px)
- } else if destinationCenter > currentCenter {
-// print("centerX(#\(self.properties._id)) to centerX(#\(destinationView.properties._id)) case 4.2")
- self.left(($0.value + destinationCenter - currentCenter).px)
- } else {
-// print("centerX(#\(self.properties._id)) to centerX(#\(destinationView.properties._id)) case 4.3")
- self.left($0)
- }
- }
- default:
- break
- }
- case .centerY:
- switch attribute2 {
- case .top:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .centerYToTopOfView,
- trackSelf: true
- ) { [weak self] in
-// print("centerY to top called")
- guard let self = self else { return }
- self.top(($0.value - (self.clientHeight / 2)).px)
- }
- case .bottom:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteBottom },
- constraint: .centerYToBottomOfView,
- trackSelf: true
- ) { [weak self] in
-// print("centerY to bottom called")
- guard let self = self else { return }
- self.top(($0.value - (self.clientHeight / 2)).px)
- }
- case .centerY:
- _setupSides(
- currentAbsolute: { [weak self] in self?.absoluteTop },
- currentOffset: { [weak self] in self?.offsetTop },
- destinationAbsolute: { [weak destinationView] in destinationView?.absoluteTop },
- constraint: .centerYToCenterYOfView,
- trackSelf: true
- ) { [weak self] in
- print("centerY to centerY called: destinationView.clientHeight: \(destinationView.clientHeight)")
- guard let self = self else { return }
- let currentCenter = (self.clientHeight / 2)
- let destinationCenter = (destinationView.clientHeight / 2)
- if currentCenter > destinationCenter {
- print("centerY to centerY case 1")
- self.top(($0.value + destinationCenter - currentCenter).px)
- } else if destinationCenter > currentCenter {
- print("centerY to centerY case 2")
- self.top(($0.value + destinationCenter - currentCenter).px)
- } else {
- print("centerY to centerY case 3")
- self.top($0)
- }
- }
- default:
- break
- }
+ if index >= 0 {
+ _autolayout.ruleIndexCache[className] = index
}
- return self
}
- // MARK: - top
+ // MARK: - Edges
- /// Has no effect with **position: static**
+ /// Convenience setter for all sides: top, right, bottom, left
@discardableResult
- public func top(
- to side: Autolayout.ConstraintYSide,
- of view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ _ value: U = 0.px,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .top,
- attribute2: side.side,
- destinationView: view)
+ top(value)
+ .left(value)
+ .right(UnitValue(value.value.doubleValue * (-1), value.unit))
+ .bottom(UnitValue(value.value.doubleValue * (-1), value.unit))
}
- /// By default to `bottom` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for all sides: top, right, bottom, left
@discardableResult
- public func top(
- to view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ _ value: U = 0.px,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- top(to: .bottom, of: view, state, multiplier: multiplier)
+ edges(value, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Convenience setter for all sides: top, right, bottom, left
+ public func edges(
+ _ value: State,
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ top(value)
+ .left(value)
+ .right(value.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
+ .bottom(value.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
+ }
+
+ /// Convenience setter for all sides: top, right, bottom, left
+ public func edges(
+ _ value: State,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ edges(value, breakpoints: breakpoints)
+ }
+
+ /// Convenience setter for horizontal sides: left and right
@discardableResult
- public func top(
- to side: Autolayout.ConstraintYSide,
- of view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ h: U,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- top(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ left(h)
+ .right(UnitValue(h.value.doubleValue * (-1), h.unit))
}
- /// By default to `bottom` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right
@discardableResult
- public func top(
- to view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ h: U,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- top(to: .bottom, of: view, value)
+ edges(h: h, breakpoints: breakpoints)
}
- // MARK: - leading
+ /// Convenience setter for horizontal sides: left and right
+ @discardableResult
+ public func edges(
+ h: State,
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ left(h)
+ .right(h.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
+ }
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right
@discardableResult
- public func leading(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ h: State,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .leading,
- attribute2: side.side,
- destinationView: view)
+ edges(h: h, breakpoints: breakpoints)
}
- /// By default to `trailing` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for vertical sides: top and bottom
@discardableResult
- public func leading(
- to view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ v: U,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- leading(to: .trailing, of: view, state, multiplier: multiplier)
+ top(v)
+ .bottom(UnitValue(v.value.doubleValue * (-1), v.unit))
}
- /// Has no effect with **position: static**
+ /// Convenience setter for vertical sides: top and bottom
@discardableResult
- public func leading(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ v: U,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- leading(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ edges(v: v, breakpoints: breakpoints)
}
- /// By default to `trailing` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for vertical sides: top and bottom
@discardableResult
- public func leading(
- to view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ v: State,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- leading(to: .trailing, of: view, value)
+ top(v)
+ .bottom(v.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
}
- // MARK: - left
+ /// Convenience setter for vertical sides: top and bottom
+ @discardableResult
+ public func edges(
+ v: State,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ edges(v: v, breakpoints: breakpoints)
+ }
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right, and vertical sides: top and bottom
@discardableResult
- public func left(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ h: H,
+ v: V,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .left,
- attribute2: side.side,
- destinationView: view)
+ top(v)
+ .left(h)
+ .right(UnitValue(h.value.doubleValue * (-1), h.unit))
+ .bottom(UnitValue(v.value.doubleValue * (-1), v.unit))
}
- /// By default to `right` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right, and vertical sides: top and bottom
@discardableResult
- public func left(
- to view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func edges(
+ h: H,
+ v: V,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- left(to: .right, of: view, state, multiplier: multiplier)
+ edges(h: h, v: v, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right, and vertical sides: top and bottom
@discardableResult
- public func left(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ h: State,
+ v: State,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- left(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ top(v)
+ .left(h)
+ .right(h.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
+ .bottom(v.map { UnitValue($0.value.doubleValue * (-1), $0.unit) })
}
- /// By default to `right` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Convenience setter for horizontal sides: left and right, and vertical sides: top and bottom
@discardableResult
- public func left(
- to view: BaseElement,
- _ value: U = 0.px
+ public func edges(
+ h: State,
+ v: State,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- left(to: .right, of: view, value)
+ edges(h: h, v: v, breakpoints: breakpoints)
}
- // MARK: - trailing
+ // MARK: - Top
+
+ /// Specifies the top position to the first parent element with relative position
+ @discardableResult
+ public func top(
+ _ state: State,
+ to side: State = .init(wrappedValue: .top),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("top", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Autolayout.ConstraintYSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .top: percentage = 0
+ case .center: percentage = 50
+ case .bottom: percentage = 100
+ }
+ if value.value.doubleValue > 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("top", value.description + important)
+ } else {
+ return rule.custom("top", "calc(\(percentage)% + \(value.description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("top", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("top", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ }
+ }
+ } else if value.value.doubleValue < 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("top", value.description + important)
+ } else {
+ return rule.custom("top", "calc(\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("top", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("top", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ }
+ }
+ } else {
+ return rule.custom("top", "0px" + important)
+ }
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ }
+ return self
+ }
- /// Has no effect with **position: static**
+ /// Specifies the top position to the first parent element with relative position
@discardableResult
- public func trailing(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
+ public func top(
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .top),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ top(state, to: side, multiplier: multiplier, breakpoints: breakpoints)
+ }
+
+ /// Specifies the top position to the first parent element with relative position
+ @discardableResult
+ public func top(
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintYSide = .top,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .trailing,
- attribute2: side.side,
- destinationView: view)
+ top(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `leading` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the top position to the first parent element with relative position
@discardableResult
- public func trailing(
- to view: BaseElement,
+ public func top(
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintYSide = .top,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ top(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
+ }
+
+ // MARK: - Left
+
+ /// Specifies the left position to the first parent element with relative position
+ @discardableResult
+ public func left(
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .left),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("left", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Autolayout.ConstraintXSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .left: percentage = 0
+ case .center: percentage = 50
+ case .right: percentage = 100
+ }
+ if value.value.doubleValue > 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("left", value.description + important)
+ } else {
+ return rule.custom("left", "calc(\(percentage)% + \(value.description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("left", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("left", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ }
+ }
+ } else if value.value.doubleValue < 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("left", value.description + important)
+ } else {
+ return rule.custom("left", "calc(\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("left", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("left", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ }
+ }
+ } else {
+ return rule.custom("left", "0px" + important)
+ }
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ }
+ return self
+ }
+
+ /// Specifies the left position to the first parent element with relative position
+ @discardableResult
+ public func left(
+ _ state: State,
+ to side: State = .init(wrappedValue: .left),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- trailing(to: .leading, of: view, state, multiplier: multiplier)
+ left(state, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies the left position to the first parent element with relative position
@discardableResult
- public func trailing(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ value: U = 0.px
+ public func left(
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintXSide = .left,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- trailing(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ left(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `leading` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the left position to the first parent element with relative position
@discardableResult
- public func trailing(
- to view: BaseElement,
- _ value: U = 0.px
+ public func left(
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintXSide = .left,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- trailing(to: .leading, of: view, value)
+ left(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - right
+ // MARK: - Right
- /// Has no effect with **position: static**
+ /// Specifies the right position to the first parent element with relative position
@discardableResult
public func right(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
_ state: State,
- multiplier: Double = 1
- ) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .right,
- attribute2: side.side,
- destinationView: view)
+ to side: State = .init(wrappedValue: .right),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("right", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Autolayout.ConstraintXSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .left: percentage = 100
+ case .center: percentage = 50
+ case .right: percentage = 0
+ }
+ if value.value.doubleValue > 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("right", value.description + important)
+ } else {
+ return rule.custom("right", "calc(\(percentage)% + \(value.description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("right", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("right", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ }
+ }
+ } else if value.value.doubleValue < 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("right", value.description + important)
+ } else {
+ return rule.custom("right", "calc(\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("right", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("right", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ }
+ }
+ } else {
+ return rule.custom("right", "0px" + important)
+ }
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ }
+ return self
}
- /// By default to `left` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the right position to the first parent element with relative position
@discardableResult
public func right(
- to view: BaseElement,
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .right),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- right(to: .left, of: view, state, multiplier: multiplier)
+ right(state, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies the right position to the first parent element with relative position
@discardableResult
public func right(
- to side: Autolayout.ConstraintXSide,
- of view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintXSide = .right,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- right(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ right(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `left` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the right position to the first parent element with relative position
@discardableResult
public func right(
- to view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintXSide = .right,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- right(to: .left, of: view, value)
+ right(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - bottom
+ // MARK: - Bottom
- /// Has no effect with **position: static**
+ /// Specifies the bottom position to the first parent element with relative position
@discardableResult
public func bottom(
- to side: Autolayout.ConstraintYSide,
- of view: BaseElement,
_ state: State,
- multiplier: Double = 1
- ) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .bottom,
- attribute2: side.side,
- destinationView: view)
+ to side: State = .init(wrappedValue: .bottom),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("bottom", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Autolayout.ConstraintYSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .top: percentage = 100
+ case .center: percentage = 50
+ case .bottom: percentage = 0
+ }
+ if value.value.doubleValue > 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("bottom", value.description + important)
+ } else {
+ return rule.custom("bottom", "calc(\(percentage)% + \(value.description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("bottom", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("bottom", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ }
+ }
+ } else if value.value.doubleValue < 0 {
+ if multiplier == 1 {
+ if percentage == 0 {
+ return rule.custom("bottom", value.description + important)
+ } else {
+ return rule.custom("bottom", "calc(\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description))" + important)
+ }
+ } else {
+ if percentage == 0 {
+ return rule.custom("bottom", "calc(\(value.description) * \(multiplier))" + important)
+ } else {
+ return rule.custom("bottom", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ }
+ }
+ } else {
+ return rule.custom("bottom", "0px" + important)
+ }
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ }
+ return self
}
- /// By default to `top` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the bottom position to the first parent element with relative position
@discardableResult
public func bottom(
- to view: BaseElement,
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .bottom),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- bottom(to: .top, of: view, state, multiplier: multiplier)
+ bottom(state, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies the bottom position to the first parent element with relative position
@discardableResult
public func bottom(
- to side: Autolayout.ConstraintYSide,
- of view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintYSide = .bottom,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- bottom(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ bottom(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `top` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the bottom position to the first parent element with relative position
@discardableResult
public func bottom(
- to view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ to side: Autolayout.ConstraintYSide = .bottom,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- bottom(to: .top, of: view, value)
+ bottom(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - center x
+ // MARK: - Center X
- /// Has no effect with **position: static**
+ /// Specifies the horizontal center position to the first parent element with relative position
@discardableResult
public func centerX(
- to side: Autolayout.ConstraintCXSide,
- of view: BaseElement,
_ state: State,
- multiplier: Double = 1
- ) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .centerX,
- attribute2: side.side,
- destinationView: view)
+ to side: State = .init(wrappedValue: .center),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("left", breakpoints: breakpoints)
+ let translationClassName = _getClassName("translate", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className), .init(stringLiteral: translationClassName))
+ let perform: (U, Autolayout.ConstraintCXSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .left: percentage = 0
+ case .center: percentage = 50
+ case .right: percentage = 100
+ }
+ if value.value.doubleValue > 0 {
+ return rule.custom("left", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ } else if value.value.doubleValue < 0 {
+ return rule.custom("left", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ } else {
+ if multiplier == 1 {
+ return rule.custom("left", "\(percentage)%" + important)
+ } else {
+ return rule.custom("left", "calc(\(percentage)% * \(multiplier))" + important)
+ }
+ }
+ }
+ }
+ let performTranslate: () -> Void = { [weak self] in
+ self?._setRule(translationClassName, breakpoints: breakpoints) { rule in
+ rule.custom("--translate-x", "-50%").custom("translate", "var(--translate-x, 0) var(--translate-y, 0)" + important)
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ performTranslate()
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ performTranslate()
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ performTranslate()
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ performTranslate()
+ }
+ return self
}
- /// By default to `centerX` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the horizontal center position to the first parent element with relative position
@discardableResult
public func centerX(
- to view: BaseElement,
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .center),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerX(to: .x, of: view, state, multiplier: multiplier)
+ centerX(state, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies the horizontal center position to the first parent element with relative position
@discardableResult
public func centerX(
- to side: Autolayout.ConstraintCXSide,
- of view: BaseElement,
_ value: U = 0.px,
- multiplier: Double = 1
+ to side: Autolayout.ConstraintCXSide = .center,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerX(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: multiplier)
+ centerX(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `centerX` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the horizontal center position to the first parent element with relative position
@discardableResult
public func centerX(
- to view: BaseElement,
_ value: U = 0.px,
- multiplier: Double = 1
+ to side: Autolayout.ConstraintCXSide = .center,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerX(to: .x, of: view, value, multiplier: multiplier)
+ centerX(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - center y
+ // MARK: - Center Y
- /// Has no effect with **position: static**
+ /// Specifies the vertical center position to the first parent element with relative position
@discardableResult
public func centerY(
- to side: Autolayout.ConstraintCYSide,
- of view: BaseElement,
_ state: State,
- multiplier: Double = 1
- ) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .centerY,
- attribute2: side.side,
- destinationView: view)
+ to side: State = .init(wrappedValue: .center),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("top", breakpoints: breakpoints)
+ let translationClassName = _getClassName("translate", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className), .init(stringLiteral: translationClassName))
+ let perform: (U, Autolayout.ConstraintCYSide, Double) -> Void = { [weak self] value, side, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let percentage: Int
+ switch side {
+ case .top: percentage = 0
+ case .center: percentage = 50
+ case .bottom: percentage = 100
+ }
+ if value.value.doubleValue > 0 {
+ return rule.custom("top", "calc((\(percentage)% + \(value.description)) * \(multiplier))" + important)
+ } else if value.value.doubleValue < 0 {
+ return rule.custom("top", "calc((\(percentage)% - \(UnitValue(-value.value.doubleValue, value.unit).description)) * \(multiplier))" + important)
+ } else {
+ if multiplier == 1 {
+ return rule.custom("top", "\(percentage)%" + important)
+ } else {
+ return rule.custom("top", "calc(\(percentage)% * \(multiplier))" + important)
+ }
+ }
+ }
+ }
+ let performTranslate: () -> Void = { [weak self] in
+ self?._setRule(translationClassName, breakpoints: breakpoints) { rule in
+ rule.custom("--translate-y", "-50%").custom("translate", "var(--translate-x, 0) var(--translate-y, 0)" + important)
+ }
+ }
+ perform(state.wrappedValue, side.wrappedValue, multiplier.wrappedValue)
+ performTranslate()
+ state.listen {
+ perform($0, side.wrappedValue, multiplier.wrappedValue)
+ performTranslate()
+ }
+ side.listen {
+ perform(state.wrappedValue, $0, multiplier.wrappedValue)
+ performTranslate()
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, side.wrappedValue, $0)
+ performTranslate()
+ }
+ return self
}
- /// By default to `centerY` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the vertical center position to the first parent element with relative position
@discardableResult
public func centerY(
- to view: BaseElement,
_ state: State,
- multiplier: Double = 1
+ to side: State = .init(wrappedValue: .center),
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerY(to: .y, of: view, state, multiplier: multiplier)
+ centerY(state, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies the vertical center position to the first parent element with relative position
@discardableResult
public func centerY(
- to side: Autolayout.ConstraintCYSide,
- of view: BaseElement,
_ value: U = 0.px,
- multiplier: Double = 1
+ to side: Autolayout.ConstraintCYSide = .center,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerY(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: multiplier)
+ centerY(.init(wrappedValue: value), to: .init(wrappedValue: side), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `centerY` of destination view
- ///
- /// Has no effect with **position: static**
+ /// Specifies the vertical center position to the first parent element with relative position
@discardableResult
public func centerY(
- to view: BaseElement,
_ value: U = 0.px,
- multiplier: Double = 1
+ to side: Autolayout.ConstraintCYSide = .center,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerY(to: .y, of: view, value, multiplier: multiplier)
+ centerY(value, to: side, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - center both
+ // MARK: Center X+Y
- /// Has no effect with **position: static**
+ /// Specifies both vertical and horizontal center position to the first parent element with relative position
@discardableResult
public func center(
- to view: BaseElement,
- _ value: State,
- multiplier: Double = 1
+ _ state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerX(to: view, value, multiplier: multiplier)
- .centerY(to: view, value, multiplier: multiplier)
+ centerX(state, multiplier: multiplier, breakpoints: breakpoints)
+ .centerY(state, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies both vertical and horizontal center position to the first parent element with relative position
@discardableResult
public func center(
- to view: BaseElement,
- _ value: U
- ) -> Self {
- centerX(to: view, value).centerY(to: view, value)
- }
-
- /// Has no effect with **position: static**
- @discardableResult
- public func center(
- to view: BaseElement,
- x: State,
- y: State,
- multiplier: Double = 1
- ) -> Self {
- centerX(to: view, x, multiplier: multiplier)
- .centerY(to: view, y, multiplier: multiplier)
- }
-
- /// Has no effect with **position: static**
- @discardableResult
- public func center(
- to view: BaseElement,
- x: State,
- y: Y,
- multiplier: Double = 1
+ _ state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerX(to: view, x, multiplier: multiplier)
- .centerY(to: view, y, multiplier: multiplier)
+ center(state, multiplier: multiplier, breakpoints: breakpoints)
}
- /// Has no effect with **position: static**
+ /// Specifies both vertical and horizontal center position to the first parent element with relative position
@discardableResult
- public func center(
- to view: BaseElement,
- x: X,
- y: State,
- multiplier: Double = 1
+ public func center(
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerX(to: view, x, multiplier: multiplier)
- .centerY(to: view, y, multiplier: multiplier)
+ center(
+ .init(wrappedValue: value),
+ multiplier: .init(wrappedValue: multiplier),
+ breakpoints: breakpoints
+ )
}
- /// Has no effect with **position: static**
+ /// Specifies both vertical and horizontal center position to the first parent element with relative position
@discardableResult
- public func center(
- to view: BaseElement,
- x: X = 0.px,
- y: Y = 0.px
+ public func center(
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerX(to: view, x).centerY(to: view, y)
+ center(value, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - width
+ // MARK: - Width
+ /// Sets the width of an element
@discardableResult
public func width(
- to side: Autolayout.ConstraintDSide,
- of view: BaseElement,
_ state: State,
- multiplier: Double = 1
- ) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .width,
- attribute2: side.side,
- destinationView: view)
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let className = _getClassName("width", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Double) -> Void = { [weak self] value, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ return rule.custom("width", UnitValue(value.value.doubleValue, value.unit, important: breakpoints.count > 0).description)
+ }
+ }
+ perform(state.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, $0)
+ }
+ return self
}
- /// By default to `width` of destination view
+ /// Sets the width of an element
@discardableResult
public func width(
- to view: BaseElement,
_ state: State,
- multiplier: Double = 1
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- width(to: .width, of: view, state, multiplier: multiplier)
+ width(state, multiplier: multiplier, breakpoints: breakpoints)
}
+ /// Sets the width of an element
@discardableResult
public func width(
- to side: Autolayout.ConstraintDSide,
- of view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- width(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ width(.init(wrappedValue: value), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `width` of destination view
+ /// Sets the width of an element
@discardableResult
public func width(
- to view: BaseElement,
- _ value: U = 0.px
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- width(to: .width, of: view, value)
+ width(value, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - height
+ // MARK: - Width to parent
+ /// Sets the width of an element to fit first parent element with relative position
@discardableResult
- public func height(
- to side: Autolayout.ConstraintDSide,
- of view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func widthToParent(
+ extra state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- _createRelative(value: state,
- multiplier: multiplier,
- attribute1: .height,
- attribute2: side.side,
- destinationView: view)
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("width", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Double) -> Void = { [weak self] extra, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ if extra.value.doubleValue > 0 {
+ return rule.custom("width", "calc(\(100 * multiplier)% + \(extra.description))" + important)
+ } else if extra.value.doubleValue < 0 {
+ return rule.custom("width", "calc(\(100 * multiplier)% - \(extra.description))" + important)
+ } else {
+ return rule.custom("width", "\(100 * multiplier)%" + important)
+ }
+ }
+ }
+ perform(state.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, $0)
+ }
+ return self
}
- /// By default to `height` of destination view
+ /// Sets the width of an element to fit first parent element with relative position
@discardableResult
- public func height(
- to view: BaseElement,
- _ state: State,
- multiplier: Double = 1
+ public func widthToParent(
+ extra state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- height(to: .height, of: view, state, multiplier: multiplier)
+ widthToParent(extra: state, multiplier: multiplier, breakpoints: breakpoints)
}
+ /// Sets the width of an element to fit first parent element with relative position
@discardableResult
- public func height(
- to side: Autolayout.ConstraintDSide,
- of view: BaseElement,
- _ value: U = 0.px
+ public func widthToParent(
+ extra value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- height(to: side,
- of: view,
- .init(wrappedValue: value),
- multiplier: 1)
+ widthToParent(extra: .init(wrappedValue: value), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
- /// By default to `height` of destination view
+ /// Sets the width of an element to fit first parent element with relative position
@discardableResult
- public func height(
- to view: BaseElement,
- _ value: U = 0.px
+ public func widthToParent(
+ extra value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- width(to: .width, of: view, value)
+ widthToParent(extra: value, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - equal
+ // MARK: - Height
+ /// Sets the height of an element
@discardableResult
- public func equalSize(
- to: BaseElement,
- _ value: U = 0.px
- ) -> Self {
- width(to: to, value)
- .height(to: to, value)
+ public func height(
+ _ state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let className = _getClassName("height", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Double) -> Void = { [weak self] value, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ return rule.custom("height", UnitValue(value.value.doubleValue, value.unit, important: breakpoints.count > 0).description)
+ }
+ }
+ perform(state.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, multiplier.wrappedValue)
+ }
+ multiplier.listen {
+ perform(state.wrappedValue, $0)
+ }
+ return self
}
+ /// Sets the height of an element
@discardableResult
- public func equalSize(
- to: BaseElement,
+ public func height(
_ state: State,
- multiplier: Double = 1
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- width(to: to, state, multiplier: multiplier)
- .height(to: to, state, multiplier: multiplier)
- }
-
- // MARK: - Super
-
- @discardableResult
- public func edgesToSuperview(_ value: U = 0.px) -> Self {
- topToSuperview(value)
- .leadingToSuperview(value)
- .trailingToSuperview(UnitValue(value.value.doubleValue * (-1), .px))
- .bottomToSuperview(UnitValue(value.value.doubleValue * (-1), .px))
- }
-
- @discardableResult
- public func edgesToSuperview(h: U) -> Self {
- leadingToSuperview(h)
- .trailingToSuperview(UnitValue(h.value.doubleValue * (-1), .px))
+ height(state, multiplier: multiplier, breakpoints: breakpoints)
}
+ /// Sets the height of an element
@discardableResult
- public func edgesToSuperview(v: U) -> Self {
- topToSuperview(v)
- .bottomToSuperview(UnitValue(v.value.doubleValue * (-1), .px))
- }
-
- @discardableResult
- public func edgesToSuperview(h: H, v: V) -> Self {
- topToSuperview(v)
- .leadingToSuperview(h)
- .trailingToSuperview(UnitValue(h.value.doubleValue * (-1), .px))
- .bottomToSuperview(UnitValue(v.value.doubleValue * (-1), .px))
- }
-
- public func edgesToSuperview(_ value: State) -> Self {
- topToSuperview(value)
- .leadingToSuperview(value)
- .trailingToSuperview(value.map { UnitValue($0.value.doubleValue * (-1), .px) })
- .bottomToSuperview(value.map { UnitValue($0.value.doubleValue * (-1), .px) })
+ public func height(
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ height(.init(wrappedValue: value), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
+ /// Sets the height of an element
@discardableResult
- public func edgesToSuperview(h: State) -> Self {
- leadingToSuperview(h)
- .trailingToSuperview(h.map { UnitValue($0.value.doubleValue * (-1), .px) })
+ public func height(
+ _ value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ height(value, multiplier: multiplier, breakpoints: breakpoints)
}
- @discardableResult
- public func edgesToSuperview(v: State) -> Self {
- topToSuperview(v)
- .bottomToSuperview(v.map { UnitValue($0.value.doubleValue * (-1), .px) })
- }
+ // MARK: - Height to parent
+ /// Sets the height of an element to fit first parent element with relative position
@discardableResult
- public func edgesToSuperview(h: State, v: State) -> Self {
- topToSuperview(v)
- .leadingToSuperview(h)
- .trailingToSuperview(h.map { UnitValue($0.value.doubleValue * (-1), .px) })
- .bottomToSuperview(v.map { UnitValue($0.value.doubleValue * (-1), .px) })
- }
-
- @discardableResult
- public func edgesToSuperview(top: U? = nil, leading: U? = nil, trailing: U? = nil, bottom: U? = nil) -> Self {
- if let top = top {
- topToSuperview(top)
- }
- if let leading = leading {
- leadingToSuperview(leading)
- }
- if let trailing = trailing {
- trailingToSuperview(trailing)
- }
- if let bottom = bottom {
- bottomToSuperview(bottom)
- }
- return self
- }
-
- @discardableResult
- public func edgesToSuperview(top: State? = nil, leading: State? = nil, trailing: State? = nil, bottom: State? = nil) -> Self {
- if let top = top {
- topToSuperview(top)
- }
- if let leading = leading {
- leadingToSuperview(leading)
+ public func heightToParent(
+ extra state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self {
+ let important = breakpoints.count > 0 ? "!important" : ""
+ let className = _getClassName("height", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (U, Double) -> Void = { [weak self] extra, multiplier in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ if extra.value.doubleValue > 0 {
+ return rule.custom("height", "calc(\(100 * multiplier)% + \(extra.description))" + important)
+ } else if extra.value.doubleValue < 0 {
+ return rule.custom("height", "calc(\(100 * multiplier)% - \(extra.description))" + important)
+ } else {
+ return rule.custom("height", "\(100 * multiplier)%" + important)
+ }
+ }
}
- if let trailing = trailing {
- trailingToSuperview(trailing)
+ perform(state.wrappedValue, multiplier.wrappedValue)
+ state.listen {
+ perform($0, multiplier.wrappedValue)
}
- if let bottom = bottom {
- bottomToSuperview(bottom)
+ multiplier.listen {
+ perform(state.wrappedValue, $0)
}
return self
}
- // MARK: - top
-
+ /// Sets the height of an element to fit first parent element with relative position
@discardableResult
- public func topToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func heightToParent(
+ extra state: State,
+ multiplier: State = .init(wrappedValue: 1),
+ breakpoints: MediaRule.MediaType...
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.top(to: .top, of: superview, state, multiplier: multiplier)
- }
- setup()
- onDidAddToDOM(setup)
- return self
+ heightToParent(extra: state, multiplier: multiplier, breakpoints: breakpoints)
}
+ /// Sets the height of an element to fit first parent element with relative position
@discardableResult
- public func topToSuperview(_ value: U = 0.px, multiplier: Double = 1) -> Self {
- topToSuperview(
- .init(wrappedValue: value),
- multiplier: multiplier
- )
- }
-
- // MARK: - left
-
- @discardableResult
- public func leftToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func heightToParent(
+ extra value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.left(to: .left, of: superview, state, multiplier: multiplier)
- }
- setup()
- onDidAddToDOM(setup)
- return self
+ heightToParent(extra: .init(wrappedValue: value), multiplier: .init(wrappedValue: multiplier), breakpoints: breakpoints)
}
-
+
+ /// Sets the height of an element to fit first parent element with relative position
@discardableResult
- public func leftToSuperview(
- _ value: U = 0.px,
- multiplier: Double = 1
+ public func heightToParent(
+ extra value: U = 0.px,
+ multiplier: Double = 1,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- leftToSuperview(.init(wrappedValue: value), multiplier: multiplier)
+ heightToParent(extra: value, multiplier: multiplier, breakpoints: breakpoints)
}
- // MARK: - leading
+ // MARK: - Position
+ /// Specifies the type of positioning method used for an element (static, relative, absolute or fixed)
@discardableResult
- public func leadingToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func position(
+ _ state: State,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.leading(to: .leading, of: superview, state, multiplier: multiplier)
+ let className = _getClassName("position", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (PositionType) -> Void = { [weak self] value in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ if breakpoints.count > 0 {
+ return rule.position(value.important)
+ } else {
+ return rule.position(value)
+ }
+ }
+ }
+ perform(state.wrappedValue)
+ state.listen {
+ perform($0)
}
- setup()
- onDidAddToDOM(setup)
return self
}
-
+
+ /// Specifies the type of positioning method used for an element (static, relative, absolute or fixed)
@discardableResult
- public func leadingToSuperview(
- _ value: U = 0.px,
- multiplier: Double = 1
+ public func position(
+ _ state: State,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- leadingToSuperview(.init(wrappedValue: value), multiplier: multiplier)
+ position(state, breakpoints: breakpoints)
}
- // MARK: - right
-
+ /// Specifies the type of positioning method used for an element (static, relative, absolute or fixed)
@discardableResult
- public func rightToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func position(
+ _ value: PositionType,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.right(to: .right, of: superview, state, multiplier: multiplier)
- }
- setup()
- onDidAddToDOM(setup)
- return self
+ position(.init(wrappedValue: value), breakpoints: breakpoints)
}
+ /// Specifies the type of positioning method used for an element (static, relative, absolute or fixed)
@discardableResult
- public func rightToSuperview(
- _ value: U = 0.px,
- multiplier: Double = 1
+ public func position(
+ _ value: PositionType,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- rightToSuperview(.init(wrappedValue: value), multiplier: multiplier)
+ position(value, breakpoints: breakpoints)
}
- // MARK: - trailing
+ // MARK: - Display
+ /// Specifies how a certain HTML element should be displayed
@discardableResult
- public func trailingToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func display(
+ _ state: State,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.trailing(to: .trailing, of: superview, state, multiplier: multiplier)
+ let className = _getClassName("display", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (DisplayType) -> Void = { [weak self] value in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ if breakpoints.count > 0 {
+ return rule.display(value.important)
+ } else {
+ return rule.display(value)
+ }
+ }
+ }
+ perform(state.wrappedValue)
+ state.listen {
+ perform($0)
}
- setup()
- onDidAddToDOM(setup)
return self
}
+ /// Specifies how a certain HTML element should be displayed
@discardableResult
- public func trailingToSuperview(
- _ value: U = 0.px,
- multiplier: Double = 1
+ public func display(
+ _ state: State,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- trailingToSuperview(.init(wrappedValue: value), multiplier: multiplier)
+ display(state, breakpoints: breakpoints)
}
- // MARK: - bottom
-
+ /// Specifies how a certain HTML element should be displayed
@discardableResult
- public func bottomToSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func display(
+ _ value: DisplayType,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- let setup = { [weak self] in
- guard let self = self else { return }
- guard let superview = self.superview else { return }
- self.bottom(to: .bottom, of: superview, state, multiplier: multiplier)
- }
- setup()
- onDidAddToDOM(setup)
- return self
+ display(.init(wrappedValue: value), breakpoints: breakpoints)
}
+ /// Specifies how a certain HTML element should be displayed
@discardableResult
- public func bottomToSuperview(
- _ value: U = 0.px,
- multiplier: Double = 1
+ public func display(
+ _ value: DisplayType,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- bottomToSuperview(.init(wrappedValue: value), multiplier: multiplier)
+ display(value, breakpoints: breakpoints)
}
- // MARK: - center x
+ // MARK: - Visibility
+ /// Specifies whether or not an element is visible
@discardableResult
- public func centerXInSuperview(
- _ state: State,
- side: Autolayout.ConstraintCXSide = .centerX,
- multiplier: Double = 1
- ) -> Self {
-//// print("centerXInSuperview 1")
-// let setup = { [weak self] in
-//// print("centerXInSuperview 2")
-// guard let self = self else { return }
-//// print("centerXInSuperview 3")
-// guard let superview = self.superview else { return }
-//// print("centerXInSuperview 4")
-// self.centerX(to: side, of: superview, state, multiplier: multiplier)
-// }
-// setup()
-// onDidAddToDOM(setup)
-// return self
- left(50.percent).transform(.translateX(-50.percent))
- }
-
- @discardableResult
- public func centerXInSuperview(
- _ value: U = 0.px,
- side: Autolayout.ConstraintCXSide = .centerX,
- multiplier: Double = 1
+ public func visibility(
+ _ state: State,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerXInSuperview(.init(wrappedValue: value), side: side, multiplier: multiplier)
- }
-
- // MARK: - center y
-
- @discardableResult
- public func centerYInSuperview(
- _ state: State,
- side: Autolayout.ConstraintCYSide = .centerY,
- multiplier: Double = 1
- ) -> Self {
-// let setup = { [weak self] in
-// guard let self = self else { return }
-// guard let superview = self.superview else { return }
-// self.centerY(to: side, of: superview, state, multiplier: multiplier)
-// }
-// setup()
-// onDidAddToDOM(setup)
-// return self
- let set: (U) -> Void = { [weak self] value in
- guard let self = self else { return }
- if value.value.doubleValue > 0 {
- self.custom("top", "calc((50% + \(value.value.doubleValue)) * \(multiplier))")
- } else if value.value.doubleValue < 0 {
- self.custom("top", "calc((50% - \(value.value.doubleValue)) * \(multiplier))")
- } else {
- self.custom("top", "calc(50% * \(multiplier))")
+ let className = _getClassName("visibility", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (VisibilityType) -> Void = { [weak self] value in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ if breakpoints.count > 0 {
+ return rule.visibility(value.important)
+ } else {
+ return rule.visibility(value)
+ }
}
}
- state.listen { set($0) }
- set(state.wrappedValue)
- return transform(.translate(-20.percent, -50.percent))
+ perform(state.wrappedValue)
+ state.listen {
+ perform($0)
+ }
+ return self
}
+ /// Specifies whether or not an element is visible
@discardableResult
- public func centerYInSuperview(
- _ value: U = 0.px,
- side: Autolayout.ConstraintCYSide = .centerY,
- multiplier: Double = 1
+ public func visibility(
+ _ state: State,
+ breakpoints: MediaRule.MediaType...
) -> Self {
- centerYInSuperview(.init(wrappedValue: value), side: side, multiplier: multiplier)
+ visibility(state, breakpoints: breakpoints)
}
- // MARK: center both
-
+ /// Specifies whether or not an element is visible
@discardableResult
- public func centerInSuperview(
- _ state: State,
- multiplier: Double = 1
+ public func visibility(
+ _ value: VisibilityType,
+ breakpoints: [MediaRule.MediaType]
) -> Self {
- centerXInSuperview(state, multiplier: multiplier)
- .centerYInSuperview(state, multiplier: multiplier)
+ visibility(.init(wrappedValue: value), breakpoints: breakpoints)
}
+ /// Specifies whether or not an element is visible
@discardableResult
- public func centerInSuperview(_ value: U = 0.px) -> Self {
- centerXInSuperview(value)
- .centerYInSuperview(value)
+ public func visibility(
+ _ value: VisibilityType,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self {
+ visibility(value, breakpoints: breakpoints)
}
+ // MARK: - Opacity
+
+ /// Sets the opacity level for an element
@discardableResult
- public func centerInSuperview(x: X, y: Y) -> Self {
- centerXInSuperview(x)
- .centerYInSuperview(y)
+ public func opacity(
+ _ state: State,
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self where N: UniValue, N.UniValue: NumericValue {
+ let className = _getClassName("opacity", breakpoints: breakpoints)
+ self.class(.init(stringLiteral: className))
+ let perform: (N) -> Void = { [weak self] value in
+ self?._setRule(className, breakpoints: breakpoints) { rule in
+ let container = NumericValueContainer(value, important: breakpoints.count > 0)
+ return rule.custom("opacity", container.value)
+ }
+ }
+ perform(state.wrappedValue)
+ state.listen {
+ perform($0)
+ }
+ return self
}
+ /// Sets the opacity level for an element
@discardableResult
- public func centerInSuperview(
- x: State,
- y: State,
- multiplier: Double = 1
- ) -> Self {
- centerXInSuperview(x, multiplier: multiplier)
- .centerYInSuperview(y, multiplier: multiplier)
+ public func opacity(
+ _ state: State,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self where N: UniValue, N.UniValue: NumericValue {
+ opacity(state, breakpoints: breakpoints)
}
- // MARK: - width
-
+ /// Sets the opacity level for an element
@discardableResult
- public func widthToSuperview(
- _ state: State,
- dimension: Autolayout.ConstraintDSide = .width,
- multiplier: Double = 1
- ) -> Self {
- width((multiplier * 100).percent)
+ public func opacity(
+ _ value: N,
+ breakpoints: [MediaRule.MediaType]
+ ) -> Self where N: UniValue, N.UniValue: NumericValue {
+ opacity(.init(wrappedValue: value), breakpoints: breakpoints)
}
-// @discardableResult
-// public func widthToHeightOfSuperview(
-// _ value: U = 0.px,
-// multiplier: Double = 1
-// ) -> Self {
-// let setup = { [weak self] in
-// guard let self = self else { return }
-// guard let superview = self.superview else { return }
-// self.width(to: .height, of: superview, state, multiplier: multiplier)
-// }
-// setup()
-// onDidAddToDOM(setup)
-// }
-
- // MARK: - height
-
+ /// Sets the opacity level for an element
@discardableResult
- public func heightToSuperview(
- _ state: State,
- dimension: Autolayout.ConstraintDSide = .height,
- multiplier: Double = 1
- ) -> Self {
-// let setup = { [weak self] in
-// guard let self = self else { return }
-// guard let superview = self.superview else { return }
-// self.height(to: dimension, of: superview, state, multiplier: multiplier)
-// }
-// setup()
-// onDidAddToDOM(setup)
-// return self
- height((multiplier * 100).percent)
- }
-
-// @discardableResult
-// public func heightToSuperview(
-// _ value: U = 0.px,
-// dimension: Autolayout.ConstraintDSide = .height,
-// multiplier: Double = 1
-// ) -> Self {
-// heightToSuperview(
-// .init(wrappedValue: value),
-// dimension: dimension,
-// multiplier: multiplier
-// )
-// }
+ public func opacity(
+ _ value: N,
+ breakpoints: MediaRule.MediaType...
+ ) -> Self where N: UniValue, N.UniValue: NumericValue {
+ opacity(value, breakpoints: breakpoints)
+ }
}