Connor Fogarty
commited on
feat: add ability to change preview URL (#26)
Browse files
packages/bolt/app/components/workbench/Preview.tsx
CHANGED
@@ -1,10 +1,11 @@
|
|
1 |
import { useStore } from '@nanostores/react';
|
2 |
-
import { memo, useEffect, useRef, useState } from 'react';
|
3 |
import { IconButton } from '~/components/ui/IconButton';
|
4 |
import { workbenchStore } from '~/lib/stores/workbench';
|
5 |
|
6 |
export const Preview = memo(() => {
|
7 |
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
|
8 |
const [activePreviewIndex] = useState(0);
|
9 |
const previews = useStore(workbenchStore.previews);
|
10 |
const activePreview = previews[activePreviewIndex];
|
@@ -28,6 +29,25 @@ export const Preview = memo(() => {
|
|
28 |
}
|
29 |
}, [activePreview, iframeUrl]);
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
const reloadPreview = () => {
|
32 |
if (iframeRef.current) {
|
33 |
iframeRef.current.src = iframeRef.current.src;
|
@@ -43,12 +63,22 @@ export const Preview = memo(() => {
|
|
43 |
<div className="i-ph:info-bold text-lg" />
|
44 |
</div>
|
45 |
<input
|
|
|
46 |
className="w-full bg-transparent outline-none"
|
47 |
type="text"
|
48 |
value={url}
|
49 |
onChange={(event) => {
|
50 |
setUrl(event.target.value);
|
51 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
/>
|
53 |
</div>
|
54 |
</div>
|
|
|
1 |
import { useStore } from '@nanostores/react';
|
2 |
+
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
3 |
import { IconButton } from '~/components/ui/IconButton';
|
4 |
import { workbenchStore } from '~/lib/stores/workbench';
|
5 |
|
6 |
export const Preview = memo(() => {
|
7 |
const iframeRef = useRef<HTMLIFrameElement>(null);
|
8 |
+
const inputRef = useRef<HTMLInputElement>(null);
|
9 |
const [activePreviewIndex] = useState(0);
|
10 |
const previews = useStore(workbenchStore.previews);
|
11 |
const activePreview = previews[activePreviewIndex];
|
|
|
29 |
}
|
30 |
}, [activePreview, iframeUrl]);
|
31 |
|
32 |
+
const validateUrl = useCallback(
|
33 |
+
(value: string) => {
|
34 |
+
if (!activePreview) {
|
35 |
+
return false;
|
36 |
+
}
|
37 |
+
|
38 |
+
const { baseUrl } = activePreview;
|
39 |
+
|
40 |
+
if (value === baseUrl) {
|
41 |
+
return true;
|
42 |
+
} else if (value.startsWith(baseUrl)) {
|
43 |
+
return ['/', '?', '#'].includes(value.charAt(baseUrl.length));
|
44 |
+
}
|
45 |
+
|
46 |
+
return false;
|
47 |
+
},
|
48 |
+
[activePreview],
|
49 |
+
);
|
50 |
+
|
51 |
const reloadPreview = () => {
|
52 |
if (iframeRef.current) {
|
53 |
iframeRef.current.src = iframeRef.current.src;
|
|
|
63 |
<div className="i-ph:info-bold text-lg" />
|
64 |
</div>
|
65 |
<input
|
66 |
+
ref={inputRef}
|
67 |
className="w-full bg-transparent outline-none"
|
68 |
type="text"
|
69 |
value={url}
|
70 |
onChange={(event) => {
|
71 |
setUrl(event.target.value);
|
72 |
}}
|
73 |
+
onKeyDown={(event) => {
|
74 |
+
if (event.key === 'Enter' && validateUrl(url)) {
|
75 |
+
setIframeUrl(url);
|
76 |
+
|
77 |
+
if (inputRef.current) {
|
78 |
+
inputRef.current.blur();
|
79 |
+
}
|
80 |
+
}
|
81 |
+
}}
|
82 |
/>
|
83 |
</div>
|
84 |
</div>
|