react-native-resource-calendar 1.0.18 β 1.0.20
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 +15 -4
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +67 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,8 +42,7 @@ react-native-gesture-handler \
|
|
|
42
42
|
react-native-reanimated \
|
|
43
43
|
react-native-svg \
|
|
44
44
|
@shopify/flash-list \
|
|
45
|
-
@shopify/react-native-skia
|
|
46
|
-
expo-haptics
|
|
45
|
+
@shopify/react-native-skia
|
|
47
46
|
```
|
|
48
47
|
|
|
49
48
|
If youβre using bare React Native (not Expo), install them manually:
|
|
@@ -54,8 +53,18 @@ react-native-gesture-handler \
|
|
|
54
53
|
react-native-reanimated \
|
|
55
54
|
react-native-svg \
|
|
56
55
|
@shopify/flash-list \
|
|
57
|
-
@shopify/react-native-skia
|
|
58
|
-
|
|
56
|
+
@shopify/react-native-skia
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
π¦ Optional: Haptics Support (Expo Only)
|
|
60
|
+
|
|
61
|
+
Haptic feedback is optional.
|
|
62
|
+
If you want to enable vibration feedback when interacting with components, install the Expo Haptics package and set enableHapticFeedback to true in your component config.
|
|
63
|
+
|
|
64
|
+
π¦ Install (Expo)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx expo install expo-haptics
|
|
59
68
|
```
|
|
60
69
|
|
|
61
70
|
---
|
|
@@ -264,12 +273,14 @@ The `Calendar` component accepts a flexible set of props for customizing layout,
|
|
|
264
273
|
| **`snapIntervalInMinutes`** | `number` | `5` | Drag/resize snapping granularity (in minutes). |
|
|
265
274
|
| **`overLappingLayoutMode`** | `LayoutMode` (`'stacked' \| 'columns'`) | `'stacked'` | Strategy to lay out overlapping events inside a column. |
|
|
266
275
|
| **`theme`** | `CalendarTheme` | β | Typography & palette overrides. |
|
|
276
|
+
| **`enableHapticFeedback`** | `boolean` | `false` | Enable haptic feedback. |
|
|
267
277
|
| **`eventSlots`** | `EventSlots` | β | Slot renderers to customize event content (e.g. `{ Body, TopRight }`). |
|
|
268
278
|
| **`eventStyleOverrides`** | `StyleOverrides \| ((event: Event) => StyleOverrides \| undefined)` | β | Per-event style override (object or function). |
|
|
269
279
|
| **`isEventSelected`** | `(event: Event) => boolean` | `() => false` | Marks which events are currently selected. |
|
|
270
280
|
| **`isEventDisabled`** | `(event: Event) => boolean` | `() => false` | Marks events as disabled (non-interactive). |
|
|
271
281
|
| **`onResourcePress`** | `(resource: Resource) => void` | β | Invoked when a resource header is pressed. |
|
|
272
282
|
| **`onBlockLongPress`** | `(resource: Resource, date: Date) => void` | β | Long-press on an empty block (grid). |
|
|
283
|
+
| **`onBlockTap`** | `(resource: Resource, date: Date) => void` | β | Tap on an empty block (grid). |
|
|
273
284
|
| **`onDisabledBlockPress`** | `(block: DisabledBlock) => void` | β | Tap on a disabled block (e.g., lunch). |
|
|
274
285
|
| **`onEventPress`** | `(event: Event) => void` | β | Tap on an event. |
|
|
275
286
|
| **`onEventLongPress`** | `(event: Event) => void` | β | Long-press on an event. The calendar also preps internal drag state here. |
|
package/dist/index.d.mts
CHANGED
|
@@ -86,9 +86,11 @@ interface CalendarProps {
|
|
|
86
86
|
hourHeight?: number;
|
|
87
87
|
onResourcePress?: (resource: Resource) => void;
|
|
88
88
|
onBlockLongPress?: (resource: Resource, date: Date) => void;
|
|
89
|
+
onBlockTap?: (resource: Resource, date: Date) => void;
|
|
89
90
|
onDisabledBlockPress?: (block: DisabledBlock) => void;
|
|
90
91
|
onEventPress?: (event: Event) => void;
|
|
91
92
|
onEventLongPress?: (event: Event) => void;
|
|
93
|
+
enableHapticFeedback?: boolean;
|
|
92
94
|
eventSlots?: EventSlots;
|
|
93
95
|
eventStyleOverrides?: StyleOverrides | ((event: Event) => StyleOverrides | undefined);
|
|
94
96
|
isEventSelected?: FlagFn;
|
package/dist/index.d.ts
CHANGED
|
@@ -86,9 +86,11 @@ interface CalendarProps {
|
|
|
86
86
|
hourHeight?: number;
|
|
87
87
|
onResourcePress?: (resource: Resource) => void;
|
|
88
88
|
onBlockLongPress?: (resource: Resource, date: Date) => void;
|
|
89
|
+
onBlockTap?: (resource: Resource, date: Date) => void;
|
|
89
90
|
onDisabledBlockPress?: (block: DisabledBlock) => void;
|
|
90
91
|
onEventPress?: (event: Event) => void;
|
|
91
92
|
onEventLongPress?: (event: Event) => void;
|
|
93
|
+
enableHapticFeedback?: boolean;
|
|
92
94
|
eventSlots?: EventSlots;
|
|
93
95
|
eventStyleOverrides?: StyleOverrides | ((event: Event) => StyleOverrides | undefined);
|
|
94
96
|
isEventSelected?: FlagFn;
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,6 @@ var reactNativeGestureHandler = require('react-native-gesture-handler');
|
|
|
5
5
|
var Animated2 = require('react-native-reanimated');
|
|
6
6
|
var reactNative = require('react-native');
|
|
7
7
|
var flashList = require('@shopify/flash-list');
|
|
8
|
-
var Haptics = require('expo-haptics');
|
|
9
8
|
var dateFnsTz = require('date-fns-tz');
|
|
10
9
|
var dateFns = require('date-fns');
|
|
11
10
|
var lodash = require('lodash');
|
|
@@ -39,7 +38,6 @@ function _interopNamespace(e) {
|
|
|
39
38
|
|
|
40
39
|
var React19__namespace = /*#__PURE__*/_interopNamespace(React19);
|
|
41
40
|
var Animated2__default = /*#__PURE__*/_interopDefault(Animated2);
|
|
42
|
-
var Haptics__namespace = /*#__PURE__*/_interopNamespace(Haptics);
|
|
43
41
|
var Svg__default = /*#__PURE__*/_interopDefault(Svg);
|
|
44
42
|
|
|
45
43
|
// src/components/Calendar.tsx
|
|
@@ -747,6 +745,7 @@ function StaffAvatar({
|
|
|
747
745
|
}
|
|
748
746
|
var EventGridBlocksSkia = ({
|
|
749
747
|
handleBlockPress,
|
|
748
|
+
handleBlockLongPress,
|
|
750
749
|
hourHeight,
|
|
751
750
|
APPOINTMENT_BLOCK_WIDTH
|
|
752
751
|
}) => {
|
|
@@ -788,6 +787,16 @@ var EventGridBlocksSkia = ({
|
|
|
788
787
|
},
|
|
789
788
|
[handleBlockPress, timeLabels]
|
|
790
789
|
);
|
|
790
|
+
const onSlotLongPress = React19__namespace.useCallback(
|
|
791
|
+
(row) => {
|
|
792
|
+
setPressedRow(null);
|
|
793
|
+
const slot = timeLabels[row];
|
|
794
|
+
if (slot) {
|
|
795
|
+
handleBlockLongPress(slot);
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
[timeLabels, handleBlockLongPress]
|
|
799
|
+
);
|
|
791
800
|
const onPressBegin = React19__namespace.useCallback((row) => {
|
|
792
801
|
setPressedRow(row);
|
|
793
802
|
}, []);
|
|
@@ -797,14 +806,31 @@ var EventGridBlocksSkia = ({
|
|
|
797
806
|
const longPressGesture = reactNativeGestureHandler.Gesture.LongPress().onBegin((e) => {
|
|
798
807
|
"worklet";
|
|
799
808
|
Animated2.runOnJS(onPressBegin)(Math.floor(e.y / rowHeight));
|
|
809
|
+
}).onTouchesUp(() => {
|
|
810
|
+
"worklet";
|
|
811
|
+
Animated2.runOnJS(onTouchesUp)();
|
|
812
|
+
}).onEnd((e) => {
|
|
813
|
+
"worklet";
|
|
814
|
+
Animated2.runOnJS(onSlotLongPress)(Math.floor(e.y / rowHeight));
|
|
815
|
+
}).onFinalize(() => {
|
|
816
|
+
"worklet";
|
|
817
|
+
Animated2.runOnJS(onTouchesUp)();
|
|
818
|
+
});
|
|
819
|
+
const tapGesture = reactNativeGestureHandler.Gesture.Tap().onBegin((e) => {
|
|
820
|
+
"worklet";
|
|
821
|
+
Animated2.runOnJS(onPressBegin)(Math.floor(e.y / rowHeight));
|
|
800
822
|
}).onEnd((e) => {
|
|
801
823
|
"worklet";
|
|
802
824
|
Animated2.runOnJS(onSlotPress)(Math.floor(e.y / rowHeight));
|
|
825
|
+
}).onTouchesUp(() => {
|
|
826
|
+
"worklet";
|
|
827
|
+
Animated2.runOnJS(onTouchesUp)();
|
|
803
828
|
}).onFinalize(() => {
|
|
804
829
|
"worklet";
|
|
805
830
|
Animated2.runOnJS(onTouchesUp)();
|
|
806
831
|
});
|
|
807
|
-
|
|
832
|
+
const composedGesture = reactNativeGestureHandler.Gesture.Race(longPressGesture, tapGesture);
|
|
833
|
+
return /* @__PURE__ */ React19__namespace.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composedGesture }, /* @__PURE__ */ React19__namespace.createElement(reactNative.View, null, /* @__PURE__ */ React19__namespace.createElement(reactNativeSkia.Canvas, { style: { width: APPOINTMENT_BLOCK_WIDTH, height: segmentHeight } }, firstRects.map(({ x, y, width: w, height: h, row }, idx) => /* @__PURE__ */ React19__namespace.createElement(React19__namespace.Fragment, { key: idx }, /* @__PURE__ */ React19__namespace.createElement(
|
|
808
834
|
reactNativeSkia.Rect,
|
|
809
835
|
{
|
|
810
836
|
x,
|
|
@@ -1294,7 +1320,7 @@ var DaysComponent = ({ onResourcePress, activeResourceId, mode, date, APPOINTMEN
|
|
|
1294
1320
|
width: APPOINTMENT_BLOCK_WIDTH
|
|
1295
1321
|
},
|
|
1296
1322
|
space: 4,
|
|
1297
|
-
key:
|
|
1323
|
+
key: d.toString()
|
|
1298
1324
|
},
|
|
1299
1325
|
/* @__PURE__ */ React19__namespace.createElement(Center_default, { style: {
|
|
1300
1326
|
backgroundColor: selected ? "#4d959c" : void 0,
|
|
@@ -1346,9 +1372,11 @@ var CalendarInner = (props) => {
|
|
|
1346
1372
|
resources,
|
|
1347
1373
|
onResourcePress,
|
|
1348
1374
|
onBlockLongPress,
|
|
1375
|
+
onBlockTap,
|
|
1349
1376
|
onEventPress,
|
|
1350
1377
|
onEventLongPress,
|
|
1351
1378
|
onDisabledBlockPress,
|
|
1379
|
+
enableHapticFeedback = false,
|
|
1352
1380
|
eventSlots,
|
|
1353
1381
|
eventStyleOverrides,
|
|
1354
1382
|
overLappingLayoutMode = "stacked",
|
|
@@ -1437,8 +1465,9 @@ var CalendarInner = (props) => {
|
|
|
1437
1465
|
React19.useEffect(() => {
|
|
1438
1466
|
if (!selectedEvent) {
|
|
1439
1467
|
setDraggedEventDraft(null);
|
|
1468
|
+
setDragReady(false);
|
|
1440
1469
|
}
|
|
1441
|
-
}, [selectedEvent]);
|
|
1470
|
+
}, [selectedEvent, setSelectedEvent, setDraggedEventDraft]);
|
|
1442
1471
|
React19.useEffect(() => {
|
|
1443
1472
|
scrollX.value = 0;
|
|
1444
1473
|
}, [mode]);
|
|
@@ -1447,6 +1476,7 @@ var CalendarInner = (props) => {
|
|
|
1447
1476
|
const flashListRef = React19.useRef(null);
|
|
1448
1477
|
const prevResourceIdsRef = React19.useRef([]);
|
|
1449
1478
|
const [layout, setLayout] = React19.useState(null);
|
|
1479
|
+
const [dragReady, setDragReady] = React19.useState(false);
|
|
1450
1480
|
const dateRef = React19.useRef(date);
|
|
1451
1481
|
const eventStartedTop = Animated2.useSharedValue(0);
|
|
1452
1482
|
const eventHeight = Animated2.useSharedValue(0);
|
|
@@ -1463,9 +1493,19 @@ var CalendarInner = (props) => {
|
|
|
1463
1493
|
const startedX = Animated2.useSharedValue(0);
|
|
1464
1494
|
const startedY = Animated2.useSharedValue(0);
|
|
1465
1495
|
const touchY = Animated2.useSharedValue(0);
|
|
1466
|
-
const triggerHaptic = React19.useCallback(
|
|
1467
|
-
|
|
1468
|
-
|
|
1496
|
+
const triggerHaptic = React19.useCallback(
|
|
1497
|
+
async (style = "Light") => {
|
|
1498
|
+
try {
|
|
1499
|
+
const Haptics = await import('expo-haptics');
|
|
1500
|
+
const feedbackStyle = Haptics.ImpactFeedbackStyle[style];
|
|
1501
|
+
if (enableHapticFeedback)
|
|
1502
|
+
await Haptics.impactAsync(feedbackStyle);
|
|
1503
|
+
} catch (e) {
|
|
1504
|
+
console.log("Haptics not available, skipping...");
|
|
1505
|
+
}
|
|
1506
|
+
},
|
|
1507
|
+
[enableHapticFeedback]
|
|
1508
|
+
);
|
|
1469
1509
|
const resourceIds = React19.useMemo(() => {
|
|
1470
1510
|
const ids = resources?.map((item) => item?.id) || [];
|
|
1471
1511
|
if (JSON.stringify(prevResourceIdsRef.current) !== JSON.stringify(ids)) {
|
|
@@ -1626,7 +1666,7 @@ var CalendarInner = (props) => {
|
|
|
1626
1666
|
const increment = APPOINTMENT_BLOCK_WIDTH * Math.sign(autoScrollXSpeed.value);
|
|
1627
1667
|
const newScrollX = scrollX.value + increment;
|
|
1628
1668
|
Animated2.runOnJS(scrollListTo)(newScrollX);
|
|
1629
|
-
Animated2.runOnJS(
|
|
1669
|
+
Animated2.runOnJS(triggerHaptic)("Medium");
|
|
1630
1670
|
}
|
|
1631
1671
|
});
|
|
1632
1672
|
Animated2.useFrameCallback(() => {
|
|
@@ -1660,7 +1700,7 @@ var CalendarInner = (props) => {
|
|
|
1660
1700
|
const scrollDiff = Math.abs(newScrollY - lastHapticScrollY.value);
|
|
1661
1701
|
if (scrollDiff >= snapInterval) {
|
|
1662
1702
|
lastHapticScrollY.value = newScrollY;
|
|
1663
|
-
Animated2.runOnJS(
|
|
1703
|
+
Animated2.runOnJS(triggerHaptic)("Medium");
|
|
1664
1704
|
}
|
|
1665
1705
|
});
|
|
1666
1706
|
React19.useEffect(() => {
|
|
@@ -1693,7 +1733,8 @@ var CalendarInner = (props) => {
|
|
|
1693
1733
|
lastHapticScrollY.value = scrollY.value;
|
|
1694
1734
|
eventHeight.value = initialHeight;
|
|
1695
1735
|
setSelectedEvent(event);
|
|
1696
|
-
|
|
1736
|
+
requestAnimationFrame(() => setDragReady(true));
|
|
1737
|
+
Animated2.runOnJS(triggerHaptic)("Medium");
|
|
1697
1738
|
};
|
|
1698
1739
|
}, []);
|
|
1699
1740
|
const internalStableOnLongPress = React19.useCallback((e) => {
|
|
@@ -1716,22 +1757,30 @@ var CalendarInner = (props) => {
|
|
|
1716
1757
|
}
|
|
1717
1758
|
}
|
|
1718
1759
|
});
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1760
|
+
const handleBlockLongPress = React19.useCallback((resourceId, time) => {
|
|
1761
|
+
Animated2.runOnJS(triggerHaptic)("Medium");
|
|
1721
1762
|
const resource = resources.find((r) => r.id === resourceId);
|
|
1722
1763
|
if (onBlockLongPress)
|
|
1723
1764
|
onBlockLongPress(resource, new Date(time));
|
|
1724
1765
|
}, [resources, onBlockLongPress]);
|
|
1766
|
+
const handleBlockPress = React19.useCallback((resourceId, time) => {
|
|
1767
|
+
Animated2.runOnJS(triggerHaptic)("Medium");
|
|
1768
|
+
const resource = resources.find((r) => r.id === resourceId);
|
|
1769
|
+
if (onBlockTap)
|
|
1770
|
+
onBlockTap(resource, new Date(time));
|
|
1771
|
+
}, [resources, onBlockTap]);
|
|
1725
1772
|
React19.useEffect(() => {
|
|
1726
1773
|
const handleOrientationChange = () => {
|
|
1727
|
-
if (selectedEvent)
|
|
1774
|
+
if (selectedEvent) {
|
|
1728
1775
|
setSelectedEvent(null);
|
|
1776
|
+
setDragReady(false);
|
|
1777
|
+
}
|
|
1729
1778
|
};
|
|
1730
1779
|
const subscription = reactNative.Dimensions.addEventListener("change", handleOrientationChange);
|
|
1731
1780
|
return () => {
|
|
1732
1781
|
subscription.remove();
|
|
1733
1782
|
};
|
|
1734
|
-
}, [setSelectedEvent, selectedEvent]);
|
|
1783
|
+
}, [setSelectedEvent, selectedEvent, setDragReady]);
|
|
1735
1784
|
React19.useEffect(() => {
|
|
1736
1785
|
dateRef.current = date;
|
|
1737
1786
|
}, [date]);
|
|
@@ -1743,7 +1792,8 @@ var CalendarInner = (props) => {
|
|
|
1743
1792
|
{
|
|
1744
1793
|
hourHeight,
|
|
1745
1794
|
APPOINTMENT_BLOCK_WIDTH,
|
|
1746
|
-
handleBlockPress: (time) => handleBlockPress(rid, combineDateAndTime(dayDate ?? dateRef.current, time))
|
|
1795
|
+
handleBlockPress: (time) => handleBlockPress(rid, combineDateAndTime(dayDate ?? dateRef.current, time)),
|
|
1796
|
+
handleBlockLongPress: (time) => handleBlockLongPress(rid, combineDateAndTime(dayDate ?? dateRef.current, time))
|
|
1747
1797
|
}
|
|
1748
1798
|
), /* @__PURE__ */ React19__namespace.default.createElement(
|
|
1749
1799
|
DisabledIntervals_default,
|
|
@@ -1890,7 +1940,7 @@ var CalendarInner = (props) => {
|
|
|
1890
1940
|
}
|
|
1891
1941
|
)
|
|
1892
1942
|
),
|
|
1893
|
-
selectedEvent && /* @__PURE__ */ React19__namespace.default.createElement(
|
|
1943
|
+
selectedEvent && dragReady && /* @__PURE__ */ React19__namespace.default.createElement(
|
|
1894
1944
|
DraggableEvent,
|
|
1895
1945
|
{
|
|
1896
1946
|
selectedEvent,
|