@pagopa/io-app-design-system 1.12.0 → 1.13.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.
Files changed (104) hide show
  1. package/lib/commonjs/components/buttons/ButtonSolid.js +36 -8
  2. package/lib/commonjs/components/buttons/ButtonSolid.js.map +1 -1
  3. package/lib/commonjs/components/buttons/__test__/__snapshots__/button.test.tsx.snap +46 -29
  4. package/lib/commonjs/components/index.js +11 -0
  5. package/lib/commonjs/components/index.js.map +1 -1
  6. package/lib/commonjs/components/layout/BlockButtons.js +1 -4
  7. package/lib/commonjs/components/layout/BlockButtons.js.map +1 -1
  8. package/lib/commonjs/components/layout/FooterWithButtons.js +20 -9
  9. package/lib/commonjs/components/layout/FooterWithButtons.js.map +1 -1
  10. package/lib/commonjs/components/layout/HeaderFirstLevel.js +6 -6
  11. package/lib/commonjs/components/layout/HeaderFirstLevel.js.map +1 -1
  12. package/lib/commonjs/components/layout/HeaderSecondLevel.js +8 -8
  13. package/lib/commonjs/components/layout/HeaderSecondLevel.js.map +1 -1
  14. package/lib/commonjs/components/loadingSpinner/LoadingSpinner.js +94 -0
  15. package/lib/commonjs/components/loadingSpinner/LoadingSpinner.js.map +1 -0
  16. package/lib/commonjs/components/loadingSpinner/index.js +17 -0
  17. package/lib/commonjs/components/loadingSpinner/index.js.map +1 -0
  18. package/lib/commonjs/components/pictograms/Pictogram.js +5 -1
  19. package/lib/commonjs/components/pictograms/Pictogram.js.map +1 -1
  20. package/lib/commonjs/components/pictograms/svg/PictogramBleedNotification.js +47 -0
  21. package/lib/commonjs/components/pictograms/svg/PictogramBleedNotification.js.map +1 -0
  22. package/lib/commonjs/components/pictograms/svg/PictogramNotification.js +47 -0
  23. package/lib/commonjs/components/pictograms/svg/PictogramNotification.js.map +1 -0
  24. package/lib/commonjs/components/pictograms/svg/originals/PictogramBleedNotification.svg +30 -0
  25. package/lib/commonjs/components/pictograms/svg/originals/PictogramNotification.svg +30 -0
  26. package/lib/commonjs/core/IOStyles.js +10 -0
  27. package/lib/commonjs/core/IOStyles.js.map +1 -1
  28. package/lib/commonjs/core/IOTransitions.js +106 -0
  29. package/lib/commonjs/core/IOTransitions.js.map +1 -0
  30. package/lib/commonjs/core/index.js +11 -0
  31. package/lib/commonjs/core/index.js.map +1 -1
  32. package/lib/module/components/buttons/ButtonSolid.js +36 -9
  33. package/lib/module/components/buttons/ButtonSolid.js.map +1 -1
  34. package/lib/module/components/buttons/__test__/__snapshots__/button.test.tsx.snap +46 -29
  35. package/lib/module/components/index.js +1 -0
  36. package/lib/module/components/index.js.map +1 -1
  37. package/lib/module/components/layout/BlockButtons.js +1 -4
  38. package/lib/module/components/layout/BlockButtons.js.map +1 -1
  39. package/lib/module/components/layout/FooterWithButtons.js +20 -10
  40. package/lib/module/components/layout/FooterWithButtons.js.map +1 -1
  41. package/lib/module/components/layout/HeaderFirstLevel.js +6 -6
  42. package/lib/module/components/layout/HeaderFirstLevel.js.map +1 -1
  43. package/lib/module/components/layout/HeaderSecondLevel.js +8 -8
  44. package/lib/module/components/layout/HeaderSecondLevel.js.map +1 -1
  45. package/lib/module/components/loadingSpinner/LoadingSpinner.js +86 -0
  46. package/lib/module/components/loadingSpinner/LoadingSpinner.js.map +1 -0
  47. package/lib/module/components/loadingSpinner/index.js +2 -0
  48. package/lib/module/components/loadingSpinner/index.js.map +1 -0
  49. package/lib/module/components/pictograms/Pictogram.js +5 -1
  50. package/lib/module/components/pictograms/Pictogram.js.map +1 -1
  51. package/lib/module/components/pictograms/svg/PictogramBleedNotification.js +39 -0
  52. package/lib/module/components/pictograms/svg/PictogramBleedNotification.js.map +1 -0
  53. package/lib/module/components/pictograms/svg/PictogramNotification.js +39 -0
  54. package/lib/module/components/pictograms/svg/PictogramNotification.js.map +1 -0
  55. package/lib/module/components/pictograms/svg/originals/PictogramBleedNotification.svg +30 -0
  56. package/lib/module/components/pictograms/svg/originals/PictogramNotification.svg +30 -0
  57. package/lib/module/core/IOStyles.js +10 -0
  58. package/lib/module/core/IOStyles.js.map +1 -1
  59. package/lib/module/core/IOTransitions.js +98 -0
  60. package/lib/module/core/IOTransitions.js.map +1 -0
  61. package/lib/module/core/index.js +1 -0
  62. package/lib/module/core/index.js.map +1 -1
  63. package/lib/typescript/components/buttons/ButtonSolid.d.ts +5 -1
  64. package/lib/typescript/components/buttons/ButtonSolid.d.ts.map +1 -1
  65. package/lib/typescript/components/index.d.ts +1 -0
  66. package/lib/typescript/components/index.d.ts.map +1 -1
  67. package/lib/typescript/components/layout/FooterWithButtons.d.ts +4 -1
  68. package/lib/typescript/components/layout/FooterWithButtons.d.ts.map +1 -1
  69. package/lib/typescript/components/layout/HeaderFirstLevel.d.ts.map +1 -1
  70. package/lib/typescript/components/loadingSpinner/LoadingSpinner.d.ts +17 -0
  71. package/lib/typescript/components/loadingSpinner/LoadingSpinner.d.ts.map +1 -0
  72. package/lib/typescript/components/loadingSpinner/index.d.ts +2 -0
  73. package/lib/typescript/components/loadingSpinner/index.d.ts.map +1 -0
  74. package/lib/typescript/components/pictograms/Pictogram.d.ts +2 -1
  75. package/lib/typescript/components/pictograms/Pictogram.d.ts.map +1 -1
  76. package/lib/typescript/components/pictograms/svg/PictogramBleedNotification.d.ts +5 -0
  77. package/lib/typescript/components/pictograms/svg/PictogramBleedNotification.d.ts.map +1 -0
  78. package/lib/typescript/components/pictograms/svg/PictogramNotification.d.ts +5 -0
  79. package/lib/typescript/components/pictograms/svg/PictogramNotification.d.ts.map +1 -0
  80. package/lib/typescript/core/IOStyles.d.ts +10 -0
  81. package/lib/typescript/core/IOStyles.d.ts.map +1 -1
  82. package/lib/typescript/core/IOTransitions.d.ts +58 -0
  83. package/lib/typescript/core/IOTransitions.d.ts.map +1 -0
  84. package/lib/typescript/core/index.d.ts +1 -0
  85. package/lib/typescript/core/index.d.ts.map +1 -1
  86. package/lib/typescript/stories/ButtonDS.stories.d.ts +1 -1
  87. package/package.json +1 -1
  88. package/src/components/buttons/ButtonSolid.tsx +67 -22
  89. package/src/components/buttons/__test__/__snapshots__/button.test.tsx.snap +46 -29
  90. package/src/components/index.tsx +1 -0
  91. package/src/components/layout/BlockButtons.tsx +1 -1
  92. package/src/components/layout/FooterWithButtons.tsx +11 -5
  93. package/src/components/layout/HeaderFirstLevel.tsx +5 -6
  94. package/src/components/layout/HeaderSecondLevel.tsx +11 -11
  95. package/src/components/loadingSpinner/LoadingSpinner.tsx +121 -0
  96. package/src/components/loadingSpinner/index.tsx +1 -0
  97. package/src/components/pictograms/Pictogram.tsx +7 -2
  98. package/src/components/pictograms/svg/PictogramBleedNotification.tsx +42 -0
  99. package/src/components/pictograms/svg/PictogramNotification.tsx +42 -0
  100. package/src/components/pictograms/svg/originals/PictogramBleedNotification.svg +30 -0
  101. package/src/components/pictograms/svg/originals/PictogramNotification.svg +30 -0
  102. package/src/core/IOStyles.ts +10 -0
  103. package/src/core/IOTransitions.ts +94 -0
  104. package/src/core/index.ts +1 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ A custom enter transition designed for the average size
3
+ inner content of a button or module (e.g. text).
4
+ The scaling effect is slight.
5
+ */
6
+ export declare const enterTransitionInnerContent: () => {
7
+ initialValues: {
8
+ opacity: number;
9
+ transform: {
10
+ scale: number;
11
+ }[];
12
+ };
13
+ animations: {
14
+ opacity: 1;
15
+ transform: {
16
+ scale: 1;
17
+ }[];
18
+ };
19
+ };
20
+ /**
21
+ A custom enter transition designed for the small size
22
+ inner content of a button or module (e.g. loading spinner).
23
+ The scaling effect is accentuated.
24
+ */
25
+ export declare const enterTransitionInnerContentSmall: () => {
26
+ initialValues: {
27
+ opacity: number;
28
+ transform: {
29
+ scale: number;
30
+ }[];
31
+ };
32
+ animations: {
33
+ opacity: 1;
34
+ transform: {
35
+ scale: 1;
36
+ }[];
37
+ };
38
+ };
39
+ /**
40
+ A custom exit transition designed for both small
41
+ and average size inner content of a button or module.
42
+ The scaling effect is slight.
43
+ */
44
+ export declare const exitTransitionInnerContent: () => {
45
+ initialValues: {
46
+ opacity: number;
47
+ transform: {
48
+ scale: number;
49
+ }[];
50
+ };
51
+ animations: {
52
+ opacity: 0;
53
+ transform: {
54
+ scale: 0.9;
55
+ }[];
56
+ };
57
+ };
58
+ //# sourceMappingURL=IOTransitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IOTransitions.d.ts","sourceRoot":"","sources":["../../../src/core/IOTransitions.ts"],"names":[],"mappings":"AAEA;;;;EAIE;AACF,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;CAwBvC,CAAC;AAEF;;;;EAIE;AACF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;CAwB5C,CAAC;AAEF;;;;EAIE;AACF,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;CAwBtC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export * from "./IOColors";
2
2
  export * from "./IOAnimations";
3
+ export * from "./IOTransitions";
3
4
  export * from "./IOStyles";
4
5
  export * from "./IOShapes";
5
6
  export * from "./IOSpacing";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC"}
@@ -2,7 +2,7 @@
2
2
  import type { StoryObj } from "@storybook/react";
3
3
  declare const meta: {
4
4
  title: string;
5
- component: import("react").MemoExoticComponent<({ color, label, fullWidth, disabled, icon, iconPosition, onPress, accessibilityLabel, accessibilityHint, testID }: import("../components").ButtonSolidProps) => import("react").JSX.Element>;
5
+ component: import("react").MemoExoticComponent<({ color, label, fullWidth, disabled, loading, icon, iconPosition, onPress, accessibilityLabel, accessibilityHint, testID }: import("../components").ButtonSolidProps) => import("react").JSX.Element>;
6
6
  parameters: {
7
7
  layout: string;
8
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/io-app-design-system",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "The library defining the core components of the design system of @pagopa/io-app",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -9,6 +9,7 @@ import Animated, {
9
9
  useSharedValue,
10
10
  withSpring
11
11
  } from "react-native-reanimated";
12
+ import ReactNativeHapticFeedback from "react-native-haptic-feedback";
12
13
  import { IOIconSizeScale, IOIcons, Icon } from "../icons";
13
14
  import { WithTestID } from "../../utils/types";
14
15
  import { HSpacer } from "../spacer/Spacer";
@@ -19,8 +20,12 @@ import {
19
20
  IOColors,
20
21
  IOScaleValues,
21
22
  IOSpringValues,
23
+ enterTransitionInnerContent,
24
+ enterTransitionInnerContentSmall,
25
+ exitTransitionInnerContent,
22
26
  useIOExperimentalDesign
23
27
  } from "../../core";
28
+ import { LoadingSpinner } from "../loadingSpinner";
24
29
 
25
30
  type ButtonSolidColor = "primary" | "danger" | "contrast";
26
31
 
@@ -65,6 +70,10 @@ export type ButtonSolidProps = WithTestID<{
65
70
  * @default false
66
71
  */
67
72
  fullWidth?: boolean;
73
+ /**
74
+ * @default false
75
+ */
76
+ loading?: boolean;
68
77
  /**
69
78
  * @default false
70
79
  */
@@ -152,6 +161,7 @@ export const ButtonSolid = React.memo(
152
161
  label,
153
162
  fullWidth = false,
154
163
  disabled = false,
164
+ loading = false,
155
165
  icon,
156
166
  iconPosition = "start",
157
167
  onPress,
@@ -211,6 +221,19 @@ export const ButtonSolid = React.memo(
211
221
  isPressed.value = 0;
212
222
  }, [isPressed]);
213
223
 
224
+ const handleOnPress = useCallback(
225
+ (event: GestureResponderEvent) => {
226
+ /* Don't call `onPress` if the button is
227
+ in loading state */
228
+ if (loading) {
229
+ return;
230
+ }
231
+ ReactNativeHapticFeedback.trigger("impactLight");
232
+ onPress(event);
233
+ },
234
+ [loading, onPress]
235
+ );
236
+
214
237
  // Label & Icons colors
215
238
  const foregroundColor: IOColors = disabled
216
239
  ? colorMap[color]?.label?.disabled
@@ -218,22 +241,23 @@ export const ButtonSolid = React.memo(
218
241
 
219
242
  return (
220
243
  <Pressable
244
+ testID={testID}
245
+ accessible={true}
221
246
  accessibilityLabel={accessibilityLabel}
222
247
  accessibilityHint={accessibilityHint}
248
+ accessibilityState={{ busy: loading }}
223
249
  accessibilityRole={"button"}
224
- testID={testID}
225
- onPress={onPress}
250
+ onPress={handleOnPress}
226
251
  onPressIn={onPressIn}
227
252
  onPressOut={onPressOut}
228
- accessible={true}
229
253
  disabled={disabled}
230
254
  style={!fullWidth ? IOButtonStyles.dimensionsDefault : {}}
231
255
  >
232
256
  <Animated.View
233
257
  style={[
234
258
  buttonStyles.button,
259
+ { overflow: "hidden" },
235
260
  isExperimental && fullWidth && { paddingHorizontal: 16 },
236
- iconPosition === "end" && { flexDirection: "row-reverse" },
237
261
  buttonStyles.buttonSizeDefault,
238
262
  disabled
239
263
  ? isExperimental
@@ -241,30 +265,51 @@ export const ButtonSolid = React.memo(
241
265
  : legacyStyles.backgroundDisabled
242
266
  : { backgroundColor: colorMap[color]?.default },
243
267
  /* Prevent Reanimated from overriding background colors
244
- if button is disabled */
268
+ if button is disabled */
245
269
  !disabled && pressedAnimationStyle
246
270
  ]}
247
271
  >
248
- {icon && (
249
- <>
250
- {/* If 'iconPosition' is set to 'end', we use
272
+ {loading && (
273
+ <Animated.View
274
+ style={buttonStyles.buttonInner}
275
+ entering={enterTransitionInnerContentSmall}
276
+ exiting={exitTransitionInnerContent}
277
+ >
278
+ <LoadingSpinner color={foregroundColor} />
279
+ </Animated.View>
280
+ )}
281
+
282
+ {!loading && (
283
+ <Animated.View
284
+ style={[
285
+ buttonStyles.buttonInner,
286
+ iconPosition === "end" && { flexDirection: "row-reverse" }
287
+ ]}
288
+ entering={enterTransitionInnerContent}
289
+ exiting={exitTransitionInnerContent}
290
+ >
291
+ {icon && (
292
+ <>
293
+ {/* If 'iconPosition' is set to 'end', we use
251
294
  reverse flex property to invert the position */}
252
- <Icon name={icon} size={iconSize} color={foregroundColor} />
253
- {/* Once we have support for 'gap' property,
295
+ <Icon name={icon} size={iconSize} color={foregroundColor} />
296
+ {/* Once we have support for 'gap' property,
254
297
  we can get rid of that spacer */}
255
- <HSpacer size={8} />
256
- </>
298
+ <HSpacer size={8} />
299
+ </>
300
+ )}
301
+ <ButtonText
302
+ color={foregroundColor}
303
+ style={IOButtonStyles.label}
304
+ numberOfLines={1}
305
+ ellipsizeMode="tail"
306
+ allowFontScaling={isExperimental}
307
+ maxFontSizeMultiplier={1.3}
308
+ >
309
+ {label}
310
+ </ButtonText>
311
+ </Animated.View>
257
312
  )}
258
- <ButtonText
259
- color={foregroundColor}
260
- style={IOButtonStyles.label}
261
- numberOfLines={1}
262
- ellipsizeMode="tail"
263
- allowFontScaling={isExperimental}
264
- maxFontSizeMultiplier={1.3}
265
- >
266
- {label}
267
- </ButtonText>
268
313
  </Animated.View>
269
314
  </Pressable>
270
315
  );
@@ -371,7 +371,7 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
371
371
  accessibilityRole="button"
372
372
  accessibilityState={
373
373
  {
374
- "busy": undefined,
374
+ "busy": false,
375
375
  "checked": undefined,
376
376
  "disabled": false,
377
377
  "expanded": undefined,
@@ -416,7 +416,9 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
416
416
  "paddingHorizontal": 16,
417
417
  "textAlignVertical": "center",
418
418
  },
419
- false,
419
+ {
420
+ "overflow": "hidden",
421
+ },
420
422
  false,
421
423
  {
422
424
  "height": 40,
@@ -435,40 +437,55 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
435
437
  ]
436
438
  }
437
439
  >
438
- <Text
439
- allowFontScaling={false}
440
- color="white"
441
- defaultColor="white"
442
- defaultWeight="Bold"
443
- ellipsizeMode="tail"
444
- font="TitilliumWeb"
445
- fontStyle={
446
- {
447
- "fontSize": 16,
448
- }
449
- }
450
- maxFontSizeMultiplier={1.3}
451
- numberOfLines={1}
440
+ <View
441
+ entering={[Function]}
442
+ exiting={[Function]}
452
443
  style={
453
444
  [
454
445
  {
455
- "alignSelf": "center",
456
- },
457
- {
458
- "fontSize": 16,
459
- },
460
- {
461
- "color": "#FFFFFF",
462
- "fontFamily": "Titillium Web",
463
- "fontStyle": "normal",
464
- "fontWeight": "700",
446
+ "alignItems": "center",
447
+ "flexDirection": "row",
448
+ "justifyContent": "center",
465
449
  },
450
+ false,
466
451
  ]
467
452
  }
468
- weight="Bold"
469
453
  >
470
- label
471
- </Text>
454
+ <Text
455
+ allowFontScaling={false}
456
+ color="white"
457
+ defaultColor="white"
458
+ defaultWeight="Bold"
459
+ ellipsizeMode="tail"
460
+ font="TitilliumWeb"
461
+ fontStyle={
462
+ {
463
+ "fontSize": 16,
464
+ }
465
+ }
466
+ maxFontSizeMultiplier={1.3}
467
+ numberOfLines={1}
468
+ style={
469
+ [
470
+ {
471
+ "alignSelf": "center",
472
+ },
473
+ {
474
+ "fontSize": 16,
475
+ },
476
+ {
477
+ "color": "#FFFFFF",
478
+ "fontFamily": "Titillium Web",
479
+ "fontStyle": "normal",
480
+ "fontWeight": "700",
481
+ },
482
+ ]
483
+ }
484
+ weight="Bold"
485
+ >
486
+ label
487
+ </Text>
488
+ </View>
472
489
  </View>
473
490
  </View>
474
491
  `;
@@ -10,6 +10,7 @@ export * from "./featureInfo";
10
10
  export * from "./icons";
11
11
  export * from "./listitems";
12
12
  export * from "./logos";
13
+ export * from "./loadingSpinner";
13
14
  export * from "./modules";
14
15
  export * from "./banner";
15
16
  export * from "./pictograms";
@@ -133,7 +133,7 @@ export const BlockButtons = (props: Props) => {
133
133
  );
134
134
 
135
135
  return (
136
- <View style={{ ...IOStyles.flex, ...IOStyles.row }}>
136
+ <View style={IOStyles.row}>
137
137
  {renderLeftButton()}
138
138
  {renderMidButton()}
139
139
  {renderRightButton()}
@@ -4,6 +4,10 @@ import { StyleSheet, View } from "react-native";
4
4
  import { IOStyles } from "../../core";
5
5
  import { BlockButtons, BlockButtonsProps } from "./BlockButtons";
6
6
 
7
+ type Props = BlockButtonsProps & {
8
+ sticky?: boolean;
9
+ };
10
+
7
11
  // TODO: Refactor with an unique component like `FooterTopShadow` after bonus vacanze
8
12
  const styles = StyleSheet.create({
9
13
  container: {
@@ -11,7 +15,9 @@ const styles = StyleSheet.create({
11
15
  width: "100%",
12
16
  // This Magic number is an heritage of the app code-base, this component should be removed in favor of `GradientBottomAction`
13
17
  marginTop: -50,
14
- paddingTop: 50,
18
+ paddingTop: 50
19
+ },
20
+ sticky: {
15
21
  position: "absolute",
16
22
  bottom: 0
17
23
  }
@@ -21,15 +27,15 @@ const styles = StyleSheet.create({
21
27
  * Implements a component that show buttons as sticky footer
22
28
  * It can include 1, 2 or 3 buttons. If they are 2, they can have the inlineHalf or the inlineOneThird style
23
29
  */
24
- export const FooterWithButtons = (props: BlockButtonsProps) => (
30
+ export const FooterWithButtons = ({ sticky = false, ...rest }: Props) => (
25
31
  <View
26
- style={styles.container}
27
- accessible={props.accessible}
32
+ style={{ ...styles.container, ...(sticky ? styles.sticky : {}) }}
33
+ accessible={rest.accessible}
28
34
  pointerEvents={"box-none"}
29
35
  testID="FooterWithButtons"
30
36
  >
31
37
  <View style={IOStyles.footer}>
32
- <BlockButtons {...props} />
38
+ <BlockButtons {...rest} />
33
39
  </View>
34
40
  </View>
35
41
  );
@@ -79,23 +79,22 @@ export const HeaderFirstLevel = ({
79
79
  {title}
80
80
  </H3>
81
81
  <View style={[IOStyles.row, { flexShrink: 0 }]}>
82
- {type !== "base" && <IconButton {...firstAction} color="neutral" />}
83
- {(type === "twoActions" || type === "threeActions") && (
82
+ {type === "threeActions" && (
84
83
  <>
84
+ <IconButton {...thirdAction} color="neutral" />
85
85
  {/* Ideally, with the "gap" flex property,
86
86
  we can get rid of these ugly constructs */}
87
- +.9+
88
87
  <HSpacer size={16} />
89
- <IconButton {...secondAction} color="neutral" />
90
88
  </>
91
89
  )}
92
- {type === "threeActions" && (
90
+ {(type === "twoActions" || type === "threeActions") && (
93
91
  <>
92
+ <IconButton {...secondAction} color="neutral" />
94
93
  {/* Same as above */}
95
94
  <HSpacer size={16} />
96
- <IconButton {...thirdAction} color="neutral" />
97
95
  </>
98
96
  )}
97
+ {type !== "base" && <IconButton {...firstAction} color="neutral" />}
99
98
  </View>
100
99
  </View>
101
100
  </View>
@@ -179,25 +179,25 @@ export const HeaderSecondLevel = ({
179
179
  {title}
180
180
  </Animated.Text>
181
181
  <View style={[IOStyles.row, { flexShrink: 0 }]}>
182
- {type !== "base" ? (
183
- <IconButton {...firstAction} color="neutral" />
184
- ) : (
185
- <HSpacer size={iconBtnSizeSmall as IOSpacer} />
182
+ {type === "threeActions" && (
183
+ <>
184
+ <IconButton {...thirdAction} color="neutral" />
185
+ {/* Same as above */}
186
+ <HSpacer size={16} />
187
+ </>
186
188
  )}
187
189
  {(type === "twoActions" || type === "threeActions") && (
188
190
  <>
191
+ <IconButton {...secondAction} color="neutral" />
189
192
  {/* Ideally, with the "gap" flex property,
190
193
  we can get rid of these ugly constructs */}
191
194
  <HSpacer size={16} />
192
- <IconButton {...secondAction} color="neutral" />
193
195
  </>
194
196
  )}
195
- {type === "threeActions" && (
196
- <>
197
- {/* Same as above */}
198
- <HSpacer size={16} />
199
- <IconButton {...thirdAction} color="neutral" />
200
- </>
197
+ {type !== "base" ? (
198
+ <IconButton {...firstAction} color="neutral" />
199
+ ) : (
200
+ <HSpacer size={iconBtnSizeSmall as IOSpacer} />
201
201
  )}
202
202
  </View>
203
203
  </Animated.View>
@@ -0,0 +1,121 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { View, Animated, Easing } from "react-native";
3
+ import Svg, { Defs, G, LinearGradient, Path, Stop } from "react-native-svg";
4
+ import { WithTestID } from "../../utils/types";
5
+ import { IOColors } from "../../core";
6
+
7
+ type Props = WithTestID<{
8
+ color?: IOColors;
9
+ stroke?: number;
10
+ size?: IOLoadingSpinnerSizeScale;
11
+ durationMs?: number;
12
+ }>;
13
+
14
+ /**
15
+ * Size scale, 76 is kept for backward compatibility with the old design system but 48 is enough for the new one.
16
+ * It will be removed in the future.
17
+ */
18
+ export type IOLoadingSpinnerSizeScale = 24 | 48 | 76;
19
+
20
+ const startRotationAnimation = (
21
+ durationMs: number,
22
+ rotationDegree: Animated.Value
23
+ ): void => {
24
+ Animated.loop(
25
+ Animated.timing(rotationDegree, {
26
+ toValue: 360,
27
+ duration: durationMs,
28
+ easing: Easing.linear,
29
+ useNativeDriver: true
30
+ })
31
+ ).start();
32
+ };
33
+
34
+ export const LoadingSpinner = ({
35
+ color = "blueIO-500",
36
+ stroke = 3,
37
+ size = 24,
38
+ durationMs = 750
39
+ }: Props): React.ReactElement => {
40
+ const rotationDegree = useRef(new Animated.Value(0)).current;
41
+
42
+ useEffect(() => {
43
+ startRotationAnimation(durationMs, rotationDegree);
44
+ }, [durationMs, rotationDegree]);
45
+
46
+ return (
47
+ <>
48
+ <View
49
+ style={{ width: size, height: size }}
50
+ accessibilityRole="progressbar"
51
+ testID={"LoadingSpinnerTestID"}
52
+ >
53
+ <Animated.View
54
+ testID={"LoadingSpinnerAnimatedTestID"}
55
+ style={{
56
+ transform: [
57
+ {
58
+ rotateZ: rotationDegree.interpolate({
59
+ inputRange: [0, 360],
60
+ outputRange: ["0deg", "360deg"]
61
+ })
62
+ }
63
+ ]
64
+ }}
65
+ >
66
+ {/* Thanks to Ben Ilegbodu for the article on how to
67
+ create a a SVG gradient loading spinner. Below is
68
+ a parameterized version of his version of his code.
69
+ Source: https://www.benmvp.com/blog/how-to-create-circle-svg-gradient-loading-spinner/ */}
70
+ <Svg
71
+ width={size}
72
+ height={size}
73
+ viewBox={`0 0 ${size} ${size}`}
74
+ fill="none"
75
+ >
76
+ <Defs>
77
+ <LinearGradient id="spinner-secondHalf">
78
+ <Stop offset="0%" stopOpacity="0" stopColor={IOColors[color]} />
79
+ <Stop
80
+ offset="100%"
81
+ stopOpacity="1"
82
+ stopColor={IOColors[color]}
83
+ />
84
+ </LinearGradient>
85
+ <LinearGradient id="spinner-firstHalf">
86
+ <Stop offset="0%" stopOpacity="1" stopColor={IOColors[color]} />
87
+ <Stop
88
+ offset="100%"
89
+ stopOpacity="1"
90
+ stopColor={IOColors[color]}
91
+ />
92
+ </LinearGradient>
93
+ </Defs>
94
+
95
+ <G strokeWidth={stroke}>
96
+ <Path
97
+ stroke="url(#spinner-secondHalf)"
98
+ d={`M ${stroke / 2} ${size / 2} A ${size / 2 - stroke / 2} ${
99
+ size / 2 - stroke / 2
100
+ } 0 0 1 ${size - stroke / 2} ${size / 2}`}
101
+ />
102
+ <Path
103
+ stroke="url(#spinner-firstHalf)"
104
+ d={`M ${size - stroke / 2} ${size / 2} A ${
105
+ size / 2 - stroke / 2
106
+ } ${size / 2 - stroke / 2} 0 0 1 ${stroke / 2} ${size / 2}`}
107
+ />
108
+ <Path
109
+ stroke={IOColors[color]}
110
+ strokeLinecap="round"
111
+ d={`M ${stroke / 2} ${size / 2} A ${size / 2 - stroke / 2} ${
112
+ size / 2 - stroke / 2
113
+ } 0 0 1 ${stroke / 2} ${size / 2 - stroke / 4}`}
114
+ />
115
+ </G>
116
+ </Svg>
117
+ </Animated.View>
118
+ </View>
119
+ </>
120
+ );
121
+ };
@@ -0,0 +1 @@
1
+ export * from "./LoadingSpinner";
@@ -60,6 +60,7 @@ import PictogramCharity from "./svg/PictogramCharity";
60
60
  import PictogramEmpty from "./svg/PictogramEmpty";
61
61
  import PictogramCie from "./svg/PictogramCie";
62
62
  import PictogramFeature from "./svg/PictogramFeature";
63
+ import PictogramNotification from "./svg/PictogramNotification";
63
64
  import PictogramObjClock from "./svg/PictogramObjClock";
64
65
  import PictogramObjIbanCard from "./svg/PictogramObjIbanCard";
65
66
  import PictogramObjManual from "./svg/PictogramObjManual";
@@ -77,6 +78,7 @@ import PictogramBleedFeature from "./svg/PictogramBleedFeature";
77
78
  import PictogramBleedCie from "./svg/PictogramBleedCie";
78
79
  import PictogramBleedCameraRequest from "./svg/PictogramBleedCameraRequest";
79
80
  import PictogramBleedCameraDenied from "./svg/PictogramBleedCameraDenied";
81
+ import PictogramBleedNotification from "./svg/PictogramBleedNotification";
80
82
 
81
83
  export const IOPictograms = {
82
84
  // Start legacy pictograms //
@@ -134,6 +136,7 @@ export const IOPictograms = {
134
136
  cie: PictogramCie,
135
137
  time: PictogramTime,
136
138
  passcode: PictogramPasscode,
139
+ notification: PictogramNotification,
137
140
  // Start Objects Pictogram
138
141
  ibanCard: PictogramObjIbanCard,
139
142
  followMessage: PictogramObjFollowMessage,
@@ -227,7 +230,8 @@ export type IOPictogramsBleed = Extract<
227
230
  | "feature"
228
231
  | "cie"
229
232
  | "cameraRequest"
230
- | "cameraDenied",
233
+ | "cameraDenied"
234
+ | "notification",
231
235
  IOPictograms
232
236
  >;
233
237
 
@@ -242,7 +246,8 @@ export const IOPictogramsBleed: {
242
246
  feature: PictogramBleedFeature,
243
247
  cie: PictogramBleedCie,
244
248
  cameraRequest: PictogramBleedCameraRequest,
245
- cameraDenied: PictogramBleedCameraDenied
249
+ cameraDenied: PictogramBleedCameraDenied,
250
+ notification: PictogramBleedNotification
246
251
  };
247
252
 
248
253
  export const PictogramBleed = ({