import React, { Fragment, useCallback, useState, } from 'react';
import { Button, ButtonGroup, ChevronDownIcon, ChevronUpIcon, CopyIcon, Dialog, ExecuteButton, GraphiQLProvider, HeaderEditor, KeyboardShortcutIcon, MergeIcon, PlusIcon, PrettifyIcon, QueryEditor, ReloadIcon, ResponseEditor, SettingsIcon, Spinner, Tab, Tabs, ToolbarButton, Tooltip, UnStyledButton, useCopyQuery, useDragResize, useEditorContext, useExecutionContext, useMergeQuery, usePluginContext, usePrettifyEditors, useSchemaContext, useStorageContext, useTheme, VariableEditor, } from '@graphiql/react';
const majorVersion = parseInt(React.version.slice(0, 2), 10);
if (majorVersion < 16) {
    throw new Error([
        'GraphiQL 0.18.0 and after is not compatible with React 15 or below.',
        'If you are using a CDN source (jsdelivr, unpkg, etc), follow this example:',
        'https://github.com/graphql/graphiql/blob/master/examples/graphiql-cdn/index.html#L49',
    ].join('\n'));
}
export function GraphiQL({ dangerouslyAssumeSchemaIsValid, defaultQuery, defaultTabs, externalFragments, fetcher, getDefaultFieldNames, headers, inputValueDeprecation, introspectionQueryName, maxHistoryLength, onEditOperationName, onSchemaChange, onTabChange, onTogglePluginVisibility, operationName, plugins, query, response, schema, schemaDescription, shouldPersistHeaders, storage, validationRules, variables, visiblePlugin, defaultHeaders, ...props }) {
    if (typeof fetcher !== 'function') {
        throw new TypeError('The `GraphiQL` component requires a `fetcher` function to be passed as prop.');
    }
    return (React.createElement(GraphiQLProvider, { getDefaultFieldNames: getDefaultFieldNames, dangerouslyAssumeSchemaIsValid: dangerouslyAssumeSchemaIsValid, defaultQuery: defaultQuery, defaultHeaders: defaultHeaders, defaultTabs: defaultTabs, externalFragments: externalFragments, fetcher: fetcher, headers: headers, inputValueDeprecation: inputValueDeprecation, introspectionQueryName: introspectionQueryName, maxHistoryLength: maxHistoryLength, onEditOperationName: onEditOperationName, onSchemaChange: onSchemaChange, onTabChange: onTabChange, onTogglePluginVisibility: onTogglePluginVisibility, plugins: plugins, visiblePlugin: visiblePlugin, operationName: operationName, query: query, response: response, schema: schema, schemaDescription: schemaDescription, shouldPersistHeaders: shouldPersistHeaders, storage: storage, validationRules: validationRules, variables: variables },
        React.createElement(GraphiQLInterface, { showPersistHeadersSettings: shouldPersistHeaders !== false, ...props })));
}
GraphiQL.Logo = GraphiQLLogo;
GraphiQL.Toolbar = GraphiQLToolbar;
GraphiQL.Footer = GraphiQLFooter;
export function GraphiQLInterface(props) {
    const isHeadersEditorEnabled = props.isHeadersEditorEnabled ?? true;
    const editorContext = useEditorContext({ nonNull: true });
    const executionContext = useExecutionContext({ nonNull: true });
    const schemaContext = useSchemaContext({ nonNull: true });
    const storageContext = useStorageContext();
    const pluginContext = usePluginContext();
    const copy = useCopyQuery({ onCopyQuery: props.onCopyQuery });
    const merge = useMergeQuery();
    const prettify = usePrettifyEditors();
    const { theme, setTheme } = useTheme();
    const PluginContent = pluginContext?.visiblePlugin?.content;
    const pluginResize = useDragResize({
        defaultSizeRelation: 1 / 3,
        direction: 'horizontal',
        initiallyHidden: pluginContext?.visiblePlugin ? undefined : 'first',
        onHiddenElementChange(resizableElement) {
            if (resizableElement === 'first') {
                pluginContext?.setVisiblePlugin(null);
            }
        },
        sizeThresholdSecond: 200,
        storageKey: 'docExplorerFlex',
    });
    const editorResize = useDragResize({
        direction: 'horizontal',
        storageKey: 'editorFlex',
    });
    const editorToolsResize = useDragResize({
        defaultSizeRelation: 3,
        direction: 'vertical',
        initiallyHidden: (() => {
            if (props.defaultEditorToolsVisibility === 'variables' ||
                props.defaultEditorToolsVisibility === 'headers') {
                return;
            }
            if (typeof props.defaultEditorToolsVisibility === 'boolean') {
                return props.defaultEditorToolsVisibility ? undefined : 'second';
            }
            return editorContext.initialVariables || editorContext.initialHeaders
                ? undefined
                : 'second';
        })(),
        sizeThresholdSecond: 60,
        storageKey: 'secondaryEditorFlex',
    });
    const [activeSecondaryEditor, setActiveSecondaryEditor] = useState(() => {
        if (props.defaultEditorToolsVisibility === 'variables' ||
            props.defaultEditorToolsVisibility === 'headers') {
            return props.defaultEditorToolsVisibility;
        }
        return !editorContext.initialVariables &&
            editorContext.initialHeaders &&
            isHeadersEditorEnabled
            ? 'headers'
            : 'variables';
    });
    const [showDialog, setShowDialog] = useState(null);
    const [clearStorageStatus, setClearStorageStatus] = useState(null);
    const children = React.Children.toArray(props.children);
    const logo = children.find(child => isChildComponentType(child, GraphiQL.Logo)) || React.createElement(GraphiQL.Logo, null);
    const toolbar = children.find(child => isChildComponentType(child, GraphiQL.Toolbar)) || (React.createElement(React.Fragment, null,
        React.createElement(ToolbarButton, { onClick: prettify, label: "Prettify query (Shift-Ctrl-P)" },
            React.createElement(PrettifyIcon, { className: "graphiql-toolbar-icon", "aria-hidden": "true" })),
        React.createElement(ToolbarButton, { onClick: merge, label: "Merge fragments into query (Shift-Ctrl-M)" },
            React.createElement(MergeIcon, { className: "graphiql-toolbar-icon", "aria-hidden": "true" })),
        React.createElement(ToolbarButton, { onClick: copy, label: "Copy query (Shift-Ctrl-C)" },
            React.createElement(CopyIcon, { className: "graphiql-toolbar-icon", "aria-hidden": "true" })),
        props.toolbar?.additionalContent));
    const footer = children.find(child => isChildComponentType(child, GraphiQL.Footer));
    const onClickReference = useCallback(() => {
        if (pluginResize.hiddenElement === 'first') {
            pluginResize.setHiddenElement(null);
        }
    }, [pluginResize]);
    const handleClearData = useCallback(() => {
        try {
            storageContext?.clear();
            setClearStorageStatus('success');
        }
        catch {
            setClearStorageStatus('error');
        }
    }, [storageContext]);
    const handlePersistHeaders = useCallback(event => {
        editorContext.setShouldPersistHeaders(event.currentTarget.dataset.value === 'true');
    }, [editorContext]);
    const handleChangeTheme = useCallback(event => {
        const selectedTheme = event.currentTarget.dataset.theme;
        setTheme(selectedTheme || null);
    }, [setTheme]);
    const handleAddTab = editorContext.addTab;
    const handleRefetchSchema = schemaContext.introspect;
    const handleReorder = editorContext.moveTab;
    const handleShowDialog = useCallback(event => {
        setShowDialog(event.currentTarget.dataset.value);
    }, []);
    const handlePluginClick = useCallback(e => {
        const context = pluginContext;
        const pluginIndex = Number(e.currentTarget.dataset.index);
        const plugin = context.plugins.find((_, index) => pluginIndex === index);
        const isVisible = plugin === context.visiblePlugin;
        if (isVisible) {
            context.setVisiblePlugin(null);
            pluginResize.setHiddenElement('first');
        }
        else {
            context.setVisiblePlugin(plugin);
            pluginResize.setHiddenElement(null);
        }
    }, [pluginContext, pluginResize]);
    const handleToolsTabClick = useCallback(event => {
        if (editorToolsResize.hiddenElement === 'second') {
            editorToolsResize.setHiddenElement(null);
        }
        setActiveSecondaryEditor(event.currentTarget.dataset.name);
    }, [editorToolsResize]);
    const toggleEditorTools = useCallback(() => {
        editorToolsResize.setHiddenElement(editorToolsResize.hiddenElement === 'second' ? null : 'second');
    }, [editorToolsResize]);
    const handleOpenShortKeysDialog = useCallback((isOpen) => {
        if (!isOpen) {
            setShowDialog(null);
        }
    }, []);
    const handleOpenSettingsDialog = useCallback((isOpen) => {
        if (!isOpen) {
            setShowDialog(null);
            setClearStorageStatus(null);
        }
    }, []);
    const addTab = (React.createElement(Tooltip, { label: "Add tab" },
        React.createElement(UnStyledButton, { type: "button", className: "graphiql-tab-add", onClick: handleAddTab, "aria-label": "Add tab" },
            React.createElement(PlusIcon, { "aria-hidden": "true" }))));
    return (React.createElement(Tooltip.Provider, null,
        React.createElement("div", { "data-testid": "graphiql-container", className: "graphiql-container" },
            React.createElement("div", { className: "graphiql-sidebar" },
                React.createElement("div", { className: "graphiql-sidebar-section" }, pluginContext?.plugins.map((plugin, index) => {
                    const isVisible = plugin === pluginContext.visiblePlugin;
                    const label = `${isVisible ? 'Hide' : 'Show'} ${plugin.title}`;
                    const Icon = plugin.icon;
                    return (React.createElement(Tooltip, { key: plugin.title, label: label },
                        React.createElement(UnStyledButton, { type: "button", className: isVisible ? 'active' : '', onClick: handlePluginClick, "data-index": index, "aria-label": label },
                            React.createElement(Icon, { "aria-hidden": "true" }))));
                })),
                React.createElement("div", { className: "graphiql-sidebar-section" },
                    React.createElement(Tooltip, { label: "Re-fetch GraphQL schema" },
                        React.createElement(UnStyledButton, { type: "button", disabled: schemaContext.isFetching, onClick: handleRefetchSchema, "aria-label": "Re-fetch GraphQL schema" },
                            React.createElement(ReloadIcon, { className: schemaContext.isFetching ? 'graphiql-spin' : '', "aria-hidden": "true" }))),
                    React.createElement(Tooltip, { label: "Open short keys dialog" },
                        React.createElement(UnStyledButton, { type: "button", "data-value": "short-keys", onClick: handleShowDialog, "aria-label": "Open short keys dialog" },
                            React.createElement(KeyboardShortcutIcon, { "aria-hidden": "true" }))),
                    React.createElement(Tooltip, { label: "Open settings dialog" },
                        React.createElement(UnStyledButton, { type: "button", "data-value": "settings", onClick: handleShowDialog, "aria-label": "Open settings dialog" },
                            React.createElement(SettingsIcon, { "aria-hidden": "true" }))))),
            React.createElement("div", { className: "graphiql-main" },
                React.createElement("div", { ref: pluginResize.firstRef, style: {
                        minWidth: '200px',
                    } },
                    React.createElement("div", { className: "graphiql-plugin" }, PluginContent ? React.createElement(PluginContent, null) : null)),
                pluginContext?.visiblePlugin && (React.createElement("div", { className: "graphiql-horizontal-drag-bar", ref: pluginResize.dragBarRef })),
                React.createElement("div", { ref: pluginResize.secondRef, className: "graphiql-sessions" },
                    React.createElement("div", { className: "graphiql-session-header" },
                        React.createElement(Tabs, { values: editorContext.tabs, onReorder: handleReorder, "aria-label": "Select active operation" }, editorContext.tabs.length > 1 && (React.createElement(React.Fragment, null,
                            editorContext.tabs.map((tab, index) => (React.createElement(Tab, { key: tab.id, value: tab, isActive: index === editorContext.activeTabIndex },
                                React.createElement(Tab.Button, { "aria-controls": "graphiql-session", id: `graphiql-session-tab-${index}`, onClick: () => {
                                        executionContext.stop();
                                        editorContext.changeTab(index);
                                    } }, tab.title),
                                React.createElement(Tab.Close, { onClick: () => {
                                        if (editorContext.activeTabIndex === index) {
                                            executionContext.stop();
                                        }
                                        editorContext.closeTab(index);
                                    } })))),
                            addTab))),
                        React.createElement("div", { className: "graphiql-session-header-right" },
                            editorContext.tabs.length === 1 && addTab,
                            logo)),
                    React.createElement("div", { role: "tabpanel", id: "graphiql-session", className: "graphiql-session", "aria-labelledby": `graphiql-session-tab-${editorContext.activeTabIndex}` },
                        React.createElement("div", { ref: editorResize.firstRef },
                            React.createElement("div", { className: `graphiql-editors${editorContext.tabs.length === 1 ? ' full-height' : ''}` },
                                React.createElement("div", { ref: editorToolsResize.firstRef },
                                    React.createElement("section", { className: "graphiql-query-editor", "aria-label": "Query Editor" },
                                        React.createElement(QueryEditor, { editorTheme: props.editorTheme, keyMap: props.keyMap, onClickReference: onClickReference, onCopyQuery: props.onCopyQuery, onEdit: props.onEditQuery, readOnly: props.readOnly }),
                                        React.createElement("div", { className: "graphiql-toolbar", role: "toolbar", "aria-label": "Editor Commands" },
                                            React.createElement(ExecuteButton, null),
                                            toolbar))),
                                React.createElement("div", { ref: editorToolsResize.dragBarRef },
                                    React.createElement("div", { className: "graphiql-editor-tools" },
                                        React.createElement(UnStyledButton, { type: "button", className: activeSecondaryEditor === 'variables' &&
                                                editorToolsResize.hiddenElement !== 'second'
                                                ? 'active'
                                                : '', onClick: handleToolsTabClick, "data-name": "variables" }, "Variables"),
                                        isHeadersEditorEnabled && (React.createElement(UnStyledButton, { type: "button", className: activeSecondaryEditor === 'headers' &&
                                                editorToolsResize.hiddenElement !== 'second'
                                                ? 'active'
                                                : '', onClick: handleToolsTabClick, "data-name": "headers" }, "Headers")),
                                        React.createElement(Tooltip, { label: editorToolsResize.hiddenElement === 'second'
                                                ? 'Show editor tools'
                                                : 'Hide editor tools' },
                                            React.createElement(UnStyledButton, { type: "button", onClick: toggleEditorTools, "aria-label": editorToolsResize.hiddenElement === 'second'
                                                    ? 'Show editor tools'
                                                    : 'Hide editor tools', className: "graphiql-toggle-editor-tools" }, editorToolsResize.hiddenElement === 'second' ? (React.createElement(ChevronUpIcon, { className: "graphiql-chevron-icon", "aria-hidden": "true" })) : (React.createElement(ChevronDownIcon, { className: "graphiql-chevron-icon", "aria-hidden": "true" })))))),
                                React.createElement("div", { ref: editorToolsResize.secondRef },
                                    React.createElement("section", { className: "graphiql-editor-tool", "aria-label": activeSecondaryEditor === 'variables'
                                            ? 'Variables'
                                            : 'Headers' },
                                        React.createElement(VariableEditor, { editorTheme: props.editorTheme, isHidden: activeSecondaryEditor !== 'variables', keyMap: props.keyMap, onEdit: props.onEditVariables, onClickReference: onClickReference, readOnly: props.readOnly }),
                                        isHeadersEditorEnabled && (React.createElement(HeaderEditor, { editorTheme: props.editorTheme, isHidden: activeSecondaryEditor !== 'headers', keyMap: props.keyMap, onEdit: props.onEditHeaders, readOnly: props.readOnly })))))),
                        React.createElement("div", { className: "graphiql-horizontal-drag-bar", ref: editorResize.dragBarRef }),
                        React.createElement("div", { ref: editorResize.secondRef },
                            React.createElement("div", { className: "graphiql-response" },
                                executionContext.isFetching ? React.createElement(Spinner, null) : null,
                                React.createElement(ResponseEditor, { editorTheme: props.editorTheme, responseTooltip: props.responseTooltip, keyMap: props.keyMap }),
                                footer))))),
            React.createElement(Dialog, { open: showDialog === 'short-keys', onOpenChange: handleOpenShortKeysDialog },
                React.createElement("div", { className: "graphiql-dialog-header" },
                    React.createElement(Dialog.Title, { className: "graphiql-dialog-title" }, "Short Keys"),
                    React.createElement(Dialog.Close, null)),
                React.createElement("div", { className: "graphiql-dialog-section" },
                    React.createElement(ShortKeys, { keyMap: props.keyMap || 'sublime' }))),
            React.createElement(Dialog, { open: showDialog === 'settings', onOpenChange: handleOpenSettingsDialog },
                React.createElement("div", { className: "graphiql-dialog-header" },
                    React.createElement(Dialog.Title, { className: "graphiql-dialog-title" }, "Settings"),
                    React.createElement(Dialog.Close, null)),
                props.showPersistHeadersSettings ? (React.createElement("div", { className: "graphiql-dialog-section" },
                    React.createElement("div", null,
                        React.createElement("div", { className: "graphiql-dialog-section-title" }, "Persist headers"),
                        React.createElement("div", { className: "graphiql-dialog-section-caption" },
                            "Save headers upon reloading.",
                            ' ',
                            React.createElement("span", { className: "graphiql-warning-text" }, "Only enable if you trust this device."))),
                    React.createElement(ButtonGroup, null,
                        React.createElement(Button, { type: "button", id: "enable-persist-headers", className: editorContext.shouldPersistHeaders ? 'active' : '', "data-value": "true", onClick: handlePersistHeaders }, "On"),
                        React.createElement(Button, { type: "button", id: "disable-persist-headers", className: editorContext.shouldPersistHeaders ? '' : 'active', onClick: handlePersistHeaders }, "Off")))) : null,
                React.createElement("div", { className: "graphiql-dialog-section" },
                    React.createElement("div", null,
                        React.createElement("div", { className: "graphiql-dialog-section-title" }, "Theme"),
                        React.createElement("div", { className: "graphiql-dialog-section-caption" }, "Adjust how the interface looks like.")),
                    React.createElement(ButtonGroup, null,
                        React.createElement(Button, { type: "button", className: theme === null ? 'active' : '', onClick: handleChangeTheme }, "System"),
                        React.createElement(Button, { type: "button", className: theme === 'light' ? 'active' : '', "data-theme": "light", onClick: handleChangeTheme }, "Light"),
                        React.createElement(Button, { type: "button", className: theme === 'dark' ? 'active' : '', "data-theme": "dark", onClick: handleChangeTheme }, "Dark"))),
                storageContext ? (React.createElement("div", { className: "graphiql-dialog-section" },
                    React.createElement("div", null,
                        React.createElement("div", { className: "graphiql-dialog-section-title" }, "Clear storage"),
                        React.createElement("div", { className: "graphiql-dialog-section-caption" }, "Remove all locally stored data and start fresh.")),
                    React.createElement(Button, { type: "button", state: clearStorageStatus || undefined, disabled: clearStorageStatus === 'success', onClick: handleClearData }, {
                        success: 'Cleared data',
                        error: 'Failed',
                    }[clearStorageStatus] || 'Clear data'))) : null))));
}
const modifier = typeof window !== 'undefined' &&
    window.navigator.platform.toLowerCase().indexOf('mac') === 0
    ? 'Cmd'
    : 'Ctrl';
const SHORT_KEYS = Object.entries({
    'Search in editor': [modifier, 'F'],
    'Search in documentation': [modifier, 'K'],
    'Execute query': [modifier, 'Enter'],
    'Prettify editors': ['Ctrl', 'Shift', 'P'],
    'Merge fragments definitions into operation definition': [
        'Ctrl',
        'Shift',
        'M',
    ],
    'Copy query': ['Ctrl', 'Shift', 'C'],
    'Re-fetch schema using introspection': ['Ctrl', 'Shift', 'R'],
});
function ShortKeys({ keyMap }) {
    return (React.createElement("div", null,
        React.createElement("table", { className: "graphiql-table" },
            React.createElement("thead", null,
                React.createElement("tr", null,
                    React.createElement("th", null, "Short Key"),
                    React.createElement("th", null, "Function"))),
            React.createElement("tbody", null, SHORT_KEYS.map(([title, keys]) => (React.createElement("tr", { key: title },
                React.createElement("td", null, keys.map((key, index, array) => (React.createElement(Fragment, { key: key },
                    React.createElement("code", { className: "graphiql-key" }, key),
                    index !== array.length - 1 && ' + ')))),
                React.createElement("td", null, title)))))),
        React.createElement("p", null,
            "The editors use",
            ' ',
            React.createElement("a", { href: "https://codemirror.net/5/doc/manual.html#keymaps", target: "_blank", rel: "noopener noreferrer" }, "CodeMirror Key Maps"),
            ' ',
            "that add more short keys. This instance of Graph",
            React.createElement("em", null, "i"),
            "QL uses",
            ' ',
            React.createElement("code", null, keyMap),
            ".")));
}
function GraphiQLLogo(props) {
    return (React.createElement("div", { className: "graphiql-logo" }, props.children || (React.createElement("a", { className: "graphiql-logo-link", href: "https://github.com/graphql/graphiql", target: "_blank", rel: "noreferrer" },
        "Graph",
        React.createElement("em", null, "i"),
        "QL"))));
}
GraphiQLLogo.displayName = 'GraphiQLLogo';
function GraphiQLToolbar(props) {
    return React.createElement(React.Fragment, null, props.children);
}
GraphiQLToolbar.displayName = 'GraphiQLToolbar';
function GraphiQLFooter(props) {
    return React.createElement("div", { className: "graphiql-footer" }, props.children);
}
GraphiQLFooter.displayName = 'GraphiQLFooter';
function isChildComponentType(child, component) {
    if (child?.type?.displayName &&
        child.type.displayName === component.displayName) {
        return true;
    }
    return child.type === component;
}
//# sourceMappingURL=GraphiQL.js.map