feat: codemirror for graphql query, scheme and response
This commit is contained in:
@@ -55,20 +55,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<GraphqlQueryEditor
|
<div ref="queryEditor" class="w-full block"></div>
|
||||||
ref="queryEditor"
|
|
||||||
v-model="gqlQueryString"
|
|
||||||
:on-run-g-q-l-query="runQuery"
|
|
||||||
:options="{
|
|
||||||
maxLines: Infinity,
|
|
||||||
minLines: 16,
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
styles="border-b border-dividerLight"
|
|
||||||
@update-query="updateQuery"
|
|
||||||
/>
|
|
||||||
</AppSection>
|
</AppSection>
|
||||||
</SmartTab>
|
</SmartTab>
|
||||||
|
|
||||||
@@ -284,6 +271,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, useContext, watch } from "@nuxtjs/composition-api"
|
import { onMounted, ref, useContext, watch } from "@nuxtjs/composition-api"
|
||||||
import clone from "lodash/clone"
|
import clone from "lodash/clone"
|
||||||
|
import * as gql from "graphql"
|
||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import {
|
import {
|
||||||
useNuxt,
|
useNuxt,
|
||||||
@@ -312,6 +300,9 @@ import { getCurrentStrategyID } from "~/helpers/network"
|
|||||||
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
|
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import "codemirror/mode/javascript/javascript"
|
import "codemirror/mode/javascript/javascript"
|
||||||
|
import jsonLinter from "~/helpers/editor/linting/json"
|
||||||
|
import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery"
|
||||||
|
import queryCompleter from "~/helpers/editor/completion/gqlQuery"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
conn: GQLConnection
|
conn: GQLConnection
|
||||||
@@ -363,13 +354,22 @@ const variableEditor = ref<any | null>(null)
|
|||||||
|
|
||||||
useCodemirror(variableEditor, variableString, {
|
useCodemirror(variableEditor, variableString, {
|
||||||
extendedEditorConfig: {
|
extendedEditorConfig: {
|
||||||
mode: "javascript",
|
mode: "application/ld+json",
|
||||||
},
|
},
|
||||||
linter: null,
|
linter: jsonLinter,
|
||||||
completer: null,
|
completer: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const queryEditor = ref<any | null>(null)
|
const queryEditor = ref<any | null>(null)
|
||||||
|
const schemaString = useReadonlyStream(props.conn.schema$, null)
|
||||||
|
|
||||||
|
useCodemirror(queryEditor, gqlQueryString, {
|
||||||
|
extendedEditorConfig: {
|
||||||
|
mode: "application/ld+json",
|
||||||
|
},
|
||||||
|
linter: createGQLQueryLinter(schemaString),
|
||||||
|
completer: queryCompleter(schemaString),
|
||||||
|
})
|
||||||
|
|
||||||
const copyQueryIcon = ref("copy")
|
const copyQueryIcon = ref("copy")
|
||||||
const prettifyQueryIcon = ref("align-left")
|
const prettifyQueryIcon = ref("align-left")
|
||||||
@@ -466,7 +466,13 @@ const hideRequestModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prettifyQuery = () => {
|
const prettifyQuery = () => {
|
||||||
queryEditor.value.prettifyQuery()
|
try {
|
||||||
|
gqlQueryString.value = gql.print(gql.parse(gqlQueryString.value))
|
||||||
|
} catch (e) {
|
||||||
|
$toast.error(t("error.gql_prettify_invalid_query").toString(), {
|
||||||
|
icon: "error_outline",
|
||||||
|
})
|
||||||
|
}
|
||||||
prettifyQueryIcon.value = "check"
|
prettifyQueryIcon.value = "check"
|
||||||
setTimeout(() => (prettifyQueryIcon.value = "align-left"), 1000)
|
setTimeout(() => (prettifyQueryIcon.value = "align-left"), 1000)
|
||||||
}
|
}
|
||||||
@@ -475,11 +481,6 @@ const saveRequest = () => {
|
|||||||
showSaveRequestModal.value = true
|
showSaveRequestModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Why ?
|
|
||||||
const updateQuery = (updatedQuery: string) => {
|
|
||||||
gqlQueryString.value = updatedQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyVariables = () => {
|
const copyVariables = () => {
|
||||||
copyToClipboard(variableString.value)
|
copyToClipboard(variableString.value)
|
||||||
copyVariablesIcon.value = "check"
|
copyVariablesIcon.value = "check"
|
||||||
|
|||||||
@@ -18,6 +18,13 @@
|
|||||||
{{ $t("response.title") }}
|
{{ $t("response.title") }}
|
||||||
</label>
|
</label>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('state.linewrap')"
|
||||||
|
:class="{ '!text-accent': linewrapEnabled }"
|
||||||
|
svg="corner-down-left"
|
||||||
|
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
|
||||||
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
ref="downloadResponse"
|
ref="downloadResponse"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -34,21 +41,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SmartAceEditor
|
<div v-if="responseString" ref="schemaEditor" class="w-full block"></div>
|
||||||
v-if="responseString"
|
|
||||||
:value="responseString"
|
|
||||||
:lang="'json'"
|
|
||||||
:lint="false"
|
|
||||||
:options="{
|
|
||||||
maxLines: Infinity,
|
|
||||||
minLines: 16,
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
styles="border-b border-dividerLight"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="
|
class="
|
||||||
@@ -72,19 +65,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType, ref, useContext } from "@nuxtjs/composition-api"
|
import { reactive, ref, useContext } from "@nuxtjs/composition-api"
|
||||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import { gqlResponse$ } from "~/newstore/GQLSession"
|
import { gqlResponse$ } from "~/newstore/GQLSession"
|
||||||
|
|
||||||
defineProps({
|
|
||||||
conn: {
|
|
||||||
type: Object as PropType<GQLConnection>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
$toast,
|
$toast,
|
||||||
app: { i18n },
|
app: { i18n },
|
||||||
@@ -93,6 +79,23 @@ const t = i18n.t.bind(i18n)
|
|||||||
|
|
||||||
const responseString = useReadonlyStream(gqlResponse$, "")
|
const responseString = useReadonlyStream(gqlResponse$, "")
|
||||||
|
|
||||||
|
const schemaEditor = ref<any | null>(null)
|
||||||
|
const linewrapEnabled = ref(true)
|
||||||
|
|
||||||
|
useCodemirror(
|
||||||
|
schemaEditor,
|
||||||
|
responseString,
|
||||||
|
reactive({
|
||||||
|
extendedEditorConfig: {
|
||||||
|
mode: "application/ld+json",
|
||||||
|
readOnly: true,
|
||||||
|
lineWrapping: linewrapEnabled,
|
||||||
|
},
|
||||||
|
linter: null,
|
||||||
|
completer: null,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const downloadResponseIcon = ref("download")
|
const downloadResponseIcon = ref("download")
|
||||||
const copyResponseIcon = ref("copy")
|
const copyResponseIcon = ref("copy")
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,13 @@
|
|||||||
:title="$t('app.wiki')"
|
:title="$t('app.wiki')"
|
||||||
svg="help-circle"
|
svg="help-circle"
|
||||||
/>
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('state.linewrap')"
|
||||||
|
:class="{ '!text-accent': linewrapEnabled }"
|
||||||
|
svg="corner-down-left"
|
||||||
|
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
|
||||||
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
ref="downloadSchema"
|
ref="downloadSchema"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -165,20 +172,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SmartAceEditor
|
<div
|
||||||
v-if="schemaString"
|
v-if="schemaString"
|
||||||
v-model="schemaString"
|
ref="schemaEditor"
|
||||||
:lang="'graphqlschema'"
|
class="w-full block"
|
||||||
:options="{
|
></div>
|
||||||
maxLines: Infinity,
|
|
||||||
minLines: 16,
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
styles="border-b border-dividerLight"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="
|
class="
|
||||||
@@ -200,17 +198,17 @@
|
|||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
|
||||||
nextTick,
|
nextTick,
|
||||||
PropType,
|
reactive,
|
||||||
ref,
|
ref,
|
||||||
useContext,
|
useContext,
|
||||||
} from "@nuxtjs/composition-api"
|
} from "@nuxtjs/composition-api"
|
||||||
import { GraphQLField, GraphQLType } from "graphql"
|
import { GraphQLField, GraphQLType } from "graphql"
|
||||||
import { map } from "rxjs/operators"
|
import { map } from "rxjs/operators"
|
||||||
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||||
import { GQLHeader } from "~/helpers/types/HoppGQLRequest"
|
import { GQLHeader } from "~/helpers/types/HoppGQLRequest"
|
||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
@@ -285,186 +283,168 @@ type GQLHistoryEntry = {
|
|||||||
variables: string
|
variables: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
props: {
|
conn: GQLConnection
|
||||||
conn: {
|
}>()
|
||||||
type: Object as PropType<GQLConnection>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const {
|
|
||||||
$toast,
|
|
||||||
app: { i18n },
|
|
||||||
} = useContext()
|
|
||||||
const t = i18n.t.bind(i18n)
|
|
||||||
|
|
||||||
const queryFields = useReadonlyStream(
|
const {
|
||||||
props.conn.queryFields$.pipe(map((x) => x ?? [])),
|
$toast,
|
||||||
[]
|
app: { i18n },
|
||||||
)
|
} = useContext()
|
||||||
const mutationFields = useReadonlyStream(
|
const t = i18n.t.bind(i18n)
|
||||||
props.conn.mutationFields$.pipe(map((x) => x ?? [])),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const subscriptionFields = useReadonlyStream(
|
|
||||||
props.conn.subscriptionFields$.pipe(map((x) => x ?? [])),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const graphqlTypes = useReadonlyStream(
|
|
||||||
props.conn.graphqlTypes$.pipe(map((x) => x ?? [])),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const downloadSchemaIcon = ref("download")
|
const queryFields = useReadonlyStream(
|
||||||
const copySchemaIcon = ref("copy")
|
props.conn.queryFields$.pipe(map((x) => x ?? [])),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const graphqlFieldsFilterText = ref("")
|
const mutationFields = useReadonlyStream(
|
||||||
|
props.conn.mutationFields$.pipe(map((x) => x ?? [])),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const gqlTabs = ref<any | null>(null)
|
const subscriptionFields = useReadonlyStream(
|
||||||
const typesTab = ref<any | null>(null)
|
props.conn.subscriptionFields$.pipe(map((x) => x ?? [])),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const filteredQueryFields = computed(() => {
|
const graphqlTypes = useReadonlyStream(
|
||||||
return getFilteredGraphqlFields(
|
props.conn.graphqlTypes$.pipe(map((x) => x ?? [])),
|
||||||
graphqlFieldsFilterText.value,
|
[]
|
||||||
queryFields.value as any
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const filteredMutationFields = computed(() => {
|
const downloadSchemaIcon = ref("download")
|
||||||
return getFilteredGraphqlFields(
|
const copySchemaIcon = ref("copy")
|
||||||
graphqlFieldsFilterText.value,
|
|
||||||
mutationFields.value as any
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const filteredSubscriptionFields = computed(() => {
|
const graphqlFieldsFilterText = ref("")
|
||||||
return getFilteredGraphqlFields(
|
|
||||||
graphqlFieldsFilterText.value,
|
|
||||||
subscriptionFields.value as any
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const filteredGraphqlTypes = computed(() => {
|
const gqlTabs = ref<any | null>(null)
|
||||||
return getFilteredGraphqlTypes(
|
const typesTab = ref<any | null>(null)
|
||||||
graphqlFieldsFilterText.value,
|
|
||||||
graphqlTypes.value as any
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isGqlTypeHighlighted = (gqlType: GraphQLType) => {
|
const filteredQueryFields = computed(() => {
|
||||||
if (!graphqlFieldsFilterText.value) return false
|
return getFilteredGraphqlFields(
|
||||||
|
graphqlFieldsFilterText.value,
|
||||||
return isTextFoundInGraphqlFieldObject(
|
queryFields.value as any
|
||||||
graphqlFieldsFilterText.value,
|
)
|
||||||
gqlType as any
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getGqlTypeHighlightedFields = (gqlType: GraphQLType) => {
|
|
||||||
if (!graphqlFieldsFilterText.value) return []
|
|
||||||
|
|
||||||
const fields = Object.values((gqlType as any)._fields || {})
|
|
||||||
if (!fields || fields.length === 0) return []
|
|
||||||
|
|
||||||
return fields.filter((field) =>
|
|
||||||
isTextFoundInGraphqlFieldObject(
|
|
||||||
graphqlFieldsFilterText.value,
|
|
||||||
field as any
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleJumpToType = async (type: GraphQLType) => {
|
|
||||||
gqlTabs.value.selectTab(typesTab.value)
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
const rootTypeName = resolveRootType(type).name
|
|
||||||
|
|
||||||
const target = document.getElementById(`type_${rootTypeName}`)
|
|
||||||
if (target) {
|
|
||||||
gqlTabs.value.$el
|
|
||||||
.querySelector(".gqlTabs")
|
|
||||||
.scrollTo({ top: target.offsetTop, behavior: "smooth" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const schemaString = useReadonlyStream(
|
|
||||||
props.conn.schemaString$.pipe(map((x) => x ?? "")),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
const downloadSchema = () => {
|
|
||||||
const dataToWrite = JSON.stringify(schemaString.value, null, 2)
|
|
||||||
const file = new Blob([dataToWrite], { type: "application/graphql" })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
a.download = `${
|
|
||||||
url.split("/").pop()!.split("#")[0].split("?")[0]
|
|
||||||
}.graphql`
|
|
||||||
document.body.appendChild(a)
|
|
||||||
a.click()
|
|
||||||
downloadSchemaIcon.value = "check"
|
|
||||||
$toast.success(t("state.download_started").toString(), {
|
|
||||||
icon: "downloading",
|
|
||||||
})
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(a)
|
|
||||||
URL.revokeObjectURL(url)
|
|
||||||
downloadSchemaIcon.value = "download"
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const copySchema = () => {
|
|
||||||
if (!schemaString.value) return
|
|
||||||
|
|
||||||
copyToClipboard(schemaString.value)
|
|
||||||
copySchemaIcon.value = "check"
|
|
||||||
setTimeout(() => (copySchemaIcon.value = "copy"), 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleUseHistory = (entry: GQLHistoryEntry) => {
|
|
||||||
const url = entry.url
|
|
||||||
const headers = entry.headers
|
|
||||||
const gqlQueryString = entry.query
|
|
||||||
const variableString = entry.variables
|
|
||||||
const responseText = entry.response
|
|
||||||
|
|
||||||
setGQLURL(url)
|
|
||||||
setGQLHeaders(headers)
|
|
||||||
setGQLQuery(gqlQueryString)
|
|
||||||
setGQLVariables(variableString)
|
|
||||||
setGQLResponse(responseText)
|
|
||||||
props.conn.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryFields,
|
|
||||||
mutationFields,
|
|
||||||
subscriptionFields,
|
|
||||||
graphqlTypes,
|
|
||||||
schemaString,
|
|
||||||
|
|
||||||
graphqlFieldsFilterText,
|
|
||||||
|
|
||||||
filteredQueryFields,
|
|
||||||
filteredMutationFields,
|
|
||||||
filteredSubscriptionFields,
|
|
||||||
filteredGraphqlTypes,
|
|
||||||
|
|
||||||
isGqlTypeHighlighted,
|
|
||||||
getGqlTypeHighlightedFields,
|
|
||||||
|
|
||||||
gqlTabs,
|
|
||||||
typesTab,
|
|
||||||
handleJumpToType,
|
|
||||||
|
|
||||||
downloadSchema,
|
|
||||||
downloadSchemaIcon,
|
|
||||||
copySchemaIcon,
|
|
||||||
copySchema,
|
|
||||||
handleUseHistory,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const filteredMutationFields = computed(() => {
|
||||||
|
return getFilteredGraphqlFields(
|
||||||
|
graphqlFieldsFilterText.value,
|
||||||
|
mutationFields.value as any
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredSubscriptionFields = computed(() => {
|
||||||
|
return getFilteredGraphqlFields(
|
||||||
|
graphqlFieldsFilterText.value,
|
||||||
|
subscriptionFields.value as any
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredGraphqlTypes = computed(() => {
|
||||||
|
return getFilteredGraphqlTypes(
|
||||||
|
graphqlFieldsFilterText.value,
|
||||||
|
graphqlTypes.value as any
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isGqlTypeHighlighted = (gqlType: GraphQLType) => {
|
||||||
|
if (!graphqlFieldsFilterText.value) return false
|
||||||
|
|
||||||
|
return isTextFoundInGraphqlFieldObject(
|
||||||
|
graphqlFieldsFilterText.value,
|
||||||
|
gqlType as any
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGqlTypeHighlightedFields = (gqlType: GraphQLType) => {
|
||||||
|
if (!graphqlFieldsFilterText.value) return []
|
||||||
|
|
||||||
|
const fields = Object.values((gqlType as any)._fields || {})
|
||||||
|
if (!fields || fields.length === 0) return []
|
||||||
|
|
||||||
|
return fields.filter((field) =>
|
||||||
|
isTextFoundInGraphqlFieldObject(graphqlFieldsFilterText.value, field as any)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJumpToType = async (type: GraphQLType) => {
|
||||||
|
gqlTabs.value.selectTab(typesTab.value)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const rootTypeName = resolveRootType(type).name
|
||||||
|
|
||||||
|
const target = document.getElementById(`type_${rootTypeName}`)
|
||||||
|
if (target) {
|
||||||
|
gqlTabs.value.$el
|
||||||
|
.querySelector(".gqlTabs")
|
||||||
|
.scrollTo({ top: target.offsetTop, behavior: "smooth" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaString = useReadonlyStream(
|
||||||
|
props.conn.schemaString$.pipe(map((x) => x ?? "")),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
const schemaEditor = ref<any | null>(null)
|
||||||
|
const linewrapEnabled = ref(true)
|
||||||
|
|
||||||
|
useCodemirror(
|
||||||
|
schemaEditor,
|
||||||
|
schemaString,
|
||||||
|
reactive({
|
||||||
|
extendedEditorConfig: {
|
||||||
|
mode: "application/ld+json",
|
||||||
|
readOnly: true,
|
||||||
|
lineWrapping: linewrapEnabled,
|
||||||
|
},
|
||||||
|
linter: null,
|
||||||
|
completer: null,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const downloadSchema = () => {
|
||||||
|
const dataToWrite = JSON.stringify(schemaString.value, null, 2)
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/graphql" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.graphql`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
downloadSchemaIcon.value = "check"
|
||||||
|
$toast.success(t("state.download_started").toString(), {
|
||||||
|
icon: "downloading",
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
downloadSchemaIcon.value = "download"
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copySchema = () => {
|
||||||
|
if (!schemaString.value) return
|
||||||
|
|
||||||
|
copyToClipboard(schemaString.value)
|
||||||
|
copySchemaIcon.value = "check"
|
||||||
|
setTimeout(() => (copySchemaIcon.value = "copy"), 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUseHistory = (entry: GQLHistoryEntry) => {
|
||||||
|
const url = entry.url
|
||||||
|
const headers = entry.headers
|
||||||
|
const gqlQueryString = entry.query
|
||||||
|
const variableString = entry.variables
|
||||||
|
const responseText = entry.response
|
||||||
|
|
||||||
|
setGQLURL(url)
|
||||||
|
setGQLHeaders(headers)
|
||||||
|
setGQLQuery(gqlQueryString)
|
||||||
|
setGQLVariables(variableString)
|
||||||
|
setGQLResponse(responseText)
|
||||||
|
props.conn.reset()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
>
|
>
|
||||||
<Pane class="flex flex-1 hide-scrollbar !overflow-auto">
|
<Pane class="flex flex-1 hide-scrollbar !overflow-auto">
|
||||||
<main class="flex flex-1 w-full">
|
<main class="flex flex-1 w-full">
|
||||||
<nuxt class="flex flex-1" />
|
<nuxt class="flex overflow-y-auto flex-1" />
|
||||||
</main>
|
</main>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
|
|||||||
Reference in New Issue
Block a user