@tecsinapse/react-native-kit 1.12.10 → 1.12.14

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 (67) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/components/atoms/Modal/ModalLifecycleHandler.js +24 -22
  3. package/dist/components/atoms/Modal/ModalLifecycleHandler.js.map +1 -1
  4. package/dist/components/atoms/Modal/index.d.ts +1 -0
  5. package/dist/components/atoms/Modal/index.js +13 -0
  6. package/dist/components/atoms/Modal/index.js.map +1 -1
  7. package/dist/components/atoms/Modal/ui/BaseModalView.js +20 -8
  8. package/dist/components/atoms/Modal/ui/BaseModalView.js.map +1 -1
  9. package/dist/components/atoms/Modal/ui/types.d.ts +1 -0
  10. package/dist/components/atoms/Modal/useLazyModalManager.d.ts +8 -0
  11. package/dist/components/atoms/Modal/useLazyModalManager.js +40 -0
  12. package/dist/components/atoms/Modal/useLazyModalManager.js.map +1 -0
  13. package/dist/components/molecules/Calendar/Calendar.d.ts +1 -4
  14. package/dist/components/molecules/Calendar/Calendar.js +2 -2
  15. package/dist/components/molecules/Calendar/Calendar.js.map +1 -1
  16. package/dist/components/molecules/DatePicker/DatePicker.d.ts +3 -4
  17. package/dist/components/molecules/DatePicker/DatePicker.js +29 -3
  18. package/dist/components/molecules/DatePicker/DatePicker.js.map +1 -1
  19. package/dist/components/molecules/DatePicker/index.d.ts +1 -1
  20. package/dist/components/molecules/DatePicker/index.js +6 -0
  21. package/dist/components/molecules/DatePicker/index.js.map +1 -1
  22. package/dist/components/molecules/DatePicker/styled.d.ts +4 -0
  23. package/dist/components/molecules/DatePicker/styled.js +20 -0
  24. package/dist/components/molecules/DatePicker/styled.js.map +1 -0
  25. package/dist/components/molecules/DateTimePicker/DateTimePicker.d.ts +2 -1
  26. package/dist/components/molecules/DateTimePicker/DateTimePicker.js +18 -4
  27. package/dist/components/molecules/DateTimePicker/DateTimePicker.js.map +1 -1
  28. package/dist/components/molecules/DateTimePicker/index.d.ts +1 -1
  29. package/dist/components/molecules/DateTimePicker/index.js +6 -0
  30. package/dist/components/molecules/DateTimePicker/index.js.map +1 -1
  31. package/dist/components/molecules/Select/Modal.d.ts +7 -0
  32. package/dist/components/{atoms → molecules}/Select/Modal.js +31 -18
  33. package/dist/components/molecules/Select/Modal.js.map +1 -0
  34. package/dist/components/{atoms → molecules}/Select/Select.d.ts +2 -1
  35. package/dist/components/{atoms → molecules}/Select/Select.js +35 -36
  36. package/dist/components/molecules/Select/Select.js.map +1 -0
  37. package/dist/components/{atoms → molecules}/Select/index.d.ts +0 -0
  38. package/dist/components/{atoms → molecules}/Select/index.js +0 -0
  39. package/dist/components/molecules/Select/index.js.map +1 -0
  40. package/dist/components/{atoms → molecules}/Select/styled.d.ts +1 -1
  41. package/dist/components/{atoms → molecules}/Select/styled.js +14 -12
  42. package/dist/components/molecules/Select/styled.js.map +1 -0
  43. package/dist/index.d.ts +4 -4
  44. package/dist/index.js +22 -1
  45. package/dist/index.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/components/atoms/Modal/ModalLifecycleHandler.ts +22 -20
  48. package/src/components/atoms/Modal/index.ts +2 -1
  49. package/src/components/atoms/Modal/ui/BaseModalView.tsx +29 -15
  50. package/src/components/atoms/Modal/ui/types.ts +1 -0
  51. package/src/components/atoms/Modal/useLazyModalManager.ts +43 -0
  52. package/src/components/molecules/Calendar/Calendar.tsx +3 -7
  53. package/src/components/molecules/DatePicker/DatePicker.tsx +33 -10
  54. package/src/components/molecules/DatePicker/index.ts +1 -1
  55. package/src/components/molecules/DatePicker/styled.ts +6 -0
  56. package/src/components/molecules/DateTimePicker/DateTimePicker.tsx +24 -7
  57. package/src/components/molecules/DateTimePicker/index.ts +1 -1
  58. package/src/components/{atoms → molecules}/Select/Modal.tsx +32 -33
  59. package/src/components/{atoms → molecules}/Select/Select.tsx +38 -39
  60. package/src/components/{atoms → molecules}/Select/index.ts +0 -0
  61. package/src/components/{atoms → molecules}/Select/styled.ts +10 -8
  62. package/src/index.ts +4 -4
  63. package/dist/components/atoms/Select/Modal.d.ts +0 -7
  64. package/dist/components/atoms/Select/Modal.js.map +0 -1
  65. package/dist/components/atoms/Select/Select.js.map +0 -1
  66. package/dist/components/atoms/Select/index.js.map +0 -1
  67. package/dist/components/atoms/Select/styled.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { BoxContent } from "@tecsinapse/react-core";
2
2
  import React, { FC, useCallback, useEffect, useRef, useState } from "react";
3
- import { Animated, Easing, Keyboard, KeyboardAvoidingView, LayoutChangeEvent, Pressable } from "react-native";
3
+ import { Animated, Dimensions, Easing, Keyboard, LayoutChangeEvent, Pressable, StatusBar } from "react-native";
4
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import { BackDropView, CloseBar, StyledPressableBackDrop } from "./styled";
6
6
  import { IBaseModal } from "./types";
@@ -16,18 +16,30 @@ export const ModalView: FC<IBaseModal> = ({
16
16
  BoxComponent = BoxContent,
17
17
  frozen,
18
18
  isLastShown,
19
+ showCloseBar = true,
19
20
  close,
20
21
  onClose
21
22
  }) => {
22
23
 
23
24
  const { bottom } = useSafeAreaInsets()
24
25
  const [ ready, setReady ] = useState(false)
25
- const [ keyboardOpened, setKeyboardOpened ] = useState(false)
26
+ const [ keyboardOpened, setKeyboardOpened ] = useState(0)
26
27
  const [ boxHeight, setBoxHeight ] = useState(0)
27
28
  const backgroundCarrier = useRef(new Animated.Value(0)).current
28
29
  const translationCarrier = useRef(new Animated.Value(0)).current
29
30
  const opacityCarrier = useRef(new Animated.Value(0)).current
30
- const offset = isLastShown && keyboardOpened ? 0 : bottom
31
+ const offset = isLastShown && keyboardOpened > 0 ? 0 : bottom
32
+
33
+ const getKeyboardHeight = (keyboard: number) => {
34
+ if (keyboard === 0) return 0
35
+
36
+ let wHeight = Math.ceil(Dimensions.get('window').height)
37
+ let sHeight = Math.ceil(Dimensions.get('screen').height)
38
+ if (wHeight !== sHeight) {
39
+ return keyboard + (sHeight - wHeight - (StatusBar.currentHeight || 0))
40
+ }
41
+ return keyboard
42
+ }
31
43
 
32
44
  const show = useCallback(() => {
33
45
  Animated.sequence([
@@ -100,8 +112,8 @@ export const ModalView: FC<IBaseModal> = ({
100
112
  }, [ready, visible])
101
113
 
102
114
  useEffect(() => {
103
- const showEvent = Keyboard.addListener('keyboardDidShow', () => setKeyboardOpened(true))
104
- const hideEvent = Keyboard.addListener('keyboardDidHide', () => setKeyboardOpened(false))
115
+ const showEvent = Keyboard.addListener('keyboardDidShow', (e) => setKeyboardOpened(e.endCoordinates.height))
116
+ const hideEvent = Keyboard.addListener('keyboardDidHide', () => setKeyboardOpened(0))
105
117
  return () => {
106
118
  showEvent.remove()
107
119
  hideEvent.remove()
@@ -111,16 +123,18 @@ export const ModalView: FC<IBaseModal> = ({
111
123
  return (
112
124
  <StyledPressableBackDrop onPress={!frozen ? close : undefined}>
113
125
  <BackDropView style={{ backgroundColor: backgroundInterpolation }}>
114
- <KeyboardAvoidingView enabled={isLastShown} behavior="padding">
115
- <Animated.View style={{ opacity: opacityCarrier, transform: [{ translateY: translationCarrier }]}}>
116
- <Pressable>
117
- <BoxComponent onLayout={handleBoxLayoutChanges} style={{ paddingBottom: offset }} variant="bottom">
118
- <CloseBar/>
119
- {children}
120
- </BoxComponent>
121
- </Pressable>
122
- </Animated.View>
123
- </KeyboardAvoidingView>
126
+ <Animated.View style={{
127
+ paddingBottom: isLastShown ? getKeyboardHeight(keyboardOpened) : 0,
128
+ opacity: opacityCarrier,
129
+ transform: [{ translateY: translationCarrier }]
130
+ }}>
131
+ <Pressable>
132
+ <BoxComponent onLayout={handleBoxLayoutChanges} style={{ paddingBottom: offset }} variant="bottom">
133
+ {showCloseBar && <CloseBar/>}
134
+ {children}
135
+ </BoxComponent>
136
+ </Pressable>
137
+ </Animated.View>
124
138
  </BackDropView>
125
139
  </StyledPressableBackDrop>
126
140
  )
@@ -7,6 +7,7 @@ export interface IBaseModal {
7
7
  BoxComponent?: React.FC<any>
8
8
  frozen?: boolean
9
9
  isLastShown?: boolean
10
+ showCloseBar?: boolean
10
11
  close?: () => void
11
12
  onClose?: () => void
12
13
  }
@@ -0,0 +1,43 @@
1
+ import { ReactElement, useCallback, useEffect, useState } from "react"
2
+ import { v4 as uuidv4 } from 'uuid'
3
+ import { modalLifecycle } from "./ModalGroupManager"
4
+ import { IBaseModal } from "./ui/types"
5
+
6
+ /**
7
+ * Use this hook to tell the modal lifecycle handler that you want to add
8
+ * a new modal component.
9
+ *
10
+ * @param id
11
+ * @param modal
12
+ * @returns
13
+ */
14
+ export const useLazyModalManager = () => {
15
+
16
+ const [id] = useState(uuidv4())
17
+
18
+ const requestUpdate = useCallback(() => modalLifecycle.update(), [])
19
+
20
+ const sync = useCallback((modal: ReactElement<IBaseModal>) => {
21
+ modalLifecycle.sync(id, () => modal)
22
+ return null
23
+ }, [id])
24
+
25
+ const show = useCallback(() => {
26
+ modalLifecycle.show(id)
27
+ }, [id])
28
+
29
+ const close = useCallback(() => {
30
+ modalLifecycle.close(id)
31
+ }, [id])
32
+
33
+ useEffect(() => {
34
+ return () => modalLifecycle.destroy(id)
35
+ }, [])
36
+
37
+ return {
38
+ requestUpdate,
39
+ sync,
40
+ show,
41
+ close
42
+ }
43
+ }
@@ -1,16 +1,12 @@
1
- import {
2
- Calendar as CalendarCore,
3
- CalendarProps,
4
- SelectionType,
5
- } from '@tecsinapse/react-core';
1
+ import { Calendar as CalendarCore, CalendarProps, SelectionType } from '@tecsinapse/react-core';
6
2
  import React from 'react';
7
- import { Text } from '../../atoms/Text';
8
3
  import { getLocale } from '../../../utils/date';
4
+ import { Text } from '../../atoms/Text';
9
5
 
10
6
  export const Calendar = <T extends SelectionType>({
11
7
  locale,
12
8
  ...rest
13
- }): JSX.Element => {
9
+ }: CalendarProps<T>): JSX.Element => {
14
10
  return (
15
11
  <CalendarCore
16
12
  {...rest}
@@ -1,24 +1,47 @@
1
1
  import {
2
- DatePicker as DatePickerCore,
3
- DatePickerProps,
4
- SelectionType,
2
+ DatePicker as DatePickerCore, DatePickerProps, SelectionType, Value
5
3
  } from '@tecsinapse/react-core';
6
- import React from 'react';
4
+ import React, { FC } from 'react';
5
+ import { getLocale } from '../../../utils/date';
6
+ import { IBaseModal, ModalView, useLazyModalManager } from '../../atoms/Modal';
7
7
  import { Text } from '../../atoms/Text';
8
8
  import { Calendar } from '../Calendar';
9
- import { getLocale } from '../../../utils/date';
9
+ import { CalendarBoxContent } from './styled';
10
+
11
+ export type NativeDatePickerProps<T extends SelectionType> = Omit<DatePickerProps<T>, 'CalendarComponent' | 'renderCalendar' | 'requestCloseCalendar' | 'requestShowCalendar'>
12
+
13
+ export const DatePicker = <T extends SelectionType>({ locale, onChange, ...rest }: NativeDatePickerProps<T>): JSX.Element => {
10
14
 
11
- export const DatePicker = <T extends SelectionType>({
12
- locale,
13
- ...rest
14
- }): JSX.Element => {
15
+ const modal = useLazyModalManager()
16
+
17
+ const handleChange = (value?: Value<T>) => {
18
+ onChange?.(value)
19
+ modal.requestUpdate()
20
+ }
21
+
15
22
  return (
16
23
  <DatePickerCore
17
24
  {...rest}
18
25
  TextComponent={Text}
19
26
  CalendarComponent={Calendar}
20
- animationType="slide"
21
27
  locale={locale ?? getLocale()}
28
+ onChange={handleChange}
29
+ requestShowCalendar={() => modal.show()}
30
+ requestCloseCalendar={() => modal.close()}
31
+ renderCalendar={(calendar, blur) => modal.sync(
32
+ <NativeModal onClose={blur}>
33
+ {calendar}
34
+ </NativeModal>
35
+ )}
22
36
  />
23
37
  );
38
+
24
39
  };
40
+
41
+ const NativeModal: FC<IBaseModal> = ({ children, ...others }) => {
42
+ return (
43
+ <ModalView BoxComponent={CalendarBoxContent} {...others}>
44
+ {children}
45
+ </ModalView>
46
+ )
47
+ }
@@ -1 +1 @@
1
- export { DatePicker } from './DatePicker';
1
+ export { DatePicker, NativeDatePickerProps } from './DatePicker';
@@ -0,0 +1,6 @@
1
+ import styled from "@emotion/native";
2
+ import { BoxContent, StyleProps } from "@tecsinapse/react-core";
3
+
4
+ export const CalendarBoxContent = styled(BoxContent)<Partial<StyleProps>>`
5
+ background-color: ${({ theme }) => theme.color.secondary.xlight};
6
+ `
@@ -1,23 +1,40 @@
1
1
  import {
2
2
  DateTimePicker as DateTimePickerCore,
3
- DateTimePickerProps,
3
+ DateTimePickerProps
4
4
  } from '@tecsinapse/react-core';
5
5
  import React, { FC } from 'react';
6
+ import { getLocale } from '../../../utils/date';
7
+ import { IBaseModal, ModalView, useLazyModalManager } from '../../atoms/Modal';
6
8
  import { Text } from '../../atoms/Text';
7
9
  import { DateTimeSelector } from '../DateTimeSelector';
8
- import { getLocale } from '../../../utils/date';
9
10
 
10
- export const DateTimePicker: FC<DateTimePickerProps> = ({
11
- locale,
12
- ...rest
13
- }) => {
11
+ export type NativeDateTimePickerProps = Omit<DateTimePickerProps, 'DateTimeSelectorComponent' | 'renderSelector' | 'requestCloseSelector' | 'requestShowSelector'>
12
+
13
+ export const DateTimePicker: FC<NativeDateTimePickerProps> = ({ locale, ...rest }) => {
14
+
15
+ const modal = useLazyModalManager()
16
+
14
17
  return (
15
18
  <DateTimePickerCore
16
19
  {...rest}
17
20
  TextComponent={Text}
18
21
  DateTimeSelectorComponent={DateTimeSelector}
19
- animationType="slide"
20
22
  locale={locale ?? getLocale()}
23
+ requestShowSelector={() => modal.show()}
24
+ requestCloseSelector={() => modal.close()}
25
+ renderSelector={(selector, blur) => modal.sync(
26
+ <NativeModal onClose={blur}>
27
+ {selector}
28
+ </NativeModal>
29
+ )}
21
30
  />
22
31
  );
23
32
  };
33
+
34
+ const NativeModal: FC<IBaseModal> = ({ children, ...others }) => {
35
+ return (
36
+ <ModalView {...others}>
37
+ {children}
38
+ </ModalView>
39
+ )
40
+ }
@@ -1 +1 @@
1
- export { DateTimePicker } from './DateTimePicker';
1
+ export { DateTimePicker, NativeDateTimePickerProps } from './DateTimePicker';
@@ -1,23 +1,16 @@
1
+ import { Checkbox, getStatusBarHeight, RadioButton, useDebouncedState } from '@tecsinapse/react-core';
1
2
  import * as React from 'react';
2
- import {
3
- FetchIndicator,
4
- ModalFooter,
5
- ListItem,
6
- SearchBarContainer,
7
- SelectIcon,
8
- StyledModal,
9
- } from './styled';
10
- import { FlatList, Modal as RNModal, ModalProps, View } from 'react-native';
3
+ import { FlatList, StatusBar, View } from 'react-native';
4
+ import { Button } from '../../atoms/Button';
5
+ import { Header } from '../../atoms/Header';
6
+ import { Input } from '../../atoms/Input';
7
+ import { IBaseModal, ModalView } from '../../atoms/Modal';
8
+ import { Text } from '../../atoms/Text';
11
9
  import { SelectNativeProps } from './Select';
12
- import { Text } from '../Text';
13
10
  import {
14
- Button,
15
- Checkbox,
16
- RadioButton,
17
- useDebouncedState,
18
- } from '@tecsinapse/react-core';
19
- import { Input } from '../Input';
20
- import { Header } from '../Header';
11
+ FetchIndicator, getStyledModal, ListItem, ModalFooter, SearchBarContainer,
12
+ SelectIcon
13
+ } from './styled';
21
14
 
22
15
  interface LoadingProps {
23
16
  loading?: boolean;
@@ -35,15 +28,18 @@ const Component = <Data, Type extends 'single' | 'multi'>({
35
28
  value,
36
29
  onSelect,
37
30
  onSearch,
38
- onRequestClose,
39
31
  selectModalTitle,
40
32
  selectModalTitleComponent,
41
33
  confirmButtonText,
42
34
  loading,
43
- ...modalProps
44
- }: SelectNativeProps<Data, Type> & ModalProps & LoadingProps): JSX.Element => {
35
+ close,
36
+ closeOnPick,
37
+ ...others
38
+ }: SelectNativeProps<Data, Type> & LoadingProps & IBaseModal): JSX.Element => {
45
39
  const [selectedValues, setSelectedValues] = React.useState<Data[]>([]);
46
40
  const [searchArg, setSearchArg] = useDebouncedState<string>('', onSearch);
41
+ const ModalComponent = React.useMemo(() => getStyledModal(getStatusBarHeight(true)), [])
42
+ const _closeOnPick = closeOnPick && type === 'single'
47
43
 
48
44
  // Resets the temporary state to the initial state whenever the
49
45
  // modal is reopened or the value changes
@@ -87,13 +83,19 @@ const Component = <Data, Type extends 'single' | 'multi'>({
87
83
  });
88
84
  };
89
85
 
86
+ React.useEffect(() => {
87
+ if (_closeOnPick && selectedValues[0] && (selectedValues[0] !== value)) {
88
+ handleConfirm()
89
+ }
90
+ }, [selectedValues[0], value, closeOnPick])
91
+
90
92
  const handleConfirm = () => {
91
93
  // TS Workaround since TS won't infer the ternary operator's result type correctly
92
94
  type OnSelectArg = Parameters<typeof onSelect>[0];
93
95
  onSelect(
94
96
  (type === 'single' ? selectedValues[0] : selectedValues) as OnSelectArg
95
97
  );
96
- onRequestClose && onRequestClose();
98
+ close?.()
97
99
  };
98
100
 
99
101
  const headerContent = selectModalTitleComponent ? (
@@ -105,16 +107,10 @@ const Component = <Data, Type extends 'single' | 'multi'>({
105
107
  ) : null;
106
108
 
107
109
  return (
108
- <RNModal
109
- transparent
110
- hardwareAccelerated
111
- {...modalProps}
112
- onRequestClose={onRequestClose}
113
- >
114
- <StyledModal>
110
+ <ModalView {...others} BoxComponent={ModalComponent} showCloseBar={false}>
115
111
  <Header
116
112
  rightButton={{
117
- onPress: onRequestClose,
113
+ onPress: close,
118
114
  icon: {
119
115
  name: 'close',
120
116
  type: 'material-community',
@@ -124,6 +120,7 @@ const Component = <Data, Type extends 'single' | 'multi'>({
124
120
  >
125
121
  {headerContent}
126
122
  </Header>
123
+
127
124
  {!hideSearchBar && (
128
125
  <SearchBarContainer>
129
126
  <Input
@@ -136,9 +133,11 @@ const Component = <Data, Type extends 'single' | 'multi'>({
136
133
  />
137
134
  </SearchBarContainer>
138
135
  )}
136
+
139
137
  {loading && (
140
138
  <FetchIndicator animating={true} color={'grey'} size={'large'} />
141
139
  )}
140
+
142
141
  <FlatList
143
142
  data={data}
144
143
  keyExtractor={keyExtractor}
@@ -171,7 +170,8 @@ const Component = <Data, Type extends 'single' | 'multi'>({
171
170
  </ListItem>
172
171
  )}
173
172
  />
174
- <ModalFooter>
173
+
174
+ { !_closeOnPick && <ModalFooter>
175
175
  <Button
176
176
  variant={'filled'}
177
177
  color={'primary'}
@@ -182,9 +182,8 @@ const Component = <Data, Type extends 'single' | 'multi'>({
182
182
  {confirmButtonText}
183
183
  </Text>
184
184
  </Button>
185
- </ModalFooter>
186
- </StyledModal>
187
- </RNModal>
185
+ </ModalFooter>}
186
+ </ModalView>
188
187
  );
189
188
  };
190
189
 
@@ -1,13 +1,13 @@
1
- import * as React from 'react';
2
1
  import {
3
- InputContainerProps,
4
- useInputFocus,
5
- HintInputContainer,
2
+ HintInputContainer, InputContainerProps,
3
+ useInputFocus
6
4
  } from '@tecsinapse/react-core';
7
- import { Text } from '../Text';
5
+ import * as React from 'react';
6
+ import { useEffect, useState } from 'react';
7
+ import { useLazyModalManager } from '../../atoms/Modal';
8
+ import { Text } from '../../atoms/Text';
8
9
  import { Modal } from './Modal';
9
10
  import { SelectIcon, StyledSelectionText } from './styled';
10
- import { useEffect, useState } from 'react';
11
11
 
12
12
  export interface SelectNativeProps<Data, Type extends 'single' | 'multi'>
13
13
  extends Omit<InputContainerProps, 'value' | 'onChange' | 'onChangeText'> {
@@ -34,6 +34,7 @@ export interface SelectNativeProps<Data, Type extends 'single' | 'multi'>
34
34
  confirmButtonText?: string;
35
35
  selectModalTitle?: string;
36
36
  selectModalTitleComponent?: JSX.Element;
37
+ closeOnPick?: boolean;
37
38
  }
38
39
 
39
40
  function Select<Data, Type extends 'single' | 'multi'>({
@@ -60,6 +61,7 @@ function Select<Data, Type extends 'single' | 'multi'>({
60
61
  hintComponent,
61
62
  hint,
62
63
  style,
64
+ closeOnPick = type === 'single',
63
65
  ...rest
64
66
  }: SelectNativeProps<Data, Type>): JSX.Element {
65
67
  const { focused, handleBlur, handleFocus } = useInputFocus(
@@ -68,8 +70,8 @@ function Select<Data, Type extends 'single' | 'multi'>({
68
70
  !disabled
69
71
  );
70
72
 
71
- const [modalVisible, setModalVisible] = React.useState(false);
72
73
  const [selectOptions, setSelectOptions] = useState<Data[]>([]);
74
+ const modal = useLazyModalManager()
73
75
 
74
76
  // TODO: Add Skeleton to modal height when loading is true
75
77
  const [loading, setLoading] = useState<boolean>(false);
@@ -145,6 +147,7 @@ function Select<Data, Type extends 'single' | 'multi'>({
145
147
  } catch (e) {
146
148
  // TODO: Catch error
147
149
  } finally {
150
+ modal.requestUpdate()
148
151
  setLoading(false);
149
152
  }
150
153
  }
@@ -152,17 +155,6 @@ function Select<Data, Type extends 'single' | 'multi'>({
152
155
  [options, value, keyExtractor]
153
156
  );
154
157
 
155
- const handlePressInput = async () => {
156
- setModalVisible(true);
157
- handleFocus();
158
- await handleLazyFocus();
159
- };
160
-
161
- const handleCloseModal = () => {
162
- setModalVisible(false);
163
- handleBlur();
164
- };
165
-
166
158
  const getDisplayValue = () => {
167
159
  if (Array.isArray(value)) {
168
160
  if (value.length === 0) return placeholder;
@@ -189,6 +181,34 @@ function Select<Data, Type extends 'single' | 'multi'>({
189
181
  }
190
182
  };
191
183
 
184
+ modal.sync(
185
+ <Modal
186
+ options={selectOptions || []}
187
+ focused={true}
188
+ keyExtractor={keyExtractor}
189
+ labelExtractor={labelExtractor}
190
+ groupKeyExtractor={groupKeyExtractor}
191
+ searchBarPlaceholder={searchBarPlaceholder}
192
+ type={type}
193
+ onSelect={onSelect}
194
+ value={value}
195
+ hideSearchBar={hideSearchBar}
196
+ onSearch={handleOnSearch}
197
+ selectModalTitle={selectModalTitle}
198
+ selectModalTitleComponent={selectModalTitleComponent}
199
+ confirmButtonText={confirmButtonText}
200
+ loading={loading}
201
+ onClose={handleBlur}
202
+ closeOnPick={closeOnPick}
203
+ />
204
+ )
205
+
206
+ const handlePressInput = async () => {
207
+ modal.show()
208
+ handleFocus();
209
+ await handleLazyFocus();
210
+ };
211
+
192
212
  return (
193
213
  <>
194
214
  <HintInputContainer
@@ -212,27 +232,6 @@ function Select<Data, Type extends 'single' | 'multi'>({
212
232
  {getDisplayValue() || ' '}
213
233
  </StyledSelectionText>
214
234
  </HintInputContainer>
215
- <Modal
216
- visible={modalVisible}
217
- options={selectOptions || []}
218
- focused={modalVisible}
219
- keyExtractor={keyExtractor}
220
- labelExtractor={labelExtractor}
221
- groupKeyExtractor={groupKeyExtractor}
222
- searchBarPlaceholder={searchBarPlaceholder}
223
- type={type}
224
- onSelect={onSelect}
225
- value={value}
226
- hideSearchBar={hideSearchBar}
227
- onRequestClose={handleCloseModal}
228
- animated
229
- animationType={'slide'}
230
- onSearch={handleOnSearch}
231
- selectModalTitle={selectModalTitle}
232
- selectModalTitleComponent={selectModalTitleComponent}
233
- confirmButtonText={confirmButtonText}
234
- loading={loading}
235
- />
236
235
  </>
237
236
  );
238
237
  }
@@ -7,19 +7,21 @@ import {
7
7
  InputContainerProps,
8
8
  PressableSurface,
9
9
  PressableSurfaceProps,
10
+ RFValue,
10
11
  RFValueStr,
11
12
  StyleProps
12
13
  } from '@tecsinapse/react-core';
13
14
  import { ActivityIndicator, ModalProps, View, ViewProps } from 'react-native';
14
- import { Input, InputNativeProps } from '../Input';
15
- import { Text } from '../Text';
15
+ import { Input, InputNativeProps } from '../../atoms/Input';
16
+ import { Text } from '../../atoms/Text';
16
17
 
17
- export const StyledModal = styled(View)<ModalProps & Partial<StyleProps>>`
18
- position: relative;
19
- background-color: ${({ theme }) => theme.miscellaneous.bodyColor};
20
- height: 100%;
21
- width: 100%;
22
- `;
18
+ export const getStyledModal = (safeTop: number = 0) => {
19
+ return styled(View)<ModalProps & Partial<StyleProps>>`
20
+ padding-top: ${`${RFValue(safeTop)}px`};
21
+ background-color: ${({ theme }) => theme.miscellaneous.bodyColor};
22
+ height: 100%;
23
+ `
24
+ }
23
25
 
24
26
  export const StyledSelectionText = styled(Text)(
25
27
  (props: Partial<InputContainerProps> & Partial<StyleProps>) => css`
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from '@tecsinapse/react-core';
2
2
  export { Header, HeaderProps } from './components/atoms/Header';
3
- export { Select, SelectNativeProps } from './components/atoms/Select';
3
+ export { Select, SelectNativeProps } from './components/molecules/Select';
4
4
  export { Input, InputNativeProps } from './components/atoms/Input';
5
5
  export { TextArea, TextAreaProps } from './components/atoms/TextArea';
6
6
  export { Text, TextNativeProps } from './components/atoms/Text';
@@ -21,9 +21,9 @@ export {
21
21
  } from './components/atoms/SnappingSlider';
22
22
  export { Badge, BadgeNativeProps } from './components/atoms/Badge';
23
23
  export { Snackbar, SnackbarNativeProps } from './components/molecules/Snackbar';
24
- export { DatePicker } from './components/molecules/DatePicker';
25
- export { DateTimePicker } from './components/molecules/DateTimePicker';
24
+ export { DatePicker, NativeDatePickerProps } from './components/molecules/DatePicker';
25
+ export { DateTimePicker, NativeDateTimePickerProps } from './components/molecules/DateTimePicker';
26
26
  export { Avatar } from './components/atoms/Avatar';
27
27
  export { Calendar } from './components/molecules/Calendar';
28
28
  export { DateTimeSelector } from './components/molecules/DateTimeSelector';
29
- export { ModalGroupManager, ModalView, ModalLifecycleHandler, useModalManager, IBaseModal } from './components/atoms/Modal';
29
+ export { ModalGroupManager, ModalView, ModalLifecycleHandler, useLazyModalManager, useModalManager, IBaseModal } from './components/atoms/Modal';
@@ -1,7 +0,0 @@
1
- import { ModalProps } from 'react-native';
2
- import { SelectNativeProps } from './Select';
3
- interface LoadingProps {
4
- loading?: boolean;
5
- }
6
- export declare const Modal: <Data, Type extends "single" | "multi">({ options, keyExtractor, labelExtractor, groupKeyExtractor, hideSearchBar, searchBarPlaceholder, focused, type, value, onSelect, onSearch, onRequestClose, selectModalTitle, selectModalTitleComponent, confirmButtonText, loading, ...modalProps }: SelectNativeProps<Data, Type> & import("react-native").ModalBaseProps & import("react-native").ModalPropsIOS & import("react-native").ModalPropsAndroid & import("react-native").ViewProps & LoadingProps) => JSX.Element;
7
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/components/atoms/Select/Modal.tsx"],"names":["Component","options","keyExtractor","labelExtractor","groupKeyExtractor","hideSearchBar","searchBarPlaceholder","focused","type","value","onSelect","onSearch","onRequestClose","selectModalTitle","selectModalTitleComponent","confirmButtonText","loading","modalProps","selectedValues","setSelectedValues","React","useState","searchArg","setSearchArg","useEffect","getData","map","option","index","_checked","find","data","handlePressItem","newArr","found","push","handleConfirm","headerContent","onPress","icon","name","fontColor","text","item","Modal"],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport {\n FetchIndicator,\n ModalFooter,\n ListItem,\n SearchBarContainer,\n SelectIcon,\n StyledModal,\n} from './styled';\nimport { FlatList, Modal as RNModal, ModalProps, View } from 'react-native';\nimport { SelectNativeProps } from './Select';\nimport { Text } from '../Text';\nimport {\n Button,\n Checkbox,\n RadioButton,\n useDebouncedState,\n} from '@tecsinapse/react-core';\nimport { Input } from '../Input';\nimport { Header } from '../Header';\n\ninterface LoadingProps {\n loading?: boolean;\n}\n\nconst Component = <Data, Type extends 'single' | 'multi'>({\n options,\n keyExtractor,\n labelExtractor,\n groupKeyExtractor,\n hideSearchBar,\n searchBarPlaceholder,\n focused,\n type,\n value,\n onSelect,\n onSearch,\n onRequestClose,\n selectModalTitle,\n selectModalTitleComponent,\n confirmButtonText,\n loading,\n ...modalProps\n}: SelectNativeProps<Data, Type> & ModalProps & LoadingProps): JSX.Element => {\n const [selectedValues, setSelectedValues] = React.useState<Data[]>([]);\n const [searchArg, setSearchArg] = useDebouncedState<string>('', onSearch);\n\n // Resets the temporary state to the initial state whenever the\n // modal is reopened or the value changes\n React.useEffect(() => {\n setSelectedValues(\n (value ? (type === 'multi' ? value : [value]) : []) as Data[]\n );\n }, [value, focused, setSelectedValues]);\n\n const getData = (options: Data[]) => {\n return options?.map((option, index) => ({\n ...option,\n _checked:\n type === 'multi'\n ? !!selectedValues.find(\n value => keyExtractor(option, index) == keyExtractor(value, index)\n )\n : keyExtractor((selectedValues[0] || {}) as Data, index) ==\n keyExtractor(option, index),\n }));\n };\n\n const data = typeof options !== 'function' ? getData(options) : [];\n\n const handlePressItem = (option: Data) => () => {\n setSelectedValues(selectedValues => {\n if (type === 'multi') {\n const newArr: Data[] = [];\n let found = false;\n for (const value of selectedValues) {\n if (keyExtractor(value) != keyExtractor(option)) newArr.push(value);\n else found = true;\n }\n if (!found) newArr.push(option);\n return newArr;\n }\n return keyExtractor((selectedValues[0] || {}) as Data) ===\n keyExtractor(option)\n ? []\n : [option];\n });\n };\n\n const handleConfirm = () => {\n // TS Workaround since TS won't infer the ternary operator's result type correctly\n type OnSelectArg = Parameters<typeof onSelect>[0];\n onSelect(\n (type === 'single' ? selectedValues[0] : selectedValues) as OnSelectArg\n );\n onRequestClose && onRequestClose();\n };\n\n const headerContent = selectModalTitleComponent ? (\n selectModalTitleComponent\n ) : selectModalTitle ? (\n <Text typography=\"h4\" fontWeight=\"bold\">\n {selectModalTitle}\n </Text>\n ) : null;\n\n return (\n <RNModal\n transparent\n hardwareAccelerated\n {...modalProps}\n onRequestClose={onRequestClose}\n >\n <StyledModal>\n <Header\n rightButton={{\n onPress: onRequestClose,\n icon: {\n name: 'close',\n type: 'material-community',\n fontColor: 'light',\n },\n }}\n >\n {headerContent}\n </Header>\n {!hideSearchBar && (\n <SearchBarContainer>\n <Input\n placeholder={searchBarPlaceholder}\n value={searchArg}\n onChange={text => setSearchArg(text)}\n leftComponent={\n <SelectIcon name=\"search\" type=\"ionicon\" size=\"centi\" />\n }\n />\n </SearchBarContainer>\n )}\n {loading && (\n <FetchIndicator animating={true} color={'grey'} size={'large'} />\n )}\n <FlatList\n data={data}\n keyExtractor={keyExtractor}\n fadingEdgeLength={200}\n renderItem={({ item }) => (\n <ListItem onPress={handlePressItem(item)}>\n <View pointerEvents={'none'}>\n {type === 'multi' ? (\n <Checkbox\n color={'primary'}\n labelPosition={'right'}\n checked={item._checked}\n >\n <Text fontWeight={item._checked ? 'bold' : 'regular'}>\n {labelExtractor(item)}\n </Text>\n </Checkbox>\n ) : (\n <RadioButton\n color={'primary'}\n labelPosition={'right'}\n checked={item._checked}\n >\n <Text fontWeight={item._checked ? 'bold' : 'regular'}>\n {labelExtractor(item)}\n </Text>\n </RadioButton>\n )}\n </View>\n </ListItem>\n )}\n />\n <ModalFooter>\n <Button\n variant={'filled'}\n color={'primary'}\n onPress={handleConfirm}\n disabled={loading}\n >\n <Text fontColor={'light'} fontWeight=\"bold\">\n {confirmButtonText}\n </Text>\n </Button>\n </ModalFooter>\n </StyledModal>\n </RNModal>\n );\n};\n\nexport const Modal = Component;\n"],"file":"Modal.js"}