@neko-os/ui 0.5.0 → 0.5.2

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 (41) hide show
  1. package/dist/NekoUI.js +4 -1
  2. package/dist/abstractions/index.js +1 -0
  3. package/dist/components/actions/Dropdown.js +5 -3
  4. package/dist/components/calendar/PeriodNavBar.js +176 -0
  5. package/dist/components/calendar/WeekDaysBar.js +8 -4
  6. package/dist/components/calendar/_helpers/calendarDays.js +5 -1
  7. package/dist/components/calendar/index.js +1 -0
  8. package/dist/components/carousel/CarouselSlider.native.js +12 -1
  9. package/dist/components/filter/DateFilter.js +1 -1
  10. package/dist/components/inputs/Select.js +56 -52
  11. package/dist/components/inputs/datePicker/DayPicker.js +15 -9
  12. package/dist/components/inputs/datePicker/MonthPicker.js +15 -10
  13. package/dist/components/inputs/datePicker/QuarterPicker.js +15 -10
  14. package/dist/components/inputs/datePicker/WeekPicker.js +15 -9
  15. package/dist/components/inputs/datePicker/YearPicker.js +15 -10
  16. package/dist/helpers/index.js +2 -0
  17. package/dist/helpers/weekStart.js +25 -0
  18. package/dist/helpers/weekStartSetup.js +11 -0
  19. package/dist/helpers/weekStartSetup.native.js +11 -0
  20. package/dist/index.js +1 -0
  21. package/package.json +1 -1
  22. package/src/NekoUI.js +3 -0
  23. package/src/abstractions/index.js +1 -0
  24. package/src/components/actions/Dropdown.js +2 -0
  25. package/src/components/calendar/PeriodNavBar.js +176 -0
  26. package/src/components/calendar/WeekDaysBar.js +6 -2
  27. package/src/components/calendar/_helpers/calendarDays.js +5 -1
  28. package/src/components/calendar/index.js +1 -1
  29. package/src/components/carousel/CarouselSlider.native.js +12 -1
  30. package/src/components/filter/DateFilter.js +1 -1
  31. package/src/components/inputs/Select.js +19 -15
  32. package/src/components/inputs/datePicker/DayPicker.js +14 -8
  33. package/src/components/inputs/datePicker/MonthPicker.js +14 -9
  34. package/src/components/inputs/datePicker/QuarterPicker.js +14 -9
  35. package/src/components/inputs/datePicker/WeekPicker.js +14 -8
  36. package/src/components/inputs/datePicker/YearPicker.js +14 -9
  37. package/src/helpers/index.js +2 -0
  38. package/src/helpers/weekStart.js +25 -0
  39. package/src/helpers/weekStartSetup.js +11 -0
  40. package/src/helpers/weekStartSetup.native.js +11 -0
  41. package/src/index.js +1 -0
@@ -25,7 +25,9 @@ function fromMonthValue(v) {
25
25
  startOf('month');
26
26
  }
27
27
 
28
- function MonthWeeks(_ref) {var _this = this;var month = _ref.month,selectedValue = _ref.selectedValue,onSelect = _ref.onSelect,min = _ref.min,max = _ref.max,onCheckDisabled = _ref.onCheckDisabled;
28
+ var MonthWeeks = React.memo(function MonthWeeks(_ref) {var _this = this;var monthValue = _ref.monthValue,selectedKey = _ref.selectedKey,onSelect = _ref.onSelect,min = _ref.min,max = _ref.max,onCheckDisabled = _ref.onCheckDisabled;
29
+ var month = fromMonthValue(monthValue);
30
+ var selectedValue = selectedKey ? dayjs(selectedKey) : null;
29
31
  var _useCalendarDays = useCalendarDays(month),cells = _useCalendarDays.cells;
30
32
  var weeks = splitEvery(7, cells);
31
33
 
@@ -67,9 +69,9 @@ function MonthWeeks(_ref) {var _this = this;var month = _ref.month,selectedValue
67
69
  )] }
68
70
  ));
69
71
 
70
- }
72
+ });
71
73
 
72
- export function WeekPicker(_ref2) {var _value2,_value3,_value4,_this2 = this;var value = _ref2.value,onChange = _ref2.onChange,min = _ref2.min,max = _ref2.max,onCheckDisabled = _ref2.onCheckDisabled,allowClear = _ref2.allowClear,props = _objectWithoutProperties(_ref2, _excluded);
74
+ export function WeekPicker(_ref2) {var _value2,_value3,_value4,_value5,_this2 = this;var value = _ref2.value,onChange = _ref2.onChange,min = _ref2.min,max = _ref2.max,onCheckDisabled = _ref2.onCheckDisabled,allowClear = _ref2.allowClear,props = _objectWithoutProperties(_ref2, _excluded);
73
75
  var _React$useState = React.useState(value),_React$useState2 = _slicedToArray(_React$useState, 2),localValue = _React$useState2[0],setLocalValue = _React$useState2[1];
74
76
  var _React$useState3 = React.useState(function () {return dayjs(value || undefined).startOf('month');}),_React$useState4 = _slicedToArray(_React$useState3, 2),currentMonth = _React$useState4[0],setCurrentMonth = _React$useState4[1];
75
77
  value = value === undefined ? localValue : value;
@@ -79,18 +81,22 @@ export function WeekPicker(_ref2) {var _value2,_value3,_value4,_this2 = this;var
79
81
  if ((_value = value) != null && _value.isValid != null && _value.isValid()) setCurrentMonth(value.startOf('month'));
80
82
  }, [(_value2 = value) == null ? void 0 : _value2.day == null ? void 0 : _value2.day(), (_value3 = value) == null ? void 0 : _value3.month == null ? void 0 : _value3.month(), (_value4 = value) == null ? void 0 : _value4.year == null ? void 0 : _value4.year()]);
81
83
 
82
- var handleChange = function handleChange(v) {
83
- var newValue = v.startOf('week');
84
- setLocalValue(newValue);
85
- onChange == null ? void 0 : onChange(newValue);
86
- };
84
+ var handleChange = React.useCallback(
85
+ function (v) {
86
+ var newValue = v.startOf('week');
87
+ setLocalValue(newValue);
88
+ onChange == null ? void 0 : onChange(newValue);
89
+ },
90
+ [onChange]
91
+ );
87
92
 
88
93
  var monthValue = toMonthValue(currentMonth);
89
94
  var minMonth = min ? toMonthValue(dayjs(min).startOf('month')) : undefined;
90
95
  var maxMonth = max ? toMonthValue(dayjs(max).startOf('month')) : undefined;
96
+ var selectedKey = (_value5 = value) == null ? void 0 : _value5.valueOf == null ? void 0 : _value5.valueOf();
91
97
 
92
98
  var renderSlide = function renderSlide(v) {return (
93
- _jsx(MonthWeeks, { month: fromMonthValue(v), selectedValue: value, onSelect: handleChange, min: min, max: max, onCheckDisabled: onCheckDisabled }));};
99
+ _jsx(MonthWeeks, { monthValue: v, selectedKey: selectedKey, onSelect: handleChange, min: min, max: max, onCheckDisabled: onCheckDisabled }));};
94
100
 
95
101
 
96
102
  return (
@@ -21,15 +21,16 @@ function decadeFromIndex(i) {
21
21
  return dayjs().year(i * 10).startOf('year');
22
22
  }
23
23
 
24
- function DecadeGrid(_ref) {var _this = this;var decadeIndex = _ref.decadeIndex,selectedValue = _ref.selectedValue,onSelect = _ref.onSelect,min = _ref.min,max = _ref.max,onCheckDisabled = _ref.onCheckDisabled;
24
+ var DecadeGrid = React.memo(function DecadeGrid(_ref) {var _this = this;var decadeIndex = _ref.decadeIndex,selectedKey = _ref.selectedKey,onSelect = _ref.onSelect,min = _ref.min,max = _ref.max,onCheckDisabled = _ref.onCheckDisabled;
25
25
  var decadeStart = decadeFromIndex(decadeIndex);
26
+ var selectedValue = selectedKey ? dayjs(selectedKey) : null;
26
27
  var years = range(decadeStart.year(), decadeStart.year() + 10);
27
28
 
28
29
  return (
29
30
  _jsx(Grid, { colSpan: 12, gap: "xs", children:
30
31
  years.map(function (year) {
31
32
  var dateVal = decadeStart.year(year);
32
- var isActive = !!selectedValue && dateVal.isSame(selectedValue, 'week');
33
+ var isActive = !!selectedValue && dateVal.isSame(selectedValue, 'year');
33
34
  var disabled = isDateDisabled(dateVal, { min: min, max: max, onCheckDisabled: onCheckDisabled });
34
35
 
35
36
  return (
@@ -51,9 +52,9 @@ function DecadeGrid(_ref) {var _this = this;var decadeIndex = _ref.decadeIndex,s
51
52
  }) }
52
53
  ));
53
54
 
54
- }
55
+ });
55
56
 
56
- export function YearPicker(_ref2) {var _value2,_this2 = this;var value = _ref2.value,onChange = _ref2.onChange,min = _ref2.min,max = _ref2.max,onCheckDisabled = _ref2.onCheckDisabled,allowClear = _ref2.allowClear,props = _objectWithoutProperties(_ref2, _excluded);
57
+ export function YearPicker(_ref2) {var _value2,_value3,_this2 = this;var value = _ref2.value,onChange = _ref2.onChange,min = _ref2.min,max = _ref2.max,onCheckDisabled = _ref2.onCheckDisabled,allowClear = _ref2.allowClear,props = _objectWithoutProperties(_ref2, _excluded);
57
58
  var _React$useState = React.useState(value),_React$useState2 = _slicedToArray(_React$useState, 2),localValue = _React$useState2[0],setLocalValue = _React$useState2[1];
58
59
  var _React$useState3 = React.useState(function () {return getDecadeIndex(value);}),_React$useState4 = _slicedToArray(_React$useState3, 2),currentDecade = _React$useState4[0],setCurrentDecade = _React$useState4[1];
59
60
 
@@ -64,17 +65,21 @@ export function YearPicker(_ref2) {var _value2,_this2 = this;var value = _ref2.v
64
65
  if ((_value = value) != null && _value.isValid != null && _value.isValid()) setCurrentDecade(getDecadeIndex(value));
65
66
  }, [(_value2 = value) == null ? void 0 : _value2.year == null ? void 0 : _value2.year()]);
66
67
 
67
- var handleChange = function handleChange(v) {
68
- var newValue = v.startOf('year');
69
- setLocalValue(newValue);
70
- onChange == null ? void 0 : onChange(newValue);
71
- };
68
+ var handleChange = React.useCallback(
69
+ function (v) {
70
+ var newValue = v.startOf('year');
71
+ setLocalValue(newValue);
72
+ onChange == null ? void 0 : onChange(newValue);
73
+ },
74
+ [onChange]
75
+ );
72
76
 
73
77
  var minDecade = min ? getDecadeIndex(min) : undefined;
74
78
  var maxDecade = max ? getDecadeIndex(max) : undefined;
79
+ var selectedKey = (_value3 = value) == null ? void 0 : _value3.valueOf == null ? void 0 : _value3.valueOf();
75
80
 
76
81
  var renderSlide = function renderSlide(v) {return (
77
- _jsx(DecadeGrid, { decadeIndex: v, selectedValue: value, onSelect: handleChange, min: min, max: max, onCheckDisabled: onCheckDisabled }));};
82
+ _jsx(DecadeGrid, { decadeIndex: v, selectedKey: selectedKey, onSelect: handleChange, min: min, max: max, onCheckDisabled: onCheckDisabled }));};
78
83
 
79
84
 
80
85
  return (
@@ -2,4 +2,6 @@ export * from "./debounce";
2
2
  export * from "./string";
3
3
  export * from "./random";
4
4
  export * from "./storage";
5
+ export * from "./weekStart";
6
+ export * from "./weekStartSetup";
5
7
  export * from "./../abstractions/helpers/useSafeAreaInsets";
@@ -0,0 +1,25 @@
1
+ import dayjs from 'dayjs';
2
+ import updateLocale from 'dayjs/esm/plugin/updateLocale';
3
+
4
+ dayjs.extend(updateLocale);
5
+
6
+ var _override = null;
7
+
8
+ export function getFirstDayOfWeek() {
9
+ if (_override !== null) return _override;
10
+ return dayjs().$locale().weekStart || 0;
11
+ }
12
+
13
+ export function setFirstDayOfWeek(day) {
14
+ _override = day;
15
+ dayjs.updateLocale(dayjs().$locale().name || 'en', { weekStart: day });
16
+ }
17
+
18
+ export function getOrderedWeekdays() {
19
+ var first = getFirstDayOfWeek();
20
+ return Array.from({ length: 7 }, function (_, i) {return (first + i) % 7;});
21
+ }
22
+
23
+ export function getWeekdayOffset(dayIndex) {
24
+ return (dayIndex - getFirstDayOfWeek() + 7) % 7;
25
+ }
@@ -0,0 +1,11 @@
1
+ import { setFirstDayOfWeek } from "./weekStart";
2
+
3
+ export function initFirstDayOfWeek() {
4
+ try {var _locale$weekInfo;
5
+ var locale = new Intl.Locale(navigator.language);
6
+ var weekInfo = (_locale$weekInfo = locale.weekInfo) != null ? _locale$weekInfo : locale.getWeekInfo == null ? void 0 : locale.getWeekInfo();
7
+ if ((weekInfo == null ? void 0 : weekInfo.firstDay) != null) {
8
+ setFirstDayOfWeek(weekInfo.firstDay === 7 ? 0 : weekInfo.firstDay);
9
+ }
10
+ } catch (_unused) {}
11
+ }
@@ -0,0 +1,11 @@
1
+ import { setFirstDayOfWeek } from "./weekStart";
2
+
3
+ export function initFirstDayOfWeek() {
4
+ try {
5
+ var Localization = require('expo-localization');
6
+ var cal = Localization.getCalendars()[0];
7
+ if ((cal == null ? void 0 : cal.firstWeekday) != null) {
8
+ setFirstDayOfWeek((cal.firstWeekday - 1) % 7);
9
+ }
10
+ } catch (_unused) {}
11
+ }
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@ export * from "./theme";
4
4
  export * from "./responsive";
5
5
  export * from "./i18n";
6
6
  export * from "./NekoUI";
7
+ export * from "./abstractions";
7
8
 
8
9
  export var version = 41;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neko-os/ui",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "author": "Christian Storch <ccstorch@gmail.com>",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
package/src/NekoUI.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { initFirstDayOfWeek } from './helpers/weekStartSetup'
1
2
  import { DynamicStyleTag } from './DynamicStyleTag'
2
3
  import { I18nProvider } from './i18n'
3
4
  import { ModalsHandler } from './components/modals/modal/handler/ModalsHandler'
@@ -9,6 +10,8 @@ import { ThemeHandler } from './theme/ThemeHandler'
9
10
  import { ThemePickerDrawer } from './components/theme'
10
11
  import { useThemeHandler } from './theme'
11
12
 
13
+ initFirstDayOfWeek()
14
+
12
15
  export function NekoUI({ children, i18n, ...props }) {
13
16
  return (
14
17
  <ThemeHandler {...props}>
@@ -0,0 +1 @@
1
+ export * from './Platform'
@@ -37,6 +37,7 @@ export function Dropdown({ items, ...rootProps }) {
37
37
  placement,
38
38
  gap,
39
39
  useBottomDrawer,
40
+ snapPoints,
40
41
  ...props
41
42
  } = formattedProps
42
43
 
@@ -52,6 +53,7 @@ export function Dropdown({ items, ...rootProps }) {
52
53
  // In case its web use the Drawer component
53
54
  contentProps={{ padding: 0 }}
54
55
  useBottomDrawer={useBottomDrawer}
56
+ snapPoints={snapPoints}
55
57
  {...popoverProps}
56
58
  renderContent={({ onClose }) => {
57
59
  const handleChange = (...params) => {
@@ -0,0 +1,176 @@
1
+ import React from 'react'
2
+ import dayjs from 'dayjs'
3
+ import quarterOfYear from 'dayjs/esm/plugin/quarterOfYear'
4
+
5
+ import { InfiniteCarousel } from '../carousel/InfiniteCarousel'
6
+ import { Link } from '../actions/Link'
7
+ import { Text } from '../text/Text'
8
+ import { View } from '../structure/View'
9
+ import { isDateDisabled } from './_helpers/dateDisabled'
10
+
11
+ dayjs.extend(quarterOfYear)
12
+
13
+ function getWeekEpoch() {
14
+ return dayjs('2000-01-01').startOf('week')
15
+ }
16
+
17
+ const TYPES = {
18
+ day: {
19
+ count: 7,
20
+ unit: 'day',
21
+ toPageValue(date) {
22
+ return date.startOf('week').diff(getWeekEpoch(), 'week')
23
+ },
24
+ getItems(pageValue) {
25
+ const weekStart = getWeekEpoch().add(pageValue, 'week')
26
+ return Array.from({ length: 7 }, (_, i) => {
27
+ const d = weekStart.add(i, 'day')
28
+ return { key: d.valueOf(), date: d, label: d.format('ddd'), sublabel: String(d.date()) }
29
+ })
30
+ },
31
+ },
32
+ week: {
33
+ count: 7,
34
+ unit: 'week',
35
+ toPageValue(date) {
36
+ const weekIndex = date.startOf('week').diff(getWeekEpoch(), 'week')
37
+ return Math.floor(weekIndex / 7)
38
+ },
39
+ getItems(pageValue) {
40
+ return Array.from({ length: 7 }, (_, i) => {
41
+ const weekIndex = pageValue * 7 + i
42
+ const d = getWeekEpoch().add(weekIndex, 'week')
43
+ const end = d.add(6, 'day')
44
+ return { key: d.valueOf(), date: d, label: `${d.date()}-${end.date()}`, sublabel: d.format('MMM') }
45
+ })
46
+ },
47
+ },
48
+ month: {
49
+ count: 7,
50
+ unit: 'month',
51
+ toPageValue(date) {
52
+ const absMonth = date.year() * 12 + date.month()
53
+ return Math.floor(absMonth / 7)
54
+ },
55
+ getItems(pageValue) {
56
+ return Array.from({ length: 7 }, (_, i) => {
57
+ const absMonth = pageValue * 7 + i
58
+ const year = Math.floor(absMonth / 12)
59
+ const month = absMonth % 12
60
+ const d = dayjs().year(year).month(month).startOf('month')
61
+ return { key: d.valueOf(), date: d, label: d.format('MMM'), sublabel: d.format("'YY") }
62
+ })
63
+ },
64
+ },
65
+ quarter: {
66
+ count: 4,
67
+ unit: 'quarter',
68
+ toPageValue(date) {
69
+ return date.year()
70
+ },
71
+ getItems(pageValue) {
72
+ return Array.from({ length: 4 }, (_, i) => {
73
+ const d = dayjs().year(pageValue).month(i * 3).startOf('month')
74
+ return { key: d.valueOf(), date: d, label: `Q${i + 1}`, sublabel: String(pageValue) }
75
+ })
76
+ },
77
+ },
78
+ year: {
79
+ count: 7,
80
+ unit: 'year',
81
+ toPageValue(date) {
82
+ return Math.floor(date.year() / 7)
83
+ },
84
+ getItems(pageValue) {
85
+ return Array.from({ length: 7 }, (_, i) => {
86
+ const year = pageValue * 7 + i
87
+ const d = dayjs().year(year).startOf('year')
88
+ return { key: d.valueOf(), date: d, label: String(year), sublabel: null }
89
+ })
90
+ },
91
+ },
92
+ }
93
+
94
+ const PeriodSlide = React.memo(function PeriodSlide({ items, value, unit, onChange, min, max, onCheckDisabled }) {
95
+ return (
96
+ <View row center gap="xs">
97
+ {items.map((item) => {
98
+ const isActive = !!value && dayjs(value).isSame(item.date, unit)
99
+ const disabled = isDateDisabled(item.date, { min, max, onCheckDisabled })
100
+
101
+ return (
102
+ <Link
103
+ key={item.key}
104
+ flex
105
+ center
106
+ br="md"
107
+ paddingV="xs"
108
+ onPress={() => onChange(item.date)}
109
+ bg={isActive && 'primary'}
110
+ disabled={disabled}
111
+ >
112
+ <Text sm center strong={isActive} text2={!isActive}>
113
+ {item.label}
114
+ </Text>
115
+ {item.sublabel && (
116
+ <Text xxs center text4={!isActive} strong={isActive}>
117
+ {item.sublabel}
118
+ </Text>
119
+ )}
120
+ </Link>
121
+ )
122
+ })}
123
+ </View>
124
+ )
125
+ })
126
+
127
+ export function PeriodNavBar({ type = 'day', value, onChange, min, max, onCheckDisabled, ...props }) {
128
+ const config = TYPES[type]
129
+ const [localValue, setLocalValue] = React.useState(value)
130
+ const resolvedValue = value === undefined ? localValue : value
131
+
132
+ const [currentPage, setCurrentPage] = React.useState(() => {
133
+ const date = resolvedValue ? dayjs(resolvedValue) : dayjs()
134
+ return config.toPageValue(date)
135
+ })
136
+
137
+ React.useEffect(() => {
138
+ const date = resolvedValue ? dayjs(resolvedValue) : dayjs()
139
+ setCurrentPage(TYPES[type].toPageValue(date))
140
+ }, [resolvedValue, type])
141
+
142
+ const handleSelect = React.useCallback(
143
+ (date) => {
144
+ setLocalValue(date)
145
+ onChange?.(date)
146
+ },
147
+ [onChange]
148
+ )
149
+
150
+ const minPage = min ? config.toPageValue(dayjs(min)) : undefined
151
+ const maxPage = max ? config.toPageValue(dayjs(max)) : undefined
152
+
153
+ const renderSlide = (pageValue) => (
154
+ <PeriodSlide
155
+ items={config.getItems(pageValue)}
156
+ value={resolvedValue}
157
+ unit={config.unit}
158
+ onChange={handleSelect}
159
+ min={min}
160
+ max={max}
161
+ onCheckDisabled={onCheckDisabled}
162
+ />
163
+ )
164
+
165
+ return (
166
+ <View className="neko-period-nav-bar" {...props}>
167
+ <InfiniteCarousel
168
+ value={currentPage}
169
+ onChange={setCurrentPage}
170
+ renderSlide={renderSlide}
171
+ min={minPage}
172
+ max={maxPage}
173
+ />
174
+ </View>
175
+ )
176
+ }
@@ -1,12 +1,16 @@
1
1
  import { Text } from '../text/Text'
2
2
  import { View } from '../structure/View'
3
+ import { getFirstDayOfWeek } from '../../helpers/weekStart'
3
4
 
4
- const weekdayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
5
+ const ALL_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
5
6
 
6
7
  export function WeekDaysBar() {
8
+ const firstDay = getFirstDayOfWeek()
9
+ const labels = [...ALL_DAYS.slice(firstDay), ...ALL_DAYS.slice(0, firstDay)]
10
+
7
11
  return (
8
12
  <View className="neko-week-days-bar" row center gap="sm">
9
- {weekdayLabels.map((w) => (
13
+ {labels.map((w) => (
10
14
  <View key={w} flex height={30} center>
11
15
  <Text center sm text4>
12
16
  {w}
@@ -1,13 +1,17 @@
1
1
  import dayjs from 'dayjs'
2
2
  import React from 'react'
3
3
 
4
+ import { getFirstDayOfWeek } from '../../../helpers/weekStart'
5
+
4
6
  export function useCalendarDays(currentMonth) {
5
7
  return React.useMemo(() => {
6
8
  if (!currentMonth?.isValid?.()) currentMonth = dayjs()
7
9
  const startWeekday = currentMonth.startOf('month').day()
10
+ const firstDay = getFirstDayOfWeek()
11
+ const offset = (startWeekday - firstDay + 7) % 7
8
12
  const daysInMonth = currentMonth.daysInMonth()
9
13
 
10
- const blanks = Array.from({ length: startWeekday }, () => null)
14
+ const blanks = Array.from({ length: offset }, () => null)
11
15
  const days = Array.from({ length: daysInMonth }, (_, i) => i + 1)
12
16
  const cells = [...blanks, ...days]
13
17
 
@@ -1 +1 @@
1
- // export * from './DayPicker'
1
+ export * from './PeriodNavBar'
@@ -27,12 +27,20 @@ export function CarouselSlider() {
27
27
  const translateX = useSharedValue(0)
28
28
  const gestureStartX = useSharedValue(0)
29
29
  const prevItemsRef = React.useRef(items)
30
+ const gestureAnimatingRef = React.useRef(false)
31
+
32
+ const setGestureAnimating = React.useCallback((v) => {
33
+ gestureAnimatingRef.current = v
34
+ }, [])
30
35
 
31
36
  React.useEffect(() => {
32
37
  if (slideWidth > 0) {
33
38
  if (prevItemsRef.current !== items) {
34
39
  prevItemsRef.current = items
35
40
  translateX.value = -activeIndex * slideWidth
41
+ } else if (gestureAnimatingRef.current) {
42
+ // Gesture onEnd already animating — skip to avoid double animation
43
+ gestureAnimatingRef.current = false
36
44
  } else {
37
45
  translateX.value = withTiming(-activeIndex * slideWidth, { duration: 300 }, (finished) => {
38
46
  if (finished && afterChange) runOnJS(afterChange)(items?.[activeIndex]?.key, activeIndex)
@@ -87,7 +95,10 @@ export function CarouselSlider() {
87
95
  }
88
96
 
89
97
  const clamped = clampIndex(targetIndex, itemsCount, loop)
90
- translateX.value = withTiming(-clamped * slideWidth, { duration: 300 })
98
+ translateX.value = withTiming(-clamped * slideWidth, { duration: 300 }, (finished) => {
99
+ if (finished && afterChange) runOnJS(afterChange)(items?.[clamped]?.key, clamped)
100
+ })
101
+ runOnJS(setGestureAnimating)(true)
91
102
  runOnJS(goTo)(targetIndex)
92
103
  runOnJS(resumeAutoplay)()
93
104
  })
@@ -33,7 +33,7 @@ function formatValue(value, type) {
33
33
  case 'month':
34
34
  return [date.startOf('month'), date.endOf('month')]
35
35
  case 'week':
36
- return [date.startOf('isoWeek'), date.endOf('isoWeek')]
36
+ return [date.startOf('week'), date.endOf('week')]
37
37
  default:
38
38
  return [date.startOf('day'), date.endOf('day')]
39
39
  }
@@ -2,6 +2,7 @@ import { dissoc } from 'ramda'
2
2
  import React from 'react'
3
3
 
4
4
  import { Icon, IconLabel } from '../presentation'
5
+ import { KeyboardDismissButton } from '../keyboard'
5
6
  import { Link } from '../actions'
6
7
  import { LinkInput } from './LinkInput'
7
8
  import { Picker, getOptionLabel, searchOptions } from './Picker'
@@ -134,6 +135,8 @@ export function Select({
134
135
  maxHeight={popoverMaxHeight}
135
136
  {...popoverProps}
136
137
  renderContent={({ onClose }) => (
138
+ <>
139
+ {useBottomDrawer && useSearch && <KeyboardDismissButton />}
137
140
  <Picker
138
141
  row={false}
139
142
  options={searchOptions(options, search, { labelKey })}
@@ -152,21 +155,21 @@ export function Select({
152
155
  }}
153
156
  {...pickerProps}
154
157
  renderHeader={
155
- useBottomDrawer && useSearch
156
- ? () => (
157
- <>
158
- <View padding="md" paddingB="xs">
159
- <TextInput
160
- prefixIcon="search-line"
161
- prefixIconColor="text4"
162
- value={search}
163
- onChange={handleChangeSearch}
164
- />
165
- </View>
166
- {renderHeader?.()}
167
- </>
168
- )
169
- : renderHeader
158
+ useBottomDrawer && useSearch ? (
159
+ <>
160
+ <View padding="md" paddingB="xs">
161
+ <TextInput
162
+ prefixIcon="search-line"
163
+ prefixIconColor="text4"
164
+ value={search}
165
+ onChange={handleChangeSearch}
166
+ />
167
+ </View>
168
+ {renderHeader?.()}
169
+ </>
170
+ ) : (
171
+ renderHeader
172
+ )
170
173
  }
171
174
  renderOption={({ option, selected, onChange }) => (
172
175
  <Link
@@ -187,6 +190,7 @@ export function Select({
187
190
  </Link>
188
191
  )}
189
192
  />
193
+ </>
190
194
  )}
191
195
  >
192
196
  <Input
@@ -24,7 +24,9 @@ function fromMonthValue(v) {
24
24
  .startOf('month')
25
25
  }
26
26
 
27
- function MonthDays({ month, selectedValue, onSelect, min, max, onCheckDisabled }) {
27
+ const MonthDays = React.memo(function MonthDays({ monthValue, selectedKey, onSelect, min, max, onCheckDisabled }) {
28
+ const month = fromMonthValue(monthValue)
29
+ const selectedValue = selectedKey ? dayjs(selectedKey) : null
28
30
  const { cells } = useCalendarDays(month)
29
31
 
30
32
  return (
@@ -57,7 +59,7 @@ function MonthDays({ month, selectedValue, onSelect, min, max, onCheckDisabled }
57
59
  </Grid>
58
60
  </View>
59
61
  )
60
- }
62
+ })
61
63
 
62
64
  export function DayPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
63
65
  if (!!value) value = dayjs(value)
@@ -70,19 +72,23 @@ export function DayPicker({ value, onChange, min, max, onCheckDisabled, allowCle
70
72
  if (value?.isValid?.()) setCurrentMonth(value.startOf('month'))
71
73
  }, [value?.day?.(), value?.month?.(), value?.year?.()])
72
74
 
73
- const handleChange = (v) => {
74
- setLocalValue(v)
75
- onChange?.(v)
76
- }
75
+ const handleChange = React.useCallback(
76
+ (v) => {
77
+ setLocalValue(v)
78
+ onChange?.(v)
79
+ },
80
+ [onChange]
81
+ )
77
82
 
78
83
  const monthValue = toMonthValue(currentMonth)
79
84
  const minMonth = min ? toMonthValue(dayjs(min).startOf('month')) : undefined
80
85
  const maxMonth = max ? toMonthValue(dayjs(max).startOf('month')) : undefined
86
+ const selectedKey = value?.valueOf?.()
81
87
 
82
88
  const renderSlide = (v) => (
83
89
  <MonthDays
84
- month={fromMonthValue(v)}
85
- selectedValue={value}
90
+ monthValue={v}
91
+ selectedKey={selectedKey}
86
92
  onSelect={handleChange}
87
93
  min={min}
88
94
  max={max}
@@ -14,14 +14,15 @@ import { isDateDisabled } from '../../calendar/_helpers/dateDisabled'
14
14
 
15
15
  const months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
16
16
 
17
- function MonthGrid({ year, selectedValue, onSelect, min, max, onCheckDisabled }) {
17
+ const MonthGrid = React.memo(function MonthGrid({ year, selectedKey, onSelect, min, max, onCheckDisabled }) {
18
18
  const yearDate = dayjs().year(year).startOf('year')
19
+ const selectedValue = selectedKey ? dayjs(selectedKey) : null
19
20
 
20
21
  return (
21
22
  <Grid colSpan={8} gap="xs">
22
23
  {months.map((month) => {
23
24
  const dateVal = yearDate.month(month)
24
- const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'week')
25
+ const isActive = !!selectedValue && dateVal.isSame(selectedValue, 'month')
25
26
  const disabled = isDateDisabled(dateVal, { min, max, onCheckDisabled })
26
27
 
27
28
  return (
@@ -43,7 +44,7 @@ function MonthGrid({ year, selectedValue, onSelect, min, max, onCheckDisabled })
43
44
  })}
44
45
  </Grid>
45
46
  )
46
- }
47
+ })
47
48
 
48
49
  export function MonthPicker({ value, onChange, min, max, onCheckDisabled, allowClear, ...props }) {
49
50
  const [localValue, setLocalValue] = React.useState(value)
@@ -55,18 +56,22 @@ export function MonthPicker({ value, onChange, min, max, onCheckDisabled, allowC
55
56
  if (value?.isValid?.()) setCurrentYear(value.startOf('year'))
56
57
  }, [value?.month?.(), value?.year?.()])
57
58
 
58
- const handleChange = (v) => {
59
- const newValue = v.startOf('month')
60
- setLocalValue(newValue)
61
- onChange?.(newValue)
62
- }
59
+ const handleChange = React.useCallback(
60
+ (v) => {
61
+ const newValue = v.startOf('month')
62
+ setLocalValue(newValue)
63
+ onChange?.(newValue)
64
+ },
65
+ [onChange]
66
+ )
63
67
 
64
68
  const yearValue = currentYear.year()
65
69
  const minYear = min ? dayjs(min).year() : undefined
66
70
  const maxYear = max ? dayjs(max).year() : undefined
71
+ const selectedKey = value?.valueOf?.()
67
72
 
68
73
  const renderSlide = (v) => (
69
- <MonthGrid year={v} selectedValue={value} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
74
+ <MonthGrid year={v} selectedKey={selectedKey} onSelect={handleChange} min={min} max={max} onCheckDisabled={onCheckDisabled} />
70
75
  )
71
76
 
72
77
  return (