1- import React , { useState , useEffect , CSSProperties } from "react" ;
1+ import { useState , useEffect , CSSProperties } from "react" ;
2+ import { CopyButton } from "./CopyButton" ;
23
34const parseParams = ( paramString : string ) : Record < string , string > => {
45 const params = new URLSearchParams ( paramString ) ;
@@ -12,7 +13,9 @@ const parseParams = (paramString: string): Record<string, string> => {
1213const stringifyParams = ( params : Record < string , string > ) : string => {
1314 const urlParams = new URLSearchParams ( ) ;
1415 Object . entries ( params ) . forEach ( ( [ key , value ] ) => {
15- urlParams . set ( key , encodeURIComponent ( value ) ) ;
16+ if ( key . trim ( ) !== "" ) {
17+ urlParams . set ( key , encodeURIComponent ( value ) ) ;
18+ }
1619 } ) ;
1720 return urlParams . toString ( ) ;
1821} ;
@@ -74,11 +77,85 @@ const styles: Record<string, CSSProperties> = {
7477 } ,
7578} ;
7679
77- const URLVisualizer : React . FC = ( ) => {
80+ interface ParamsEditorProps {
81+ type : "query" | "hash" ;
82+ params : Record < string , string > ;
83+ onChange : ( key : string , value : string ) => void ;
84+ onRemove : ( key : string ) => void ;
85+ onAdd : ( key : string , value : string ) => void ;
86+ newKey : string ;
87+ newValue : string ;
88+ setNewKey : ( val : string ) => void ;
89+ setNewValue : ( val : string ) => void ;
90+ }
91+
92+ const ParamsEditor = ( {
93+ type,
94+ params,
95+ onChange,
96+ onRemove,
97+ onAdd,
98+ newKey,
99+ newValue,
100+ setNewKey,
101+ setNewValue,
102+ } : ParamsEditorProps ) => (
103+ < div style = { styles . section } >
104+ < strong > { type === "query" ? "Query" : "Hash" } Parameters:</ strong >
105+ { Object . keys ( params ) . length === 0 ? (
106+ < div style = { styles . empty } > None</ div >
107+ ) : (
108+ Object . entries ( params ) . map ( ( [ key , value ] ) => (
109+ < div key = { key } style = { styles . paramItem } >
110+ < label style = { styles . label } > { key } </ label >
111+ < input
112+ type = "text"
113+ value = { value }
114+ onChange = { ( e ) => onChange ( key , e . target . value ) }
115+ style = { styles . paramInput }
116+ />
117+ < button
118+ onClick = { ( ) => onRemove ( key ) }
119+ style = { { ...styles . button , backgroundColor : "#eb3f59" , marginLeft : "8px" } }
120+ >
121+ –
122+ </ button >
123+ </ div >
124+ ) )
125+ ) }
126+ < div style = { styles . paramItem } >
127+ < input placeholder = "key" value = { newKey } onChange = { ( e ) => setNewKey ( e . target . value ) } style = { styles . paramInput } />
128+ < input
129+ placeholder = "value"
130+ value = { newValue }
131+ onChange = { ( e ) => setNewValue ( e . target . value ) }
132+ style = { { ...styles . paramInput , marginLeft : "8px" } }
133+ />
134+ < button
135+ onClick = { ( ) => {
136+ if ( newKey . trim ( ) ) {
137+ onAdd ( newKey , newValue ) ;
138+ setNewKey ( "" ) ;
139+ setNewValue ( "" ) ;
140+ }
141+ } }
142+ style = { { ...styles . button , marginLeft : "8px" } }
143+ >
144+ +
145+ </ button >
146+ </ div >
147+ </ div >
148+ ) ;
149+
150+ const URLVisualizer = ( ) => {
78151 const [ urlInput , setUrlInput ] = useState < string > ( "" ) ;
79152 const [ urlObject , setUrlObject ] = useState < URL | null > ( null ) ;
80153 const [ queryParams , setQueryParams ] = useState < Record < string , string > > ( { } ) ;
81154 const [ hashParams , setHashParams ] = useState < Record < string , string > > ( { } ) ;
155+ const [ newQueryKey , setNewQueryKey ] = useState ( "" ) ;
156+ const [ newQueryValue , setNewQueryValue ] = useState ( "" ) ;
157+ const [ newHashKey , setNewHashKey ] = useState ( "" ) ;
158+ const [ newHashValue , setNewHashValue ] = useState ( "" ) ;
82159
83160 useEffect ( ( ) => {
84161 try {
@@ -93,29 +170,15 @@ const URLVisualizer: React.FC = () => {
93170 }
94171 } , [ urlInput ] ) ;
95172
96- const handleParamChange = ( type : "query" | "hash" , key : string , value : string ) => {
97- const newParams = {
98- ...( type === "query" ? queryParams : hashParams ) ,
99- [ key ] : value ,
100- } ;
101- if ( type === "query" ) {
102- setQueryParams ( newParams ) ;
103- } else {
104- setHashParams ( newParams ) ;
105- }
106-
173+ const updateUrlInput = ( updatedQueryParams : Record < string , string > , updatedHashParams : Record < string , string > ) => {
107174 if ( urlObject ) {
108175 const updatedUrl = new URL ( urlObject . toString ( ) ) ;
109- updatedUrl . search = stringifyParams ( type === "query" ? newParams : queryParams ) ;
110- updatedUrl . hash = stringifyParams ( type === "hash" ? newParams : hashParams ) ;
176+ updatedUrl . search = stringifyParams ( updatedQueryParams ) ;
177+ updatedUrl . hash = stringifyParams ( updatedHashParams ) ;
111178 setUrlInput ( updatedUrl . toString ( ) ) ;
112179 }
113180 } ;
114181
115- const copyToClipboard = ( ) => {
116- navigator . clipboard . writeText ( urlInput ) ;
117- } ;
118-
119182 return (
120183 < div style = { styles . container } >
121184 < div style = { styles . inputGroup } >
@@ -127,9 +190,7 @@ const URLVisualizer: React.FC = () => {
127190 onChange = { ( e ) => setUrlInput ( e . target . value ) }
128191 style = { styles . input }
129192 />
130- < button onClick = { copyToClipboard } style = { styles . button } >
131- Copy URL
132- </ button >
193+ < CopyButton content = { urlInput } />
133194 </ div >
134195
135196 { urlObject && (
@@ -144,43 +205,55 @@ const URLVisualizer: React.FC = () => {
144205 < strong > Path:</ strong > { urlObject . pathname }
145206 </ div >
146207
147- < div style = { styles . section } >
148- < strong > Query Parameters:</ strong >
149- { Object . keys ( queryParams ) . length === 0 ? (
150- < div style = { styles . empty } > None</ div >
151- ) : (
152- Object . entries ( queryParams ) . map ( ( [ key , value ] ) => (
153- < div key = { key } style = { styles . paramItem } >
154- < label style = { styles . label } > { key } </ label >
155- < input
156- type = "text"
157- value = { value }
158- onChange = { ( e ) => handleParamChange ( "query" , key , e . target . value ) }
159- style = { styles . paramInput }
160- />
161- </ div >
162- ) )
163- ) }
164- </ div >
208+ < ParamsEditor
209+ type = "query"
210+ params = { queryParams }
211+ onChange = { ( key , value ) => {
212+ const updated = { ...queryParams , [ key ] : value } ;
213+ setQueryParams ( updated ) ;
214+ updateUrlInput ( updated , hashParams ) ;
215+ } }
216+ onRemove = { ( key ) => {
217+ const updated = { ...queryParams } ;
218+ delete updated [ key ] ;
219+ setQueryParams ( updated ) ;
220+ updateUrlInput ( updated , hashParams ) ;
221+ } }
222+ onAdd = { ( key , value ) => {
223+ const updated = { ...queryParams , [ key ] : value } ;
224+ setQueryParams ( updated ) ;
225+ updateUrlInput ( updated , hashParams ) ;
226+ } }
227+ newKey = { newQueryKey }
228+ newValue = { newQueryValue }
229+ setNewKey = { setNewQueryKey }
230+ setNewValue = { setNewQueryValue }
231+ />
165232
166- < div style = { styles . section } >
167- < strong > Hash Parameters:</ strong >
168- { Object . keys ( hashParams ) . length === 0 ? (
169- < div style = { styles . empty } > None</ div >
170- ) : (
171- Object . entries ( hashParams ) . map ( ( [ key , value ] ) => (
172- < div key = { key } style = { styles . paramItem } >
173- < label style = { styles . label } > { key } </ label >
174- < input
175- type = "text"
176- value = { value }
177- onChange = { ( e ) => handleParamChange ( "hash" , key , e . target . value ) }
178- style = { styles . paramInput }
179- />
180- </ div >
181- ) )
182- ) }
183- </ div >
233+ < ParamsEditor
234+ type = "hash"
235+ params = { hashParams }
236+ onChange = { ( key , value ) => {
237+ const updated = { ...hashParams , [ key ] : value } ;
238+ setHashParams ( updated ) ;
239+ updateUrlInput ( queryParams , updated ) ;
240+ } }
241+ onRemove = { ( key ) => {
242+ const updated = { ...hashParams } ;
243+ delete updated [ key ] ;
244+ setHashParams ( updated ) ;
245+ updateUrlInput ( queryParams , updated ) ;
246+ } }
247+ onAdd = { ( key , value ) => {
248+ const updated = { ...hashParams , [ key ] : value } ;
249+ setHashParams ( updated ) ;
250+ updateUrlInput ( queryParams , updated ) ;
251+ } }
252+ newKey = { newHashKey }
253+ newValue = { newHashValue }
254+ setNewKey = { setNewHashKey }
255+ setNewValue = { setNewHashValue }
256+ />
184257 </ div >
185258 ) }
186259 </ div >
0 commit comments