@jobber/components-native 0.26.0 → 0.27.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/src/ButtonGroup/ButtonGroup.js +29 -0
- package/dist/src/ButtonGroup/ButtonGroup.style.js +18 -0
- package/dist/src/ButtonGroup/ButtonGroupAction.js +10 -0
- package/dist/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.js +17 -0
- package/dist/src/ButtonGroup/components/SecondaryActionSheet/index.js +1 -0
- package/dist/src/ButtonGroup/index.js +2 -0
- package/dist/src/ButtonGroup/messages.js +18 -0
- package/dist/src/ButtonGroup/types.js +1 -0
- package/dist/src/ButtonGroup/utils.js +42 -0
- package/dist/src/index.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/src/ButtonGroup/ButtonGroup.d.ts +30 -0
- package/dist/types/src/ButtonGroup/ButtonGroup.style.d.ts +16 -0
- package/dist/types/src/ButtonGroup/ButtonGroupAction.d.ts +47 -0
- package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.d.ts +13 -0
- package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/index.d.ts +1 -0
- package/dist/types/src/ButtonGroup/index.d.ts +3 -0
- package/dist/types/src/ButtonGroup/messages.d.ts +17 -0
- package/dist/types/src/ButtonGroup/types.d.ts +7 -0
- package/dist/types/src/ButtonGroup/utils.d.ts +19 -0
- package/dist/types/src/index.d.ts +1 -0
- package/package.json +2 -2
- package/src/ButtonGroup/ButtonGroup.style.ts +19 -0
- package/src/ButtonGroup/ButtonGroup.test.tsx +325 -0
- package/src/ButtonGroup/ButtonGroup.tsx +113 -0
- package/src/ButtonGroup/ButtonGroupAction.tsx +66 -0
- package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +55 -0
- package/src/ButtonGroup/components/SecondaryActionSheet/index.ts +1 -0
- package/src/ButtonGroup/index.ts +8 -0
- package/src/ButtonGroup/messages.ts +19 -0
- package/src/ButtonGroup/types.ts +30 -0
- package/src/ButtonGroup/utils.ts +86 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { IconColorNames, IconNames } from "@jobber/design";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ButtonType, ButtonVariation } from "../Button";
|
|
4
|
+
|
|
5
|
+
export interface ButtonGroupActionProps {
|
|
6
|
+
/**
|
|
7
|
+
* Text to be displayed on the action button
|
|
8
|
+
*/
|
|
9
|
+
label: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Icon to be displayed on the action button
|
|
13
|
+
*/
|
|
14
|
+
icon?: IconNames;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Determines the color of the icon. If not specified, some icons have a default system colour which will be used
|
|
18
|
+
* Others that don't have a system colour fall back to greyBlue.
|
|
19
|
+
* Only applies the iconColor to ButtonGroup.PrimaryAction when it is rendered under the "more" menu.
|
|
20
|
+
*/
|
|
21
|
+
iconColor?: IconColorNames;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Press handler for the action button
|
|
25
|
+
*/
|
|
26
|
+
onPress: () => void;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ButtonGroupSecondaryActionProps
|
|
31
|
+
extends ButtonGroupActionProps {
|
|
32
|
+
/**
|
|
33
|
+
* Indicates whether the secondary action is destructive in nature.
|
|
34
|
+
*/
|
|
35
|
+
destructive?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ButtonGroupPrimaryActionProps extends ButtonGroupActionProps {
|
|
39
|
+
/**
|
|
40
|
+
* Sets the action button style (default: "primary")
|
|
41
|
+
*/
|
|
42
|
+
buttonType?: ButtonType;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Themes the action button to the type of action it performs (default: "work")
|
|
46
|
+
*/
|
|
47
|
+
buttonVariation?: ButtonVariation;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional custom button that can be rendered in place of the primary action button
|
|
51
|
+
*/
|
|
52
|
+
customButton?: JSX.Element;
|
|
53
|
+
loading?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
57
|
+
export function PrimaryAction(_: ButtonGroupPrimaryActionProps): JSX.Element {
|
|
58
|
+
return <></>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function SecondaryAction(
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
|
+
_: ButtonGroupSecondaryActionProps,
|
|
64
|
+
): JSX.Element {
|
|
65
|
+
return <></>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { RefObject } from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { Portal } from "react-native-portalize";
|
|
4
|
+
import { ButtonGroupSecondaryActionProps } from "../../types";
|
|
5
|
+
import { BottomSheetOption } from "../../../BottomSheet/components/BottomSheetOption";
|
|
6
|
+
import { BottomSheet, BottomSheetRef } from "../../../BottomSheet/BottomSheet";
|
|
7
|
+
|
|
8
|
+
interface SecondaryActionSheetProps {
|
|
9
|
+
actions: ButtonGroupSecondaryActionProps[];
|
|
10
|
+
secondaryActionsRef: RefObject<BottomSheetRef>;
|
|
11
|
+
showCancel?: boolean;
|
|
12
|
+
heading?: string;
|
|
13
|
+
onOpenBottomSheet?: () => void;
|
|
14
|
+
onCloseBottomSheet?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function SecondaryActionSheet({
|
|
18
|
+
actions,
|
|
19
|
+
secondaryActionsRef,
|
|
20
|
+
heading,
|
|
21
|
+
showCancel,
|
|
22
|
+
onOpenBottomSheet,
|
|
23
|
+
onCloseBottomSheet,
|
|
24
|
+
}: SecondaryActionSheetProps): JSX.Element {
|
|
25
|
+
return (
|
|
26
|
+
<Portal>
|
|
27
|
+
<BottomSheet
|
|
28
|
+
heading={heading}
|
|
29
|
+
showCancel={showCancel}
|
|
30
|
+
ref={secondaryActionsRef}
|
|
31
|
+
onOpen={onOpenBottomSheet}
|
|
32
|
+
onClose={onCloseBottomSheet}
|
|
33
|
+
>
|
|
34
|
+
<View>
|
|
35
|
+
{actions.map((action, index) => {
|
|
36
|
+
const { label, onPress, icon, iconColor, destructive } = action;
|
|
37
|
+
return (
|
|
38
|
+
<BottomSheetOption
|
|
39
|
+
destructive={destructive}
|
|
40
|
+
key={index}
|
|
41
|
+
text={label}
|
|
42
|
+
onPress={() => {
|
|
43
|
+
secondaryActionsRef?.current?.close();
|
|
44
|
+
onPress();
|
|
45
|
+
}}
|
|
46
|
+
icon={icon}
|
|
47
|
+
iconColor={iconColor}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
</View>
|
|
52
|
+
</BottomSheet>
|
|
53
|
+
</Portal>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SecondaryActionSheet } from "./SecondaryActionSheet";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineMessages } from "react-intl";
|
|
2
|
+
|
|
3
|
+
export const messages = defineMessages({
|
|
4
|
+
more: {
|
|
5
|
+
id: "more",
|
|
6
|
+
defaultMessage: "More",
|
|
7
|
+
description: "Accessibility label for the More button",
|
|
8
|
+
},
|
|
9
|
+
unavailableNetworkTitle: {
|
|
10
|
+
id: "unavailableNetworkTitle",
|
|
11
|
+
defaultMessage: "Network unavailable",
|
|
12
|
+
description: "The title for alert about network unavailable",
|
|
13
|
+
},
|
|
14
|
+
unavailableNetworkMessage: {
|
|
15
|
+
id: "unavailableNetworkMessage",
|
|
16
|
+
defaultMessage: "Check your internet connection and try again later.",
|
|
17
|
+
description: "The message for alert about network unavailable",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
import {
|
|
3
|
+
ButtonGroupActionProps,
|
|
4
|
+
ButtonGroupPrimaryActionProps,
|
|
5
|
+
ButtonGroupSecondaryActionProps,
|
|
6
|
+
PrimaryAction,
|
|
7
|
+
SecondaryAction,
|
|
8
|
+
} from "./ButtonGroupAction";
|
|
9
|
+
|
|
10
|
+
export type ButtonGroupPrimaryActionElement = ReactElement<
|
|
11
|
+
ButtonGroupPrimaryActionProps,
|
|
12
|
+
typeof PrimaryAction
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
export type ButtonGroupSecondaryActionElement = ReactElement<
|
|
16
|
+
ButtonGroupActionProps,
|
|
17
|
+
typeof SecondaryAction
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
export type ButtonGroupActionElement =
|
|
21
|
+
| ButtonGroupPrimaryActionElement
|
|
22
|
+
| ButtonGroupSecondaryActionElement;
|
|
23
|
+
|
|
24
|
+
export type BottomSheetTextTransform = "capitalize" | "none";
|
|
25
|
+
|
|
26
|
+
export type {
|
|
27
|
+
ButtonGroupActionProps,
|
|
28
|
+
ButtonGroupPrimaryActionProps,
|
|
29
|
+
ButtonGroupSecondaryActionProps,
|
|
30
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
3
|
+
import { Alert } from "react-native";
|
|
4
|
+
import { messages } from "./messages";
|
|
5
|
+
import {
|
|
6
|
+
ButtonGroupActionElement,
|
|
7
|
+
ButtonGroupPrimaryActionElement,
|
|
8
|
+
} from "./types";
|
|
9
|
+
import {
|
|
10
|
+
ButtonGroupActionProps,
|
|
11
|
+
PrimaryAction,
|
|
12
|
+
SecondaryAction,
|
|
13
|
+
} from "./ButtonGroupAction";
|
|
14
|
+
import { useAtlantisContext } from "../AtlantisContext/AtlantisContext";
|
|
15
|
+
|
|
16
|
+
interface UsePreventTapWhenOfflineShape {
|
|
17
|
+
readonly handlePress: (
|
|
18
|
+
callback: ButtonGroupActionProps["onPress"],
|
|
19
|
+
) => () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Determine if the onPress should be fired or an alert when the device is
|
|
24
|
+
* online or offline
|
|
25
|
+
*/
|
|
26
|
+
export function usePreventTapWhenOffline(): UsePreventTapWhenOfflineShape {
|
|
27
|
+
const { formatMessage } = useIntl();
|
|
28
|
+
const { isOnline } = useAtlantisContext();
|
|
29
|
+
|
|
30
|
+
const handlePress = useCallback(
|
|
31
|
+
(callback: ButtonGroupActionProps["onPress"]) => {
|
|
32
|
+
if (isOnline) return callback;
|
|
33
|
+
|
|
34
|
+
return () =>
|
|
35
|
+
Alert.alert(
|
|
36
|
+
formatMessage(messages.unavailableNetworkTitle),
|
|
37
|
+
formatMessage(messages.unavailableNetworkMessage),
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
[formatMessage, isOnline],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return { handlePress };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface GetActionShape {
|
|
47
|
+
readonly primaryActions: ButtonGroupPrimaryActionElement[];
|
|
48
|
+
readonly secondaryActions: ButtonGroupActionElement[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Separate the Primary and Secondary actions into 2 const
|
|
53
|
+
*/
|
|
54
|
+
export function getActions(
|
|
55
|
+
children: ButtonGroupActionElement | ButtonGroupActionElement[],
|
|
56
|
+
): GetActionShape {
|
|
57
|
+
const childArray = React.Children.toArray(
|
|
58
|
+
children,
|
|
59
|
+
) as ButtonGroupActionElement[];
|
|
60
|
+
|
|
61
|
+
let primaryActions = childArray.filter(
|
|
62
|
+
action =>
|
|
63
|
+
/* Checking the component type does not work in dev environments due to
|
|
64
|
+
* wrapper components used for hot-reload
|
|
65
|
+
* However, checking the type name does not work in prod due to code minification
|
|
66
|
+
* Hence 2 different checks here */
|
|
67
|
+
action.type === PrimaryAction || action.type.name === "PrimaryAction",
|
|
68
|
+
) as ButtonGroupPrimaryActionElement[];
|
|
69
|
+
|
|
70
|
+
let secondaryActions = childArray.filter(
|
|
71
|
+
action =>
|
|
72
|
+
action.type === SecondaryAction || action.type.name === "SecondaryAction",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (primaryActions.length > 2) {
|
|
76
|
+
secondaryActions = primaryActions.slice(2).concat(secondaryActions);
|
|
77
|
+
primaryActions = primaryActions.slice(0, 2);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (primaryActions.length === 0) {
|
|
81
|
+
primaryActions = secondaryActions.slice(0, 1);
|
|
82
|
+
secondaryActions = secondaryActions.slice(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { primaryActions, secondaryActions };
|
|
86
|
+
}
|
package/src/index.ts
CHANGED