Import source: src/components/CenterPanel/forms/URLForm
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useQR } from '../../../context/QRContext';
|
||||||
|
import { useToast } from '../../../context/ToastContext';
|
||||||
|
|
||||||
|
function isValidUrl(v: string) {
|
||||||
|
if (!v.trim()) return true;
|
||||||
|
return /^https?:\/\/.+\..+/.test(v.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function URLForm() {
|
||||||
|
const { state, dispatch } = useQR();
|
||||||
|
const { show } = useToast();
|
||||||
|
const { websiteUrl } = state.formData.url;
|
||||||
|
const [shortening, setShortening] = useState(false);
|
||||||
|
const valid = isValidUrl(websiteUrl);
|
||||||
|
|
||||||
|
const set = (v: string) => dispatch({ type: 'SET_FORM_FIELD', qrType: 'url', field: 'websiteUrl', value: v });
|
||||||
|
|
||||||
|
const shorten = async () => {
|
||||||
|
if (!websiteUrl.trim() || !valid) return;
|
||||||
|
setShortening(true);
|
||||||
|
try {
|
||||||
|
const res = await fetch(`https://is.gd/create.php?format=json&url=${encodeURIComponent(websiteUrl)}`);
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.shorturl) { set(data.shorturl); show('URL shortened!', 'success'); }
|
||||||
|
else show(data.errormessage ?? 'Could not shorten URL', 'error');
|
||||||
|
} catch {
|
||||||
|
show('Shortener unavailable', 'error');
|
||||||
|
} finally {
|
||||||
|
setShortening(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<label className="text-[15px] font-semibold" style={{ color: 'var(--text-primary)' }}>Website URL</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="flex-1 flex flex-col gap-1">
|
||||||
|
<input
|
||||||
|
type="url" value={websiteUrl} onChange={e => set(e.target.value)}
|
||||||
|
placeholder="https://example.com"
|
||||||
|
className="h-12 px-4 rounded-[11px] text-[14px] outline-none transition-all w-full"
|
||||||
|
style={{
|
||||||
|
background: 'var(--bg-input)',
|
||||||
|
border: `1px solid ${!valid ? '#ef4444' : 'var(--border-sub)'}`,
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
}}
|
||||||
|
onFocus={e => { if (valid) { e.currentTarget.style.borderColor = '#7C6FE0'; e.currentTarget.style.boxShadow = '0 0 0 3px rgba(124,111,224,0.12)'; }}}
|
||||||
|
onBlur={e => { e.currentTarget.style.borderColor = valid ? 'var(--border-sub)' : '#ef4444'; e.currentTarget.style.boxShadow = 'none'; }}
|
||||||
|
/>
|
||||||
|
{!valid && <p className="text-[12px]" style={{ color: '#ef4444' }}>Must start with http:// or https://</p>}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={shorten} disabled={shortening || !websiteUrl.trim() || !valid}
|
||||||
|
title="Shorten with is.gd"
|
||||||
|
className="h-12 px-4 rounded-[11px] text-[13px] font-medium flex-shrink-0 transition-all"
|
||||||
|
style={{
|
||||||
|
background: websiteUrl.trim() && valid ? 'rgba(124,111,224,0.12)' : 'var(--bg-elevated)',
|
||||||
|
border: '1px solid var(--border-sub)',
|
||||||
|
color: websiteUrl.trim() && valid ? '#7C6FE0' : 'var(--text-placeholder)',
|
||||||
|
cursor: websiteUrl.trim() && valid ? 'pointer' : 'not-allowed',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{shortening ? '…' : 'Shorten'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user