expo-2k-datetime-picker 1.0.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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/android/build.gradle +18 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/java/expo/modules/calendar2k/Calendar2kModule.kt +15 -0
  6. package/android/src/main/java/expo/modules/calendar2k/Calendar2kView.kt +26 -0
  7. package/build/Calendar2k.types.d.ts +38 -0
  8. package/build/Calendar2k.types.js +2 -0
  9. package/build/Calendar2kModule.d.ts +8 -0
  10. package/build/Calendar2kModule.js +10 -0
  11. package/build/Calendar2kModule.web.d.ts +6 -0
  12. package/build/Calendar2kModule.web.js +26 -0
  13. package/build/Calendar2kView.d.ts +4 -0
  14. package/build/Calendar2kView.js +366 -0
  15. package/build/Calendar2kView.web.d.ts +3 -0
  16. package/build/Calendar2kView.web.js +49 -0
  17. package/build/assets/assets/images/calendar.png +0 -0
  18. package/build/assets/assets/images/chevron-left.png +0 -0
  19. package/build/assets/assets/images/chevron-right.png +0 -0
  20. package/build/assets/assets/images/clock.png +0 -0
  21. package/build/assets/assets/images/swipe.png +0 -0
  22. package/build/assets/images/calendar.png +0 -0
  23. package/build/assets/images/chevron-left.png +0 -0
  24. package/build/assets/images/chevron-right.png +0 -0
  25. package/build/assets/images/clock.png +0 -0
  26. package/build/assets/images/swipe.png +0 -0
  27. package/build/components/ArrowButton.d.ts +8 -0
  28. package/build/components/ArrowButton.js +19 -0
  29. package/build/components/Backdrop.d.ts +5 -0
  30. package/build/components/Backdrop.js +25 -0
  31. package/build/components/DayLabel.d.ts +6 -0
  32. package/build/components/DayLabel.js +16 -0
  33. package/build/components/Icon.d.ts +6 -0
  34. package/build/components/Icon.js +16 -0
  35. package/build/components/SwipeIcon.d.ts +5 -0
  36. package/build/components/SwipeIcon.js +8 -0
  37. package/build/components/Time.d.ts +11 -0
  38. package/build/components/Time.js +95 -0
  39. package/build/components/TimeView.d.ts +10 -0
  40. package/build/components/TimeView.js +25 -0
  41. package/build/hooks/useCalendarDisclose.d.ts +4 -0
  42. package/build/hooks/useCalendarDisclose.js +9 -0
  43. package/build/libs/format.d.ts +4 -0
  44. package/build/libs/format.js +52 -0
  45. package/build/libs/formatNb.d.ts +1 -0
  46. package/build/libs/formatNb.js +6 -0
  47. package/build/libs/getDayMatrix.d.ts +1 -0
  48. package/build/libs/getDayMatrix.js +74 -0
  49. package/build/libs/getStatus.d.ts +14 -0
  50. package/build/libs/getStatus.js +37 -0
  51. package/build/libs/getStyle.d.ts +14 -0
  52. package/build/libs/getStyle.js +35 -0
  53. package/build/libs/isActive.d.ts +8 -0
  54. package/build/libs/isActive.js +17 -0
  55. package/build/libs/isToday.d.ts +1 -0
  56. package/build/libs/isToday.js +9 -0
  57. package/build/libs/resetTime.d.ts +1 -0
  58. package/build/libs/resetTime.js +6 -0
  59. package/build/libs/toHexColor.d.ts +1 -0
  60. package/build/libs/toHexColor.js +13 -0
  61. package/build/ui/index.d.ts +15 -0
  62. package/build/ui/index.js +35 -0
  63. package/expo-module.config.json +9 -0
  64. package/ios/Calendar2k.podspec +23 -0
  65. package/ios/Calendar2kModule.swift +12 -0
  66. package/ios/Calendar2kView.swift +18 -0
  67. package/package.json +53 -0
  68. package/src/Calendar2k.types.ts +40 -0
  69. package/src/Calendar2kModule.ts +9 -0
  70. package/src/Calendar2kModule.web.ts +7 -0
  71. package/src/Calendar2kView.tsx +416 -0
  72. package/src/Calendar2kView.web.tsx +19 -0
  73. package/src/assets/images/calendar.png +0 -0
  74. package/src/assets/images/chevron-left.png +0 -0
  75. package/src/assets/images/chevron-right.png +0 -0
  76. package/src/assets/images/clock.png +0 -0
  77. package/src/assets/images/swipe.png +0 -0
  78. package/src/components/ArrowButton.tsx +40 -0
  79. package/src/components/Backdrop.tsx +31 -0
  80. package/src/components/DayLabel.tsx +20 -0
  81. package/src/components/Icon.tsx +24 -0
  82. package/src/components/SwipeIcon.tsx +13 -0
  83. package/src/components/Time.tsx +93 -0
  84. package/src/components/TimeView.tsx +64 -0
  85. package/src/hooks/useCalendarDisclose.ts +9 -0
  86. package/src/libs/format.ts +57 -0
  87. package/src/libs/formatNb.ts +3 -0
  88. package/src/libs/getDayMatrix.ts +73 -0
  89. package/src/libs/getStatus.ts +47 -0
  90. package/src/libs/getStyle.ts +53 -0
  91. package/src/libs/isActive.ts +20 -0
  92. package/src/libs/isToday.ts +9 -0
  93. package/src/libs/resetTime.ts +3 -0
  94. package/src/libs/toHexColor.ts +13 -0
  95. package/src/ui/index.tsx +51 -0
  96. package/tsconfig.json +22 -0
@@ -0,0 +1,93 @@
1
+ import React, { useEffect, useMemo, useRef } from "react";
2
+ import {
3
+ FlatList,
4
+ NativeScrollEvent,
5
+ NativeSyntheticEvent,
6
+ } from "react-native";
7
+ import formatNb from "../libs/formatNb";
8
+ import { KText, KView } from "../ui";
9
+
10
+ export default function Time({
11
+ length,
12
+ onScrollEnd,
13
+ color,
14
+ index = 0,
15
+ height = 46,
16
+ start = 0,
17
+ }: Props) {
18
+ const flatListRef = useRef<FlatList>(null);
19
+
20
+ const data = useMemo(() => {
21
+ let data = Array.from({ length });
22
+
23
+ if (start) data = data.map((_, i) => i + start);
24
+
25
+ return data;
26
+ }, [start]);
27
+
28
+ useEffect(() => {
29
+ if (start) index = data.findIndex((d) => d === index);
30
+
31
+ flatListRef.current?.scrollToIndex({ index, animated: true });
32
+ }, [flatListRef.current, data, start, index]);
33
+
34
+ const onMomentumScrollEnd = (
35
+ event: NativeSyntheticEvent<NativeScrollEvent>,
36
+ ) => {
37
+ const { contentOffset, layoutMeasurement } = event.nativeEvent;
38
+ let value = Math.round(contentOffset.y / layoutMeasurement.height);
39
+
40
+ if (start) value = Number(data[value]);
41
+
42
+ onScrollEnd?.(value);
43
+ };
44
+
45
+ const onScrollEndDrag = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
46
+ const { contentOffset } = event.nativeEvent;
47
+ const currentIndex = Math.round(contentOffset.y / height);
48
+
49
+ if (currentIndex === -1) {
50
+ flatListRef.current?.scrollToIndex({
51
+ index: data.length - 1,
52
+ animated: false,
53
+ });
54
+ } else if (currentIndex === data.length) {
55
+ flatListRef.current?.scrollToIndex({
56
+ index: 0,
57
+ animated: false,
58
+ });
59
+ }
60
+ };
61
+
62
+ return (
63
+ <FlatList
64
+ pagingEnabled
65
+ ref={flatListRef}
66
+ data={data}
67
+ showsVerticalScrollIndicator={false}
68
+ onScrollEndDrag={onScrollEndDrag}
69
+ onMomentumScrollEnd={onMomentumScrollEnd}
70
+ renderItem={({ item, index }) => (
71
+ <KView height={height} justifyContent="center" alignItems="center">
72
+ <KText fontSize={24} fontWeight="bold" color={color}>
73
+ {formatNb(item || index)}
74
+ </KText>
75
+ </KView>
76
+ )}
77
+ getItemLayout={(_, index) => ({
78
+ length: height,
79
+ offset: height * index,
80
+ index,
81
+ })}
82
+ />
83
+ );
84
+ }
85
+
86
+ type Props = {
87
+ length: number;
88
+ index?: number;
89
+ onScrollEnd?: (index: number) => void;
90
+ height?: number;
91
+ start?: number;
92
+ color?: string;
93
+ };
@@ -0,0 +1,64 @@
1
+ import React from "react";
2
+ import { formatShortDate } from "../libs/format";
3
+ import { KText, KView } from "../ui";
4
+ import Time from "./Time";
5
+
6
+ type Props = {
7
+ date: Date;
8
+ showDate?: boolean;
9
+ onChangeHours?: (hours: number) => void;
10
+ onChangeMinutes?: (minutes: number) => void;
11
+ color?: string;
12
+ };
13
+ export default function TimeView({
14
+ date,
15
+ onChangeHours,
16
+ onChangeMinutes,
17
+ showDate,
18
+ color,
19
+ }: Props) {
20
+ return (
21
+ <KView
22
+ flexDirection="row"
23
+ height={54}
24
+ backgroundColor={`${color}11`}
25
+ alignItems="center"
26
+ width={120}
27
+ alignSelf="center"
28
+ paddingHorizontal={8}
29
+ paddingVertical={4}
30
+ // borderWidth={3}
31
+ borderColor={`${color}11`}
32
+ >
33
+ {showDate && (
34
+ <KView
35
+ backgroundColor={color}
36
+ paddingHorizontal={10}
37
+ paddingVertical={1}
38
+ position="absolute"
39
+ top={-12}
40
+ left={19}
41
+ borderWidth={1}
42
+ borderColor="white"
43
+ >
44
+ <KText color="white">{formatShortDate(date)}</KText>
45
+ </KView>
46
+ )}
47
+ <Time
48
+ length={24}
49
+ index={date.getHours()}
50
+ onScrollEnd={onChangeHours}
51
+ color={color}
52
+ />
53
+ <KText fontSize={18} textAlign="center" fontWeight="bold" color={color}>
54
+ h
55
+ </KText>
56
+ <Time
57
+ length={60}
58
+ index={date.getMinutes()}
59
+ onScrollEnd={onChangeMinutes}
60
+ color={color}
61
+ />
62
+ </KView>
63
+ );
64
+ }
@@ -0,0 +1,9 @@
1
+ import { useState } from "react";
2
+
3
+ export default function useCalendarDisclose() {
4
+ const [isOpen, setIsOpen] = useState(false);
5
+
6
+ const onToggle = () => setIsOpen((v) => !v);
7
+
8
+ return { isOpen, onToggle };
9
+ }
@@ -0,0 +1,57 @@
1
+ export function formatDate(
2
+ date: Date,
3
+ locales: Intl.LocalesArgument = "fr-FR",
4
+ mode: "date" | "time" | "datetime" = "date",
5
+ ) {
6
+ switch (mode) {
7
+ case "time":
8
+ return date.toLocaleTimeString(locales, {
9
+ hour: "2-digit",
10
+ minute: "2-digit",
11
+ });
12
+
13
+ case "date":
14
+ return date.toLocaleDateString(locales, {
15
+ day: "2-digit",
16
+ month: "2-digit",
17
+ year: "numeric",
18
+ });
19
+
20
+ case "datetime":
21
+ return date.toLocaleDateString(locales, {
22
+ day: "2-digit",
23
+ month: "2-digit",
24
+ year: "numeric",
25
+ hour: "2-digit",
26
+ minute: "2-digit",
27
+ });
28
+
29
+ default:
30
+ return "Invalid mode";
31
+ }
32
+ }
33
+
34
+ export function formatMonth(
35
+ date: Date,
36
+ locales: Intl.LocalesArgument = "fr-FR",
37
+ ) {
38
+ return date.toLocaleDateString(locales, {
39
+ month: "long",
40
+ });
41
+ }
42
+
43
+ export function formatShortDate(
44
+ date: Date,
45
+ locales: Intl.LocalesArgument = "fr-FR",
46
+ ) {
47
+ return date.toLocaleDateString(locales, {
48
+ day: "2-digit",
49
+ month: "2-digit",
50
+ year: "2-digit",
51
+ });
52
+ }
53
+
54
+ export const getWeekDay = (
55
+ date: Date,
56
+ locales: Intl.LocalesArgument = "fr-FR",
57
+ ) => date.toLocaleString(locales, { weekday: "short" }).slice(0, 3);
@@ -0,0 +1,3 @@
1
+ export default function formatNb(nb: number) {
2
+ return nb < 10 ? "0" + nb.toString() : nb.toString();
3
+ }
@@ -0,0 +1,73 @@
1
+ export default function getDayMatrix(value: Date) {
2
+ const year = value.getFullYear();
3
+ const month = value.getMonth();
4
+
5
+ const offset = new Date(year, month, 1).getDay() - 1;
6
+ const getDate = (date: number) => new Date(year, month, date - offset);
7
+
8
+ return [
9
+ [
10
+ getDate(1),
11
+ getDate(8),
12
+ getDate(15),
13
+ getDate(22),
14
+ getDate(29),
15
+ getDate(36),
16
+ getDate(43),
17
+ ],
18
+ [
19
+ getDate(2),
20
+ getDate(9),
21
+ getDate(16),
22
+ getDate(23),
23
+ getDate(30),
24
+ getDate(37),
25
+ getDate(44),
26
+ ],
27
+ [
28
+ getDate(3),
29
+ getDate(10),
30
+ getDate(17),
31
+ getDate(24),
32
+ getDate(31),
33
+ getDate(38),
34
+ getDate(45),
35
+ ],
36
+ [
37
+ getDate(4),
38
+ getDate(11),
39
+ getDate(18),
40
+ getDate(25),
41
+ getDate(32),
42
+ getDate(39),
43
+ getDate(46),
44
+ ],
45
+ [
46
+ getDate(5),
47
+ getDate(12),
48
+ getDate(19),
49
+ getDate(26),
50
+ getDate(33),
51
+ getDate(40),
52
+ getDate(47),
53
+ ],
54
+ [
55
+ getDate(6),
56
+ getDate(13),
57
+ getDate(20),
58
+ getDate(27),
59
+ getDate(34),
60
+ getDate(41),
61
+ getDate(48),
62
+ ],
63
+ [
64
+ getDate(0),
65
+ getDate(7),
66
+ getDate(14),
67
+ getDate(21),
68
+ getDate(28),
69
+ getDate(35),
70
+ getDate(42),
71
+ ],
72
+ ];
73
+ }
@@ -0,0 +1,47 @@
1
+ import { DateValueType } from "../Calendar2k.types";
2
+ import isToday from "./isToday";
3
+ import resetTime from "./resetTime";
4
+
5
+ const getStatus = ({ date, shortDate, selectedDate }: ArgType) => {
6
+ const status = {
7
+ isToday: isToday(date),
8
+ isStartBorn: false,
9
+ isEndBorn: false,
10
+ isSelected: false,
11
+ isForCurrentMonth: date.getMonth() === shortDate.getMonth(),
12
+ };
13
+
14
+ if (selectedDate instanceof Date) {
15
+ const valueDate = resetTime(selectedDate);
16
+
17
+ status.isSelected = date.getTime() === valueDate.getTime();
18
+ } else if (
19
+ selectedDate instanceof Object &&
20
+ selectedDate.start instanceof Date
21
+ ) {
22
+ const start = resetTime(selectedDate.start);
23
+
24
+ status.isStartBorn = start.getTime() === date.getTime();
25
+
26
+ if (selectedDate.end) {
27
+ const end = resetTime(selectedDate.end as Date);
28
+
29
+ status.isSelected = start <= date && date <= end;
30
+
31
+ if (status.isSelected)
32
+ status.isEndBorn = date.getTime() === end.getTime();
33
+ } else {
34
+ status.isSelected = status.isStartBorn;
35
+ }
36
+ }
37
+
38
+ return status;
39
+ };
40
+
41
+ export default getStatus;
42
+
43
+ type ArgType = {
44
+ date: Date;
45
+ shortDate: Date;
46
+ selectedDate?: DateValueType;
47
+ };
@@ -0,0 +1,53 @@
1
+ import { TextStyle, ViewStyle } from "react-native";
2
+ import { DateValueType } from "../Calendar2k.types";
3
+ import getStatus from "./getStatus";
4
+
5
+ const getStyle = ({
6
+ date,
7
+ shortDate,
8
+ selectedDate,
9
+ color,
10
+ active,
11
+ }: ArgType) => {
12
+ const status = getStatus({ date, shortDate, selectedDate });
13
+
14
+ const style = { touchableOpacity: {} as ViewStyle, text: {} as TextStyle };
15
+
16
+ style.text.color = color;
17
+
18
+ if (!status.isForCurrentMonth) style.text.color = `${color}55`;
19
+ if (!active) style.text.color = `${color}22`;
20
+
21
+ if (status.isToday) {
22
+ style.touchableOpacity.backgroundColor = color;
23
+ style.text.color = "white";
24
+ style.text.fontWeight = "bold";
25
+ }
26
+
27
+ if (status.isSelected) {
28
+ style.touchableOpacity.backgroundColor = `${color}22`;
29
+ style.text.fontWeight = "bold";
30
+ }
31
+
32
+ if (status.isStartBorn) {
33
+ style.touchableOpacity.borderTopLeftRadius = 10;
34
+ style.touchableOpacity.borderBottomLeftRadius = 10;
35
+ }
36
+
37
+ if (status.isEndBorn) {
38
+ style.touchableOpacity.borderTopRightRadius = 10;
39
+ style.touchableOpacity.borderBottomRightRadius = 10;
40
+ }
41
+
42
+ return style;
43
+ };
44
+
45
+ export default getStyle;
46
+
47
+ type ArgType = {
48
+ date: Date;
49
+ shortDate: Date;
50
+ active?: boolean;
51
+ selectedDate?: DateValueType;
52
+ color?: string;
53
+ };
@@ -0,0 +1,20 @@
1
+ export default function isActive({ min, max, date, disableDate }: ArgType) {
2
+ if (!date) return false;
3
+
4
+ if (min && max)
5
+ return date.getTime() >= min.getTime() && date.getTime() <= max.getTime();
6
+
7
+ if (min) return date.getTime() >= min.getTime();
8
+ if (max) return date.getTime() <= max.getTime();
9
+
10
+ if (disableDate) return !disableDate(date);
11
+
12
+ return true;
13
+ }
14
+
15
+ type ArgType = {
16
+ min?: Date;
17
+ max?: Date;
18
+ date?: Date;
19
+ disableDate?: (date: Date) => boolean;
20
+ };
@@ -0,0 +1,9 @@
1
+ export default function isToday(date: Date) {
2
+ const today = new Date();
3
+
4
+ return (
5
+ today.getDate() === date.getDate() &&
6
+ today.getMonth() === date.getMonth() &&
7
+ today.getFullYear() === date.getFullYear()
8
+ );
9
+ }
@@ -0,0 +1,3 @@
1
+ export default function resetTime(date: Date) {
2
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
3
+ }
@@ -0,0 +1,13 @@
1
+ import { processColor } from "react-native";
2
+
3
+ export function toHexColor(color?: string) {
4
+ if (!color) return "#000000";
5
+
6
+ const processed = processColor(color);
7
+
8
+ if (!processed) return "#000000";
9
+
10
+ const hex = processed.toString(16).padStart(8, "0");
11
+
12
+ return `#${hex.slice(2)}`;
13
+ }
@@ -0,0 +1,51 @@
1
+ import { PropsWithChildren } from "react";
2
+ import {
3
+ Text,
4
+ TextProps,
5
+ TextStyle,
6
+ TouchableOpacity,
7
+ TouchableOpacityProps,
8
+ View,
9
+ ViewProps,
10
+ ViewStyle,
11
+ } from "react-native";
12
+
13
+ type KViewProps = ViewStyle &
14
+ PropsWithChildren & {
15
+ nativeProps?: ViewProps;
16
+ };
17
+ export function KView({ nativeProps, children, ...style }: KViewProps) {
18
+ return (
19
+ <View style={style} {...nativeProps}>
20
+ {children}
21
+ </View>
22
+ );
23
+ }
24
+
25
+ type KTouchableOpacityProps = ViewStyle &
26
+ PropsWithChildren & {
27
+ nativeProps?: TouchableOpacityProps;
28
+ };
29
+ export function KTouchableOpacity({
30
+ nativeProps,
31
+ children,
32
+ ...style
33
+ }: KTouchableOpacityProps) {
34
+ return (
35
+ <TouchableOpacity style={style} {...nativeProps}>
36
+ {children}
37
+ </TouchableOpacity>
38
+ );
39
+ }
40
+
41
+ type KTextProps = TextStyle &
42
+ PropsWithChildren & {
43
+ nativeProps?: TextProps;
44
+ };
45
+ export function KText({ nativeProps, children, ...style }: KTextProps) {
46
+ return (
47
+ <Text style={style} {...nativeProps}>
48
+ {children}
49
+ </Text>
50
+ );
51
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "./build",
4
+ "declaration": true,
5
+ "esModuleInterop": true,
6
+ "allowSyntheticDefaultImports": true,
7
+ "skipLibCheck": true,
8
+ "jsx": "react-native",
9
+ "lib": [
10
+ "esnext"
11
+ ]
12
+ },
13
+ "exclude": [
14
+ "node_modules",
15
+ "../../node_modules"
16
+ ],
17
+ "include": [
18
+ "src/**/*",
19
+ "index.ts",
20
+ "assets/**/*",
21
+ ],
22
+ }