react-native-resource-calendar 1.0.19 β†’ 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 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
- expo-haptics
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
- return /* @__PURE__ */ React19__namespace.createElement(reactNativeGestureHandler.GestureDetector, { gesture: longPressGesture }, /* @__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(
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,
@@ -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",
@@ -1465,9 +1493,19 @@ var CalendarInner = (props) => {
1465
1493
  const startedX = Animated2.useSharedValue(0);
1466
1494
  const startedY = Animated2.useSharedValue(0);
1467
1495
  const touchY = Animated2.useSharedValue(0);
1468
- const triggerHaptic = React19.useCallback(() => {
1469
- Haptics__namespace.impactAsync(Haptics__namespace.ImpactFeedbackStyle.Light);
1470
- }, []);
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
+ );
1471
1509
  const resourceIds = React19.useMemo(() => {
1472
1510
  const ids = resources?.map((item) => item?.id) || [];
1473
1511
  if (JSON.stringify(prevResourceIdsRef.current) !== JSON.stringify(ids)) {
@@ -1628,7 +1666,7 @@ var CalendarInner = (props) => {
1628
1666
  const increment = APPOINTMENT_BLOCK_WIDTH * Math.sign(autoScrollXSpeed.value);
1629
1667
  const newScrollX = scrollX.value + increment;
1630
1668
  Animated2.runOnJS(scrollListTo)(newScrollX);
1631
- Animated2.runOnJS(Haptics__namespace.impactAsync)(Haptics__namespace.ImpactFeedbackStyle.Medium);
1669
+ Animated2.runOnJS(triggerHaptic)("Medium");
1632
1670
  }
1633
1671
  });
1634
1672
  Animated2.useFrameCallback(() => {
@@ -1662,7 +1700,7 @@ var CalendarInner = (props) => {
1662
1700
  const scrollDiff = Math.abs(newScrollY - lastHapticScrollY.value);
1663
1701
  if (scrollDiff >= snapInterval) {
1664
1702
  lastHapticScrollY.value = newScrollY;
1665
- Animated2.runOnJS(Haptics__namespace.impactAsync)(Haptics__namespace.ImpactFeedbackStyle.Medium);
1703
+ Animated2.runOnJS(triggerHaptic)("Medium");
1666
1704
  }
1667
1705
  });
1668
1706
  React19.useEffect(() => {
@@ -1696,7 +1734,7 @@ var CalendarInner = (props) => {
1696
1734
  eventHeight.value = initialHeight;
1697
1735
  setSelectedEvent(event);
1698
1736
  requestAnimationFrame(() => setDragReady(true));
1699
- Haptics__namespace.impactAsync(Haptics__namespace.ImpactFeedbackStyle.Medium);
1737
+ Animated2.runOnJS(triggerHaptic)("Medium");
1700
1738
  };
1701
1739
  }, []);
1702
1740
  const internalStableOnLongPress = React19.useCallback((e) => {
@@ -1719,12 +1757,18 @@ var CalendarInner = (props) => {
1719
1757
  }
1720
1758
  }
1721
1759
  });
1722
- const handleBlockPress = React19.useCallback((resourceId, time) => {
1723
- Haptics__namespace.impactAsync(Haptics__namespace.ImpactFeedbackStyle.Medium);
1760
+ const handleBlockLongPress = React19.useCallback((resourceId, time) => {
1761
+ Animated2.runOnJS(triggerHaptic)("Medium");
1724
1762
  const resource = resources.find((r) => r.id === resourceId);
1725
1763
  if (onBlockLongPress)
1726
1764
  onBlockLongPress(resource, new Date(time));
1727
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]);
1728
1772
  React19.useEffect(() => {
1729
1773
  const handleOrientationChange = () => {
1730
1774
  if (selectedEvent) {
@@ -1748,7 +1792,8 @@ var CalendarInner = (props) => {
1748
1792
  {
1749
1793
  hourHeight,
1750
1794
  APPOINTMENT_BLOCK_WIDTH,
1751
- 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))
1752
1797
  }
1753
1798
  ), /* @__PURE__ */ React19__namespace.default.createElement(
1754
1799
  DisabledIntervals_default,