@react-spectrum/s2 3.0.0-nightly-c904e066c-240917 → 3.0.0-nightly-9e79420c1-240919

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.
@@ -10,13 +10,12 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {baseColor, style} from '../style/spectrum-theme' with {type: 'macro'};
14
- import {clamp} from '@react-aria/utils';
15
13
  import {ContextValue, ProgressBar as RACProgressBar, ProgressBarProps as RACProgressBarProps} from 'react-aria-components';
16
- import {createContext, CSSProperties, forwardRef} from 'react';
14
+ import {createContext, forwardRef} from 'react';
17
15
  import {DOMRef, DOMRefValue} from '@react-types/shared';
18
16
  import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
19
17
  import {keyframes} from '../style/style-macro' with {type: 'macro'};
18
+ import {style} from '../style/spectrum-theme' with {type: 'macro'};
20
19
  import {useDOMRef} from '@react-spectrum/utils';
21
20
  import {useSpectrumContextProps} from './useSpectrumContextProps';
22
21
 
@@ -37,455 +36,85 @@ export interface ProgressCircleStyleProps {
37
36
 
38
37
  export const ProgressCircleContext = createContext<ContextValue<ProgressCircleProps, DOMRefValue<HTMLDivElement>>>(null);
39
38
 
40
- const fillMask1Frames = keyframes(`
41
- 0% {
42
- transform: rotate(90deg);
43
- }
44
-
45
- 1.69% {
46
- transform: rotate(72.3deg);
47
- }
48
-
49
- 3.39% {
50
- transform: rotate(55.5deg);
51
- }
52
-
53
- 5.08% {
54
- transform: rotate(40.3deg);
55
- }
56
-
57
- 6.78% {
58
- transform: rotate(25deg);
59
- }
60
-
61
- 8.47% {
62
- transform: rotate(10.6deg);
63
- }
64
-
65
- 10.17%, 40.68% {
66
- transform: rotate(0deg);
67
- }
68
-
69
- 42.37% {
70
- transform: rotate(5.3deg);
71
- }
72
-
73
- 44.07% {
74
- transform: rotate(13.4deg);
75
- }
76
-
77
- 45.76% {
78
- transform: rotate(20.6deg);
79
- }
80
-
81
- 47.46% {
82
- transform: rotate(29deg);
83
- }
84
-
85
- 49.15% {
86
- transform: rotate(36.5deg);
87
- }
88
-
89
- 50.85% {
90
- transform: rotate(42.6deg);
91
- }
92
-
93
- 52.54% {
94
- transform: rotate(48.8deg);
95
- }
96
-
97
- 54.24% {
98
- transform: rotate(54.2deg);
99
- }
100
-
101
- 55.93% {
102
- transform: rotate(59.4deg);
103
- }
104
-
105
- 57.63% {
106
- transform: rotate(63.2deg);
107
- }
108
-
109
- 59.32% {
110
- transform: rotate(67.2deg);
111
- }
112
-
113
- 61.02% {
114
- transform: rotate(70.8deg);
115
- }
116
-
117
- 62.71% {
118
- transform: rotate(73.8deg);
119
- }
120
-
121
- 64.41% {
122
- transform: rotate(76.2deg);
123
- }
124
-
125
- 66.1% {
126
- transform: rotate(78.7deg);
127
- }
128
-
129
- 67.8% {
130
- transform: rotate(80.6deg);
131
- }
132
-
133
- 69.49% {
134
- transform: rotate(82.6deg);
135
- }
136
-
137
- 71.19% {
138
- transform: rotate(83.7deg);
139
- }
140
-
141
- 72.88% {
142
- transform: rotate(85deg);
143
- }
144
-
145
- 74.58% {
146
- transform: rotate(86.3deg);
147
- }
148
-
149
- 76.27% {
150
- transform: rotate(87deg);
151
- }
152
-
153
- 77.97% {
154
- transform: rotate(87.7deg);
155
- }
156
-
157
- 79.66% {
158
- transform: rotate(88.3deg);
159
- }
160
-
161
- 81.36% {
162
- transform: rotate(88.6deg);
163
- }
164
-
165
- 83.05% {
166
- transform: rotate(89.2deg);
167
- }
168
-
169
- 84.75% {
170
- transform: rotate(89.2deg);
171
- }
172
-
173
- 86.44% {
174
- transform: rotate(89.5deg);
175
- }
176
-
177
- 88.14% {
178
- transform: rotate(89.9deg);
179
- }
180
-
181
- 89.83% {
182
- transform: rotate(89.7deg);
183
- }
184
-
185
- 91.53% {
186
- transform: rotate(90.1deg);
187
- }
188
-
189
- 93.22% {
190
- transform: rotate(90.2deg);
191
- }
192
-
193
- 94.92% {
194
- transform: rotate(90.1deg);
195
- }
196
-
197
- 96.61% {
198
- transform: rotate(90deg);
199
- }
200
-
201
- 98.31% {
202
- transform: rotate(89.8deg);
203
- }
204
-
205
- 100% {
206
- transform: rotate(90deg);
207
- }
208
- `);
209
-
210
- const fillMask2Frames = keyframes(`
211
- 0%, 8.47% {
212
- transform: rotate(180deg);
213
- }
214
-
215
- 10.17% {
216
- transform: rotate(179.2deg);
217
- }
218
-
219
- 11.86% {
220
- transform: rotate(164deg);
221
- }
222
-
223
- 13.56% {
224
- transform: rotate(151.8deg);
225
- }
226
-
227
- 15.25% {
228
- transform: rotate(140.8deg);
229
- }
230
-
231
- 16.95% {
232
- transform: rotate(130.3deg);
233
- }
234
-
235
- 18.64% {
236
- transform: rotate(120.4deg);
237
- }
238
-
239
- 20.34% {
240
- transform: rotate(110.8deg);
241
- }
242
-
243
- 22.03% {
244
- transform: rotate(101.6deg);
245
- }
246
-
247
- 23.73% {
248
- transform: rotate(93.5deg);
249
- }
250
-
251
- 25.42% {
252
- transform: rotate(85.4deg);
253
- }
254
-
255
- 27.12% {
256
- transform: rotate(78.1deg);
257
- }
258
-
259
- 28.81% {
260
- transform: rotate(71.2deg);
261
- }
262
-
263
- 30.51% {
264
- transform: rotate(89.1deg);
265
- }
266
-
267
- 32.2% {
268
- transform: rotate(105.5deg);
269
- }
270
-
271
- 33.9% {
272
- transform: rotate(121.3deg);
273
- }
274
-
275
- 35.59% {
276
- transform: rotate(135.5deg);
277
- }
278
-
279
- 37.29% {
280
- transform: rotate(148.4deg);
281
- }
282
-
283
- 38.98% {
284
- transform: rotate(161deg);
285
- }
286
-
287
- 40.68% {
288
- transform: rotate(173.5deg);
289
- }
290
-
291
- 42.37%, 100% {
292
- transform: rotate(180deg);
293
- }
294
- `);
295
-
296
- const fillsRotate = keyframes(`
297
- 0% {transform: rotate(-90deg)}
298
- 100% {transform: rotate(270deg)}
299
- `);
300
-
301
- const circleDims = {
302
- height: {
303
- default: 32,
304
- size: {
305
- S: 16,
306
- L: 64
307
- }
308
- },
309
- width: {
39
+ // Double check the types passed to each style, may not need all for each
40
+ const wrapper = style<ProgressCircleStyleProps>({
41
+ size: {
310
42
  default: 32,
311
43
  size: {
312
44
  S: 16,
313
45
  L: 64
314
46
  }
315
47
  },
316
- aspectRatio: 'square'
317
- } as const;
318
-
319
- // Double check the types passed to each style, may not need all for each
320
- const wrapper = style<ProgressCircleStyleProps>({
321
- ...circleDims,
322
- display: 'inline-block',
323
- position: 'relative'
324
- }, getAllowedOverrides());
325
-
326
- const trackStyles = {
327
- ...circleDims,
328
- boxSizing: 'border-box',
329
- borderStyle: 'solid',
330
- borderWidth: {
331
- default: '[3px]',
332
- size: {
333
- S: 2,
334
- L: 4
335
- }
336
- },
337
- borderRadius: 'full'
338
- } as const;
48
+ aspectRatio: 'square',
49
+ display: 'inline-block'
50
+ }, getAllowedOverrides({height: true}));
339
51
 
340
52
  const track = style<ProgressCircleStyleProps>({
341
- ...trackStyles,
342
- borderColor: {
343
- default: baseColor('gray-300'),
53
+ stroke: {
54
+ default: 'gray-300',
344
55
  staticColor: {
345
- white: {
346
- default: baseColor('transparent-white-300'),
347
- forcedColors: 'Background'
348
- },
349
- // TODO: no designs for this one
350
- black: {
351
- default: baseColor('transparent-black-300'),
352
- forcedColors: 'Background'
353
- }
56
+ white: 'transparent-white-300',
57
+ black: 'transparent-black-300'
354
58
  },
355
59
  forcedColors: 'Background'
356
60
  }
357
61
  });
358
62
 
359
63
  const fill = style<ProgressCircleStyleProps>({
360
- ...trackStyles,
361
- borderColor: {
362
- default: baseColor('blue-900'),
64
+ stroke: {
65
+ default: 'blue-900',
363
66
  staticColor: {
364
- white: {
365
- default: baseColor('transparent-white-900'),
366
- forcedColors: 'Highlight'
367
- },
368
- // TODO: no designs for this one
369
- black: {
370
- default: baseColor('transparent-black-900'),
371
- forcedColors: 'Highlight'
372
- }
67
+ white: 'transparent-white-900',
68
+ black: 'transparent-black-900'
373
69
  },
374
70
  forcedColors: 'Highlight'
375
- }
376
- });
377
-
378
- const fillsWrapperStyles = {
379
- position: 'absolute',
380
- top: 0,
381
- left: 0,
382
- size: 'full'
383
- } as const;
384
-
385
- const fillsWrapper = style({
386
- ...fillsWrapperStyles
387
- });
388
-
389
- const fillsWrapperIndeterminate = style({
390
- ...fillsWrapperStyles,
391
- willChange: 'transform',
392
- transform: 'translateZ(0)',
393
- animation: fillsRotate,
394
- animationDuration: 1000,
395
- animationTimingFunction: '[cubic-bezier(.25,.78,.48,.89)]',
396
- animationIterationCount: 'infinite',
71
+ },
72
+ rotate: -90,
397
73
  transformOrigin: 'center'
398
74
  });
399
75
 
400
- const commonFillMask = {
401
- position: 'absolute',
402
- width: '[50%]',
403
- height: 'full',
404
- transformOrigin: '[100% center]',
405
- overflow: 'hidden'
406
- } as const;
407
-
408
- const fillMask1 = style({
409
- ...commonFillMask,
410
- transform: 'rotate(180deg)'
411
- });
412
-
413
- const fillMask2 = style({
414
- ...commonFillMask,
415
- transform: 'rotate(0deg)'
416
- });
417
-
418
- const commonFillSubMask = {
419
- width: 'full',
420
- height: 'full',
421
- transformOrigin: '[100% center]',
422
- transform: 'rotate(-180deg)',
423
- overflow: 'hidden'
424
- } as const;
425
-
426
- const commonFillSubMaskIndeterminate = {
427
- transform: 'translateZ(0)',
428
- willChange: 'transform',
429
- animationDuration: 1000,
430
- animationTimingFunction: 'linear',
431
- animationIterationCount: 'infinite'
432
- } as const;
76
+ export interface ProgressCircleProps extends Omit<RACProgressBarProps, 'children' | 'style' | 'valueLabel' | 'formatOptions' | 'label' | 'className'>, ProgressCircleStyleProps, StyleProps {}
433
77
 
434
- const fillSubMask = style({
435
- ...commonFillSubMask
436
- });
78
+ const rotationAnimation = keyframes(`
79
+ 0% {
80
+ transform: rotate(0deg);
81
+ }
437
82
 
438
- const fillSubMask1Indeterminate = style({
439
- ...commonFillSubMask,
440
- animation: fillMask1Frames,
441
- ...commonFillSubMaskIndeterminate
442
- });
83
+ 100% {
84
+ transform: rotate(360deg);
85
+ }
86
+ `);
443
87
 
444
- const fillSubMask2Indeterminate = style({
445
- ...commonFillSubMask,
446
- animation: fillMask2Frames,
447
- ...commonFillSubMaskIndeterminate
448
- });
88
+ // stroke-dashoffset represents `100 - percentage`. See below for how this works.
89
+ const dashoffsetAnimation = keyframes(`
90
+ 0%, 100% {
91
+ stroke-dashoffset: 75;
92
+ }
449
93
 
450
- export interface ProgressCircleProps extends Omit<RACProgressBarProps, 'children' | 'style' | 'valueLabel' | 'formatOptions' | 'label' | 'className'>, ProgressCircleStyleProps, StyleProps {}
94
+ 30% {
95
+ stroke-dashoffset: 20;
96
+ }
97
+ `);
451
98
 
452
99
  function ProgressCircle(props: ProgressCircleProps, ref: DOMRef<HTMLDivElement>) {
453
100
  [props, ref] = useSpectrumContextProps(props, ref, ProgressCircleContext);
454
101
  let {
455
- value = 0,
456
- minValue = 0,
457
- maxValue = 100,
458
102
  size = 'M',
459
103
  staticColor,
460
- isIndeterminate = false,
461
- 'aria-label': ariaLabel,
462
- 'aria-labelledby': ariaLabelledby,
463
104
  UNSAFE_style,
464
105
  UNSAFE_className = ''
465
106
  } = props;
466
107
  let domRef = useDOMRef(ref);
467
108
 
468
- value = clamp(value, minValue, maxValue);
469
-
470
- let subMask1Style: CSSProperties = {};
471
- let subMask2Style: CSSProperties = {};
472
- if (!isIndeterminate) {
473
- let percentage = (value - minValue) / (maxValue - minValue) * 100;
474
- let angle;
475
- if (percentage > 0 && percentage <= 50) {
476
- angle = -180 + (percentage / 50 * 180);
477
- subMask1Style.transform = `rotate(${angle}deg)`;
478
- subMask2Style.transform = 'rotate(-180deg)';
479
- } else if (percentage > 50) {
480
- angle = -180 + (percentage - 50) / 50 * 180;
481
- subMask1Style.transform = 'rotate(0deg)';
482
- subMask2Style.transform = `rotate(${angle}deg)`;
483
- }
109
+ let strokeWidth = 3;
110
+ if (size === 'S') {
111
+ strokeWidth = 2;
112
+ } else if (size === 'L') {
113
+ strokeWidth = 4;
484
114
  }
485
115
 
486
- if (!ariaLabel && !ariaLabelledby) {
487
- console.warn('ProgressCircle requires an aria-label or aria-labelledby attribute for accessibility');
488
- }
116
+ // SVG strokes are centered, so subtract half the stroke width from the radius to create an inner stroke.
117
+ let radius = `calc(50% - ${strokeWidth / 2}px)`;
489
118
 
490
119
  return (
491
120
  <RACProgressBar
@@ -496,25 +125,36 @@ function ProgressCircle(props: ProgressCircleProps, ref: DOMRef<HTMLDivElement>)
496
125
  ...renderProps,
497
126
  size
498
127
  }, props.styles)}>
499
- <div dir="ltr">
500
- <div className={track({size, staticColor})} />
501
- <div className={isIndeterminate ? fillsWrapperIndeterminate : fillsWrapper}>
502
- <div className={fillMask1}>
503
- <div
504
- className={isIndeterminate ? fillSubMask1Indeterminate : fillSubMask}
505
- style={subMask1Style}>
506
- <div className={fill({size, staticColor})} />
507
- </div>
508
- </div>
509
- <div className={fillMask2}>
510
- <div
511
- className={isIndeterminate ? fillSubMask2Indeterminate : fillSubMask}
512
- style={subMask2Style}>
513
- <div className={fill({size, staticColor})} />
514
- </div>
515
- </div>
516
- </div>
517
- </div>
128
+ {({percentage, isIndeterminate}) => (
129
+ <svg
130
+ fill="none"
131
+ width="100%"
132
+ height="100%">
133
+ <circle
134
+ cx="50%"
135
+ cy="50%"
136
+ r={radius}
137
+ strokeWidth={strokeWidth}
138
+ className={track({staticColor})} />
139
+ <circle
140
+ cx="50%"
141
+ cy="50%"
142
+ r={radius}
143
+ strokeWidth={strokeWidth}
144
+ className={fill({staticColor})}
145
+ style={{
146
+ // These cubic-bezier timing functions were derived from the previous animation keyframes
147
+ // using a best fit algorithm, and then manually adjusted to approximate the original animation.
148
+ animation: isIndeterminate ? `${rotationAnimation} 1s cubic-bezier(.6, .1, .3, .9) infinite, ${dashoffsetAnimation} 1s cubic-bezier(.25, .1, .25, 1.3) infinite` : undefined
149
+ }}
150
+ // Normalize the path length to 100 so we can easily set stroke-dashoffset to a percentage.
151
+ pathLength="100"
152
+ // Add extra gap between dashes so 0% works in Chrome.
153
+ strokeDasharray="100 200"
154
+ strokeDashoffset={isIndeterminate || percentage == null ? undefined : 100 - percentage}
155
+ strokeLinecap="round" />
156
+ </svg>
157
+ )}
518
158
  </RACProgressBar>
519
159
  );
520
160
  }