UNPKG

next-sanity

Version:
145 lines (144 loc) 5.8 kB
import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { VisualEditing as VisualEditing$1 } from "@sanity/visual-editing/react"; import { jsx } from "react/jsx-runtime"; import { useCallback, useEffect, useMemo, useState } from "react"; /** * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/path-has-prefix.ts#L10-L17 * Checks if a given path starts with a given prefix. It ensures it matches * exactly without containing extra chars. e.g. prefix /docs should replace * for /docs, /docs/, /docs/a but not /docsss * @param path The path to check. * @param prefix The prefix to check against. */ function pathHasPrefix(path, prefix) { if (typeof path !== "string") return false; const { pathname } = parsePath(path); return pathname === prefix || pathname.startsWith(`${prefix}/`); } /** * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/parse-path.ts#L6-L22 * Given a path this function will find the pathname, query and hash and return * them. This is useful to parse full paths on the client side. * @param path A path to parse e.g. /foo/bar?id=1#hash */ function parsePath(path) { const hashIndex = path.indexOf("#"); const queryIndex = path.indexOf("?"); const hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex); if (hasQuery || hashIndex > -1) return { pathname: path.substring(0, hasQuery ? queryIndex : hashIndex), query: hasQuery ? path.substring(queryIndex, hashIndex > -1 ? hashIndex : void 0) : "", hash: hashIndex > -1 ? path.slice(hashIndex) : "" }; return { pathname: path, query: "", hash: "" }; } /** * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/add-path-prefix.ts#L3C1-L14C2 * Adds the provided prefix to the given path. It first ensures that the path * is indeed starting with a slash. */ function addPathPrefix(path, prefix) { if (!path.startsWith("/") || !prefix) return path; if (path === "/" && prefix) return prefix; const { pathname, query, hash } = parsePath(path); return `${prefix}${pathname}${query}${hash}`; } /** * From: https://github.com/vercel/next.js/blob/5469e6427b54ab7e9876d4c85b47f9c3afdc5c1f/packages/next/src/shared/lib/router/utils/remove-path-prefix.ts#L3-L39 * Given a path and a prefix it will remove the prefix when it exists in the * given path. It ensures it matches exactly without containing extra chars * and if the prefix is not there it will be noop. * * @param path The path to remove the prefix from. * @param prefix The prefix to be removed. */ function removePathPrefix(path, prefix) { if (!pathHasPrefix(path, prefix)) return path; const withoutPrefix = path.slice(prefix.length); if (withoutPrefix.startsWith("/")) return withoutPrefix; return `/${withoutPrefix}`; } /** * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/client/normalize-trailing-slash.ts#L16 * Normalizes the trailing slash of a path according to the `trailingSlash` option * in `next.config.js`. */ const normalizePathTrailingSlash = (path, trailingSlash) => { const { pathname, query, hash } = parsePath(path); if (trailingSlash) { if (pathname.endsWith("/")) return `${pathname}${query}${hash}`; return `${pathname}/${query}${hash}`; } return `${removeTrailingSlash(pathname)}${query}${hash}`; }; /** * From: https://github.com/vercel/next.js/blob/dfe7fc03e2268e7cb765dce6a89e02c831c922d5/packages/next/src/shared/lib/router/utils/remove-trailing-slash.ts#L8 * Removes the trailing slash for a given route or page path. Preserves the * root page. Examples: * - `/foo/bar/` -> `/foo/bar` * - `/foo/bar` -> `/foo/bar` * - `/` -> `/` */ function removeTrailingSlash(route) { return route.replace(/\/$/, "") || "/"; } function VisualEditing(props) { const { basePath = "", plugins, components, refresh, trailingSlash = false, zIndex, onPerspectiveChange } = props; const router = useRouter(); const [navigate, setNavigate] = useState(); const history = useMemo(() => ({ subscribe: (_navigate) => { setNavigate(() => _navigate); return () => setNavigate(void 0); }, update: (update) => { switch (update.type) { case "push": return router.push(removePathPrefix(update.url, basePath)); case "pop": return router.back(); case "replace": return router.replace(removePathPrefix(update.url, basePath)); default: throw new Error(`Unknown update type`, { cause: update }); } } }), [basePath, router]); const pathname = usePathname(); const searchParams = useSearchParams(); useEffect(() => { if (navigate) navigate({ type: "push", url: normalizePathTrailingSlash(addPathPrefix(`${pathname}${searchParams?.size ? `?${searchParams.toString()}` : ""}`, basePath), trailingSlash) }); }, [ basePath, navigate, pathname, searchParams, trailingSlash ]); const handleRefresh = useCallback((payload) => { switch (payload.source) { case "manual": router.refresh(); break; case "mutation": console.debug("<VisualEditing /> refresh called with source \"mutation\", if you want automatic refresh when this happens, or silence this message, provide your own handler to the refresh prop"); return false; default: throw new Error("Unknown refresh source", { cause: payload }); } return new Promise((resolve) => setTimeout(resolve, 1e3)); }, [router]); return /* @__PURE__ */ jsx(VisualEditing$1, { plugins, components, history, portal: true, refresh: refresh ?? handleRefresh, onPerspectiveChange, zIndex }); } export { VisualEditing as default }; //# sourceMappingURL=VisualEditing.js.map