refactor: use pane layout component in pages

This commit is contained in:
liyasthomas
2022-03-02 07:57:05 +05:30
parent 6e59ae6424
commit 4b6581934e
9 changed files with 583 additions and 779 deletions

View File

@@ -0,0 +1,55 @@
<template>
<Splitpanes
class="smart-splitter"
:rtl="SIDEBAR_ON_LEFT && mdAndLarger"
:class="{
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger,
}"
:horizontal="!mdAndLarger"
>
<Pane
size="75"
min-size="65"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
<Pane
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<slot name="primary" />
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="flex flex-col hide-scrollbar !overflow-auto"
>
<slot name="secondary" />
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<slot name="sidebar" />
</Pane>
</Splitpanes>
</template>
<script setup lang="ts">
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { useSetting } from "~/newstore/settings"
const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT")
const SIDEBAR = useSetting("SIDEBAR")
</script>

View File

@@ -1,84 +1,56 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <div
:class="{ class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, >
}" <div class="inline-flex flex-1 space-x-2">
:horizontal="!mdAndLarger" <input
> id="mqtt-url"
<Pane v-model="url"
size="75" type="url"
min-size="65" autocomplete="off"
class="hide-scrollbar !overflow-auto flex flex-col" spellcheck="false"
> class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark"
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT"> :placeholder="$t('mqtt.url')"
<Pane :disabled="connectionState"
:size="COLUMN_LAYOUT ? 45 : 50" @keyup.enter="validUrl ? toggleConnection() : null"
class="hide-scrollbar !overflow-auto flex flex-col" />
> <ButtonPrimary
<div id="connect"
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar" :disabled="!validUrl"
> class="w-32"
<div class="inline-flex flex-1 space-x-2"> :label="
<input connectionState ? $t('action.disconnect') : $t('action.connect')
id="mqtt-url" "
v-model="url" :loading="connectingState"
type="url" @click.native="toggleConnection"
autocomplete="off" />
spellcheck="false" </div>
class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark" <div class="flex space-x-4">
:placeholder="$t('mqtt.url')" <input
:disabled="connectionState" id="mqtt-username"
@keyup.enter="validUrl ? toggleConnection() : null" v-model="username"
/> type="text"
<ButtonPrimary spellcheck="false"
id="connect" class="input"
:disabled="!validUrl" :placeholder="$t('authorization.username')"
class="w-32" />
:label=" <input
connectionState id="mqtt-password"
? $t('action.disconnect') v-model="password"
: $t('action.connect') type="password"
" spellcheck="false"
:loading="connectingState" class="input"
@click.native="toggleConnection" :placeholder="$t('authorization.password')"
/> />
</div> </div>
<div class="flex space-x-4"> </div>
<input </template>
id="mqtt-username" <template #secondary>
v-model="username" <RealtimeLog :title="$t('mqtt.log')" :log="log" />
type="text" </template>
spellcheck="false" <template #sidebar>
class="input"
:placeholder="$t('authorization.username')"
/>
<input
id="mqtt-password"
v-model="password"
type="password"
spellcheck="false"
class="input"
:placeholder="$t('authorization.password')"
/>
</div>
</div>
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<RealtimeLog :title="$t('mqtt.log')" :log="log" />
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<div class="flex items-center justify-between p-4"> <div class="flex items-center justify-between p-4">
<label for="pub_topic" class="font-semibold text-secondaryLight"> <label for="pub_topic" class="font-semibold text-secondaryLight">
{{ $t("mqtt.topic") }} {{ $t("mqtt.topic") }}
@@ -146,19 +118,15 @@
@click.native="toggleSubscription" @click.native="toggleSubscription"
/> />
</div> </div>
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script> <script>
import { defineComponent } from "@nuxtjs/composition-api" import { defineComponent } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import Paho from "paho-mqtt" import Paho from "paho-mqtt"
import debounce from "lodash/debounce" import debounce from "lodash/debounce"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { useSetting } from "~/newstore/settings"
import { import {
MQTTEndpoint$, MQTTEndpoint$,
setMQTTEndpoint, setMQTTEndpoint,
@@ -177,16 +145,8 @@ import {
import { useStream } from "~/helpers/utils/composables" import { useStream } from "~/helpers/utils/composables"
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
url: useStream(MQTTEndpoint$, "", setMQTTEndpoint), url: useStream(MQTTEndpoint$, "", setMQTTEndpoint),
connectionState: useStream( connectionState: useStream(
MQTTConnectionState$, MQTTConnectionState$,

View File

@@ -1,103 +1,14 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <div
:class="{ class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, >
}" <div class="inline-flex flex-1 space-x-2">
:horizontal="!mdAndLarger" <div class="flex flex-1">
> <label for="client-version">
<Pane
size="75"
min-size="65"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
<Pane
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<div
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
>
<div class="inline-flex flex-1 space-x-2">
<div class="flex flex-1">
<label for="client-version">
<tippy
ref="versionOptions"
interactive
trigger="click"
theme="popover"
arrow
>
<template #trigger>
<span class="select-wrapper">
<input
id="client-version"
v-tippy="{ theme: 'tooltip' }"
title="socket.io-client version"
class="flex px-4 py-2 font-semibold border rounded-l cursor-pointer bg-primaryLight border-divider text-secondaryDark w-26"
:value="`Client ${clientVersion}`"
readonly
:disabled="connectionState"
/>
</span>
</template>
<div class="flex flex-col" role="menu">
<SmartItem
v-for="(_, version) in socketIoClients"
:key="`client-${version}`"
:label="`Client ${version}`"
@click.native="onSelectVersion(version)"
/>
</div>
</tippy>
</label>
<input
id="socketio-url"
v-model="url"
type="url"
autocomplete="off"
spellcheck="false"
:class="{ error: !urlValid }"
class="flex flex-1 w-full px-4 py-2 border bg-primaryLight border-divider text-secondaryDark"
:placeholder="$t('socketio.url')"
:disabled="connectionState"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
<input
id="socketio-path"
v-model="path"
class="flex flex-1 w-full px-4 py-2 border rounded-r bg-primaryLight border-divider text-secondaryDark"
spellcheck="false"
:disabled="connectionState"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
</div>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
name="connect"
class="w-32"
:label="
!connectionState
? $t('action.connect')
: $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/>
</div>
</div>
<div
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold"
>
<span class="flex items-center">
<label class="font-semibold text-secondaryLight">
{{ $t("authorization.type") }}
</label>
<tippy <tippy
ref="authTypeOptions" ref="versionOptions"
interactive interactive
trigger="click" trigger="click"
theme="popover" theme="popover"
@@ -105,132 +16,193 @@
> >
<template #trigger> <template #trigger>
<span class="select-wrapper"> <span class="select-wrapper">
<ButtonSecondary <input
class="pr-8 ml-2 rounded-none" id="client-version"
:label="authType" v-tippy="{ theme: 'tooltip' }"
title="socket.io-client version"
class="flex px-4 py-2 font-semibold border rounded-l cursor-pointer bg-primaryLight border-divider text-secondaryDark w-26"
:value="`Client ${clientVersion}`"
readonly
:disabled="connectionState"
/> />
</span> </span>
</template> </template>
<div class="flex flex-col" role="menu"> <div class="flex flex-col" role="menu">
<SmartItem <SmartItem
label="None" v-for="(_, version) in socketIoClients"
:icon=" :key="`client-${version}`"
authType === 'None' :label="`Client ${version}`"
? 'radio_button_checked' @click.native="onSelectVersion(version)"
: 'radio_button_unchecked'
"
:active="authType === 'None'"
@click.native="
() => {
authType = 'None'
authTypeOptions.tippy().hide()
}
"
/>
<SmartItem
label="Bearer Token"
:icon="
authType === 'Bearer'
? 'radio_button_checked'
: 'radio_button_unchecked'
"
:active="authType === 'Bearer'"
@click.native="
() => {
authType = 'Bearer'
authTypeOptions.tippy().hide()
}
"
/> />
</div> </div>
</tippy> </tippy>
</span> </label>
<div class="flex"> <input
<SmartCheckbox id="socketio-url"
:on="authActive" v-model="url"
class="px-2" type="url"
@change="authActive = !authActive" autocomplete="off"
> spellcheck="false"
{{ $t("state.enabled") }} :class="{ error: !urlValid }"
</SmartCheckbox> class="flex flex-1 w-full px-4 py-2 border bg-primaryLight border-divider text-secondaryDark"
<ButtonSecondary :placeholder="$t('socketio.url')"
v-tippy="{ theme: 'tooltip' }" :disabled="connectionState"
to="https://docs.hoppscotch.io/features/authorization" @keyup.enter="urlValid ? toggleConnection() : null"
blank />
:title="$t('app.wiki')" <input
svg="help-circle" id="socketio-path"
v-model="path"
class="flex flex-1 w-full px-4 py-2 border rounded-r bg-primaryLight border-divider text-secondaryDark"
spellcheck="false"
:disabled="connectionState"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
</div>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
name="connect"
class="w-32"
:label="
!connectionState ? $t('action.connect') : $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/>
</div>
</div>
<div
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold"
>
<span class="flex items-center">
<label class="font-semibold text-secondaryLight">
{{ $t("authorization.type") }}
</label>
<tippy
ref="authTypeOptions"
interactive
trigger="click"
theme="popover"
arrow
>
<template #trigger>
<span class="select-wrapper">
<ButtonSecondary
class="pr-8 ml-2 rounded-none"
:label="authType"
/>
</span>
</template>
<div class="flex flex-col" role="menu">
<SmartItem
label="None"
:icon="
authType === 'None'
? 'radio_button_checked'
: 'radio_button_unchecked'
"
:active="authType === 'None'"
@click.native="
() => {
authType = 'None'
authTypeOptions.tippy().hide()
}
"
/> />
<ButtonSecondary <SmartItem
v-tippy="{ theme: 'tooltip' }" label="Bearer Token"
:title="$t('action.clear')" :icon="
svg="trash-2" authType === 'Bearer'
@click.native="clearContent" ? 'radio_button_checked'
: 'radio_button_unchecked'
"
:active="authType === 'Bearer'"
@click.native="
() => {
authType = 'Bearer'
authTypeOptions.tippy().hide()
}
"
/> />
</div> </div>
</div> </tippy>
<div </span>
v-if="authType === 'None'" <div class="flex">
class="flex flex-col items-center justify-center p-4 text-secondaryLight" <SmartCheckbox
:on="authActive"
class="px-2"
@change="authActive = !authActive"
> >
<img {{ $t("state.enabled") }}
:src="`/images/states/${$colorMode.value}/login.svg`" </SmartCheckbox>
loading="lazy" <ButtonSecondary
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4" v-tippy="{ theme: 'tooltip' }"
:alt="$t('empty.authorization')" to="https://docs.hoppscotch.io/features/authorization"
/> blank
<span class="pb-4 text-center"> :title="$t('app.wiki')"
This SocketIO connection does not use any authentication. svg="help-circle"
</span> />
<ButtonSecondary <ButtonSecondary
outline v-tippy="{ theme: 'tooltip' }"
:label="$t('app.documentation')" :title="$t('action.clear')"
svg="trash-2"
@click.native="clearContent"
/>
</div>
</div>
<div
v-if="authType === 'None'"
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
>
<img
:src="`/images/states/${$colorMode.value}/login.svg`"
loading="lazy"
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
:alt="$t('empty.authorization')"
/>
<span class="pb-4 text-center">
This SocketIO connection does not use any authentication.
</span>
<ButtonSecondary
outline
:label="$t('app.documentation')"
to="https://docs.hoppscotch.io/features/authorization"
blank
svg="external-link"
reverse
class="mb-4"
/>
</div>
<div
v-if="authType === 'Bearer'"
class="flex flex-1 border-b border-dividerLight"
>
<div class="w-2/3 border-r border-dividerLight">
<div class="flex flex-1 border-b border-dividerLight">
<SmartEnvInput v-model="bearerToken" placeholder="Token" />
</div>
</div>
<div
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
>
<div class="p-2">
<div class="pb-2 text-secondaryLight">
{{ $t("helpers.authorization") }}
</div>
<SmartAnchor
class="link"
:label="`${$t('authorization.learn')} \xA0 →`"
to="https://docs.hoppscotch.io/features/authorization" to="https://docs.hoppscotch.io/features/authorization"
blank blank
svg="external-link"
reverse
class="mb-4"
/> />
</div> </div>
<div </div>
v-if="authType === 'Bearer'" </div>
class="flex flex-1 border-b border-dividerLight" </template>
> <template #secondary>
<div class="w-2/3 border-r border-dividerLight"> <RealtimeLog :title="$t('socketio.log')" :log="log" />
<div class="flex flex-1 border-b border-dividerLight"> </template>
<SmartEnvInput v-model="bearerToken" placeholder="Token" /> <template #sidebar>
</div>
</div>
<div
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
>
<div class="p-2">
<div class="pb-2 text-secondaryLight">
{{ $t("helpers.authorization") }}
</div>
<SmartAnchor
class="link"
:label="`${$t('authorization.learn')} \xA0 →`"
to="https://docs.hoppscotch.io/features/authorization"
blank
/>
</div>
</div>
</div>
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<RealtimeLog :title="$t('socketio.log')" :log="log" />
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<div class="flex items-center justify-between p-4"> <div class="flex items-center justify-between p-4">
<label for="events" class="font-semibold text-secondaryLight"> <label for="events" class="font-semibold text-secondaryLight">
{{ $t("socketio.events") }} {{ $t("socketio.events") }}
@@ -297,24 +269,19 @@
</div> </div>
</div> </div>
</div> </div>
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script> <script>
import { defineComponent, ref } from "@nuxtjs/composition-api" import { defineComponent, ref } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
// All Socket.IO client version imports // All Socket.IO client version imports
import ClientV2 from "socket.io-client-v2" import ClientV2 from "socket.io-client-v2"
import { io as ClientV3 } from "socket.io-client-v3" import { io as ClientV3 } from "socket.io-client-v3"
import { io as ClientV4 } from "socket.io-client-v4" import { io as ClientV4 } from "socket.io-client-v4"
import wildcard from "socketio-wildcard" import wildcard from "socketio-wildcard"
import debounce from "lodash/debounce" import debounce from "lodash/debounce"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { useSetting } from "~/newstore/settings"
import { import {
SIOEndpoint$, SIOEndpoint$,
setSIOEndpoint, setSIOEndpoint,
@@ -341,16 +308,8 @@ const socketIoClients = {
} }
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
socketIoClients, socketIoClients,
url: useStream(SIOEndpoint$, "", setSIOEndpoint), url: useStream(SIOEndpoint$, "", setSIOEndpoint),
clientVersion: useStream(SIOVersion$, "", setSIOVersion), clientVersion: useStream(SIOVersion$, "", setSIOVersion),

View File

@@ -1,9 +1,6 @@
<template> <template>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT"> <AppPaneLayout>
<Pane <template #primary>
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<div <div
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar" class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
> >
@@ -48,23 +45,18 @@
/> />
</div> </div>
</div> </div>
</Pane> </template>
<Pane <template #secondary>
:size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<RealtimeLog :title="$t('sse.log')" :log="log" /> <RealtimeLog :title="$t('sse.log')" :log="log" />
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script> <script>
import { defineComponent } from "@nuxtjs/composition-api" import { defineComponent } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css" import "splitpanes/dist/splitpanes.css"
import debounce from "lodash/debounce" import debounce from "lodash/debounce"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { useSetting } from "~/newstore/settings"
import { import {
SSEEndpoint$, SSEEndpoint$,
setSSEEndpoint, setSSEEndpoint,
@@ -83,10 +75,8 @@ import {
import { useStream } from "~/helpers/utils/composables" import { useStream } from "~/helpers/utils/composables"
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
return { return {
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
connectionSSEState: useStream( connectionSSEState: useStream(
SSEConnectionState$, SSEConnectionState$,
false, false,

View File

@@ -1,158 +1,130 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <div
:class="{ class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar"
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, >
}" <div class="inline-flex flex-1 space-x-2">
:horizontal="!mdAndLarger" <input
> id="websocket-url"
<Pane v-model="url"
size="75" class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark"
min-size="65" type="url"
class="hide-scrollbar !overflow-auto flex flex-col" autocomplete="off"
> spellcheck="false"
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT"> :class="{ error: !urlValid }"
<Pane :placeholder="$t('websocket.url')"
:size="COLUMN_LAYOUT ? 45 : 50" :disabled="connectionState"
class="hide-scrollbar !overflow-auto flex flex-col" @keyup.enter="urlValid ? toggleConnection() : null"
> />
<div <ButtonPrimary
class="sticky top-0 z-10 flex flex-shrink-0 p-4 overflow-x-auto space-x-2 bg-primary hide-scrollbar" id="connect"
> :disabled="!urlValid"
<div class="inline-flex flex-1 space-x-2"> class="w-32"
<input name="connect"
id="websocket-url" :label="
v-model="url" !connectionState ? $t('action.connect') : $t('action.disconnect')
class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark" "
type="url" :loading="connectingState"
autocomplete="off" @click.native="toggleConnection"
spellcheck="false" />
:class="{ error: !urlValid }" </div>
:placeholder="$t('websocket.url')" </div>
:disabled="connectionState" <div
@keyup.enter="urlValid ? toggleConnection() : null" class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold"
/> >
<ButtonPrimary <label class="font-semibold text-secondaryLight">
id="connect" {{ $t("websocket.protocols") }}
:disabled="!urlValid" </label>
class="w-32" <div class="flex">
name="connect" <ButtonSecondary
:label=" v-tippy="{ theme: 'tooltip' }"
!connectionState :title="$t('action.clear_all')"
? $t('action.connect') svg="trash-2"
: $t('action.disconnect') @click.native="clearContent"
" />
:loading="connectingState" <ButtonSecondary
@click.native="toggleConnection" v-tippy="{ theme: 'tooltip' }"
/> :title="$t('add.new')"
</div> svg="plus"
</div> @click.native="addProtocol"
<div />
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold" </div>
> </div>
<label class="font-semibold text-secondaryLight"> <div
{{ $t("websocket.protocols") }} v-for="(protocol, index) of protocols"
</label> :key="`protocol-${index}`"
<div class="flex"> class="flex border-b divide-x divide-dividerLight border-dividerLight"
<ButtonSecondary >
v-tippy="{ theme: 'tooltip' }" <input
:title="$t('action.clear_all')" v-model="protocol.value"
svg="trash-2" class="flex flex-1 px-4 py-2 bg-transparent"
@click.native="clearContent" :placeholder="$t('count.protocol', { count: index + 1 })"
/> name="message"
<ButtonSecondary type="text"
v-tippy="{ theme: 'tooltip' }" autocomplete="off"
:title="$t('add.new')" @change="
svg="plus" updateProtocol(index, {
@click.native="addProtocol" value: $event.target.value,
/> active: protocol.active,
</div> })
</div> "
<div />
v-for="(protocol, index) of protocols" <span>
:key="`protocol-${index}`" <ButtonSecondary
class="flex border-b divide-x divide-dividerLight border-dividerLight" v-tippy="{ theme: 'tooltip' }"
> :title="
<input protocol.hasOwnProperty('active')
v-model="protocol.value" ? protocol.active
class="flex flex-1 px-4 py-2 bg-transparent" ? $t('action.turn_off')
:placeholder="$t('count.protocol', { count: index + 1 })" : $t('action.turn_on')
name="message" : $t('action.turn_off')
type="text" "
autocomplete="off" :svg="
@change=" protocol.hasOwnProperty('active')
updateProtocol(index, { ? protocol.active
value: $event.target.value, ? 'check-circle'
active: protocol.active, : 'circle'
}) : 'check-circle'
" "
/> color="green"
<span> @click.native="
<ButtonSecondary updateProtocol(index, {
v-tippy="{ theme: 'tooltip' }" value: protocol.value,
:title=" active: !protocol.active,
protocol.hasOwnProperty('active') })
? protocol.active "
? $t('action.turn_off') />
: $t('action.turn_on') </span>
: $t('action.turn_off') <span>
" <ButtonSecondary
:svg=" v-tippy="{ theme: 'tooltip' }"
protocol.hasOwnProperty('active') :title="$t('action.remove')"
? protocol.active svg="trash"
? 'check-circle' color="red"
: 'circle' @click.native="deleteProtocol({ index })"
: 'check-circle' />
" </span>
color="green" </div>
@click.native=" <div
updateProtocol(index, { v-if="protocols.length === 0"
value: protocol.value, class="flex flex-col items-center justify-center p-4 text-secondaryLight"
active: !protocol.active, >
}) <img
" :src="`/images/states/${$colorMode.value}/add_category.svg`"
/> loading="lazy"
</span> class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
<span> :alt="$t('empty.protocols')"
<ButtonSecondary />
v-tippy="{ theme: 'tooltip' }" <span class="mb-4 text-center">
:title="$t('action.remove')" {{ $t("empty.protocols") }}
svg="trash" </span>
color="red" </div>
@click.native="deleteProtocol({ index })" </template>
/> <template #secondary>
</span> <RealtimeLog :title="$t('websocket.log')" :log="log" />
</div> </template>
<div <template #sidebar>
v-if="protocols.length === 0"
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
>
<img
:src="`/images/states/${$colorMode.value}/add_category.svg`"
loading="lazy"
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
:alt="$t('empty.protocols')"
/>
<span class="mb-4 text-center">
{{ $t("empty.protocols") }}
</span>
</div>
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<RealtimeLog :title="$t('websocket.log')" :log="log" />
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<div class="flex items-center justify-between p-4"> <div class="flex items-center justify-between p-4">
<label <label
for="websocket-message" for="websocket-message"
@@ -183,18 +155,14 @@
@click.native="sendMessage" @click.native="sendMessage"
/> />
</div> </div>
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script> <script>
import { defineComponent } from "@nuxtjs/composition-api" import { defineComponent } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import debounce from "lodash/debounce" import debounce from "lodash/debounce"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics" import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { useSetting } from "~/newstore/settings"
import { import {
setWSEndpoint, setWSEndpoint,
WSEndpoint$, WSEndpoint$,
@@ -217,16 +185,8 @@ import {
import { useStream } from "~/helpers/utils/composables" import { useStream } from "~/helpers/utils/composables"
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
url: useStream(WSEndpoint$, "", setWSEndpoint), url: useStream(WSEndpoint$, "", setWSEndpoint),
protocols: useStream(WSProtocols$, [], setWSProtocols), protocols: useStream(WSProtocols$, [], setWSProtocols),
connectionState: useStream( connectionState: useStream(

View File

@@ -23,7 +23,7 @@
horizontal horizontal
> >
<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" role="main">
<nuxt class="flex flex-1" /> <nuxt class="flex flex-1" />
</main> </main>
</Pane> </Pane>

View File

@@ -1,132 +1,103 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <div class="flex items-start justify-between p-4">
:class="{ <label>
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, {{ $t("documentation.generate_message") }}
}" </label>
:horizontal="!mdAndLarger" <span
> class="inline-flex px-2 py-1 rounded bg-accentDark text-accentContrast"
<Pane
size="75"
min-size="65"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
<Pane
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
> >
<div class="flex items-start justify-between p-4"> BETA
<label> </span>
{{ $t("documentation.generate_message") }} </div>
</label> <div
<span class="sticky top-0 z-10 flex items-start justify-between border-b bg-primary border-dividerLight"
class="inline-flex px-2 py-1 rounded bg-accentDark text-accentContrast" >
> <label for="collectionUpload">
BETA <ButtonSecondary
</span> v-tippy="{ theme: 'tooltip' }"
</div> title="JSON"
<div svg="folder"
class="sticky top-0 z-10 flex items-start justify-between border-b bg-primary border-dividerLight" class="!rounded-none"
> :label="$t('import.collections')"
<label for="collectionUpload"> @click.native="$refs.collectionUpload.click()"
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
title="JSON"
svg="folder"
class="!rounded-none"
:label="$t('import.collections')"
@click.native="$refs.collectionUpload.click()"
/>
</label>
<input
ref="collectionUpload"
class="input"
name="collectionUpload"
type="file"
@change="uploadCollection"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.clear')"
svg="trash-2"
@click.native="collectionJSON = '[]'"
/>
</div>
<textarea-autosize
id="import-curl"
v-model="collectionJSON"
class="w-full p-4 font-mono bg-primary"
autofocus
rows="8"
/> />
<div </label>
class="sticky bottom-0 z-10 flex items-start justify-between p-4 border-t border-b bg-primary border-dividerLight" <input
ref="collectionUpload"
class="input"
name="collectionUpload"
type="file"
@change="uploadCollection"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.clear')"
svg="trash-2"
@click.native="collectionJSON = '[]'"
/>
</div>
<textarea-autosize
id="import-curl"
v-model="collectionJSON"
class="w-full p-4 font-mono bg-primary"
autofocus
rows="8"
/>
<div
class="sticky bottom-0 z-10 flex items-start justify-between p-4 border-t border-b bg-primary border-dividerLight"
>
<ButtonPrimary
:label="$t('documentation.generate')"
@click.native="getDoc"
/>
</div>
</template>
<template #secondary>
<div class="flex flex-col">
<div
v-if="items.length === 0"
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
>
<i class="pb-2 opacity-75 material-icons">topic</i>
<span class="text-center">
{{ $t("helpers.generate_documentation_first") }}
</span>
</div>
<div
v-else
class="sticky top-0 z-10 flex flex-1 p-4 border-b bg-primary border-dividerLight"
>
<span
v-tippy="{ theme: 'tooltip' }"
:title="
!currentUser
? $t('export.require_github')
: currentUser.provider !== 'github.com'
? $t('export.require_github')
: 'Beta'
"
> >
<ButtonPrimary <ButtonPrimary
:label="$t('documentation.generate')" :disabled="
@click.native="getDoc" !currentUser
? true
: currentUser.provider !== 'github.com'
? true
: false
"
:label="$t('export.create_secret_gist')"
@click.native="createDocsGist"
/> />
</div> </span>
</Pane> </div>
<Pane <div v-for="(collection, index) in items" :key="`collection-${index}`">
:size="COLUMN_LAYOUT ? 65 : 50" <DocsCollection :collection="collection" />
class="hide-scrollbar !overflow-auto flex flex-col" </div>
> </div>
<div class="flex flex-col"> </template>
<div <template #sidebar>
v-if="items.length === 0"
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
>
<i class="pb-2 opacity-75 material-icons">topic</i>
<span class="text-center">
{{ $t("helpers.generate_documentation_first") }}
</span>
</div>
<div
v-else
class="sticky top-0 z-10 flex flex-1 p-4 border-b bg-primary border-dividerLight"
>
<span
v-tippy="{ theme: 'tooltip' }"
:title="
!currentUser
? $t('export.require_github')
: currentUser.provider !== 'github.com'
? $t('export.require_github')
: 'Beta'
"
>
<ButtonPrimary
:disabled="
!currentUser
? true
: currentUser.provider !== 'github.com'
? true
: false
"
:label="$t('export.create_secret_gist')"
@click.native="createDocsGist"
/>
</span>
</div>
<div
v-for="(collection, index) in items"
:key="`collection-${index}`"
>
<DocsCollection :collection="collection" />
</div>
</div>
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<aside> <aside>
<Collections <Collections
:selected="selected" :selected="selected"
@@ -135,35 +106,23 @@
@remove-collection="removeSelectedCollection($event)" @remove-collection="removeSelectedCollection($event)"
/> />
</aside> </aside>
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script> <script>
import { defineComponent } from "@nuxtjs/composition-api" import { defineComponent } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import Mustache from "mustache" import Mustache from "mustache"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { currentUser$ } from "~/helpers/fb/auth" import { currentUser$ } from "~/helpers/fb/auth"
import DocsTemplate from "~/assets/md/docs.md" import DocsTemplate from "~/assets/md/docs.md"
import folderContents from "~/assets/md/folderContents.md" import folderContents from "~/assets/md/folderContents.md"
import folderBody from "~/assets/md/folderBody.md" import folderBody from "~/assets/md/folderBody.md"
import { useSetting } from "~/newstore/settings"
import { useReadonlyStream } from "~/helpers/utils/composables" import { useReadonlyStream } from "~/helpers/utils/composables"
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
currentUser: useReadonlyStream(currentUser$, null), currentUser: useReadonlyStream(currentUser$, null),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
} }
}, },
data() { data() {

View File

@@ -1,55 +1,24 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <GraphqlRequest :conn="gqlConn" />
:class="{ <GraphqlRequestOptions :conn="gqlConn" />
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, </template>
}" <template #secondary>
:horizontal="!mdAndLarger" <GraphqlResponse :conn="gqlConn" />
> </template>
<Pane <template #sidebar>
size="75"
min-size="65"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
<Pane
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<GraphqlRequest :conn="gqlConn" />
<GraphqlRequestOptions :conn="gqlConn" />
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<GraphqlResponse :conn="gqlConn" />
</Pane>
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR"
size="25"
min-size="20"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<GraphqlSidebar :conn="gqlConn" /> <GraphqlSidebar :conn="gqlConn" />
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, watch } from "@nuxtjs/composition-api" import { defineComponent, watch } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { useSetting } from "~/newstore/settings"
import { GQLConnection } from "~/helpers/GQLConnection" import { GQLConnection } from "~/helpers/GQLConnection"
import { useNuxt, useReadonlyStream } from "~/helpers/utils/composables" import { useNuxt, useReadonlyStream } from "~/helpers/utils/composables"
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
beforeRouteLeave(_to, _from, next) { beforeRouteLeave(_to, _from, next) {
if (this.gqlConn.connected$.value) { if (this.gqlConn.connected$.value) {
this.gqlConn.disconnect() this.gqlConn.disconnect()
@@ -68,14 +37,7 @@ export default defineComponent({
else nuxt.value.$loading.finish() else nuxt.value.$loading.finish()
}) })
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
gqlConn, gqlConn,
} }
}, },

View File

@@ -1,85 +1,56 @@
<template> <template>
<Splitpanes <AppPaneLayout>
class="smart-splitter" <template #primary>
:rtl="SIDEBAR_ON_LEFT && mdAndLarger" <HttpRequest />
:class="{ <SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10">
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger, <SmartTab
}" :id="'params'"
:horizontal="!mdAndLarger" :label="`${$t('tab.parameters')}`"
> :selected="true"
<Pane :info="`${newActiveParamsCount$}`"
size="75"
min-size="65"
class="hide-scrollbar !overflow-auto flex flex-col"
>
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
<Pane
:size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto flex flex-col"
> >
<HttpRequest /> <HttpParameters />
<SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10"> </SmartTab>
<SmartTab
:id="'params'"
:label="`${$t('tab.parameters')}`"
:selected="true"
:info="`${newActiveParamsCount$}`"
>
<HttpParameters />
</SmartTab>
<SmartTab :id="'bodyParams'" :label="`${$t('tab.body')}`"> <SmartTab :id="'bodyParams'" :label="`${$t('tab.body')}`">
<HttpBody /> <HttpBody />
</SmartTab> </SmartTab>
<SmartTab <SmartTab
:id="'headers'" :id="'headers'"
:label="`${$t('tab.headers')}`" :label="`${$t('tab.headers')}`"
:info="`${newActiveHeadersCount$}`" :info="`${newActiveHeadersCount$}`"
>
<HttpHeaders />
</SmartTab>
<SmartTab
:id="'authorization'"
:label="`${$t('tab.authorization')}`"
>
<HttpAuthorization />
</SmartTab>
<SmartTab
:id="'preRequestScript'"
:label="`${$t('tab.pre_request_script')}`"
:indicator="
preRequestScript && preRequestScript.length > 0 ? true : false
"
>
<HttpPreRequestScript />
</SmartTab>
<SmartTab
:id="'tests'"
:label="`${$t('tab.tests')}`"
:indicator="testScript && testScript.length > 0 ? true : false"
>
<HttpTests />
</SmartTab>
</SmartTabs>
</Pane>
<Pane
:size="COLUMN_LAYOUT ? 65 : 50"
class="flex flex-col hide-scrollbar !overflow-auto flex flex-col"
> >
<HttpResponse ref="response" /> <HttpHeaders />
</Pane> </SmartTab>
</Splitpanes>
</Pane> <SmartTab :id="'authorization'" :label="`${$t('tab.authorization')}`">
<Pane <HttpAuthorization />
v-if="SIDEBAR" </SmartTab>
size="25"
min-size="20" <SmartTab
class="hide-scrollbar !overflow-auto flex flex-col" :id="'preRequestScript'"
> :label="`${$t('tab.pre_request_script')}`"
:indicator="
preRequestScript && preRequestScript.length > 0 ? true : false
"
>
<HttpPreRequestScript />
</SmartTab>
<SmartTab
:id="'tests'"
:label="`${$t('tab.tests')}`"
:indicator="testScript && testScript.length > 0 ? true : false"
>
<HttpTests />
</SmartTab>
</SmartTabs>
</template>
<template #secondary>
<HttpResponse ref="response" />
</template>
<template #sidebar>
<SmartTabs styles="sticky bg-primary z-10 top-0" vertical> <SmartTabs styles="sticky bg-primary z-10 top-0" vertical>
<SmartTab <SmartTab
:id="'history'" :id="'history'"
@@ -106,8 +77,8 @@
<Environments /> <Environments />
</SmartTab> </SmartTab>
</SmartTabs> </SmartTabs>
</Pane> </template>
</Splitpanes> </AppPaneLayout>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -121,8 +92,6 @@ import {
useContext, useContext,
watch, watch,
} from "@nuxtjs/composition-api" } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import { map } from "rxjs/operators" import { map } from "rxjs/operators"
import { Subscription } from "rxjs" import { Subscription } from "rxjs"
import isEqual from "lodash/isEqual" import isEqual from "lodash/isEqual"
@@ -131,8 +100,6 @@ import {
HoppRESTAuthOAuth2, HoppRESTAuthOAuth2,
safelyExtractRESTRequest, safelyExtractRESTRequest,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { useSetting } from "~/newstore/settings"
import { import {
restActiveParamsCount$, restActiveParamsCount$,
restActiveHeadersCount$, restActiveHeadersCount$,
@@ -227,7 +194,6 @@ function setupRequestSync(
} }
export default defineComponent({ export default defineComponent({
components: { Splitpanes, Pane },
setup() { setup() {
const requestForSync = ref<HoppRESTRequest | null>(null) const requestForSync = ref<HoppRESTRequest | null>(null)
@@ -271,11 +237,7 @@ export default defineComponent({
setupRequestSync(confirmSync, requestForSync) setupRequestSync(confirmSync, requestForSync)
bindRequestToURLParams() bindRequestToURLParams()
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
return { return {
mdAndLarger,
newActiveParamsCount$: useReadonlyStream( newActiveParamsCount$: useReadonlyStream(
restActiveParamsCount$.pipe( restActiveParamsCount$.pipe(
map((e) => { map((e) => {
@@ -294,9 +256,6 @@ export default defineComponent({
), ),
null null
), ),
SIDEBAR: useSetting("SIDEBAR"),
COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
confirmSync, confirmSync,
syncRequest, syncRequest,
oAuthURL, oAuthURL,