@jobber/components-native 0.62.3 → 0.63.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 (32) hide show
  1. package/dist/package.json +3 -3
  2. package/dist/src/Glimmer/Glimmer.js +42 -0
  3. package/dist/src/Glimmer/Glimmer.shape.style.js +16 -0
  4. package/dist/src/Glimmer/Glimmer.size.style.js +9 -0
  5. package/dist/src/Glimmer/Glimmer.style.js +20 -0
  6. package/dist/src/Glimmer/index.js +1 -0
  7. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +20 -2
  8. package/dist/src/InputFieldWrapper/InputFieldWrapper.style.js +22 -0
  9. package/dist/src/InputText/InputText.js +4 -3
  10. package/dist/src/index.js +6 -5
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/dist/types/src/Glimmer/Glimmer.d.ts +31 -0
  13. package/dist/types/src/Glimmer/Glimmer.shape.style.d.ts +14 -0
  14. package/dist/types/src/Glimmer/Glimmer.size.style.d.ts +17 -0
  15. package/dist/types/src/Glimmer/Glimmer.style.d.ts +18 -0
  16. package/dist/types/src/Glimmer/index.d.ts +1 -0
  17. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +11 -1
  18. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.style.d.ts +22 -0
  19. package/dist/types/src/InputText/InputText.d.ts +1 -1
  20. package/dist/types/src/index.d.ts +6 -5
  21. package/package.json +3 -3
  22. package/src/Glimmer/Glimmer.shape.style.ts +17 -0
  23. package/src/Glimmer/Glimmer.size.style.ts +10 -0
  24. package/src/Glimmer/Glimmer.style.ts +23 -0
  25. package/src/Glimmer/Glimmer.test.tsx +73 -0
  26. package/src/Glimmer/Glimmer.tsx +106 -0
  27. package/src/Glimmer/index.ts +1 -0
  28. package/src/InputFieldWrapper/InputFieldWrapper.style.ts +25 -0
  29. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +30 -0
  30. package/src/InputFieldWrapper/InputFieldWrapper.tsx +50 -1
  31. package/src/InputText/InputText.tsx +10 -1
  32. package/src/index.ts +6 -5
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.62.3",
3
+ "version": "0.63.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "build:clean": "rm -rf ./dist"
37
37
  },
38
38
  "dependencies": {
39
- "@jobber/design": "^0.56.3",
39
+ "@jobber/design": "^0.56.4",
40
40
  "@jobber/hooks": "^2.9.4",
41
41
  "@react-native-clipboard/clipboard": "^1.11.2",
42
42
  "@react-native-picker/picker": "^2.4.10",
@@ -84,5 +84,5 @@
84
84
  "react-native-safe-area-context": "^4.5.2",
85
85
  "react-native-svg": ">=12.0.0"
86
86
  },
87
- "gitHead": "49814f608cafabdde783c712972de4665cc6c10c"
87
+ "gitHead": "15034baa004c4a11f17338b96d6f2678f3183d56"
88
88
  }
@@ -0,0 +1,42 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import { Animated, Easing, View } from "react-native";
3
+ import Svg, { Defs, LinearGradient, Rect, Stop } from "react-native-svg";
4
+ import { shineWidth, styles } from "./Glimmer.style";
5
+ import { sizeStyles } from "./Glimmer.size.style";
6
+ import { shapeStyles } from "./Glimmer.shape.style";
7
+ import { tokens } from "../utils/design";
8
+ export const GLIMMER_TEST_ID = "ATL-Glimmer";
9
+ export const GLIMMER_SHINE_TEST_ID = "ATL-Glimmer-Shine";
10
+ export function Glimmer({ width, shape = "rectangle", size = "base", timing = "base", }) {
11
+ const leftPosition = useRef(new Animated.Value(-shineWidth)).current;
12
+ const [parentWidth, setParentWidth] = useState(0);
13
+ useEffect(() => {
14
+ const shine = Animated.loop(Animated.timing(leftPosition, {
15
+ toValue: parentWidth + shineWidth,
16
+ duration: timing === "base"
17
+ ? tokens["timing-loading--extended"]
18
+ : tokens["timing-loading"],
19
+ easing: Easing.ease,
20
+ useNativeDriver: true,
21
+ }));
22
+ shine.start();
23
+ return shine.stop;
24
+ }, [parentWidth]);
25
+ return (React.createElement(View, { style: [
26
+ styles.container,
27
+ sizeStyles[size],
28
+ shapeStyles[shape],
29
+ { width },
30
+ ], onLayout: getWidth, testID: GLIMMER_TEST_ID },
31
+ React.createElement(Animated.View, { style: [styles.shine, { transform: [{ translateX: leftPosition }] }], testID: GLIMMER_SHINE_TEST_ID },
32
+ React.createElement(Svg, null,
33
+ React.createElement(Defs, null,
34
+ React.createElement(LinearGradient, { id: "gradientShine", x1: 0, y1: 0.5, x2: 1, y2: 0.5 },
35
+ React.createElement(Stop, { offset: "0%", stopColor: tokens["color-surface--background"] }),
36
+ React.createElement(Stop, { offset: "50%", stopColor: tokens["color-surface"] }),
37
+ React.createElement(Stop, { offset: "100%", stopColor: tokens["color-surface--background"] }))),
38
+ React.createElement(Rect, { fill: "url(#gradientShine)", height: "100%", width: "100%" })))));
39
+ function getWidth(event) {
40
+ setParentWidth(event.nativeEvent.layout.width);
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+ export const shapeStyles = StyleSheet.create({
4
+ rectangle: {
5
+ width: "100%",
6
+ },
7
+ square: {
8
+ width: "auto",
9
+ aspectRatio: 1 / 1,
10
+ },
11
+ circle: {
12
+ width: "auto",
13
+ aspectRatio: 1 / 1,
14
+ borderRadius: tokens["radius-circle"],
15
+ },
16
+ });
@@ -0,0 +1,9 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+ export const sizeStyles = StyleSheet.create({
4
+ small: { height: tokens["space-small"] },
5
+ base: { height: tokens["space-base"] },
6
+ large: { height: tokens["space-large"] },
7
+ larger: { height: tokens["space-larger"] },
8
+ largest: { height: tokens["space-largest"] },
9
+ });
@@ -0,0 +1,20 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+ export const shineWidth = tokens["space-largest"];
4
+ export const styles = StyleSheet.create({
5
+ container: {
6
+ backgroundColor: tokens["color-surface--background"],
7
+ overflow: "hidden",
8
+ position: "relative",
9
+ width: "100%",
10
+ height: tokens["space-base"],
11
+ borderRadius: tokens["radius-base"],
12
+ },
13
+ shine: {
14
+ position: "absolute",
15
+ top: 0,
16
+ left: 0,
17
+ width: shineWidth,
18
+ height: "100%",
19
+ },
20
+ });
@@ -0,0 +1 @@
1
+ export * from "./Glimmer";
@@ -6,15 +6,21 @@ import { styles } from "./InputFieldWrapper.style";
6
6
  import { PrefixIcon, PrefixLabel } from "./components/Prefix/Prefix";
7
7
  import { SuffixIcon, SuffixLabel } from "./components/Suffix/Suffix";
8
8
  import { ClearAction } from "./components/ClearAction";
9
+ import { Glimmer } from "../Glimmer/Glimmer";
9
10
  import { ErrorMessageWrapper } from "../ErrorMessageWrapper";
10
11
  import { typographyStyles } from "../Typography";
11
12
  import { Text } from "../Text";
12
- export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, hasMiniLabel = false, hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, toolbar, toolbarVisibility = "while-editing", }) {
13
+ import { ActivityIndicator } from "../ActivityIndicator";
14
+ export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
15
+ export const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
16
+ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, hasMiniLabel = false, hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, toolbar, toolbarVisibility = "while-editing", loading = false, loadingType = "spinner", }) {
13
17
  fieldAffixRequiredPropsCheck([prefix, suffix]);
14
18
  const handleClear = onClear !== null && onClear !== void 0 ? onClear : noopClear;
15
19
  warnIfClearActionWithNoOnClear(onClear, showClearAction);
16
20
  const inputInvalid = Boolean(invalid) || Boolean(error);
17
21
  const isToolbarVisible = toolbar && (toolbarVisibility === "always" || focused);
22
+ const showLoadingSpinner = loading && loadingType === "spinner";
23
+ const showLoadingGlimmer = loading && loadingType === "glimmer";
18
24
  return (React.createElement(ErrorMessageWrapper, { message: getMessage({ invalid, error }) },
19
25
  React.createElement(View, { testID: "ATL-InputFieldWrapper", style: [
20
26
  styles.container,
@@ -37,9 +43,21 @@ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveTex
37
43
  React.createElement(Placeholder, { placeholder: placeholder, labelVariation: getLabelVariation(error, invalid, disabled), hasMiniLabel: hasMiniLabel, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.placeholderText })),
38
44
  (prefix === null || prefix === void 0 ? void 0 : prefix.label) && hasValue && (React.createElement(PrefixLabel, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, label: prefix.label, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.prefixLabel })),
39
45
  children,
40
- (showClearAction || (suffix === null || suffix === void 0 ? void 0 : suffix.label) || (suffix === null || suffix === void 0 ? void 0 : suffix.icon)) && (React.createElement(View, { style: styles.inputEndContainer },
46
+ showLoadingGlimmer && (React.createElement(View, { testID: INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID, style: [
47
+ styles.loadingGlimmers,
48
+ hasValue && styles.loadingGlimmersHasValue,
49
+ ] },
50
+ React.createElement(Glimmer, { size: "small", width: "80%" }),
51
+ React.createElement(Glimmer, { size: "small" }),
52
+ React.createElement(Glimmer, { size: "small", width: "70%" }))),
53
+ (showClearAction ||
54
+ (suffix === null || suffix === void 0 ? void 0 : suffix.label) ||
55
+ (suffix === null || suffix === void 0 ? void 0 : suffix.icon) ||
56
+ showLoadingSpinner) && (React.createElement(View, { style: styles.inputEndContainer },
41
57
  showClearAction && (React.createElement(ClearAction, { hasMarginRight: !!(suffix === null || suffix === void 0 ? void 0 : suffix.icon) || !!(suffix === null || suffix === void 0 ? void 0 : suffix.label), onPress: handleClear })),
42
58
  (suffix === null || suffix === void 0 ? void 0 : suffix.label) && hasValue && (React.createElement(SuffixLabel, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, label: suffix.label, hasLeftMargin: !showClearAction, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.suffixLabel })),
59
+ showLoadingSpinner && (React.createElement(View, { style: styles.loadingSpinner },
60
+ React.createElement(ActivityIndicator, { testID: INPUT_FIELD_WRAPPER_SPINNER_TEST_ID }))),
43
61
  (suffix === null || suffix === void 0 ? void 0 : suffix.icon) && (React.createElement(SuffixIcon, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, hasLeftMargin: !!(!showClearAction || (suffix === null || suffix === void 0 ? void 0 : suffix.label)), inputInvalid: inputInvalid, icon: suffix.icon, onPress: suffix.onPress })))))),
44
62
  isToolbarVisible && React.createElement(View, { style: styles.toolbar }, toolbar)),
45
63
  assistiveText && !error && !invalid && (React.createElement(Text, { level: "textSupporting", variation: disabled ? "disabled" : focused ? "interactive" : "subdued" }, assistiveText))));
@@ -89,8 +89,30 @@ export const styles = StyleSheet.create({
89
89
  zIndex: 1,
90
90
  },
91
91
  toolbar: {
92
+ flexBasis: "100%",
92
93
  flexDirection: "row",
93
94
  gap: tokens["space-small"],
94
95
  paddingBottom: tokens["space-small"],
95
96
  },
97
+ loadingSpinner: {
98
+ justifyContent: "center",
99
+ paddingRight: tokens["space-small"],
100
+ },
101
+ loadingGlimmers: {
102
+ position: "absolute",
103
+ top: tokens["space-base"],
104
+ bottom: tokens["space-base"],
105
+ left: 0,
106
+ right: 0,
107
+ gap: tokens["space-small"],
108
+ paddingTop: tokens["space-small"],
109
+ paddingRight: tokens["space-large"],
110
+ backgroundColor: tokens["color-surface"],
111
+ overflow: "hidden",
112
+ },
113
+ loadingGlimmersHasValue: {
114
+ top: tokens["space-large"],
115
+ paddingTop: tokens["space-base"] - tokens["space-smaller"],
116
+ bottom: tokens["space-smaller"],
117
+ },
96
118
  });
@@ -9,7 +9,7 @@ import { InputFieldWrapper } from "../InputFieldWrapper";
9
9
  import { commonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
10
10
  export const InputText = forwardRef(InputTextInternal);
11
11
  // eslint-disable-next-line max-statements
12
- function InputTextInternal({ invalid, disabled, readonly = false, name, placeholder, assistiveText, keyboard, value: controlledValue, defaultValue, autoFocus, autoComplete = "off", spellCheck, textContentType = "none", validations, onChangeText, onSubmitEditing, onFocus, accessibilityLabel, accessibilityHint, autoCorrect, autoCapitalize, onBlur, multiline = false, prefix, suffix, transform = {}, clearable = multiline ? "never" : "while-editing", testID, secureTextEntry, styleOverride, toolbar, toolbarVisibility, }, ref) {
12
+ function InputTextInternal({ invalid, disabled, readonly = false, name, placeholder, assistiveText, keyboard, value: controlledValue, defaultValue, autoFocus, autoComplete = "off", spellCheck, textContentType = "none", validations, onChangeText, onSubmitEditing, onFocus, accessibilityLabel, accessibilityHint, autoCorrect, autoCapitalize, onBlur, multiline = false, prefix, suffix, transform = {}, clearable = multiline ? "never" : "while-editing", testID, secureTextEntry, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }, ref) {
13
13
  var _a;
14
14
  const isAndroid = Platform.OS === "android";
15
15
  const isIOS = Platform.OS === "ios";
@@ -72,7 +72,7 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
72
72
  }
73
73
  onFocusNext();
74
74
  }
75
- return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, hasMiniLabel: hasMiniLabel, assistiveText: assistiveText, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, onClear: handleClear, showClearAction: showClear, styleOverride: styleOverride, toolbar: toolbar, toolbarVisibility: toolbarVisibility },
75
+ return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, hasMiniLabel: hasMiniLabel, assistiveText: assistiveText, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, onClear: handleClear, showClearAction: showClear, styleOverride: styleOverride, toolbar: toolbar, toolbarVisibility: toolbarVisibility, loading: loading, loadingType: loadingType },
76
76
  React.createElement(TextInput, Object.assign({ inputAccessoryViewID: inputAccessoryID || undefined, testID: testID, autoCapitalize: autoCapitalize, autoCorrect: autoCorrect, spellCheck: spellCheck, style: [
77
77
  commonInputStyles.input,
78
78
  styles.inputPaddingTop,
@@ -82,9 +82,10 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
82
82
  multiline && Platform.OS === "ios" && styles.multilineInputiOS,
83
83
  multiline && hasMiniLabel && styles.multiLineInputWithMini,
84
84
  styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.inputText,
85
+ loading && loadingType === "glimmer" && { color: "transparent" },
85
86
  ],
86
87
  // @ts-expect-error - does exist on 0.71 and up https://github.com/facebook/react-native/pull/39281
87
- readOnly: readonly, editable: !disabled, keyboardType: keyboard, value: inputTransform(internalValue), autoFocus: autoFocus, autoComplete: autoComplete, multiline: multiline, scrollEnabled: false, textContentType: textContentType, onChangeText: handleChangeText, onSubmitEditing: handleOnSubmitEditing, returnKeyType: returnKeyType, blurOnSubmit: shouldBlurOnSubmit, accessibilityLabel: accessibilityLabel || placeholder, accessibilityHint: accessibilityHint, secureTextEntry: secureTextEntry }, androidA11yProps, { onFocus: event => {
88
+ readOnly: readonly, editable: !disabled, keyboardType: keyboard, value: inputTransform(internalValue), autoFocus: autoFocus, autoComplete: autoComplete, multiline: multiline, scrollEnabled: false, textContentType: textContentType, onChangeText: handleChangeText, onSubmitEditing: handleOnSubmitEditing, returnKeyType: returnKeyType, blurOnSubmit: shouldBlurOnSubmit, accessibilityLabel: accessibilityLabel || placeholder, accessibilityHint: accessibilityHint, accessibilityState: { busy: loading }, secureTextEntry: secureTextEntry }, androidA11yProps, { onFocus: event => {
88
89
  _name && setFocusedInput(_name);
89
90
  setFocused(true);
90
91
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
package/dist/src/index.js CHANGED
@@ -17,29 +17,30 @@ export * from "./Divider";
17
17
  export * from "./EmptyState";
18
18
  export * from "./ErrorMessageWrapper";
19
19
  export * from "./Flex";
20
- export * from "./FormatFile";
21
20
  export * from "./Form";
21
+ export * from "./FormatFile";
22
22
  export * from "./FormField";
23
+ export * from "./Glimmer";
23
24
  export * from "./Heading";
24
25
  export * from "./Icon";
25
26
  export * from "./IconButton";
26
- export * from "./InputFieldWrapper";
27
27
  export * from "./InputCurrency";
28
28
  export * from "./InputDate";
29
29
  export * from "./InputEmail";
30
+ export * from "./InputFieldWrapper";
30
31
  export * from "./InputNumber";
31
32
  export * from "./InputPassword";
32
33
  export * from "./InputPressable";
33
34
  export * from "./InputSearch";
34
- export * from "./InputTime";
35
35
  export * from "./InputText";
36
+ export * from "./InputTime";
36
37
  export * from "./Menu";
37
- export * from "./TextList";
38
- export * from "./ThumbnailList";
39
38
  export * from "./ProgressBar";
40
39
  export * from "./Select";
41
40
  export * from "./StatusLabel";
42
41
  export * from "./Switch";
43
42
  export * from "./Text";
43
+ export * from "./TextList";
44
+ export * from "./ThumbnailList";
44
45
  export * from "./Toast";
45
46
  export * from "./Typography";