@neko-os/ui 0.4.0 → 0.5.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/abstractions/KeyboardDismissView.js +3 -0
- package/dist/abstractions/KeyboardDismissView.native.js +9 -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 +110 -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/TextInput.js +7 -6
- package/dist/components/inputs/datePicker/DayPicker.js +65 -23
- package/dist/components/inputs/datePicker/MonthPicker.js +51 -27
- package/dist/components/inputs/datePicker/QuarterPicker.js +52 -28
- package/dist/components/inputs/datePicker/WeekPicker.js +59 -24
- package/dist/components/inputs/datePicker/YearPicker.js +59 -35
- 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/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/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 +110 -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/TextInput.js +5 -4
- package/src/components/inputs/datePicker/DayPicker.js +63 -21
- package/src/components/inputs/datePicker/MonthPicker.js +50 -26
- package/src/components/inputs/datePicker/QuarterPicker.js +50 -26
- package/src/components/inputs/datePicker/WeekPicker.js +57 -22
- package/src/components/inputs/datePicker/YearPicker.js +58 -34
- 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/theme/ThemeHandler.js +31 -3
|
@@ -48,6 +48,7 @@ export function DateInput({
|
|
|
48
48
|
type = 'day',
|
|
49
49
|
format,
|
|
50
50
|
startsOpen,
|
|
51
|
+
allowClear,
|
|
51
52
|
useBottomDrawer = { native: true, sm: true, md: true },
|
|
52
53
|
...props
|
|
53
54
|
}) {
|
|
@@ -91,7 +92,7 @@ export function DateInput({
|
|
|
91
92
|
trigger="click"
|
|
92
93
|
startsOpen={startsOpen}
|
|
93
94
|
placement={placement || 'bottomLeft'}
|
|
94
|
-
snapPoints={[
|
|
95
|
+
snapPoints={[450]}
|
|
95
96
|
useBottomDrawer={useBottomDrawer}
|
|
96
97
|
bottomDrawerProps={{ contentProps: { padding: 'md' } }}
|
|
97
98
|
watch={[value?.format?.('YYYYMMDD')]}
|
|
@@ -103,6 +104,8 @@ export function DateInput({
|
|
|
103
104
|
handleChange(v)
|
|
104
105
|
onClose()
|
|
105
106
|
}}
|
|
107
|
+
width={useBottomDrawer ? '100%' : 275}
|
|
108
|
+
allowClear={allowClear}
|
|
106
109
|
{...validations}
|
|
107
110
|
type={type}
|
|
108
111
|
/>
|
|
@@ -65,8 +65,7 @@ export function InputWrapper({
|
|
|
65
65
|
<View
|
|
66
66
|
className="neko-input-wrapper"
|
|
67
67
|
height={multiline ? undefined : size}
|
|
68
|
-
minHeight={multiline ? size : undefined}
|
|
69
|
-
paddingV={multiline ? 'sm' : undefined}
|
|
68
|
+
minHeight={multiline ? 1.5 * size : undefined}
|
|
70
69
|
onPress={handlePress}
|
|
71
70
|
borderColor={borderColor}
|
|
72
71
|
onMouseEnter={() => setHover(true)}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AbsTextInput } from '../../abstractions/TextInput'
|
|
2
2
|
import { InputWrapper } from './InputWrapper'
|
|
3
3
|
import { useColors } from '../../theme/ThemeHandler'
|
|
4
|
+
import { useSpaces } from '../../theme'
|
|
4
5
|
|
|
5
6
|
export function TextInput({ onChange, multiline, rows, ...props }) {
|
|
6
7
|
const colors = useColors()
|
|
8
|
+
const spaces = useSpaces()
|
|
7
9
|
|
|
8
10
|
const STYLE = {
|
|
9
11
|
width: '100%',
|
|
@@ -11,10 +13,9 @@ export function TextInput({ onChange, multiline, rows, ...props }) {
|
|
|
11
13
|
background: 'transparent',
|
|
12
14
|
outline: 'none',
|
|
13
15
|
color: colors.text,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
...(multiline ? { resize: 'none' } : { height: '100%' }),
|
|
16
|
+
flex: 1,
|
|
17
|
+
height: '100%',
|
|
18
|
+
...(multiline ? { paddingTop: spaces.sm, resize: 'none', flex: 1, textAlignVertical: 'top' } : {}),
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
return (
|
|
@@ -2,8 +2,10 @@ import React from 'react'
|
|
|
2
2
|
import dayjs from 'dayjs'
|
|
3
3
|
|
|
4
4
|
import { CalendarNav } from '../../calendar/CalendarNav'
|
|
5
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
5
6
|
import { Col } from '../../structure/Col'
|
|
6
7
|
import { Grid } from '../../structure/Row'
|
|
8
|
+
import { InfiniteCarousel } from '../../carousel/InfiniteCarousel'
|
|
7
9
|
import { Link } from '../../actions/Link'
|
|
8
10
|
import { Text } from '../../text/Text'
|
|
9
11
|
import { View } from '../../structure/View'
|
|
@@ -11,33 +13,27 @@ import { WeekDaysBar } from '../../calendar/WeekDaysBar'
|
|
|
11
13
|
import { isDateDisabled } from '../../calendar/_helpers/dateDisabled'
|
|
12
14
|
import { useCalendarDays } from '../../calendar/_helpers/calendarDays'
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const [currentMonth, setCurrentMonth] = React.useState(() => dayjs(value || undefined).startOf('month'))
|
|
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?.()])
|
|
16
|
+
function toMonthValue(date) {
|
|
17
|
+
return date.year() * 12 + date.month()
|
|
18
|
+
}
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
function fromMonthValue(v) {
|
|
21
|
+
return dayjs()
|
|
22
|
+
.year(Math.floor(v / 12))
|
|
23
|
+
.month(v % 12)
|
|
24
|
+
.startOf('month')
|
|
25
|
+
}
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
function MonthDays({ month, selectedValue, onSelect, min, max, onCheckDisabled }) {
|
|
28
|
+
const { cells } = useCalendarDays(month)
|
|
31
29
|
|
|
32
30
|
return (
|
|
33
|
-
<View
|
|
34
|
-
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
31
|
+
<View>
|
|
35
32
|
<WeekDaysBar />
|
|
36
|
-
|
|
37
33
|
<Grid className="neko-day-picker-days" colSpan={24 / 7} gap="sm">
|
|
38
34
|
{cells.map((day, i) => {
|
|
39
|
-
const dateVal =
|
|
40
|
-
const isActive = !!
|
|
35
|
+
const dateVal = month.date(day)
|
|
36
|
+
const isActive = !!selectedValue && !!day && dateVal.isSame(selectedValue, 'day')
|
|
41
37
|
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
42
38
|
|
|
43
39
|
return (
|
|
@@ -47,7 +43,7 @@ export function DayPicker({ value, onChange, min, max, onCheckDisabled, ...props
|
|
|
47
43
|
fullW
|
|
48
44
|
center
|
|
49
45
|
br="md"
|
|
50
|
-
onPress={() => !!day &&
|
|
46
|
+
onPress={() => !!day && onSelect(dateVal)}
|
|
51
47
|
bg={isActive && 'primary'}
|
|
52
48
|
disabled={disabled}
|
|
53
49
|
>
|
|
@@ -62,3 +58,49 @@ export function DayPicker({ value, onChange, min, max, onCheckDisabled, ...props
|
|
|
62
58
|
</View>
|
|
63
59
|
)
|
|
64
60
|
}
|
|
61
|
+
|
|
62
|
+
export function DayPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
63
|
+
if (!!value) value = dayjs(value)
|
|
64
|
+
const [localValue, setLocalValue] = React.useState(value)
|
|
65
|
+
const [currentMonth, setCurrentMonth] = React.useState(() => dayjs(value || undefined).startOf('month'))
|
|
66
|
+
value = value === undefined ? localValue : value
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
setLocalValue(value)
|
|
70
|
+
if (value?.isValid?.()) setCurrentMonth(value.startOf('month'))
|
|
71
|
+
}, [value?.day?.(), value?.month?.(), value?.year?.()])
|
|
72
|
+
|
|
73
|
+
const handleChange = (v) => {
|
|
74
|
+
setLocalValue(v)
|
|
75
|
+
onChange?.(v)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const monthValue = toMonthValue(currentMonth)
|
|
79
|
+
const minMonth = min ? toMonthValue(dayjs(min).startOf('month')) : undefined
|
|
80
|
+
const maxMonth = max ? toMonthValue(dayjs(max).startOf('month')) : undefined
|
|
81
|
+
|
|
82
|
+
const renderSlide = (v) => (
|
|
83
|
+
<MonthDays
|
|
84
|
+
month={fromMonthValue(v)}
|
|
85
|
+
selectedValue={value}
|
|
86
|
+
onSelect={handleChange}
|
|
87
|
+
min={min}
|
|
88
|
+
max={max}
|
|
89
|
+
onCheckDisabled={onCheckDisabled}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<View className="neko-day-picker" width={275} maxW={350} {...props}>
|
|
95
|
+
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
96
|
+
<InfiniteCarousel
|
|
97
|
+
value={monthValue}
|
|
98
|
+
onChange={(v) => setCurrentMonth(fromMonthValue(v))}
|
|
99
|
+
renderSlide={renderSlide}
|
|
100
|
+
min={minMonth}
|
|
101
|
+
max={maxMonth}
|
|
102
|
+
/>
|
|
103
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
104
|
+
</View>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -2,9 +2,11 @@ import React from 'react'
|
|
|
2
2
|
import dayjs from 'dayjs'
|
|
3
3
|
|
|
4
4
|
import { CalendarNav } from '../../calendar/CalendarNav'
|
|
5
|
+
import { ClearLink } from '../../actions/ClearLink'
|
|
5
6
|
import { Col } from '../../structure/Col'
|
|
6
7
|
import { Divider } from '../../helpers'
|
|
7
8
|
import { Grid } from '../../structure/Row'
|
|
9
|
+
import { InfiniteCarousel } from '../../carousel/InfiniteCarousel'
|
|
8
10
|
import { Link } from '../../actions/Link'
|
|
9
11
|
import { Text } from '../../text/Text'
|
|
10
12
|
import { View } from '../../structure/View'
|
|
@@ -12,7 +14,38 @@ import { isDateDisabled } from '../../calendar/_helpers/dateDisabled'
|
|
|
12
14
|
|
|
13
15
|
const months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
function MonthGrid({ year, selectedValue, onSelect, min, max, onCheckDisabled }) {
|
|
18
|
+
const yearDate = dayjs().year(year).startOf('year')
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Grid colSpan={8} gap="xs">
|
|
22
|
+
{months.map((month) => {
|
|
23
|
+
const dateVal = yearDate.month(month)
|
|
24
|
+
const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'week')
|
|
25
|
+
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Col key={month}>
|
|
29
|
+
<Link
|
|
30
|
+
fullW
|
|
31
|
+
br="md"
|
|
32
|
+
padding="sm"
|
|
33
|
+
onPress={() => onSelect(dateVal)}
|
|
34
|
+
bg={isActive && 'primary'}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
>
|
|
37
|
+
<Text text2={!isActive} strong={isActive} center>
|
|
38
|
+
{dateVal.format('MMM')}
|
|
39
|
+
</Text>
|
|
40
|
+
</Link>
|
|
41
|
+
</Col>
|
|
42
|
+
)
|
|
43
|
+
})}
|
|
44
|
+
</Grid>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function MonthPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
16
49
|
const [localValue, setLocalValue] = React.useState(value)
|
|
17
50
|
const [currentYear, setCurrentYear] = React.useState(() => dayjs(value || undefined).startOf('year'))
|
|
18
51
|
value = value === undefined ? localValue : value
|
|
@@ -28,35 +61,26 @@ export function MonthPicker({ value, onChange, min, max, onCheckDisabled, ...pro
|
|
|
28
61
|
onChange?.(newValue)
|
|
29
62
|
}
|
|
30
63
|
|
|
64
|
+
const yearValue = currentYear.year()
|
|
65
|
+
const minYear = min ? dayjs(min).year() : undefined
|
|
66
|
+
const maxYear = max ? dayjs(max).year() : undefined
|
|
67
|
+
|
|
68
|
+
const renderSlide = (v) => (
|
|
69
|
+
<MonthGrid year={v} selectedValue={value} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
70
|
+
)
|
|
71
|
+
|
|
31
72
|
return (
|
|
32
73
|
<View className="neko-day-picker" width={275} {...props}>
|
|
33
74
|
<CalendarNav value={currentYear} onChange={setCurrentYear} level="year" />
|
|
34
75
|
<Divider />
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<Col key={month}>
|
|
44
|
-
<Link
|
|
45
|
-
fullW
|
|
46
|
-
br="md"
|
|
47
|
-
padding="sm"
|
|
48
|
-
onPress={() => handleChange(dateVal)}
|
|
49
|
-
bg={isActive && 'primary'}
|
|
50
|
-
disabled={disabled}
|
|
51
|
-
>
|
|
52
|
-
<Text text2={!isActive} strong={isActive} center>
|
|
53
|
-
{dateVal.format('MMM')}
|
|
54
|
-
</Text>
|
|
55
|
-
</Link>
|
|
56
|
-
</Col>
|
|
57
|
-
)
|
|
58
|
-
})}
|
|
59
|
-
</Grid>
|
|
76
|
+
<InfiniteCarousel
|
|
77
|
+
value={yearValue}
|
|
78
|
+
onChange={(v) => setCurrentYear(dayjs().year(v).startOf('year'))}
|
|
79
|
+
renderSlide={renderSlide}
|
|
80
|
+
min={minYear}
|
|
81
|
+
max={maxYear}
|
|
82
|
+
/>
|
|
83
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
60
84
|
</View>
|
|
61
85
|
)
|
|
62
86
|
}
|
|
@@ -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,38 @@ dayjs.extend(quarterOfYear)
|
|
|
15
17
|
|
|
16
18
|
const quarters = [1, 2, 3, 4]
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
function QuarterGrid({ year, selectedValue, onSelect, min, max, onCheckDisabled }) {
|
|
21
|
+
const yearDate = dayjs().year(year).startOf('year')
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Grid colSpan={6} gap="xs">
|
|
25
|
+
{quarters.map((quarter) => {
|
|
26
|
+
const dateVal = yearDate.quarter(quarter)
|
|
27
|
+
const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'week')
|
|
28
|
+
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Col key={quarter}>
|
|
32
|
+
<Link
|
|
33
|
+
fullW
|
|
34
|
+
br="md"
|
|
35
|
+
padding="sm"
|
|
36
|
+
onPress={() => onSelect(dateVal)}
|
|
37
|
+
bg={isActive && 'primary'}
|
|
38
|
+
disabled={disabled}
|
|
39
|
+
>
|
|
40
|
+
<Text text2={!isActive} strong={isActive} center>
|
|
41
|
+
Q{dateVal.quarter()}
|
|
42
|
+
</Text>
|
|
43
|
+
</Link>
|
|
44
|
+
</Col>
|
|
45
|
+
)
|
|
46
|
+
})}
|
|
47
|
+
</Grid>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function QuarterPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
19
52
|
const [localValue, setLocalValue] = React.useState(value)
|
|
20
53
|
const [currentYear, setCurrentYear] = React.useState(() => dayjs(value || undefined).startOf('year'))
|
|
21
54
|
value = value === undefined ? localValue : value
|
|
@@ -31,35 +64,26 @@ export function QuarterPicker({ value, onChange, min, max, onCheckDisabled, ...p
|
|
|
31
64
|
onChange?.(newValue)
|
|
32
65
|
}
|
|
33
66
|
|
|
67
|
+
const yearValue = currentYear.year()
|
|
68
|
+
const minYear = min ? dayjs(min).year() : undefined
|
|
69
|
+
const maxYear = max ? dayjs(max).year() : undefined
|
|
70
|
+
|
|
71
|
+
const renderSlide = (v) => (
|
|
72
|
+
<QuarterGrid year={v} selectedValue={value} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
73
|
+
)
|
|
74
|
+
|
|
34
75
|
return (
|
|
35
76
|
<View className="neko-day-picker" width={275} {...props}>
|
|
36
77
|
<CalendarNav value={currentYear} onChange={setCurrentYear} level="year" />
|
|
37
78
|
<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>
|
|
79
|
+
<InfiniteCarousel
|
|
80
|
+
value={yearValue}
|
|
81
|
+
onChange={(v) => setCurrentYear(dayjs().year(v).startOf('year'))}
|
|
82
|
+
renderSlide={renderSlide}
|
|
83
|
+
min={minYear}
|
|
84
|
+
max={maxYear}
|
|
85
|
+
/>
|
|
86
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
63
87
|
</View>
|
|
64
88
|
)
|
|
65
89
|
}
|
|
@@ -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,29 @@ 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
|
+
function MonthWeeks({ month, selectedValue, onSelect, min, max, onCheckDisabled }) {
|
|
29
|
+
const { cells } = useCalendarDays(month)
|
|
32
30
|
const weeks = splitEvery(7, cells)
|
|
33
31
|
|
|
34
32
|
return (
|
|
35
|
-
<View
|
|
36
|
-
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
33
|
+
<View>
|
|
37
34
|
<WeekDaysBar />
|
|
38
|
-
|
|
39
35
|
<View colSpan={24 / 7} gap="sm">
|
|
40
36
|
{weeks.map((week, wi) => {
|
|
41
37
|
const firstDay = week.find(identity)
|
|
42
|
-
const dateVal =
|
|
43
|
-
const isActive = !!
|
|
38
|
+
const dateVal = month.date(firstDay)
|
|
39
|
+
const isActive = !!selectedValue && !!firstDay && dateVal.isSame(selectedValue, 'week')
|
|
44
40
|
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
45
41
|
|
|
46
42
|
return (
|
|
@@ -48,13 +44,13 @@ export function WeekPicker({ value, onChange, min, max, onCheckDisabled, ...prop
|
|
|
48
44
|
key={firstDay ? dateVal.format('YYYYMMDD') : wi}
|
|
49
45
|
fullW
|
|
50
46
|
br="md"
|
|
51
|
-
onPress={() => !!firstDay &&
|
|
47
|
+
onPress={() => !!firstDay && onSelect(dateVal)}
|
|
52
48
|
bg={isActive && 'primary'}
|
|
53
49
|
disabled={disabled}
|
|
54
50
|
>
|
|
55
51
|
<Row colSpan={24 / 7} gap="sm">
|
|
56
52
|
{week.map((day, i) => {
|
|
57
|
-
const dateVal =
|
|
53
|
+
const dateVal = month.date(day)
|
|
58
54
|
|
|
59
55
|
return (
|
|
60
56
|
<Col key={day ? dateVal.format('YYYYMMDD') : i} className="day-cell" center ratio={1}>
|
|
@@ -72,3 +68,42 @@ export function WeekPicker({ value, onChange, min, max, onCheckDisabled, ...prop
|
|
|
72
68
|
</View>
|
|
73
69
|
)
|
|
74
70
|
}
|
|
71
|
+
|
|
72
|
+
export function WeekPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
73
|
+
const [localValue, setLocalValue] = React.useState(value)
|
|
74
|
+
const [currentMonth, setCurrentMonth] = React.useState(() => dayjs(value || undefined).startOf('month'))
|
|
75
|
+
value = value === undefined ? localValue : value
|
|
76
|
+
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
setLocalValue(value)
|
|
79
|
+
if (value?.isValid?.()) setCurrentMonth(value.startOf('month'))
|
|
80
|
+
}, [value?.day?.(), value?.month?.(), value?.year?.()])
|
|
81
|
+
|
|
82
|
+
const handleChange = (v) => {
|
|
83
|
+
const newValue = v.startOf('week')
|
|
84
|
+
setLocalValue(newValue)
|
|
85
|
+
onChange?.(newValue)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const monthValue = toMonthValue(currentMonth)
|
|
89
|
+
const minMonth = min ? toMonthValue(dayjs(min).startOf('month')) : undefined
|
|
90
|
+
const maxMonth = max ? toMonthValue(dayjs(max).startOf('month')) : undefined
|
|
91
|
+
|
|
92
|
+
const renderSlide = (v) => (
|
|
93
|
+
<MonthWeeks month={fromMonthValue(v)} selectedValue={value} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<View className="neko-day-picker" width={275} {...props}>
|
|
98
|
+
<CalendarNav value={currentMonth} onChange={setCurrentMonth} />
|
|
99
|
+
<InfiniteCarousel
|
|
100
|
+
value={monthValue}
|
|
101
|
+
onChange={(v) => setCurrentMonth(fromMonthValue(v))}
|
|
102
|
+
renderSlide={renderSlide}
|
|
103
|
+
min={minMonth}
|
|
104
|
+
max={maxMonth}
|
|
105
|
+
/>
|
|
106
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
107
|
+
</View>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -3,29 +3,65 @@ 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
|
+
function DecadeGrid({ decadeIndex, selectedValue, onSelect, min, max, onCheckDisabled }) {
|
|
25
|
+
const decadeStart = decadeFromIndex(decadeIndex)
|
|
26
|
+
const years = range(decadeStart.year(), decadeStart.year() + 10)
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Grid colSpan={12} gap="xs">
|
|
30
|
+
{years.map((year) => {
|
|
31
|
+
const dateVal = decadeStart.year(year)
|
|
32
|
+
const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'week')
|
|
33
|
+
const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Col key={year}>
|
|
37
|
+
<Link
|
|
38
|
+
fullW
|
|
39
|
+
br="md"
|
|
40
|
+
padding="sm"
|
|
41
|
+
onPress={() => onSelect(dateVal)}
|
|
42
|
+
bg={isActive && 'primary'}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
>
|
|
45
|
+
<Text text2={!isActive} strong={isActive} center>
|
|
46
|
+
{dateVal.year()}
|
|
47
|
+
</Text>
|
|
48
|
+
</Link>
|
|
49
|
+
</Col>
|
|
50
|
+
)
|
|
51
|
+
})}
|
|
52
|
+
</Grid>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function YearPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
|
|
21
57
|
const [localValue, setLocalValue] = React.useState(value)
|
|
22
|
-
const [currentDecade, setCurrentDecade] = React.useState(() =>
|
|
58
|
+
const [currentDecade, setCurrentDecade] = React.useState(() => getDecadeIndex(value))
|
|
23
59
|
|
|
24
60
|
value = value === undefined ? localValue : value
|
|
25
61
|
|
|
26
62
|
React.useEffect(() => {
|
|
27
63
|
setLocalValue(value)
|
|
28
|
-
if (value?.isValid?.()) setCurrentDecade(
|
|
64
|
+
if (value?.isValid?.()) setCurrentDecade(getDecadeIndex(value))
|
|
29
65
|
}, [value?.year?.()])
|
|
30
66
|
|
|
31
67
|
const handleChange = (v) => {
|
|
@@ -34,37 +70,25 @@ export function YearPicker({ value, onChange, min, max, onCheckDisabled, ...prop
|
|
|
34
70
|
onChange?.(newValue)
|
|
35
71
|
}
|
|
36
72
|
|
|
37
|
-
const
|
|
73
|
+
const minDecade = min ? getDecadeIndex(min) : undefined
|
|
74
|
+
const maxDecade = max ? getDecadeIndex(max) : undefined
|
|
75
|
+
|
|
76
|
+
const renderSlide = (v) => (
|
|
77
|
+
<DecadeGrid decadeIndex={v} selectedValue={value} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
|
|
78
|
+
)
|
|
38
79
|
|
|
39
80
|
return (
|
|
40
81
|
<View className="neko-day-picker" width={275} {...props}>
|
|
41
|
-
<CalendarNav value={currentDecade} onChange={setCurrentDecade} level="decade" />
|
|
82
|
+
<CalendarNav value={decadeFromIndex(currentDecade)} onChange={(v) => setCurrentDecade(getDecadeIndex(v))} level="decade" />
|
|
42
83
|
<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>
|
|
84
|
+
<InfiniteCarousel
|
|
85
|
+
value={currentDecade}
|
|
86
|
+
onChange={setCurrentDecade}
|
|
87
|
+
renderSlide={renderSlide}
|
|
88
|
+
min={minDecade}
|
|
89
|
+
max={maxDecade}
|
|
90
|
+
/>
|
|
91
|
+
<ClearLink hide={!allowClear} value={value} onChange={onChange} />
|
|
68
92
|
</View>
|
|
69
93
|
)
|
|
70
94
|
}
|
|
@@ -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
|
+
}
|