@box/blueprint-web 18.8.0 → 18.9.0
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/lib-esm/badge/base-badge.d.ts +6 -1
- package/dist/lib-esm/badge/base-badge.js +9 -1
- package/dist/lib-esm/badge/base-badge.module.js +1 -1
- package/dist/lib-esm/badge/numeric-badge.js +36 -2
- package/dist/lib-esm/components.css +46 -22
- package/dist/lib-esm/index.css +46 -22
- package/dist/lib-esm/utils/use-one-shot-animation.d.ts +35 -0
- package/dist/lib-esm/utils/use-one-shot-animation.js +112 -0
- package/package.json +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AnimationEventHandler } from 'react';
|
|
1
2
|
import { type BadgeProps } from './types';
|
|
2
3
|
export type BaseBadgeProps = Omit<BadgeProps, 'children'> & {
|
|
3
4
|
/** The color of the badge background. */
|
|
@@ -5,5 +6,9 @@ export type BaseBadgeProps = Omit<BadgeProps, 'children'> & {
|
|
|
5
6
|
className?: string;
|
|
6
7
|
/** The child element to display in the badge: string for NumericBadge and svg for IconBadge). */
|
|
7
8
|
children?: React.ReactElement;
|
|
9
|
+
'data-bp-animated'?: 'true' | 'false';
|
|
10
|
+
'data-bp-animation-generation'?: number;
|
|
11
|
+
'data-state'?: 'idle' | 'playing';
|
|
12
|
+
onAnimationEnd?: AnimationEventHandler<HTMLDivElement>;
|
|
8
13
|
};
|
|
9
|
-
export declare const BaseBadge: ({ backgroundColor, className, children, size, offset }: BaseBadgeProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const BaseBadge: ({ backgroundColor, className, children, size, offset, "data-bp-animated": dataBpAnimated, "data-bp-animation-generation": dataBpAnimationGeneration, "data-state": dataState, onAnimationEnd, }: BaseBadgeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -8,11 +8,19 @@ const BaseBadge = ({
|
|
|
8
8
|
className,
|
|
9
9
|
children,
|
|
10
10
|
size,
|
|
11
|
-
offset
|
|
11
|
+
offset,
|
|
12
|
+
'data-bp-animated': dataBpAnimated,
|
|
13
|
+
'data-bp-animation-generation': dataBpAnimationGeneration,
|
|
14
|
+
'data-state': dataState,
|
|
15
|
+
onAnimationEnd
|
|
12
16
|
}) => {
|
|
13
17
|
const offsetStyle = mapValues(offset, value => Number.isInteger(value) ? `${value}px` : `0`);
|
|
14
18
|
return jsx("div", {
|
|
15
19
|
className: clsx(className, styles.badge, styles[`${size}SizeBadge`]),
|
|
20
|
+
"data-bp-animated": dataBpAnimated,
|
|
21
|
+
"data-bp-animation-generation": dataBpAnimationGeneration,
|
|
22
|
+
"data-state": dataState,
|
|
23
|
+
onAnimationEnd: onAnimationEnd,
|
|
16
24
|
style: {
|
|
17
25
|
backgroundColor,
|
|
18
26
|
...offsetStyle
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '../index.css';
|
|
2
|
-
var styles = {"badgeContainer":"bp_base_badge_module_badgeContainer--
|
|
2
|
+
var styles = {"badgeContainer":"bp_base_badge_module_badgeContainer--44c64","badge":"bp_base_badge_module_badge--44c64","smallSizeBadge":"bp_base_badge_module_smallSizeBadge--44c64","mediumSizeBadge":"bp_base_badge_module_mediumSizeBadge--44c64","largeSizeBadge":"bp_base_badge_module_largeSizeBadge--44c64","xlargeSizeBadge":"bp_base_badge_module_xlargeSizeBadge--44c64","xxlargeSizeBadge":"bp_base_badge_module_xxlargeSizeBadge--44c64","iconBadge":"bp_base_badge_module_iconBadge--44c64","interactiveWrapper":"bp_base_badge_module_interactiveWrapper--44c64","statusBadge":"bp_base_badge_module_statusBadge--44c64","numericBadge":"bp_base_badge_module_numericBadge--44c64","bpNumericBadgePopIn":"bp_base_badge_module_bpNumericBadgePopIn--44c64","numericBadgeSingleDigit":"bp_base_badge_module_numericBadgeSingleDigit--44c64","numericBadgeMoreDigits":"bp_base_badge_module_numericBadgeMoreDigits--44c64"};
|
|
3
3
|
|
|
4
4
|
export { styles as default };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { WatermelonRed110 } from '@box/blueprint-web-assets/tokens/tokens';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
-
import { forwardRef, Children, cloneElement } from 'react';
|
|
4
|
+
import { forwardRef, useState, useCallback, Children, cloneElement } from 'react';
|
|
5
|
+
import '../blueprint-configuration-context/blueprint-configuration-context.js';
|
|
6
|
+
import '../blueprint-configuration-context/consts.js';
|
|
7
|
+
import { useBlueprintConfiguration } from '../blueprint-configuration-context/useBlueprintConfiguration.js';
|
|
5
8
|
import { Text } from '../text/text.js';
|
|
9
|
+
import { useOneShotAnimation, ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE } from '../utils/use-one-shot-animation.js';
|
|
6
10
|
import { useUniqueId } from '../utils/useUniqueId.js';
|
|
7
11
|
import { BaseBadge } from './base-badge.js';
|
|
8
12
|
import styles from './base-badge.module.js';
|
|
@@ -14,6 +18,32 @@ const NumericBadge = /*#__PURE__*/forwardRef((props, forwardedRef) => {
|
|
|
14
18
|
offset,
|
|
15
19
|
render
|
|
16
20
|
} = props;
|
|
21
|
+
const {
|
|
22
|
+
componentsWithAnimationEnabled
|
|
23
|
+
} = useBlueprintConfiguration();
|
|
24
|
+
const isAnimationEnabled = componentsWithAnimationEnabled.includes('NumericBadge');
|
|
25
|
+
const {
|
|
26
|
+
animationState,
|
|
27
|
+
animationGeneration,
|
|
28
|
+
onAnimationEnd
|
|
29
|
+
} = useOneShotAnimation(value, isAnimationEnabled);
|
|
30
|
+
// First paint must play enter: the hook's initial trigger is idle and does not
|
|
31
|
+
// auto-play on mount. Flip to entered on animationend so idle does not loop.
|
|
32
|
+
const [hasEntered, setHasEntered] = useState(false);
|
|
33
|
+
const handleAnimationEnd = useCallback(event => {
|
|
34
|
+
if (event.target !== event.currentTarget) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// A completion from the previous keyed node must not finish this generation.
|
|
38
|
+
if (event.currentTarget.getAttribute(ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE) !== String(animationGeneration)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!hasEntered) {
|
|
42
|
+
setHasEntered(true);
|
|
43
|
+
}
|
|
44
|
+
onAnimationEnd(event);
|
|
45
|
+
}, [animationGeneration, hasEntered, onAnimationEnd]);
|
|
46
|
+
const animationDataState = isAnimationEnabled && (!hasEntered || animationState === 'playing') ? 'playing' : 'idle';
|
|
17
47
|
const valueId = useUniqueId('numeric-badge-value');
|
|
18
48
|
// In the case of a deeply nested child (e.g., the child is wrapped by a tooltip), the render prop must be passed instead of children to correctly apply aria-describedby.
|
|
19
49
|
let badgeChild;
|
|
@@ -36,7 +66,11 @@ const NumericBadge = /*#__PURE__*/forwardRef((props, forwardedRef) => {
|
|
|
36
66
|
[styles.numericBadgeMoreDigits]: value.length > 1,
|
|
37
67
|
[styles.numericBadgeSingleDigit]: value.length === 1
|
|
38
68
|
}),
|
|
69
|
+
"data-bp-animated": isAnimationEnabled ? 'true' : 'false',
|
|
70
|
+
"data-bp-animation-generation": animationGeneration,
|
|
71
|
+
"data-state": animationDataState,
|
|
39
72
|
offset: offset,
|
|
73
|
+
onAnimationEnd: handleAnimationEnd,
|
|
40
74
|
size: "medium",
|
|
41
75
|
children: jsx(Text, {
|
|
42
76
|
as: "span",
|
|
@@ -45,7 +79,7 @@ const NumericBadge = /*#__PURE__*/forwardRef((props, forwardedRef) => {
|
|
|
45
79
|
variant: "labelBold",
|
|
46
80
|
children: value
|
|
47
81
|
})
|
|
48
|
-
})]
|
|
82
|
+
}, animationGeneration)]
|
|
49
83
|
});
|
|
50
84
|
});
|
|
51
85
|
|
|
@@ -2542,12 +2542,12 @@
|
|
|
2542
2542
|
transition:none;
|
|
2543
2543
|
}
|
|
2544
2544
|
}
|
|
2545
|
-
.bp_base_badge_module_badgeContainer--
|
|
2545
|
+
.bp_base_badge_module_badgeContainer--44c64{
|
|
2546
2546
|
display:inline-flex;
|
|
2547
2547
|
position:relative;
|
|
2548
2548
|
}
|
|
2549
2549
|
|
|
2550
|
-
.bp_base_badge_module_badge--
|
|
2550
|
+
.bp_base_badge_module_badge--44c64{
|
|
2551
2551
|
align-items:center;
|
|
2552
2552
|
border:var(--border-2) solid var(--border-badge-border);
|
|
2553
2553
|
border-radius:var(--radius-half);
|
|
@@ -2559,37 +2559,37 @@
|
|
|
2559
2559
|
user-select:none;
|
|
2560
2560
|
}
|
|
2561
2561
|
|
|
2562
|
-
.bp_base_badge_module_smallSizeBadge--
|
|
2562
|
+
.bp_base_badge_module_smallSizeBadge--44c64{
|
|
2563
2563
|
height:var(--size-2);
|
|
2564
2564
|
min-width:var(--size-2);
|
|
2565
2565
|
}
|
|
2566
2566
|
|
|
2567
|
-
.bp_base_badge_module_mediumSizeBadge--
|
|
2567
|
+
.bp_base_badge_module_mediumSizeBadge--44c64{
|
|
2568
2568
|
height:var(--size-3);
|
|
2569
2569
|
min-width:var(--size-3);
|
|
2570
2570
|
}
|
|
2571
2571
|
|
|
2572
|
-
.bp_base_badge_module_largeSizeBadge--
|
|
2572
|
+
.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2573
2573
|
height:var(--size-4);
|
|
2574
2574
|
min-width:var(--size-4);
|
|
2575
2575
|
}
|
|
2576
2576
|
|
|
2577
|
-
.bp_base_badge_module_xlargeSizeBadge--
|
|
2577
|
+
.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2578
2578
|
height:var(--size-5);
|
|
2579
2579
|
min-width:var(--size-5);
|
|
2580
2580
|
}
|
|
2581
2581
|
|
|
2582
|
-
.bp_base_badge_module_xxlargeSizeBadge--
|
|
2582
|
+
.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2583
2583
|
height:var(--size-6);
|
|
2584
2584
|
min-width:var(--size-6);
|
|
2585
2585
|
}
|
|
2586
2586
|
|
|
2587
|
-
.bp_base_badge_module_iconBadge--
|
|
2587
|
+
.bp_base_badge_module_iconBadge--44c64{
|
|
2588
2588
|
bottom:-5%;
|
|
2589
2589
|
right:-5%;
|
|
2590
2590
|
}
|
|
2591
2591
|
|
|
2592
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2592
|
+
.bp_base_badge_module_interactiveWrapper--44c64{
|
|
2593
2593
|
border:var(--border-2) solid #0000;
|
|
2594
2594
|
border-radius:50%;
|
|
2595
2595
|
bottom:calc(-5% - var(--border-2)/2);
|
|
@@ -2597,22 +2597,22 @@
|
|
|
2597
2597
|
position:absolute;
|
|
2598
2598
|
right:calc(-5% - var(--border-2)/2);
|
|
2599
2599
|
}
|
|
2600
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2600
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_smallSizeBadge--44c64{
|
|
2601
2601
|
width:var(--size-2);
|
|
2602
2602
|
}
|
|
2603
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2603
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_mediumSizeBadge--44c64{
|
|
2604
2604
|
width:var(--size-3);
|
|
2605
2605
|
}
|
|
2606
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2606
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2607
2607
|
width:var(--size-4);
|
|
2608
2608
|
}
|
|
2609
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2609
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2610
2610
|
width:var(--size-5);
|
|
2611
2611
|
}
|
|
2612
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2612
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2613
2613
|
width:var(--size-6);
|
|
2614
2614
|
}
|
|
2615
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2615
|
+
.bp_base_badge_module_interactiveWrapper--44c64::before{
|
|
2616
2616
|
content:"";
|
|
2617
2617
|
left:calc(var(--size-6)*-1/4);
|
|
2618
2618
|
min-height:var(--size-6);
|
|
@@ -2621,36 +2621,60 @@
|
|
|
2621
2621
|
top:calc(var(--size-6)*-1/4);
|
|
2622
2622
|
}
|
|
2623
2623
|
|
|
2624
|
-
.bp_base_badge_module_statusBadge--
|
|
2624
|
+
.bp_base_badge_module_statusBadge--44c64{
|
|
2625
2625
|
bottom:-20%;
|
|
2626
2626
|
right:-20%;
|
|
2627
2627
|
}
|
|
2628
2628
|
|
|
2629
|
-
.bp_base_badge_module_statusBadge--
|
|
2629
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2630
2630
|
bottom:-22%;
|
|
2631
2631
|
right:-22%;
|
|
2632
2632
|
}
|
|
2633
2633
|
|
|
2634
|
-
.bp_base_badge_module_statusBadge--
|
|
2634
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2635
2635
|
bottom:-15%;
|
|
2636
2636
|
right:-15%;
|
|
2637
2637
|
}
|
|
2638
2638
|
|
|
2639
|
-
.bp_base_badge_module_statusBadge--
|
|
2639
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2640
2640
|
bottom:-4%;
|
|
2641
2641
|
right:-4%;
|
|
2642
2642
|
}
|
|
2643
2643
|
|
|
2644
|
-
.bp_base_badge_module_numericBadge--
|
|
2644
|
+
.bp_base_badge_module_numericBadge--44c64{
|
|
2645
|
+
--bp-badge-enter-x:calc(var(--bp-space-010)*-1);
|
|
2646
|
+
--bp-badge-enter-y:var(--bp-space-010);
|
|
2645
2647
|
left:60%;
|
|
2646
2648
|
top:0;
|
|
2647
2649
|
}
|
|
2648
2650
|
|
|
2649
|
-
.
|
|
2651
|
+
.bp_base_badge_module_numericBadge--44c64[data-bp-animated=true][data-state=playing]{
|
|
2652
|
+
animation-duration:var(--bp-duration-medium);
|
|
2653
|
+
animation-fill-mode:both;
|
|
2654
|
+
animation-name:bp_base_badge_module_bpNumericBadgePopIn--44c64;
|
|
2655
|
+
animation-timing-function:var(--bp-curve-large-on);
|
|
2656
|
+
}
|
|
2657
|
+
@media (prefers-reduced-motion: reduce){
|
|
2658
|
+
.bp_base_badge_module_numericBadge--44c64[data-bp-animated=true][data-state=playing]{
|
|
2659
|
+
animation:none;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
@keyframes bp_base_badge_module_bpNumericBadgePopIn--44c64{
|
|
2664
|
+
from{
|
|
2665
|
+
opacity:var(--bp-opacity-hidden);
|
|
2666
|
+
transform:translate(var(--bp-badge-enter-x), var(--bp-badge-enter-y)) scale(.5);
|
|
2667
|
+
}
|
|
2668
|
+
to{
|
|
2669
|
+
opacity:var(--bp-opacity-visible);
|
|
2670
|
+
transform:none;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
.bp_base_badge_module_numericBadgeSingleDigit--44c64{
|
|
2650
2674
|
padding:1px;
|
|
2651
2675
|
}
|
|
2652
2676
|
|
|
2653
|
-
.bp_base_badge_module_numericBadgeMoreDigits--
|
|
2677
|
+
.bp_base_badge_module_numericBadgeMoreDigits--44c64{
|
|
2654
2678
|
padding:1px 3px;
|
|
2655
2679
|
}
|
|
2656
2680
|
.bp_icon_button_module_iconButton--17523[data-bp-animated=true]{
|
package/dist/lib-esm/index.css
CHANGED
|
@@ -2542,12 +2542,12 @@
|
|
|
2542
2542
|
transition:none;
|
|
2543
2543
|
}
|
|
2544
2544
|
}
|
|
2545
|
-
.bp_base_badge_module_badgeContainer--
|
|
2545
|
+
.bp_base_badge_module_badgeContainer--44c64{
|
|
2546
2546
|
display:inline-flex;
|
|
2547
2547
|
position:relative;
|
|
2548
2548
|
}
|
|
2549
2549
|
|
|
2550
|
-
.bp_base_badge_module_badge--
|
|
2550
|
+
.bp_base_badge_module_badge--44c64{
|
|
2551
2551
|
align-items:center;
|
|
2552
2552
|
border:var(--border-2) solid var(--border-badge-border);
|
|
2553
2553
|
border-radius:var(--radius-half);
|
|
@@ -2559,37 +2559,37 @@
|
|
|
2559
2559
|
user-select:none;
|
|
2560
2560
|
}
|
|
2561
2561
|
|
|
2562
|
-
.bp_base_badge_module_smallSizeBadge--
|
|
2562
|
+
.bp_base_badge_module_smallSizeBadge--44c64{
|
|
2563
2563
|
height:var(--size-2);
|
|
2564
2564
|
min-width:var(--size-2);
|
|
2565
2565
|
}
|
|
2566
2566
|
|
|
2567
|
-
.bp_base_badge_module_mediumSizeBadge--
|
|
2567
|
+
.bp_base_badge_module_mediumSizeBadge--44c64{
|
|
2568
2568
|
height:var(--size-3);
|
|
2569
2569
|
min-width:var(--size-3);
|
|
2570
2570
|
}
|
|
2571
2571
|
|
|
2572
|
-
.bp_base_badge_module_largeSizeBadge--
|
|
2572
|
+
.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2573
2573
|
height:var(--size-4);
|
|
2574
2574
|
min-width:var(--size-4);
|
|
2575
2575
|
}
|
|
2576
2576
|
|
|
2577
|
-
.bp_base_badge_module_xlargeSizeBadge--
|
|
2577
|
+
.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2578
2578
|
height:var(--size-5);
|
|
2579
2579
|
min-width:var(--size-5);
|
|
2580
2580
|
}
|
|
2581
2581
|
|
|
2582
|
-
.bp_base_badge_module_xxlargeSizeBadge--
|
|
2582
|
+
.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2583
2583
|
height:var(--size-6);
|
|
2584
2584
|
min-width:var(--size-6);
|
|
2585
2585
|
}
|
|
2586
2586
|
|
|
2587
|
-
.bp_base_badge_module_iconBadge--
|
|
2587
|
+
.bp_base_badge_module_iconBadge--44c64{
|
|
2588
2588
|
bottom:-5%;
|
|
2589
2589
|
right:-5%;
|
|
2590
2590
|
}
|
|
2591
2591
|
|
|
2592
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2592
|
+
.bp_base_badge_module_interactiveWrapper--44c64{
|
|
2593
2593
|
border:var(--border-2) solid #0000;
|
|
2594
2594
|
border-radius:50%;
|
|
2595
2595
|
bottom:calc(-5% - var(--border-2)/2);
|
|
@@ -2597,22 +2597,22 @@
|
|
|
2597
2597
|
position:absolute;
|
|
2598
2598
|
right:calc(-5% - var(--border-2)/2);
|
|
2599
2599
|
}
|
|
2600
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2600
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_smallSizeBadge--44c64{
|
|
2601
2601
|
width:var(--size-2);
|
|
2602
2602
|
}
|
|
2603
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2603
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_mediumSizeBadge--44c64{
|
|
2604
2604
|
width:var(--size-3);
|
|
2605
2605
|
}
|
|
2606
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2606
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2607
2607
|
width:var(--size-4);
|
|
2608
2608
|
}
|
|
2609
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2609
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2610
2610
|
width:var(--size-5);
|
|
2611
2611
|
}
|
|
2612
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2612
|
+
.bp_base_badge_module_interactiveWrapper--44c64.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2613
2613
|
width:var(--size-6);
|
|
2614
2614
|
}
|
|
2615
|
-
.bp_base_badge_module_interactiveWrapper--
|
|
2615
|
+
.bp_base_badge_module_interactiveWrapper--44c64::before{
|
|
2616
2616
|
content:"";
|
|
2617
2617
|
left:calc(var(--size-6)*-1/4);
|
|
2618
2618
|
min-height:var(--size-6);
|
|
@@ -2621,36 +2621,60 @@
|
|
|
2621
2621
|
top:calc(var(--size-6)*-1/4);
|
|
2622
2622
|
}
|
|
2623
2623
|
|
|
2624
|
-
.bp_base_badge_module_statusBadge--
|
|
2624
|
+
.bp_base_badge_module_statusBadge--44c64{
|
|
2625
2625
|
bottom:-20%;
|
|
2626
2626
|
right:-20%;
|
|
2627
2627
|
}
|
|
2628
2628
|
|
|
2629
|
-
.bp_base_badge_module_statusBadge--
|
|
2629
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_largeSizeBadge--44c64{
|
|
2630
2630
|
bottom:-22%;
|
|
2631
2631
|
right:-22%;
|
|
2632
2632
|
}
|
|
2633
2633
|
|
|
2634
|
-
.bp_base_badge_module_statusBadge--
|
|
2634
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_xlargeSizeBadge--44c64{
|
|
2635
2635
|
bottom:-15%;
|
|
2636
2636
|
right:-15%;
|
|
2637
2637
|
}
|
|
2638
2638
|
|
|
2639
|
-
.bp_base_badge_module_statusBadge--
|
|
2639
|
+
.bp_base_badge_module_statusBadge--44c64.bp_base_badge_module_xxlargeSizeBadge--44c64{
|
|
2640
2640
|
bottom:-4%;
|
|
2641
2641
|
right:-4%;
|
|
2642
2642
|
}
|
|
2643
2643
|
|
|
2644
|
-
.bp_base_badge_module_numericBadge--
|
|
2644
|
+
.bp_base_badge_module_numericBadge--44c64{
|
|
2645
|
+
--bp-badge-enter-x:calc(var(--bp-space-010)*-1);
|
|
2646
|
+
--bp-badge-enter-y:var(--bp-space-010);
|
|
2645
2647
|
left:60%;
|
|
2646
2648
|
top:0;
|
|
2647
2649
|
}
|
|
2648
2650
|
|
|
2649
|
-
.
|
|
2651
|
+
.bp_base_badge_module_numericBadge--44c64[data-bp-animated=true][data-state=playing]{
|
|
2652
|
+
animation-duration:var(--bp-duration-medium);
|
|
2653
|
+
animation-fill-mode:both;
|
|
2654
|
+
animation-name:bp_base_badge_module_bpNumericBadgePopIn--44c64;
|
|
2655
|
+
animation-timing-function:var(--bp-curve-large-on);
|
|
2656
|
+
}
|
|
2657
|
+
@media (prefers-reduced-motion: reduce){
|
|
2658
|
+
.bp_base_badge_module_numericBadge--44c64[data-bp-animated=true][data-state=playing]{
|
|
2659
|
+
animation:none;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
@keyframes bp_base_badge_module_bpNumericBadgePopIn--44c64{
|
|
2664
|
+
from{
|
|
2665
|
+
opacity:var(--bp-opacity-hidden);
|
|
2666
|
+
transform:translate(var(--bp-badge-enter-x), var(--bp-badge-enter-y)) scale(.5);
|
|
2667
|
+
}
|
|
2668
|
+
to{
|
|
2669
|
+
opacity:var(--bp-opacity-visible);
|
|
2670
|
+
transform:none;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
.bp_base_badge_module_numericBadgeSingleDigit--44c64{
|
|
2650
2674
|
padding:1px;
|
|
2651
2675
|
}
|
|
2652
2676
|
|
|
2653
|
-
.bp_base_badge_module_numericBadgeMoreDigits--
|
|
2677
|
+
.bp_base_badge_module_numericBadgeMoreDigits--44c64{
|
|
2654
2678
|
padding:1px 3px;
|
|
2655
2679
|
}
|
|
2656
2680
|
.bp_icon_button_module_iconButton--17523[data-bp-animated=true]{
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type AnimationEvent } from 'react';
|
|
2
|
+
export type OneShotAnimationState = 'idle' | 'playing';
|
|
3
|
+
/**
|
|
4
|
+
* Stamp this on the animated node together with `key={animationGeneration}`.
|
|
5
|
+
* A completion whose stamp does not match the current generation is ignored.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE = "data-bp-animation-generation";
|
|
8
|
+
/**
|
|
9
|
+
* Replays a CSS animation when `trigger` changes by `Object.is`.
|
|
10
|
+
*
|
|
11
|
+
* CSS will not restart while `data-state` stays `'playing'`, so this hook
|
|
12
|
+
* sets `'playing'` on a trigger change and returns to `'idle'` on
|
|
13
|
+
* `animationend`.
|
|
14
|
+
*
|
|
15
|
+
* Each accepted trigger change bumps `animationGeneration`. When remounting is
|
|
16
|
+
* safe, key the animated node with that value and set
|
|
17
|
+
* `data-bp-animation-generation` to the same number so an `animationend` from
|
|
18
|
+
* the previous node cannot idle the new one.
|
|
19
|
+
*
|
|
20
|
+
* If the trigger changes while already playing, the hook goes to `'idle'` first,
|
|
21
|
+
* then `'playing'` again. Use that path when the node must stay mounted (for
|
|
22
|
+
* example a text field that should keep focus and input). The idle update is
|
|
23
|
+
* flushed before paint; a follow-up effect sets `'playing'`.
|
|
24
|
+
*
|
|
25
|
+
* A same-value resubmit (for example the same validation error twice) does not
|
|
26
|
+
* change `trigger` by `Object.is`, so it will not replay. Pass a nonce as
|
|
27
|
+
* `trigger` when you need another play without a value change.
|
|
28
|
+
*
|
|
29
|
+
* A rerender that keeps the same `trigger` after idle does not auto-play.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useOneShotAnimation(trigger: unknown, isEnabled: boolean): {
|
|
32
|
+
animationState: OneShotAnimationState;
|
|
33
|
+
animationGeneration: number;
|
|
34
|
+
onAnimationEnd: (event: AnimationEvent) => void;
|
|
35
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { noop } from './noop.js';
|
|
3
|
+
import { useEnhancedEffect } from './useEnhancedEffect.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stamp this on the animated node together with `key={animationGeneration}`.
|
|
7
|
+
* A completion whose stamp does not match the current generation is ignored.
|
|
8
|
+
*/
|
|
9
|
+
const ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE = 'data-bp-animation-generation';
|
|
10
|
+
const animationEndNoop = noop;
|
|
11
|
+
const readStampedAnimationGeneration = event => {
|
|
12
|
+
const {
|
|
13
|
+
currentTarget
|
|
14
|
+
} = event;
|
|
15
|
+
if (!(currentTarget instanceof Element)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const generation = currentTarget.getAttribute(ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE);
|
|
19
|
+
if (generation === null) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return Number(generation);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Replays a CSS animation when `trigger` changes by `Object.is`.
|
|
26
|
+
*
|
|
27
|
+
* CSS will not restart while `data-state` stays `'playing'`, so this hook
|
|
28
|
+
* sets `'playing'` on a trigger change and returns to `'idle'` on
|
|
29
|
+
* `animationend`.
|
|
30
|
+
*
|
|
31
|
+
* Each accepted trigger change bumps `animationGeneration`. When remounting is
|
|
32
|
+
* safe, key the animated node with that value and set
|
|
33
|
+
* `data-bp-animation-generation` to the same number so an `animationend` from
|
|
34
|
+
* the previous node cannot idle the new one.
|
|
35
|
+
*
|
|
36
|
+
* If the trigger changes while already playing, the hook goes to `'idle'` first,
|
|
37
|
+
* then `'playing'` again. Use that path when the node must stay mounted (for
|
|
38
|
+
* example a text field that should keep focus and input). The idle update is
|
|
39
|
+
* flushed before paint; a follow-up effect sets `'playing'`.
|
|
40
|
+
*
|
|
41
|
+
* A same-value resubmit (for example the same validation error twice) does not
|
|
42
|
+
* change `trigger` by `Object.is`, so it will not replay. Pass a nonce as
|
|
43
|
+
* `trigger` when you need another play without a value change.
|
|
44
|
+
*
|
|
45
|
+
* A rerender that keeps the same `trigger` after idle does not auto-play.
|
|
46
|
+
*/
|
|
47
|
+
function useOneShotAnimation(trigger, isEnabled) {
|
|
48
|
+
const [animationState, setAnimationState] = useState('idle');
|
|
49
|
+
const [animationGeneration, setAnimationGeneration] = useState(0);
|
|
50
|
+
const previousTriggerRef = useRef(trigger);
|
|
51
|
+
const animationGenerationRef = useRef(0);
|
|
52
|
+
const animationStateRef = useRef(animationState);
|
|
53
|
+
const restartAfterIdleRef = useRef(false);
|
|
54
|
+
animationStateRef.current = animationState;
|
|
55
|
+
const onAnimationEnd = useCallback(event => {
|
|
56
|
+
if (event.target !== event.currentTarget || animationState !== 'playing') {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// This handler was created for a generation that a newer trigger replaced.
|
|
60
|
+
if (animationGeneration !== animationGenerationRef.current) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const stampedGeneration = readStampedAnimationGeneration(event);
|
|
64
|
+
// Unstamped callers (no keyed node) keep the previous completion behavior.
|
|
65
|
+
if (stampedGeneration !== null && stampedGeneration !== animationGenerationRef.current) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setAnimationState('idle');
|
|
69
|
+
}, [animationGeneration, animationState]);
|
|
70
|
+
useEnhancedEffect(() => {
|
|
71
|
+
if (!isEnabled) {
|
|
72
|
+
previousTriggerRef.current = trigger;
|
|
73
|
+
restartAfterIdleRef.current = false;
|
|
74
|
+
setAnimationState('idle');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (Object.is(previousTriggerRef.current, trigger)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
previousTriggerRef.current = trigger;
|
|
81
|
+
animationGenerationRef.current += 1;
|
|
82
|
+
setAnimationGeneration(animationGenerationRef.current);
|
|
83
|
+
if (animationStateRef.current === 'playing') {
|
|
84
|
+
restartAfterIdleRef.current = true;
|
|
85
|
+
setAnimationState('idle');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
restartAfterIdleRef.current = false;
|
|
89
|
+
setAnimationState('playing');
|
|
90
|
+
}, [isEnabled, trigger]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!restartAfterIdleRef.current || animationState !== 'idle') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
restartAfterIdleRef.current = false;
|
|
96
|
+
setAnimationState('playing');
|
|
97
|
+
}, [animationState]);
|
|
98
|
+
if (!isEnabled) {
|
|
99
|
+
return {
|
|
100
|
+
animationState: 'idle',
|
|
101
|
+
animationGeneration,
|
|
102
|
+
onAnimationEnd: animationEndNoop
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
animationState,
|
|
107
|
+
animationGeneration,
|
|
108
|
+
onAnimationEnd
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { ONE_SHOT_ANIMATION_GENERATION_ATTRIBUTE, useOneShotAnimation };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@box/blueprint-web",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"publishConfig": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@ariakit/react": "0.4.21",
|
|
57
57
|
"@ariakit/react-core": "0.4.21",
|
|
58
|
-
"@box/blueprint-web-assets": "^5.13.
|
|
58
|
+
"@box/blueprint-web-assets": "^5.13.8",
|
|
59
59
|
"@internationalized/date": "^3.12.0",
|
|
60
60
|
"@radix-ui/react-accordion": "1.1.2",
|
|
61
61
|
"@radix-ui/react-checkbox": "1.0.4",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@box/box-test-client": "^3.2.7",
|
|
87
87
|
"@box/playwright-utils": "^2.8.5",
|
|
88
|
-
"@box/storybook-utils": "^1.4.
|
|
88
|
+
"@box/storybook-utils": "^1.4.27",
|
|
89
89
|
"@figma/code-connect": "1.4.4",
|
|
90
90
|
"@playwright/test": "1.46.1",
|
|
91
91
|
"@types/js-yaml": "^4.0.9",
|