diff --git a/components/DraggablePaneContainer.jsx b/components/DraggablePaneContainer.jsx deleted file mode 100644 index 343c4b9..0000000 --- a/components/DraggablePaneContainer.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Allotment } from "allotment"; -import PropTypes from "prop-types"; -import React from "react"; -import "allotment/dist/style.css"; - -// DraggablePaneContainer: A component that uses Allotment to create a resizable pane container. -// Fixed height is set on the container wrapping the Allotment component. -const DraggablePaneContainer = ({ children, height = '500px' }) => { - // Ensure children is an array with two elements for two panes. - if (children.length !== 2) { - throw new Error("DraggablePaneContainer requires exactly two children."); - } - - const containerStyle = { height }; - const innerPaneStyle = { overflowY: "auto", height: "100%" }; - - return ( -
- - -
{children[0]}
-
- -
{children[1]}
-
-
-
- ); -}; - -DraggablePaneContainer.propTypes = { - children: PropTypes.arrayOf(PropTypes.node).isRequired, - height: PropTypes.string, -}; - -export default DraggablePaneContainer; diff --git a/components/FeatureTabsLeft.jsx b/components/FeatureTabsLeft.jsx index a1649c9..51090d4 100644 --- a/components/FeatureTabsLeft.jsx +++ b/components/FeatureTabsLeft.jsx @@ -59,7 +59,7 @@ const FeatureTabsLeft = (layout) => { 'group relative my-2 rounded-full px-4 py-1 lg:rounded-l-xl lg:rounded-r-none lg:p-6', selectedIndex === featureIndex ? 'bg-slate-600 group-hover:text-white dark:bg-slate-800 lg:ring-1 lg:ring-inset lg:ring-white/10 lg:dark:bg-slate-700 ' - : 'bg-gray-100 group-hover:text-white hover:bg-slate-600 dark:hover:bg-slate-800 lg:dark:hover:bg-slate-700', + : 'bg-gray-100 hover:bg-slate-600 group-hover:text-white dark:hover:bg-slate-800 lg:dark:hover:bg-slate-700', )} >

@@ -67,8 +67,8 @@ const FeatureTabsLeft = (layout) => { className={clsx( 'font-display text-lg [&:not(:focus-visible)]:focus:outline-none', selectedIndex === featureIndex - ? 'text-white group-hover:text-white hover:text-white dark:text-slate-200 dark:hover:text-slate-300 lg:dark:hover:text-slate-200' - : 'text-slate-800 group-hover:text-white hover:text-white dark:hover:text-slate-200', + ? 'text-white hover:text-white group-hover:text-white dark:text-slate-200 dark:hover:text-slate-300 lg:dark:hover:text-slate-200' + : 'text-slate-800 hover:text-white group-hover:text-white dark:hover:text-slate-200', )} > diff --git a/components/MiniGraphQL.jsx b/components/MiniGraphQL.jsx deleted file mode 100644 index 7faf39e..0000000 --- a/components/MiniGraphQL.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import hljs from 'highlight.js/lib/core' -import graphql from 'highlight.js/lib/languages/graphql' -import json from 'highlight.js/lib/languages/json' -import React, { useState, useEffect, useRef } from 'react' - -import 'highlight.js/styles/atom-one-dark.css' -import DraggablePaneContainer from './DraggablePaneContainer' - -// Register GraphQL and JSON for syntax highlighting -hljs.registerLanguage('graphql', graphql) -hljs.registerLanguage('json', json) - -export function MiniGraphQL({ initialQuery, initialVariables, endpoint }) { - const [query, setQuery] = useState(initialQuery || '') - const [variables, setVariables] = useState(initialVariables || {}) - const [data, setData] = useState(null) - const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) - const queryRef = useRef(null) - - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(highlightQuery, []) // Highlight query on mount - - function highlightQuery() { - if (query && queryRef.current) { - queryRef.current.innerHTML = hljs.highlight(query, { - language: 'graphql', - }).value - } - } - - function resetForm() { - setQuery(initialQuery || '') // Reset query to initial - setVariables(initialVariables || {}) // Reset variables to initial - setData(null) // Clear fetched data - setError(null) // Clear any errors - } - - async function fetchData() { - if (loading) return // Prevent fetching if already loading - - setLoading(true) - setError(null) - - try { - const response = await fetch(endpoint, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query, variables }), - }) - - const result = await response.json() - if (response.ok && result.data) { - setData(result.data) - } else { - setError( - new Error( - result.errors?.map((e) => e.message).join('\n') || - 'Error fetching data', - ), - ) - } - } catch (e) { - setError(e) - } finally { - setLoading(false) - } - } - - function handleQueryChange() { - if (queryRef.current) { - setQuery(queryRef.current.textContent) - resetForm() // Reset the form when query changes - } - } - - function handleBlur() { - highlightQuery() // Re-highlight the query on blur - } - - function handleRunQuery() { - if (data) { - resetForm() // Act as reset if data is present - } else { - fetchData() // Fetch data if no data present - } - } - - return ( -
- -
-
-        
-
-
- {error && ( -
{error.message}
- )} - {data && ( -
-                
-                  {JSON.stringify(data, null, 2)}
-                
-              
- )} -
-
-
-
- -
-
- ) -} diff --git a/components/MiniGraphiQL.jsx b/components/MiniGraphiQL.jsx new file mode 100644 index 0000000..ce53227 --- /dev/null +++ b/components/MiniGraphiQL.jsx @@ -0,0 +1,158 @@ +import { useTheme } from 'next-themes'; +import PropTypes from 'prop-types'; +import React, { useEffect, useState } from 'react'; + +const MiniGraphiQLClient = ({ initialQuery, initialVariables, endpoint, readOnly }) => { + const [GraphiQL, setGraphiQL] = useState(null); + const { theme, resolvedTheme } = useTheme(); + + useEffect(() => { + let isMounted = true; + (async () => { + if (typeof window !== 'undefined') { + try { + const GraphiQLModule = await import('graphiql'); + await import('graphiql/graphiql.min.css'); + if (isMounted) { + setGraphiQL(() => GraphiQLModule.default); + } + } catch (error) { + console.error('Failed to load GraphiQL module:', error); + } + } + })(); + + return () => { + isMounted = false; + }; + }, []); + + useEffect(() => { + if (typeof window !== 'undefined') { + localStorage.removeItem('graphiql:tabState'); + localStorage.setItem('graphiql:theme', theme); + + // Check for the theme and update the body class + const bodyClassList = document.body.classList; + if (theme === 'dark') { + bodyClassList.add('graphiql-dark'); + } else { + bodyClassList.remove('graphiql-dark'); + } + } + }, [theme]); + + const fetcher = async (graphQLParams) => { + try { + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(graphQLParams), + }); + + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('Error fetching data:', error); + throw error; // Re-throw to make sure GraphiQL displays the error + } + }; + + let parsedVariables = initialVariables; + if (typeof initialVariables === 'string') { + try { + parsedVariables = JSON.parse(initialVariables); + } catch (e) { + console.error('Failed to parse variables as JSON:', e); + } + } + + const containerStyles = { + height: '80vh', + maxHeight: 'auto', + borderRadius: '4px', + padding: '0.5rem', + display: 'flex', + flex: '1 1 0%', + }; + + const graphiqlStyles = ` + :root { + color-scheme: ${theme}; + } + .graphiql-container { + background-color: transparent !important; + font-size: 14px; + } + .graphiql-container * { + box-shadow: none !important; + } + .graphiql-container .graphiql-editor-tools button:nth-child(2) { + display: none; + } + .graphiql-container .graphiql-editors { + border-radius: 2px; + } + .graphiql-container .graphiql-editors.full-height { + margin-top: 8px; + } + .graphiql-container .graphiql-query-editor { + border-bottom: 0; + padding: 6px; + column-gap: 6px; + } + .graphiql-container .graphiql-response .result-window { + padding-top: 8px; + } + .graphiql-container .graphiql-session-header { + display: none; + } + .graphiql-container .graphiql-sessions { + border-radius: 2px; + } + .graphiql-container .graphiql-sidebar { + display: none; + } + .graphiql-toolbar button:nth-child(2) { + display: none; /* prettify */ + } + .graphiql-toolbar button:nth-child(3) { + display: none; /* merge */ + } + `; + + return ( +
+