react-native-gleam 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Gleam.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +150 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/gleam/GleamPackage.kt +17 -0
- package/android/src/main/java/com/gleam/GleamView.kt +379 -0
- package/android/src/main/java/com/gleam/GleamViewManager.kt +89 -0
- package/ios/GleamView.h +14 -0
- package/ios/GleamView.mm +430 -0
- package/lib/module/GleamViewNativeComponent.ts +26 -0
- package/lib/module/index.js +16 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/GleamViewNativeComponent.d.ts +18 -0
- package/lib/typescript/src/GleamViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +150 -0
- package/src/GleamViewNativeComponent.ts +26 -0
- package/src/index.tsx +14 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package com.gleam
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
7
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
8
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
9
|
+
import com.facebook.react.viewmanagers.GleamViewManagerInterface
|
|
10
|
+
import com.facebook.react.viewmanagers.GleamViewManagerDelegate
|
|
11
|
+
|
|
12
|
+
@ReactModule(name = GleamViewManager.NAME)
|
|
13
|
+
class GleamViewManager : ViewGroupManager<GleamView>(),
|
|
14
|
+
GleamViewManagerInterface<GleamView> {
|
|
15
|
+
|
|
16
|
+
private val mDelegate: ViewManagerDelegate<GleamView> = GleamViewManagerDelegate(this)
|
|
17
|
+
|
|
18
|
+
override fun getDelegate(): ViewManagerDelegate<GleamView> = mDelegate
|
|
19
|
+
|
|
20
|
+
override fun getName(): String = NAME
|
|
21
|
+
|
|
22
|
+
override fun createViewInstance(context: ThemedReactContext): GleamView {
|
|
23
|
+
return GleamView(context)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@ReactProp(name = "loading", defaultBoolean = true)
|
|
27
|
+
override fun setLoading(view: GleamView, loading: Boolean) {
|
|
28
|
+
view.loading = loading
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ReactProp(name = "speed", defaultFloat = 1000f)
|
|
32
|
+
override fun setSpeed(view: GleamView, speed: Float) {
|
|
33
|
+
view.speed = speed
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@ReactProp(name = "delay", defaultFloat = 0f)
|
|
37
|
+
override fun setDelay(view: GleamView, delay: Float) {
|
|
38
|
+
view.delay = delay
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@ReactProp(name = "transitionDuration", defaultFloat = 300f)
|
|
42
|
+
override fun setTransitionDuration(view: GleamView, transitionDuration: Float) {
|
|
43
|
+
view.transitionDuration = transitionDuration
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@ReactProp(name = "transitionType")
|
|
47
|
+
override fun setTransitionType(view: GleamView, transitionType: String?) {
|
|
48
|
+
view.transitionType = when (transitionType) {
|
|
49
|
+
"shrink" -> GleamView.TransitionType.SHRINK
|
|
50
|
+
"collapse" -> GleamView.TransitionType.COLLAPSE
|
|
51
|
+
else -> GleamView.TransitionType.FADE
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@ReactProp(name = "intensity", defaultFloat = 1f)
|
|
56
|
+
override fun setIntensity(view: GleamView, intensity: Float) {
|
|
57
|
+
view.intensity = intensity
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@ReactProp(name = "direction")
|
|
61
|
+
override fun setDirection(view: GleamView, direction: String?) {
|
|
62
|
+
view.direction = when (direction) {
|
|
63
|
+
"rtl" -> GleamView.Direction.RTL
|
|
64
|
+
"ttb" -> GleamView.Direction.TTB
|
|
65
|
+
else -> GleamView.Direction.LTR
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@ReactProp(name = "baseColor", customType = "Color")
|
|
70
|
+
override fun setBaseColor(view: GleamView, baseColor: Int?) {
|
|
71
|
+
view.baseColor = baseColor ?: 0xFFE0E0E0.toInt()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@ReactProp(name = "highlightColor", customType = "Color")
|
|
75
|
+
override fun setHighlightColor(view: GleamView, highlightColor: Int?) {
|
|
76
|
+
view.highlightColor = highlightColor ?: 0xFFF5F5F5.toInt()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override fun setBorderRadius(view: GleamView, borderRadius: Float) {
|
|
80
|
+
view.cornerRadius = borderRadius
|
|
81
|
+
super.setBorderRadius(view, borderRadius)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override fun needsCustomLayoutForChildren(): Boolean = false
|
|
85
|
+
|
|
86
|
+
companion object {
|
|
87
|
+
const val NAME = "GleamView"
|
|
88
|
+
}
|
|
89
|
+
}
|
package/ios/GleamView.h
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#import <React/RCTViewComponentView.h>
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
#ifndef GleamViewNativeComponent_h
|
|
5
|
+
#define GleamViewNativeComponent_h
|
|
6
|
+
|
|
7
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
+
|
|
9
|
+
@interface GleamView : RCTViewComponentView
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_END
|
|
13
|
+
|
|
14
|
+
#endif /* GleamViewNativeComponent_h */
|
package/ios/GleamView.mm
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#import "GleamView.h"
|
|
2
|
+
|
|
3
|
+
#import <QuartzCore/QuartzCore.h>
|
|
4
|
+
#import <React/RCTConversions.h>
|
|
5
|
+
|
|
6
|
+
#import <react/renderer/components/GleamViewSpec/ComponentDescriptors.h>
|
|
7
|
+
#import <react/renderer/components/GleamViewSpec/EventEmitters.h>
|
|
8
|
+
#import <react/renderer/components/GleamViewSpec/Props.h>
|
|
9
|
+
#import <react/renderer/components/GleamViewSpec/RCTComponentViewHelpers.h>
|
|
10
|
+
|
|
11
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
12
|
+
|
|
13
|
+
using namespace facebook::react;
|
|
14
|
+
|
|
15
|
+
typedef NS_ENUM(NSInteger, GleamDirection) {
|
|
16
|
+
GleamDirectionLTR = 0,
|
|
17
|
+
GleamDirectionRTL,
|
|
18
|
+
GleamDirectionTTB,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
#pragma mark - Shared Display Link Clock
|
|
22
|
+
|
|
23
|
+
static NSMutableSet<GleamView *> *_registeredViews;
|
|
24
|
+
static CADisplayLink *_displayLink;
|
|
25
|
+
|
|
26
|
+
static void _ensureDisplayLink(void) {
|
|
27
|
+
if (!_registeredViews) {
|
|
28
|
+
_registeredViews = [NSMutableSet new];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static void _startDisplayLinkIfNeeded(void) {
|
|
33
|
+
if (_displayLink) return;
|
|
34
|
+
_displayLink = [CADisplayLink displayLinkWithTarget:[GleamView class] selector:@selector(_onFrame:)];
|
|
35
|
+
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static void _stopDisplayLinkIfNeeded(void) {
|
|
39
|
+
if (_registeredViews.count > 0) return;
|
|
40
|
+
[_displayLink invalidate];
|
|
41
|
+
_displayLink = nil;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void _registerView(GleamView *view) {
|
|
45
|
+
_ensureDisplayLink();
|
|
46
|
+
[_registeredViews addObject:view];
|
|
47
|
+
_startDisplayLinkIfNeeded();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static void _unregisterView(GleamView *view) {
|
|
51
|
+
[_registeredViews removeObject:view];
|
|
52
|
+
_stopDisplayLinkIfNeeded();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#pragma mark - GleamView
|
|
56
|
+
|
|
57
|
+
@implementation GleamView {
|
|
58
|
+
UIView *_contentView;
|
|
59
|
+
CAGradientLayer *_shimmerLayer;
|
|
60
|
+
BOOL _loading;
|
|
61
|
+
BOOL _wasLoading;
|
|
62
|
+
BOOL _isRegistered;
|
|
63
|
+
CGFloat _speed;
|
|
64
|
+
CGFloat _delay;
|
|
65
|
+
CGFloat _transitionDuration;
|
|
66
|
+
NSInteger _transitionTypeValue; // 0=fade, 1=slide, 2=dissolve, 3=scale
|
|
67
|
+
CGFloat _intensity;
|
|
68
|
+
GleamDirection _direction;
|
|
69
|
+
UIColor *_baseColor;
|
|
70
|
+
UIColor *_highlightColor;
|
|
71
|
+
|
|
72
|
+
// Transition state
|
|
73
|
+
BOOL _isTransitioning;
|
|
74
|
+
CGFloat _transitionElapsed;
|
|
75
|
+
CGFloat _shimmerOpacity;
|
|
76
|
+
CGFloat _contentAlpha;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
80
|
+
{
|
|
81
|
+
return concreteComponentDescriptorProvider<GleamViewComponentDescriptor>();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
+ (void)_onFrame:(CADisplayLink *)link
|
|
85
|
+
{
|
|
86
|
+
NSArray *views = [_registeredViews allObjects];
|
|
87
|
+
for (GleamView *view in views) {
|
|
88
|
+
[view _tick];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
93
|
+
{
|
|
94
|
+
if (self = [super initWithFrame:frame]) {
|
|
95
|
+
static const auto defaultProps = std::make_shared<const GleamViewProps>();
|
|
96
|
+
_props = defaultProps;
|
|
97
|
+
|
|
98
|
+
_loading = YES;
|
|
99
|
+
_wasLoading = YES;
|
|
100
|
+
_isRegistered = NO;
|
|
101
|
+
_speed = 1.0;
|
|
102
|
+
_delay = 0.0;
|
|
103
|
+
_transitionDuration = 0.3;
|
|
104
|
+
_transitionTypeValue = 0;
|
|
105
|
+
_intensity = 1.0;
|
|
106
|
+
_direction = GleamDirectionLTR;
|
|
107
|
+
_baseColor = [UIColor colorWithRed:0.878 green:0.878 blue:0.878 alpha:1.0];
|
|
108
|
+
_highlightColor = [UIColor colorWithRed:0.961 green:0.961 blue:0.961 alpha:1.0];
|
|
109
|
+
|
|
110
|
+
_isTransitioning = NO;
|
|
111
|
+
_shimmerOpacity = 1.0;
|
|
112
|
+
_contentAlpha = 0.0;
|
|
113
|
+
|
|
114
|
+
_contentView = [[UIView alloc] init];
|
|
115
|
+
_contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
116
|
+
_contentView.alpha = 0.0;
|
|
117
|
+
|
|
118
|
+
_shimmerLayer = [CAGradientLayer layer];
|
|
119
|
+
// Disable implicit animations on the gradient layer
|
|
120
|
+
_shimmerLayer.actions = @{
|
|
121
|
+
@"startPoint": [NSNull null],
|
|
122
|
+
@"endPoint": [NSNull null],
|
|
123
|
+
@"opacity": [NSNull null],
|
|
124
|
+
@"bounds": [NSNull null],
|
|
125
|
+
@"position": [NSNull null],
|
|
126
|
+
@"transform": [NSNull null],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
self.contentView = _contentView;
|
|
130
|
+
}
|
|
131
|
+
return self;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
- (void)layoutSubviews
|
|
135
|
+
{
|
|
136
|
+
[super layoutSubviews];
|
|
137
|
+
_shimmerLayer.frame = self.bounds;
|
|
138
|
+
_shimmerLayer.cornerRadius = self.layer.cornerRadius;
|
|
139
|
+
_shimmerLayer.maskedCorners = self.layer.maskedCorners;
|
|
140
|
+
|
|
141
|
+
if (_loading && !_isRegistered) {
|
|
142
|
+
[self _registerClock];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
- (void)removeFromSuperview
|
|
147
|
+
{
|
|
148
|
+
[self _unregisterClock];
|
|
149
|
+
_shimmerLayer.mask = nil;
|
|
150
|
+
[super removeFromSuperview];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
- (void)dealloc
|
|
154
|
+
{
|
|
155
|
+
[self _unregisterClock];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
- (void)_registerClock
|
|
159
|
+
{
|
|
160
|
+
if (!_isRegistered) {
|
|
161
|
+
_isRegistered = YES;
|
|
162
|
+
_registerView(self);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
- (void)_unregisterClock
|
|
167
|
+
{
|
|
168
|
+
if (_isRegistered) {
|
|
169
|
+
_isRegistered = NO;
|
|
170
|
+
_unregisterView(self);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
175
|
+
{
|
|
176
|
+
const auto &oldViewProps = *std::static_pointer_cast<GleamViewProps const>(_props);
|
|
177
|
+
const auto &newViewProps = *std::static_pointer_cast<GleamViewProps const>(props);
|
|
178
|
+
|
|
179
|
+
if (oldViewProps.speed != newViewProps.speed) {
|
|
180
|
+
_speed = newViewProps.speed / 1000.0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (oldViewProps.delay != newViewProps.delay) {
|
|
184
|
+
_delay = newViewProps.delay / 1000.0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (oldViewProps.transitionDuration != newViewProps.transitionDuration) {
|
|
188
|
+
_transitionDuration = newViewProps.transitionDuration / 1000.0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (oldViewProps.transitionType != newViewProps.transitionType) {
|
|
192
|
+
auto tt = newViewProps.transitionType;
|
|
193
|
+
if (tt == GleamViewTransitionType::Shrink) _transitionTypeValue = 1;
|
|
194
|
+
else if (tt == GleamViewTransitionType::Collapse) _transitionTypeValue = 2;
|
|
195
|
+
else _transitionTypeValue = 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (oldViewProps.intensity != newViewProps.intensity) {
|
|
199
|
+
_intensity = fmin(fmax(newViewProps.intensity, 0.0), 1.0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (oldViewProps.direction != newViewProps.direction) {
|
|
203
|
+
auto dir = newViewProps.direction;
|
|
204
|
+
if (dir == GleamViewDirection::Rtl) {
|
|
205
|
+
_direction = GleamDirectionRTL;
|
|
206
|
+
} else if (dir == GleamViewDirection::Ttb) {
|
|
207
|
+
_direction = GleamDirectionTTB;
|
|
208
|
+
} else {
|
|
209
|
+
_direction = GleamDirectionLTR;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (oldViewProps.baseColor != newViewProps.baseColor) {
|
|
214
|
+
UIColor *color = RCTUIColorFromSharedColor(newViewProps.baseColor);
|
|
215
|
+
_baseColor = color ?: [UIColor colorWithRed:0.878 green:0.878 blue:0.878 alpha:1.0];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (oldViewProps.highlightColor != newViewProps.highlightColor) {
|
|
219
|
+
UIColor *color = RCTUIColorFromSharedColor(newViewProps.highlightColor);
|
|
220
|
+
_highlightColor = color ?: [UIColor colorWithRed:0.961 green:0.961 blue:0.961 alpha:1.0];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (oldViewProps.loading != newViewProps.loading) {
|
|
224
|
+
_loading = newViewProps.loading;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[self _updateShimmerColors];
|
|
228
|
+
[self _applyLoadingState];
|
|
229
|
+
|
|
230
|
+
[super updateProps:props oldProps:oldProps];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#pragma mark - Private
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Compute shimmer progress from global time.
|
|
237
|
+
* All views with same speed/delay share the same progress value.
|
|
238
|
+
* Uses cosine easing for smooth looping (matches AccelerateDecelerateInterpolator).
|
|
239
|
+
*/
|
|
240
|
+
- (CGFloat)_computeProgress
|
|
241
|
+
{
|
|
242
|
+
CFTimeInterval time = CACurrentMediaTime();
|
|
243
|
+
CGFloat effectiveTime = fmax(time - _delay, 0.0);
|
|
244
|
+
CGFloat rawProgress = fmod(effectiveTime, _speed) / _speed;
|
|
245
|
+
// Cosine easing: (1 - cos(rawProgress * PI)) / 2
|
|
246
|
+
return (1.0 - cos(rawProgress * M_PI)) / 2.0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
- (void)_tick
|
|
250
|
+
{
|
|
251
|
+
if (_isTransitioning) {
|
|
252
|
+
_transitionElapsed += _displayLink.duration;
|
|
253
|
+
CGFloat t = fmin(_transitionElapsed / _transitionDuration, 1.0);
|
|
254
|
+
// Ease out
|
|
255
|
+
CGFloat eased = 1.0 - (1.0 - t) * (1.0 - t);
|
|
256
|
+
|
|
257
|
+
switch (_transitionTypeValue) {
|
|
258
|
+
case 1: { // Shrink — scale down with mask clipping
|
|
259
|
+
_contentAlpha = eased;
|
|
260
|
+
_contentView.alpha = _contentAlpha;
|
|
261
|
+
CGFloat shrinkOpacity = 1.0 - fmin(eased * 2.5, 1.0);
|
|
262
|
+
_shimmerLayer.opacity = shrinkOpacity;
|
|
263
|
+
CGFloat scale = 1.0 - eased * 0.5;
|
|
264
|
+
CGRect bounds = self.bounds;
|
|
265
|
+
CGFloat w = bounds.size.width * scale;
|
|
266
|
+
CGFloat h = bounds.size.height * scale;
|
|
267
|
+
CGFloat x = (bounds.size.width - w) / 2.0;
|
|
268
|
+
CGFloat y = (bounds.size.height - h) / 2.0;
|
|
269
|
+
|
|
270
|
+
CAShapeLayer *mask = (CAShapeLayer *)_shimmerLayer.mask;
|
|
271
|
+
if (!mask) {
|
|
272
|
+
mask = [CAShapeLayer layer];
|
|
273
|
+
mask.actions = @{@"path": [NSNull null]};
|
|
274
|
+
_shimmerLayer.mask = mask;
|
|
275
|
+
}
|
|
276
|
+
mask.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, fmax(w, 0.1), fmax(h, 0.1)) cornerRadius:self.layer.cornerRadius * scale].CGPath;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
case 2: { // Collapse — vertically then horizontally via clip rect
|
|
280
|
+
_contentAlpha = eased;
|
|
281
|
+
_contentView.alpha = _contentAlpha;
|
|
282
|
+
CGFloat collapseOpacity = 1.0 - fmin(eased * 2.5, 1.0);
|
|
283
|
+
_shimmerLayer.opacity = collapseOpacity;
|
|
284
|
+
CGRect bounds = self.bounds;
|
|
285
|
+
CGFloat scaleY = eased < 0.6 ? 1.0 - (eased / 0.6) * 0.98 : 0.02;
|
|
286
|
+
CGFloat scaleX = eased < 0.6 ? 1.0 : 1.0 - ((eased - 0.6) / 0.4);
|
|
287
|
+
CGFloat w = bounds.size.width * scaleX;
|
|
288
|
+
CGFloat h = bounds.size.height * scaleY;
|
|
289
|
+
CGFloat x = (bounds.size.width - w) / 2.0;
|
|
290
|
+
CGFloat y = (bounds.size.height - h) / 2.0;
|
|
291
|
+
|
|
292
|
+
// Use a mask layer to clip the shimmer to the collapsing rect
|
|
293
|
+
CAShapeLayer *mask = (CAShapeLayer *)_shimmerLayer.mask;
|
|
294
|
+
if (!mask) {
|
|
295
|
+
mask = [CAShapeLayer layer];
|
|
296
|
+
mask.actions = @{@"path": [NSNull null]};
|
|
297
|
+
_shimmerLayer.mask = mask;
|
|
298
|
+
}
|
|
299
|
+
mask.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, fmax(w, 0.1), fmax(h, 0.1)) cornerRadius:self.layer.cornerRadius].CGPath;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
default: // Fade
|
|
303
|
+
_contentAlpha = eased;
|
|
304
|
+
_contentView.alpha = _contentAlpha;
|
|
305
|
+
_shimmerOpacity = 1.0 - eased;
|
|
306
|
+
_shimmerLayer.opacity = _shimmerOpacity;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
[self _updateGradientPosition];
|
|
311
|
+
|
|
312
|
+
if (t >= 1.0) {
|
|
313
|
+
[self _finishTransition];
|
|
314
|
+
}
|
|
315
|
+
} else if (_loading) {
|
|
316
|
+
[self _updateGradientPosition];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
- (void)_updateGradientPosition
|
|
321
|
+
{
|
|
322
|
+
CGFloat progress = [self _computeProgress];
|
|
323
|
+
|
|
324
|
+
CGPoint startPoint, endPoint;
|
|
325
|
+
|
|
326
|
+
switch (_direction) {
|
|
327
|
+
case GleamDirectionRTL: {
|
|
328
|
+
CGFloat s = 2.0 - progress * 3.0;
|
|
329
|
+
startPoint = CGPointMake(s, 0.5);
|
|
330
|
+
endPoint = CGPointMake(s - 1.0, 0.5);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
case GleamDirectionTTB: {
|
|
334
|
+
CGFloat s = -1.0 + progress * 3.0;
|
|
335
|
+
startPoint = CGPointMake(0.5, s);
|
|
336
|
+
endPoint = CGPointMake(0.5, s + 1.0);
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
case GleamDirectionLTR:
|
|
340
|
+
default: {
|
|
341
|
+
CGFloat s = -1.0 + progress * 3.0;
|
|
342
|
+
startPoint = CGPointMake(s, 0.5);
|
|
343
|
+
endPoint = CGPointMake(s + 1.0, 0.5);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
_shimmerLayer.startPoint = startPoint;
|
|
349
|
+
_shimmerLayer.endPoint = endPoint;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
- (void)_updateShimmerColors
|
|
353
|
+
{
|
|
354
|
+
UIColor *effectiveHighlight = _highlightColor;
|
|
355
|
+
if (_intensity < 1.0) {
|
|
356
|
+
CGFloat br, bg, bb, ba;
|
|
357
|
+
CGFloat hr, hg, hb, ha;
|
|
358
|
+
[_baseColor getRed:&br green:&bg blue:&bb alpha:&ba];
|
|
359
|
+
[_highlightColor getRed:&hr green:&hg blue:&hb alpha:&ha];
|
|
360
|
+
CGFloat r = br + (hr - br) * _intensity;
|
|
361
|
+
CGFloat g = bg + (hg - bg) * _intensity;
|
|
362
|
+
CGFloat b = bb + (hb - bb) * _intensity;
|
|
363
|
+
CGFloat a = ba + (ha - ba) * _intensity;
|
|
364
|
+
effectiveHighlight = [UIColor colorWithRed:r green:g blue:b alpha:a];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
_shimmerLayer.colors = @[
|
|
368
|
+
(id)_baseColor.CGColor,
|
|
369
|
+
(id)effectiveHighlight.CGColor,
|
|
370
|
+
(id)_baseColor.CGColor,
|
|
371
|
+
];
|
|
372
|
+
_shimmerLayer.locations = @[@0.0, @0.5, @1.0];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
- (void)_applyLoadingState
|
|
376
|
+
{
|
|
377
|
+
if (_loading) {
|
|
378
|
+
_isTransitioning = NO;
|
|
379
|
+
_contentAlpha = 0.0;
|
|
380
|
+
_shimmerOpacity = 1.0;
|
|
381
|
+
_contentView.alpha = 0.0;
|
|
382
|
+
_shimmerLayer.opacity = 1.0;
|
|
383
|
+
_shimmerLayer.frame = self.bounds;
|
|
384
|
+
if (_shimmerLayer.superlayer != self.layer) {
|
|
385
|
+
[self.layer addSublayer:_shimmerLayer];
|
|
386
|
+
}
|
|
387
|
+
[self _registerClock];
|
|
388
|
+
_wasLoading = YES;
|
|
389
|
+
} else {
|
|
390
|
+
BOOL shouldAnimate = _wasLoading && _transitionDuration > 0;
|
|
391
|
+
_wasLoading = NO;
|
|
392
|
+
|
|
393
|
+
if (shouldAnimate) {
|
|
394
|
+
_isTransitioning = YES;
|
|
395
|
+
_transitionElapsed = 0.0;
|
|
396
|
+
// Clock stays registered to drive the transition
|
|
397
|
+
} else {
|
|
398
|
+
[self _unregisterClock];
|
|
399
|
+
_contentView.alpha = 1.0;
|
|
400
|
+
_shimmerLayer.opacity = 0.0;
|
|
401
|
+
[_shimmerLayer removeFromSuperlayer];
|
|
402
|
+
[self _emitTransitionEnd:YES];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
- (void)_finishTransition
|
|
408
|
+
{
|
|
409
|
+
_isTransitioning = NO;
|
|
410
|
+
[self _unregisterClock];
|
|
411
|
+
_contentView.alpha = 1.0;
|
|
412
|
+
_shimmerLayer.opacity = 0.0;
|
|
413
|
+
_shimmerLayer.transform = CATransform3DIdentity;
|
|
414
|
+
_shimmerLayer.mask = nil;
|
|
415
|
+
_shimmerLayer.frame = self.bounds;
|
|
416
|
+
[_shimmerLayer removeFromSuperlayer];
|
|
417
|
+
[self _updateShimmerColors]; // Reset colors after dissolve
|
|
418
|
+
[self _emitTransitionEnd:YES];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
- (void)_emitTransitionEnd:(BOOL)finished
|
|
422
|
+
{
|
|
423
|
+
if (!_eventEmitter) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
auto emitter = std::static_pointer_cast<const GleamViewEventEmitter>(_eventEmitter);
|
|
427
|
+
emitter->onTransitionEnd(GleamViewEventEmitter::OnTransitionEnd{.finished = finished});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
@end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codegenNativeComponent,
|
|
3
|
+
type ColorValue,
|
|
4
|
+
type ViewProps,
|
|
5
|
+
type CodegenTypes,
|
|
6
|
+
} from 'react-native';
|
|
7
|
+
|
|
8
|
+
export interface NativeProps extends ViewProps {
|
|
9
|
+
loading?: CodegenTypes.WithDefault<boolean, true>;
|
|
10
|
+
speed?: CodegenTypes.WithDefault<CodegenTypes.Float, 1000>;
|
|
11
|
+
direction?: CodegenTypes.WithDefault<'ltr' | 'rtl' | 'ttb', 'ltr'>;
|
|
12
|
+
delay?: CodegenTypes.WithDefault<CodegenTypes.Float, 0>;
|
|
13
|
+
transitionDuration?: CodegenTypes.WithDefault<CodegenTypes.Float, 300>;
|
|
14
|
+
transitionType?: CodegenTypes.WithDefault<
|
|
15
|
+
'fade' | 'shrink' | 'collapse',
|
|
16
|
+
'fade'
|
|
17
|
+
>;
|
|
18
|
+
intensity?: CodegenTypes.WithDefault<CodegenTypes.Float, 1>;
|
|
19
|
+
baseColor?: ColorValue;
|
|
20
|
+
highlightColor?: ColorValue;
|
|
21
|
+
onTransitionEnd?: CodegenTypes.DirectEventHandler<
|
|
22
|
+
Readonly<{ finished: boolean }>
|
|
23
|
+
>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default codegenNativeComponent<NativeProps>('GleamView');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export { default as GleamView } from './GleamViewNativeComponent';
|
|
4
|
+
export let GleamDirection = /*#__PURE__*/function (GleamDirection) {
|
|
5
|
+
GleamDirection["LeftToRight"] = "ltr";
|
|
6
|
+
GleamDirection["RightToLeft"] = "rtl";
|
|
7
|
+
GleamDirection["TopToBottom"] = "ttb";
|
|
8
|
+
return GleamDirection;
|
|
9
|
+
}({});
|
|
10
|
+
export let GleamTransition = /*#__PURE__*/function (GleamTransition) {
|
|
11
|
+
GleamTransition["Fade"] = "fade";
|
|
12
|
+
GleamTransition["Shrink"] = "shrink";
|
|
13
|
+
GleamTransition["Collapse"] = "collapse";
|
|
14
|
+
return GleamTransition;
|
|
15
|
+
}({});
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["default","GleamView","GleamDirection","GleamTransition"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,OAAO,IAAIC,SAAS,QAAQ,4BAA4B;AAGjE,WAAYC,cAAc,0BAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAdA,cAAc;EAAA,OAAdA,cAAc;AAAA;AAM1B,WAAYC,eAAe,0BAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAfA,eAAe;EAAA,OAAfA,eAAe;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ColorValue, type ViewProps, type CodegenTypes } from 'react-native';
|
|
2
|
+
export interface NativeProps extends ViewProps {
|
|
3
|
+
loading?: CodegenTypes.WithDefault<boolean, true>;
|
|
4
|
+
speed?: CodegenTypes.WithDefault<CodegenTypes.Float, 1000>;
|
|
5
|
+
direction?: CodegenTypes.WithDefault<'ltr' | 'rtl' | 'ttb', 'ltr'>;
|
|
6
|
+
delay?: CodegenTypes.WithDefault<CodegenTypes.Float, 0>;
|
|
7
|
+
transitionDuration?: CodegenTypes.WithDefault<CodegenTypes.Float, 300>;
|
|
8
|
+
transitionType?: CodegenTypes.WithDefault<'fade' | 'shrink' | 'collapse', 'fade'>;
|
|
9
|
+
intensity?: CodegenTypes.WithDefault<CodegenTypes.Float, 1>;
|
|
10
|
+
baseColor?: ColorValue;
|
|
11
|
+
highlightColor?: ColorValue;
|
|
12
|
+
onTransitionEnd?: CodegenTypes.DirectEventHandler<Readonly<{
|
|
13
|
+
finished: boolean;
|
|
14
|
+
}>>;
|
|
15
|
+
}
|
|
16
|
+
declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<NativeProps>;
|
|
17
|
+
export default _default;
|
|
18
|
+
//# sourceMappingURL=GleamViewNativeComponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GleamViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/GleamViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,OAAO,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;IACnE,KAAK,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACxD,kBAAkB,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvE,cAAc,CAAC,EAAE,YAAY,CAAC,WAAW,CACvC,MAAM,GAAG,QAAQ,GAAG,UAAU,EAC9B,MAAM,CACP,CAAC;IACF,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5D,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,eAAe,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAC/C,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAChC,CAAC;CACH;;AAED,wBAAgE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { default as GleamView } from './GleamViewNativeComponent';
|
|
2
|
+
export type { NativeProps as GleamViewProps } from './GleamViewNativeComponent';
|
|
3
|
+
export declare enum GleamDirection {
|
|
4
|
+
LeftToRight = "ltr",
|
|
5
|
+
RightToLeft = "rtl",
|
|
6
|
+
TopToBottom = "ttb"
|
|
7
|
+
}
|
|
8
|
+
export declare enum GleamTransition {
|
|
9
|
+
Fade = "fade",
|
|
10
|
+
Shrink = "shrink",
|
|
11
|
+
Collapse = "collapse"
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAClE,YAAY,EAAE,WAAW,IAAI,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEhF,oBAAY,cAAc;IACxB,WAAW,QAAQ;IACnB,WAAW,QAAQ;IACnB,WAAW,QAAQ;CACpB;AAED,oBAAY,eAAe;IACzB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,QAAQ,aAAa;CACtB"}
|