@tileflow/react-native 0.1.0-alpha.1 → 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,10 @@
5
5
  #import <MapLibreReactNative/MLRNMapView.h>
6
6
  #import <MapLibreReactNative/MLRNCamera.h>
7
7
  #import <QuartzCore/QuartzCore.h>
8
+ #import <algorithm>
8
9
  #import "TFAdmissionEngine.h"
9
10
  #import "TFNativeSurfaceState.h"
11
+ #import "TFNativeTheme.h"
10
12
  #import <cmath>
11
13
 
12
14
  static void TFSurfaceInvalid(void) {
@@ -67,6 +69,32 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
67
69
  return found;
68
70
  }
69
71
 
72
+ // The view MapLibre draws into: the subview backed by a Metal (or OpenGL) layer.
73
+ static UIView *TFSurfaceRenderView(MLNMapView *map) {
74
+ for (UIView *view in map.subviews) {
75
+ NSString *name = NSStringFromClass(view.layer.class);
76
+ if ([name containsString:@"Metal"] || [name containsString:@"EAGL"]) return view;
77
+ }
78
+ return nil;
79
+ }
80
+ // A snapshot of one frame over the rendering view. It keeps four ground points of that frame and
81
+ // follows them with the ground plane's projective transform while the camera moves.
82
+ @interface TFSurfaceCover : NSObject
83
+ @property (nonatomic, strong) UIView *view;
84
+ @property (nonatomic, copy) NSArray<NSValue *> *corners;
85
+ @property (nonatomic, copy) NSArray<NSValue *> *anchors;
86
+ @property (nonatomic) BOOL fading;
87
+ @end
88
+ @implementation TFSurfaceCover
89
+ @end
90
+ @interface TFSurfaceReveal : NSObject
91
+ @property (nonatomic) double duration;
92
+ @property (nonatomic, strong) NSMutableArray<TFSurfaceCover *> *covers;
93
+ @property (nonatomic, copy) RCTPromiseResolveBlock resolve;
94
+ @end
95
+ @implementation TFSurfaceReveal
96
+ @end
97
+
70
98
  @interface TFSurfaceLayoutProbe : UIView
71
99
  @property (nonatomic, copy) dispatch_block_t changed;
72
100
  @end
@@ -90,8 +118,19 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
90
118
  @property (nonatomic) BOOL retiring;
91
119
  @property (nonatomic) BOOL foreground;
92
120
  @property (nonatomic) BOOL observing;
121
+ @property (nonatomic, strong) NSMutableArray<TFSurfaceCover *> *covers;
122
+ @property (nonatomic, strong) NSMutableArray<TFSurfaceReveal *> *reveals;
123
+ @property (nonatomic, strong) NSMutableArray<TFSurfaceReveal *> *settling;
124
+ @property (nonatomic, copy, nullable) dispatch_block_t revealDeadline;
125
+ @property (nonatomic, copy, nullable) NSString *themedToken;
126
+ @property (nonatomic, strong) NSMutableDictionary<NSString *, MLNStyleLayer *> *themedLayers;
127
+ @property (nonatomic, strong) NSMutableDictionary<NSString *, UIImage *> *themedImages;
93
128
  - (void)attach;
129
+ - (NSUInteger)applyThemeValues:(NSDictionary *)values;
94
130
  - (void)retire;
131
+ - (BOOL)cover;
132
+ - (void)reveal:(double)duration resolve:(RCTPromiseResolveBlock)resolve;
133
+ - (void)discardCovers;
95
134
  - (BOOL)owns;
96
135
  - (MLNStyle *)style;
97
136
  - (NSDictionary *)view;
@@ -114,6 +153,7 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
114
153
  }
115
154
  - (void)attach {
116
155
  self.token = @""; self.detached = [NSMutableArray array]; self.previous = self.map.delegate;
156
+ self.covers = [NSMutableArray array]; self.reveals = [NSMutableArray array]; self.settling = [NSMutableArray array];
117
157
  if ([self.previous isKindOfClass:TFSurfaceAttachment.class]) TFSurfaceInvalid();
118
158
  self.map.delegate = self;
119
159
  [self.map addObserver:self forKeyPath:@"delegate" options:0 context:(__bridge void *)self]; self.observing = YES;
@@ -149,8 +189,182 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
149
189
  for (NSUInteger index = 0; index < 4; index++) visible = visible && [snapshot[index] doubleValue] > 0;
150
190
  [self.state layout:visible];
151
191
  }
192
+ - (BOOL)cover {
193
+ if (UIAccessibilityIsReduceMotionEnabled() || !self.foreground || !self.root.window || !self.style) return NO;
194
+ for (TFSurfaceCover *existing in self.covers) if (!existing.fading) return YES;
195
+ UIView *render = TFSurfaceRenderView(self.map);
196
+ CGRect frame = render.frame;
197
+ if (!render || frame.size.width < 1 || frame.size.height < 1 || self.covers.count >= 4) return NO;
198
+ UIView *snapshot = [render snapshotViewAfterScreenUpdates:NO];
199
+ if (!snapshot) return NO;
200
+ snapshot.userInteractionEnabled = NO; snapshot.accessibilityElementsHidden = YES;
201
+ snapshot.layer.anchorPoint = CGPointZero; snapshot.frame = frame;
202
+ // Ground points below the horizon at any supported pitch, in map view coordinates.
203
+ CGFloat width = frame.size.width, height = frame.size.height;
204
+ NSMutableArray<NSValue *> *corners = [NSMutableArray array], *anchors = [NSMutableArray array];
205
+ for (NSValue *value in @[[NSValue valueWithCGPoint:CGPointMake(width * 0.1, height * 0.45)], [NSValue valueWithCGPoint:CGPointMake(width * 0.9, height * 0.45)],
206
+ [NSValue valueWithCGPoint:CGPointMake(width * 0.9, height * 0.9)], [NSValue valueWithCGPoint:CGPointMake(width * 0.1, height * 0.9)]]) {
207
+ CGPoint point = value.CGPointValue;
208
+ CLLocationCoordinate2D coordinate = [self.map convertPoint:CGPointMake(point.x + frame.origin.x, point.y + frame.origin.y) toCoordinateFromView:self.map];
209
+ if (!std::isfinite(coordinate.latitude) || !std::isfinite(coordinate.longitude) || std::fabs(coordinate.latitude) > 90) return NO;
210
+ [corners addObject:value];
211
+ [anchors addObject:[NSValue valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)]];
212
+ }
213
+ TFSurfaceCover *cover = [TFSurfaceCover new];
214
+ cover.view = snapshot; cover.corners = corners; cover.anchors = anchors;
215
+ // Newer covers go below older ones, which keep fading on top.
216
+ [self.map insertSubview:snapshot aboveSubview:render];
217
+ [self.covers addObject:cover];
218
+ return YES;
219
+ }
220
+ - (void)followCovers {
221
+ if (!self.covers.count) return;
222
+ UIView *render = TFSurfaceRenderView(self.map);
223
+ if (!render) return;
224
+ CGPoint origin = render.frame.origin;
225
+ for (TFSurfaceCover *cover in self.covers) {
226
+ CGPoint from[4], to[4];
227
+ for (int index = 0; index < 4; index++) {
228
+ from[index] = cover.corners[index].CGPointValue;
229
+ CLLocationCoordinate2D coordinate;
230
+ [cover.anchors[index] getValue:&coordinate size:sizeof(coordinate)];
231
+ CGPoint point = [self.map convertCoordinate:coordinate toPointToView:self.map];
232
+ if (!std::isfinite(point.x) || !std::isfinite(point.y)) return;
233
+ to[index] = CGPointMake(point.x - origin.x, point.y - origin.y);
234
+ }
235
+ double h[9];
236
+ if (!TFThemeHomography(from, to, h)) continue;
237
+ [CATransaction begin]; [CATransaction setDisableActions:YES];
238
+ cover.view.layer.position = origin; cover.view.layer.transform = TFThemeTransform(h);
239
+ [CATransaction commit];
240
+ }
241
+ }
242
+ - (void)reveal:(double)duration resolve:(RCTPromiseResolveBlock)resolve {
243
+ TFSurfaceReveal *reveal = [TFSurfaceReveal new];
244
+ reveal.duration = duration; reveal.resolve = resolve; reveal.covers = [self.covers mutableCopy];
245
+ if (!reveal.covers.count) { resolve(@{@"revealed": @YES}); return; }
246
+ [self.reveals addObject:reveal];
247
+ // Fades start on the next complete frame, or after ten seconds at most.
248
+ if (!self.revealDeadline) {
249
+ __weak TFSurfaceAttachment *weakSelf = self;
250
+ dispatch_block_t deadline = dispatch_block_create((dispatch_block_flags_t)0, ^{ weakSelf.revealDeadline = nil; [weakSelf startReveals]; });
251
+ self.revealDeadline = deadline;
252
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), deadline);
253
+ }
254
+ [self.map triggerRepaint];
255
+ }
256
+ - (void)startReveals {
257
+ if (self.revealDeadline) { dispatch_block_cancel(self.revealDeadline); self.revealDeadline = nil; }
258
+ NSArray<TFSurfaceReveal *> *reveals = [self.reveals copy];
259
+ [self.reveals removeAllObjects];
260
+ for (TFSurfaceReveal *reveal in reveals) {
261
+ for (TFSurfaceCover *cover in reveal.covers) {
262
+ if (cover.fading) continue;
263
+ cover.fading = YES;
264
+ __weak TFSurfaceAttachment *weakSelf = self;
265
+ [UIView animateWithDuration:reveal.duration / 1000.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
266
+ animations:^{ cover.view.alpha = 0; } completion:^(__unused BOOL finished) { [weakSelf removeCover:cover]; }];
267
+ }
268
+ [self settle:reveal];
269
+ }
270
+ }
271
+ - (void)removeCover:(TFSurfaceCover *)cover {
272
+ [cover.view.layer removeAllAnimations]; [cover.view removeFromSuperview];
273
+ [self.covers removeObject:cover];
274
+ for (TFSurfaceReveal *reveal in [self.settling copy]) [self settle:reveal];
275
+ }
276
+ // A reveal resolves once every cover that existed when it was requested is gone.
277
+ - (void)settle:(TFSurfaceReveal *)reveal {
278
+ for (TFSurfaceCover *cover in reveal.covers) if ([self.covers containsObject:cover]) {
279
+ if (![self.settling containsObject:reveal]) [self.settling addObject:reveal];
280
+ return;
281
+ }
282
+ [self.settling removeObject:reveal];
283
+ if (reveal.resolve) { RCTPromiseResolveBlock resolve = reveal.resolve; reveal.resolve = nil; resolve(@{@"revealed": @YES}); }
284
+ }
285
+ - (void)discardCovers {
286
+ if (self.revealDeadline) { dispatch_block_cancel(self.revealDeadline); self.revealDeadline = nil; }
287
+ for (TFSurfaceCover *cover in [self.covers copy]) { [cover.view.layer removeAllAnimations]; [cover.view removeFromSuperview]; }
288
+ [self.covers removeAllObjects];
289
+ NSArray *reveals = [self.reveals arrayByAddingObjectsFromArray:self.settling];
290
+ [self.reveals removeAllObjects]; [self.settling removeAllObjects];
291
+ for (TFSurfaceReveal *reveal in reveals) if (reveal.resolve) { RCTPromiseResolveBlock resolve = reveal.resolve; reveal.resolve = nil; resolve(@{@"revealed": @YES}); }
292
+ }
293
+ - (NSUInteger)applyThemeValues:(NSDictionary *)values {
294
+ MLNStyle *style = self.style;
295
+ if (!style) TFSurfaceInvalid();
296
+ if (![self.themedToken isEqual:self.token]) {
297
+ // Layers and original artwork belong to one style.
298
+ self.themedToken = self.token; self.themedLayers = [NSMutableDictionary dictionary]; self.themedImages = [NSMutableDictionary dictionary];
299
+ }
300
+ NSUInteger applied = 0;
301
+ for (NSString *group in @[@"paint", @"layout"]) {
302
+ id entries = values[group];
303
+ if (!entries) continue;
304
+ if (![entries isKindOfClass:NSArray.class] || [entries count] > 8192) TFSurfaceInvalid();
305
+ for (id entry in entries) {
306
+ if (![entry isKindOfClass:NSArray.class] || [entry count] != 3 || ![entry[0] isKindOfClass:NSString.class] || ![entry[1] isKindOfClass:NSString.class]) TFSurfaceInvalid();
307
+ NSString *identifier = entry[0];
308
+ MLNStyleLayer *layer = self.themedLayers[identifier];
309
+ if (!layer) {
310
+ layer = [style layerWithIdentifier:identifier];
311
+ if (!layer) continue;
312
+ self.themedLayers[identifier] = layer;
313
+ }
314
+ if ([entry[1] isEqual:@"visibility"]) {
315
+ // Per-theme copies of a layer switch by visibility, a plain flag.
316
+ layer.visible = ![entry[2] isEqual:@"none"];
317
+ applied++;
318
+ continue;
319
+ }
320
+ NSString *key = TFThemeStyleKey(entry[1]);
321
+ if (![layer respondsToSelector:NSSelectorFromString(key)] || !TFThemeCameraValue(entry[2], 0)) continue;
322
+ // A value MapLibre rejects keeps its previous value; the rest of the batch still applies.
323
+ BOOL supported;
324
+ NSExpression *expression = TFThemeExpression(entry[2], key, &supported);
325
+ if (!supported) continue;
326
+ @try { [layer setValue:expression forKey:key]; applied++; }
327
+ @catch (__unused NSException *exception) {}
328
+ }
329
+ }
330
+ id images = values[@"images"];
331
+ if (images) {
332
+ if (![images isKindOfClass:NSArray.class] || [images count] > 64) TFSurfaceInvalid();
333
+ // Originals are read before any artwork is replaced, so tuples can share names.
334
+ for (NSArray *entry in images) {
335
+ if (![entry isKindOfClass:NSArray.class] || entry.count != 4) TFSurfaceInvalid();
336
+ for (NSUInteger index = 0; index < 3; index++) {
337
+ if (![entry[index] isKindOfClass:NSString.class]) TFSurfaceInvalid();
338
+ if (!self.themedImages[entry[index]]) { UIImage *image = [style imageForName:entry[index]]; if (image) self.themedImages[entry[index]] = image; }
339
+ }
340
+ }
341
+ for (NSArray *entry in images) {
342
+ UIImage *from = self.themedImages[entry[1]], *to = self.themedImages[entry[2]];
343
+ double t = TFSurfaceNumber(entry[3], 0, 1);
344
+ if (!from || !to) continue;
345
+ [style setImage:TFThemeMixImage(from, to, t) forName:entry[0]];
346
+ applied++;
347
+ }
348
+ }
349
+ id light = values[@"light"];
350
+ if (light) {
351
+ if (![light isKindOfClass:NSDictionary.class]) TFSurfaceInvalid();
352
+ MLNLight *next = style.light ?: [MLNLight new];
353
+ id colour = light[@"color"], intensity = light[@"intensity"], position = light[@"position"];
354
+ if ([colour isKindOfClass:NSString.class]) { UIColor *parsed = TFThemeColor(colour); if (parsed) next.color = [NSExpression expressionForConstantValue:parsed]; }
355
+ if ([intensity isKindOfClass:NSNumber.class]) next.intensity = [NSExpression expressionForConstantValue:intensity];
356
+ if ([position isKindOfClass:NSArray.class] && [position count] == 3 && TFThemeNumbers(position)) {
357
+ MLNSphericalPosition spherical = MLNSphericalPositionMake([position[0] doubleValue], [position[1] doubleValue], [position[2] doubleValue]);
358
+ next.position = [NSExpression expressionForConstantValue:[NSValue valueWithMLNSphericalPosition:spherical]];
359
+ }
360
+ style.light = next;
361
+ applied++;
362
+ }
363
+ return applied;
364
+ }
152
365
  - (void)retire {
153
366
  if (self.retiring) return;
367
+ [self discardCovers];
154
368
  self.retiring = YES; [self.state close];
155
369
  if (self.deadline) dispatch_block_cancel(self.deadline); self.deadline = nil;
156
370
  if (self.layoutCancel) self.layoutCancel(); self.layoutCancel = nil;
@@ -165,23 +379,23 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
165
379
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView didFinishLoadingStyle:style];
166
380
  }
167
381
  - (void)mapViewWillStartRenderingFrame:(MLNMapView *)mapView {
168
- if ([self owns]) { [self sampleLayout]; [self.state frameStart:self.style]; }
382
+ if ([self owns]) { [self sampleLayout]; [self.state frameStart:self.style]; [self followCovers]; }
169
383
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewWillStartRenderingFrame:mapView];
170
384
  }
171
385
  - (void)mapViewDidFinishRenderingMap:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
172
386
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingMap:mapView fullyRendered:fully];
173
387
  }
174
388
  - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
175
- if ([self owns]) [self.state frameEnd:self.style fully:fully];
389
+ if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
176
390
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
177
391
  }
178
392
  - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully frameEncodingTime:(double)encoding frameRenderingTime:(double)rendering {
179
- if ([self owns]) [self.state frameEnd:self.style fully:fully];
393
+ if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
180
394
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully frameEncodingTime:encoding frameRenderingTime:rendering];
181
395
  else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
182
396
  }
183
397
  - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully renderingStats:(MLNRenderingStats *)stats {
184
- if ([self owns]) [self.state frameEnd:self.style fully:fully];
398
+ if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
185
399
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully renderingStats:stats];
186
400
  else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
187
401
  }
@@ -194,7 +408,7 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
194
408
  else if ([self.previous respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) [self.previous mapView:mapView regionWillChangeAnimated:animated];
195
409
  }
196
410
  - (void)mapView:(MLNMapView *)mapView regionIsChangingWithReason:(MLNCameraChangeReason)reason {
197
- if ([self owns]) [self.state gestureChange:self.view];
411
+ if ([self owns]) { [self.state gestureChange:self.view]; [self followCovers]; }
198
412
  if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView regionIsChangingWithReason:reason];
199
413
  else if ([self.previous respondsToSelector:@selector(mapViewRegionIsChanging:)]) [self.previous mapViewRegionIsChanging:mapView];
200
414
  }
@@ -338,6 +552,40 @@ RCT_REMAP_METHOD(cancelCamera, cancelCameraForSurface:(NSString *)identifier seq
338
552
  @try { if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid(); [self.surfaces[identifier].state cancelCommand:TFSurfaceInteger(sequence)]; resolve(@{@"cancelled": @YES}); }
339
553
  @catch (NSException *exception) { TFSurfaceReject(reject); }
340
554
  }
555
+ static double TFSurfaceDuration(id value) {
556
+ if (![value isKindOfClass:NSNumber.class] || CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) TFSurfaceInvalid();
557
+ double number = [value doubleValue];
558
+ if (!std::isfinite(number) || number < 0 || number > 5000 || std::floor(number) != number) TFSurfaceInvalid();
559
+ return number;
560
+ }
561
+ RCT_REMAP_METHOD(coverSurface, coverSurface:(NSString *)identifier duration:(NSNumber *)duration resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
562
+ @try {
563
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
564
+ double milliseconds = TFSurfaceDuration(duration);
565
+ resolve(@{@"covered": @(milliseconds > 0 && [surface cover])});
566
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
567
+ }
568
+ RCT_REMAP_METHOD(revealSurface, revealSurface:(NSString *)identifier duration:(NSNumber *)duration resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
569
+ @try {
570
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:YES];
571
+ [surface reveal:TFSurfaceDuration(duration) resolve:resolve];
572
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
573
+ }
574
+ RCT_REMAP_METHOD(applyThemeValues, applyThemeValuesForSurface:(NSString *)identifier style:(NSString *)token values:(NSDictionary *)values resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
575
+ @try {
576
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
577
+ if (![values isKindOfClass:NSDictionary.class] || ![surface.token isEqual:token]) TFSurfaceInvalid();
578
+ NSUInteger applied = [surface applyThemeValues:values];
579
+ resolve(@{@"applied": @(applied)});
580
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
581
+ }
582
+ RCT_REMAP_METHOD(discardSurfaceCover, discardSurfaceCover:(NSString *)identifier resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
583
+ @try {
584
+ if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid();
585
+ [self.surfaces[identifier] discardCovers];
586
+ resolve(@{@"discarded": @YES});
587
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
588
+ }
341
589
  - (void)retire:(TFSurfaceAttachment *)surface resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
342
590
  [surface retire];
343
591
  if (!surface.root.window) { [self.surfaces removeObjectForKey:surface.identifier]; resolve(@{@"detached": @YES}); return; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/react-native",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Tileflow maps, camera control and semantic interactions for React Native.",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@tileflow/core": ">=0.1.0-alpha.28 <0.1.0-beta.0",
39
+ "@tileflow/core": ">=0.1.0-alpha.32 <0.1.0-beta.0",
40
40
  "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
41
41
  },
42
42
  "peerDependencies": {