react-native-agenda-kit 0.1.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.
- package/LICENSE +21 -0
- package/README.md +364 -0
- package/lib/commonjs/agenda/AgendaBackdrop.js +68 -0
- package/lib/commonjs/agenda/AgendaBackdrop.js.map +1 -0
- package/lib/commonjs/agenda/AgendaList.js +354 -0
- package/lib/commonjs/agenda/AgendaList.js.map +1 -0
- package/lib/commonjs/agenda/AgendaSlot.js +88 -0
- package/lib/commonjs/agenda/AgendaSlot.js.map +1 -0
- package/lib/commonjs/agenda/AgendaWindow.js +171 -0
- package/lib/commonjs/agenda/AgendaWindow.js.map +1 -0
- package/lib/commonjs/agenda/StickyHeaderOverlay.js +125 -0
- package/lib/commonjs/agenda/StickyHeaderOverlay.js.map +1 -0
- package/lib/commonjs/agenda/buildAgendaLayout.js +100 -0
- package/lib/commonjs/agenda/buildAgendaLayout.js.map +1 -0
- package/lib/commonjs/agenda/constants.js +29 -0
- package/lib/commonjs/agenda/constants.js.map +1 -0
- package/lib/commonjs/agenda/heights.js +49 -0
- package/lib/commonjs/agenda/heights.js.map +1 -0
- package/lib/commonjs/agenda/useAgendaScrollSync.js +204 -0
- package/lib/commonjs/agenda/useAgendaScrollSync.js.map +1 -0
- package/lib/commonjs/core/ArrowGlyph.js +65 -0
- package/lib/commonjs/core/ArrowGlyph.js.map +1 -0
- package/lib/commonjs/core/CalendarContext.js +32 -0
- package/lib/commonjs/core/CalendarContext.js.map +1 -0
- package/lib/commonjs/core/CalendarProvider.js +141 -0
- package/lib/commonjs/core/CalendarProvider.js.map +1 -0
- package/lib/commonjs/core/CalendarStore.js +70 -0
- package/lib/commonjs/core/CalendarStore.js.map +1 -0
- package/lib/commonjs/core/KitText.js +23 -0
- package/lib/commonjs/core/KitText.js.map +1 -0
- package/lib/commonjs/core/TodayButton.js +136 -0
- package/lib/commonjs/core/TodayButton.js.map +1 -0
- package/lib/commonjs/core/civil.js +101 -0
- package/lib/commonjs/core/civil.js.map +1 -0
- package/lib/commonjs/core/dateKey.js +56 -0
- package/lib/commonjs/core/dateKey.js.map +1 -0
- package/lib/commonjs/core/motion.js +56 -0
- package/lib/commonjs/core/motion.js.map +1 -0
- package/lib/commonjs/core/worklets.js +44 -0
- package/lib/commonjs/core/worklets.js.map +1 -0
- package/lib/commonjs/expandable/DayCell.js +120 -0
- package/lib/commonjs/expandable/DayCell.js.map +1 -0
- package/lib/commonjs/expandable/ExpandableCalendar.js +280 -0
- package/lib/commonjs/expandable/ExpandableCalendar.js.map +1 -0
- package/lib/commonjs/expandable/Knob.js +72 -0
- package/lib/commonjs/expandable/Knob.js.map +1 -0
- package/lib/commonjs/expandable/MonthPager.js +180 -0
- package/lib/commonjs/expandable/MonthPager.js.map +1 -0
- package/lib/commonjs/expandable/WeekStrip.js +451 -0
- package/lib/commonjs/expandable/WeekStrip.js.map +1 -0
- package/lib/commonjs/expandable/calendarCells.js +152 -0
- package/lib/commonjs/expandable/calendarCells.js.map +1 -0
- package/lib/commonjs/expandable/geometry.js +62 -0
- package/lib/commonjs/expandable/geometry.js.map +1 -0
- package/lib/commonjs/expandable/useExpandGesture.js +124 -0
- package/lib/commonjs/expandable/useExpandGesture.js.map +1 -0
- package/lib/commonjs/expandable/useHorizontalPager.js +97 -0
- package/lib/commonjs/expandable/useHorizontalPager.js.map +1 -0
- package/lib/commonjs/index.js +167 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/theme/ThemeProvider.js +73 -0
- package/lib/commonjs/theme/ThemeProvider.js.map +1 -0
- package/lib/commonjs/theme/index.js +75 -0
- package/lib/commonjs/theme/index.js.map +1 -0
- package/lib/commonjs/theme/theme.js +79 -0
- package/lib/commonjs/theme/theme.js.map +1 -0
- package/lib/commonjs/theme/tokens.js +54 -0
- package/lib/commonjs/theme/tokens.js.map +1 -0
- package/lib/commonjs/timeline/DayColumn.js +95 -0
- package/lib/commonjs/timeline/DayColumn.js.map +1 -0
- package/lib/commonjs/timeline/DayPager.js +250 -0
- package/lib/commonjs/timeline/DayPager.js.map +1 -0
- package/lib/commonjs/timeline/EventBlock.js +91 -0
- package/lib/commonjs/timeline/EventBlock.js.map +1 -0
- package/lib/commonjs/timeline/GridLines.js +47 -0
- package/lib/commonjs/timeline/GridLines.js.map +1 -0
- package/lib/commonjs/timeline/HourGrid.js +96 -0
- package/lib/commonjs/timeline/HourGrid.js.map +1 -0
- package/lib/commonjs/timeline/NowIndicator.js +132 -0
- package/lib/commonjs/timeline/NowIndicator.js.map +1 -0
- package/lib/commonjs/timeline/TimelineDay.js +126 -0
- package/lib/commonjs/timeline/TimelineDay.js.map +1 -0
- package/lib/commonjs/timeline/packEvents.js +102 -0
- package/lib/commonjs/timeline/packEvents.js.map +1 -0
- package/lib/commonjs/timeline/timelineGeometry.js +27 -0
- package/lib/commonjs/timeline/timelineGeometry.js.map +1 -0
- package/lib/commonjs/timeline/timelineMath.js +82 -0
- package/lib/commonjs/timeline/timelineMath.js.map +1 -0
- package/lib/commonjs/types.js +71 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/agenda/AgendaBackdrop.js +63 -0
- package/lib/module/agenda/AgendaBackdrop.js.map +1 -0
- package/lib/module/agenda/AgendaList.js +349 -0
- package/lib/module/agenda/AgendaList.js.map +1 -0
- package/lib/module/agenda/AgendaSlot.js +84 -0
- package/lib/module/agenda/AgendaSlot.js.map +1 -0
- package/lib/module/agenda/AgendaWindow.js +167 -0
- package/lib/module/agenda/AgendaWindow.js.map +1 -0
- package/lib/module/agenda/StickyHeaderOverlay.js +120 -0
- package/lib/module/agenda/StickyHeaderOverlay.js.map +1 -0
- package/lib/module/agenda/buildAgendaLayout.js +95 -0
- package/lib/module/agenda/buildAgendaLayout.js.map +1 -0
- package/lib/module/agenda/constants.js +25 -0
- package/lib/module/agenda/constants.js.map +1 -0
- package/lib/module/agenda/heights.js +42 -0
- package/lib/module/agenda/heights.js.map +1 -0
- package/lib/module/agenda/useAgendaScrollSync.js +200 -0
- package/lib/module/agenda/useAgendaScrollSync.js.map +1 -0
- package/lib/module/core/ArrowGlyph.js +60 -0
- package/lib/module/core/ArrowGlyph.js.map +1 -0
- package/lib/module/core/CalendarContext.js +26 -0
- package/lib/module/core/CalendarContext.js.map +1 -0
- package/lib/module/core/CalendarProvider.js +136 -0
- package/lib/module/core/CalendarProvider.js.map +1 -0
- package/lib/module/core/CalendarStore.js +66 -0
- package/lib/module/core/CalendarStore.js.map +1 -0
- package/lib/module/core/KitText.js +19 -0
- package/lib/module/core/KitText.js.map +1 -0
- package/lib/module/core/TodayButton.js +133 -0
- package/lib/module/core/TodayButton.js.map +1 -0
- package/lib/module/core/civil.js +88 -0
- package/lib/module/core/civil.js.map +1 -0
- package/lib/module/core/dateKey.js +47 -0
- package/lib/module/core/dateKey.js.map +1 -0
- package/lib/module/core/motion.js +49 -0
- package/lib/module/core/motion.js.map +1 -0
- package/lib/module/core/worklets.js +38 -0
- package/lib/module/core/worklets.js.map +1 -0
- package/lib/module/expandable/DayCell.js +116 -0
- package/lib/module/expandable/DayCell.js.map +1 -0
- package/lib/module/expandable/ExpandableCalendar.js +275 -0
- package/lib/module/expandable/ExpandableCalendar.js.map +1 -0
- package/lib/module/expandable/Knob.js +67 -0
- package/lib/module/expandable/Knob.js.map +1 -0
- package/lib/module/expandable/MonthPager.js +175 -0
- package/lib/module/expandable/MonthPager.js.map +1 -0
- package/lib/module/expandable/WeekStrip.js +446 -0
- package/lib/module/expandable/WeekStrip.js.map +1 -0
- package/lib/module/expandable/calendarCells.js +142 -0
- package/lib/module/expandable/calendarCells.js.map +1 -0
- package/lib/module/expandable/geometry.js +58 -0
- package/lib/module/expandable/geometry.js.map +1 -0
- package/lib/module/expandable/useExpandGesture.js +118 -0
- package/lib/module/expandable/useExpandGesture.js.map +1 -0
- package/lib/module/expandable/useHorizontalPager.js +92 -0
- package/lib/module/expandable/useHorizontalPager.js.map +1 -0
- package/lib/module/index.js +35 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/theme/ThemeProvider.js +65 -0
- package/lib/module/theme/ThemeProvider.js.map +1 -0
- package/lib/module/theme/index.js +6 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/theme.js +74 -0
- package/lib/module/theme/theme.js.map +1 -0
- package/lib/module/theme/tokens.js +50 -0
- package/lib/module/theme/tokens.js.map +1 -0
- package/lib/module/timeline/DayColumn.js +91 -0
- package/lib/module/timeline/DayColumn.js.map +1 -0
- package/lib/module/timeline/DayPager.js +245 -0
- package/lib/module/timeline/DayPager.js.map +1 -0
- package/lib/module/timeline/EventBlock.js +87 -0
- package/lib/module/timeline/EventBlock.js.map +1 -0
- package/lib/module/timeline/GridLines.js +43 -0
- package/lib/module/timeline/GridLines.js.map +1 -0
- package/lib/module/timeline/HourGrid.js +94 -0
- package/lib/module/timeline/HourGrid.js.map +1 -0
- package/lib/module/timeline/NowIndicator.js +126 -0
- package/lib/module/timeline/NowIndicator.js.map +1 -0
- package/lib/module/timeline/TimelineDay.js +122 -0
- package/lib/module/timeline/TimelineDay.js.map +1 -0
- package/lib/module/timeline/packEvents.js +97 -0
- package/lib/module/timeline/packEvents.js.map +1 -0
- package/lib/module/timeline/timelineGeometry.js +22 -0
- package/lib/module/timeline/timelineGeometry.js.map +1 -0
- package/lib/module/timeline/timelineMath.js +75 -0
- package/lib/module/timeline/timelineMath.js.map +1 -0
- package/lib/module/types.js +67 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/commonjs/agenda/AgendaBackdrop.d.ts +20 -0
- package/lib/typescript/commonjs/agenda/AgendaBackdrop.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/AgendaList.d.ts +12 -0
- package/lib/typescript/commonjs/agenda/AgendaList.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/AgendaSlot.d.ts +45 -0
- package/lib/typescript/commonjs/agenda/AgendaSlot.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/AgendaWindow.d.ts +49 -0
- package/lib/typescript/commonjs/agenda/AgendaWindow.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/StickyHeaderOverlay.d.ts +39 -0
- package/lib/typescript/commonjs/agenda/StickyHeaderOverlay.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/buildAgendaLayout.d.ts +57 -0
- package/lib/typescript/commonjs/agenda/buildAgendaLayout.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/constants.d.ts +19 -0
- package/lib/typescript/commonjs/agenda/constants.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/heights.d.ts +20 -0
- package/lib/typescript/commonjs/agenda/heights.d.ts.map +1 -0
- package/lib/typescript/commonjs/agenda/useAgendaScrollSync.d.ts +44 -0
- package/lib/typescript/commonjs/agenda/useAgendaScrollSync.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/ArrowGlyph.d.ts +14 -0
- package/lib/typescript/commonjs/core/ArrowGlyph.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/CalendarContext.d.ts +35 -0
- package/lib/typescript/commonjs/core/CalendarContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/CalendarProvider.d.ts +41 -0
- package/lib/typescript/commonjs/core/CalendarProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/CalendarStore.d.ts +28 -0
- package/lib/typescript/commonjs/core/CalendarStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/KitText.d.ts +10 -0
- package/lib/typescript/commonjs/core/KitText.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/TodayButton.d.ts +18 -0
- package/lib/typescript/commonjs/core/TodayButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/civil.d.ts +26 -0
- package/lib/typescript/commonjs/core/civil.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/dateKey.d.ts +21 -0
- package/lib/typescript/commonjs/core/dateKey.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/motion.d.ts +18 -0
- package/lib/typescript/commonjs/core/motion.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/worklets.d.ts +12 -0
- package/lib/typescript/commonjs/core/worklets.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/DayCell.d.ts +22 -0
- package/lib/typescript/commonjs/expandable/DayCell.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/ExpandableCalendar.d.ts +40 -0
- package/lib/typescript/commonjs/expandable/ExpandableCalendar.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/Knob.d.ts +19 -0
- package/lib/typescript/commonjs/expandable/Knob.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/MonthPager.d.ts +35 -0
- package/lib/typescript/commonjs/expandable/MonthPager.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/WeekStrip.d.ts +34 -0
- package/lib/typescript/commonjs/expandable/WeekStrip.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/calendarCells.d.ts +52 -0
- package/lib/typescript/commonjs/expandable/calendarCells.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/geometry.d.ts +43 -0
- package/lib/typescript/commonjs/expandable/geometry.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/useExpandGesture.d.ts +35 -0
- package/lib/typescript/commonjs/expandable/useExpandGesture.d.ts.map +1 -0
- package/lib/typescript/commonjs/expandable/useHorizontalPager.d.ts +35 -0
- package/lib/typescript/commonjs/expandable/useHorizontalPager.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +26 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/theme/ThemeProvider.d.ts +27 -0
- package/lib/typescript/commonjs/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/index.d.ts +4 -0
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/theme.d.ts +62 -0
- package/lib/typescript/commonjs/theme/theme.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/tokens.d.ts +46 -0
- package/lib/typescript/commonjs/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/DayColumn.d.ts +32 -0
- package/lib/typescript/commonjs/timeline/DayColumn.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/DayPager.d.ts +32 -0
- package/lib/typescript/commonjs/timeline/DayPager.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/EventBlock.d.ts +17 -0
- package/lib/typescript/commonjs/timeline/EventBlock.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/GridLines.d.ts +5 -0
- package/lib/typescript/commonjs/timeline/GridLines.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/HourGrid.d.ts +12 -0
- package/lib/typescript/commonjs/timeline/HourGrid.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/NowIndicator.d.ts +28 -0
- package/lib/typescript/commonjs/timeline/NowIndicator.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/TimelineDay.d.ts +14 -0
- package/lib/typescript/commonjs/timeline/TimelineDay.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/packEvents.d.ts +25 -0
- package/lib/typescript/commonjs/timeline/packEvents.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/timelineGeometry.d.ts +14 -0
- package/lib/typescript/commonjs/timeline/timelineGeometry.d.ts.map +1 -0
- package/lib/typescript/commonjs/timeline/timelineMath.d.ts +32 -0
- package/lib/typescript/commonjs/timeline/timelineMath.d.ts.map +1 -0
- package/lib/typescript/commonjs/types.d.ts +237 -0
- package/lib/typescript/commonjs/types.d.ts.map +1 -0
- package/lib/typescript/module/agenda/AgendaBackdrop.d.ts +20 -0
- package/lib/typescript/module/agenda/AgendaBackdrop.d.ts.map +1 -0
- package/lib/typescript/module/agenda/AgendaList.d.ts +12 -0
- package/lib/typescript/module/agenda/AgendaList.d.ts.map +1 -0
- package/lib/typescript/module/agenda/AgendaSlot.d.ts +45 -0
- package/lib/typescript/module/agenda/AgendaSlot.d.ts.map +1 -0
- package/lib/typescript/module/agenda/AgendaWindow.d.ts +49 -0
- package/lib/typescript/module/agenda/AgendaWindow.d.ts.map +1 -0
- package/lib/typescript/module/agenda/StickyHeaderOverlay.d.ts +39 -0
- package/lib/typescript/module/agenda/StickyHeaderOverlay.d.ts.map +1 -0
- package/lib/typescript/module/agenda/buildAgendaLayout.d.ts +57 -0
- package/lib/typescript/module/agenda/buildAgendaLayout.d.ts.map +1 -0
- package/lib/typescript/module/agenda/constants.d.ts +19 -0
- package/lib/typescript/module/agenda/constants.d.ts.map +1 -0
- package/lib/typescript/module/agenda/heights.d.ts +20 -0
- package/lib/typescript/module/agenda/heights.d.ts.map +1 -0
- package/lib/typescript/module/agenda/useAgendaScrollSync.d.ts +44 -0
- package/lib/typescript/module/agenda/useAgendaScrollSync.d.ts.map +1 -0
- package/lib/typescript/module/core/ArrowGlyph.d.ts +14 -0
- package/lib/typescript/module/core/ArrowGlyph.d.ts.map +1 -0
- package/lib/typescript/module/core/CalendarContext.d.ts +35 -0
- package/lib/typescript/module/core/CalendarContext.d.ts.map +1 -0
- package/lib/typescript/module/core/CalendarProvider.d.ts +41 -0
- package/lib/typescript/module/core/CalendarProvider.d.ts.map +1 -0
- package/lib/typescript/module/core/CalendarStore.d.ts +28 -0
- package/lib/typescript/module/core/CalendarStore.d.ts.map +1 -0
- package/lib/typescript/module/core/KitText.d.ts +10 -0
- package/lib/typescript/module/core/KitText.d.ts.map +1 -0
- package/lib/typescript/module/core/TodayButton.d.ts +18 -0
- package/lib/typescript/module/core/TodayButton.d.ts.map +1 -0
- package/lib/typescript/module/core/civil.d.ts +26 -0
- package/lib/typescript/module/core/civil.d.ts.map +1 -0
- package/lib/typescript/module/core/dateKey.d.ts +21 -0
- package/lib/typescript/module/core/dateKey.d.ts.map +1 -0
- package/lib/typescript/module/core/motion.d.ts +18 -0
- package/lib/typescript/module/core/motion.d.ts.map +1 -0
- package/lib/typescript/module/core/worklets.d.ts +12 -0
- package/lib/typescript/module/core/worklets.d.ts.map +1 -0
- package/lib/typescript/module/expandable/DayCell.d.ts +22 -0
- package/lib/typescript/module/expandable/DayCell.d.ts.map +1 -0
- package/lib/typescript/module/expandable/ExpandableCalendar.d.ts +40 -0
- package/lib/typescript/module/expandable/ExpandableCalendar.d.ts.map +1 -0
- package/lib/typescript/module/expandable/Knob.d.ts +19 -0
- package/lib/typescript/module/expandable/Knob.d.ts.map +1 -0
- package/lib/typescript/module/expandable/MonthPager.d.ts +35 -0
- package/lib/typescript/module/expandable/MonthPager.d.ts.map +1 -0
- package/lib/typescript/module/expandable/WeekStrip.d.ts +34 -0
- package/lib/typescript/module/expandable/WeekStrip.d.ts.map +1 -0
- package/lib/typescript/module/expandable/calendarCells.d.ts +52 -0
- package/lib/typescript/module/expandable/calendarCells.d.ts.map +1 -0
- package/lib/typescript/module/expandable/geometry.d.ts +43 -0
- package/lib/typescript/module/expandable/geometry.d.ts.map +1 -0
- package/lib/typescript/module/expandable/useExpandGesture.d.ts +35 -0
- package/lib/typescript/module/expandable/useExpandGesture.d.ts.map +1 -0
- package/lib/typescript/module/expandable/useHorizontalPager.d.ts +35 -0
- package/lib/typescript/module/expandable/useHorizontalPager.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +26 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/theme/ThemeProvider.d.ts +27 -0
- package/lib/typescript/module/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/module/theme/index.d.ts +4 -0
- package/lib/typescript/module/theme/index.d.ts.map +1 -0
- package/lib/typescript/module/theme/theme.d.ts +62 -0
- package/lib/typescript/module/theme/theme.d.ts.map +1 -0
- package/lib/typescript/module/theme/tokens.d.ts +46 -0
- package/lib/typescript/module/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/module/timeline/DayColumn.d.ts +32 -0
- package/lib/typescript/module/timeline/DayColumn.d.ts.map +1 -0
- package/lib/typescript/module/timeline/DayPager.d.ts +32 -0
- package/lib/typescript/module/timeline/DayPager.d.ts.map +1 -0
- package/lib/typescript/module/timeline/EventBlock.d.ts +17 -0
- package/lib/typescript/module/timeline/EventBlock.d.ts.map +1 -0
- package/lib/typescript/module/timeline/GridLines.d.ts +5 -0
- package/lib/typescript/module/timeline/GridLines.d.ts.map +1 -0
- package/lib/typescript/module/timeline/HourGrid.d.ts +12 -0
- package/lib/typescript/module/timeline/HourGrid.d.ts.map +1 -0
- package/lib/typescript/module/timeline/NowIndicator.d.ts +28 -0
- package/lib/typescript/module/timeline/NowIndicator.d.ts.map +1 -0
- package/lib/typescript/module/timeline/TimelineDay.d.ts +14 -0
- package/lib/typescript/module/timeline/TimelineDay.d.ts.map +1 -0
- package/lib/typescript/module/timeline/packEvents.d.ts +25 -0
- package/lib/typescript/module/timeline/packEvents.d.ts.map +1 -0
- package/lib/typescript/module/timeline/timelineGeometry.d.ts +14 -0
- package/lib/typescript/module/timeline/timelineGeometry.d.ts.map +1 -0
- package/lib/typescript/module/timeline/timelineMath.d.ts +32 -0
- package/lib/typescript/module/timeline/timelineMath.d.ts.map +1 -0
- package/lib/typescript/module/types.d.ts +237 -0
- package/lib/typescript/module/types.d.ts.map +1 -0
- package/package.json +105 -0
- package/src/agenda/AgendaBackdrop.tsx +55 -0
- package/src/agenda/AgendaList.tsx +370 -0
- package/src/agenda/AgendaSlot.tsx +88 -0
- package/src/agenda/AgendaWindow.tsx +183 -0
- package/src/agenda/StickyHeaderOverlay.tsx +126 -0
- package/src/agenda/buildAgendaLayout.ts +110 -0
- package/src/agenda/constants.ts +22 -0
- package/src/agenda/heights.ts +44 -0
- package/src/agenda/useAgendaScrollSync.ts +251 -0
- package/src/core/ArrowGlyph.tsx +53 -0
- package/src/core/CalendarContext.ts +47 -0
- package/src/core/CalendarProvider.tsx +165 -0
- package/src/core/CalendarStore.ts +64 -0
- package/src/core/KitText.tsx +10 -0
- package/src/core/TodayButton.tsx +121 -0
- package/src/core/civil.ts +92 -0
- package/src/core/dateKey.ts +52 -0
- package/src/core/motion.ts +51 -0
- package/src/core/worklets.ts +37 -0
- package/src/expandable/DayCell.tsx +97 -0
- package/src/expandable/ExpandableCalendar.tsx +278 -0
- package/src/expandable/Knob.tsx +67 -0
- package/src/expandable/MonthPager.tsx +208 -0
- package/src/expandable/WeekStrip.tsx +439 -0
- package/src/expandable/calendarCells.ts +155 -0
- package/src/expandable/geometry.ts +51 -0
- package/src/expandable/useExpandGesture.ts +147 -0
- package/src/expandable/useHorizontalPager.ts +116 -0
- package/src/index.ts +62 -0
- package/src/theme/ThemeProvider.tsx +85 -0
- package/src/theme/index.ts +18 -0
- package/src/theme/theme.ts +110 -0
- package/src/theme/tokens.ts +50 -0
- package/src/timeline/DayColumn.tsx +84 -0
- package/src/timeline/DayPager.tsx +295 -0
- package/src/timeline/EventBlock.tsx +76 -0
- package/src/timeline/GridLines.tsx +36 -0
- package/src/timeline/HourGrid.tsx +90 -0
- package/src/timeline/NowIndicator.tsx +123 -0
- package/src/timeline/TimelineDay.tsx +112 -0
- package/src/timeline/packEvents.ts +118 -0
- package/src/timeline/timelineGeometry.ts +23 -0
- package/src/timeline/timelineMath.ts +86 -0
- package/src/types.ts +252 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agenda: one `Animated.ScrollView`, absolute slots over a declared layout,
|
|
3
|
+
* a skeleton backdrop behind it and the stuck header over it.
|
|
4
|
+
*
|
|
5
|
+
* Compat with the react-native-calendars screen's `AgendaList` JSX, plus two things the library
|
|
6
|
+
* only pretended to do: real sticky headers and real pull-to-refresh.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
memo,
|
|
11
|
+
useCallback,
|
|
12
|
+
useImperativeHandle,
|
|
13
|
+
useLayoutEffect,
|
|
14
|
+
useMemo,
|
|
15
|
+
useRef,
|
|
16
|
+
useState,
|
|
17
|
+
} from 'react';
|
|
18
|
+
import { RefreshControl, StyleSheet, View, type LayoutChangeEvent } from 'react-native';
|
|
19
|
+
import Animated, {
|
|
20
|
+
scrollTo,
|
|
21
|
+
useAnimatedReaction,
|
|
22
|
+
useAnimatedRef,
|
|
23
|
+
useSharedValue,
|
|
24
|
+
type AnimatedRef,
|
|
25
|
+
type SharedValue,
|
|
26
|
+
} from 'react-native-reanimated';
|
|
27
|
+
import { runOnJS, scheduleOnUI } from 'react-native-worklets';
|
|
28
|
+
import { useKitTheme } from '../theme';
|
|
29
|
+
import { useCalendarContext } from '../core/CalendarContext';
|
|
30
|
+
import { clampIndex, dayIndexOf } from '../core/dateKey';
|
|
31
|
+
import { bsearchLE } from '../core/worklets';
|
|
32
|
+
import type { AgendaListProps, DateString, MoveOrigin } from '../types';
|
|
33
|
+
import { AgendaBackdrop } from './AgendaBackdrop';
|
|
34
|
+
import { AgendaWindow } from './AgendaWindow';
|
|
35
|
+
import { buildAgendaLayout, type AgendaRowRef } from './buildAgendaLayout';
|
|
36
|
+
import { BUCKET_REST } from './constants';
|
|
37
|
+
import { StickyHeaderOverlay, type StickyInputs } from './StickyHeaderOverlay';
|
|
38
|
+
import { useAgendaScrollSync, type AgendaTables } from './useAgendaScrollSync';
|
|
39
|
+
|
|
40
|
+
const MY_ORIGIN: MoveOrigin = 'agenda';
|
|
41
|
+
const EMPTY_TABLES: AgendaTables = { offsets: [], sectionOffset: [], sectionStart: [], monthOfSection: [] };
|
|
42
|
+
|
|
43
|
+
const defaultGetItemType = (item: unknown): string =>
|
|
44
|
+
(item as { itemCustomHeightType?: string })?.itemCustomHeightType ?? 'Regular';
|
|
45
|
+
|
|
46
|
+
const defaultKeyExtractor = (item: unknown, index: number): string =>
|
|
47
|
+
(item as { id?: string })?.id ?? `row-${index}`;
|
|
48
|
+
|
|
49
|
+
/** Every scroll this list issues goes through here; see `programmaticY` in the sync. */
|
|
50
|
+
const scrollListTo = (
|
|
51
|
+
ref: AnimatedRef<Animated.ScrollView>,
|
|
52
|
+
programmaticY: SharedValue<number>,
|
|
53
|
+
moving: SharedValue<number>,
|
|
54
|
+
y: number,
|
|
55
|
+
animated: boolean,
|
|
56
|
+
) => {
|
|
57
|
+
'worklet';
|
|
58
|
+
moving.value = 0;
|
|
59
|
+
programmaticY.value = animated ? y : -1;
|
|
60
|
+
scrollTo(ref, 0, y, animated);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type Range = { first: number; last: number };
|
|
64
|
+
const AgendaListInner = <T,>({
|
|
65
|
+
sections,
|
|
66
|
+
renderItem,
|
|
67
|
+
renderSectionHeader,
|
|
68
|
+
itemHeightByType,
|
|
69
|
+
sectionHeaderHeight = 32,
|
|
70
|
+
getItemType = defaultGetItemType,
|
|
71
|
+
keyExtractor = defaultKeyExtractor,
|
|
72
|
+
stickySectionHeadersEnabled = false,
|
|
73
|
+
refreshing = false,
|
|
74
|
+
onRefresh,
|
|
75
|
+
decelerationRate = 0.985,
|
|
76
|
+
backdrop,
|
|
77
|
+
rowGroundColor,
|
|
78
|
+
ref,
|
|
79
|
+
onDragBegin,
|
|
80
|
+
onSettle,
|
|
81
|
+
}: AgendaListProps<T>) => {
|
|
82
|
+
const { colors } = useKitTheme();
|
|
83
|
+
const { range, store, dateRef, setDate, commitDate, reduceMotion } = useCalendarContext();
|
|
84
|
+
const listRef = useAnimatedRef<Animated.ScrollView>();
|
|
85
|
+
|
|
86
|
+
const headerIntern = useRef(new Map<DateString, Extract<AgendaRowRef<T>, { kind: 'header' }>>());
|
|
87
|
+
const heightForType = useCallback(
|
|
88
|
+
(type: string) => itemHeightByType[type] ?? itemHeightByType.Regular ?? 0,
|
|
89
|
+
[itemHeightByType],
|
|
90
|
+
);
|
|
91
|
+
const layout = useMemo(
|
|
92
|
+
() =>
|
|
93
|
+
buildAgendaLayout<T>({
|
|
94
|
+
sections,
|
|
95
|
+
sectionHeaderHeight,
|
|
96
|
+
heightForType,
|
|
97
|
+
getItemType,
|
|
98
|
+
keyExtractor,
|
|
99
|
+
headerIntern: headerIntern.current,
|
|
100
|
+
}),
|
|
101
|
+
[sections, sectionHeaderHeight, heightForType, getItemType, keyExtractor],
|
|
102
|
+
);
|
|
103
|
+
const layoutRef = useRef(layout);
|
|
104
|
+
layoutRef.current = layout;
|
|
105
|
+
|
|
106
|
+
const tablesSV = useSharedValue<AgendaTables>(EMPTY_TABLES);
|
|
107
|
+
const viewportSV = useSharedValue(0);
|
|
108
|
+
|
|
109
|
+
// Mount at the committed day with zero scroll: `contentOffset` is honoured on
|
|
110
|
+
// the first frame (M0), and the first window already holds its rows.
|
|
111
|
+
const [initialSection] = useState(() => clampIndex(range, dayIndexOf(range, dateRef.current)));
|
|
112
|
+
const [initialOffset] = useState(() => layout.sectionOffset[initialSection] ?? 0);
|
|
113
|
+
const [contentOffset] = useState(() => ({ x: 0, y: initialOffset }));
|
|
114
|
+
const [windowRange, setWindowRange] = useState<Range>(() => {
|
|
115
|
+
const row = layout.sectionStart[initialSection] ?? 0;
|
|
116
|
+
return { first: Math.max(0, row - BUCKET_REST), last: row + 4 * BUCKET_REST };
|
|
117
|
+
});
|
|
118
|
+
const [fastMotion, setFastMotion] = useState(false);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A long programmatic move lands on rows that are not mounted. The destination
|
|
122
|
+
* slice mounts first, beside the current rows; the jump happens once it exists
|
|
123
|
+
* and the slice is released by the next window update. No skeleton frames.
|
|
124
|
+
*/
|
|
125
|
+
const [jumpRange, setJumpRange] = useState<Range | null>(null);
|
|
126
|
+
const pendingJumpY = useRef<number | null>(null);
|
|
127
|
+
const releaseJump = useRef(false);
|
|
128
|
+
|
|
129
|
+
const onWindowChange = useCallback((first: number, last: number) => {
|
|
130
|
+
setWindowRange((was) => (was.first === first && was.last === last ? was : { first, last }));
|
|
131
|
+
if (releaseJump.current) {
|
|
132
|
+
releaseJump.current = false;
|
|
133
|
+
setJumpRange(null);
|
|
134
|
+
}
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
const onCommitDate = useCallback(
|
|
138
|
+
(idx: number) => commitDate(idx, 'listDrag', MY_ORIGIN),
|
|
139
|
+
[commitDate],
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const { scrollY, currentSection, moving, fast, programmaticY, windowFirst, windowLast, scrollHandler, applyOffset } =
|
|
143
|
+
useAgendaScrollSync({
|
|
144
|
+
tablesSV,
|
|
145
|
+
viewportSV,
|
|
146
|
+
headerHeight: sectionHeaderHeight,
|
|
147
|
+
dayIndex: store.dayIndex,
|
|
148
|
+
listDragSeq: store.listDragSeq,
|
|
149
|
+
onWindowChange,
|
|
150
|
+
onCommitDate,
|
|
151
|
+
onFastChange: setFastMotion,
|
|
152
|
+
onDragBegin,
|
|
153
|
+
onSettle,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Publish the tables; if the content changed under a laid-out list, keep the
|
|
158
|
+
* doctor's day in place — the current section plus how far into it we were —
|
|
159
|
+
* in the same commit that resizes the content.
|
|
160
|
+
*
|
|
161
|
+
* The anchor is computed ON the UI thread, which owns `scrollY` and
|
|
162
|
+
* `currentSection`: read from JS they can predate a queued re-anchor or
|
|
163
|
+
* `onLayout`'s seeding, and a rebuild landing in that gap kept the wrong offset.
|
|
164
|
+
*/
|
|
165
|
+
const prevSectionOffset = useRef<readonly number[] | null>(null);
|
|
166
|
+
useLayoutEffect(() => {
|
|
167
|
+
const prev = prevSectionOffset.current;
|
|
168
|
+
const next = layout.sectionOffset;
|
|
169
|
+
prevSectionOffset.current = next;
|
|
170
|
+
tablesSV.value = {
|
|
171
|
+
offsets: layout.offsets,
|
|
172
|
+
sectionOffset: layout.sectionOffset,
|
|
173
|
+
sectionStart: layout.sectionStart,
|
|
174
|
+
monthOfSection: layout.monthOfSection,
|
|
175
|
+
};
|
|
176
|
+
if (prev === null || (prev.length === next.length && prev.every((v, i) => v === next[i]))) return;
|
|
177
|
+
scheduleOnUI(() => {
|
|
178
|
+
'worklet';
|
|
179
|
+
// No viewport yet means no position to keep: `onLayout` anchors on the day.
|
|
180
|
+
if (viewportSV.value === 0) return;
|
|
181
|
+
const s = currentSection.value;
|
|
182
|
+
const from = prev[s];
|
|
183
|
+
const to = next[s];
|
|
184
|
+
if (from === undefined || to === undefined) return;
|
|
185
|
+
const y = to + (scrollY.value - from);
|
|
186
|
+
if (Math.abs(y - scrollY.value) <= 1) return;
|
|
187
|
+
scrollListTo(listRef, programmaticY, moving, y, false);
|
|
188
|
+
// Owned now: the next rebuild may be queued before this scroll's event arrives.
|
|
189
|
+
scrollY.value = y;
|
|
190
|
+
});
|
|
191
|
+
}, [currentSection, layout, listRef, moving, programmaticY, scrollY, tablesSV, viewportSV]);
|
|
192
|
+
|
|
193
|
+
const prepareJump = useCallback(
|
|
194
|
+
(y: number) => {
|
|
195
|
+
const H = viewportSV.value || 800;
|
|
196
|
+
const { offsets, rows } = layoutRef.current;
|
|
197
|
+
pendingJumpY.current = y;
|
|
198
|
+
setJumpRange({
|
|
199
|
+
first: bsearchLE(offsets, y - 0.5 * H) & -BUCKET_REST,
|
|
200
|
+
last: Math.min(rows.length - 1, bsearchLE(offsets, y + 2 * H) | (BUCKET_REST - 1)),
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
[viewportSV],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Moves from other surfaces. Our own are ignored: that is the echo-proofing.
|
|
207
|
+
useAnimatedReaction(
|
|
208
|
+
() => store.move.value,
|
|
209
|
+
(m, prev) => {
|
|
210
|
+
if (!prev || m.seq === prev.seq || m.origin === MY_ORIGIN || viewportSV.value === 0) return;
|
|
211
|
+
const { offsets, sectionOffset } = tablesSV.value;
|
|
212
|
+
const y = sectionOffset[m.idx];
|
|
213
|
+
if (y === undefined) return;
|
|
214
|
+
const top = offsets[windowFirst.value] ?? 0;
|
|
215
|
+
const bottom = offsets[windowLast.value + 1] ?? offsets[offsets.length - 1] ?? 0;
|
|
216
|
+
if (y >= top && y + viewportSV.value <= bottom) scrollListTo(listRef, programmaticY, moving, y, m.animated);
|
|
217
|
+
else runOnJS(prepareJump)(y);
|
|
218
|
+
},
|
|
219
|
+
[prepareJump],
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
useLayoutEffect(() => {
|
|
223
|
+
const y = pendingJumpY.current;
|
|
224
|
+
if (jumpRange === null || y === null) return;
|
|
225
|
+
pendingJumpY.current = null;
|
|
226
|
+
releaseJump.current = true;
|
|
227
|
+
scheduleOnUI(() => {
|
|
228
|
+
'worklet';
|
|
229
|
+
scrollListTo(listRef, programmaticY, moving, y, false);
|
|
230
|
+
});
|
|
231
|
+
}, [jumpRange, listRef, moving, programmaticY]);
|
|
232
|
+
|
|
233
|
+
const scrollToY = useCallback(
|
|
234
|
+
(y: number, animated: boolean) => {
|
|
235
|
+
const anim = animated && !reduceMotion;
|
|
236
|
+
scheduleOnUI(() => {
|
|
237
|
+
'worklet';
|
|
238
|
+
if (viewportSV.value > 0) scrollListTo(listRef, programmaticY, moving, y, anim);
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
[listRef, moving, programmaticY, reduceMotion, viewportSV],
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
useImperativeHandle(
|
|
245
|
+
ref,
|
|
246
|
+
() => ({
|
|
247
|
+
// Dates outside the window are dropped by design: the section list is a
|
|
248
|
+
// pure function of the window and must not grow.
|
|
249
|
+
scrollToDate: (date: DateString, animated = true) => {
|
|
250
|
+
const idx = layout.sectionByTitle.get(date);
|
|
251
|
+
if (idx === undefined) return;
|
|
252
|
+
setDate(date, 'propUpdate', MY_ORIGIN, animated);
|
|
253
|
+
scrollToY(layout.sectionOffset[idx], animated);
|
|
254
|
+
},
|
|
255
|
+
scrollToSection: (title: DateString, animated = true) => {
|
|
256
|
+
const idx = layout.sectionByTitle.get(title);
|
|
257
|
+
if (idx !== undefined) scrollToY(layout.sectionOffset[idx], animated);
|
|
258
|
+
},
|
|
259
|
+
scrollToOffset: ({ offset, animated = true }) => scrollToY(offset, animated),
|
|
260
|
+
}),
|
|
261
|
+
[layout, scrollToY, setDate],
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const laidOut = useRef(false);
|
|
265
|
+
const onLayout = useCallback(
|
|
266
|
+
(e: LayoutChangeEvent) => {
|
|
267
|
+
viewportSV.value = e.nativeEvent.layout.height;
|
|
268
|
+
if (laidOut.current) return;
|
|
269
|
+
laidOut.current = true;
|
|
270
|
+
const fallback = initialOffset;
|
|
271
|
+
scheduleOnUI(() => {
|
|
272
|
+
'worklet';
|
|
273
|
+
// Anchor on the committed DAY in the tables as they are now, not on the
|
|
274
|
+
// first render's pixel offset: rows that change between mount and first
|
|
275
|
+
// layout (shimmer → data) move that day, and the re-anchor above cannot
|
|
276
|
+
// act without a viewport. The list opened on the right pixel of the wrong
|
|
277
|
+
// day — 238 px early on the fixture (2026-09-11). Also covers a device
|
|
278
|
+
// that ignores `contentOffset` on mount.
|
|
279
|
+
const target = tablesSV.value.sectionOffset[store.dayIndex.value] ?? fallback;
|
|
280
|
+
if (Math.abs(scrollY.value - target) > 1) scrollListTo(listRef, programmaticY, moving, target, false);
|
|
281
|
+
// No scroll event follows when the native offset already matches.
|
|
282
|
+
scrollY.value = target;
|
|
283
|
+
applyOffset(target, 0);
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
[applyOffset, initialOffset, listRef, moving, programmaticY, scrollY, store, tablesSV, viewportSV],
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const refreshControl = useMemo(
|
|
290
|
+
() =>
|
|
291
|
+
onRefresh ? (
|
|
292
|
+
<RefreshControl
|
|
293
|
+
refreshing={refreshing}
|
|
294
|
+
onRefresh={onRefresh}
|
|
295
|
+
tintColor={colors.accent}
|
|
296
|
+
colors={[colors.accent]}
|
|
297
|
+
progressBackgroundColor={colors.surface}
|
|
298
|
+
/>
|
|
299
|
+
) : undefined,
|
|
300
|
+
[colors.accent, colors.surface, onRefresh, refreshing],
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const contentStyle = useMemo(() => ({ height: layout.totalHeight }), [layout.totalHeight]);
|
|
304
|
+
const sticky = useMemo<StickyInputs>(
|
|
305
|
+
() => ({ scrollY, currentSection, fast, tablesSV }),
|
|
306
|
+
[currentSection, fast, scrollY, tablesSV],
|
|
307
|
+
);
|
|
308
|
+
const titleAt = useCallback((s: number): DateString | undefined => {
|
|
309
|
+
const { rows, sectionStart } = layoutRef.current;
|
|
310
|
+
const row = rows[sectionStart[s]];
|
|
311
|
+
return row?.kind === 'header' ? row.title : undefined;
|
|
312
|
+
}, []);
|
|
313
|
+
|
|
314
|
+
return (
|
|
315
|
+
<View style={styles.host}>
|
|
316
|
+
{backdrop ? (
|
|
317
|
+
<AgendaBackdrop scrollY={scrollY} tileHeight={backdrop.tileHeight} renderTile={backdrop.renderTile} />
|
|
318
|
+
) : null}
|
|
319
|
+
<Animated.ScrollView
|
|
320
|
+
ref={listRef}
|
|
321
|
+
onLayout={onLayout}
|
|
322
|
+
onScroll={scrollHandler}
|
|
323
|
+
scrollEventThrottle={16}
|
|
324
|
+
contentOffset={contentOffset}
|
|
325
|
+
decelerationRate={decelerationRate}
|
|
326
|
+
refreshControl={refreshControl}
|
|
327
|
+
removeClippedSubviews
|
|
328
|
+
>
|
|
329
|
+
<View style={contentStyle}>
|
|
330
|
+
<AgendaWindow
|
|
331
|
+
layout={layout}
|
|
332
|
+
first={windowRange.first}
|
|
333
|
+
last={windowRange.last}
|
|
334
|
+
extraFirst={jumpRange?.first}
|
|
335
|
+
extraLast={jumpRange?.last}
|
|
336
|
+
renderItem={renderItem}
|
|
337
|
+
renderSectionHeader={renderSectionHeader}
|
|
338
|
+
sections={sections}
|
|
339
|
+
getItemType={getItemType}
|
|
340
|
+
fastMotion={fastMotion}
|
|
341
|
+
deferRows={backdrop !== undefined}
|
|
342
|
+
groundColor={rowGroundColor}
|
|
343
|
+
scrollY={scrollY}
|
|
344
|
+
viewportSV={viewportSV}
|
|
345
|
+
/>
|
|
346
|
+
</View>
|
|
347
|
+
</Animated.ScrollView>
|
|
348
|
+
{stickySectionHeadersEnabled ? (
|
|
349
|
+
<StickyHeaderOverlay
|
|
350
|
+
initialSection={initialSection}
|
|
351
|
+
sectionCount={sections.length}
|
|
352
|
+
titleAt={titleAt}
|
|
353
|
+
height={sectionHeaderHeight}
|
|
354
|
+
sticky={sticky}
|
|
355
|
+
renderSectionHeader={renderSectionHeader}
|
|
356
|
+
/>
|
|
357
|
+
) : null}
|
|
358
|
+
</View>
|
|
359
|
+
);
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export const AgendaList = memo(AgendaListInner) as typeof AgendaListInner;
|
|
363
|
+
|
|
364
|
+
const styles = StyleSheet.create({
|
|
365
|
+
// Clips at the list's own top edge. The sticky overlay's push-up translates its
|
|
366
|
+
// host — clip and all — out of the list, and the calendar sits right above: an
|
|
367
|
+
// unclipped host drew the outgoing header over the calendar's knob at every day
|
|
368
|
+
// crossing, so the knob and its gap flickered (2026-09-11).
|
|
369
|
+
host: { flex: 1, overflow: 'hidden' },
|
|
370
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One row or header, parked at its declared offset.
|
|
3
|
+
*
|
|
4
|
+
* Slots take the row's DATA plus a stable render function, never `children`: a
|
|
5
|
+
* child element is a new object every parent render, so `memo` would never bail
|
|
6
|
+
* out and every window commit would re-render every mounted row (M1: p90 69 ms).
|
|
7
|
+
*
|
|
8
|
+
* `overflow: 'hidden'` is the safety mechanism: a row taller than its declaration
|
|
9
|
+
* is clipped in place instead of shifting every offset below it, and the DEV
|
|
10
|
+
* drift check (`heights.ts`) makes the wrong number loud.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { memo, useMemo, type ReactElement } from 'react';
|
|
14
|
+
import { StyleSheet, View, type LayoutChangeEvent } from 'react-native';
|
|
15
|
+
import { needsMeasure, reportHeight } from './heights';
|
|
16
|
+
import type { AgendaSection, DateString } from '../types';
|
|
17
|
+
|
|
18
|
+
type ItemSlotProps<T> = {
|
|
19
|
+
top: number;
|
|
20
|
+
height: number;
|
|
21
|
+
/** Row type — the drift check's key. */
|
|
22
|
+
type: string;
|
|
23
|
+
item: T;
|
|
24
|
+
index: number;
|
|
25
|
+
section: AgendaSection<T>;
|
|
26
|
+
renderItem: (info: { item: T; index: number; section: AgendaSection<T> }) => ReactElement | null;
|
|
27
|
+
/**
|
|
28
|
+
* Draw nothing: the backdrop shows through. A prop on the same component, never a
|
|
29
|
+
* different component — changing the element type under a key remounts the row.
|
|
30
|
+
*/
|
|
31
|
+
deferred: boolean;
|
|
32
|
+
groundColor?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const AgendaItemSlotInner = <T,>({
|
|
36
|
+
top,
|
|
37
|
+
height,
|
|
38
|
+
type,
|
|
39
|
+
item,
|
|
40
|
+
index,
|
|
41
|
+
section,
|
|
42
|
+
renderItem,
|
|
43
|
+
deferred,
|
|
44
|
+
groundColor,
|
|
45
|
+
}: ItemSlotProps<T>) => {
|
|
46
|
+
const style = useMemo(
|
|
47
|
+
() => [styles.slot, { top, height, backgroundColor: deferred ? undefined : groundColor }],
|
|
48
|
+
[deferred, groundColor, height, top],
|
|
49
|
+
);
|
|
50
|
+
if (deferred) return null;
|
|
51
|
+
|
|
52
|
+
const content = renderItem({ item, index, section });
|
|
53
|
+
if (__DEV__ && needsMeasure(type, height)) {
|
|
54
|
+
// Measure the CHILD: the slot is `height` by construction.
|
|
55
|
+
const onLayout = (e: LayoutChangeEvent) => reportHeight(type, height, e.nativeEvent.layout.height);
|
|
56
|
+
return (
|
|
57
|
+
<View style={style}>
|
|
58
|
+
<View onLayout={onLayout}>{content}</View>
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return <View style={style}>{content}</View>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const AgendaItemSlot = memo(AgendaItemSlotInner) as typeof AgendaItemSlotInner;
|
|
66
|
+
|
|
67
|
+
type HeaderSlotProps = {
|
|
68
|
+
top: number;
|
|
69
|
+
height: number;
|
|
70
|
+
title: DateString;
|
|
71
|
+
renderSectionHeader: (title: DateString) => ReactElement | null;
|
|
72
|
+
groundColor?: string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const AgendaHeaderSlot = memo(
|
|
76
|
+
({ top, height, title, renderSectionHeader, groundColor }: HeaderSlotProps) => {
|
|
77
|
+
const style = useMemo(
|
|
78
|
+
() => [styles.slot, { top, height, backgroundColor: groundColor }],
|
|
79
|
+
[groundColor, height, top],
|
|
80
|
+
);
|
|
81
|
+
return <View style={style}>{renderSectionHeader(title)}</View>;
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
AgendaHeaderSlot.displayName = 'AgendaHeaderSlot';
|
|
85
|
+
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
slot: { position: 'absolute', left: 0, right: 0, overflow: 'hidden' },
|
|
88
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mounted slice of the list, keyed by row.
|
|
3
|
+
*
|
|
4
|
+
* DO NOT RECYCLE. A slot pool was built twice and measured worse on the device
|
|
5
|
+
* (p99 300 → 1000–1550 ms): keyed by row, a row that stays in the window has
|
|
6
|
+
* identical props and `memo` makes it free; a pool hands slots new rows and forces
|
|
7
|
+
* a full prop diff across every card.
|
|
8
|
+
*
|
|
9
|
+
* DO NOT KEEP ROWS MOUNTED "to avoid recreating them". Measured in release on the
|
|
10
|
+
* SM-F415F (2026-09-11): a grow-only window doubled the mounted views and took slow
|
|
11
|
+
* draw frames from ~180 to ~900 per 12 flings (p50 27 → 37 ms); pre-mounting the
|
|
12
|
+
* whole range (5,000 views, +130 MB) put every frame above 60 ms. Every mounted row
|
|
13
|
+
* costs frame time, on screen or not — the window stays tight.
|
|
14
|
+
*
|
|
15
|
+
* Deferral (only with a backdrop): a row ENTERING the window while the list is
|
|
16
|
+
* fast renders nothing and the backdrop shows through; a row already drawn stays
|
|
17
|
+
* drawn. When fast motion ends, deferred rows are released nearest the viewport
|
|
18
|
+
* first — every visible row in one batch, then a batch per frame — as transitions,
|
|
19
|
+
* so a new drag's window commit never waits behind them.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { memo, startTransition, useEffect, useLayoutEffect, useRef, useState, type ReactElement } from 'react';
|
|
23
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
24
|
+
import { bsearchLE } from '../core/worklets';
|
|
25
|
+
import type { AgendaSection, DateString } from '../types';
|
|
26
|
+
import { AgendaHeaderSlot, AgendaItemSlot } from './AgendaSlot';
|
|
27
|
+
import type { AgendaLayout } from './buildAgendaLayout';
|
|
28
|
+
import { UPGRADE_BATCH } from './constants';
|
|
29
|
+
|
|
30
|
+
const NO_KEYS: ReadonlySet<string> = new Set();
|
|
31
|
+
|
|
32
|
+
type Props<T> = {
|
|
33
|
+
layout: AgendaLayout<T>;
|
|
34
|
+
first: number;
|
|
35
|
+
last: number;
|
|
36
|
+
/** A second slice mounted while a long programmatic jump is pending — its rows exist before the list moves. */
|
|
37
|
+
extraFirst?: number;
|
|
38
|
+
extraLast?: number;
|
|
39
|
+
renderItem: (info: { item: T; index: number; section: AgendaSection<T> }) => ReactElement | null;
|
|
40
|
+
renderSectionHeader: (title: DateString) => ReactElement | null;
|
|
41
|
+
sections: AgendaSection<T>[];
|
|
42
|
+
getItemType: (item: T) => string;
|
|
43
|
+
fastMotion: boolean;
|
|
44
|
+
deferRows: boolean;
|
|
45
|
+
groundColor?: string;
|
|
46
|
+
scrollY: SharedValue<number>;
|
|
47
|
+
viewportSV: SharedValue<number>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const AgendaWindowInner = <T,>({
|
|
51
|
+
layout,
|
|
52
|
+
first,
|
|
53
|
+
last,
|
|
54
|
+
extraFirst,
|
|
55
|
+
extraLast,
|
|
56
|
+
renderItem,
|
|
57
|
+
renderSectionHeader,
|
|
58
|
+
sections,
|
|
59
|
+
getItemType,
|
|
60
|
+
fastMotion,
|
|
61
|
+
deferRows,
|
|
62
|
+
groundColor,
|
|
63
|
+
scrollY,
|
|
64
|
+
viewportSV,
|
|
65
|
+
}: Props<T>) => {
|
|
66
|
+
const { rows, offsets, heights } = layout;
|
|
67
|
+
const start = Math.max(0, first);
|
|
68
|
+
const end = Math.min(last, rows.length - 1);
|
|
69
|
+
|
|
70
|
+
/** Rows deferred in the last commit, and the window that commit drew. */
|
|
71
|
+
const heldRef = useRef<ReadonlySet<string>>(NO_KEYS);
|
|
72
|
+
const drawnRef = useRef({ layout, start: 0, end: -1 });
|
|
73
|
+
const [released, setReleased] = useState<ReadonlySet<string>>(NO_KEYS);
|
|
74
|
+
|
|
75
|
+
const prev = drawnRef.current;
|
|
76
|
+
const sameLayout = prev.layout === layout;
|
|
77
|
+
const held = heldRef.current;
|
|
78
|
+
let nextHeld: Set<string> | null = null;
|
|
79
|
+
const slots: ReactElement[] = [];
|
|
80
|
+
|
|
81
|
+
const pushRow = (i: number) => {
|
|
82
|
+
const row = rows[i];
|
|
83
|
+
if (row.kind === 'header') {
|
|
84
|
+
slots.push(
|
|
85
|
+
<AgendaHeaderSlot
|
|
86
|
+
key={row.key}
|
|
87
|
+
top={offsets[i]}
|
|
88
|
+
// The layout's pixel-snapped height, not the declared constant: with the
|
|
89
|
+
// constant, a header ending a fraction short of the next row's top left a
|
|
90
|
+
// hairline of backdrop between them.
|
|
91
|
+
height={heights[i]}
|
|
92
|
+
title={row.title}
|
|
93
|
+
renderSectionHeader={renderSectionHeader}
|
|
94
|
+
groundColor={groundColor}
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
let deferred = false;
|
|
100
|
+
if (deferRows) {
|
|
101
|
+
deferred = held.has(row.key)
|
|
102
|
+
? fastMotion || !released.has(row.key)
|
|
103
|
+
: fastMotion && !(sameLayout && i >= prev.start && i <= prev.end);
|
|
104
|
+
if (deferred) (nextHeld ??= new Set()).add(row.key);
|
|
105
|
+
}
|
|
106
|
+
slots.push(
|
|
107
|
+
<AgendaItemSlot
|
|
108
|
+
key={row.key}
|
|
109
|
+
top={offsets[i]}
|
|
110
|
+
height={heights[i]}
|
|
111
|
+
type={getItemType(row.item)}
|
|
112
|
+
item={row.item}
|
|
113
|
+
index={row.indexInSection}
|
|
114
|
+
section={sections[row.section]}
|
|
115
|
+
renderItem={renderItem}
|
|
116
|
+
deferred={deferred}
|
|
117
|
+
groundColor={groundColor}
|
|
118
|
+
/>,
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
for (let i = start; i <= end; i += 1) pushRow(i);
|
|
123
|
+
if (extraFirst !== undefined && extraLast !== undefined) {
|
|
124
|
+
const xEnd = Math.min(extraLast, rows.length - 1);
|
|
125
|
+
for (let i = Math.max(0, extraFirst); i <= xEnd; i += 1) {
|
|
126
|
+
if (i < start || i > end) pushRow(i);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const heldNow: ReadonlySet<string> = nextHeld ?? NO_KEYS;
|
|
131
|
+
// Committed in a layout effect, so a render React discards cannot leave the
|
|
132
|
+
// refs describing rows that never mounted.
|
|
133
|
+
useLayoutEffect(() => {
|
|
134
|
+
heldRef.current = heldNow;
|
|
135
|
+
drawnRef.current = { layout, start, end };
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const hasHeld = heldNow.size > 0;
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (fastMotion || !hasHeld) return;
|
|
141
|
+
const { layout: l, start: s, end: e } = drawnRef.current;
|
|
142
|
+
const y = scrollY.value;
|
|
143
|
+
const top = bsearchLE(l.offsets, y);
|
|
144
|
+
const bottom = bsearchLE(l.offsets, y + viewportSV.value);
|
|
145
|
+
const focus = (top + bottom) >> 1;
|
|
146
|
+
|
|
147
|
+
// Sorted ONCE; each frame then only pops a batch.
|
|
148
|
+
const queue: string[] = [];
|
|
149
|
+
const distance: number[] = [];
|
|
150
|
+
let visible = 0;
|
|
151
|
+
for (let i = s; i <= e; i += 1) {
|
|
152
|
+
const row = l.rows[i];
|
|
153
|
+
if (row.kind !== 'item' || !heldRef.current.has(row.key)) continue;
|
|
154
|
+
queue.push(row.key);
|
|
155
|
+
distance.push(Math.abs(i - focus));
|
|
156
|
+
if (i >= top && i <= bottom) visible += 1;
|
|
157
|
+
}
|
|
158
|
+
const order = queue.map((_, k) => k).sort((a, b) => distance[a] - distance[b]);
|
|
159
|
+
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
let frame = 0;
|
|
162
|
+
const releaseNext = () => {
|
|
163
|
+
const n = cursor === 0 ? Math.max(UPGRADE_BATCH, visible) : UPGRADE_BATCH;
|
|
164
|
+
const batch = order.slice(cursor, cursor + n).map((k) => queue[k]);
|
|
165
|
+
const isFirst = cursor === 0;
|
|
166
|
+
cursor += n;
|
|
167
|
+
startTransition(() => {
|
|
168
|
+
setReleased((was) => {
|
|
169
|
+
const next = new Set(isFirst ? NO_KEYS : was);
|
|
170
|
+
for (const key of batch) next.add(key);
|
|
171
|
+
return next;
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
if (cursor < order.length) frame = requestAnimationFrame(releaseNext);
|
|
175
|
+
};
|
|
176
|
+
frame = requestAnimationFrame(releaseNext);
|
|
177
|
+
return () => cancelAnimationFrame(frame);
|
|
178
|
+
}, [fastMotion, hasHeld, scrollY, viewportSV]);
|
|
179
|
+
|
|
180
|
+
return <>{slots}</>;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const AgendaWindow = memo(AgendaWindowInner) as typeof AgendaWindowInner;
|