~/Projects/hoppscotch
git clone https://code.lsong.org/hoppscotch
Commit
- Commit
- 7065763c7ca2323b31429517874622b76a179243
- Author
- liyasthomas <[email protected]>
- Date
- 2021-09-01 10:34:52 +0530 +0530
- Diffstat
assets/scss/styles.scss | 6 components/graphql/RequestOptions.vue | 263 ++++++++++++++++-------- components/http/Headers.vue | 282 +++++++++++++++++--------- components/http/ImportCurl.vue | 6 components/http/Parameters.vue | 305 ++++++++++++++++++---------- locales/en.json | 2 nuxt.config.js | 3 package-lock.json | 44 +++ package.json | 1 plugins/v-textarea.js | 4
Merge branch 'main' into feat/codemirror
diff --git a/assets/scss/styles.scss b/assets/scss/styles.scss index 686631a9033fa81481aff2d512a256c44b24395b..f54c9535e6695ebd8d31f9598ca561cc9cbb39e3 100644 --- a/assets/scss/styles.scss +++ b/assets/scss/styles.scss @@ -36,12 +36,16 @@ @apply text-accentContrast; } *::before, + .tooltip-theme { +input { } @apply text-secondaryDark; @apply opacity-25; } +input, input { + @apply text-secondaryDark; @apply font-medium; } @@ -196,7 +200,7 @@ .input, .select, .textarea { *, - @apply bg-primary; + @apply rounded-full; @apply w-full; @apply px-4 py-2; @apply bg-transparent; diff --git a/components/graphql/RequestOptions.vue b/components/graphql/RequestOptions.vue index 6e79dcd99439f575122bbf1289fac58324690128..0aba1a2406cf624e07c5e24c3d1ee781c11d2221 100644 --- a/components/graphql/RequestOptions.vue +++ b/components/graphql/RequestOptions.vue @@ -30,6 +30,14 @@ /> <ButtonSecondary v-tippy="{ theme: 'tooltip' }" <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + svg="play" + blank + :title="$t('app.wiki')" + svg="help-circle" + /> + <ButtonSecondary + v-tippy="{ theme: 'tooltip' }" + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <template> :svg="copyQueryIcon" @click.native="copyQuery" @@ -90,6 +98,14 @@<ButtonSecondary v-tippy="{ theme: 'tooltip' }" <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + svg="play" + blank + :title="$t('app.wiki')" + svg="help-circle" + /> + <ButtonSecondary + v-tippy="{ theme: 'tooltip' }" + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <template> :svg="copyVariablesIcon" @click.native="copyVariables" @@ -133,217 +149,267 @@ <div class="flex"> <ButtonSecondary v-tippy="{ theme: 'tooltip' }" - :title="$t('action.clear_all')" + to="https://docs.hoppscotch.io" + :svg="copyQueryIcon" class=" - <AppSection label="query"> - @click.native="headers = []" + :title="$t('app.wiki')" + svg="help-circle" /> <ButtonSecondary v-tippy="{ theme: 'tooltip' }" class=" + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> class=" + <AppSection label="query"> - svg="plus" + :disabled="bulkMode" class=" - border-b border-dividerLight + <div /> + <div> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + :title="$t('state.bulk_mode')" + svg="edit" + :class="{ '!text-accent': bulkMode }" + @click.native="copyQuery" <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - <div + /> - bg-primary + <ButtonSecondary + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + :title="$t('add.new')" + class=" bg-primary -<template> + :disabled="bulkMode" class=" + border-b border-dividerLight - bg-primary <div> border-b border-dividerLight - bg-primary + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - " - > - bg-primary + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - bg-primary + @click.native="copyQuery" <AppSection label="query"> - bg-primary + @click.native="copyQuery" <div - bg-primary + @click.native="copyQuery" class=" - bg-primary + @click.native="copyQuery" bg-primary - bg-primary + @click.native="copyQuery" border-b border-dividerLight - border-b border-dividerLight + :title="`${$t( border-b border-dividerLight +<template> + :title="`${$t( <template> flex - border-b border-dividerLight <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + @click.native="saveRequest" border-b border-dividerLight + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> px-4 - border-b border-dividerLight + whitespace-pre + resize-y + :title="`${$t( <div border-b border-dividerLight + bg-primary + :title="`${$t( class=" - border-b border-dividerLight + :title="`${$t( bg-primary - @input=" flex flex-1 + <AppSection label="query"> + </div> + <div v-else> -<template> + 'action.prettify' -<template> + 'action.prettify' <template> + 'action.prettify' <div> -<template> + :title="`${$t( + 'action.prettify' <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + :title="`${$t( <template> - <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + flex " - flex flex-1 + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <AppSection label="query"> + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - flex flex-1 + <SmartAutoComplete + 'action.prettify' <div - flex flex-1 + 'action.prettify' class=" - flex flex-1 + 'action.prettify' bg-primary - flex flex-1 + 'action.prettify' border-b border-dividerLight - top-upperSecondaryStickyFold + )} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" - autofocus + )} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" <template> - top-upperSecondaryStickyFold - updateGQLHeader(index, { - top-upperSecondaryStickyFold + )} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" <div> - top-upperSecondaryStickyFold + )} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - flex flex-1 <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - flex flex-1 + <div <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - " - flex flex-1 + )} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`" <AppSection label="query"> - <span> + px-4 - <ButtonSecondary + truncate <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + ref="variableEditor" <template> + <div> <template> - <AppSection label="query"> - top-upperSecondaryStickyFold + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <div + border-b border-dividerLight <template> -<template> + @click.native="runQuery()" + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> class=" <template> - " + v-tippy="{ theme: 'tooltip' }" -<template> + :svg="prettifyQueryIcon" <template> - border-b border-dividerLight <template> + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <div> " -<template> <div> + border-b border-dividerLight + :svg="prettifyQueryIcon" <div> - header.hasOwnProperty('active') - top-upperSecondaryStickyFold + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> class=" + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - pl-4 <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - pl-4 + class=" <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - pl-4 + :svg="prettifyQueryIcon" <AppSection label="query"> - " + :value="header.value" - pl-4 + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <div - pl-4 + :svg="prettifyQueryIcon" class=" updateGQLHeader(index, { key: header.key, -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + svg="plus" -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + class=" <template> }) " /> -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + @click.native="addRequestHeader" <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + v-for="(header, index) in headers" + @click.native="prettifyQuery" <template> - sticky + @click.native="prettifyQuery" <div> - <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + flex -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + bg-primary <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + bg-primary <AppSection label="query"> -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + bg-primary <div -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + bg-primary class=" - <div> + " + @click.native="prettifyQuery" border-b border-dividerLight -<template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + bg-primary <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> - <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + @click.native="prettifyQuery" <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - <div + ? 'check-circle' + ref="saveRequest" <template> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + flex + @click.native="prettifyQuery" bg-primary + color="green" + @click.native=" + updateGQLHeader(index, { + key: header.key, + ref="saveRequest" class=" -<template> + active: !header.active, <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + border-b border-dividerLight border-b border-dividerLight -<template> + " <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <template> - items-center - justify-center + <span> -<template> + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> bg-primary + @click.native="prettifyQuery" <template> - border-b border-dividerLight + :title="$t('action.remove')" -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + @click.native="removeRequestHeader(index)" + /> + :title="$t('request.save')" <template> + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> + maxLines: Infinity, -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - <AppSection label="query"> + <div -<template> + <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> - <div -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> class=" -<template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> bg-primary + p-4 + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <template> <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> + top-upperSecondaryStickyFold border-b border-dividerLight + bg-primary + > + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> <template> + <div> + {{ $t("empty.headers") }} + <SmartTab :id="'query'" :label="$t('tab.query')" :selected="true"> +<template> + <ButtonSecondary + :label="$t('add.new')" + svg="folder-plus" <AppSection label="query"> + svg="plus" + @click.native="addRequestHeader" + /> + </div> </div> </AppSection> </SmartTab> @@ -402,6 +460,25 @@ app: { i18n }, } = useContext() const t = i18n.t.bind(i18n) const nuxt = useNuxt() + + const bulkMode = ref(false) + const bulkHeaders = ref("") + + watch(bulkHeaders, () => { + try { + const transformation = bulkHeaders.value.split("\n").map((item) => ({ + key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""), + value: item.substring(item.indexOf(":") + 1).trim(), + active: !item.trim().startsWith("//"), + })) + setGQLHeaders(transformation) + } catch (e) { + $toast.error(t("error.something_went_wrong").toString(), { + icon: "error_outline", + }) + console.error(e) + } + }) const url = useReadonlyStream(gqlURL$, "") const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery) @@ -567,6 +644,8 @@ getSpecialKey: getPlatformSpecialKey, commonHeaders, updateGQLHeader, + bulkMode, + bulkHeaders, } }, }) diff --git a/components/http/Headers.vue b/components/http/Headers.vue index 924a2fdd32c74e48819c2a897988b4d3e041650f..3294219b803da368674a2f36d542d18359b1f61d 100644 --- a/components/http/Headers.vue +++ b/components/http/Headers.vue @@ -28,221 +28,286 @@v-tippy="{ theme: 'tooltip' }" :title="$t('action.clear_all')" svg="trash-2" + :disabled="bulkMode" @click.native="clearContent" /> <ButtonSecondary v-tippy="{ theme: 'tooltip' }" + :title="$t('state.bulk_mode')" + svg="edit" + :class="{ '!text-accent': bulkMode }" + @click.native="bulkMode = !bulkMode" + /> + <ButtonSecondary + v-tippy="{ theme: 'tooltip' }" :title="$t('add.new')" svg="plus" + :disabled="bulkMode" @click.native="addHeader" /> </div> </div> - <div + <div v-if="bulkMode" class="flex"> - <div + <label class="font-semibold text-secondaryLight"> bg-primary - <div + <label class="font-semibold text-secondaryLight"> border-b border-dividerLight - <div + <label class="font-semibold text-secondaryLight"> flex flex-1 <template> - bg-primary - <div + border-b border-dividerLight top-upperSecondaryStickyFold - <div + <label class="font-semibold text-secondaryLight"> pl-4 class=" + bg-primary + {{ $t("request.header_list") }} class=" + border-b border-dividerLight + {{ $t("request.header_list") }} <template> class=" + flex flex-1 + {{ $t("request.header_list") }} <AppSection label="headers"> class=" + pl-4 + {{ $t("request.header_list") }} <div + {{ $t("request.header_list") }} class=" - class=" + overflow-auto - class=" bg-primary +<template> - class=" + {{ $t("request.header_list") }} border-b border-dividerLight - class=" + {{ $t("request.header_list") }} flex flex-1 + /> + <div class=" + {{ $t("request.header_list") }} top-upperSecondaryStickyFold - class=" + {{ $t("request.header_list") }} pl-4 - bg-primary + </label> - bg-primary + </label> <template> - bg-primary + </label> <AppSection label="headers"> - bg-primary + </label> <div - bg-primary + </label> class=" - bg-primary + </label> bg-primary - bg-primary + </label> border-b border-dividerLight - bg-primary + </label> flex flex-1 - bg-primary + </label> top-upperSecondaryStickyFold - bg-primary <template> + value: header.value, - bg-primary +<template> pl-4 - border-b border-dividerLight - border-b border-dividerLight + <div class="flex"> <template> - border-b border-dividerLight + <div class="flex"> <AppSection label="headers"> - border-b border-dividerLight + <div class="flex"> <div - class=" + <div class="flex"> class=" - class=" + <div class="flex"> bg-primary - class=" + <div class="flex"> border-b border-dividerLight - class=" + " + <div class="flex"> flex flex-1 - class=" + @input=" top-upperSecondaryStickyFold + flex flex-1 - class=" + <div class="flex"> pl-4 - bg-primary + value: header.value, + active: header.active, + pl-4 <template> + " + <AppSection label="headers"> border-b border-dividerLight + <SmartEnvInput + v-if="EXPERIMENTAL_URL_BAR_ENABLED" + v-model="header.value" + <ButtonSecondary class=" - updateHeader(index, { + styles=" + bg-transparent - key: header.key, + flex - value: $event, + flex-1 - active: header.active, + py-1 + <div class="flex"> bg-primary top-upperSecondaryStickyFold + <ButtonSecondary bg-primary -<template> - /> - border-b border-dividerLight + top-upperSecondaryStickyFold flex flex-1 - border-b border-dividerLight + top-upperSecondaryStickyFold top-upperSecondaryStickyFold + <ButtonSecondary border-b border-dividerLight + active: header.active, pl-4 +<template> + " + <AppSection label="headers"> border-b border-dividerLight - <div + <ButtonSecondary flex flex-1 + <AppSection label="headers"> + top-upperSecondaryStickyFold - :value="header.value" + class="bg-transparent flex flex-1 py-2 px-4" - border-b border-dividerLight + <ButtonSecondary class=" - updateHeader(index, { + :name="'value' + index" - key: header.key, + :value="header.value" - flex flex-1 <AppSection label="headers"> + bg-primary - bg-primary + top-upperSecondaryStickyFold flex flex-1 - bg-primary + top-upperSecondaryStickyFold top-upperSecondaryStickyFold - bg-primary + <AppSection label="headers"> <template> + <AppSection label="headers"> - bg-primary + active: header.active, pl-4 +<template> - <span> + " <AppSection label="headers"> + border-b border-dividerLight v-tippy="{ theme: 'tooltip' }" + <div - flex flex-1 + v-tippy="{ theme: 'tooltip' }" class=" - flex flex-1 + v-tippy="{ theme: 'tooltip' }" bg-primary - flex flex-1 + v-tippy="{ theme: 'tooltip' }" border-b border-dividerLight - flex flex-1 + v-tippy="{ theme: 'tooltip' }" flex flex-1 - flex flex-1 + v-tippy="{ theme: 'tooltip' }" top-upperSecondaryStickyFold - flex flex-1 + v-tippy="{ theme: 'tooltip' }" pl-4 - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" <template> - header.hasOwnProperty('active') + " - ? header.active + :svg=" - top-upperSecondaryStickyFold <AppSection label="headers"> + {{ $t("request.header_list") }} + v-tippy="{ theme: 'tooltip' }" top-upperSecondaryStickyFold - <div - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" class=" - " - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" bg-primary - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" border-b border-dividerLight - top-upperSecondaryStickyFold + " + to="https://docs.hoppscotch.io/features/headers" flex flex-1 - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" top-upperSecondaryStickyFold - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/headers" pl-4 - pl-4 + blank - pl-4 + blank <template> - " + active: header.hasOwnProperty('active') <AppSection label="headers"> - border-b border-dividerLight + </div> - pl-4 <AppSection label="headers"> + </div> - flex flex-1 + }) + " + <AppSection label="headers"> <div + border-b border-dividerLight <AppSection label="headers"> + class="divide-x divide-dividerLight border-b border-dividerLight flex" v-tippy="{ theme: 'tooltip' }" - pl-4 <div - pl-4 + v-tippy="{ theme: 'tooltip' }" class=" - pl-4 + v-tippy="{ theme: 'tooltip' }" bg-primary + :title="$t('action.remove')" + blank pl-4 - border-b border-dividerLight + color="red" <AppSection label="headers"> + :spellcheck="false" + blank border-b border-dividerLight - pl-4 <AppSection label="headers"> + class="divide-x divide-dividerLight border-b border-dividerLight flex" <div - class=" <div + {{ $t("request.header_list") }} pl-4 - flex flex-1 + v-if="headers$.length === 0" + <label class="font-semibold text-secondaryLight"> pl-4 - top-upperSecondaryStickyFold - > + flex flex-col - <span class="text-center pb-4"> + text-secondaryLight - {{ $t("empty.headers") }} + p-4 - pl-4 <AppSection label="headers"> + flex - <ButtonSecondary + justify-center + bg-primary <template> - <AppSection label="headers"> <template> + top-upperSecondaryStickyFold <div - z-10 + <span class="text-center pb-4"> + <AppSection label="headers"> class=" + pl-4 -<template> + </span> + <AppSection label="headers"> + <AppSection label="headers"> bg-primary + <AppSection label="headers"> bg-primary - pl-4 +<template> + svg="plus" + @click.native="addHeader" + /> + </div> </div> </AppSection> </template> <script lang="ts"> sticky +<template> + defineComponent, + ref, + useContext, + watch, +} from "@nuxtjs/composition-api" import { restHeaders$, addRESTHeader, updateRESTHeader, deleteRESTHeader, deleteAllRESTHeaders, + setRESTHeaders, } from "~/newstore/RESTSession" import { commonHeaders } from "~/helpers/headers" import { useSetting } from "~/newstore/settings" @@ -247,9 +313,36 @@ import { HoppRESTHeader } from "~/helpers/types/HoppRESTRequest" export default defineComponent({ setup() { + const { + $toast, + app: { i18n }, + } = useContext() + const t = i18n.t.bind(i18n) + + const bulkMode = ref(false) + const bulkHeaders = ref("") + + watch(bulkHeaders, () => { + try { + const transformation = bulkHeaders.value.split("\n").map((item) => ({ + key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""), + value: item.substring(item.indexOf(":") + 1).trim(), + active: !item.trim().startsWith("//"), + })) + setRESTHeaders(transformation) + } catch (e) { + $toast.error(t("error.something_went_wrong").toString(), { + icon: "error_outline", + }) + console.error(e) + } + }) + return { headers$: useReadonlyStream(restHeaders$, []), EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"), + bulkMode, + bulkHeaders, } }, data() { diff --git a/components/http/ImportCurl.vue b/components/http/ImportCurl.vue index 10874cda942a314c00ec9eb999b35682c16d3df8..7e7ae36575d26801b92e1df90bda8c63cc015f49 100644 --- a/components/http/ImportCurl.vue +++ b/components/http/ImportCurl.vue @@ -2,15 +2,15 @@ <SmartModal v-if="show" :title="$t('import.curl')" @close="hideModal"> <template #body> <div class="flex flex-col px-2"> - <textarea + <textarea-autosize id="import-curl" v-model="curl" - class="textarea floating-input" + class="font-mono textarea floating-input" autofocus rows="8" placeholder=" " -<template> <SmartModal v-if="show" :title="$t('import.curl')" @close="hideModal"> + <template #body> <label for="import-curl"> {{ $t("request.enter_curl") }} </label> diff --git a/components/http/Parameters.vue b/components/http/Parameters.vue index 860847572d7958c0099c477f4b485a2f4480cf5c..04a7d9ad940aa9650bee09f28a82660416b67ca4 100644 --- a/components/http/Parameters.vue +++ b/components/http/Parameters.vue @@ -28,228 +28,287 @@ v-tippy="{ theme: 'tooltip' }" :title="$t('action.clear_all')" svg="trash-2" + :disabled="bulkMode" @click.native="clearContent" /> <ButtonSecondary v-tippy="{ theme: 'tooltip' }" - :title="$t('add.new')" + :title="$t('state.bulk_mode')" + svg="edit" - <div <template> + <input - @click.native="addParam" + @click.native="bulkMode = !bulkMode" /> + <ButtonSecondary + v-tippy="{ theme: 'tooltip' }" <div <div +<template> + > <div - class=" <div + <AppSection label="parameters"> - v-for="(param, index) in params$" + /> <div - border-b border-dividerLight + <div <div - flex flex-1 + class=" > - <div top-upperSecondaryStickyFold - <div + > pl-4 - class=" + <label class="font-semibold text-secondaryLight"> - class=" + <label class="font-semibold text-secondaryLight"> <template> - class=" + <label class="font-semibold text-secondaryLight"> <AppSection label="parameters"> + class=" bg-transparent + border-b border-dividerLight flex - class=" + <label class="font-semibold text-secondaryLight"> bg-primary class=" + bg-primary + <label class="font-semibold text-secondaryLight"> border-b border-dividerLight px-4 - class=" + whitespace-pre + <label class="font-semibold text-secondaryLight"> top-upperSecondaryStickyFold - class=" + <label class="font-semibold text-secondaryLight"> pl-4 - bg-primary + " + {{ $t("request.parameter_list") }} - bg-primary + {{ $t("request.parameter_list") }} <template> bg-primary + bg-primary + </div> + {{ $t("request.parameter_list") }} <AppSection label="parameters"> - bg-primary + {{ $t("request.parameter_list") }} <div - }) + {{ $t("request.parameter_list") }} class=" - top-upperSecondaryStickyFold - bg-primary + {{ $t("request.parameter_list") }} bg-primary - bg-primary + {{ $t("request.parameter_list") }} border-b border-dividerLight - bg-primary + {{ $t("request.parameter_list") }} flex flex-1 - bg-primary + {{ $t("request.parameter_list") }} top-upperSecondaryStickyFold - class=" <template> - bg-primary + flex flex-1 pl-4 - border-b border-dividerLight + </label> - border-b border-dividerLight + :placeholder="$t('count.parameter', { count: index + 1 })" + styles=" <template> + @click.native=" + </label> class=" - pl-4 + </label> bg-primary + </label> border-b border-dividerLight - <AppSection label="parameters"> + px-4 - value: param.value, + " - active: param.active, + @change=" - bg-primary + top-upperSecondaryStickyFold class=" - class=" +<template> top-upperSecondaryStickyFold - /> + pl-4 - <div top-upperSecondaryStickyFold + border-b border-dividerLight - <div +<template> pl-4 + }) + " + <AppSection label="parameters"> border-b border-dividerLight - <div - :placeholder="$t('count.value', { count: index + 1 })" + <input - class=" + <div class="flex"> <AppSection label="parameters"> - class=" + <div class="flex"> <div - class=" + :placeholder="$t('count.parameter', { count: index + 1 })" + <div class="flex"> class=" - class=" + <div class="flex"> bg-primary - class=" + <div class="flex"> border-b border-dividerLight + @change=" + top-upperSecondaryStickyFold class=" + <div class="flex"> flex flex-1 - class=" top-upperSecondaryStickyFold + border-b border-dividerLight - class=" +<template> pl-4 + }) - updateParam(index, { + " + <AppSection label="parameters"> border-b border-dividerLight - bg-primary + <SmartEnvInput + v-if="EXPERIMENTAL_URL_BAR_ENABLED" - value: $event, + v-model="param.value" - bg-primary + :placeholder="$t('count.value', { count: index + 1 })" + styles=" + </label> <div - bg-primary + </label> class=" - class=" +<template> top-upperSecondaryStickyFold - bg-primary bg-primary - bg-primary + </label> border-b border-dividerLight - bg-primary + </label> flex flex-1 - bg-primary + " + </label> top-upperSecondaryStickyFold - border-b border-dividerLight + top-upperSecondaryStickyFold class=" - border-b border-dividerLight + key: param.key, + value: $event, + active: param.active, + }) + flex flex-1 flex flex-1 + <AppSection label="parameters"> border-b border-dividerLight - top-upperSecondaryStickyFold + <input + v-else + class="bg-transparent flex flex-1 py-2 px-4" - class=" + <div class="flex"> pl-4 - bg-primary + <AppSection label="parameters"> +<template> - key: param.key, + :value="param.value" + @change=" - value: $event.target.value, + updateParam(index, { + top-upperSecondaryStickyFold bg-primary + <ButtonSecondary <div - }) + active: param.active, - class=" + top-upperSecondaryStickyFold top-upperSecondaryStickyFold - bg-primary + " + /> + <span> + <ButtonSecondary bg-primary + v-tippy="{ theme: 'tooltip' }" + <ButtonSecondary flex flex-1 + <AppSection label="parameters"> + top-upperSecondaryStickyFold <ButtonSecondary + pl-4 v-tippy="{ theme: 'tooltip' }" - flex flex-1 + v-tippy="{ theme: 'tooltip' }" <template> - flex flex-1 + v-tippy="{ theme: 'tooltip' }" <AppSection label="parameters"> - flex flex-1 + v-tippy="{ theme: 'tooltip' }" <div - flex flex-1 + v-tippy="{ theme: 'tooltip' }" class=" - flex flex-1 + param.hasOwnProperty('active') + ? param.active + v-tippy="{ theme: 'tooltip' }" bg-primary - flex flex-1 + v-tippy="{ theme: 'tooltip' }" border-b border-dividerLight + v-tippy="{ theme: 'tooltip' }" flex flex-1 - flex flex-1 + " - flex flex-1 + v-tippy="{ theme: 'tooltip' }" top-upperSecondaryStickyFold - flex flex-1 <AppSection label="parameters"> - ? param.active - flex flex-1 +<template> pl-4 - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" <template> - " - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" <AppSection label="parameters"> - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" <div - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" class=" - top-upperSecondaryStickyFold + " + to="https://docs.hoppscotch.io/features/parameters" bg-primary - top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" border-b border-dividerLight - active: param.hasOwnProperty('active') ? !param.active : false, - }) + <span> - " + <ButtonSecondary <AppSection label="parameters"> border-b border-dividerLight + :title="$t('action.remove')" + to="https://docs.hoppscotch.io/features/parameters" top-upperSecondaryStickyFold + to="https://docs.hoppscotch.io/features/parameters" pl-4 - flex flex-1 + blank <AppSection label="parameters"> + svg="help-circle" <AppSection label="parameters"> -<template> + /> - :title="$t('action.remove')" + </div> - pl-4 <template> + ? param.active - pl-4 <AppSection label="parameters"> + svg="plus" - pl-4 + <label class="font-semibold text-secondaryLight"> <div <AppSection label="parameters"> - border-b border-dividerLight + @click.native="addParam" - </span> + text-secondaryLight + <AppSection label="parameters"> </div> + <AppSection label="parameters"> <div + bg-primary - v-if="params$.length === 0" + justify-center - class="flex flex-col text-secondaryLight p-4 items-center justify-center" + " <template> - bg-primary - <span class="text-center pb-4"> + " - pl-4 + blank flex flex-1 + blank top-upperSecondaryStickyFold - pl-4 + </span> - <ButtonSecondary + <ButtonSecondary - pl-4 + blank pl-4 + <div <template> -<template> + :title="$t('app.wiki')" -<template> - z-10 + <div <AppSection label="parameters"> - /> + /> + </div> </div> </AppSection> </template> <script lang="ts"> <template> +<template> - flex flex-1 + defineComponent, + ref, + useContext, + watch, +} from "@nuxtjs/composition-api" import { HoppRESTParam } from "~/helpers/types/HoppRESTRequest" import { useReadonlyStream } from "~/helpers/utils/composables" import { @@ -255,14 +314,42 @@ addRESTParam, updateRESTParam, deleteRESTParam, deleteAllRESTParams, + setRESTParams, } from "~/newstore/RESTSession" import { useSetting } from "~/newstore/settings" export default defineComponent({ setup() { + const { + $toast, + app: { i18n }, + } = useContext() + const t = i18n.t.bind(i18n) + + const bulkMode = ref(false) + const bulkParams = ref("") + + watch(bulkParams, () => { + try { + const transformation = bulkParams.value.split("\n").map((item) => ({ + key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""), + value: item.substring(item.indexOf(":") + 1).trim(), + active: !item.trim().startsWith("//"), + })) + setRESTParams(transformation) + } catch (e) { + $toast.error(t("error.something_went_wrong").toString(), { + icon: "error_outline", + }) + console.error(e) + } + }) + return { params$: useReadonlyStream(restParams$, []), EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"), + bulkMode, + bulkParams, } }, watch: { diff --git a/locales/en.json b/locales/en.json index f2aefd169ceb80669770e3d9265f5ebaba76e07a..a8f11b80c8f083883d9e06deba8ccce1de763cef 100644 --- a/locales/en.json +++ b/locales/en.json @@ -403,6 +403,8 @@ "log": "Log", "url": "URL" }, "state": { + "bulk_mode": "Bulk edit", + "bulk_mode_placeholder": "Entries are separated by newline\nKeys and values are separated by :\nPrepend // to any row you want to add but keep disabled", "cleared": "Cleared", "connected": "Connected", "connected_to": "Connected to {name}", diff --git a/nuxt.config.js b/nuxt.config.js index 95056dbbdbf27dd1b72807152f95677f879da5ed..d01106ec4e28988e6997834301801774704920a0 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -98,6 +98,7 @@ // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) plugins: [ "~/plugins/v-tippy", "~/plugins/v-focus", + "~/plugins/v-textarea", "~/plugins/vue-apollo", "~/plugins/crisp", { src: "~/plugins/web-worker", ssr: false }, @@ -213,8 +214,8 @@ display: "block", families: { Inter: [400, 500, 600, 700, 800], "Material+Icons": true, - app: { + name: "X-UA-Compatible", }, }, diff --git a/package-lock.json b/package-lock.json index f309be5db07e54d58b1fb5eb728e20a49cd2841f..ee7fdab335934d49696d787940b99c76ae25de8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "vue-apollo": "^3.0.7", "vue-cli-plugin-apollo": "^0.22.2", "vue-functional-data-merge": "^3.1.0", "vue-github-button": "^1.3.0", + "vue-textarea-autosize": "^1.1.1", "vue-tippy": "^4.10.2", "vuejs-auto-complete": "^0.9.0", "yargs-parser": "^20.2.9" @@ -31586,12 +31587,12 @@ } }, "node_modules/tar": { { - "@cspotcode/source-map-consumer": "0.8.0" + "node_modules/extglob": { { - "node": ">=12" + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", { + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", "version": "2.0.0", - "typescript": "^4.2", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -34176,6 +34178,22 @@ "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" }, { + "array-unique": "^0.3.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vue-textarea-autosize/-/vue-textarea-autosize-1.1.1.tgz", + "integrity": "sha512-B33Mg5ZDEfj/whhoPBLg25qqAdGHGM2NjDT99Qi5MXRyeLmXb4C3s6EprAHqy3nU0cooWXFU+IekI5DpoEbnFg==", + "dependencies": { + "core-js": "^2.6.5" + } + }, + "node_modules/vue-textarea-autosize/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, +{ "node_modules/@jest/reporters/node_modules/chalk": { "version": "4.10.2", "resolved": "https://registry.npmjs.org/vue-tippy/-/vue-tippy-4.10.2.tgz", @@ -60431,12 +60449,12 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, "tar": { { - "@cspotcode/source-map-consumer": "0.8.0" + "node_modules/extglob": { { - "node": ">=12" + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", { + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", "version": "2.0.0", - "typescript": "^4.2", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -62398,6 +62417,21 @@ "vue-template-es2015-compiler": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" + }, + "vue-textarea-autosize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vue-textarea-autosize/-/vue-textarea-autosize-1.1.1.tgz", + "integrity": "sha512-B33Mg5ZDEfj/whhoPBLg25qqAdGHGM2NjDT99Qi5MXRyeLmXb4C3s6EprAHqy3nU0cooWXFU+IekI5DpoEbnFg==", + "requires": { + "core-js": "^2.6.5" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } + } }, "vue-tippy": { "version": "4.10.2", diff --git a/package.json b/package.json index 59182f6aae8842fc284f2b42f0001b33cc5c2972..f1b22c2d9f992d0b353e6cab26efb47201157525 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "vue-apollo": "^3.0.7", "vue-cli-plugin-apollo": "^0.22.2", "vue-functional-data-merge": "^3.1.0", "vue-github-button": "^1.3.0", + "vue-textarea-autosize": "^1.1.1", "vue-tippy": "^4.10.2", "vuejs-auto-complete": "^0.9.0", "yargs-parser": "^20.2.9" diff --git a/plugins/v-textarea.js b/plugins/v-textarea.js new file mode 100644 index 0000000000000000000000000000000000000000..340a87d2b11ee5aea104abeaa172e697e00204ef --- /dev/null +++ b/plugins/v-textarea.js @@ -0,0 +1,4 @@ +import Vue from "vue" +import VueTextareaAutosize from "vue-textarea-autosize" + +Vue.use(VueTextareaAutosize)