@sunsama/event-calendar 0.10.4 → 0.11.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/README.md +1 -0
- package/lib/commonjs/components/background-hours-content.js +12 -4
- package/lib/commonjs/components/background-hours-content.js.map +1 -1
- package/lib/commonjs/components/background-hours-layout.js +17 -9
- package/lib/commonjs/components/background-hours-layout.js.map +1 -1
- package/lib/commonjs/components/timed-events.js +2 -0
- package/lib/commonjs/components/timed-events.js.map +1 -1
- package/lib/commonjs/components/zoom-provider.js +6 -74
- package/lib/commonjs/components/zoom-provider.js.map +1 -1
- package/lib/commonjs/hooks/use-long-press-new-event.js +83 -0
- package/lib/commonjs/hooks/use-long-press-new-event.js.map +1 -0
- package/lib/commonjs/index.js +3 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/background-hours-content.js +12 -5
- package/lib/module/components/background-hours-content.js.map +1 -1
- package/lib/module/components/background-hours-layout.js +16 -9
- package/lib/module/components/background-hours-layout.js.map +1 -1
- package/lib/module/components/timed-events.js +2 -0
- package/lib/module/components/timed-events.js.map +1 -1
- package/lib/module/components/zoom-provider.js +7 -75
- package/lib/module/components/zoom-provider.js.map +1 -1
- package/lib/module/hooks/use-long-press-new-event.js +79 -0
- package/lib/module/hooks/use-long-press-new-event.js.map +1 -0
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/components/background-hours-content.d.ts +3 -1
- package/lib/typescript/commonjs/components/background-hours-content.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/background-hours-layout.d.ts +3 -1
- package/lib/typescript/commonjs/components/background-hours-layout.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/zoom-provider.d.ts +2 -2
- package/lib/typescript/commonjs/components/zoom-provider.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/use-long-press-new-event.d.ts +3 -0
- package/lib/typescript/commonjs/hooks/use-long-press-new-event.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/module/components/background-hours-content.d.ts +3 -1
- package/lib/typescript/module/components/background-hours-content.d.ts.map +1 -1
- package/lib/typescript/module/components/background-hours-layout.d.ts +3 -1
- package/lib/typescript/module/components/background-hours-layout.d.ts.map +1 -1
- package/lib/typescript/module/components/zoom-provider.d.ts +2 -2
- package/lib/typescript/module/components/zoom-provider.d.ts.map +1 -1
- package/lib/typescript/module/hooks/use-long-press-new-event.d.ts +3 -0
- package/lib/typescript/module/hooks/use-long-press-new-event.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/background-hours-content.tsx +24 -17
- package/src/components/background-hours-layout.tsx +25 -18
- package/src/components/timed-events.tsx +2 -2
- package/src/components/zoom-provider.tsx +54 -154
- package/src/hooks/use-long-press-new-event.ts +103 -0
- package/src/index.tsx +5 -1
|
@@ -1,38 +1,45 @@
|
|
|
1
|
-
import { memo, useContext } from "react";
|
|
1
|
+
import { memo, type RefObject, useContext } from "react";
|
|
2
2
|
import Animated, { useAnimatedStyle } from "react-native-reanimated";
|
|
3
3
|
import { StyleSheet, Text, View } from "react-native";
|
|
4
4
|
import { ConfigProvider, TOP_MARGIN_PIXEL_OFFSET } from "../utils/globals";
|
|
5
5
|
import { PrefabHour } from "../types";
|
|
6
|
+
import { GestureDetector } from "react-native-gesture-handler";
|
|
7
|
+
import useLongPressNewEvent from "src/hooks/use-long-press-new-event";
|
|
6
8
|
|
|
7
9
|
type BackgroundHoursLayoutProps = {
|
|
8
10
|
hours: PrefabHour[];
|
|
11
|
+
refNewEvent: RefObject<any>;
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
const BackgroundHoursLayout = memo(
|
|
12
|
-
({ hours }: BackgroundHoursLayoutProps) => {
|
|
15
|
+
({ refNewEvent, hours }: BackgroundHoursLayoutProps) => {
|
|
13
16
|
const { theme, zoomLevel } = useContext(ConfigProvider);
|
|
14
17
|
|
|
15
18
|
const styleHourSize = useAnimatedStyle(() => {
|
|
16
19
|
return { height: zoomLevel.value * 60 };
|
|
17
20
|
}, []);
|
|
18
21
|
|
|
22
|
+
const longPressNewEvent = useLongPressNewEvent(refNewEvent);
|
|
23
|
+
|
|
19
24
|
return (
|
|
20
|
-
<
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
<GestureDetector gesture={longPressNewEvent}>
|
|
26
|
+
<View style={[styles.hourContainer, theme?.backgroundHoursContainer]}>
|
|
27
|
+
{hours.map((hour) => (
|
|
28
|
+
<Animated.View
|
|
29
|
+
style={[
|
|
30
|
+
styles.hourInnerContainer,
|
|
31
|
+
theme?.backgroundHoursInnerContainer,
|
|
32
|
+
styleHourSize,
|
|
33
|
+
]}
|
|
34
|
+
key={hour.increment}
|
|
35
|
+
>
|
|
36
|
+
<Text style={[styles.hourText, theme?.backgroundHoursText]}>
|
|
37
|
+
{hour.hourFormatted}
|
|
38
|
+
</Text>
|
|
39
|
+
</Animated.View>
|
|
40
|
+
))}
|
|
41
|
+
</View>
|
|
42
|
+
</GestureDetector>
|
|
36
43
|
);
|
|
37
44
|
},
|
|
38
45
|
() => true
|
|
@@ -33,9 +33,9 @@ const TimedEvents = ({ refNewEvent }: TimedEventsProps) => {
|
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<View style={[styles.container, theme?.timedEventsContainer]}>
|
|
36
|
-
<BackgroundHoursLayout hours={hours} />
|
|
36
|
+
<BackgroundHoursLayout refNewEvent={refNewEvent} hours={hours} />
|
|
37
37
|
<View style={styles.backgroundContainer}>
|
|
38
|
-
<BackgroundHoursContent hours={hours} />
|
|
38
|
+
<BackgroundHoursContent refNewEvent={refNewEvent} hours={hours} />
|
|
39
39
|
{layout.partDayEventsLayout.map((partDayLayout) => (
|
|
40
40
|
<TimedEventContainer
|
|
41
41
|
key={partDayLayout.event.id}
|
|
@@ -4,10 +4,9 @@ import Animated, {
|
|
|
4
4
|
useSharedValue,
|
|
5
5
|
} from "react-native-reanimated";
|
|
6
6
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
7
|
-
import {
|
|
8
|
-
import { ConfigProvider
|
|
7
|
+
import { useContext, useEffect } from "react";
|
|
8
|
+
import { ConfigProvider } from "../utils/globals";
|
|
9
9
|
import { StyleSheet } from "react-native";
|
|
10
|
-
import { useIsEditing } from "../hooks/use-is-editing";
|
|
11
10
|
import doubleTapGesture from "../utils/double-tap-reset-zoom-gesture";
|
|
12
11
|
|
|
13
12
|
type ZoomProviderProps = {
|
|
@@ -17,160 +16,61 @@ type ZoomProviderProps = {
|
|
|
17
16
|
// This fraction determines how quickly zoom grows
|
|
18
17
|
const fraction = 0.1;
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.enabled(canCreateEvents && !isEditing)
|
|
73
|
-
.withRef(refNewEvent as any)
|
|
74
|
-
.numberOfPointers(1)
|
|
75
|
-
.minDuration(250)
|
|
76
|
-
.maxDistance(10000)
|
|
77
|
-
.onStart((event) => {
|
|
78
|
-
"worklet";
|
|
79
|
-
|
|
80
|
-
isDragging.value = true;
|
|
81
|
-
createY.value = Math.max(
|
|
82
|
-
0,
|
|
83
|
-
event.y - TOP_MARGIN_PIXEL_OFFSET - (zoomLevel.value * 60) / 2
|
|
84
|
-
);
|
|
85
|
-
})
|
|
86
|
-
.onTouchesMove((event) => {
|
|
87
|
-
"worklet";
|
|
88
|
-
|
|
89
|
-
if (!isDragging.value) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!fiveMinuteInterval) {
|
|
94
|
-
createY.value = Math.max(
|
|
95
|
-
0,
|
|
96
|
-
event.allTouches[0].y -
|
|
97
|
-
TOP_MARGIN_PIXEL_OFFSET -
|
|
98
|
-
(zoomLevel.value * 60) / 2
|
|
99
|
-
);
|
|
100
|
-
} else {
|
|
101
|
-
const normalizedY =
|
|
102
|
-
event.allTouches[0].y -
|
|
103
|
-
TOP_MARGIN_PIXEL_OFFSET -
|
|
104
|
-
(zoomLevel.value * 60) / 2;
|
|
105
|
-
const time = Math.floor(normalizedY / zoomLevel.value);
|
|
106
|
-
const hour = Math.floor(time / 60);
|
|
107
|
-
const minute = time - hour * 60;
|
|
108
|
-
const minuteInterval = Math.floor(minute / 5) * 5;
|
|
109
|
-
|
|
110
|
-
createY.value = (hour * 60 + minuteInterval) * zoomLevel.value;
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
.onEnd((event, success) => {
|
|
114
|
-
"worklet";
|
|
115
|
-
|
|
116
|
-
if (!isDragging.value) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Make sure it doesn't show the new event component anymore
|
|
121
|
-
createY.value = -1;
|
|
122
|
-
yPosition.value = -1;
|
|
123
|
-
isDragging.value = false;
|
|
124
|
-
|
|
125
|
-
if (!success) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Determine the hour that was clicked and trigger the event creation
|
|
130
|
-
const normalizedY =
|
|
131
|
-
event.y - TOP_MARGIN_PIXEL_OFFSET - (zoomLevel.value * 60) / 2;
|
|
132
|
-
const time = Math.floor(normalizedY / zoomLevel.value);
|
|
133
|
-
const hour = Math.floor(time / 60);
|
|
134
|
-
const minute = time - hour * 60;
|
|
135
|
-
|
|
136
|
-
if (!onCreateEvent) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (fiveMinuteInterval) {
|
|
141
|
-
const minuteInterval = Math.floor(minute / 5) * 5;
|
|
142
|
-
|
|
143
|
-
runOnJS(onCreateEvent)({
|
|
144
|
-
hour,
|
|
145
|
-
minute: minuteInterval,
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
runOnJS(onCreateEvent)({
|
|
151
|
-
hour,
|
|
152
|
-
minute,
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
const combinedGesture = Gesture.Simultaneous(
|
|
157
|
-
pinchGesture,
|
|
158
|
-
longPressGesture,
|
|
159
|
-
doubleTapGesture(zoomLevel, defaultZoomLevel, onZoomChange)
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return (
|
|
163
|
-
<GestureDetector gesture={combinedGesture}>
|
|
164
|
-
<Animated.View style={styles.container}>{children}</Animated.View>
|
|
165
|
-
</GestureDetector>
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
);
|
|
19
|
+
export default function ZoomProvider({ children }: ZoomProviderProps) {
|
|
20
|
+
const {
|
|
21
|
+
zoomLevel,
|
|
22
|
+
defaultZoomLevel,
|
|
23
|
+
maxZoomLevel,
|
|
24
|
+
minZoomLevel,
|
|
25
|
+
maximumHour,
|
|
26
|
+
onZoomChange,
|
|
27
|
+
} = useContext(ConfigProvider);
|
|
28
|
+
const previewScale = useSharedValue(-1);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
previewScale.value = zoomLevel.get();
|
|
32
|
+
}, [zoomLevel, previewScale]);
|
|
33
|
+
|
|
34
|
+
const pinchGesture = Gesture.Pinch()
|
|
35
|
+
.onUpdate((event) => {
|
|
36
|
+
"worklet";
|
|
37
|
+
|
|
38
|
+
const newScale = previewScale.value * (1 + fraction * (event.scale - 1));
|
|
39
|
+
|
|
40
|
+
zoomLevel.value = Math.min(
|
|
41
|
+
maxZoomLevel,
|
|
42
|
+
Math.max(minZoomLevel, newScale)
|
|
43
|
+
);
|
|
44
|
+
previewScale.value = zoomLevel.value;
|
|
45
|
+
})
|
|
46
|
+
.onEnd(() => {
|
|
47
|
+
if (onZoomChange) {
|
|
48
|
+
runOnJS(onZoomChange)(zoomLevel.value);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
useAnimatedReaction(
|
|
53
|
+
() => zoomLevel.value,
|
|
54
|
+
(zoom) => {
|
|
55
|
+
maximumHour.value = 1440 * zoom;
|
|
56
|
+
},
|
|
57
|
+
[maximumHour]
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const combinedGesture = Gesture.Simultaneous(
|
|
61
|
+
pinchGesture,
|
|
62
|
+
doubleTapGesture(zoomLevel, defaultZoomLevel, onZoomChange)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<GestureDetector gesture={combinedGesture}>
|
|
67
|
+
<Animated.View style={styles.container}>{children}</Animated.View>
|
|
68
|
+
</GestureDetector>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
169
71
|
|
|
170
72
|
const styles = StyleSheet.create({
|
|
171
73
|
container: {
|
|
172
74
|
flex: 1,
|
|
173
75
|
},
|
|
174
76
|
});
|
|
175
|
-
|
|
176
|
-
export default ZoomProvider;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Gesture } from "react-native-gesture-handler";
|
|
2
|
+
import { ConfigProvider, TOP_MARGIN_PIXEL_OFFSET } from "src/utils/globals";
|
|
3
|
+
import { runOnJS, useSharedValue } from "react-native-reanimated";
|
|
4
|
+
import { useIsEditing } from "src/hooks/use-is-editing";
|
|
5
|
+
import { type RefObject, useContext } from "react";
|
|
6
|
+
|
|
7
|
+
export default function useLongPressNewEvent(refNewEvent: RefObject<any>) {
|
|
8
|
+
const {
|
|
9
|
+
canCreateEvents,
|
|
10
|
+
zoomLevel,
|
|
11
|
+
createY,
|
|
12
|
+
onCreateEvent,
|
|
13
|
+
fiveMinuteInterval,
|
|
14
|
+
} = useContext(ConfigProvider);
|
|
15
|
+
const yPosition = useSharedValue(-1);
|
|
16
|
+
const { isEditing } = useIsEditing();
|
|
17
|
+
const isDragging = useSharedValue(false);
|
|
18
|
+
|
|
19
|
+
return Gesture.LongPress()
|
|
20
|
+
.enabled(canCreateEvents && !isEditing)
|
|
21
|
+
.withRef(refNewEvent as any)
|
|
22
|
+
.numberOfPointers(1)
|
|
23
|
+
.minDuration(250)
|
|
24
|
+
.maxDistance(10000)
|
|
25
|
+
.onStart((event) => {
|
|
26
|
+
"worklet";
|
|
27
|
+
|
|
28
|
+
isDragging.value = true;
|
|
29
|
+
createY.value = Math.max(
|
|
30
|
+
0,
|
|
31
|
+
event.y - TOP_MARGIN_PIXEL_OFFSET - (zoomLevel.value * 60) / 2
|
|
32
|
+
);
|
|
33
|
+
})
|
|
34
|
+
.onTouchesMove((event) => {
|
|
35
|
+
"worklet";
|
|
36
|
+
|
|
37
|
+
if (!isDragging.value) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!fiveMinuteInterval) {
|
|
42
|
+
createY.value = Math.max(
|
|
43
|
+
0,
|
|
44
|
+
event.allTouches[0].y -
|
|
45
|
+
TOP_MARGIN_PIXEL_OFFSET -
|
|
46
|
+
(zoomLevel.value * 60) / 2
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
const normalizedY =
|
|
50
|
+
event.allTouches[0].y -
|
|
51
|
+
TOP_MARGIN_PIXEL_OFFSET -
|
|
52
|
+
(zoomLevel.value * 60) / 2;
|
|
53
|
+
const time = Math.floor(normalizedY / zoomLevel.value);
|
|
54
|
+
const hour = Math.floor(time / 60);
|
|
55
|
+
const minute = time - hour * 60;
|
|
56
|
+
const minuteInterval = Math.floor(minute / 5) * 5;
|
|
57
|
+
|
|
58
|
+
createY.value = (hour * 60 + minuteInterval) * zoomLevel.value;
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.onEnd((event, success) => {
|
|
62
|
+
"worklet";
|
|
63
|
+
|
|
64
|
+
if (!isDragging.value) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Make sure it doesn't show the new event component anymore
|
|
69
|
+
createY.value = -1;
|
|
70
|
+
yPosition.value = -1;
|
|
71
|
+
isDragging.value = false;
|
|
72
|
+
|
|
73
|
+
if (!success) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Determine the hour that was clicked and trigger the event creation
|
|
78
|
+
const normalizedY =
|
|
79
|
+
event.y - TOP_MARGIN_PIXEL_OFFSET - (zoomLevel.value * 60) / 2;
|
|
80
|
+
const time = Math.floor(normalizedY / zoomLevel.value);
|
|
81
|
+
const hour = Math.floor(time / 60);
|
|
82
|
+
const minute = time - hour * 60;
|
|
83
|
+
|
|
84
|
+
if (!onCreateEvent) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (fiveMinuteInterval) {
|
|
89
|
+
const minuteInterval = Math.floor(minute / 5) * 5;
|
|
90
|
+
|
|
91
|
+
runOnJS(onCreateEvent)({
|
|
92
|
+
hour,
|
|
93
|
+
minute: minuteInterval,
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
runOnJS(onCreateEvent)({
|
|
99
|
+
hour,
|
|
100
|
+
minute,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -92,6 +92,7 @@ export interface EventCalendarMethods {
|
|
|
92
92
|
scrollToOffset: (y: number, animated?: boolean) => void;
|
|
93
93
|
startEditMode: (eventId: string) => void;
|
|
94
94
|
endEditMode: () => void;
|
|
95
|
+
setZoomLevel: (newZoomLevel: number) => void;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
function EventCalendarContentInner<T extends CalendarEvent>(
|
|
@@ -167,6 +168,9 @@ function EventCalendarContentInner<T extends CalendarEvent>(
|
|
|
167
168
|
|
|
168
169
|
refEditingProvider.current?.startEditing(layout);
|
|
169
170
|
},
|
|
171
|
+
setZoomLevel: (newZoomLevel: number) => {
|
|
172
|
+
zoomLevel.value = newZoomLevel;
|
|
173
|
+
},
|
|
170
174
|
}),
|
|
171
175
|
[zoomLevel, eventsLayout]
|
|
172
176
|
);
|
|
@@ -221,7 +225,7 @@ function EventCalendarContentInner<T extends CalendarEvent>(
|
|
|
221
225
|
onScroll={onScrollFeedback}
|
|
222
226
|
>
|
|
223
227
|
<IsEditingProvider ref={refEditingProvider}>
|
|
224
|
-
<ZoomProvider
|
|
228
|
+
<ZoomProvider>
|
|
225
229
|
<View style={[styles.borderContainer, theme?.borderContainer]} />
|
|
226
230
|
<TimedEvents refNewEvent={refNewEvent} />
|
|
227
231
|
</ZoomProvider>
|