Skip to content

Commit eb0e8ee

Browse files
authored
basic implementation of transactions and animations (#25)
* wip * "fixed" math function symbols * wip * animation math fixes * spring math fixes * animation delay and speed * import fix + ci stuff * switch to 6.2 + imports * revert to snapshot - gosh darn swift.... * linter rule... smh
1 parent b858175 commit eb0e8ee

29 files changed

+1374
-139
lines changed

.github/workflows/ci.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
strategy:
2020
fail-fast: false
2121
matrix:
22-
image: ["swift:6.1", "swiftlang/swift:nightly-6.2-noble"]
22+
image: ["swift:6.1", "swift:6.2"]
2323

2424
container:
2525
image: ${{ matrix.image }}
@@ -32,3 +32,11 @@ jobs:
3232

3333
- name: Run Tests
3434
run: swift test
35+
36+
format:
37+
runs-on: ubuntu-latest
38+
timeout-minutes: 5
39+
container: "swift:6.2"
40+
steps:
41+
- uses: actions/checkout@v4
42+
- run: swift format lint -rps .

.swift-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.2-snapshot-2025-08-30
1+
6.2.0
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import ElementaryDOM
2+
import _ElementaryMath
3+
4+
@View
5+
struct AnimationsView {
6+
@State var angle: Double = 0
7+
8+
var content: some View {
9+
10+
div {
11+
AnimatedView(angle: angle)
12+
button { "Animate" }
13+
.onClick { _ in
14+
withAnimation(.bouncy(duration: 1)) {
15+
angle += 1
16+
}
17+
}
18+
}
19+
}
20+
}
21+
22+
@View
23+
struct AnimatedView {
24+
var angle: Double
25+
26+
let size = 100.0
27+
var x: Double { size * (1 - cos(angle)) }
28+
var y: Double { size * (1 - sin(angle)) }
29+
30+
var content: some View {
31+
p { "Angle: \(angle) x: \(x) y: \(y)" }
32+
div {
33+
Ball()
34+
.attributes(
35+
.style([
36+
"transform": "translate(\(x)px, \(y)px)",
37+
"position": "relative",
38+
])
39+
)
40+
}.attributes(
41+
.style([
42+
"height": "\(2 * size + 10)px",
43+
"width": "\(2 * size + 10)px",
44+
"position": "relative",
45+
])
46+
)
47+
}
48+
}
49+
50+
extension AnimatedView: Animatable {
51+
var animatableValue: Double {
52+
get { angle }
53+
set { angle = newValue }
54+
}
55+
}
56+
57+
@View
58+
struct Ball {
59+
var content: some HTML<HTMLTag.span> & View {
60+
span {}
61+
.attributes(
62+
.style([
63+
"background": "red",
64+
"height": "10px",
65+
"width": "10px",
66+
"border-radius": "50%",
67+
"display": "block",
68+
])
69+
)
70+
}
71+
}

Examples/Basic/Sources/App/Views.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ struct App {
1313

1414
var content: some View {
1515
div {
16+
AnimationsView()
17+
hr()
1618
TextField(value: #Binding(data.name))
1719

1820
div {

Package.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 6.0
1+
// swift-tools-version: 6.1
22
import CompilerPluginSupport
33
import PackageDescription
44

@@ -11,7 +11,7 @@ let package = Package(
1111
dependencies: [
1212
.package(url: "https://github.com/swiftwasm/JavaScriptKit", from: "0.33.1"),
1313
.package(url: "https://github.com/sliemeobn/elementary", from: "0.5.4"),
14-
.package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"602.0.0-prerelease"),
14+
.package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"603.0.0"),
1515
],
1616
targets: [
1717
.target(
@@ -21,6 +21,7 @@ let package = Package(
2121
.product(name: "JavaScriptKit", package: "JavaScriptKit"),
2222
.target(name: "ElementaryDOMMacros"),
2323
.target(name: "Reactivity"),
24+
.target(name: "_ElementaryMath"),
2425
],
2526
swiftSettings: [
2627
.swiftLanguageMode(.v5),
@@ -29,6 +30,12 @@ let package = Package(
2930
.enableUpcomingFeature("ImplicitOpenExistentials"),
3031
]
3132
),
33+
.target(
34+
name: "_ElementaryMath",
35+
swiftSettings: [
36+
.enableExperimentalFeature("Extern")
37+
]
38+
),
3239
.macro(
3340
name: "ElementaryDOMMacros",
3441
dependencies: [
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
public protocol Animatable {
2+
associatedtype Value: AnimatableVectorConvertible
3+
var animatableValue: Value { get set }
4+
}
5+
6+
public protocol AnimatableVectorConvertible: Equatable {
7+
init(_ animatableVector: AnimatableVector)
8+
var animatableVector: AnimatableVector { get }
9+
}
10+
11+
extension AnimatableVectorConvertible where Self: Animatable, Value == Self {
12+
public typealias Value = Self
13+
public var animatableValue: Self {
14+
get { self }
15+
set { self = newValue }
16+
}
17+
}
18+
19+
extension Double: AnimatableVectorConvertible, Animatable {
20+
public init(_ animatableVector: AnimatableVector) {
21+
guard case .d1(let value) = animatableVector else {
22+
fatalError("Unsupported animatable vector")
23+
}
24+
self = Double(value)
25+
}
26+
27+
public var animatableVector: AnimatableVector {
28+
.d1(Float(self))
29+
}
30+
}
31+
32+
extension Float: AnimatableVectorConvertible, Animatable {
33+
public init(_ animatableVector: AnimatableVector) {
34+
guard case .d1(let value) = animatableVector else {
35+
fatalError("Unsupported animatable vector")
36+
}
37+
self = value
38+
}
39+
40+
public var animatableVector: AnimatableVector {
41+
.d1(self)
42+
}
43+
}
44+
45+
struct EmptyAnimatableData: AnimatableVectorConvertible {
46+
init() {}
47+
48+
init(_ animatableVector: AnimatableVector) {
49+
guard case .d0 = animatableVector else {
50+
fatalError("Unsupported animatable vector")
51+
}
52+
}
53+
54+
var animatableVector: AnimatableVector {
55+
.d0
56+
}
57+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import _ElementaryMath
2+
3+
// TODO: maybe this can be replaced with Spans of floats somehow
4+
public enum AnimatableVector {
5+
case d0
6+
case d1(Float)
7+
case d2(Float, Float)
8+
case d4(SIMD4<Float>)
9+
case d8(SIMD4<Float>, SIMD4<Float>)
10+
}
11+
12+
extension AnimatableVector {
13+
internal var isEmpty: Bool {
14+
switch self {
15+
case .d0:
16+
return true
17+
default:
18+
return false
19+
}
20+
}
21+
}
22+
23+
extension AnimatableVector: AnimatableVectorConvertible {
24+
public init(_ animatableVector: AnimatableVector) {
25+
self = animatableVector
26+
}
27+
28+
public var animatableVector: AnimatableVector {
29+
self
30+
}
31+
}
32+
33+
extension AnimatableVector: Equatable {
34+
public static func == (lhs: Self, rhs: Self) -> Bool {
35+
switch (lhs, rhs) {
36+
case (.d0, .d0):
37+
return true
38+
case (.d1(let l1), .d1(let r1)):
39+
return l1 == r1
40+
case (.d2(let l1, let l2), .d2(let r1, let r2)):
41+
return l1 == r1 && l2 == r2
42+
case (.d4(let l1), .d4(let r1)):
43+
return l1 == r1
44+
case (.d8(let l1, let l2), .d8(let r1, let r2)):
45+
return l1 == r1 && l2 == r2
46+
default:
47+
return false
48+
}
49+
}
50+
}
51+
52+
extension AnimatableVector {
53+
public static func zero(_ vector: borrowing Self) -> Self {
54+
switch vector {
55+
case .d0:
56+
return .d0
57+
case .d1(_):
58+
return .d1(0)
59+
case .d2(_, _):
60+
return .d2(0, 0)
61+
case .d4(_):
62+
return .d4(SIMD4<Float>(repeating: 0))
63+
case .d8(_, _):
64+
return .d8(SIMD4<Float>(repeating: 0), SIMD4<Float>(repeating: 0))
65+
}
66+
}
67+
68+
public static func + (lhs: Self, rhs: Self) -> Self {
69+
switch (lhs, rhs) {
70+
case (.d0, .d0):
71+
return .d0
72+
case (.d1(let l1), .d1(let r1)):
73+
return .d1(l1 + r1)
74+
case (.d2(let l1, let l2), .d2(let r1, let r2)):
75+
return .d2(l1 + r1, l2 + r2)
76+
case (.d4(let l1), .d4(let r1)):
77+
return .d4(l1 + r1)
78+
case (.d8(let l1, let l2), .d8(let r1, let r2)):
79+
return .d8(l1 + r1, l2 + r2)
80+
default:
81+
fatalError("mismatching dimensions")
82+
}
83+
}
84+
85+
public static func += (lhs: inout Self, rhs: Self) {
86+
lhs = lhs + rhs
87+
}
88+
89+
public static func * (lhs: Self, rhs: Float) -> Self {
90+
switch lhs {
91+
case .d0:
92+
return .d0
93+
case .d1(let l1):
94+
return .d1(l1 * rhs)
95+
case .d2(let l1, let l2):
96+
return .d2(l1 * rhs, l2 * rhs)
97+
case .d4(let l1):
98+
return .d4(l1 * rhs)
99+
case .d8(let l1, let l2):
100+
return .d8(l1 * rhs, l2 * rhs)
101+
}
102+
}
103+
104+
public static func * (lhs: Self, rhs: Double) -> Self {
105+
lhs * Float(rhs)
106+
}
107+
108+
public static func - (lhs: Self, rhs: Self) -> Self {
109+
switch (lhs, rhs) {
110+
case (.d0, .d0):
111+
return .d0
112+
case (.d1(let l1), .d1(let r1)):
113+
return .d1(l1 - r1)
114+
case (.d2(let l1, let l2), .d2(let r1, let r2)):
115+
return .d2(l1 - r1, l2 - r2)
116+
case (.d4(let l1), .d4(let r1)):
117+
return .d4(l1 - r1)
118+
case (.d8(let l1, let l2), .d8(let r1, let r2)):
119+
return .d8(l1 - r1, l2 - r2)
120+
default:
121+
fatalError("mismatching dimensions")
122+
}
123+
}
124+
125+
/// Calculates the magnitude (length) of the vector
126+
public var magnitude: Float {
127+
switch self {
128+
case .d0:
129+
return 0
130+
case .d1(let x):
131+
return abs(x)
132+
case .d2(let x, let y):
133+
return sqrtf(x * x + y * y)
134+
case .d4(let v):
135+
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w)
136+
case .d8(let v1, let v2):
137+
let mag1 = v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w * v1.w
138+
let mag2 = v2.x * v2.x + v2.y * v2.y + v2.z * v2.z + v2.w * v2.w
139+
return sqrtf(mag1 + mag2)
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)