@neko-os/ui 0.4.0 → 0.5.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.
- package/dist/abstractions/KeyboardDismissView.js +3 -0
- package/dist/abstractions/KeyboardDismissView.native.js +9 -0
- package/dist/abstractions/index.js +1 -0
- package/dist/components/actions/ClearLink.js +6 -0
- package/dist/components/actions/FloatingMenu.js +1 -1
- package/dist/components/calendar/CalendarNav.js +6 -6
- package/dist/components/carousel/Carousel.js +48 -0
- package/dist/components/carousel/CarouselArrows.js +40 -0
- package/dist/components/carousel/CarouselArrows.native.js +40 -0
- package/dist/components/carousel/CarouselDots.js +32 -0
- package/dist/components/carousel/CarouselDots.native.js +36 -0
- package/dist/components/carousel/CarouselHandler.js +86 -0
- package/dist/components/carousel/CarouselSlider.js +124 -0
- package/dist/components/carousel/CarouselSlider.native.js +121 -0
- package/dist/components/carousel/InfiniteCarousel.js +50 -0
- package/dist/components/carousel/index.js +6 -0
- package/dist/components/form/Form.js +5 -3
- package/dist/components/index.js +3 -1
- package/dist/components/inputs/DateInput.js +7 -4
- package/dist/components/inputs/InputWrapper.js +1 -2
- package/dist/components/inputs/Select.js +56 -52
- package/dist/components/inputs/TextInput.js +7 -6
- package/dist/components/inputs/datePicker/DayPicker.js +71 -23
- package/dist/components/inputs/datePicker/MonthPicker.js +61 -32
- package/dist/components/inputs/datePicker/QuarterPicker.js +62 -33
- package/dist/components/inputs/datePicker/WeekPicker.js +65 -24
- package/dist/components/inputs/datePicker/YearPicker.js +69 -40
- package/dist/components/keyboard/KeyboardDismissButton.js +3 -0
- package/dist/components/keyboard/KeyboardDismissButton.native.js +38 -0
- package/dist/components/keyboard/index.js +1 -0
- package/dist/components/modals/bottomDrawer/native/BottomDrawer.js +28 -7
- package/dist/components/presentation/LabelValue.js +1 -1
- package/dist/components/presentation/Result.js +11 -3
- package/dist/components/structure/KeyboardAvoidingView.js +9 -2
- package/dist/components/theme/ThemePicker.js +7 -12
- package/dist/components/theme/ThemeThumb.js +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/ThemeHandler.js +31 -3
- package/package.json +1 -1
- package/src/abstractions/KeyboardDismissView.js +3 -0
- package/src/abstractions/KeyboardDismissView.native.js +9 -0
- package/src/abstractions/index.js +1 -0
- package/src/components/actions/ClearLink.js +6 -0
- package/src/components/actions/FloatingMenu.js +1 -1
- package/src/components/calendar/CalendarNav.js +6 -6
- package/src/components/carousel/Carousel.js +48 -0
- package/src/components/carousel/CarouselArrows.js +40 -0
- package/src/components/carousel/CarouselArrows.native.js +40 -0
- package/src/components/carousel/CarouselDots.js +32 -0
- package/src/components/carousel/CarouselDots.native.js +36 -0
- package/src/components/carousel/CarouselHandler.js +86 -0
- package/src/components/carousel/CarouselSlider.js +124 -0
- package/src/components/carousel/CarouselSlider.native.js +121 -0
- package/src/components/carousel/InfiniteCarousel.js +50 -0
- package/src/components/carousel/index.js +6 -0
- package/src/components/form/Form.js +2 -0
- package/src/components/index.js +2 -0
- package/src/components/inputs/DateInput.js +4 -1
- package/src/components/inputs/InputWrapper.js +1 -2
- package/src/components/inputs/Select.js +19 -15
- package/src/components/inputs/TextInput.js +5 -4
- package/src/components/inputs/datePicker/DayPicker.js +69 -21
- package/src/components/inputs/datePicker/MonthPicker.js +60 -31
- package/src/components/inputs/datePicker/QuarterPicker.js +60 -31
- package/src/components/inputs/datePicker/WeekPicker.js +63 -22
- package/src/components/inputs/datePicker/YearPicker.js +68 -39
- package/src/components/keyboard/KeyboardDismissButton.js +3 -0
- package/src/components/keyboard/KeyboardDismissButton.native.js +38 -0
- package/src/components/keyboard/index.js +1 -0
- package/src/components/modals/bottomDrawer/native/BottomDrawer.js +27 -6
- package/src/components/presentation/LabelValue.js +1 -1
- package/src/components/presentation/Result.js +10 -2
- package/src/components/structure/KeyboardAvoidingView.js +9 -2
- package/src/components/theme/ThemePicker.js +8 -13
- package/src/components/theme/ThemeThumb.js +1 -1
- package/src/index.js +1 -0
- package/src/theme/ThemeHandler.js +31 -3
|
@@ -3,9 +3,11 @@ import dayjs from 'dayjs'
|
|
|
3
3
|
import quarterOfYear from 'dayjs/esm/plugin/quarterOfYear'
|
|
4
4
|
|
|
5
5
|
import { CalendarNav } from '../../calendar/CalendarNav'
|
|
6
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
6
7
|
import { Col } from '../../structure/Col'
|
|
7
8
|
import { Divider } from '../../helpers'
|
|
8
9
|
import { Grid } from '../../structure/Row'
|
|
10
|
+
import { InfiniteCarousel } from '../../carousel/InfiniteCarousel'
|
|
9
11
|
import { Link } from '../../actions/Link'
|
|
10
12
|
import { Text } from '../../text/Text'
|
|
11
13
|
import { View } from '../../structure/View'
|
|
@@ -15,7 +17,39 @@ dayjs.extend(quarterOfYear)
|
|
|
15
17
|
|
|
16
18
|
const quarters = [1, 2, 3, 4]
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
const QuarterGrid = React.memo(function QuarterGrid({ year, selectedKey, onSelect, min, max, onCheckDisabled }) {
|
|
21
|
+
const yearDate = dayjs().year(year).startOf('year')
|
|
22
|
+
const selectedValue = selectedKey ? dayjs(selectedKey) : null
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Grid colSpan={6} gap="xs">
|
|
26
|
+
{quarters.map((quarter) => {
|
|
27
|
+
const dateVal = yearDate.quarter(quarter)
|
|
28
|
+
const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'quarter')
|
|
29
|
+
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Col key={quarter}>
|
|
33
|
+
<Link
|
|
34
|
+
fullW
|
|
35
|
+
br="md"
|
|
36
|
+
padding="sm"
|
|
37
|
+
onPress={() => onSelect(dateVal)}
|
|
38
|
+
bg={isActive && 'primary'}
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
>
|
|
41
|
+
<Text text2={!isActive} strong={isActive} center>
|
|
42
|
+
Q{dateVal.quarter()}
|
|
43
|
+
</Text>
|
|
44
|
+
</Link>
|
|
45
|
+
</Col>
|
|
46
|
+
)
|
|
47
|
+
})}
|
|
48
|
+
</Grid>
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export function QuarterPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
19
53
|
const [localValue, setLocalValue] = React.useState(value)
|
|
20
54
|
const [currentYear, setCurrentYear] = React.useState(() => dayjs(value || undefined).startOf('year'))
|
|
21
55
|
value = value === undefined ? localValue : value
|
|
@@ -25,41 +59,36 @@ export function QuarterPicker({ value, onChange, min, max, onCheckDisabled, ...p
|
|
|
25
59
|
if (value?.isValid?.()) setCurrentYear(value.startOf('year'))
|
|
26
60
|
}, [value?.quarter?.(), value?.year?.()])
|
|
27
61
|
|
|
28
|
-
const handleChange = (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
62
|
+
const handleChange = React.useCallback(
|
|
63
|
+
(v) => {
|
|
64
|
+
const newValue = v.startOf('quarter')
|
|
65
|
+
setLocalValue(newValue)
|
|
66
|
+
onChange?.(newValue)
|
|
67
|
+
},
|
|
68
|
+
[onChange]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const yearValue = currentYear.year()
|
|
72
|
+
const minYear = min ? dayjs(min).year() : undefined
|
|
73
|
+
const maxYear = max ? dayjs(max).year() : undefined
|
|
74
|
+
const selectedKey = value?.valueOf?.()
|
|
75
|
+
|
|
76
|
+
const renderSlide = (v) => (
|
|
77
|
+
<QuarterGrid year={v} selectedKey={selectedKey} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
78
|
+
)
|
|
33
79
|
|
|
34
80
|
return (
|
|
35
81
|
<View className="neko-day-picker" width={275} {...props}>
|
|
36
82
|
<CalendarNav value={currentYear} onChange={setCurrentYear} level="year" />
|
|
37
83
|
<Divider />
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<Col key={quarter}>
|
|
47
|
-
<Link
|
|
48
|
-
fullW
|
|
49
|
-
br="md"
|
|
50
|
-
padding="sm"
|
|
51
|
-
onPress={() => handleChange(dateVal)}
|
|
52
|
-
bg={isActive && 'primary'}
|
|
53
|
-
disabled={disabled}
|
|
54
|
-
>
|
|
55
|
-
<Text text2={!isActive} strong={isActive} center>
|
|
56
|
-
Q{dateVal.quarter()}
|
|
57
|
-
</Text>
|
|
58
|
-
</Link>
|
|
59
|
-
</Col>
|
|
60
|
-
)
|
|
61
|
-
})}
|
|
62
|
-
</Grid>
|
|
84
|
+
<InfiniteCarousel
|
|
85
|
+
value={yearValue}
|
|
86
|
+
onChange={(v) => setCurrentYear(dayjs().year(v).startOf('year'))}
|
|
87
|
+
renderSlide={renderSlide}
|
|
88
|
+
min={minYear}
|
|
89
|
+
max={maxYear}
|
|
90
|
+
/>
|
|
91
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
63
92
|
</View>
|
|
64
93
|
)
|
|
65
94
|
}
|
|
@@ -3,7 +3,9 @@ import React from 'react'
|
|
|
3
3
|
import dayjs from 'dayjs'
|
|
4
4
|
|
|
5
5
|
import { CalendarNav } from '../../calendar/CalendarNav'
|
|
6
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
6
7
|
import { Col } from '../../structure/Col'
|
|
8
|
+
import { InfiniteCarousel } from '../../carousel/InfiniteCarousel'
|
|
7
9
|
import { Link } from '../../actions/Link'
|
|
8
10
|
import { Row } from '../../structure/Row'
|
|
9
11
|
import { Text } from '../../text/Text'
|
|
@@ -12,35 +14,31 @@ import { WeekDaysBar } from '../../calendar/WeekDaysBar'
|
|
|
12
14
|
import { isDateDisabled } from '../../calendar/_helpers/dateDisabled'
|
|
13
15
|
import { useCalendarDays } from '../../calendar/_helpers/calendarDays'
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
value = value === undefined ? localValue : value
|
|
19
|
-
|
|
20
|
-
React.useEffect(() => {
|
|
21
|
-
setLocalValue(value)
|
|
22
|
-
if (value?.isValid?.()) setCurrentMonth(value.startOf('month'))
|
|
23
|
-
}, [value?.day?.(), value?.month?.(), value?.year?.()])
|
|
17
|
+
function toMonthValue(date) {
|
|
18
|
+
return date.year() * 12 + date.month()
|
|
19
|
+
}
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
function fromMonthValue(v) {
|
|
22
|
+
return dayjs()
|
|
23
|
+
.year(Math.floor(v / 12))
|
|
24
|
+
.month(v % 12)
|
|
25
|
+
.startOf('month')
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
const MonthWeeks = React.memo(function MonthWeeks({ monthValue, selectedKey, onSelect, min, max, onCheckDisabled }) {
|
|
29
|
+
const month = fromMonthValue(monthValue)
|
|
30
|
+
const selectedValue = selectedKey ? dayjs(selectedKey) : null
|
|
31
|
+
const { cells } = useCalendarDays(month)
|
|
32
32
|
const weeks = splitEvery(7, cells)
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<View
|
|
36
|
-
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
35
|
+
<View>
|
|
37
36
|
<WeekDaysBar />
|
|
38
|
-
|
|
39
37
|
<View colSpan={24 / 7} gap="sm">
|
|
40
38
|
{weeks.map((week, wi) => {
|
|
41
39
|
const firstDay = week.find(identity)
|
|
42
|
-
const dateVal =
|
|
43
|
-
const isActive = !!
|
|
40
|
+
const dateVal = month.date(firstDay)
|
|
41
|
+
const isActive = !!selectedValue && !!firstDay && dateVal.isSame(selectedValue, 'week')
|
|
44
42
|
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
45
43
|
|
|
46
44
|
return (
|
|
@@ -48,13 +46,13 @@ export function WeekPicker({ value, onChange, min, max, onCheckDisabled, ...prop
|
|
|
48
46
|
key={firstDay ? dateVal.format('YYYYMMDD') : wi}
|
|
49
47
|
fullW
|
|
50
48
|
br="md"
|
|
51
|
-
onPress={() => !!firstDay &&
|
|
49
|
+
onPress={() => !!firstDay && onSelect(dateVal)}
|
|
52
50
|
bg={isActive && 'primary'}
|
|
53
51
|
disabled={disabled}
|
|
54
52
|
>
|
|
55
53
|
<Row colSpan={24 / 7} gap="sm">
|
|
56
54
|
{week.map((day, i) => {
|
|
57
|
-
const dateVal =
|
|
55
|
+
const dateVal = month.date(day)
|
|
58
56
|
|
|
59
57
|
return (
|
|
60
58
|
<Col key={day ? dateVal.format('YYYYMMDD') : i} className="day-cell" center ratio={1}>
|
|
@@ -71,4 +69,47 @@ export function WeekPicker({ value, onChange, min, max, onCheckDisabled, ...prop
|
|
|
71
69
|
</View>
|
|
72
70
|
</View>
|
|
73
71
|
)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export function WeekPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
75
|
+
const [localValue, setLocalValue] = React.useState(value)
|
|
76
|
+
const [currentMonth, setCurrentMonth] = React.useState(() => dayjs(value || undefined).startOf('month'))
|
|
77
|
+
value = value === undefined ? localValue : value
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
setLocalValue(value)
|
|
81
|
+
if (value?.isValid?.()) setCurrentMonth(value.startOf('month'))
|
|
82
|
+
}, [value?.day?.(), value?.month?.(), value?.year?.()])
|
|
83
|
+
|
|
84
|
+
const handleChange = React.useCallback(
|
|
85
|
+
(v) => {
|
|
86
|
+
const newValue = v.startOf('week')
|
|
87
|
+
setLocalValue(newValue)
|
|
88
|
+
onChange?.(newValue)
|
|
89
|
+
},
|
|
90
|
+
[onChange]
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const monthValue = toMonthValue(currentMonth)
|
|
94
|
+
const minMonth = min ? toMonthValue(dayjs(min).startOf('month')) : undefined
|
|
95
|
+
const maxMonth = max ? toMonthValue(dayjs(max).startOf('month')) : undefined
|
|
96
|
+
const selectedKey = value?.valueOf?.()
|
|
97
|
+
|
|
98
|
+
const renderSlide = (v) => (
|
|
99
|
+
<MonthWeeks monthValue={v} selectedKey={selectedKey} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View className="neko-day-picker" width={275} {...props}>
|
|
104
|
+
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
105
|
+
<InfiniteCarousel
|
|
106
|
+
value={monthValue}
|
|
107
|
+
onChange={(v) => setCurrentMonth(fromMonthValue(v))}
|
|
108
|
+
renderSlide={renderSlide}
|
|
109
|
+
min={minMonth}
|
|
110
|
+
max={maxMonth}
|
|
111
|
+
/>
|
|
112
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
113
|
+
</View>
|
|
114
|
+
)
|
|
74
115
|
}
|
|
@@ -3,68 +3,97 @@ import React from 'react'
|
|
|
3
3
|
import dayjs from 'dayjs'
|
|
4
4
|
|
|
5
5
|
import { CalendarNav } from '../../calendar/CalendarNav'
|
|
6
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
6
7
|
import { Col } from '../../structure/Col'
|
|
7
8
|
import { Divider } from '../../helpers'
|
|
8
9
|
import { Grid } from '../../structure/Row'
|
|
10
|
+
import { InfiniteCarousel } from '../../carousel/InfiniteCarousel'
|
|
9
11
|
import { Link } from '../../actions/Link'
|
|
10
12
|
import { Text } from '../../text/Text'
|
|
11
13
|
import { View } from '../../structure/View'
|
|
12
14
|
import { isDateDisabled } from '../../calendar/_helpers/dateDisabled'
|
|
13
15
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
const decadeStartYear = Math.floor(year / 10) * 10
|
|
17
|
-
return dayjs().year(decadeStartYear).startOf('year')
|
|
16
|
+
function getDecadeIndex(value) {
|
|
17
|
+
return Math.floor(dayjs(value || undefined).year() / 10)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
function decadeFromIndex(i) {
|
|
21
|
+
return dayjs().year(i * 10).startOf('year')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DecadeGrid = React.memo(function DecadeGrid({ decadeIndex, selectedKey, onSelect, min, max, onCheckDisabled }) {
|
|
25
|
+
const decadeStart = decadeFromIndex(decadeIndex)
|
|
26
|
+
const selectedValue = selectedKey ? dayjs(selectedKey) : null
|
|
27
|
+
const years = range(decadeStart.year(), decadeStart.year() + 10)
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Grid colSpan={12} gap="xs">
|
|
31
|
+
{years.map((year) => {
|
|
32
|
+
const dateVal = decadeStart.year(year)
|
|
33
|
+
const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'year')
|
|
34
|
+
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Col key={year}>
|
|
38
|
+
<Link
|
|
39
|
+
fullW
|
|
40
|
+
br="md"
|
|
41
|
+
padding="sm"
|
|
42
|
+
onPress={() => onSelect(dateVal)}
|
|
43
|
+
bg={isActive && 'primary'}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
>
|
|
46
|
+
<Text text2={!isActive} strong={isActive} center>
|
|
47
|
+
{dateVal.year()}
|
|
48
|
+
</Text>
|
|
49
|
+
</Link>
|
|
50
|
+
</Col>
|
|
51
|
+
)
|
|
52
|
+
})}
|
|
53
|
+
</Grid>
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
export function YearPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
21
58
|
const [localValue, setLocalValue] = React.useState(value)
|
|
22
|
-
const [currentDecade, setCurrentDecade] = React.useState(() =>
|
|
59
|
+
const [currentDecade, setCurrentDecade] = React.useState(() => getDecadeIndex(value))
|
|
23
60
|
|
|
24
61
|
value = value === undefined ? localValue : value
|
|
25
62
|
|
|
26
63
|
React.useEffect(() => {
|
|
27
64
|
setLocalValue(value)
|
|
28
|
-
if (value?.isValid?.()) setCurrentDecade(
|
|
65
|
+
if (value?.isValid?.()) setCurrentDecade(getDecadeIndex(value))
|
|
29
66
|
}, [value?.year?.()])
|
|
30
67
|
|
|
31
|
-
const handleChange = (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
const handleChange = React.useCallback(
|
|
69
|
+
(v) => {
|
|
70
|
+
const newValue = v.startOf('year')
|
|
71
|
+
setLocalValue(newValue)
|
|
72
|
+
onChange?.(newValue)
|
|
73
|
+
},
|
|
74
|
+
[onChange]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const minDecade = min ? getDecadeIndex(min) : undefined
|
|
78
|
+
const maxDecade = max ? getDecadeIndex(max) : undefined
|
|
79
|
+
const selectedKey = value?.valueOf?.()
|
|
36
80
|
|
|
37
|
-
const
|
|
81
|
+
const renderSlide = (v) => (
|
|
82
|
+
<DecadeGrid decadeIndex={v} selectedKey={selectedKey} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
83
|
+
)
|
|
38
84
|
|
|
39
85
|
return (
|
|
40
86
|
<View className="neko-day-picker" width={275} {...props}>
|
|
41
|
-
<CalendarNav value={currentDecade} onChange={setCurrentDecade} level="decade" />
|
|
87
|
+
<CalendarNav value={decadeFromIndex(currentDecade)} onChange={(v) => setCurrentDecade(getDecadeIndex(v))} level="decade" />
|
|
42
88
|
<Divider />
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<Col key={year}>
|
|
52
|
-
<Link
|
|
53
|
-
fullW
|
|
54
|
-
br="md"
|
|
55
|
-
padding="sm"
|
|
56
|
-
onPress={() => handleChange(dateVal)}
|
|
57
|
-
bg={isActive && 'primary'}
|
|
58
|
-
disabled={disabled}
|
|
59
|
-
>
|
|
60
|
-
<Text text2={!isActive} strong={isActive} center>
|
|
61
|
-
{dateVal.year()}
|
|
62
|
-
</Text>
|
|
63
|
-
</Link>
|
|
64
|
-
</Col>
|
|
65
|
-
)
|
|
66
|
-
})}
|
|
67
|
-
</Grid>
|
|
89
|
+
<InfiniteCarousel
|
|
90
|
+
value={currentDecade}
|
|
91
|
+
onChange={setCurrentDecade}
|
|
92
|
+
renderSlide={renderSlide}
|
|
93
|
+
min={minDecade}
|
|
94
|
+
max={maxDecade}
|
|
95
|
+
/>
|
|
96
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
68
97
|
</View>
|
|
69
98
|
)
|
|
70
99
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Keyboard } from 'react-native'
|
|
2
|
+
import Animated, { useAnimatedKeyboard, useAnimatedStyle, KeyboardState, withTiming } from 'react-native-reanimated'
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
|
+
|
|
5
|
+
import { Button } from '../actions'
|
|
6
|
+
|
|
7
|
+
const STATIC_STYLE = { position: 'absolute', right: 5, zIndex: 1000 }
|
|
8
|
+
|
|
9
|
+
export function KeyboardDismissButton() {
|
|
10
|
+
const keyboard = useAnimatedKeyboard()
|
|
11
|
+
const { bottom: bottomInset } = useSafeAreaInsets()
|
|
12
|
+
|
|
13
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
14
|
+
const isVisible = keyboard.height.value > 0
|
|
15
|
+
return {
|
|
16
|
+
bottom: keyboard.height.value - bottomInset + 10,
|
|
17
|
+
opacity: keyboard.state.value === KeyboardState.CLOSING ? withTiming(0, { duration: 150 }) : isVisible ? 1 : 0,
|
|
18
|
+
pointerEvents: isVisible ? 'auto' : 'none',
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Animated.View style={[STATIC_STYLE, animatedStyle]}>
|
|
24
|
+
<Button
|
|
25
|
+
icon="arrow-down-box-line"
|
|
26
|
+
onPress={Keyboard.dismiss}
|
|
27
|
+
ratio={1}
|
|
28
|
+
shadow
|
|
29
|
+
overlayBG
|
|
30
|
+
sm
|
|
31
|
+
opacity={0.85}
|
|
32
|
+
iconProps={{ size: 'md' }}
|
|
33
|
+
border
|
|
34
|
+
borderColor="divider"
|
|
35
|
+
/>
|
|
36
|
+
</Animated.View>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './KeyboardDismissButton'
|
|
@@ -4,6 +4,8 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
|
4
4
|
import Animated, {
|
|
5
5
|
useSharedValue,
|
|
6
6
|
useAnimatedStyle,
|
|
7
|
+
useAnimatedKeyboard,
|
|
8
|
+
KeyboardState,
|
|
7
9
|
withSpring,
|
|
8
10
|
runOnJS,
|
|
9
11
|
} from 'react-native-reanimated'
|
|
@@ -27,6 +29,7 @@ function InnerContent({
|
|
|
27
29
|
enableOverScroll = true,
|
|
28
30
|
enableHandlePanningGesture = true,
|
|
29
31
|
enableContentPanningGesture = true,
|
|
32
|
+
keyboardBehavior = 'interactive',
|
|
30
33
|
animationConfig = {
|
|
31
34
|
damping: 50,
|
|
32
35
|
stiffness: 500,
|
|
@@ -44,6 +47,7 @@ function InnerContent({
|
|
|
44
47
|
const bottomInset = useSafeArea ? insets.bottom : 0
|
|
45
48
|
|
|
46
49
|
const colors = useColors()
|
|
50
|
+
const keyboard = useAnimatedKeyboard()
|
|
47
51
|
|
|
48
52
|
const translateY = useSharedValue(SCREEN_HEIGHT)
|
|
49
53
|
const snapIndex = useSharedValue(0)
|
|
@@ -122,11 +126,9 @@ function InnerContent({
|
|
|
122
126
|
const currentPosition = SCREEN_HEIGHT - translateY.value
|
|
123
127
|
const shouldClose =
|
|
124
128
|
!!handleClose &&
|
|
125
|
-
(
|
|
126
|
-
velocityY.value > 1500 ||
|
|
129
|
+
(velocityY.value > 1500 ||
|
|
127
130
|
(velocityY.value > 800 && currentPosition < minSnapPoint) ||
|
|
128
|
-
currentPosition < minSnapPoint * 0.35
|
|
129
|
-
)
|
|
131
|
+
currentPosition < minSnapPoint * 0.35)
|
|
130
132
|
|
|
131
133
|
if (shouldClose) {
|
|
132
134
|
runOnJS(handleClose)()
|
|
@@ -148,10 +150,28 @@ function InnerContent({
|
|
|
148
150
|
handleClose,
|
|
149
151
|
])
|
|
150
152
|
|
|
153
|
+
const topInset = insets.top
|
|
151
154
|
const animatedSheetStyle = useAnimatedStyle(() => {
|
|
152
|
-
|
|
155
|
+
let kbShift = 0
|
|
156
|
+
|
|
157
|
+
const kbVisible = keyboard.state.value === KeyboardState.OPEN || keyboard.state.value === KeyboardState.OPENING
|
|
158
|
+
if (keyboardBehavior !== 'none' && kbVisible) {
|
|
159
|
+
const kbHeight = keyboard.height.value
|
|
160
|
+
|
|
161
|
+
if (keyboardBehavior === 'interactive') {
|
|
162
|
+
// Shift up by keyboard height, but don't go above safe area
|
|
163
|
+
const maxShift = Math.max(0, translateY.value - topInset)
|
|
164
|
+
kbShift = Math.min(kbHeight, maxShift)
|
|
165
|
+
} else if (keyboardBehavior === 'extend') {
|
|
166
|
+
// Snap to fill available space above keyboard
|
|
167
|
+
kbShift = Math.max(0, translateY.value - topInset)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const adjustedY = translateY.value - kbShift
|
|
172
|
+
const currentHeight = SCREEN_HEIGHT - adjustedY
|
|
153
173
|
return {
|
|
154
|
-
transform: [{ translateY:
|
|
174
|
+
transform: [{ translateY: adjustedY }],
|
|
155
175
|
maxHeight: currentHeight,
|
|
156
176
|
}
|
|
157
177
|
})
|
|
@@ -200,6 +220,7 @@ function InnerContent({
|
|
|
200
220
|
marginR="auto"
|
|
201
221
|
fullW
|
|
202
222
|
{...props}
|
|
223
|
+
minH={minSnapPoint}
|
|
203
224
|
>
|
|
204
225
|
<DrawerHandle hide={hideHandle} />
|
|
205
226
|
<View flex {...contentProps}>
|
|
@@ -18,7 +18,7 @@ const DEFAULT_PROPS = ([{ sizeCode, color }, { vertical, spread }]) => {
|
|
|
18
18
|
labelProps: {
|
|
19
19
|
size: moveScale(sizeCode, !vertical ? 0 : -2),
|
|
20
20
|
moveIconSizeScale: !vertical ? -1 : -2,
|
|
21
|
-
color: color || '
|
|
21
|
+
color: color || 'text2',
|
|
22
22
|
},
|
|
23
23
|
valueProps: {
|
|
24
24
|
size: sizeCode,
|
|
@@ -2,6 +2,7 @@ import { Divider } from '../helpers/Separator'
|
|
|
2
2
|
import { Icon } from './Icon'
|
|
3
3
|
import { Text } from '../text/Text'
|
|
4
4
|
import { View } from '../structure/View'
|
|
5
|
+
import { useResponsiveValue } from '../../responsive'
|
|
5
6
|
|
|
6
7
|
export const RESULT_TYPES = {
|
|
7
8
|
error: {
|
|
@@ -23,6 +24,12 @@ export const RESULT_TYPES = {
|
|
|
23
24
|
icon: 'information-2-fill',
|
|
24
25
|
color: 'blue',
|
|
25
26
|
},
|
|
27
|
+
|
|
28
|
+
empty: {
|
|
29
|
+
icon: 'inbox-line',
|
|
30
|
+
color: 'text4',
|
|
31
|
+
opacity: 0.7,
|
|
32
|
+
},
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
export function Result({
|
|
@@ -41,12 +48,13 @@ export function Result({
|
|
|
41
48
|
const typeProps = RESULT_TYPES[type] || {}
|
|
42
49
|
icon = icon || typeProps.icon
|
|
43
50
|
iconColor = iconColor || typeProps.color || 'primary'
|
|
51
|
+
const size = useResponsiveValue({ lgu: 'h4', df: 'h5' })
|
|
44
52
|
|
|
45
53
|
return (
|
|
46
|
-
<View className="neko-result" center padding="lg" {...props}>
|
|
54
|
+
<View className="neko-result" center padding="lg" opacity={typeProps.opacity} {...props}>
|
|
47
55
|
{!!icon && <Icon name={icon} color={iconColor} size={42} primary {...iconProps} />}
|
|
48
56
|
{!!icon && <Divider height={10} />}
|
|
49
|
-
<Text
|
|
57
|
+
<Text size={size} center color="text2" {...textProps} {...titleProps}>
|
|
50
58
|
{title}
|
|
51
59
|
</Text>
|
|
52
60
|
{!!description && (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pipe } from 'ramda'
|
|
2
2
|
|
|
3
3
|
import { AbsKeyboardAvoidingView } from '../../abstractions/KeyboardAvoidingView'
|
|
4
|
+
import { AbsKeyboardDismissView } from '../../abstractions/KeyboardDismissView'
|
|
4
5
|
import { Platform } from '../../abstractions/Platform'
|
|
5
6
|
import { useSafeAreaInsets } from '../../abstractions/helpers/useSafeAreaInsets'
|
|
6
7
|
import { useAnimationModifier } from '../../modifiers/animation'
|
|
@@ -19,7 +20,7 @@ import { useSizeModifier } from '../../modifiers/size'
|
|
|
19
20
|
import { useStateModifier } from '../../modifiers/state'
|
|
20
21
|
import { useThemeComponentModifier } from '../../modifiers/themeComponent'
|
|
21
22
|
|
|
22
|
-
export function KeyboardAvoidingView({ children, keyboardVerticalOffset = 0, ...rootProps }) {
|
|
23
|
+
export function KeyboardAvoidingView({ children, keyboardVerticalOffset = 0, dismissOnTap = true, ...rootProps }) {
|
|
23
24
|
const { bottom } = useSafeAreaInsets()
|
|
24
25
|
|
|
25
26
|
const [_, props] = pipe(
|
|
@@ -46,7 +47,13 @@ export function KeyboardAvoidingView({ children, keyboardVerticalOffset = 0, ...
|
|
|
46
47
|
keyboardVerticalOffset={keyboardVerticalOffset + bottom}
|
|
47
48
|
{...props}
|
|
48
49
|
>
|
|
49
|
-
{
|
|
50
|
+
{dismissOnTap ? (
|
|
51
|
+
<AbsKeyboardDismissView style={{ flex: 1 }}>
|
|
52
|
+
{children}
|
|
53
|
+
</AbsKeyboardDismissView>
|
|
54
|
+
) : (
|
|
55
|
+
children
|
|
56
|
+
)}
|
|
50
57
|
</AbsKeyboardAvoidingView>
|
|
51
58
|
)
|
|
52
59
|
}
|
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
import { mapObjIndexed,
|
|
1
|
+
import { mapObjIndexed, values, pipe, reject, propEq } from 'ramda'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useAllThemes, useThemeHandler } from '../../theme'
|
|
4
4
|
import { IconLabel } from '../presentation'
|
|
5
5
|
import { Link } from '../actions'
|
|
6
6
|
import { Picker } from '../inputs'
|
|
7
7
|
import { ThemeThumb } from './ThemeThumb'
|
|
8
8
|
|
|
9
|
-
export function ThemePicker({ onChange
|
|
10
|
-
const { activeThemeKey,
|
|
9
|
+
export function ThemePicker({ onChange }) {
|
|
10
|
+
const { activeThemeKey, onChangeTheme } = useThemeHandler()
|
|
11
|
+
const allThemes = useAllThemes()
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
mergeDeepRight(DEFAULT_THEMES),
|
|
13
|
+
const options = pipe(
|
|
14
14
|
mapObjIndexed((obj, key) => ({ ...obj, value: key, key })),
|
|
15
15
|
values,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (onlyKeys && onlyKeys.includes(item.value)) return true
|
|
19
|
-
if (hideKeys && hideKeys.includes(item.value)) return false
|
|
20
|
-
return true
|
|
21
|
-
})
|
|
22
|
-
)(themes)
|
|
16
|
+
reject(propEq('_all', 'value'))
|
|
17
|
+
)(allThemes)
|
|
23
18
|
|
|
24
19
|
return (
|
|
25
20
|
<Picker
|
|
@@ -4,7 +4,7 @@ import { useResponsiveValue } from '../../responsive'
|
|
|
4
4
|
import { useThemeHandler } from '../../theme'
|
|
5
5
|
|
|
6
6
|
export function ThemeThumb({ value }) {
|
|
7
|
-
const { themes } = useThemeHandler()
|
|
7
|
+
const { rawThemesParam: themes } = useThemeHandler()
|
|
8
8
|
const { colors, label } = useFormattedTheme(themes, value)
|
|
9
9
|
const isMobile = useResponsiveValue({ smd: true, df: false })
|
|
10
10
|
|