@jobber/components-native 0.90.1-JOB-142149-547612b.8 → 0.91.1

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 (44) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/Form/Form.js +1 -1
  3. package/dist/src/Form/components/FormBody/FormBody.js +5 -5
  4. package/dist/src/FormatFile/components/MediaView/MediaView.js +22 -5
  5. package/dist/src/InputDate/InputDate.js +2 -2
  6. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
  7. package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
  8. package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
  9. package/dist/src/InputPressable/InputPressable.js +20 -8
  10. package/dist/src/InputPressable/InputPressable.style.js +3 -0
  11. package/dist/src/InputText/InputText.js +22 -11
  12. package/dist/src/InputText/InputText.style.js +4 -0
  13. package/dist/src/InputTime/InputTime.js +2 -2
  14. package/dist/tsconfig.build.tsbuildinfo +1 -1
  15. package/dist/types/src/InputDate/InputDate.d.ts +2 -1
  16. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
  17. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +2 -3
  18. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +2 -3
  19. package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
  20. package/dist/types/src/InputPressable/InputPressable.style.d.ts +3 -0
  21. package/dist/types/src/InputSearch/InputSearch.d.ts +1 -1
  22. package/dist/types/src/InputText/InputText.d.ts +8 -0
  23. package/dist/types/src/InputText/InputText.style.d.ts +4 -0
  24. package/dist/types/src/InputTime/InputTime.d.ts +2 -1
  25. package/package.json +2 -2
  26. package/src/Form/Form.tsx +1 -0
  27. package/src/Form/components/FormBody/FormBody.tsx +6 -6
  28. package/src/FormatFile/components/MediaView/MediaView.test.tsx +283 -0
  29. package/src/FormatFile/components/MediaView/MediaView.tsx +27 -6
  30. package/src/InputDate/InputDate.tsx +5 -1
  31. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
  32. package/src/InputFieldWrapper/InputFieldWrapper.tsx +38 -28
  33. package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +3 -5
  34. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +6 -4
  35. package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
  36. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +6 -4
  37. package/src/InputPressable/InputPressable.style.ts +4 -0
  38. package/src/InputPressable/InputPressable.test.tsx +75 -1
  39. package/src/InputPressable/InputPressable.tsx +33 -7
  40. package/src/InputSearch/InputSearch.tsx +1 -0
  41. package/src/InputText/InputText.style.ts +5 -0
  42. package/src/InputText/InputText.test.tsx +75 -0
  43. package/src/InputText/InputText.tsx +32 -12
  44. package/src/InputTime/InputTime.tsx +5 -1
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.90.1-JOB-142149-547612b.8+547612b28",
3
+ "version": "0.91.1",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -94,5 +94,5 @@
94
94
  "react-native-safe-area-context": "^5.4.0",
95
95
  "react-native-svg": ">=12.0.0"
96
96
  },
97
- "gitHead": "547612b28b6ea97cd710210ecbbe6917da0c9734"
97
+ "gitHead": "5f68cfee448c36f14f589edd6359c9cb30c8ab79"
98
98
  }
@@ -110,7 +110,7 @@ function InternalForm({ children, onBeforeSubmit, onSubmit, onSubmitError, onSub
110
110
  (isSubmitting || isSecondaryActionLoading) && React.createElement(FormMask, null),
111
111
  React.createElement(FormCache, { localCacheKey: localCacheKey, localCacheExclude: localCacheExclude, setLocalCache: setLocalCache }),
112
112
  React.createElement(FormBody, { keyboardHeight: calculateSaveButtonOffset(), submit: handleSubmit(internalSubmit), isFormSubmitting: isSubmitting, saveButtonLabel: saveButtonLabel, shouldRenderActionBar: saveButtonPosition === "sticky", renderStickySection: renderStickySection, secondaryActions: secondaryActions, setSecondaryActionLoading: setIsSecondaryActionLoading, setSaveButtonHeight: setSaveButtonHeight, saveButtonOffset: saveButtonOffset },
113
- React.createElement(KeyboardAwareScrollView, Object.assign({ enableResetScrollToCoords: false, enableAutomaticScroll: true, enableOnAndroid: edgeToEdgeEnabled, keyboardOpeningTime: Platform.OS === "ios" ? tokens["timing-slowest"] : 0, keyboardShouldPersistTaps: "handled", ref: scrollViewRef }, keyboardProps, { extraHeight: headerHeight, contentContainerStyle: !keyboardHeight && styles.scrollContentContainer }),
113
+ React.createElement(KeyboardAwareScrollView, Object.assign({ enableResetScrollToCoords: false, enableAutomaticScroll: true, enableOnAndroid: edgeToEdgeEnabled, keyboardOpeningTime: Platform.OS === "ios" ? tokens["timing-slowest"] : 0, keyboardShouldPersistTaps: "handled", ref: scrollViewRef }, keyboardProps, { extraHeight: headerHeight, extraScrollHeight: edgeToEdgeEnabled ? tokens["space-large"] : 0, contentContainerStyle: !keyboardHeight && styles.scrollContentContainer }),
114
114
  React.createElement(View, { onLayout: ({ nativeEvent }) => {
115
115
  setFormContentHeight(nativeEvent.layout.height);
116
116
  } },
@@ -1,18 +1,18 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View } from "react-native";
3
- import { useSafeAreaInsets } from "react-native-safe-area-context";
4
3
  import { useStyles } from "./FormBody.style";
5
4
  import { useScreenInformation } from "../../hooks/useScreenInformation";
6
5
  import { FormActionBar } from "../FormActionBar";
7
6
  import { tokens } from "../../../utils/design";
8
7
  export function FormBody({ isFormSubmitting, submit, keyboardHeight, children, saveButtonLabel, renderStickySection, shouldRenderActionBar = true, secondaryActions, setSecondaryActionLoading, setSaveButtonHeight, saveButtonOffset, }) {
9
- const { bottom: paddingBottom } = useSafeAreaInsets();
8
+ const paddingBottom = useBottomPadding();
9
+ const fullViewPadding = useMemo(() => ({ paddingBottom }), [paddingBottom]);
10
10
  const styles = useStyles();
11
11
  return (React.createElement(React.Fragment, null,
12
- React.createElement(View, { style: styles.container },
12
+ React.createElement(View, { style: [styles.container] },
13
13
  children,
14
14
  shouldRenderActionBar && (React.createElement(FormActionBar, { setSecondaryActionLoading: setSecondaryActionLoading, keyboardHeight: keyboardHeight, submit: submit, isFormSubmitting: isFormSubmitting, saveButtonLabel: saveButtonLabel, renderStickySection: renderStickySection, secondaryActions: secondaryActions, setSaveButtonHeight: setSaveButtonHeight }))),
15
- !saveButtonOffset && (React.createElement(View, { style: [{ paddingBottom }, styles.safeArea], testID: "ATL-FormSafeArea" }))));
15
+ shouldRenderActionBar && !saveButtonOffset && (React.createElement(View, { style: [fullViewPadding, styles.safeArea], testID: "ATL-FormSafeArea" }))));
16
16
  }
17
17
  export function useBottomPadding() {
18
18
  const { insets } = useScreenInformation();
@@ -1,4 +1,4 @@
1
- import React, { useState } from "react";
1
+ import React, { useEffect, useRef, useState } from "react";
2
2
  import { ImageBackground, View } from "react-native";
3
3
  import { useStyles } from "./MediaView.style";
4
4
  import { StatusCode } from "../../types";
@@ -14,20 +14,37 @@ export function MediaView({ accessibilityLabel, showOverlay, showError, file, st
14
14
  const { useCreateThumbnail } = useAtlantisFormatFileContext();
15
15
  const { thumbnail, error } = useCreateThumbnail(file);
16
16
  const [isLoading, setIsLoading] = useState(false);
17
+ /**
18
+ * Tracks whether onLoadEnd has fired to prevent race conditions.
19
+ * ImageBackground can fire onLoadEnd before onLoadStart when loading cached images,
20
+ * which would cause isLoading to get stuck at true, showing an infinite spinner.
21
+ */
22
+ const hasLoadedRef = useRef(false);
17
23
  const a11yLabel = computeA11yLabel({
18
24
  accessibilityLabel,
19
25
  showOverlay,
20
26
  showError,
21
27
  t,
22
28
  });
23
- const hasError = showError || error;
24
- const uri = thumbnail || file.thumbnailUrl || file.source;
25
- const styles = useStyles();
29
+ const hasError = showError || error, uri = thumbnail || file.thumbnailUrl || file.source, styles = useStyles();
30
+ const handleLoadStart = () => {
31
+ if (!hasLoadedRef.current) {
32
+ setIsLoading(true);
33
+ }
34
+ };
35
+ const handleLoadEnd = () => {
36
+ hasLoadedRef.current = true;
37
+ setIsLoading(false);
38
+ };
39
+ useEffect(() => {
40
+ hasLoadedRef.current = false;
41
+ setIsLoading(false);
42
+ }, [uri]);
26
43
  return (React.createElement(View, { accessible: true, accessibilityLabel: a11yLabel },
27
44
  React.createElement(ImageBackground, { style: [
28
45
  styles.imageBackground,
29
46
  styleInGrid ? styles.imageBackgroundGrid : styles.imageBackgroundFlat,
30
- ], resizeMode: styleInGrid ? "cover" : "contain", source: { uri }, testID: "test-image", onLoadStart: () => setIsLoading(true), onLoadEnd: () => setIsLoading(false) },
47
+ ], resizeMode: styleInGrid ? "cover" : "contain", source: { uri }, testID: "test-image", onLoadStart: handleLoadStart, onLoadEnd: handleLoadEnd },
31
48
  React.createElement(Overlay, { isLoading: isLoading, showOverlay: showOverlay, hasError: hasError, file: file, onUploadComplete: onUploadComplete, styles: styles }))));
32
49
  }
33
50
  function Overlay({ isLoading, showOverlay, hasError, file, onUploadComplete, styles, }) {
@@ -27,7 +27,7 @@ export function InputDate(props) {
27
27
  }
28
28
  return React.createElement(InternalInputDate, Object.assign({}, props));
29
29
  }
30
- function InternalInputDate({ clearable = "always", disabled, emptyValueLabel, invalid, maxDate, minDate, placeholder, value, name, onChange, accessibilityLabel, accessibilityHint, }) {
30
+ function InternalInputDate({ clearable = "always", disabled, emptyValueLabel, invalid, maxDate, minDate, placeholder, value, showMiniLabel = true, name, onChange, accessibilityLabel, accessibilityHint, }) {
31
31
  const [showPicker, setShowPicker] = useState(false);
32
32
  const { t, locale, formatDate } = useAtlantisI18n();
33
33
  const date = useMemo(() => {
@@ -44,7 +44,7 @@ function InternalInputDate({ clearable = "always", disabled, emptyValueLabel, in
44
44
  const canClearDate = formattedDate === emptyValueLabel ? "never" : clearable;
45
45
  const placeholderLabel = placeholder !== null && placeholder !== void 0 ? placeholder : t("date");
46
46
  return (React.createElement(React.Fragment, null,
47
- React.createElement(InputPressable, { focused: showPicker, clearable: canClearDate, disabled: disabled, invalid: invalid, placeholder: placeholderLabel, prefix: { icon: "calendar" }, value: formattedDate, onClear: handleClear, onPress: showDatePicker, accessibilityLabel: accessibilityLabel, accessibilityHint: accessibilityHint }),
47
+ React.createElement(InputPressable, { showMiniLabel: showMiniLabel, focused: showPicker, clearable: canClearDate, disabled: disabled, invalid: invalid, placeholder: placeholderLabel, prefix: { icon: "calendar" }, value: formattedDate, onClear: handleClear, onPress: showDatePicker, accessibilityLabel: accessibilityLabel, accessibilityHint: accessibilityHint }),
48
48
  React.createElement(DateTimePicker, { testID: "inputDate-datePicker", date: date || undefined, display: display, isVisible: showPicker, maximumDate: maxDate, minimumDate: minDate, mode: "date", confirmTextIOS: t("confirm"), cancelTextIOS: t("cancel"), locale: locale, onCancel: handleCancel, onConfirm: handleConfirm })));
49
49
  function showDatePicker() {
50
50
  Keyboard.dismiss();
@@ -11,7 +11,7 @@ import { Text } from "../Text";
11
11
  import { ActivityIndicator } from "../ActivityIndicator";
12
12
  export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
13
13
  export const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
14
- 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", }) {
14
+ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode = "normal", hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, toolbar, toolbarVisibility = "while-editing", loading = false, loadingType = "spinner", }) {
15
15
  fieldAffixRequiredPropsCheck([prefix, suffix]);
16
16
  const handleClear = onClear !== null && onClear !== void 0 ? onClear : noopClear;
17
17
  warnIfClearActionWithNoOnClear(onClear, showClearAction);
@@ -20,6 +20,8 @@ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveTex
20
20
  const showLoadingSpinner = loading && loadingType === "spinner";
21
21
  const showLoadingGlimmer = loading && loadingType === "glimmer";
22
22
  const styles = useStyles();
23
+ const placeholderVisible = placeholderMode !== "hidden";
24
+ const miniLabelActive = placeholderMode === "mini";
23
25
  return (React.createElement(ErrorMessageWrapper, { message: getMessage({ invalid, error }) },
24
26
  React.createElement(View, { testID: "ATL-InputFieldWrapper", style: [
25
27
  styles.container,
@@ -29,18 +31,18 @@ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveTex
29
31
  styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.container,
30
32
  ] },
31
33
  React.createElement(View, { style: styles.field },
32
- (prefix === null || prefix === void 0 ? void 0 : prefix.icon) && (React.createElement(PrefixIcon, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, icon: prefix.icon })),
34
+ (prefix === null || prefix === void 0 ? void 0 : prefix.icon) && (React.createElement(PrefixIcon, { disabled: disabled, focused: focused, inputInvalid: inputInvalid, icon: prefix.icon })),
33
35
  React.createElement(View, { style: [styles.inputContainer] },
34
- React.createElement(View, { style: [
36
+ placeholderVisible && (React.createElement(View, { style: [
35
37
  !!placeholder && styles.label,
36
- hasMiniLabel && styles.miniLabel,
38
+ miniLabelActive && styles.miniLabel,
37
39
  disabled && styles.disabled,
38
- hasMiniLabel &&
40
+ miniLabelActive &&
39
41
  showClearAction &&
40
42
  styles.miniLabelShowClearAction,
41
43
  ], pointerEvents: "none" },
42
- React.createElement(Placeholder, { placeholder: placeholder, labelVariation: getLabelVariation(error, invalid, disabled), hasMiniLabel: hasMiniLabel, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.placeholderText })),
43
- (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 })),
44
+ React.createElement(Placeholder, { placeholder: placeholder, labelVariation: getLabelVariation(error, invalid, disabled), miniLabelActive: miniLabelActive, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.placeholderText }))),
45
+ (prefix === null || prefix === void 0 ? void 0 : prefix.label) && hasValue && (React.createElement(PrefixLabel, { disabled: disabled, focused: focused, miniLabelActive: miniLabelActive, inputInvalid: inputInvalid, label: prefix.label, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.prefixLabel })),
44
46
  children,
45
47
  showLoadingGlimmer && (React.createElement(View, { testID: INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID, style: [
46
48
  styles.loadingGlimmers,
@@ -54,10 +56,10 @@ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveTex
54
56
  (suffix === null || suffix === void 0 ? void 0 : suffix.icon) ||
55
57
  showLoadingSpinner) && (React.createElement(View, { style: styles.inputEndContainer },
56
58
  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 })),
57
- (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
+ (suffix === null || suffix === void 0 ? void 0 : suffix.label) && hasValue && (React.createElement(SuffixLabel, { disabled: disabled, focused: focused, miniLabelActive: miniLabelActive, inputInvalid: inputInvalid, label: suffix.label, hasLeftMargin: !showClearAction, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.suffixLabel })),
58
60
  showLoadingSpinner && (React.createElement(View, { style: styles.loadingSpinner },
59
61
  React.createElement(ActivityIndicator, { testID: INPUT_FIELD_WRAPPER_SPINNER_TEST_ID }))),
60
- (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 })))))),
62
+ (suffix === null || suffix === void 0 ? void 0 : suffix.icon) && (React.createElement(SuffixIcon, { disabled: disabled, focused: focused, hasLeftMargin: !!(!showClearAction || (suffix === null || suffix === void 0 ? void 0 : suffix.label)), inputInvalid: inputInvalid, icon: suffix.icon, onPress: suffix.onPress })))))),
61
63
  isToolbarVisible && React.createElement(View, { style: styles.toolbar }, toolbar)),
62
64
  assistiveText && !error && !invalid && (React.createElement(Text, { level: "textSupporting", variation: disabled ? "disabled" : focused ? "interactive" : "subdued" }, assistiveText))));
63
65
  }
@@ -93,12 +95,12 @@ function getMessage({ invalid, error, }) {
93
95
  messages.push(invalid);
94
96
  return messages.join("\n");
95
97
  }
96
- function Placeholder({ placeholder, styleOverride, labelVariation, hasMiniLabel, }) {
98
+ function Placeholder({ placeholder, styleOverride, labelVariation, miniLabelActive, }) {
97
99
  const typographyStyles = useTypographyStyles();
98
- return (React.createElement(React.Fragment, null, !styleOverride ? (React.createElement(Text, { hideFromScreenReader: true, maxLines: "single", variation: labelVariation, level: hasMiniLabel ? "textSupporting" : "text" }, placeholder)) : (React.createElement(RNText, { accessibilityRole: "none", accessible: false, importantForAccessibility: "no-hide-descendants", numberOfLines: 1, style: [
100
+ return (React.createElement(React.Fragment, null, !styleOverride ? (React.createElement(Text, { hideFromScreenReader: true, maxLines: "single", variation: labelVariation, level: miniLabelActive ? "textSupporting" : "text" }, placeholder)) : (React.createElement(RNText, { accessibilityRole: "none", accessible: false, importantForAccessibility: "no-hide-descendants", numberOfLines: 1, style: [
99
101
  typographyStyles[labelVariation],
100
102
  typographyStyles.baseRegularRegular,
101
- hasMiniLabel
103
+ miniLabelActive
102
104
  ? typographyStyles.smallSize
103
105
  : typographyStyles.defaultSize,
104
106
  styleOverride,
@@ -7,7 +7,7 @@ import { useTypographyStyles } from "../../../Typography";
7
7
  import { useStyles } from "../../InputFieldWrapper.style";
8
8
  export const prefixLabelTestId = "ATL-InputFieldWrapper-PrefixLabel";
9
9
  export const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
10
- export function PrefixLabel({ focused, disabled, hasMiniLabel, inputInvalid, label, styleOverride, }) {
10
+ export function PrefixLabel({ focused, disabled, miniLabelActive, inputInvalid, label, styleOverride, }) {
11
11
  const styles = useStyles();
12
12
  const typographyStyles = useTypographyStyles();
13
13
  return (React.createElement(View, { style: [
@@ -15,7 +15,10 @@ export function PrefixLabel({ focused, disabled, hasMiniLabel, inputInvalid, lab
15
15
  focused && styles.inputFocused,
16
16
  inputInvalid && styles.inputInvalid,
17
17
  ], testID: prefixLabelTestId },
18
- React.createElement(View, { style: [styles.prefixLabel, hasMiniLabel && styles.fieldAffixMiniLabel] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
18
+ React.createElement(View, { style: [
19
+ styles.prefixLabel,
20
+ miniLabelActive && styles.fieldAffixMiniLabel,
21
+ ] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
19
22
  typographyStyles.baseRegularRegular,
20
23
  typographyStyles.base,
21
24
  typographyStyles.defaultSize,
@@ -7,7 +7,7 @@ import { useAtlantisTheme } from "../../../AtlantisThemeContext";
7
7
  import { useStyles } from "../../InputFieldWrapper.style";
8
8
  export const suffixLabelTestId = "ATL-InputFieldWrapper-SuffixLabel";
9
9
  export const suffixIconTestId = "ATL-InputFieldWrapper-SuffixIcon";
10
- export function SuffixLabel({ focused, disabled, hasMiniLabel, inputInvalid, label, hasLeftMargin = true, styleOverride, }) {
10
+ export function SuffixLabel({ focused, disabled, miniLabelActive, inputInvalid, label, hasLeftMargin = true, styleOverride, }) {
11
11
  const styles = useStyles();
12
12
  const typographyStyles = useTypographyStyles();
13
13
  return (React.createElement(View, { testID: suffixLabelTestId, style: [
@@ -16,7 +16,10 @@ export function SuffixLabel({ focused, disabled, hasMiniLabel, inputInvalid, lab
16
16
  inputInvalid && styles.inputInvalid,
17
17
  hasLeftMargin && styles.suffixLabelMargin,
18
18
  ] },
19
- React.createElement(View, { style: [styles.suffixLabel, hasMiniLabel && styles.fieldAffixMiniLabel] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
19
+ React.createElement(View, { style: [
20
+ styles.suffixLabel,
21
+ miniLabelActive && styles.fieldAffixMiniLabel,
22
+ ] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
20
23
  typographyStyles.baseRegularRegular,
21
24
  typographyStyles.base,
22
25
  typographyStyles.defaultSize,
@@ -1,15 +1,12 @@
1
- import React, { forwardRef, useEffect, useState } from "react";
1
+ import React, { forwardRef } from "react";
2
2
  import { Text as NativeText, Pressable } from "react-native";
3
3
  import { useShowClear } from "@jobber/hooks";
4
4
  import { useStyles } from "./InputPressable.style";
5
5
  import { InputFieldWrapper, useCommonInputStyles } from "../InputFieldWrapper";
6
6
  export const InputPressable = forwardRef(InputPressableInternal);
7
- export function InputPressableInternal({ value, placeholder, disabled, invalid, error, onPress, accessibilityLabel, accessibilityHint, prefix, suffix, clearable = "never", onClear, focused, }, ref) {
7
+ export function InputPressableInternal({ value, placeholder, disabled, invalid, error, showMiniLabel = true, onPress, accessibilityLabel, accessibilityHint, prefix, suffix, clearable = "never", onClear, focused, }, ref) {
8
8
  const hasValue = !!value;
9
- const [hasMiniLabel, setHasMiniLabel] = useState(Boolean(value));
10
- useEffect(() => {
11
- setHasMiniLabel(Boolean(value));
12
- }, [value]);
9
+ const placeholderMode = getPlaceholderMode(showMiniLabel, value);
13
10
  const showClear = useShowClear({
14
11
  clearable,
15
12
  multiline: false,
@@ -19,13 +16,28 @@ export function InputPressableInternal({ value, placeholder, disabled, invalid,
19
16
  });
20
17
  const styles = useStyles();
21
18
  const commonInputStyles = useCommonInputStyles();
22
- return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, hasMiniLabel: hasMiniLabel, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, showClearAction: showClear, onClear: onClear },
19
+ return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, placeholderMode: placeholderMode, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, showClearAction: showClear, onClear: onClear },
23
20
  React.createElement(Pressable, { style: styles.pressable, onPress: onPress, disabled: disabled, accessibilityLabel: accessibilityLabel || placeholder, accessibilityHint: accessibilityHint, accessibilityValue: { text: value } },
24
21
  React.createElement(NativeText, { style: [
25
22
  commonInputStyles.input,
26
23
  styles.inputPressableStyles,
27
- !hasMiniLabel && commonInputStyles.inputEmpty,
24
+ placeholderMode === "normal" && commonInputStyles.inputEmpty,
25
+ placeholderMode === "hidden" && styles.withoutMiniLabel,
28
26
  disabled && commonInputStyles.inputDisabled,
29
27
  (Boolean(invalid) || error) && styles.inputInvalid,
30
28
  ], testID: "inputPressableText", ref: ref, accessibilityRole: "none", accessible: false, importantForAccessibility: "no-hide-descendants" }, value))));
31
29
  }
30
+ function getPlaceholderMode(isMiniLabelAllowed, internalValue) {
31
+ const hasValue = Boolean(internalValue);
32
+ if (hasValue) {
33
+ if (isMiniLabelAllowed) {
34
+ return "mini";
35
+ }
36
+ else {
37
+ return "hidden";
38
+ }
39
+ }
40
+ else {
41
+ return "normal";
42
+ }
43
+ }
@@ -21,5 +21,8 @@ export const useStyles = buildThemedStyles(tokens => {
21
21
  inputInvalid: {
22
22
  borderColor: tokens["color-critical"],
23
23
  },
24
+ withoutMiniLabel: {
25
+ paddingTop: tokens["space-base"] + tokens["space-smallest"],
26
+ },
24
27
  };
25
28
  });
@@ -9,7 +9,7 @@ import { InputFieldWrapper } from "../InputFieldWrapper";
9
9
  import { useCommonInputStyles } 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, loading, loadingType, }, ref) {
12
+ function InputTextInternal({ invalid, disabled, readonly = false, name, placeholder, assistiveText, showMiniLabel = true, 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";
@@ -22,7 +22,8 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
22
22
  const internalValue = controlledValue !== null && controlledValue !== void 0 ? controlledValue : (_a = field.value) === null || _a === void 0 ? void 0 : _a.toString();
23
23
  const hasValue = internalValue !== "" && internalValue !== undefined;
24
24
  const [focused, setFocused] = useState(false);
25
- const { hasMiniLabel } = useMiniLabel(internalValue);
25
+ const placeholderMode = getPlaceholderMode(showMiniLabel, internalValue);
26
+ const miniLabelActive = placeholderMode === "mini";
26
27
  const textInputRef = useTextInputRef({ ref, onClear: handleClear });
27
28
  const showClear = useShowClear({
28
29
  clearable,
@@ -74,15 +75,18 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
74
75
  }
75
76
  const styles = useStyles();
76
77
  const commonInputStyles = useCommonInputStyles();
77
- 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 },
78
+ return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, placeholderMode: placeholderMode, 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 },
78
79
  React.createElement(TextInput, Object.assign({ inputAccessoryViewID: inputAccessoryID || undefined, testID: testID, autoCapitalize: autoCapitalize, autoCorrect: autoCorrect, spellCheck: spellCheck, style: [
79
80
  commonInputStyles.input,
80
81
  styles.inputPaddingTop,
81
- !hasMiniLabel && commonInputStyles.inputEmpty,
82
+ !miniLabelActive && commonInputStyles.inputEmpty,
82
83
  disabled && commonInputStyles.inputDisabled,
83
84
  multiline && styles.multiLineInput,
84
85
  multiline && Platform.OS === "ios" && styles.multilineInputiOS,
85
- multiline && hasMiniLabel && styles.multiLineInputWithMini,
86
+ multiline && miniLabelActive && styles.multiLineInputWithMini,
87
+ multiline &&
88
+ placeholderMode === "hidden" &&
89
+ styles.multilineWithoutMiniLabel,
86
90
  styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.inputText,
87
91
  loading && loadingType === "glimmer" && { color: "transparent" },
88
92
  ], 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 => {
@@ -149,10 +153,17 @@ function useTextInputRef({ ref, onClear }) {
149
153
  }), [onClear]);
150
154
  return textInputRef;
151
155
  }
152
- function useMiniLabel(internalValue) {
153
- const [hasMiniLabel, setHasMiniLabel] = useState(Boolean(internalValue));
154
- useEffect(() => {
155
- setHasMiniLabel(Boolean(internalValue));
156
- }, [internalValue]);
157
- return { hasMiniLabel };
156
+ function getPlaceholderMode(isMiniLabelAllowed, internalValue) {
157
+ const hasValue = Boolean(internalValue);
158
+ if (hasValue) {
159
+ if (isMiniLabelAllowed) {
160
+ return "mini";
161
+ }
162
+ else {
163
+ return "hidden";
164
+ }
165
+ }
166
+ else {
167
+ return "normal";
168
+ }
158
169
  }
@@ -20,5 +20,9 @@ export const useStyles = buildThemedStyles(tokens => {
20
20
  multilineInputiOS: {
21
21
  paddingTop: (typographyStyles.defaultSize.fontSize || 0) + tokens["space-smallest"],
22
22
  },
23
+ multilineWithoutMiniLabel: {
24
+ paddingTop: tokens["space-base"] + tokens["space-smallest"],
25
+ paddingBottom: tokens["space-base"] + tokens["space-smallest"],
26
+ },
23
27
  };
24
28
  });
@@ -28,7 +28,7 @@ export function InputTime(props) {
28
28
  }
29
29
  return React.createElement(InternalInputTime, Object.assign({}, props));
30
30
  }
31
- function InternalInputTime({ clearable = "always", disabled, emptyValueLabel, invalid, placeholder, value, name, type = "scheduling", onChange, showIcon = true, }) {
31
+ function InternalInputTime({ clearable = "always", disabled, emptyValueLabel, invalid, placeholder, value, name, type = "scheduling", showMiniLabel = true, onChange, showIcon = true, }) {
32
32
  const [showPicker, setShowPicker] = useState(false);
33
33
  const { t, formatTime } = useAtlantisI18n();
34
34
  const { timeZone, timeFormat } = useAtlantisContext();
@@ -42,7 +42,7 @@ function InternalInputTime({ clearable = "always", disabled, emptyValueLabel, in
42
42
  }, [dateTime, emptyValueLabel]);
43
43
  const canClearTime = formattedTime === emptyValueLabel ? "never" : clearable;
44
44
  return (React.createElement(View, { style: styles.container },
45
- React.createElement(InputPressable, { clearable: canClearTime, disabled: disabled, invalid: invalid, focused: showPicker, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("time"), prefix: showIcon ? { icon: "timer" } : undefined, value: formattedTime, onClear: handleClear, onPress: showDatePicker }),
45
+ React.createElement(InputPressable, { showMiniLabel: showMiniLabel, clearable: canClearTime, disabled: disabled, invalid: invalid, focused: showPicker, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("time"), prefix: showIcon ? { icon: "timer" } : undefined, value: formattedTime, onClear: handleClear, onPress: showDatePicker }),
46
46
  React.createElement(DateTimePicker, { testID: "inputTime-Picker", minuteInterval: getMinuteInterval(type), date: getInitialPickerDate(dateTime), timeZoneOffsetInMinutes: getTimeZoneOffsetInMinutes(timeZone, dateTime), isVisible: showPicker, mode: "time", onCancel: handleCancel, onConfirm: handleConfirm, is24Hour: is24Hour, locale: is24Hour ? LOCALE_24_HOURS : LOCALE_12_HOURS })));
47
47
  function showDatePicker() {
48
48
  Keyboard.dismiss();