@moontra/moonui-pro 2.17.4 → 2.17.5
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/dist/index.mjs +1168 -274
- package/package.json +1 -1
- package/src/components/calendar-pro/index.tsx +129 -24
- package/src/components/memory-efficient-data/index.tsx +730 -66
- package/src/components/virtual-list/index.tsx +436 -37
- package/dist/index.d.ts +0 -2798
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.5",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -271,7 +271,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel,
|
|
|
271
271
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../ui/command'
|
|
272
272
|
import { motion, AnimatePresence, LayoutGroup, useMotionValue, useTransform, animate } from 'framer-motion'
|
|
273
273
|
import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, addWeeks, addMonths, addYears, subMonths, subYears, isSameMonth, isSameDay, isToday, isBefore, isAfter, differenceInDays, differenceInWeeks, differenceInMonths, parseISO, startOfDay, endOfDay, isWithinInterval, eachDayOfInterval, getDay, setHours, setMinutes, startOfYear, endOfYear, eachMonthOfInterval, getDaysInMonth, getWeek, startOfISOWeek, endOfISOWeek, addHours, subHours, isSameHour, differenceInHours, differenceInMinutes, addMinutes, subMinutes, isSameMinute, setSeconds, setMilliseconds, subDays, subWeeks } from 'date-fns'
|
|
274
|
-
|
|
274
|
+
// Removed react-beautiful-dnd imports - using HTML5 drag & drop instead
|
|
275
275
|
import { Calendar as CalendarBase } from '../ui/calendar'
|
|
276
276
|
import { HexColorPicker } from 'react-colorful'
|
|
277
277
|
|
|
@@ -441,6 +441,9 @@ export const CalendarPro = React.forwardRef<HTMLDivElement, CalendarProProps>(({
|
|
|
441
441
|
theme = 'system',
|
|
442
442
|
...props
|
|
443
443
|
}, ref) => {
|
|
444
|
+
// Drag state
|
|
445
|
+
const [draggedEventId, setDraggedEventId] = useState<string | null>(null)
|
|
446
|
+
const [dragOverInfo, setDragOverInfo] = useState<{ date: Date; hour?: number } | null>(null)
|
|
444
447
|
const [internalSidebarCollapsed, setInternalSidebarCollapsed] = useState(false)
|
|
445
448
|
const sidebarCollapsed = controlledSidebarCollapsed ?? internalSidebarCollapsed
|
|
446
449
|
|
|
@@ -617,29 +620,91 @@ export const CalendarPro = React.forwardRef<HTMLDivElement, CalendarProProps>(({
|
|
|
617
620
|
}, [selectedEvent, onEventDelete])
|
|
618
621
|
|
|
619
622
|
// Handle drag start
|
|
620
|
-
const handleDragStart = useCallback((event: CalendarEvent) => {
|
|
623
|
+
const handleDragStart = useCallback((e: React.DragEvent, event: CalendarEvent) => {
|
|
621
624
|
if (!allowEventDragging) return
|
|
625
|
+
|
|
626
|
+
setDraggedEventId(event.id)
|
|
622
627
|
setIsDragging(true)
|
|
623
|
-
|
|
628
|
+
|
|
629
|
+
// Store event data in dataTransfer
|
|
630
|
+
e.dataTransfer.effectAllowed = 'move'
|
|
631
|
+
e.dataTransfer.setData('text/plain', event.id)
|
|
632
|
+
|
|
633
|
+
// Create drag image with preserved dimensions
|
|
634
|
+
const dragImage = e.currentTarget.cloneNode(true) as HTMLElement
|
|
635
|
+
const originalRect = e.currentTarget.getBoundingClientRect()
|
|
636
|
+
dragImage.style.opacity = '0.8'
|
|
637
|
+
dragImage.style.position = 'absolute'
|
|
638
|
+
dragImage.style.top = '-1000px'
|
|
639
|
+
dragImage.style.width = `${originalRect.width}px`
|
|
640
|
+
dragImage.style.height = `${originalRect.height}px`
|
|
641
|
+
dragImage.style.boxSizing = 'border-box'
|
|
642
|
+
document.body.appendChild(dragImage)
|
|
643
|
+
e.dataTransfer.setDragImage(dragImage, e.nativeEvent.offsetX, e.nativeEvent.offsetY)
|
|
644
|
+
setTimeout(() => document.body.removeChild(dragImage), 0)
|
|
624
645
|
}, [allowEventDragging])
|
|
625
646
|
|
|
626
|
-
// Handle drag
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
647
|
+
// Handle drag over
|
|
648
|
+
const handleDragOver = useCallback((e: React.DragEvent, date: Date, hour?: number) => {
|
|
649
|
+
e.preventDefault()
|
|
650
|
+
e.dataTransfer.dropEffect = 'move'
|
|
651
|
+
setDragOverInfo({ date, hour })
|
|
652
|
+
}, [])
|
|
653
|
+
|
|
654
|
+
// Handle drag leave
|
|
655
|
+
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
|
656
|
+
// Only clear if we're leaving the drop zone entirely
|
|
657
|
+
const rect = e.currentTarget.getBoundingClientRect()
|
|
658
|
+
const x = e.clientX
|
|
659
|
+
const y = e.clientY
|
|
660
|
+
|
|
661
|
+
if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
|
|
662
|
+
setDragOverInfo(null)
|
|
663
|
+
}
|
|
664
|
+
}, [])
|
|
665
|
+
|
|
666
|
+
// Handle drop
|
|
667
|
+
const handleDrop = useCallback((e: React.DragEvent, date: Date, hour?: number) => {
|
|
668
|
+
e.preventDefault()
|
|
669
|
+
|
|
670
|
+
const eventId = e.dataTransfer.getData('text/plain')
|
|
671
|
+
const draggedEvent = events.find(ev => ev.id === eventId)
|
|
672
|
+
|
|
673
|
+
if (!draggedEvent || !onEventDrop) return
|
|
674
|
+
|
|
675
|
+
// Calculate new start and end times
|
|
676
|
+
let newStart: Date
|
|
677
|
+
let newEnd: Date
|
|
631
678
|
|
|
632
|
-
if (
|
|
679
|
+
if (hour !== undefined) {
|
|
680
|
+
// Dropped on a specific hour slot
|
|
681
|
+
newStart = setHours(setMinutes(date, 0), hour)
|
|
682
|
+
} else {
|
|
683
|
+
// Dropped on a day (keep original time)
|
|
684
|
+
const originalHours = draggedEvent.start.getHours()
|
|
685
|
+
const originalMinutes = draggedEvent.start.getMinutes()
|
|
686
|
+
newStart = setHours(setMinutes(date, originalMinutes), originalHours)
|
|
687
|
+
}
|
|
633
688
|
|
|
634
|
-
//
|
|
635
|
-
// This is a simplified version - you'd need to implement proper date calculation
|
|
636
|
-
// based on the view and drop position
|
|
637
|
-
const newStart = new Date(result.destination.droppableId)
|
|
689
|
+
// Keep the same duration
|
|
638
690
|
const duration = differenceInMinutes(draggedEvent.end, draggedEvent.start)
|
|
639
|
-
|
|
691
|
+
newEnd = addMinutes(newStart, duration)
|
|
640
692
|
|
|
693
|
+
// Update event
|
|
641
694
|
onEventDrop(draggedEvent.id, newStart, newEnd)
|
|
642
|
-
|
|
695
|
+
|
|
696
|
+
// Reset drag state
|
|
697
|
+
setDraggedEventId(null)
|
|
698
|
+
setDragOverInfo(null)
|
|
699
|
+
setIsDragging(false)
|
|
700
|
+
}, [events, onEventDrop])
|
|
701
|
+
|
|
702
|
+
// Handle drag end (cleanup)
|
|
703
|
+
const handleDragEnd = useCallback(() => {
|
|
704
|
+
setDraggedEventId(null)
|
|
705
|
+
setDragOverInfo(null)
|
|
706
|
+
setIsDragging(false)
|
|
707
|
+
}, [])
|
|
643
708
|
|
|
644
709
|
// Export calendar
|
|
645
710
|
const exportCalendar = useCallback((format: 'ics' | 'csv' | 'json') => {
|
|
@@ -721,7 +786,15 @@ END:VCALENDAR`
|
|
|
721
786
|
<div className="flex-1 overflow-auto">
|
|
722
787
|
<div className="min-h-full">
|
|
723
788
|
{/* All day events */}
|
|
724
|
-
<div
|
|
789
|
+
<div
|
|
790
|
+
className={cn(
|
|
791
|
+
"border-b p-2",
|
|
792
|
+
dragOverInfo?.date && !dragOverInfo?.hour && isSameDay(dragOverInfo.date, currentDate) && "bg-primary/10"
|
|
793
|
+
)}
|
|
794
|
+
onDragOver={(e) => handleDragOver(e, currentDate)}
|
|
795
|
+
onDragLeave={handleDragLeave}
|
|
796
|
+
onDrop={(e) => handleDrop(e, currentDate)}
|
|
797
|
+
>
|
|
725
798
|
<div className="text-xs text-muted-foreground mb-1">All Day</div>
|
|
726
799
|
<div className="space-y-1">
|
|
727
800
|
{dayEvents
|
|
@@ -729,10 +802,14 @@ END:VCALENDAR`
|
|
|
729
802
|
.map(event => (
|
|
730
803
|
<div
|
|
731
804
|
key={event.id}
|
|
805
|
+
draggable={allowEventDragging}
|
|
806
|
+
onDragStart={(e) => handleDragStart(e, event)}
|
|
807
|
+
onDragEnd={handleDragEnd}
|
|
732
808
|
className="p-2 rounded text-xs cursor-pointer hover:opacity-80"
|
|
733
809
|
style={{
|
|
734
810
|
backgroundColor: event.color || eventColors[event.category || ''] || '#3b82f6',
|
|
735
|
-
color: '#ffffff'
|
|
811
|
+
color: '#ffffff',
|
|
812
|
+
opacity: draggedEventId === event.id ? 0.5 : 1
|
|
736
813
|
}}
|
|
737
814
|
onClick={(e) => handleEventClick(event, e)}
|
|
738
815
|
>
|
|
@@ -750,7 +827,13 @@ END:VCALENDAR`
|
|
|
750
827
|
{format(setHours(new Date(), hour), timeFormat === '12h' ? 'h a' : 'HH:00')}
|
|
751
828
|
</div>
|
|
752
829
|
<div
|
|
753
|
-
className=
|
|
830
|
+
className={cn(
|
|
831
|
+
"flex-1 relative border-l cursor-pointer hover:bg-muted/20",
|
|
832
|
+
dragOverInfo?.date && dragOverInfo?.hour === hour && isSameDay(dragOverInfo.date, currentDate) && "bg-primary/10"
|
|
833
|
+
)}
|
|
834
|
+
onDragOver={(e) => handleDragOver(e, currentDate, hour)}
|
|
835
|
+
onDragLeave={handleDragLeave}
|
|
836
|
+
onDrop={(e) => handleDrop(e, currentDate, hour)}
|
|
754
837
|
onClick={() => {
|
|
755
838
|
if (allowEventCreation) {
|
|
756
839
|
const clickedTime = setHours(setMinutes(currentDate, 0), hour)
|
|
@@ -783,13 +866,18 @@ END:VCALENDAR`
|
|
|
783
866
|
return (
|
|
784
867
|
<div
|
|
785
868
|
key={event.id}
|
|
869
|
+
draggable={allowEventDragging}
|
|
870
|
+
onDragStart={(e) => handleDragStart(e, event)}
|
|
871
|
+
onDragEnd={handleDragEnd}
|
|
786
872
|
className="absolute left-0 right-0 mx-1 p-1 rounded text-xs cursor-pointer hover:opacity-80 overflow-hidden"
|
|
787
873
|
style={{
|
|
788
874
|
top: `${top}px`,
|
|
789
875
|
height: `${height}px`,
|
|
790
876
|
backgroundColor: event.color || eventColors[event.category || ''] || '#3b82f6',
|
|
791
877
|
color: '#ffffff',
|
|
792
|
-
zIndex: 10
|
|
878
|
+
zIndex: 10,
|
|
879
|
+
opacity: draggedEventId === event.id ? 0.5 : 1,
|
|
880
|
+
cursor: allowEventDragging ? 'move' : 'pointer'
|
|
793
881
|
}}
|
|
794
882
|
onClick={(e) => {
|
|
795
883
|
e.stopPropagation()
|
|
@@ -860,14 +948,17 @@ END:VCALENDAR`
|
|
|
860
948
|
const dayEvents = eventsInView.filter(event =>
|
|
861
949
|
isSameDay(new Date(event.start), day) && !event.allDay
|
|
862
950
|
)
|
|
863
|
-
|
|
864
951
|
return (
|
|
865
952
|
<div
|
|
866
953
|
key={day.toISOString()}
|
|
867
954
|
className={cn(
|
|
868
955
|
"flex-1 relative border-l border-b cursor-pointer hover:bg-muted/20",
|
|
869
|
-
isToday(day) && "bg-primary/5"
|
|
956
|
+
isToday(day) && "bg-primary/5",
|
|
957
|
+
dragOverInfo?.date && dragOverInfo?.hour === hour && isSameDay(dragOverInfo.date, day) && "bg-primary/10"
|
|
870
958
|
)}
|
|
959
|
+
onDragOver={(e) => handleDragOver(e, day, hour)}
|
|
960
|
+
onDragLeave={handleDragLeave}
|
|
961
|
+
onDrop={(e) => handleDrop(e, day, hour)}
|
|
871
962
|
onClick={() => {
|
|
872
963
|
if (allowEventCreation) {
|
|
873
964
|
const clickedTime = setHours(setMinutes(day, 0), hour)
|
|
@@ -898,13 +989,18 @@ END:VCALENDAR`
|
|
|
898
989
|
return (
|
|
899
990
|
<div
|
|
900
991
|
key={event.id}
|
|
992
|
+
draggable={allowEventDragging}
|
|
993
|
+
onDragStart={(e) => handleDragStart(e, event)}
|
|
994
|
+
onDragEnd={handleDragEnd}
|
|
901
995
|
className="absolute left-0 right-0 mx-1 p-1 rounded text-xs cursor-pointer hover:opacity-80 overflow-hidden"
|
|
902
996
|
style={{
|
|
903
997
|
top: `${top}px`,
|
|
904
998
|
height: `${height}px`,
|
|
905
999
|
backgroundColor: event.color || eventColors[event.category || ''] || '#3b82f6',
|
|
906
1000
|
color: '#ffffff',
|
|
907
|
-
zIndex: 10
|
|
1001
|
+
zIndex: 10,
|
|
1002
|
+
opacity: draggedEventId === event.id ? 0.5 : 1,
|
|
1003
|
+
cursor: allowEventDragging ? 'move' : 'pointer'
|
|
908
1004
|
}}
|
|
909
1005
|
onClick={(e) => {
|
|
910
1006
|
e.stopPropagation()
|
|
@@ -971,8 +1067,12 @@ END:VCALENDAR`
|
|
|
971
1067
|
"min-h-[100px] p-2 border rounded-lg cursor-pointer transition-colors",
|
|
972
1068
|
!isCurrentMonth && "opacity-50",
|
|
973
1069
|
isToday(day) && "bg-primary/10 border-primary",
|
|
974
|
-
"hover:bg-muted/50"
|
|
1070
|
+
"hover:bg-muted/50",
|
|
1071
|
+
dragOverInfo?.date && !dragOverInfo?.hour && isSameDay(dragOverInfo.date, day) && "bg-primary/20 border-primary"
|
|
975
1072
|
)}
|
|
1073
|
+
onDragOver={(e) => handleDragOver(e, day)}
|
|
1074
|
+
onDragLeave={handleDragLeave}
|
|
1075
|
+
onDrop={(e) => handleDrop(e, day)}
|
|
976
1076
|
onClick={() => handleDateSelect(day)}
|
|
977
1077
|
>
|
|
978
1078
|
<div className={cn(
|
|
@@ -986,10 +1086,15 @@ END:VCALENDAR`
|
|
|
986
1086
|
return (
|
|
987
1087
|
<motion.div
|
|
988
1088
|
key={event.id}
|
|
1089
|
+
draggable={allowEventDragging}
|
|
1090
|
+
onDragStart={(e) => handleDragStart(e, event)}
|
|
1091
|
+
onDragEnd={handleDragEnd}
|
|
989
1092
|
className="text-xs p-1 rounded cursor-pointer truncate"
|
|
990
1093
|
style={{
|
|
991
1094
|
backgroundColor: event.color || eventColors[event.category || ''] || '#3b82f6',
|
|
992
|
-
color: '#ffffff'
|
|
1095
|
+
color: '#ffffff',
|
|
1096
|
+
opacity: draggedEventId === event.id ? 0.5 : 1,
|
|
1097
|
+
cursor: allowEventDragging ? 'move' : 'pointer'
|
|
993
1098
|
}}
|
|
994
1099
|
whileHover={{ scale: 1.02 }}
|
|
995
1100
|
whileTap={{ scale: 0.98 }}
|