this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: detailed task view

+220 -5
+78
mast-react-vite/package-lock.json
··· 24 24 "class-variance-authority": "^0.7.1", 25 25 "clsx": "^2.1.1", 26 26 "lucide-react": "^0.454.0", 27 + "motion": "^12.7.4", 27 28 "react": "^18.3.1", 28 29 "react-dom": "^18.3.1", 29 30 "react-helmet": "^6.1.0", 31 + "react-modal-sheet": "^4.0.1", 30 32 "tailwind-merge": "^2.5.4", 31 33 "tailwindcss-animate": "^1.0.7" 32 34 }, ··· 2921 2923 "url": "https://github.com/sponsors/rawify" 2922 2924 } 2923 2925 }, 2926 + "node_modules/framer-motion": { 2927 + "version": "12.7.4", 2928 + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.4.tgz", 2929 + "integrity": "sha512-jX0bPsTmU0oPZTYz/dVyD0dmOyEOEJvdn0TaZBE5I8g2GvVnnQnW9f65cJnoVfUkY3WZWNXGXnPbVA9YnaIfVA==", 2930 + "dependencies": { 2931 + "motion-dom": "^12.7.4", 2932 + "motion-utils": "^12.7.2", 2933 + "tslib": "^2.4.0" 2934 + }, 2935 + "peerDependencies": { 2936 + "@emotion/is-prop-valid": "*", 2937 + "react": "^18.0.0 || ^19.0.0", 2938 + "react-dom": "^18.0.0 || ^19.0.0" 2939 + }, 2940 + "peerDependenciesMeta": { 2941 + "@emotion/is-prop-valid": { 2942 + "optional": true 2943 + }, 2944 + "react": { 2945 + "optional": true 2946 + }, 2947 + "react-dom": { 2948 + "optional": true 2949 + } 2950 + } 2951 + }, 2924 2952 "node_modules/function-bind": { 2925 2953 "version": "1.1.2", 2926 2954 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", ··· 3332 3360 "node": ">=16 || 14 >=14.17" 3333 3361 } 3334 3362 }, 3363 + "node_modules/motion": { 3364 + "version": "12.7.4", 3365 + "resolved": "https://registry.npmjs.org/motion/-/motion-12.7.4.tgz", 3366 + "integrity": "sha512-MBGrMbYageHw4iZJn+pGTr7abq5n53jCxYkhFC1It3vYukQPRWg5zij46MnwYGpLR8KG465MLHSASXot9edYOw==", 3367 + "dependencies": { 3368 + "framer-motion": "^12.7.4", 3369 + "tslib": "^2.4.0" 3370 + }, 3371 + "peerDependencies": { 3372 + "@emotion/is-prop-valid": "*", 3373 + "react": "^18.0.0 || ^19.0.0", 3374 + "react-dom": "^18.0.0 || ^19.0.0" 3375 + }, 3376 + "peerDependenciesMeta": { 3377 + "@emotion/is-prop-valid": { 3378 + "optional": true 3379 + }, 3380 + "react": { 3381 + "optional": true 3382 + }, 3383 + "react-dom": { 3384 + "optional": true 3385 + } 3386 + } 3387 + }, 3388 + "node_modules/motion-dom": { 3389 + "version": "12.7.4", 3390 + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.7.4.tgz", 3391 + "integrity": "sha512-1ZUHAoSUMMxP6jPqyxlk9XUfb6NxMsnWPnH2YGhrOhTURLcXWbETi6eemoKb60Pe32NVJYduL4B62VQSO5Jq8Q==", 3392 + "dependencies": { 3393 + "motion-utils": "^12.7.2" 3394 + } 3395 + }, 3396 + "node_modules/motion-utils": { 3397 + "version": "12.7.2", 3398 + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.7.2.tgz", 3399 + "integrity": "sha512-XhZwqctxyJs89oX00zn3OGCuIIpVevbTa+u82usWBC6pSHUd2AoNWiYa7Du8tJxJy9TFbZ82pcn5t7NOm1PHAw==" 3400 + }, 3335 3401 "node_modules/ms": { 3336 3402 "version": "2.1.3", 3337 3403 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", ··· 3783 3849 "version": "16.13.1", 3784 3850 "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 3785 3851 "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 3852 + }, 3853 + "node_modules/react-modal-sheet": { 3854 + "version": "4.0.1", 3855 + "resolved": "https://registry.npmjs.org/react-modal-sheet/-/react-modal-sheet-4.0.1.tgz", 3856 + "integrity": "sha512-koDkfvXHIZhQh+DOE3mcEr2XnJiZIXneGzPuA7VbPIvHQm4BGdSjA1aCEMytTo5UHkfGvQWHvB7P5nejQYRtDQ==", 3857 + "engines": { 3858 + "node": ">=18" 3859 + }, 3860 + "peerDependencies": { 3861 + "motion": ">=11", 3862 + "react": ">=16" 3863 + } 3786 3864 }, 3787 3865 "node_modules/react-remove-scroll": { 3788 3866 "version": "2.6.3",
+2
mast-react-vite/package.json
··· 26 26 "class-variance-authority": "^0.7.1", 27 27 "clsx": "^2.1.1", 28 28 "lucide-react": "^0.454.0", 29 + "motion": "^12.7.4", 29 30 "react": "^18.3.1", 30 31 "react-dom": "^18.3.1", 31 32 "react-helmet": "^6.1.0", 33 + "react-modal-sheet": "^4.0.1", 32 34 "tailwind-merge": "^2.5.4", 33 35 "tailwindcss-animate": "^1.0.7" 34 36 },
+96
mast-react-vite/src/components/ui/task-detail-view.tsx
··· 1 + import {Sheet} from "react-modal-sheet"; 2 + import { Separator } from "@/components/ui/separator"; 3 + import { Project } from "@/components/ui/project"; 4 + import { TagList } from "@/components/ui/tag"; 5 + import { Cross2Icon } from "@radix-ui/react-icons"; 6 + 7 + interface TaskDetailViewProps { 8 + isOpen: boolean; 9 + onClose: () => void; 10 + task: any; // Using any type for now, but could be replaced with a specific Task type 11 + } 12 + 13 + export function TaskDetailView({ isOpen, onClose, task }: TaskDetailViewProps) { 14 + if (!task) return null; 15 + 16 + // Parse tags if they exist 17 + const tagList = task.tags ? JSON.parse(task.tags) : []; 18 + 19 + // Handle preview changes if present 20 + const hasPreview = task.previewMode && task.preview; 21 + const displayDescription = hasPreview && task.preview.description?.length > 0 22 + ? task.preview.description 23 + : task.description; 24 + const previewTags = hasPreview && task.preview.tags ? JSON.parse(task.preview.tags) : []; 25 + const hasPreviewProject = hasPreview && task.preview.project; 26 + const displayProject = hasPreviewProject ? task.preview.project : task.project || ''; 27 + 28 + return ( 29 + <Sheet 30 + isOpen={isOpen} 31 + onClose={onClose} 32 + rootId="root" 33 + className="dark" 34 + snapPoints={[0.85, 0]} 35 + initialSnap={0} 36 + > 37 + <Sheet.Container className="bg-background text-foreground dark"> 38 + <Sheet.Content> 39 + <Separator /> 40 + <div className="p-6 h-full overflow-auto bg-background dark:bg-background"> 41 + <div className="space-y-6 px-1"> 42 + <div> 43 + <h3 className="text-sm font-medium text-muted-foreground">Description</h3> 44 + <p className="text-base mt-1">{displayDescription}</p> 45 + </div> 46 + 47 + <Separator /> 48 + 49 + {displayProject && ( 50 + <div> 51 + <h3 className="text-sm font-medium text-muted-foreground">Project</h3> 52 + <div className="mt-2"> 53 + <Project 54 + name={displayProject} 55 + status={task.completed} 56 + selected={false} 57 + isPreview={hasPreviewProject && task.project !== task.preview.project} 58 + /> 59 + </div> 60 + </div> 61 + )} 62 + 63 + {tagList.length > 0 && ( 64 + <div> 65 + <h3 className="text-sm font-medium text-muted-foreground">Tags</h3> 66 + <div className="mt-2"> 67 + <TagList 68 + tags={tagList} 69 + previewTags={previewTags} 70 + status={task.completed} 71 + /> 72 + </div> 73 + </div> 74 + )} 75 + 76 + {task.dueDate && ( 77 + <div> 78 + <h3 className="text-sm font-medium text-muted-foreground">Due Date</h3> 79 + <p className="text-base mt-1">{task.dueDate}</p> 80 + </div> 81 + )} 82 + 83 + {task.working_id && ( 84 + <div> 85 + <h3 className="text-sm font-medium text-muted-foreground">ID</h3> 86 + <p className="text-base mt-1">{task.working_id}</p> 87 + </div> 88 + )} 89 + </div> 90 + </div> 91 + </Sheet.Content> 92 + <Sheet.Backdrop /> 93 + </Sheet.Container> 94 + </Sheet> 95 + ); 96 + }
+44 -5
mast-react-vite/src/components/ui/task.tsx
··· 3 3 import { useSelection } from "@/contexts/selection-context"; 4 4 import { Project } from "@/components/ui/project"; 5 5 import { TagList } from "@/components/ui/tag"; 6 + import { TaskDetailView } from "@/components/ui/task-detail-view"; 7 + import { useIsMobile } from "@/hooks/use-mobile"; 6 8 7 9 interface TaskProps { 8 10 selected?: boolean; 9 11 onSelect?: () => void; 10 12 data: any; 11 - index: number; 13 + index?: number; 12 14 } 13 15 14 16 export function Task({ selected, onSelect, data }: TaskProps) { 15 17 const { isSelected, toggleSelection } = useSelection(); 16 18 const tagList = JSON.parse(data.tags); 19 + const [showDetailView, setShowDetailView] = React.useState(false); 20 + 21 + const [clickCount, setClickCount] = React.useState(0); 22 + const [clickTimer, setClickTimer] = React.useState<NodeJS.Timeout | null>(null); 17 23 18 - // Handle preview changes 19 24 const hasPreview = data.previewMode && data.preview; 20 25 21 - // Determine which description to display 22 26 const displayDescription = 23 - hasPreview && data.preview.description.length > 0 27 + hasPreview && data.preview.description?.length > 0 24 28 ? data.preview.description 25 29 : data.description; 26 30 ··· 44 48 : "bg-card"; 45 49 } 46 50 }; 51 + 52 + // Handle click/tap with double click detection 53 + const handleClick = (e: React.MouseEvent) => { 54 + e.preventDefault(); 55 + 56 + setClickCount(prev => prev + 1); 57 + 58 + // Clear any existing timer 59 + if (clickTimer) { 60 + clearTimeout(clickTimer); 61 + } 62 + 63 + // If this is the first click/tap, set up a timer to reset the counter 64 + if (clickCount === 0) { 65 + const timer = setTimeout(() => { 66 + if (clickCount === 0) { 67 + toggleSelection(data.working_id); 68 + } 69 + setClickCount(0); 70 + }, 300); // 300ms is a common double-click interval 71 + 72 + setClickTimer(timer); 73 + } 74 + else if (clickCount === 1) { 75 + setShowDetailView(true); 76 + setClickCount(0); 77 + } 78 + }; 47 79 48 80 return ( 49 81 <> 50 82 <div 51 83 className={`p-4 ${getStateStyles(data.completed)} md:hover:bg-muted/50 transition-colors`} 52 - onClick={() => toggleSelection(data.working_id)} 84 + onClick={handleClick} 53 85 > 54 86 <div className="flex items-start gap-4"> 55 87 <div className="flex-1 min-w-0"> ··· 86 118 </div> 87 119 </div> 88 120 <Separator /> 121 + 122 + {/* Task Detail View Sheet */} 123 + <TaskDetailView 124 + isOpen={showDetailView} 125 + onClose={() => setShowDetailView(false)} 126 + task={data} 127 + /> 89 128 </> 90 129 ); 91 130 }