+70
-20
mast-react-vite/src/components/ui/action-parser.tsx
+70
-20
mast-react-vite/src/components/ui/action-parser.tsx
···
19
19
const ActionParser = React.forwardRef<HTMLInputElement, InputProps>(
20
20
({ className, ctx, type, onActionChange, onSubmit, value, ...props }, ref) => {
21
21
const [selectedIndex, setSelectedIndex] = React.useState(0);
22
-
const [selectedToggle, setSelectedToggle] = React.useState("add");
22
+
const [selectedToggle, setSelectedToggle] = React.useState("");
23
23
const options = ["add", "filter", "done", "delete"];
24
24
const [suggestions, setSuggestions] = React.useState<string[]>([]);
25
25
const [showShadow, setShowShadow] = React.useState(false);
···
54
54
};
55
55
56
56
const handleToggleChange = (value: string) => {
57
-
if (value) {
57
+
if (value === selectedToggle) {
58
+
// Clicking the active toggle should deactivate it
59
+
setSelectedToggle("");
60
+
onActionChange?.("");
61
+
} else if (value) {
58
62
setSelectedToggle(value);
59
63
onActionChange?.(value);
60
64
}
···
262
266
return (
263
267
<div className="flex flex-col group">
264
268
<div className="hidden md:flex flex-col w-full">
265
-
<div className="flex w-full">
269
+
<motion.div
270
+
className="flex w-full"
271
+
animate={{
272
+
y: 0
273
+
}}
274
+
transition={{ duration: 0.3, ease: "easeOut" }}
275
+
>
266
276
<div className="relative w-auto">
267
277
<ToggleGroup
268
278
type="single"
269
279
value={selectedToggle}
270
280
onValueChange={handleToggleChange}
271
-
defaultValue="add"
272
281
className={cn(
273
-
"h-11 border rounded-t-md border-b-0 bg-transparent text-sm cursor-default pt-1/2 pl-1 pr-1 pb-1/2",
282
+
"h-11 border bg-background text-sm cursor-default pt-1/2 pl-1 pr-1 pb-1/2",
274
283
"shadow-sm transition-colors duration-0 text-center appearance-none group-focus-within:border-ring",
275
-
"rounded-t-md rounded-b-none",
276
284
"focus:border-ring focus-within:border-ring",
285
+
selectedToggle ? "rounded-t-md border-b-0 rounded-b-none" : "rounded-md"
277
286
)}
278
287
>
279
-
<ToggleGroupItem value="filter" aria-label="filter tasks">
288
+
<ToggleGroupItem
289
+
value="filter"
290
+
aria-label="filter tasks"
291
+
onClick={() => handleToggleChange("filter")}
292
+
>
280
293
<Search className="h-4 w-4 rounded-none" />
281
294
</ToggleGroupItem>
282
295
<ToggleGroupItem
283
296
value="modify"
284
297
aria-label="Modify selected task(s)"
298
+
onClick={() => handleToggleChange("modify")}
285
299
>
286
300
<Pencil className="h-4 w-4" />
287
301
</ToggleGroupItem>
288
-
<ToggleGroupItem value="add" aria-label="Add a task">
302
+
<ToggleGroupItem
303
+
value="add"
304
+
aria-label="Add a task"
305
+
onClick={() => handleToggleChange("add")}
306
+
>
289
307
<Plus className="h-4 w-4" />
290
308
</ToggleGroupItem>
291
309
<ToggleGroupItem
292
310
value="done"
293
311
aria-label="Toggle selected task(s) done"
312
+
onClick={() => handleToggleChange("done")}
294
313
>
295
314
<Check className="h-4 w-4" />
296
315
</ToggleGroupItem>
297
316
</ToggleGroup>
298
-
<div className="absolute bottom-0 left-0 right-0 h-[2px] border group-focus-within:border-ring border-b-0 border-t-0 bg-background z-20 translate-y-[1px]"></div>
299
317
</div>
300
318
<div className="flex-1"></div>
301
-
</div>
319
+
</motion.div>
302
320
303
321
{/* Two-line input appearance with divider */}
304
-
<div className="relative w-full flex flex-col">
322
+
<motion.div
323
+
className="relative w-full flex flex-col"
324
+
animate={{
325
+
opacity: selectedToggle ? 1 : 0,
326
+
y: selectedToggle ? -2 : -20,
327
+
height: selectedToggle ? "auto" : 0
328
+
}}
329
+
transition={{ duration: 0.3, ease: "easeOut" }}
330
+
>
331
+
{/* Connector div that bridges toggle group and input */}
332
+
<div className="absolute left-0 h-[1px] border border-b-0 border-t-0 group-focus-within:border-ring bg-background z-20" style={{ width: '166px' }}></div>
333
+
305
334
{/* First line (interactive input) */}
306
335
<div className="flex-1 min-h-[36px] border border-b-0 rounded-br-none rounded-tr-md rounded-tl-none bg-background px-3 py-2 text-sm flex items-center group-focus-within:border-ring">
307
336
<input
···
452
481
↑
453
482
</Button>
454
483
</div>
455
-
</div>
484
+
</motion.div>
456
485
</div>
457
486
458
487
{/* Mobile view layout - shown only on mobile */}
459
488
<div className="md:hidden fixed bottom-0 left-0 w-full z-50 flex flex-col items-center pb-2">
460
-
<div className="relative w-auto">
489
+
<motion.div
490
+
className="relative w-auto"
491
+
animate={{
492
+
y: selectedToggle ? 4 : -25
493
+
}}
494
+
transition={{ duration: 0.3, ease: "easeOut" }}
495
+
>
461
496
<ToggleGroup
462
497
type="single"
463
498
value={selectedToggle}
464
499
onValueChange={handleToggleChange}
465
-
defaultValue="add"
500
+
466
501
className={cn(
467
-
"h-11 border rounded-t-md border-b-0 bg-background text-sm cursor-default pt-1/2 pl-1 pr-1 pb-1/2",
502
+
"h-11 border bg-background text-sm cursor-default pt-1/2 pl-1 pr-1 pb-1/2",
468
503
"shadow-sm transition-colors duration-0 text-center appearance-none group-focus-within:border-ring",
469
-
"rounded-t-md rounded-b-none",
470
504
"focus:border-ring",
505
+
selectedToggle ? "rounded-t-md border-b-0 rounded-b-none" : "rounded-md"
471
506
)}
472
507
>
473
508
<ToggleGroupItem
474
509
value="filter"
475
510
aria-label="filter tasks"
476
511
className="rounded-md mx-1 p-2"
512
+
onClick={() => handleToggleChange("filter")}
477
513
>
478
514
<Search className="h-5 w-5" />
479
515
</ToggleGroupItem>
···
481
517
value="modify"
482
518
aria-label="Modify selected task(s)"
483
519
className="rounded-md mx-1 p-2"
520
+
onClick={() => handleToggleChange("modify")}
484
521
>
485
522
<Pencil className="h-5 w-5" />
486
523
</ToggleGroupItem>
···
488
525
value="add"
489
526
aria-label="Add a task"
490
527
className="rounded-md mx-1 p-2"
528
+
onClick={() => handleToggleChange("add")}
491
529
>
492
530
<Plus className="h-5 w-5" />
493
531
</ToggleGroupItem>
···
495
533
value="done"
496
534
aria-label="Toggle selected task(s) done"
497
535
className="rounded-md mx-1 p-2"
536
+
onClick={() => handleToggleChange("done")}
498
537
>
499
538
<Check className="h-5 w-5" />
500
539
</ToggleGroupItem>
501
540
</ToggleGroup>
502
-
<div className="absolute bottom-0 left-0 right-0 h-[2px] border border-b-0 border-t-0 group-focus-within:border-ring bg-background z-20 translate-y-[1px]" />
503
-
</div>
541
+
542
+
</motion.div>
504
543
505
544
{/* Mobile two-line input appearance */}
506
-
<div className="relative w-[90%] mb-1 flex flex-col">
545
+
<motion.div
546
+
className="relative w-[90%] mb-1 flex flex-col"
547
+
animate={{
548
+
opacity: selectedToggle ? 1 : 0,
549
+
y: selectedToggle ? 0 : 20,
550
+
height: selectedToggle ? "auto" : 0
551
+
}}
552
+
transition={{ duration: 0.3, ease: "easeOut" }}
553
+
>
554
+
{/* Connector div that bridges toggle group and input */}
555
+
<div className="absolute -top-[1px] left-1/2 -translate-x-1/2 h-[1px] border border-b-0 border-t-0 group-focus-within:border-ring bg-background z-20 translate-y-[1px]" style={{ width: '199px' }}></div>
556
+
507
557
{/* First line (interactive input) */}
508
558
<div className="flex-1 min-h-[36px] border border-b-0 rounded-t-md bg-background px-3 py-2 text-sm flex items-center group-focus-within:border-ring">
509
559
<input
···
749
799
↑
750
800
</Button>
751
801
</div>
752
-
</div>
802
+
</motion.div>
753
803
</div>
754
804
</div>
755
805
);