@jobber/components-native 0.113.1 → 0.113.2
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/dist/package.json +2 -2
- package/dist/src/ActivityIndicator/ActivityIndicator.js +40 -10
- package/dist/src/ActivityIndicator/ActivityIndicator.test.js +35 -0
- package/dist/src/FormatFile/components/MediaView/MediaView.js +10 -8
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/ActivityIndicator/ActivityIndicator.test.tsx +46 -0
- package/src/ActivityIndicator/ActivityIndicator.tsx +98 -32
- package/src/ActivityIndicator/guide.md +43 -18
- package/src/FormatFile/components/MediaView/MediaView.tsx +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.113.
|
|
3
|
+
"version": "0.113.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -124,5 +124,5 @@
|
|
|
124
124
|
"react-native-screens": ">=4.18.0",
|
|
125
125
|
"react-native-svg": ">=12.0.0"
|
|
126
126
|
},
|
|
127
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "106e281adc9a3b0e87e125e1a9f1c945ce2de3cd"
|
|
128
128
|
}
|
|
@@ -71,6 +71,52 @@ describe("ActivityIndicator", () => {
|
|
|
71
71
|
expect(flattenedStyle.width).toBe(44);
|
|
72
72
|
expect(flattenedStyle.height).toBe(44);
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
it("uses a fixed-length arc for small (no Layer 2/3 wrappers)", () => {
|
|
76
|
+
const { getByTestId } = render(<ActivityIndicator size="small" />);
|
|
77
|
+
const outer = getByTestId(testId).children[0] as ReactTestInstance;
|
|
78
|
+
const outerChildren = React.Children.toArray(outer.props.children);
|
|
79
|
+
|
|
80
|
+
function typeName(node: unknown): string {
|
|
81
|
+
if (!node || typeof node !== "object") return "";
|
|
82
|
+
const element = node as {
|
|
83
|
+
type?: { displayName?: string; name?: string };
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return element.type?.displayName || element.type?.name || "";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Small: outer rotation wrapper → Svg directly (no inner Layer 2 view).
|
|
90
|
+
expect(
|
|
91
|
+
outerChildren.some(child => typeName(child).startsWith("Svg")),
|
|
92
|
+
).toBe(true);
|
|
93
|
+
|
|
94
|
+
function findArcCircle(node: unknown): ReactTestInstance | undefined {
|
|
95
|
+
if (!node || typeof node !== "object") return undefined;
|
|
96
|
+
const element = node as ReactTestInstance;
|
|
97
|
+
const name = typeName(element);
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
(name === "Circle" || name === "RNSVGCircle") &&
|
|
101
|
+
element.props.strokeDasharray === "40, 200"
|
|
102
|
+
) {
|
|
103
|
+
return element;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const kids = React.Children.toArray(element.props?.children ?? []);
|
|
107
|
+
|
|
108
|
+
for (const kid of kids) {
|
|
109
|
+
const found = findArcCircle(kid);
|
|
110
|
+
if (found) return found;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const arc = findArcCircle(outer);
|
|
117
|
+
expect(arc).toBeTruthy();
|
|
118
|
+
expect(arc?.props.strokeDashoffset).toBe(0);
|
|
119
|
+
});
|
|
74
120
|
});
|
|
75
121
|
|
|
76
122
|
describe('deprecated size="large" alias', () => {
|
|
@@ -58,6 +58,8 @@ export interface ActivityIndicatorProps {
|
|
|
58
58
|
readonly testID?: string;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
type ResolvedSize = "small" | "base";
|
|
62
|
+
|
|
61
63
|
const SIZE_PX = { small: 28, base: 44 } as const;
|
|
62
64
|
// Stroke widths are expressed in viewBox units (the SVG is 48 across).
|
|
63
65
|
// The small variant uses a thicker stroke in viewBox units to compensate
|
|
@@ -73,9 +75,34 @@ const RADIUS = 20;
|
|
|
73
75
|
// one stroke segment is visible at a time.
|
|
74
76
|
const GAP = 200;
|
|
75
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Per-size motion profile. Adding a size is an additive map entry — not a
|
|
80
|
+
* new boolean branch. `layers: 1` is Layer 1 only (fixed arc); `layers: 3`
|
|
81
|
+
* runs the full Material 3 indeterminate ring (Layers 1–3).
|
|
82
|
+
*
|
|
83
|
+
* Small matches web's ActivityIndicator.module.css (`.small .svg` /
|
|
84
|
+
* `.small .arc`): Layer 1 only, spun ~1.8× faster. Web hardcodes 1000ms
|
|
85
|
+
* (≈1803ms ÷ 1.8); we match that absolute duration.
|
|
86
|
+
*/
|
|
87
|
+
const MOTION_BY_SIZE = {
|
|
88
|
+
small: {
|
|
89
|
+
layers: 1 as const,
|
|
90
|
+
linearRotateMs: 1000,
|
|
91
|
+
dasharray: `40, ${GAP}`,
|
|
92
|
+
},
|
|
93
|
+
base: {
|
|
94
|
+
layers: 3 as const,
|
|
95
|
+
linearRotateMs: tokens["timing-indicator--linear-rotate"],
|
|
96
|
+
},
|
|
97
|
+
} satisfies Record<
|
|
98
|
+
ResolvedSize,
|
|
99
|
+
| { layers: 1; linearRotateMs: number; dasharray: string }
|
|
100
|
+
| { layers: 3; linearRotateMs: number }
|
|
101
|
+
>;
|
|
102
|
+
|
|
76
103
|
function resolveLegacySize(
|
|
77
104
|
size: "small" | "base" | DeprecatedSize,
|
|
78
|
-
):
|
|
105
|
+
): ResolvedSize {
|
|
79
106
|
if (size === "large") {
|
|
80
107
|
if (__DEV__) {
|
|
81
108
|
console.warn(
|
|
@@ -143,6 +170,7 @@ export function ActivityIndicator({
|
|
|
143
170
|
|
|
144
171
|
return (
|
|
145
172
|
<FullMotionRing
|
|
173
|
+
size={resolvedSize}
|
|
146
174
|
pixelSize={pixelSize}
|
|
147
175
|
strokeWidth={strokeWidth}
|
|
148
176
|
arcColor={themeTokens["color-icon"]}
|
|
@@ -170,11 +198,13 @@ interface RingProps {
|
|
|
170
198
|
}
|
|
171
199
|
|
|
172
200
|
interface FullMotionRingProps extends RingProps {
|
|
201
|
+
readonly size: ResolvedSize;
|
|
173
202
|
readonly arcColor: string;
|
|
174
203
|
readonly trackColor: string;
|
|
175
204
|
}
|
|
176
205
|
|
|
177
206
|
function FullMotionRing({
|
|
207
|
+
size,
|
|
178
208
|
pixelSize,
|
|
179
209
|
strokeWidth,
|
|
180
210
|
arcColor,
|
|
@@ -183,25 +213,32 @@ function FullMotionRing({
|
|
|
183
213
|
testID,
|
|
184
214
|
a11yProps,
|
|
185
215
|
}: FullMotionRingProps) {
|
|
216
|
+
const motion = MOTION_BY_SIZE[size];
|
|
217
|
+
|
|
186
218
|
// Layer 1 — outer linear rotation. Continuous, constant speed.
|
|
187
219
|
const outerRotate = useSharedValue(0);
|
|
188
220
|
// Layer 2 — inner 8-phase rotation. Eight 135° eased segments per cycle
|
|
189
221
|
// (totalling 1080° over `timing-indicator--cycle`) reproduce the
|
|
190
|
-
// canonical Material Web "8-phase" rhythm.
|
|
222
|
+
// canonical Material Web "8-phase" rhythm. Only when motion.layers === 3.
|
|
191
223
|
const innerRotate = useSharedValue(0);
|
|
192
224
|
// Layer 3 — arc length and dash offset. Drives the visible arc growing
|
|
193
225
|
// from ~1 viewBox-unit to ~90 viewBox-units and sliding around the ring.
|
|
226
|
+
// Only when motion.layers === 3; fixed-arc sizes use motion.dasharray.
|
|
194
227
|
const arcProgress = useSharedValue(0);
|
|
195
228
|
|
|
196
229
|
useEffect(() => {
|
|
197
230
|
outerRotate.value = withRepeat(
|
|
198
231
|
withTiming(360, {
|
|
199
|
-
duration:
|
|
232
|
+
duration: motion.linearRotateMs,
|
|
200
233
|
easing: Easing.linear,
|
|
201
234
|
}),
|
|
202
235
|
-1,
|
|
203
236
|
);
|
|
204
237
|
|
|
238
|
+
if (motion.layers === 1) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
205
242
|
const phaseDuration = tokens["timing-indicator--cycle"] / 8;
|
|
206
243
|
const phaseEasing = Easing.bezier(0.4, 0, 0.2, 1);
|
|
207
244
|
innerRotate.value = withRepeat(
|
|
@@ -227,7 +264,7 @@ function FullMotionRing({
|
|
|
227
264
|
);
|
|
228
265
|
// We intentionally do not include the shared values in the dependency
|
|
229
266
|
// array — they are stable references created by useSharedValue.
|
|
230
|
-
}, []);
|
|
267
|
+
}, [motion]);
|
|
231
268
|
|
|
232
269
|
const outerStyle = useAnimatedStyle(() => ({
|
|
233
270
|
transform: [{ rotate: `${outerRotate.value}deg` }],
|
|
@@ -268,6 +305,62 @@ function FullMotionRing({
|
|
|
268
305
|
};
|
|
269
306
|
});
|
|
270
307
|
|
|
308
|
+
const track = (
|
|
309
|
+
<Circle
|
|
310
|
+
cx={CENTER}
|
|
311
|
+
cy={CENTER}
|
|
312
|
+
r={RADIUS}
|
|
313
|
+
fill="none"
|
|
314
|
+
stroke={trackColor}
|
|
315
|
+
strokeWidth={strokeWidth}
|
|
316
|
+
/>
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const ring =
|
|
320
|
+
motion.layers === 1 ? (
|
|
321
|
+
// Fixed-length arc, Layer 1 rotation only — no Layer 2 wrapper.
|
|
322
|
+
<Svg
|
|
323
|
+
width={pixelSize}
|
|
324
|
+
height={pixelSize}
|
|
325
|
+
viewBox={`0 0 ${VIEWBOX} ${VIEWBOX}`}
|
|
326
|
+
>
|
|
327
|
+
{track}
|
|
328
|
+
<Circle
|
|
329
|
+
cx={CENTER}
|
|
330
|
+
cy={CENTER}
|
|
331
|
+
r={RADIUS}
|
|
332
|
+
fill="none"
|
|
333
|
+
stroke={arcColor}
|
|
334
|
+
strokeWidth={strokeWidth}
|
|
335
|
+
strokeLinecap="round"
|
|
336
|
+
strokeDasharray={motion.dasharray}
|
|
337
|
+
strokeDashoffset={0}
|
|
338
|
+
/>
|
|
339
|
+
</Svg>
|
|
340
|
+
) : (
|
|
341
|
+
<Animated.View
|
|
342
|
+
style={[{ width: pixelSize, height: pixelSize }, innerStyle]}
|
|
343
|
+
>
|
|
344
|
+
<Svg
|
|
345
|
+
width={pixelSize}
|
|
346
|
+
height={pixelSize}
|
|
347
|
+
viewBox={`0 0 ${VIEWBOX} ${VIEWBOX}`}
|
|
348
|
+
>
|
|
349
|
+
{track}
|
|
350
|
+
<AnimatedCircle
|
|
351
|
+
cx={CENTER}
|
|
352
|
+
cy={CENTER}
|
|
353
|
+
r={RADIUS}
|
|
354
|
+
fill="none"
|
|
355
|
+
stroke={arcColor}
|
|
356
|
+
strokeWidth={strokeWidth}
|
|
357
|
+
strokeLinecap="round"
|
|
358
|
+
animatedProps={arcAnimatedProps}
|
|
359
|
+
/>
|
|
360
|
+
</Svg>
|
|
361
|
+
</Animated.View>
|
|
362
|
+
);
|
|
363
|
+
|
|
271
364
|
return (
|
|
272
365
|
<Animated.View
|
|
273
366
|
style={[{ justifyContent: "center", alignItems: "center" }, style]}
|
|
@@ -277,34 +370,7 @@ function FullMotionRing({
|
|
|
277
370
|
<Animated.View
|
|
278
371
|
style={[{ width: pixelSize, height: pixelSize }, outerStyle]}
|
|
279
372
|
>
|
|
280
|
-
|
|
281
|
-
style={[{ width: pixelSize, height: pixelSize }, innerStyle]}
|
|
282
|
-
>
|
|
283
|
-
<Svg
|
|
284
|
-
width={pixelSize}
|
|
285
|
-
height={pixelSize}
|
|
286
|
-
viewBox={`0 0 ${VIEWBOX} ${VIEWBOX}`}
|
|
287
|
-
>
|
|
288
|
-
<Circle
|
|
289
|
-
cx={CENTER}
|
|
290
|
-
cy={CENTER}
|
|
291
|
-
r={RADIUS}
|
|
292
|
-
fill="none"
|
|
293
|
-
stroke={trackColor}
|
|
294
|
-
strokeWidth={strokeWidth}
|
|
295
|
-
/>
|
|
296
|
-
<AnimatedCircle
|
|
297
|
-
cx={CENTER}
|
|
298
|
-
cy={CENTER}
|
|
299
|
-
r={RADIUS}
|
|
300
|
-
fill="none"
|
|
301
|
-
stroke={arcColor}
|
|
302
|
-
strokeWidth={strokeWidth}
|
|
303
|
-
strokeLinecap="round"
|
|
304
|
-
animatedProps={arcAnimatedProps}
|
|
305
|
-
/>
|
|
306
|
-
</Svg>
|
|
307
|
-
</Animated.View>
|
|
373
|
+
{ring}
|
|
308
374
|
</Animated.View>
|
|
309
375
|
</Animated.View>
|
|
310
376
|
);
|
|
@@ -101,36 +101,60 @@ navigation order beyond the initial announcement.
|
|
|
101
101
|
|
|
102
102
|
## Animation Model
|
|
103
103
|
|
|
104
|
-
The default presentation renders Material Design 3's three-layer
|
|
105
|
-
ring. The implementation uses `react-native-svg` for the ring
|
|
106
|
-
`react-native-reanimated` 3 for the motion — all worklet-driven so
|
|
107
|
-
runs on the UI thread regardless of JS thread load.
|
|
104
|
+
The default (`base`) presentation renders Material Design 3's three-layer
|
|
105
|
+
indeterminate ring. The implementation uses `react-native-svg` for the ring
|
|
106
|
+
geometry and `react-native-reanimated` 3 for the motion — all worklet-driven so
|
|
107
|
+
the animation runs on the UI thread regardless of JS thread load.
|
|
108
108
|
|
|
109
|
-
The three concurrent animations:
|
|
109
|
+
The three concurrent animations (base only):
|
|
110
110
|
|
|
111
111
|
1. A wrapping `Animated.View` rotates continuously at a constant linear speed
|
|
112
|
-
driven by `tokens["timing-indicator
|
|
113
|
-
This is the "outer" rotation that prevents the ring from ever
|
|
112
|
+
driven by `tokens["timing-indicator--linear-rotate"]` (1568ms per
|
|
113
|
+
revolution). This is the "outer" rotation that prevents the ring from ever
|
|
114
|
+
sitting still.
|
|
114
115
|
|
|
115
116
|
2. An inner transform rotates through 1080° per cycle on a
|
|
116
|
-
`tokens["timing-indicator
|
|
117
|
+
`tokens["timing-indicator--cycle"]` (5332ms) cubic-bezier easing curve
|
|
117
118
|
approximating Material Web's `indicatorRotateArc` keyframe. Combined with
|
|
118
119
|
Layer 1, this produces the eight-phase rhythm that reads as non-periodic.
|
|
119
120
|
|
|
120
121
|
3. An animated `<Circle>` (via `Animated.createAnimatedComponent(Circle)`) has
|
|
121
122
|
its `strokeDasharray` and `strokeDashoffset` driven by `useAnimatedProps` on
|
|
122
|
-
a `tokens["timing-indicator
|
|
123
|
-
grows from "barely visible" to roughly 270° of the ring and shrinks back.
|
|
124
|
-
second arc renders 180° out of phase.
|
|
123
|
+
a `tokens["timing-indicator--arc"]` (1333ms) sub-cycle, so the visible arc
|
|
124
|
+
grows from "barely visible" to roughly 270° of the ring and shrinks back.
|
|
125
125
|
|
|
126
126
|
A static `<Circle>` of the same dimensions renders the track ring beneath the
|
|
127
127
|
active arcs, filled with `themeTokens["color-surface--active"]` so the track
|
|
128
128
|
adapts to whichever surface (`surface`, `surface--background`,
|
|
129
129
|
`surface--background--subtle`) the indicator is placed on.
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
### Size → motion config
|
|
132
|
+
|
|
133
|
+
Motion timing is looked up from `MOTION_BY_SIZE` (alongside `SIZE_PX` /
|
|
134
|
+
`STROKE_WIDTH`), not from size-name booleans. Each entry declares `layers` (`1`
|
|
135
|
+
= fixed arc / Layer 1 only; `3` = full Material ring), `linearRotateMs`, and —
|
|
136
|
+
for fixed-arc sizes — a static `dasharray`. A future size is an additive map
|
|
137
|
+
entry rather than nested `isSmall` / `isLarge` branches.
|
|
138
|
+
|
|
139
|
+
### Small motion (parity with web)
|
|
140
|
+
|
|
141
|
+
`size="small"` deliberately does **not** run Layers 2 and 3. Matching web's
|
|
142
|
+
`ActivityIndicator.module.css` (`.small .svg` / `.small .arc`):
|
|
143
|
+
|
|
144
|
+
- Only Layer 1 runs — a fixed-length arc (`strokeDasharray: 40, 200`) spun by
|
|
145
|
+
the outer rotation.
|
|
146
|
+
- Layer 1 duration is `1000ms` (~1.8× faster than web's base linear rotate), so
|
|
147
|
+
the simpler single arc still feels lively.
|
|
148
|
+
- The 8-phase inner rotation and expand/contract arc are omitted because that
|
|
149
|
+
layered motion reads as too busy at 28px.
|
|
150
|
+
|
|
151
|
+
This keeps FormatFile grid thumbnails and other small call sites visually
|
|
152
|
+
aligned with web's small indicator, not just size-matched.
|
|
153
|
+
|
|
154
|
+
The literal base durations match Material Web's published progress motion spec;
|
|
155
|
+
the same values ship from `@jobber/design`'s `timing.tokens.json` to both web
|
|
156
|
+
and mobile so the base rhythm is identical across platforms. The small spin
|
|
157
|
+
duration is a CSS literal on web and a matching constant on native (`1000ms`).
|
|
134
158
|
|
|
135
159
|
## Reduced Motion
|
|
136
160
|
|
|
@@ -156,10 +180,10 @@ shipped — the original 0.55 lower bound was too subtle to read as activity.
|
|
|
156
180
|
|
|
157
181
|
## Sizing
|
|
158
182
|
|
|
159
|
-
| `size` | Outer diameter | Stroke width |
|
|
160
|
-
| ------- | -------------- | ----------------------------------- |
|
|
161
|
-
| `small` | 28 px | proportional to web's small variant |
|
|
162
|
-
| `base` | 44 px | proportional to web's base variant |
|
|
183
|
+
| `size` | Outer diameter | Stroke width | Motion |
|
|
184
|
+
| ------- | -------------- | ----------------------------------- | -------------------------------------------- |
|
|
185
|
+
| `small` | 28 px | proportional to web's small variant | Layer 1 only; fixed arc; 1000ms spin |
|
|
186
|
+
| `base` | 44 px | proportional to web's base variant | Full three-layer Material indeterminate ring |
|
|
163
187
|
|
|
164
188
|
Sizes are unified across iOS, Android, and web. The component deliberately does
|
|
165
189
|
not adopt iOS-native sizing (`UIActivityIndicatorView`'s ~20/~37 px) — the
|
|
@@ -193,6 +217,7 @@ Documented divergences:
|
|
|
193
217
|
| Test id | (no equivalent; tests use `getByRole` / `getByText`) | `testID` |
|
|
194
218
|
| Accessibility plumbing | `role="status"` + visually-hidden text + announce-on-mount fix for VoiceOver | `accessibilityRole="progressbar"` + `accessibilityState={{ busy: true }}` + `AccessibilityInfo.announceForAccessibility` |
|
|
195
219
|
| Reduced motion detection | CSS `@media (prefers-reduced-motion: reduce)` | `useReducedMotion()` from `react-native-reanimated` |
|
|
220
|
+
| Small motion | Layer 1 only; fixed arc; 1000ms spin (CSS) | Same behavior (Reanimated); matches web intentionally |
|
|
196
221
|
|
|
197
222
|
The divergences are intentional — each platform follows its own conventions on
|
|
198
223
|
the user-facing side while the underlying intent and visual outcome match.
|
|
@@ -128,6 +128,7 @@ export function MediaView({
|
|
|
128
128
|
file={file}
|
|
129
129
|
onUploadComplete={onUploadComplete}
|
|
130
130
|
styles={styles}
|
|
131
|
+
styleInGrid={styleInGrid}
|
|
131
132
|
/>
|
|
132
133
|
</ImageBackground>
|
|
133
134
|
</View>
|
|
@@ -141,6 +142,7 @@ interface OverlayProps {
|
|
|
141
142
|
readonly file: FormattedFile;
|
|
142
143
|
readonly onUploadComplete: () => void;
|
|
143
144
|
readonly styles: ReturnType<typeof useStyles>;
|
|
145
|
+
readonly styleInGrid: boolean;
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
function Overlay({
|
|
@@ -150,8 +152,11 @@ function Overlay({
|
|
|
150
152
|
file,
|
|
151
153
|
onUploadComplete,
|
|
152
154
|
styles,
|
|
155
|
+
styleInGrid,
|
|
153
156
|
}: OverlayProps) {
|
|
154
|
-
if (isLoading)
|
|
157
|
+
if (isLoading) {
|
|
158
|
+
return <ActivityIndicator size={styleInGrid ? "small" : "base"} />;
|
|
159
|
+
}
|
|
155
160
|
|
|
156
161
|
if (hasError) return <ErrorOverlay styles={styles} />;
|
|
157
162
|
|
|
@@ -263,6 +268,7 @@ function VideoPlaceholder({
|
|
|
263
268
|
file={file}
|
|
264
269
|
onUploadComplete={onUploadComplete}
|
|
265
270
|
styles={styles}
|
|
271
|
+
styleInGrid={styleInGrid}
|
|
266
272
|
/>
|
|
267
273
|
</View>
|
|
268
274
|
);
|
|
@@ -275,8 +281,12 @@ function PlaceholderStatusOverlay({
|
|
|
275
281
|
file,
|
|
276
282
|
onUploadComplete,
|
|
277
283
|
styles,
|
|
284
|
+
styleInGrid,
|
|
278
285
|
}: OverlayProps) {
|
|
279
|
-
if (isLoading)
|
|
286
|
+
if (isLoading) {
|
|
287
|
+
return <ActivityIndicator size={styleInGrid ? "small" : "base"} />;
|
|
288
|
+
}
|
|
289
|
+
|
|
280
290
|
if (hasError) return <ErrorOverlay styles={styles} />;
|
|
281
291
|
|
|
282
292
|
if (showOverlay) {
|