@neko-os/ui 0.5.3 → 0.6.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/NekoUI.js +12 -9
- package/dist/abstractions/Image.native.js +1 -1
- package/dist/abstractions/Image.web.js +1 -1
- package/dist/abstractions/WindowOverlay.js +3 -0
- package/dist/abstractions/WindowOverlay.native.js +21 -0
- package/dist/abstractions/helpers/storage.js +14 -4
- package/dist/abstractions/helpers/storage.native.js +9 -1
- package/dist/components/feedback/notifications/NotificationsHandler.js +10 -6
- package/dist/components/form/useNewForm.js +2 -0
- package/dist/components/index.js +3 -1
- package/dist/components/inputs/DateInput.js +10 -6
- package/dist/components/inputs/InputWrapper.js +11 -11
- package/dist/components/inputs/NumberWheelInput.js +50 -0
- package/dist/components/inputs/NumberWheelPicker.js +43 -0
- package/dist/components/inputs/SegmentedPicker.js +3 -2
- package/dist/components/inputs/UploadInput.js +4 -4
- package/dist/components/inputs/WheelPicker.js +49 -0
- package/dist/components/inputs/WheelPicker.native.js +88 -0
- package/dist/components/inputs/WheelPicker.web.js +1 -0
- package/dist/components/inputs/dateWheelPicker/DateWheelPicker.js +24 -0
- package/dist/components/inputs/dateWheelPicker/DayWheelPicker.js +48 -0
- package/dist/components/inputs/dateWheelPicker/MonthWheelPicker.js +19 -0
- package/dist/components/inputs/dateWheelPicker/QuarterWheelPicker.js +61 -0
- package/dist/components/inputs/dateWheelPicker/WeekWheelPicker.js +66 -0
- package/dist/components/inputs/dateWheelPicker/YearWheelPicker.js +35 -0
- package/dist/components/inputs/index.js +5 -1
- package/dist/components/inputs/upload/Upload.native.js +60 -52
- package/dist/components/inputs/upload/useUploadState.js +11 -3
- package/dist/components/measurements/FeetInchesInput.js +91 -0
- package/dist/components/measurements/LengthInput.js +32 -0
- package/dist/components/measurements/LengthText.js +10 -0
- package/dist/components/measurements/MeasurementHandler.js +26 -0
- package/dist/components/measurements/WeightInput.js +25 -0
- package/dist/components/measurements/WeightText.js +10 -0
- package/dist/components/measurements/helpers/detectMeasurementSystem.js +15 -0
- package/dist/components/measurements/helpers/detectMeasurementSystem.native.js +9 -0
- package/dist/components/measurements/helpers/index.js +2 -0
- package/dist/components/measurements/helpers/length.js +112 -0
- package/dist/components/measurements/helpers/weight.js +56 -0
- package/dist/components/measurements/index.js +9 -0
- package/dist/components/measurements/useLengthFormatter.js +35 -0
- package/dist/components/measurements/useLocalInputValue.js +32 -0
- package/dist/components/measurements/useWeightFormatter.js +29 -0
- package/dist/components/presentation/Avatar.js +3 -3
- package/dist/components/routing/ReturnButton.js +20 -0
- package/dist/components/routing/ReturnButton.native.js +20 -0
- package/dist/components/routing/ReturnButton.web.js +2 -0
- package/dist/components/routing/ReturnLink.js +25 -0
- package/dist/components/routing/ReturnLink.native.js +25 -0
- package/dist/components/routing/ReturnLink.web.js +2 -0
- package/dist/components/routing/RoutedStepsContent.js +21 -0
- package/dist/components/routing/RoutedStepsContent.native.js +94 -0
- package/dist/components/routing/RoutedStepsContent.web.js +3 -0
- package/dist/components/routing/index.js +3 -0
- package/dist/components/state/StatePresenter.js +1 -1
- package/dist/components/steps/StepsHandler.js +2 -0
- package/dist/components/structure/TopBar.js +18 -16
- package/dist/components/theme/ThemePickerDrawer.js +1 -1
- package/dist/helpers/compress.js +61 -0
- package/dist/helpers/compress.native.js +49 -0
- package/dist/helpers/files.js +7 -0
- package/dist/helpers/files.native.js +55 -0
- package/dist/helpers/index.js +6 -1
- package/dist/helpers/media.js +4 -0
- package/dist/helpers/media.native.js +41 -0
- package/dist/helpers/numbers.js +13 -0
- package/dist/helpers/pickAssets.js +7 -0
- package/dist/helpers/pickAssets.native.js +66 -0
- package/dist/helpers/storage.js +17 -0
- package/dist/i18n/I18n.js +4 -4
- package/dist/index.js +1 -1
- package/dist/modifiers/flex.js +8 -3
- package/dist/responsive/responsiveHooks.js +14 -0
- package/dist/theme/default/blackTheme.js +3 -1
- package/dist/theme/default/cyberpunkTheme.js +3 -1
- package/dist/theme/default/darkTheme.js +3 -1
- package/dist/theme/default/hackerTheme.js +3 -1
- package/dist/theme/default/lightTheme.js +3 -1
- package/dist/theme/default/paperTheme.js +3 -1
- package/package.json +2 -14
- package/src/NekoUI.js +16 -13
- package/src/abstractions/Image.native.js +1 -1
- package/src/abstractions/Image.web.js +1 -1
- package/src/abstractions/WindowOverlay.js +3 -0
- package/src/abstractions/WindowOverlay.native.js +21 -0
- package/src/abstractions/helpers/storage.js +13 -3
- package/src/abstractions/helpers/storage.native.js +8 -0
- package/src/components/feedback/notifications/NotificationsHandler.js +12 -8
- package/src/components/form/useNewForm.js +2 -0
- package/src/components/index.js +2 -0
- package/src/components/inputs/DateInput.js +8 -4
- package/src/components/inputs/InputWrapper.js +3 -3
- package/src/components/inputs/NumberWheelInput.js +50 -0
- package/src/components/inputs/NumberWheelPicker.js +43 -0
- package/src/components/inputs/SegmentedPicker.js +2 -1
- package/src/components/inputs/UploadInput.js +2 -2
- package/src/components/inputs/WheelPicker.js +49 -0
- package/src/components/inputs/WheelPicker.native.js +88 -0
- package/src/components/inputs/WheelPicker.web.js +1 -0
- package/src/components/inputs/dateWheelPicker/DateWheelPicker.js +24 -0
- package/src/components/inputs/dateWheelPicker/DayWheelPicker.js +48 -0
- package/src/components/inputs/dateWheelPicker/MonthWheelPicker.js +19 -0
- package/src/components/inputs/dateWheelPicker/QuarterWheelPicker.js +61 -0
- package/src/components/inputs/dateWheelPicker/WeekWheelPicker.js +66 -0
- package/src/components/inputs/dateWheelPicker/YearWheelPicker.js +35 -0
- package/src/components/inputs/index.js +4 -0
- package/src/components/inputs/upload/Upload.native.js +58 -50
- package/src/components/inputs/upload/useUploadState.js +11 -3
- package/src/components/measurements/FeetInchesInput.js +91 -0
- package/src/components/measurements/LengthInput.js +32 -0
- package/src/components/measurements/LengthText.js +10 -0
- package/src/components/measurements/MeasurementHandler.js +26 -0
- package/src/components/measurements/WeightInput.js +25 -0
- package/src/components/measurements/WeightText.js +10 -0
- package/src/components/measurements/helpers/detectMeasurementSystem.js +15 -0
- package/src/components/measurements/helpers/detectMeasurementSystem.native.js +9 -0
- package/src/components/measurements/helpers/index.js +2 -0
- package/src/components/measurements/helpers/length.js +112 -0
- package/src/components/measurements/helpers/weight.js +56 -0
- package/src/components/measurements/index.js +9 -0
- package/src/components/measurements/useLengthFormatter.js +35 -0
- package/src/components/measurements/useLocalInputValue.js +32 -0
- package/src/components/measurements/useWeightFormatter.js +29 -0
- package/src/components/presentation/Avatar.js +2 -2
- package/src/components/routing/ReturnButton.js +20 -0
- package/src/components/routing/ReturnButton.native.js +20 -0
- package/src/components/routing/ReturnButton.web.js +2 -0
- package/src/components/routing/ReturnLink.js +25 -0
- package/src/components/routing/ReturnLink.native.js +25 -0
- package/src/components/routing/ReturnLink.web.js +2 -0
- package/src/components/routing/RoutedStepsContent.js +21 -0
- package/src/components/routing/RoutedStepsContent.native.js +94 -0
- package/src/components/routing/RoutedStepsContent.web.js +3 -0
- package/src/components/routing/index.js +3 -0
- package/src/components/state/StatePresenter.js +1 -1
- package/src/components/steps/StepsHandler.js +2 -0
- package/src/components/structure/TopBar.js +16 -14
- package/src/components/theme/ThemePickerDrawer.js +1 -1
- package/src/helpers/compress.js +61 -0
- package/src/helpers/compress.native.js +49 -0
- package/src/helpers/files.js +7 -0
- package/src/helpers/files.native.js +55 -0
- package/src/helpers/index.js +6 -1
- package/src/helpers/media.js +4 -0
- package/src/helpers/media.native.js +41 -0
- package/src/helpers/numbers.js +13 -0
- package/src/helpers/pickAssets.js +7 -0
- package/src/helpers/pickAssets.native.js +66 -0
- package/src/helpers/storage.js +17 -0
- package/src/i18n/I18n.js +2 -2
- package/src/index.js +1 -1
- package/src/modifiers/flex.js +7 -2
- package/src/responsive/responsiveHooks.js +14 -0
- package/src/theme/default/blackTheme.js +2 -0
- package/src/theme/default/cyberpunkTheme.js +2 -0
- package/src/theme/default/darkTheme.js +2 -0
- package/src/theme/default/hackerTheme.js +2 -0
- package/src/theme/default/lightTheme.js +2 -0
- package/src/theme/default/paperTheme.js +2 -0
package/src/NekoUI.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { initFirstDayOfWeek } from './helpers/weekStartSetup'
|
|
2
2
|
import { DynamicStyleTag } from './DynamicStyleTag'
|
|
3
3
|
import { I18nProvider } from './i18n'
|
|
4
|
+
import { MeasurementHandler } from './components/measurements/MeasurementHandler'
|
|
4
5
|
import { ModalsHandler } from './components/modals/modal/handler/ModalsHandler'
|
|
5
6
|
import { NotificationsHandler } from './components/feedback/notifications/NotificationsHandler'
|
|
6
7
|
import { OverlayHandler } from './components/structure/overlay/OverlayHandler'
|
|
@@ -12,23 +13,25 @@ import { useThemeHandler } from './theme'
|
|
|
12
13
|
|
|
13
14
|
initFirstDayOfWeek()
|
|
14
15
|
|
|
15
|
-
export function NekoUI({ children, i18n, ...props }) {
|
|
16
|
+
export function NekoUI({ children, i18n, measurementSystem, ...props }) {
|
|
16
17
|
return (
|
|
17
18
|
<ThemeHandler {...props}>
|
|
18
19
|
<DynamicStyleTag />
|
|
19
20
|
<ResponsiveHandler>
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
<MeasurementHandler measurementSystem={measurementSystem}>
|
|
22
|
+
<PortalHandler>
|
|
23
|
+
<ModalsHandler>
|
|
24
|
+
<I18nProvider i18n={i18n}>
|
|
25
|
+
<NotificationsHandler>
|
|
26
|
+
<OverlayHandler>
|
|
27
|
+
{children}
|
|
28
|
+
<FixedComponents />
|
|
29
|
+
</OverlayHandler>
|
|
30
|
+
</NotificationsHandler>
|
|
31
|
+
</I18nProvider>
|
|
32
|
+
</ModalsHandler>
|
|
33
|
+
</PortalHandler>
|
|
34
|
+
</MeasurementHandler>
|
|
32
35
|
</ResponsiveHandler>
|
|
33
36
|
</ThemeHandler>
|
|
34
37
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Image as RNImage } from 'react-native'
|
|
2
2
|
|
|
3
3
|
export function AbsImage({ src, source, resizeMode = 'cover', ...props }) {
|
|
4
|
-
if (!source &&
|
|
4
|
+
if (!source && src != null) source = typeof src === 'string' ? { uri: src } : src
|
|
5
5
|
|
|
6
6
|
return <RNImage source={source} resizeMode={resizeMode} {...props} />
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Image as RNImage } from 'react-native'
|
|
2
2
|
|
|
3
3
|
export function AbsImage({ src, source, resizeMode = 'cover', ...props }) {
|
|
4
|
-
if (!source &&
|
|
4
|
+
if (!source && src != null) source = typeof src === 'string' ? { uri: src } : src
|
|
5
5
|
|
|
6
6
|
return <RNImage source={source} resizeMode={resizeMode} {...props} />
|
|
7
7
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Platform } from 'react-native'
|
|
2
|
+
|
|
3
|
+
let AbsWindowOverlay = ({ children }) => children
|
|
4
|
+
|
|
5
|
+
// iOS native-stack modals (presentation: 'modal') present a UIViewController above
|
|
6
|
+
// the RN root view, covering anything rendered inside it regardless of zIndex.
|
|
7
|
+
// FullWindowOverlay renders above presented view controllers and passes touches
|
|
8
|
+
// through empty areas. On Android/web screens stay in the same window, so the
|
|
9
|
+
// passthrough is enough.
|
|
10
|
+
if (Platform.OS === 'ios') {
|
|
11
|
+
try {
|
|
12
|
+
const { FullWindowOverlay } = require('react-native-screens') || {}
|
|
13
|
+
if (FullWindowOverlay) {
|
|
14
|
+
AbsWindowOverlay = ({ children }) => <FullWindowOverlay>{children}</FullWindowOverlay>
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
console.warn('react-native-screens not installed. Notifications may render behind native iOS modals.')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { AbsWindowOverlay }
|
|
@@ -3,7 +3,7 @@ function set(key, value) {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
function setAsync(key, value) {
|
|
6
|
-
return Promise.
|
|
6
|
+
return Promise.resolve(set(key, value))
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function get(key) {
|
|
@@ -11,7 +11,7 @@ function get(key) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function getAsync(key) {
|
|
14
|
-
return Promise.
|
|
14
|
+
return Promise.resolve(get(key))
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function remove(key) {
|
|
@@ -19,7 +19,15 @@ function remove(key) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function removeAsync(key) {
|
|
22
|
-
return Promise.
|
|
22
|
+
return Promise.resolve(remove(key))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clear() {
|
|
26
|
+
return localStorage.clear()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clearAsync() {
|
|
30
|
+
return Promise.resolve(clear())
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
export const AbsStorage = {
|
|
@@ -29,4 +37,6 @@ export const AbsStorage = {
|
|
|
29
37
|
getAsync,
|
|
30
38
|
remove,
|
|
31
39
|
removeAsync,
|
|
40
|
+
clear,
|
|
41
|
+
clearAsync,
|
|
32
42
|
}
|
|
@@ -7,6 +7,9 @@ let getAsync = () => Promise.resolve(get())
|
|
|
7
7
|
let remove = () => console.warn('expo-sqlite not installed. Neko Storage needs expo-sqlite to work properly')
|
|
8
8
|
let removeAsync = () => Promise.resolve(remove())
|
|
9
9
|
|
|
10
|
+
let clear = () => console.warn('expo-sqlite not installed. Neko Storage needs expo-sqlite to work properly')
|
|
11
|
+
let clearAsync = () => Promise.resolve(clear())
|
|
12
|
+
|
|
10
13
|
try {
|
|
11
14
|
const StorageModule = require('expo-sqlite/kv-store')
|
|
12
15
|
if (StorageModule?.default) {
|
|
@@ -19,6 +22,9 @@ try {
|
|
|
19
22
|
|
|
20
23
|
remove = Storage.removeItemSync.bind(Storage)
|
|
21
24
|
removeAsync = Storage.removeItem.bind(Storage)
|
|
25
|
+
|
|
26
|
+
clear = Storage.clearSync.bind(Storage)
|
|
27
|
+
clearAsync = Storage.clearAsync.bind(Storage)
|
|
22
28
|
}
|
|
23
29
|
} catch (e) {
|
|
24
30
|
console.log('expo-sqlite not available:', e)
|
|
@@ -31,4 +37,6 @@ export const AbsStorage = {
|
|
|
31
37
|
getAsync,
|
|
32
38
|
remove,
|
|
33
39
|
removeAsync,
|
|
40
|
+
clear,
|
|
41
|
+
clearAsync,
|
|
34
42
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { is } from 'ramda'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
|
|
4
|
+
import { AbsWindowOverlay } from '../../../abstractions/WindowOverlay'
|
|
4
5
|
import { Notification } from './Notification'
|
|
5
|
-
import { SafeAreaView } from '../../structure/SafeAreaView'
|
|
6
6
|
import { View } from '../../structure/View'
|
|
7
7
|
import { useResponsiveValue } from '../../../responsive/responsiveHooks'
|
|
8
|
+
import { useSafeAreaInsets } from '../../../abstractions/helpers/useSafeAreaInsets'
|
|
8
9
|
|
|
9
10
|
const NotificationsContext = React.createContext(null)
|
|
10
11
|
|
|
@@ -35,6 +36,7 @@ export function useNotifier() {
|
|
|
35
36
|
|
|
36
37
|
export function NotificationsHandler({ children }) {
|
|
37
38
|
const width = useResponsiveValue({ sm: '100%', df: 400 })
|
|
39
|
+
const insets = useSafeAreaInsets()
|
|
38
40
|
const [messages, setMessages] = React.useState([])
|
|
39
41
|
|
|
40
42
|
const add = React.useCallback((key, data) => {
|
|
@@ -52,13 +54,15 @@ export function NotificationsHandler({ children }) {
|
|
|
52
54
|
{children}
|
|
53
55
|
|
|
54
56
|
{!!messages.length && (
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
<AbsWindowOverlay>
|
|
58
|
+
<View fixed top={0} right={0} padding="md" width={width} maxWidth="100%" pointerEvents="box-none" zIndex={600}>
|
|
59
|
+
<View paddingT={insets.top} paddingR={insets.right} gap="xs" pointerEvents="box-none">
|
|
60
|
+
{messages.map(({ key, ...item }) => (
|
|
61
|
+
<Notification key={key} {...item} />
|
|
62
|
+
))}
|
|
63
|
+
</View>
|
|
64
|
+
</View>
|
|
65
|
+
</AbsWindowOverlay>
|
|
62
66
|
)}
|
|
63
67
|
</NotificationsContext.Provider>
|
|
64
68
|
)
|
|
@@ -64,11 +64,13 @@ export function useNewForm({ initialValues = {}, validate, onSubmit, onValuesCha
|
|
|
64
64
|
|
|
65
65
|
const isTouched = (name) => {
|
|
66
66
|
if (!name) return touchedRef.current.size > 0
|
|
67
|
+
if (Array.isArray(name)) return name.some((n) => touchedRef.current.has(toKey(n)))
|
|
67
68
|
return touchedRef.current.has(toKey(name))
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const isDirty = (name) => {
|
|
71
72
|
if (!name) return dirtyRef.current.size > 0
|
|
73
|
+
if (Array.isArray(name)) return name.some((n) => dirtyRef.current.has(toKey(n)))
|
|
72
74
|
return dirtyRef.current.has(toKey(name))
|
|
73
75
|
}
|
|
74
76
|
|
package/src/components/index.js
CHANGED
|
@@ -11,6 +11,7 @@ dayjs.extend(advancedFormat)
|
|
|
11
11
|
dayjs.extend(weekOfYear)
|
|
12
12
|
|
|
13
13
|
import { DatePicker } from './datePicker/DatePicker'
|
|
14
|
+
import { DateWheelPicker } from './dateWheelPicker/DateWheelPicker'
|
|
14
15
|
import { MaskInput } from './MaskInput'
|
|
15
16
|
import { Popover } from '../structure/popover/Popover'
|
|
16
17
|
import { isValidDate } from '../calendar/_helpers/dateDisabled'
|
|
@@ -46,6 +47,7 @@ export function DateInput({
|
|
|
46
47
|
onCheckDisabled,
|
|
47
48
|
placement,
|
|
48
49
|
type = 'day',
|
|
50
|
+
presentation = 'calendar',
|
|
49
51
|
format,
|
|
50
52
|
startsOpen,
|
|
51
53
|
allowClear,
|
|
@@ -85,26 +87,28 @@ export function DateInput({
|
|
|
85
87
|
setInputValue(!!value ? dayjs(value).format(format) : '')
|
|
86
88
|
}, [value])
|
|
87
89
|
|
|
90
|
+
const isWheel = presentation === 'wheel'
|
|
88
91
|
const Input = useBottomDrawer ? LinkInput : FullWidthInputWrapper
|
|
92
|
+
const Picker = isWheel ? DateWheelPicker : DatePicker
|
|
89
93
|
|
|
90
94
|
return (
|
|
91
95
|
<Popover
|
|
92
96
|
trigger="click"
|
|
93
97
|
startsOpen={startsOpen}
|
|
94
98
|
placement={placement || 'bottomLeft'}
|
|
95
|
-
snapPoints={[450]}
|
|
99
|
+
snapPoints={[isWheel ? 275 : 450]}
|
|
96
100
|
useBottomDrawer={useBottomDrawer}
|
|
97
101
|
bottomDrawerProps={{ contentProps: { padding: 'md' } }}
|
|
98
102
|
watch={[value?.format?.('YYYYMMDD')]}
|
|
99
103
|
renderContent={({ onClose }) => (
|
|
100
104
|
<View flex centerH onMouseDown={(e) => e.preventDefault()}>
|
|
101
|
-
<
|
|
105
|
+
<Picker
|
|
102
106
|
value={value}
|
|
103
107
|
onChange={(v) => {
|
|
104
108
|
handleChange(v)
|
|
105
|
-
onClose()
|
|
109
|
+
if (!isWheel) onClose()
|
|
106
110
|
}}
|
|
107
|
-
width={useBottomDrawer ? '100%' : 275}
|
|
111
|
+
width={!isWheel && (useBottomDrawer ? '100%' : 275)}
|
|
108
112
|
allowClear={allowClear}
|
|
109
113
|
{...validations}
|
|
110
114
|
type={type}
|
|
@@ -9,14 +9,14 @@ import { useDefaultModifier } from '../../modifiers/default'
|
|
|
9
9
|
import { useSizeConverter } from '../../modifiers/sizeConverter'
|
|
10
10
|
import { useThemeComponentModifier } from '../../modifiers/themeComponent'
|
|
11
11
|
|
|
12
|
-
const DEFAULT_PROPS = {
|
|
12
|
+
const DEFAULT_PROPS = ([{ sizeCode }]) => ({
|
|
13
13
|
paddingH: 'sm',
|
|
14
14
|
bg: 'overlayBG',
|
|
15
15
|
border: true,
|
|
16
|
-
br:
|
|
16
|
+
br: sizeCode,
|
|
17
17
|
row: true,
|
|
18
18
|
gap: 'sm',
|
|
19
|
-
}
|
|
19
|
+
})
|
|
20
20
|
|
|
21
21
|
export function InputWrapper({
|
|
22
22
|
prefix,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { BottomDrawer } from '../modals/bottomDrawer'
|
|
4
|
+
import { LinkInput } from './LinkInput'
|
|
5
|
+
import { NumberWheelPicker } from './NumberWheelPicker'
|
|
6
|
+
import { View } from '../structure/View'
|
|
7
|
+
|
|
8
|
+
export function NumberWheelInput({
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
11
|
+
min,
|
|
12
|
+
max,
|
|
13
|
+
step,
|
|
14
|
+
decimalStep,
|
|
15
|
+
useInt,
|
|
16
|
+
precision,
|
|
17
|
+
suffix,
|
|
18
|
+
...props
|
|
19
|
+
}) {
|
|
20
|
+
const [open, setOpen] = React.useState(false)
|
|
21
|
+
const [localValue, setLocalValue] = React.useState(value)
|
|
22
|
+
value = value ?? localValue
|
|
23
|
+
|
|
24
|
+
const handleChange = (v) => {
|
|
25
|
+
setLocalValue(v)
|
|
26
|
+
onChange?.(v)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const displayValue = value != null ? `${value}${suffix ? ` ${suffix}` : ''}` : ''
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<LinkInput value={displayValue} onPress={() => setOpen(true)} {...props} />
|
|
34
|
+
<BottomDrawer open={open} onClose={() => setOpen(false)} snapPoints={[275]} contentProps={{ padding: 'md' }}>
|
|
35
|
+
<View flex centerH>
|
|
36
|
+
<NumberWheelPicker
|
|
37
|
+
value={value}
|
|
38
|
+
onChange={handleChange}
|
|
39
|
+
min={min}
|
|
40
|
+
max={max}
|
|
41
|
+
step={step}
|
|
42
|
+
decimalStep={decimalStep}
|
|
43
|
+
useInt={useInt}
|
|
44
|
+
precision={precision}
|
|
45
|
+
/>
|
|
46
|
+
</View>
|
|
47
|
+
</BottomDrawer>
|
|
48
|
+
</>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Text } from '../text'
|
|
2
|
+
import { View } from '../structure'
|
|
3
|
+
import { WheelPicker } from './WheelPicker'
|
|
4
|
+
|
|
5
|
+
export function NumberWheelPicker({ value, onChange, min = 0, max = 100, step = 1, decimalStep = 1, useInt, precision = 2 }) {
|
|
6
|
+
if (useInt) precision = 0
|
|
7
|
+
|
|
8
|
+
const intValue = value != null ? Math.trunc(value) : min
|
|
9
|
+
const decFactor = Math.pow(10, precision)
|
|
10
|
+
const decValue = value != null ? Math.round((Math.abs(value) - Math.abs(intValue)) * decFactor) : 0
|
|
11
|
+
|
|
12
|
+
const handleIntChange = (v) => {
|
|
13
|
+
onChange?.(precision === 0 ? v : v + decValue / decFactor)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const handleDecChange = (v) => {
|
|
17
|
+
onChange?.(intValue + v / decFactor)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<View row gap="xs" centerV>
|
|
22
|
+
<View flex>
|
|
23
|
+
<WheelPicker range={[min, max]} step={step} value={intValue} onChange={handleIntChange} />
|
|
24
|
+
</View>
|
|
25
|
+
{precision > 0 && (
|
|
26
|
+
<>
|
|
27
|
+
<Text h3 strong>
|
|
28
|
+
.
|
|
29
|
+
</Text>
|
|
30
|
+
<View flex>
|
|
31
|
+
<WheelPicker
|
|
32
|
+
range={[0, decFactor - 1]}
|
|
33
|
+
step={decimalStep}
|
|
34
|
+
value={decValue}
|
|
35
|
+
onChange={handleDecChange}
|
|
36
|
+
formatLabel={(v) => String(v).padStart(precision, '0')}
|
|
37
|
+
/>
|
|
38
|
+
</View>
|
|
39
|
+
</>
|
|
40
|
+
)}
|
|
41
|
+
</View>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -111,7 +111,7 @@ function Content({ value, open, remove, isDragging, multiple, area, grid, maxCou
|
|
|
111
111
|
return (
|
|
112
112
|
<View row wrap gap="sm">
|
|
113
113
|
{items.map((item) => (
|
|
114
|
-
<GridItem key={item._id} value={item} remove={remove} />
|
|
114
|
+
<GridItem key={item._id ?? item.id} value={item} remove={remove} />
|
|
115
115
|
))}
|
|
116
116
|
{showAdd && <AddTile onPress={open} isDragging={isDragging} />}
|
|
117
117
|
</View>
|
|
@@ -129,7 +129,7 @@ function Content({ value, open, remove, isDragging, multiple, area, grid, maxCou
|
|
|
129
129
|
<View gap="xs">
|
|
130
130
|
{showAdd && link}
|
|
131
131
|
{items.map((item) => (
|
|
132
|
-
<ListItem key={item._id} value={item} remove={remove} />
|
|
132
|
+
<ListItem key={item._id ?? item.id} value={item} remove={remove} />
|
|
133
133
|
))}
|
|
134
134
|
</View>
|
|
135
135
|
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { NumberInput } from './NumberInput'
|
|
2
|
+
|
|
3
|
+
export function WheelPicker({
|
|
4
|
+
value,
|
|
5
|
+
onChange,
|
|
6
|
+
options,
|
|
7
|
+
suffix,
|
|
8
|
+
range,
|
|
9
|
+
step = 1,
|
|
10
|
+
labelKey = 'label',
|
|
11
|
+
valueKey = 'value',
|
|
12
|
+
useRawOption,
|
|
13
|
+
formatLabel,
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
let min = 0
|
|
17
|
+
let max = 100
|
|
18
|
+
|
|
19
|
+
if (!!range || !options) {
|
|
20
|
+
const [from, to] = range || [0, 100]
|
|
21
|
+
const count = Math.floor((to - from) / step) + 1
|
|
22
|
+
options = Array.from({ length: count }, (_, i) => {
|
|
23
|
+
const v = from + i * step
|
|
24
|
+
return { [labelKey]: formatLabel ? formatLabel(v) : v, [valueKey]: v }
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const values = (options || []).map((o) => o[valueKey]).filter((v) => typeof v === 'number')
|
|
29
|
+
if (values.length) {
|
|
30
|
+
min = Math.min(...values)
|
|
31
|
+
max = Math.max(...values)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleChange(newValue) {
|
|
35
|
+
if (useRawOption) {
|
|
36
|
+
// NumberInput allows free typing — snap to the nearest option on miss
|
|
37
|
+
const match = options.find((o) => o[valueKey] === newValue)
|
|
38
|
+
return onChange(
|
|
39
|
+
match ||
|
|
40
|
+
options.reduce((best, o) =>
|
|
41
|
+
Math.abs(o[valueKey] - newValue) < Math.abs(best[valueKey] - newValue) ? o : best
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
onChange(newValue)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <NumberInput value={value} onChange={handleChange} suffix={suffix} min={min} max={max} precision={0} {...props} />
|
|
49
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Text } from '../text'
|
|
4
|
+
import { View } from '../structure'
|
|
5
|
+
import { useColors } from '../../theme'
|
|
6
|
+
import { debounce } from '../../helpers/debounce'
|
|
7
|
+
|
|
8
|
+
let QuidoneWheelPicker
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
QuidoneWheelPicker = require('@quidone/react-native-wheel-picker').default
|
|
12
|
+
} catch {
|
|
13
|
+
QuidoneWheelPicker = null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function WheelPicker({
|
|
17
|
+
value,
|
|
18
|
+
onChange,
|
|
19
|
+
options,
|
|
20
|
+
suffix,
|
|
21
|
+
range,
|
|
22
|
+
step = 1,
|
|
23
|
+
labelKey = 'label',
|
|
24
|
+
valueKey = 'value',
|
|
25
|
+
useRawOption,
|
|
26
|
+
formatLabel,
|
|
27
|
+
...props
|
|
28
|
+
}) {
|
|
29
|
+
const colors = useColors()
|
|
30
|
+
const handleChange = React.useMemo(() => debounce(onChange, 300), [onChange])
|
|
31
|
+
|
|
32
|
+
if (!!range || !options) {
|
|
33
|
+
const [from, to] = range || [0, 100]
|
|
34
|
+
const count = Math.floor((to - from) / step) + 1
|
|
35
|
+
options = Array.from({ length: count }, (_, i) => {
|
|
36
|
+
const v = from + i * step
|
|
37
|
+
return { [labelKey]: formatLabel ? formatLabel(v) : v, [valueKey]: v }
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = React.useMemo(() => {
|
|
42
|
+
if (!options) return []
|
|
43
|
+
return options.map((opt) => ({
|
|
44
|
+
value: opt[valueKey],
|
|
45
|
+
label: String(opt[labelKey] ?? opt[valueKey]),
|
|
46
|
+
}))
|
|
47
|
+
}, [options, labelKey, valueKey])
|
|
48
|
+
|
|
49
|
+
if (!QuidoneWheelPicker) {
|
|
50
|
+
console.warn('@quidone/react-native-wheel-picker not installed.')
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const resolveValue = (item) => {
|
|
55
|
+
if (useRawOption) return options.find((o) => o[valueKey] === item.value)
|
|
56
|
+
return item.value
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<View relative hiddenOverflow>
|
|
61
|
+
{!!suffix && (
|
|
62
|
+
<View absoluteFill row centerV>
|
|
63
|
+
<View flex />
|
|
64
|
+
<View flex paddingL={20}>
|
|
65
|
+
<Text text2>{suffix}</Text>
|
|
66
|
+
</View>
|
|
67
|
+
</View>
|
|
68
|
+
)}
|
|
69
|
+
<QuidoneWheelPicker
|
|
70
|
+
data={data}
|
|
71
|
+
value={value}
|
|
72
|
+
itemTextStyle={{ color: colors.text }}
|
|
73
|
+
itemHeight={40}
|
|
74
|
+
overlayItemStyle={{
|
|
75
|
+
backgroundColor: 'transparent',
|
|
76
|
+
borderWidth: 1,
|
|
77
|
+
borderLeftWidth: 0,
|
|
78
|
+
borderRightWidth: 0,
|
|
79
|
+
borderColor: colors.divider,
|
|
80
|
+
opacity: 1,
|
|
81
|
+
}}
|
|
82
|
+
_onValueChanging={({ item }) => handleChange(resolveValue(item))}
|
|
83
|
+
onValueChanged={({ item }) => onChange(resolveValue(item))}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
</View>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WheelPicker } from './WheelPicker.native'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DayWheelPicker } from './DayWheelPicker'
|
|
2
|
+
import { MonthWheelPicker } from './MonthWheelPicker'
|
|
3
|
+
import { QuarterWheelPicker } from './QuarterWheelPicker'
|
|
4
|
+
import { WeekWheelPicker } from './WeekWheelPicker'
|
|
5
|
+
import { YearWheelPicker } from './YearWheelPicker'
|
|
6
|
+
|
|
7
|
+
export function DateWheelPicker({ type, ...props }) {
|
|
8
|
+
switch (type) {
|
|
9
|
+
case 'year':
|
|
10
|
+
return <YearWheelPicker {...props} />
|
|
11
|
+
|
|
12
|
+
case 'quarter':
|
|
13
|
+
return <QuarterWheelPicker {...props} />
|
|
14
|
+
|
|
15
|
+
case 'month':
|
|
16
|
+
return <MonthWheelPicker {...props} />
|
|
17
|
+
|
|
18
|
+
case 'week':
|
|
19
|
+
return <WeekWheelPicker {...props} />
|
|
20
|
+
|
|
21
|
+
default:
|
|
22
|
+
return <DayWheelPicker {...props} />
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
|
|
3
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
4
|
+
import { View } from '../../structure'
|
|
5
|
+
import { useColors } from '../../../theme'
|
|
6
|
+
|
|
7
|
+
let DatePicker
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
DatePicker = require('@quidone/react-native-wheel-picker').DatePicker
|
|
11
|
+
} catch {
|
|
12
|
+
DatePicker = null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function DayWheelPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
16
|
+
const colors = useColors()
|
|
17
|
+
if (!DatePicker) {
|
|
18
|
+
console.warn('@quidone/react-native-wheel-picker not installed.')
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
value = value ? dayjs(value) : dayjs()
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<View center height={40 * 5} overflow="hidden" relative>
|
|
26
|
+
<DatePicker
|
|
27
|
+
locale={'pt'}
|
|
28
|
+
date={value.format('YYYY-MM-DD')}
|
|
29
|
+
minDate={min ? dayjs(min).format('YYYY-MM-DD') : undefined}
|
|
30
|
+
maxDate={max ? dayjs(max).format('YYYY-MM-DD') : undefined}
|
|
31
|
+
onDateChanged={({ date }) => onChange(dayjs(date))}
|
|
32
|
+
itemTextStyle={{ color: colors.text }}
|
|
33
|
+
itemHeight={40}
|
|
34
|
+
overlayItemStyle={{
|
|
35
|
+
backgroundColor: 'transparent',
|
|
36
|
+
borderWidth: 1,
|
|
37
|
+
borderLeftWidth: 0,
|
|
38
|
+
borderRightWidth: 0,
|
|
39
|
+
borderColor: colors.divider,
|
|
40
|
+
opacity: 1,
|
|
41
|
+
}}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
46
|
+
</View>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { View } from '../../structure'
|
|
2
|
+
import { DayWheelPicker } from './DayWheelPicker'
|
|
3
|
+
|
|
4
|
+
let DatePickerDate
|
|
5
|
+
try {
|
|
6
|
+
DatePickerDate = require('@quidone/react-native-wheel-picker').DatePicker.Date
|
|
7
|
+
} catch {
|
|
8
|
+
DatePickerDate = null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const renderHiddenDate = () => (
|
|
12
|
+
<View width={0} hiddenOverflow pointerEvents="none">
|
|
13
|
+
{DatePickerDate ? <DatePickerDate /> : null}
|
|
14
|
+
</View>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
export function MonthWheelPicker({ value, ...props }) {
|
|
18
|
+
return <DayWheelPicker {...props} value={value?.startOf?.('month') ?? value} renderDate={renderHiddenDate} />
|
|
19
|
+
}
|