11import React from 'react' ;
2- import { Trash2 , Copy , Sparkles } from 'lucide-react' ;
2+ import { Trash2 , Copy , Sparkles , ClipboardPaste } from 'lucide-react' ;
33import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from './ui/select' ;
4+ import { Button } from './ui/button' ;
45import { decodePolyline } from '../utils/polylineDecoder' ;
6+ import ImportCoordinatesDialog from './ImportCoordinatesDialog' ;
7+ import { toast } from 'sonner' ;
58
69interface PolylineInputProps {
710 value : string ;
811 onChange : ( value : string ) => void ;
912 onClear : ( ) => void ;
1013 precision ?: number ;
1114 onPrecisionChange ?: ( precision : number ) => void ;
15+ onCoordinatesImport ?: ( coordinates : [ number , number ] [ ] ) => void ;
1216}
1317
1418const PolylineInput : React . FC < PolylineInputProps > = ( {
@@ -17,97 +21,115 @@ const PolylineInput: React.FC<PolylineInputProps> = ({
1721 onClear,
1822 precision = 5 ,
1923 onPrecisionChange,
24+ onCoordinatesImport,
2025} ) => {
2126 const handlePaste = async ( ) => {
2227 try {
2328 const text = await navigator . clipboard . readText ( ) ;
2429 onChange ( text ) ;
30+ toast . success ( 'Polyline pasted' ) ;
2531 } catch ( err ) {
26- console . error ( 'Failed to read clipboard contents: ' , err ) ;
32+ toast . error ( 'Failed to paste from clipboard' ) ;
2733 }
2834 } ;
2935
30- const primaryCoordinates = value ? decodePolyline ( value , precision ) : [ ] ;
36+ const handleCopy = ( ) => {
37+ navigator . clipboard . writeText ( value ) ;
38+ toast . success ( 'Polyline copied' ) ;
39+ } ;
3140
32- return (
33- < div className = "panel" >
34- < div className = "mb-3 flex items-center justify-between" >
35- < span className = "rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary" >
36- Polyline
37- </ span >
38- < span className = "text-xs text-muted-foreground" >
39- Paste encoded polyline or draw on map
40- </ span >
41- </ div >
41+ const handleImportCoordinates = ( coords : [ number , number ] [ ] ) => {
42+ if ( onCoordinatesImport ) {
43+ onCoordinatesImport ( coords ) ;
44+ toast . success ( `Imported ${ coords . length } coordinates` ) ;
45+ }
46+ } ;
4247
43- < div className = "mb-2 flex items-center justify-between" >
44- < div />
45- < div className = "flex items-center space-x-1" >
46- { onPrecisionChange && (
47- < Select
48- value = { precision . toString ( ) }
49- onValueChange = { val => onPrecisionChange ( Number ( val ) ) }
50- >
51- < SelectTrigger className = "h-8 min-w-[80px] text-xs" >
52- < SelectValue placeholder = "Precision" />
53- </ SelectTrigger >
54- < SelectContent >
55- < SelectItem value = "5" > Precision 5</ SelectItem >
56- < SelectItem value = "6" > Precision 6</ SelectItem >
57- < SelectItem value = "7" > Precision 7</ SelectItem >
58- </ SelectContent >
59- </ Select >
48+ const loadSample = ( ) => {
49+ onChange (
50+ '}~kvHmzrr@ba\\hnc@jiu@r{Zqx~@hjp@pwEhnc@zhu@zflAbxn@fhjBvqHroaAgcnAp}gAeahAtqGkngAinc@_h|@r{Zad\\y|_D}_y@swg@ysg@}llBpoZqa{@xrw@~eBaaX}{uAero@uqGadY}nr@`dYs_NquNgbjAf{l@|yh@bfc@}nr@z}q@i|i@zgz@r{ZhjFr}gApob@ff}@laIsen@dgYhdPvbIren@'
51+ ) ;
52+ toast . success ( 'Sample polyline loaded' ) ;
53+ } ;
54+
55+ let pointCount = 0 ;
56+ try {
57+ if ( value ) {
58+ pointCount = decodePolyline ( value , precision ) . length ;
59+ }
60+ } catch {
61+ pointCount = 0 ;
62+ }
63+
64+ return (
65+ < div className = "panel space-y-4" >
66+ < div className = "flex items-center justify-between" >
67+ < div className = "flex items-center gap-2" >
68+ < h3 className = "font-medium" > Polyline</ h3 >
69+ { value && (
70+ < span className = "rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary" >
71+ { pointCount } points
72+ </ span >
6073 ) }
61- < button
62- onClick = { handlePaste }
63- className = "rounded-md bg-secondary p-1.5 text-xs transition-colors hover:bg-secondary/80"
64- >
65- Paste
66- </ button >
67- < button
68- onClick = { onClear }
69- className = "rounded-md p-1.5 text-muted-foreground transition-colors hover:text-destructive"
70- disabled = { ! value }
71- >
72- < Trash2 className = "h-4 w-4" />
73- </ button >
7474 </ div >
75+ { onPrecisionChange && (
76+ < Select
77+ value = { precision . toString ( ) }
78+ onValueChange = { val => onPrecisionChange ( Number ( val ) ) }
79+ >
80+ < SelectTrigger className = "h-8 w-28 text-xs" >
81+ < SelectValue placeholder = "Precision" />
82+ </ SelectTrigger >
83+ < SelectContent >
84+ < SelectItem value = "5" > Precision 5</ SelectItem >
85+ < SelectItem value = "6" > Precision 6</ SelectItem >
86+ < SelectItem value = "7" > Precision 7</ SelectItem >
87+ </ SelectContent >
88+ </ Select >
89+ ) }
7590 </ div >
7691
77- < textarea
78- value = { value }
79- onChange = { e => onChange ( e . target . value ) }
80- placeholder = "Paste your encoded polyline here or use edit mode to draw on the map..."
81- className = "h-24 w-full resize-none border-0 bg-transparent p-0 font-mono text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-0"
82- />
92+ < div className = "relative" >
93+ < textarea
94+ value = { value }
95+ onChange = { e => onChange ( e . target . value ) }
96+ placeholder = "Paste encoded polyline here..."
97+ className = "h-28 w-full resize-none rounded-lg border bg-muted/30 p-3 font-mono text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
98+ />
99+ </ div >
83100
84- < div className = "mt-2 flex items-center justify-between" >
85- < div className = "text-xs text-muted-foreground" >
86- { value
87- ? `${ value . length } characters, ${ primaryCoordinates . length } points`
88- : 'No polyline data' }
101+ < div className = "flex flex-wrap items-center justify-between gap-2" >
102+ < div className = "flex flex-wrap gap-1.5" >
103+ < Button variant = "outline" size = "sm" onClick = { handlePaste } className = "gap-1.5" >
104+ < ClipboardPaste className = "h-3.5 w-3.5" />
105+ Paste
106+ </ Button >
107+ { onCoordinatesImport && < ImportCoordinatesDialog onImport = { handleImportCoordinates } /> }
108+ { ! value && (
109+ < Button variant = "ghost" size = "sm" onClick = { loadSample } className = "gap-1.5 text-primary" >
110+ < Sparkles className = "h-3.5 w-3.5" />
111+ Sample
112+ </ Button >
113+ ) }
89114 </ div >
90- < div className = "flex space-x-1" >
115+
116+ < div className = "flex gap-1.5" >
91117 { value && (
92- < button
93- onClick = { ( ) => navigator . clipboard . writeText ( value ) }
94- className = "flex items-center space-x-1 rounded-md bg-secondary p-1.5 text-xs transition-colors hover:bg-secondary/80"
95- >
96- < Copy className = "mr-1 h-3 w-3" />
97- < span > Copy</ span >
98- </ button >
118+ < >
119+ < Button variant = "secondary" size = "sm" onClick = { handleCopy } className = "gap-1.5" >
120+ < Copy className = "h-3.5 w-3.5" />
121+ Copy
122+ </ Button >
123+ < Button
124+ variant = "ghost"
125+ size = "sm"
126+ onClick = { onClear }
127+ className = "text-muted-foreground hover:text-destructive"
128+ >
129+ < Trash2 className = "h-3.5 w-3.5" />
130+ </ Button >
131+ </ >
99132 ) }
100- < button
101- onClick = { ( ) => {
102- onChange (
103- '}~kvHmzrr@ba\\hnc@jiu@r{Zqx~@hjp@pwEhnc@zhu@zflAbxn@fhjBvqHroaAgcnAp}gAeahAtqGkngAinc@_h|@r{Zad\\y|_D}_y@swg@ysg@}llBpoZqa{@xrw@~eBaaX}{uAero@uqGadY}nr@`dYs_NquNgbjAf{l@|yh@bfc@}nr@z}q@i|i@zgz@r{ZhjFr}gApob@ff}@laIsen@dgYhdPvbIren@'
104- ) ;
105- } }
106- className = "flex items-center space-x-1 rounded-md bg-primary/10 p-1.5 text-xs text-primary transition-colors hover:bg-primary/20"
107- >
108- < Sparkles className = { `h-3 w-3 ${ ! value ? 'mr-1' : '' } ` } />
109- { ! value && < span > Sample</ span > }
110- </ button >
111133 </ div >
112134 </ div >
113135 </ div >
0 commit comments