Merge branch 'refactor/ui' into feat/storybook
This commit is contained in:
@@ -103,8 +103,8 @@
|
||||
|
||||
---
|
||||
|
||||
- Choose theme: System, Light, Dark (default) and Black
|
||||
- Choose accent color: Blue, Green (default), Teal, Indigo, Purple, Orange, Pink, Red, and Yellow
|
||||
- Choose theme: System (default), Light, Dark and Black
|
||||
- Choose accent color: Green (default), Teal, Blue, Indigo, Purple, Yellow, Orange, Red and Pink
|
||||
- Toggle auto-scroll to response
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg width="2500" height="2432" viewBox="0 0 256 249" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="#161614"><path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0"/><path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398"/></g></svg>
|
||||
<svg width="2500" height="2432" viewBox="0 0 256 249" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="currentColor"><path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0"/><path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398"/></g></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M118.2 208.3c19.9-60.3 76.5-103.6 143.7-103.6 36.1 0 68.6 12.8 94.3 33.7L430.5 64C385.2 24.4 327 0 261.8 0 160.9 0 74 57.6 32.3 141.9l85.9 66.4z" fill="#ea4335"/><path d="M348 384.3c-23.3 15-52.8 23-86.2 23-66.9 0-123.3-43-143.4-102.9l-86.2 65.4C73.9 454.3 160.8 512 261.8 512c62.6 0 122.4-22.2 167.1-64L348 384.3z" fill="#34a853"/><path d="M428.9 448c46.8-43.7 77.2-108.7 77.2-192 0-15.1-2.3-31.4-5.8-46.5H261.8v98.9h137.3c-6.8 33.3-25 59-51.1 75.9l80.9 63.7z" fill="#4a90e2"/><path d="M118.4 304.4c-5.1-15.2-7.9-31.4-7.9-48.4 0-16.7 2.7-32.7 7.6-47.7l-85.9-66.4C15.1 176.2 5.8 214.9 5.8 256c0 40.9 9.5 79.6 26.4 113.8l86.2-65.4z" fill="#fbbc05"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M118.2 208.3c19.9-60.3 76.5-103.6 143.7-103.6 36.1 0 68.6 12.8 94.3 33.7L430.5 64C385.2 24.4 327 0 261.8 0 160.9 0 74 57.6 32.3 141.9l85.9 66.4z" fill="currentColor"/><path d="M348 384.3c-23.3 15-52.8 23-86.2 23-66.9 0-123.3-43-143.4-102.9l-86.2 65.4C73.9 454.3 160.8 512 261.8 512c62.6 0 122.4-22.2 167.1-64L348 384.3z" fill="currentColor"/><path d="M428.9 448c46.8-43.7 77.2-108.7 77.2-192 0-15.1-2.3-31.4-5.8-46.5H261.8v98.9h137.3c-6.8 33.3-25 59-51.1 75.9l80.9 63.7z" fill="currentColor"/><path d="M118.4 304.4c-5.1-15.2-7.9-31.4-7.9-48.4 0-16.7 2.7-32.7 7.6-47.7l-85.9-66.4C15.1 176.2 5.8 214.9 5.8 256c0 40.9 9.5 79.6 26.4 113.8l86.2-65.4z" fill="currentColor"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 726 B After Width: | Height: | Size: 746 B |
1
assets/icons/graphql.svg
Normal file
1
assets/icons/graphql.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="64" width="64" viewBox="0 0 29.999 30" fill="currentColor"><path d="M4.08 22.864l-1.1-.636L15.248.98l1.1.636z"/><path d="M2.727 20.53h24.538v1.272H2.727z"/><path d="M15.486 28.332L3.213 21.246l.636-1.1 12.273 7.086zm10.662-18.47L13.874 2.777l.636-1.1 12.273 7.086z"/><path d="M3.852 9.858l-.636-1.1L15.5 1.67l.636 1.1z"/><path d="M25.922 22.864l-12.27-21.25 1.1-.636 12.27 21.25zM3.7 7.914h1.272v14.172H3.7zm21.328 0H26.3v14.172h-1.272z"/><path d="M15.27 27.793l-.555-.962 10.675-6.163.555.962z"/><path d="M27.985 22.5a2.68 2.68 0 01-3.654.981 2.68 2.68 0 01-.981-3.654 2.68 2.68 0 013.654-.981 2.665 2.665 0 01.98 3.654M6.642 10.174a2.68 2.68 0 01-3.654.981A2.68 2.68 0 012.007 7.5a2.68 2.68 0 013.654-.981 2.68 2.68 0 01.981 3.654M2.015 22.5a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654c-1.287.735-2.92.3-3.654-.98m21.343-12.326a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654 2.68 2.68 0 01-3.654-.981M15 30a2.674 2.674 0 112.674-2.673A2.68 2.68 0 0115 30m0-24.652a2.67 2.67 0 01-2.674-2.674 2.67 2.67 0 115.347 0A2.67 2.67 0 0115 5.347"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -15,17 +15,10 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-divider;
|
||||
@apply bg-divider bg-clip-content;
|
||||
@apply rounded-full;
|
||||
@apply border-solid;
|
||||
@apply border-4;
|
||||
@apply border-transparent;
|
||||
@apply bg-clip-content;
|
||||
|
||||
&:hover {
|
||||
@apply bg-dividerDark;
|
||||
@apply bg-clip-content;
|
||||
}
|
||||
@apply border-solid border-4 border-transparent;
|
||||
@apply hover:(bg-dividerDark bg-clip-content);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
@@ -42,23 +35,18 @@
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@apply text-secondaryLight;
|
||||
@apply opacity-25;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-primary;
|
||||
@apply text-secondary;
|
||||
@apply !text-sm;
|
||||
@apply text-secondary text-xs;
|
||||
@apply font-medium;
|
||||
@apply select-none;
|
||||
@apply overflow-x-hidden;
|
||||
|
||||
overflow: overlay;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
@@ -73,7 +61,8 @@ body {
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to .page-enter,
|
||||
.fade-leave-to,
|
||||
.page-enter,
|
||||
.page-leave-to,
|
||||
.layout-enter,
|
||||
.layout-leave-to {
|
||||
@@ -81,14 +70,14 @@ body {
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-size: 1.25rem !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
.svg-icons {
|
||||
@apply inline-flex;
|
||||
@apply flex-shrink-0;
|
||||
@apply h-5;
|
||||
@apply w-5;
|
||||
@apply h-4;
|
||||
@apply w-4;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -96,27 +85,36 @@ a {
|
||||
@apply text-current;
|
||||
@apply no-underline;
|
||||
@apply outline-none;
|
||||
@apply focus-visible:ring;
|
||||
@apply focus-visible:ring-inset;
|
||||
@apply focus-visible:ring-accent;
|
||||
@apply transition;
|
||||
|
||||
&.link {
|
||||
@apply items-center;
|
||||
@apply px-2 py-1;
|
||||
@apply -mx-2 -my-1;
|
||||
@apply text-accent;
|
||||
@apply hover:text-accent;
|
||||
@apply focus:text-accent;
|
||||
@apply hover:text-accentDark;
|
||||
@apply focus-visible:(ring ring-inset ring-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.tippy-popper {
|
||||
.tooltip-theme {
|
||||
@apply bg-secondary;
|
||||
@apply text-primaryLight;
|
||||
@apply text-xs;
|
||||
@apply bg-tooltip;
|
||||
@apply text-primary text-xs;
|
||||
@apply font-semibold;
|
||||
@apply px-2;
|
||||
@apply px-4;
|
||||
@apply py-1 px-2;
|
||||
@apply shadow;
|
||||
|
||||
kbd {
|
||||
@apply first:ml-2;
|
||||
@apply last:-mr-1;
|
||||
@apply font-sans;
|
||||
@apply bg-secondaryDark;
|
||||
@apply text-primaryDark;
|
||||
@apply rounded;
|
||||
@apply px-1;
|
||||
@apply ml-1;
|
||||
}
|
||||
}
|
||||
|
||||
.popover-theme {
|
||||
@@ -124,59 +122,61 @@ a {
|
||||
@apply text-secondary;
|
||||
@apply p-2;
|
||||
@apply shadow-lg;
|
||||
@apply focus:outline-none;
|
||||
}
|
||||
|
||||
&[x-placement^="top"] .tippy-tooltip .tippy-arrow {
|
||||
border-top-color: var(--primary-color);
|
||||
@apply border-t-primary;
|
||||
}
|
||||
|
||||
&[x-placement^="bottom"] .tippy-tooltip .tippy-arrow {
|
||||
border-bottom-color: var(--primary-color);
|
||||
@apply border-b-primary;
|
||||
}
|
||||
|
||||
&[x-placement^="left"] .tippy-tooltip .tippy-arrow {
|
||||
border-left-color: var(--primary-color);
|
||||
@apply border-l-primary;
|
||||
}
|
||||
|
||||
&[x-placement^="right"] .tippy-tooltip .tippy-arrow {
|
||||
border-right-color: var(--primary-color);
|
||||
@apply border-r-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.tippy-content > div {
|
||||
@apply flex;
|
||||
@apply flex-col;
|
||||
@apply max-h-64;
|
||||
@apply overflow-y-auto;
|
||||
@apply flex flex-col;
|
||||
@apply max-h-48;
|
||||
@apply items-stretch;
|
||||
@apply overflow-y-auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
@apply border-b;
|
||||
@apply border-dividerLight;
|
||||
@apply border-b border-dividerLight;
|
||||
}
|
||||
|
||||
.heading {
|
||||
@apply font-bold;
|
||||
@apply text-secondaryDark;
|
||||
@apply text-lg;
|
||||
@apply text-secondaryDark text-lg;
|
||||
}
|
||||
|
||||
.textarea,
|
||||
.input,
|
||||
.select {
|
||||
@apply flex;
|
||||
@apply flex flex-1;
|
||||
@apply w-full;
|
||||
@apply px-4;
|
||||
@apply py-2;
|
||||
@apply px-4 py-2;
|
||||
@apply bg-primary;
|
||||
@apply truncate;
|
||||
@apply rounded-lg;
|
||||
@apply rounded;
|
||||
@apply text-xs;
|
||||
@apply font-semibold;
|
||||
@apply border-2;
|
||||
@apply border-divider;
|
||||
@apply border border-divider;
|
||||
@apply transition;
|
||||
@apply focus:outline-none;
|
||||
@apply focus:border-accent;
|
||||
@apply focus:(outline-none border-accent);
|
||||
@apply disabled:cursor-not-allowed;
|
||||
}
|
||||
|
||||
.input[type="file"],
|
||||
@@ -187,12 +187,11 @@ hr {
|
||||
|
||||
pre.ace_editor {
|
||||
@apply font-mono;
|
||||
@apply z-0;
|
||||
@apply resize-none;
|
||||
@apply z-0;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply cursor-pointer;
|
||||
.select {
|
||||
@apply appearance-none;
|
||||
|
||||
&::-ms-expand {
|
||||
@@ -203,20 +202,38 @@ select {
|
||||
.select-wrapper {
|
||||
@apply relative;
|
||||
@apply w-full;
|
||||
@apply cursor-pointer;
|
||||
|
||||
&::after {
|
||||
@apply inline-block;
|
||||
@apply absolute;
|
||||
@apply inline-block;
|
||||
@apply pointer-events-none;
|
||||
@apply font-icon;
|
||||
@apply text-secondaryLight;
|
||||
@apply top-3;
|
||||
@apply top-2.5;
|
||||
@apply right-3;
|
||||
|
||||
content: "\e313";
|
||||
}
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
@apply relative;
|
||||
@apply w-full;
|
||||
|
||||
&::after {
|
||||
@apply absolute;
|
||||
@apply inline-block;
|
||||
@apply pointer-events-none;
|
||||
@apply font-icon;
|
||||
@apply text-secondaryLight;
|
||||
@apply top-2;
|
||||
@apply left-3;
|
||||
|
||||
content: "\e8b6";
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
@apply hidden;
|
||||
|
||||
@@ -226,9 +243,8 @@ input[type="checkbox"] {
|
||||
@apply cursor-pointer;
|
||||
|
||||
&::before {
|
||||
@apply border;
|
||||
@apply border-secondary;
|
||||
@apply rounded-lg;
|
||||
@apply border border-secondary;
|
||||
@apply rounded;
|
||||
@apply inline-flex;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@@ -249,19 +265,19 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.info-response {
|
||||
@apply text-pink-400;
|
||||
@apply text-pink-500;
|
||||
}
|
||||
|
||||
.success-response {
|
||||
@apply text-green-400;
|
||||
@apply text-green-500;
|
||||
}
|
||||
|
||||
.redir-response {
|
||||
@apply text-yellow-400;
|
||||
@apply text-yellow-500;
|
||||
}
|
||||
|
||||
.cl-error-response {
|
||||
@apply text-red-400;
|
||||
@apply text-red-500;
|
||||
}
|
||||
|
||||
.sv-error-response {
|
||||
@@ -278,8 +294,6 @@ input[type="checkbox"] {
|
||||
textarea {
|
||||
@apply m-0;
|
||||
@apply w-full;
|
||||
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.covers-response {
|
||||
@@ -292,37 +306,41 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.toasted-container {
|
||||
margin-bottom: 68px;
|
||||
|
||||
.toasted {
|
||||
justify-content: space-between !important;
|
||||
|
||||
&.info {
|
||||
background-color: var(--accent-color) !important;
|
||||
color: var(--primary-color) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
&.bubble .action {
|
||||
color: inherit !important;
|
||||
}
|
||||
&.toasted-primary {
|
||||
@apply bg-tooltip;
|
||||
@apply text-primary text-xs;
|
||||
@apply justify-start;
|
||||
@apply shadow;
|
||||
@apply font-semibold;
|
||||
|
||||
.action {
|
||||
margin-left: auto !important;
|
||||
@apply ml-auto;
|
||||
@apply transition;
|
||||
@apply text-current;
|
||||
@apply hover:(opacity-75 no-underline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
main {
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
&.info {
|
||||
@apply !bg-accent;
|
||||
}
|
||||
|
||||
&.error {
|
||||
@apply !bg-red-200;
|
||||
@apply !text-red-800;
|
||||
}
|
||||
|
||||
&.success {
|
||||
@apply !bg-green-200;
|
||||
@apply !text-green-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.splitpanes__splitter {
|
||||
@apply relative;
|
||||
@apply bg-primaryLight;
|
||||
@apply transition;
|
||||
}
|
||||
|
||||
.splitpanes--vertical > .splitpanes__splitter {
|
||||
@@ -338,7 +356,7 @@ input[type="checkbox"] {
|
||||
@apply inset-0;
|
||||
@apply bg-dividerLight;
|
||||
@apply opacity-0;
|
||||
@apply z-1;
|
||||
@apply z-30;
|
||||
@apply transition;
|
||||
|
||||
content: "";
|
||||
@@ -347,7 +365,7 @@ input[type="checkbox"] {
|
||||
.splitpanes__splitter::after {
|
||||
@apply absolute;
|
||||
@apply inset-0;
|
||||
@apply z-1;
|
||||
@apply z-30;
|
||||
@apply transition;
|
||||
@apply flex;
|
||||
@apply items-center;
|
||||
@@ -379,3 +397,9 @@ input[type="checkbox"] {
|
||||
bottom: -2px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
main {
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@mixin baseTheme {
|
||||
--font-sans: "Montserrat", "sans-serif";
|
||||
--font-sans: "Inter", "sans-serif";
|
||||
--font-mono: "Roboto Mono", "monospace";
|
||||
--font-icon: "Material Icons";
|
||||
}
|
||||
@@ -8,25 +8,25 @@
|
||||
// Background color
|
||||
--primary-color: theme("colors.true-gray.900");
|
||||
// Light Background color
|
||||
--primary-light-color: theme("colors.true-gray.800");
|
||||
--primary-light-color: theme("colors.dark.600");
|
||||
// Dark Background color
|
||||
--primary-dark-color: theme("colors.dark.900");
|
||||
// Text color
|
||||
--secondary-color: theme("colors.true-gray.400");
|
||||
// Light Text color
|
||||
--secondary-light-color: theme("colors.true-gray.200");
|
||||
--secondary-light-color: theme("colors.true-gray.500");
|
||||
// Dark Text color
|
||||
--secondary-dark-color: theme("colors.white");
|
||||
--secondary-dark-color: theme("colors.true-gray.100");
|
||||
// Border color
|
||||
--divider-color: theme("colors.true-gray.700");
|
||||
--divider-color: theme("colors.true-gray.800");
|
||||
// Light Border color
|
||||
--divider-light-color: theme("colors.true-gray.800");
|
||||
--divider-light-color: theme("colors.dark.500");
|
||||
// Dark Border color
|
||||
--divider-dark-color: theme("colors.true-gray.600");
|
||||
--divider-dark-color: theme("colors.dark.300");
|
||||
// Error color
|
||||
--error-color: theme("colors.true-gray.600");
|
||||
--error-color: theme("colors.dark.800");
|
||||
// Tooltip color
|
||||
--tooltip-color: theme("colors.true-gray.800");
|
||||
--tooltip-color: theme("colors.true-gray.100");
|
||||
// Editor theme
|
||||
--editor-theme: "merbivore_soft";
|
||||
}
|
||||
@@ -35,25 +35,25 @@
|
||||
// Background color
|
||||
--primary-color: theme("colors.white");
|
||||
// Light Background color
|
||||
--primary-light-color: theme("colors.true-gray.50");
|
||||
--primary-light-color: theme("colors.blue-gray.50");
|
||||
// Dark Background color
|
||||
--primary-dark-color: theme("colors.true-gray.100");
|
||||
--primary-dark-color: theme("colors.blue-gray.100");
|
||||
// Text color
|
||||
--secondary-color: theme("colors.true-gray.500");
|
||||
--secondary-color: theme("colors.blue-gray.600");
|
||||
// Light Text color
|
||||
--secondary-light-color: theme("colors.true-gray.400");
|
||||
--secondary-light-color: theme("colors.blue-gray.500");
|
||||
// Dark Text color
|
||||
--secondary-dark-color: theme("colors.true-gray.600");
|
||||
--secondary-dark-color: theme("colors.blue-gray.700");
|
||||
// Border color
|
||||
--divider-color: theme("colors.true-gray.200");
|
||||
--divider-color: theme("colors.blue-gray.200");
|
||||
// Light Border color
|
||||
--divider-light-color: theme("colors.true-gray.100");
|
||||
--divider-light-color: theme("colors.blue-gray.100");
|
||||
// Dark Border color
|
||||
--divider-dark-color: theme("colors.true-gray.300");
|
||||
--divider-dark-color: theme("colors.blue-gray.300");
|
||||
// Error color
|
||||
--error-color: theme("colors.true-gray.700");
|
||||
--error-color: theme("colors.blue-gray.700");
|
||||
// Tooltip color
|
||||
--tooltip-color: theme("colors.true-gray.50");
|
||||
--tooltip-color: theme("colors.blue-gray.800");
|
||||
// Editor theme
|
||||
--editor-theme: "textmate";
|
||||
}
|
||||
@@ -62,51 +62,36 @@
|
||||
// Background color
|
||||
--primary-color: theme("colors.dark.900");
|
||||
// Light Background color
|
||||
--primary-light-color: theme("colors.dark.700");
|
||||
--primary-light-color: theme("colors.true-gray.900");
|
||||
// Dark Background color
|
||||
--primary-dark-color: theme("colors.dark.800");
|
||||
// Text color
|
||||
--secondary-color: theme("colors.true-gray.400");
|
||||
// Light Text color
|
||||
--secondary-light-color: theme("colors.true-gray.600");
|
||||
--secondary-light-color: theme("colors.true-gray.500");
|
||||
// Dark Text color
|
||||
--secondary-dark-color: theme("colors.true-gray.100");
|
||||
// Border color
|
||||
--divider-color: theme("colors.dark.800");
|
||||
--divider-color: theme("colors.true-gray.800");
|
||||
// Light Border color
|
||||
--divider-light-color: theme("colors.dark.700");
|
||||
// Dark Border color
|
||||
--divider-dark-color: theme("colors.dark.600");
|
||||
--divider-dark-color: theme("colors.dark.300");
|
||||
// Error color
|
||||
--error-color: theme("colors.dark.800");
|
||||
// Tooltip color
|
||||
--tooltip-color: theme("colors.dark.500");
|
||||
--tooltip-color: theme("colors.true-gray.100");
|
||||
// Editor theme
|
||||
--editor-theme: "vibrant_ink";
|
||||
}
|
||||
|
||||
@mixin blueTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.blue.400");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.blue.200");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.blue.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
--editor-theme: "twilight";
|
||||
}
|
||||
|
||||
@mixin greenTheme {
|
||||
// Accent color
|
||||
--accent-color: rgb(97, 207, 123);
|
||||
--accent-color: theme("colors.green.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: rgba(73, 204, 104, 1);
|
||||
--accent-light-color: theme("colors.green.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: rgb(0, 71, 17);
|
||||
--accent-dark-color: theme("colors.green.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.green.200");
|
||||
// Gradient via
|
||||
@@ -117,12 +102,27 @@
|
||||
|
||||
@mixin tealTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.teal.400");
|
||||
--accent-color: theme("colors.teal.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.teal.200");
|
||||
--accent-light-color: theme("colors.teal.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.teal.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.teal.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.teal.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.teal.600");
|
||||
}
|
||||
|
||||
@mixin blueTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.blue.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.blue.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.blue.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
@@ -132,92 +132,92 @@
|
||||
|
||||
@mixin indigoTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.indigo.400");
|
||||
--accent-color: theme("colors.indigo.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.indigo.200");
|
||||
--accent-light-color: theme("colors.indigo.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.indigo.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
--gradient-from-color: theme("colors.indigo.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
--gradient-via-color: theme("colors.indigo.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
--gradient-to-color: theme("colors.indigo.600");
|
||||
}
|
||||
|
||||
@mixin purpleTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.purple.400");
|
||||
--accent-color: theme("colors.purple.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.purple.200");
|
||||
--accent-light-color: theme("colors.purple.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.purple.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
--gradient-from-color: theme("colors.purple.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
--gradient-via-color: theme("colors.purple.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
}
|
||||
|
||||
@mixin orangeTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.orange.400");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.orange.200");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.orange.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
}
|
||||
|
||||
@mixin pinkTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.pink.400");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.pink.200");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.pink.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
}
|
||||
|
||||
@mixin redTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.red.400");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.red.200");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.red.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
--gradient-to-color: theme("colors.purple.600");
|
||||
}
|
||||
|
||||
@mixin yellowTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.yellow.400");
|
||||
--accent-color: theme("colors.yellow.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.yellow.200");
|
||||
--accent-light-color: theme("colors.yellow.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.yellow.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.blue.200");
|
||||
--gradient-from-color: theme("colors.yellow.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.blue.400");
|
||||
--gradient-via-color: theme("colors.yellow.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.blue.600");
|
||||
--gradient-to-color: theme("colors.yellow.600");
|
||||
}
|
||||
|
||||
@mixin orangeTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.orange.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.orange.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.orange.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.orange.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.orange.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.orange.600");
|
||||
}
|
||||
|
||||
@mixin redTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.red.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.red.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.red.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.red.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.red.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.red.600");
|
||||
}
|
||||
|
||||
@mixin pinkTheme {
|
||||
// Accent color
|
||||
--accent-color: theme("colors.pink.500");
|
||||
// Light Accent color
|
||||
--accent-light-color: theme("colors.pink.400");
|
||||
// Dark Accent color
|
||||
--accent-dark-color: theme("colors.pink.600");
|
||||
// Gradient from
|
||||
--gradient-from-color: theme("colors.pink.200");
|
||||
// Gradient via
|
||||
--gradient-via-color: theme("colors.pink.400");
|
||||
// Gradient to
|
||||
--gradient-to-color: theme("colors.pink.600");
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -241,27 +241,35 @@
|
||||
:root[data-accent="blue"] {
|
||||
@include blueTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="green"] {
|
||||
@include greenTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="teal"] {
|
||||
@include tealTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="indigo"] {
|
||||
@include indigoTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="purple"] {
|
||||
@include purpleTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="orange"] {
|
||||
@include orangeTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="pink"] {
|
||||
@include pinkTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="red"] {
|
||||
@include redTheme;
|
||||
}
|
||||
|
||||
:root[data-accent="yellow"] {
|
||||
@include yellowTheme;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div class="flex bg-primary border-b justify-between border-dividerLight">
|
||||
<div class="bg-dividerLight border-b border-divider flex justify-between">
|
||||
<span class="flex">
|
||||
<SmartLink
|
||||
to="https://forms.gle/8yFiEynXB7h477Ns6"
|
||||
blank
|
||||
class="
|
||||
relative
|
||||
flex
|
||||
py-2
|
||||
px-4
|
||||
transition
|
||||
relative
|
||||
items-center
|
||||
justify-center
|
||||
px-4
|
||||
py-3
|
||||
transition
|
||||
group
|
||||
"
|
||||
>
|
||||
<i class="material-icons mr-4">science</i>
|
||||
<span class="text-secondaryDark text-xs">
|
||||
<i class="mr-4 material-icons">science</i>
|
||||
<span class="text-secondaryDark">
|
||||
<span class="md:hidden"> Beta Layout </span>
|
||||
<span class="hidden md:inline">
|
||||
You're currently viewing an experimental beta layout
|
||||
@@ -24,17 +24,16 @@
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
border-l border-divider
|
||||
flex
|
||||
font-semibold
|
||||
text-accent
|
||||
ml-4
|
||||
pl-4
|
||||
transition
|
||||
items-center
|
||||
justify-center
|
||||
pl-4
|
||||
ml-4
|
||||
font-semibold
|
||||
transition
|
||||
border-l
|
||||
group-hover:text-accentDark
|
||||
border-divider
|
||||
text-accent text-xs
|
||||
"
|
||||
>
|
||||
<span class="md:hidden"> Give Feedback </span>
|
||||
@@ -43,17 +42,13 @@
|
||||
</SmartLink>
|
||||
<SmartLink
|
||||
to="https://hoppscotch.io"
|
||||
class="flex items-center justify-center transition group"
|
||||
class="flex transition items-center justify-center group"
|
||||
>
|
||||
<span class="text-secondaryDark text-xs">
|
||||
<span class="text-secondaryDark">
|
||||
Switch back to the Hoppscotch website
|
||||
</span>
|
||||
</SmartLink>
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="close"
|
||||
:title="$t('close')"
|
||||
/>
|
||||
<ButtonSecondary icon="close" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("extensions") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2 space-y-2">
|
||||
<SmartItem
|
||||
to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
|
||||
blank
|
||||
svg="firefox"
|
||||
label="Firefox"
|
||||
:info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
|
||||
/>
|
||||
<SmartItem
|
||||
to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
|
||||
blank
|
||||
svg="chrome"
|
||||
label="Chrome"
|
||||
:info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="px-2 text-secondaryLight text-xs">
|
||||
{{ $t("extensions_info1") }}
|
||||
</div>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
hasChromeExtensionInstalled,
|
||||
hasFirefoxExtensionInstalled,
|
||||
} from "~/helpers/strategies/ExtensionStrategy"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChromeExtInstalled: hasChromeExtensionInstalled(),
|
||||
hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show() {
|
||||
this.hasChromeExtInstalled = hasChromeExtensionInstalled()
|
||||
this.hasFirefoxExtInstalled = hasFirefoxExtensionInstalled()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
110
components/app/Footer.vue
Normal file
110
components/app/Footer.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="LEFT_SIDEBAR ? $t('hide.sidebar') : $t('show.sidebar')"
|
||||
icon="menu_open"
|
||||
:class="{ 'transform rotate-180': !LEFT_SIDEBAR }"
|
||||
@click.native="toggleSetting('LEFT_SIDEBAR')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="`${
|
||||
ZEN_MODE ? $t('action.turn_off') : $t('action.turn_on')
|
||||
} ${$t('layout.zen_mode')}`"
|
||||
:icon="ZEN_MODE ? 'fullscreen_exit' : 'fullscreen'"
|
||||
:class="{
|
||||
'!text-accent focus:text-accent hover:text-accent': ZEN_MODE,
|
||||
}"
|
||||
@click.native="toggleSetting('ZEN_MODE')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="keyboard"
|
||||
:title="$t('shortcuts')"
|
||||
:shortcut="['?']"
|
||||
@click.native="showShortcuts = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="navigatorShare"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="share"
|
||||
:title="$t('request.share')"
|
||||
@click.native="nativeShare()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="RIGHT_SIDEBAR ? $t('hide.sidebar') : $t('show.sidebar')"
|
||||
icon="menu_open"
|
||||
:class="['transform rotate-180', { 'rotate-0': !RIGHT_SIDEBAR }]"
|
||||
@click.native="toggleSetting('RIGHT_SIDEBAR')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
defaultSettings,
|
||||
getSettingSubject,
|
||||
applySetting,
|
||||
toggleSetting,
|
||||
} from "~/newstore/settings"
|
||||
import type { KeysMatching } from "~/types/ts-utils"
|
||||
|
||||
type SettingsType = typeof defaultSettings
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
LEFT_SIDEBAR: null,
|
||||
RIGHT_SIDEBAR: null,
|
||||
ZEN_MODE: null,
|
||||
showShortcuts: false,
|
||||
navigatorShare: navigator.share,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
LEFT_SIDEBAR: getSettingSubject("LEFT_SIDEBAR"),
|
||||
RIGHT_SIDEBAR: getSettingSubject("RIGHT_SIDEBAR"),
|
||||
ZEN_MODE: getSettingSubject("ZEN_MODE"),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
ZEN_MODE(ZEN_MODE) {
|
||||
this.applySetting("LEFT_SIDEBAR", !ZEN_MODE)
|
||||
// this.applySetting("RIGHT_SIDEBAR", !ZEN_MODE)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleSetting<K extends KeysMatching<SettingsType, boolean>>(key: K) {
|
||||
toggleSetting(key)
|
||||
},
|
||||
applySetting<K extends keyof SettingsType>(key: K, value: SettingsType[K]) {
|
||||
applySetting(key, value)
|
||||
},
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text: "Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://hoppscotch.io",
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error)
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,21 +1,30 @@
|
||||
<template>
|
||||
<header class="flex items-center justify-between p-2 flex-1">
|
||||
<div class="inline-flex space-x-2 items-center font-bold flex-shrink-0">
|
||||
<AppLogo class="h-6 mx-4" /> Hoppscotch
|
||||
<header class="flex flex-1 py-2 px-4 items-center justify-between">
|
||||
<div
|
||||
class="
|
||||
font-extrabold
|
||||
space-x-2
|
||||
flex-shrink-0
|
||||
text-sm
|
||||
inline-flex
|
||||
items-center
|
||||
"
|
||||
>
|
||||
<AppLogo />
|
||||
</div>
|
||||
<div class="inline-flex space-x-2 items-center flex-shrink-0">
|
||||
<div class="space-x-2 flex-shrink-0 inline-flex items-center">
|
||||
<AppGitHubStarButton class="mt-1 mr-2" />
|
||||
<TabPrimary
|
||||
id="installPWA"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('install_pwa')"
|
||||
:title="$t('header.install_pwa')"
|
||||
icon="offline_bolt"
|
||||
@click.native="showInstallPrompt()"
|
||||
/>
|
||||
<span tabindex="-1">
|
||||
<ButtonPrimary
|
||||
v-if="currentUser === null"
|
||||
label="Get Started"
|
||||
label="Login"
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
<tippy
|
||||
@@ -34,11 +43,11 @@
|
||||
:url="currentUser.photoURL"
|
||||
:alt="currentUser.displayName"
|
||||
:title="
|
||||
(currentUser.displayName ||
|
||||
'<label><i>Name not found</i></label>') +
|
||||
`${currentUser.displayName || 'Name not found'}` +
|
||||
'<br>' +
|
||||
(currentUser.email || '<label><i>Email not found</i></label>')
|
||||
`<sub>${currentUser.email || 'Email not found'}</sub>`
|
||||
"
|
||||
:indicator="isOnLine ? 'bg-green-500' : 'bg-red-500'"
|
||||
/>
|
||||
<TabPrimary
|
||||
v-else
|
||||
@@ -47,12 +56,6 @@
|
||||
icon="account_circle"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
to="/profile"
|
||||
icon="person"
|
||||
:label="$t('profile')"
|
||||
@click.native="$refs.user.tippy().hide()"
|
||||
/>
|
||||
<SmartItem
|
||||
to="/settings"
|
||||
icon="settings"
|
||||
@@ -62,56 +65,8 @@
|
||||
<FirebaseLogout @confirm-logout="$refs.user.tippy().hide()" />
|
||||
</tippy>
|
||||
</span>
|
||||
<span tabindex="-1">
|
||||
<tippy
|
||||
ref="options"
|
||||
interactive
|
||||
tabindex="-1"
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<TabPrimary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('more')"
|
||||
icon="drag_indicator"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="extension"
|
||||
:label="$t('extensions')"
|
||||
@click.native="
|
||||
showExtensions = true
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
icon="keyboard"
|
||||
:label="$t('shortcuts')"
|
||||
@click.native="
|
||||
showShortcuts = true
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
v-if="navigatorShare"
|
||||
icon="share"
|
||||
:label="$t('share')"
|
||||
@click.native="
|
||||
nativeShare()
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||
<AppExtensions
|
||||
:show="showExtensions"
|
||||
@hide-modal="showExtensions = false"
|
||||
/>
|
||||
<AppShortcuts :show="showShortcuts" @hide-modal="showShortcuts = false" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
@@ -119,7 +74,6 @@
|
||||
import intializePwa from "~/helpers/pwa"
|
||||
import { currentUser$ } from "~/helpers/fb/auth"
|
||||
import { getLocalConfig, setLocalConfig } from "~/newstore/localpersistence"
|
||||
// import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -129,9 +83,7 @@ export default {
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
showLogin: false,
|
||||
showExtensions: false,
|
||||
showShortcuts: false,
|
||||
navigatorShare: navigator.share,
|
||||
isOnLine: navigator.onLine,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
@@ -140,6 +92,13 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
window.addEventListener("online", () => {
|
||||
this.isOnLine = true
|
||||
})
|
||||
window.addEventListener("offline", () => {
|
||||
this.isOnLine = false
|
||||
})
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
// etc.
|
||||
this.showInstallPrompt = await intializePwa()
|
||||
@@ -151,7 +110,7 @@ export default {
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: this.$t("dismiss"),
|
||||
text: this.$t("action.dismiss"),
|
||||
onClick: (_, toastObject) => {
|
||||
setLocalConfig("cookiesAllowed", "yes")
|
||||
toastObject.goAway(0)
|
||||
@@ -161,21 +120,5 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text: "Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://hoppscotch.io",
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error)
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
<template>
|
||||
<svg
|
||||
class="logo fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 612 612"
|
||||
>
|
||||
<g xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.283 208.326c-28.015 71.685 84.358 182.589 250.992 247.71 166.634 65.121 324.428 59.801 352.442-11.884 17.432-44.606-19.496-104.396-90.027-158.893-.489 22.675-4.952 44.944-13.309 66.328-3.559 9.108-11.697 21.519-30.254 28.923-10.853 4.33-24.392 6.526-40.242 6.526-40.758 0-99.372-14.662-156.792-39.22-27.484-11.755-52.967-25.125-73.694-38.665-23.871-15.594-41.229-31.196-51.592-46.373-15.399-22.551-13.289-40.954-8.807-52.421 8.368-21.412 20.275-40.884 35.185-57.777-88.789-7.765-156.47 11.143-173.902 55.746zm542.506 203.868c7.682-5.648 19.708-2.339 26.861 7.39 7.153 9.729 6.724 22.194-.958 27.842s-19.709 2.339-26.861-7.39c-7.153-9.729-6.724-22.194.958-27.842zm-297.979-5.647c3.842-9.832 16.98-13.886 29.344-9.054 12.363 4.832 19.271 16.719 15.428 26.552-3.842 9.832-16.98 13.887-29.344 9.054-12.363-4.832-19.269-16.72-15.428-26.552zM51.312 246.776c-11.854 2.301-22.937-3.422-24.754-12.782-1.817-9.361 6.321-18.813 18.174-21.114 11.854-2.301 22.937 3.422 24.754 12.782 1.817 9.36-6.319 18.813-18.174 21.114z"
|
||||
/>
|
||||
<path
|
||||
d="M433.885 363.563c24.904 0 42.999-6.106 48.633-20.52 9.669-24.741 13.162-50.371 11.229-75.19-4.919-63.176-45.008-121.096-107.987-145.708-20.393-7.97-41.378-11.745-62.027-11.744-43.118.003-84.718 16.481-116.132 45.623-18.252 16.932-33.069 38.136-42.738 62.878-21.567 55.188 173.67 144.661 269.022 144.661z"
|
||||
/>
|
||||
</g>
|
||||
<svg class="logo" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
|
||||
<circle class="fill-current" r="8" cx="50%" cy="50%" />
|
||||
<circle class="fill-primary" r="6" cx="50%" cy="50%" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section :id="label.toLowerCase()">
|
||||
<section :id="label.toLowerCase()" class="flex flex-col flex-1">
|
||||
<slot></slot>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -1,44 +1,76 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("shortcuts") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2">
|
||||
<AppSlideOver :show="show" @close="close()">
|
||||
<template #content>
|
||||
<div
|
||||
v-for="(shortcut, index) in shortcuts"
|
||||
:key="`shortcut-${index}`"
|
||||
class="flex items-center"
|
||||
>
|
||||
<kbd
|
||||
v-for="(key, keyIndex) in shortcut.keys"
|
||||
:key="`shortcut-${index}-key-${keyIndex}`"
|
||||
class="
|
||||
py-2
|
||||
px-4
|
||||
m-1
|
||||
text-xs
|
||||
border border-divider
|
||||
rounded-lg
|
||||
font-bold
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
p-2
|
||||
top-0
|
||||
z-10
|
||||
items-center
|
||||
sticky
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
{{ key }}
|
||||
</kbd>
|
||||
<span class="flex text-xs ml-4">
|
||||
<h3 class="ml-4 heading">{{ $t("shortcuts") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary to="/settings" icon="tune" />
|
||||
<ButtonSecondary icon="close" @click.native="close()" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="search-wrapper">
|
||||
<input
|
||||
v-model="filterText"
|
||||
type="search"
|
||||
class="bg-primaryLight border-b border-dividerLight flex font-semibold font-mono w-full py-2 pr-2 pl-8 focus:outline-none truncate"
|
||||
:placeholder="$t('search')"
|
||||
/>
|
||||
</div> -->
|
||||
<div
|
||||
class="
|
||||
divide-y divide-dividerLight
|
||||
flex flex-col flex-1
|
||||
overflow-auto
|
||||
hide-scrollbar
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(map, mapIndex) in mappings"
|
||||
:key="`map-${mapIndex}`"
|
||||
class="space-y-4 py-4 px-6"
|
||||
>
|
||||
<h5 class="font-bold text-secondaryDark text-sm">
|
||||
{{ map.section }}
|
||||
</h5>
|
||||
<div
|
||||
v-for="(shortcut, shortcutIndex) in map.shortcuts"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}`"
|
||||
class="flex items-center"
|
||||
>
|
||||
<span class="flex flex-1 text-secondaryLight mr-4">
|
||||
{{ shortcut.label }}
|
||||
</span>
|
||||
<span
|
||||
v-for="(key, keyIndex) in shortcut.keys"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}-key-${keyIndex}`"
|
||||
class="shortcut-key"
|
||||
>
|
||||
{{ key }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</AppSlideOver>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
import {
|
||||
getPlatformSpecialKey,
|
||||
getPlatformAlternateKey,
|
||||
} from "~/helpers/platformutils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -46,59 +78,85 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterText: "",
|
||||
mappings: [
|
||||
{
|
||||
section: "General",
|
||||
shortcuts: [
|
||||
{
|
||||
keys: [this.getSpecialKey(), "G"],
|
||||
label: this.$t("send_request"),
|
||||
keys: [getPlatformSpecialKey(), "G"],
|
||||
label: this.$t("shortcut.send_request"),
|
||||
},
|
||||
{
|
||||
keys: [this.getSpecialKey(), "S"],
|
||||
label: this.$t("save_to_collections"),
|
||||
keys: [getPlatformSpecialKey(), "S"],
|
||||
label: this.$t("shortcut.save_to_collections"),
|
||||
},
|
||||
{
|
||||
keys: [this.getSpecialKey(), "K"],
|
||||
label: this.$t("copy_request_link"),
|
||||
keys: [getPlatformSpecialKey(), "K"],
|
||||
label: this.$t("shortcut.copy_request_link"),
|
||||
},
|
||||
{
|
||||
keys: [this.getSpecialKey(), "I"],
|
||||
label: this.$t("reset_request"),
|
||||
keys: [getPlatformSpecialKey(), "I"],
|
||||
label: this.$t("shortcut.reset_request"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "▲"],
|
||||
label: this.$t("select_next_method"),
|
||||
section: "Request",
|
||||
shortcuts: [
|
||||
{
|
||||
keys: [getPlatformAlternateKey(), "↑"],
|
||||
label: this.$t("shortcut.next_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "▼"],
|
||||
label: this.$t("select_previous_method"),
|
||||
keys: [getPlatformAlternateKey(), "↓"],
|
||||
label: this.$t("shortcut.previous_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "G"],
|
||||
label: this.$t("select_get_method"),
|
||||
keys: [getPlatformAlternateKey(), "G"],
|
||||
label: this.$t("shortcut.get_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "H"],
|
||||
label: this.$t("select_head_method"),
|
||||
keys: [getPlatformAlternateKey(), "H"],
|
||||
label: this.$t("shortcut.head_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "P"],
|
||||
label: this.$t("select_post_method"),
|
||||
keys: [getPlatformAlternateKey(), "P"],
|
||||
label: this.$t("shortcut.post_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "U"],
|
||||
label: this.$t("select_put_method"),
|
||||
keys: [getPlatformAlternateKey(), "U"],
|
||||
label: this.$t("shortcut.put_method"),
|
||||
},
|
||||
{
|
||||
keys: ["Alt", "X"],
|
||||
label: this.$t("select_delete_method"),
|
||||
keys: [getPlatformAlternateKey(), "X"],
|
||||
label: this.$t("shortcut.delete_method"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getSpecialKey: getPlatformSpecialKey,
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
close() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-key {
|
||||
@apply bg-dividerLight;
|
||||
@apply rounded;
|
||||
@apply ml-2;
|
||||
@apply py-1;
|
||||
@apply px-2;
|
||||
@apply inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
:to="localePath(navigation.target)"
|
||||
class="nav-link"
|
||||
>
|
||||
<i class="material-icons">{{ navigation.icon }}</i>
|
||||
<i v-if="navigation.icon" class="material-icons">
|
||||
{{ navigation.icon }}
|
||||
</i>
|
||||
<div v-if="navigation.svg" class="h-4 w-4">
|
||||
<SmartIcon :name="navigation.svg" class="svg-icons" />
|
||||
</div>
|
||||
<span>{{ navigation.title }}</span>
|
||||
</nuxt-link>
|
||||
</nav>
|
||||
@@ -19,12 +24,31 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
primaryNavigation: [
|
||||
{ target: "index", icon: "home", title: "Home" },
|
||||
{ target: "realtime", icon: "language", title: "Realtime" },
|
||||
{ target: "graphql", icon: "code", title: "GraphQL" },
|
||||
{ target: "doc", icon: "book", title: "Docs" },
|
||||
{ target: "profile", icon: "person", title: "Profile" },
|
||||
{ target: "settings", icon: "settings", title: "Settings" },
|
||||
{
|
||||
target: "index",
|
||||
icon: "settings_ethernet",
|
||||
title: this.$t("navigation.rest"),
|
||||
},
|
||||
{
|
||||
target: "graphql",
|
||||
svg: "graphql",
|
||||
title: this.$t("navigation.graphql"),
|
||||
},
|
||||
{
|
||||
target: "realtime",
|
||||
icon: "language",
|
||||
title: this.$t("navigation.realtime"),
|
||||
},
|
||||
{
|
||||
target: "documentation",
|
||||
icon: "book",
|
||||
title: this.$t("navigation.doc"),
|
||||
},
|
||||
{
|
||||
target: "settings",
|
||||
icon: "settings",
|
||||
title: this.$t("navigation.settings"),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
@@ -34,22 +58,20 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.nav-link {
|
||||
@apply p-4;
|
||||
@apply flex-col;
|
||||
@apply flex-1;
|
||||
@apply hover:bg-primaryDark;
|
||||
@apply hover:text-secondaryDark;
|
||||
@apply flex flex-col flex-1;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@apply transition;
|
||||
@apply hover:bg-primaryDark;
|
||||
@apply hover:text-secondaryDark;
|
||||
|
||||
.material-icons {
|
||||
@apply transition-opacity;
|
||||
@apply opacity-50;
|
||||
.material-icons,
|
||||
.svg-icons {
|
||||
@apply opacity-75;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply mt-2;
|
||||
@apply text-xs;
|
||||
@apply font-semibold;
|
||||
}
|
||||
|
||||
@@ -57,7 +79,8 @@ export default {
|
||||
@apply text-accent;
|
||||
@apply hover:text-accent;
|
||||
|
||||
.material-icons {
|
||||
.material-icons,
|
||||
.svg-icons {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
|
||||
68
components/app/SlideOver.vue
Normal file
68
components/app/SlideOver.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<transition v-if="show" name="fade" appear>
|
||||
<div class="inset-0 transition-opacity z-20 fixed" @keydown.esc="close()">
|
||||
<div
|
||||
class="bg-primaryDark opacity-75 inset-0 absolute"
|
||||
tabindex="0"
|
||||
@click="close()"
|
||||
></div>
|
||||
</div>
|
||||
</transition>
|
||||
<aside
|
||||
class="
|
||||
bg-primary
|
||||
flex flex-col
|
||||
h-full
|
||||
max-w-full
|
||||
shadow-xl
|
||||
transform
|
||||
transition
|
||||
top-0
|
||||
ease-in-out
|
||||
right-0
|
||||
w-96
|
||||
z-30
|
||||
duration-300
|
||||
fixed
|
||||
overflow-auto
|
||||
"
|
||||
:class="show ? 'translate-x-0' : 'translate-x-full'"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</aside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
immediate: true,
|
||||
handler(show) {
|
||||
if (process.client) {
|
||||
if (show) document.body.style.setProperty("overflow", "hidden")
|
||||
else document.body.style.removeProperty("overflow")
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode === 27 && this.show) this.close()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -4,28 +4,32 @@
|
||||
:exact="exact"
|
||||
:blank="blank"
|
||||
class="
|
||||
font-bold
|
||||
py-2
|
||||
transition
|
||||
inline-flex
|
||||
items-center
|
||||
justify-center
|
||||
py-2
|
||||
font-semibold
|
||||
transition
|
||||
focus:outline-none
|
||||
"
|
||||
:class="[
|
||||
color
|
||||
? `text-${color}-800 bg-${color}-200 hover:text-${color}-900 hover:bg-${color}-300 focus:text-${color}-900 focus:bg-${color}-300`
|
||||
: `text-white dark:text-accentDark bg-accent hover:bg-accentDark focus:bg-accentDark`,
|
||||
: `text-primary bg-accent hover:bg-accentDark focus:bg-accentDark`,
|
||||
label ? 'px-4' : 'px-2',
|
||||
rounded ? 'rounded-full' : 'rounded-lg',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
rounded ? 'rounded-full' : 'rounded',
|
||||
{ 'opacity-75 cursor-not-allowed': disabled },
|
||||
{ 'pointer-events-none': loading },
|
||||
{ 'px-4 py-4 text-lg': large },
|
||||
{ 'px-6 py-4 text-lg': large },
|
||||
{ 'shadow-lg hover:shadow-xl': shadow },
|
||||
{
|
||||
'text-white bg-gradient-to-tr from-gradientFrom via-gradientVia to-gradientTo':
|
||||
gradient,
|
||||
},
|
||||
{
|
||||
'border border-accent hover:border-accentDark focus:border-accentDark':
|
||||
outline,
|
||||
},
|
||||
]"
|
||||
:disabled="disabled"
|
||||
:tabindex="loading ? '-1' : '0'"
|
||||
@@ -37,27 +41,41 @@
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="material-icons"
|
||||
:class="[
|
||||
'material-icons',
|
||||
{ '!text-2xl': large },
|
||||
label ? (reverse ? 'ml-2' : 'mr-2') : '',
|
||||
]"
|
||||
>
|
||||
{{ icon }}
|
||||
</i>
|
||||
<SmartIcon
|
||||
v-if="svg"
|
||||
:name="svg"
|
||||
:class="label ? (reverse ? 'ml-4' : 'mr-4') : ''"
|
||||
class="svg-icons"
|
||||
:class="[
|
||||
'svg-icons',
|
||||
{ '!h-6 !w-6': large },
|
||||
label ? (reverse ? 'ml-2' : 'mr-2') : '',
|
||||
]"
|
||||
/>
|
||||
{{ label }}
|
||||
<span v-if="shortkey" class="px-1 ml-2 rounded bg-accentLight">{{
|
||||
shortkey
|
||||
}}</span>
|
||||
<div v-if="shortcut.length && SHORTCUT_INDICATOR" class="ml-2">
|
||||
<kbd
|
||||
v-for="(key, index) in shortcut"
|
||||
:key="`key-${index}`"
|
||||
class="bg-accentLight rounded ml-1 px-1 inline-flex"
|
||||
>
|
||||
{{ key }}
|
||||
</kbd>
|
||||
</div>
|
||||
</span>
|
||||
<SmartSpinner v-else />
|
||||
</SmartLink>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
@@ -116,10 +134,24 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shortkey: {
|
||||
type: String,
|
||||
default: "",
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shortcut: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
SHORTCUT_INDICATOR: null,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SHORTCUT_INDICATOR: getSettingSubject("SHORTCUT_INDICATOR"),
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,22 +4,24 @@
|
||||
:exact="exact"
|
||||
:blank="blank"
|
||||
class="
|
||||
font-semibold
|
||||
py-2
|
||||
transition
|
||||
inline-flex
|
||||
items-center
|
||||
justify-center
|
||||
py-2
|
||||
font-semibold
|
||||
transition
|
||||
focus:outline-none
|
||||
hover:bg-primaryDark
|
||||
"
|
||||
:class="[
|
||||
color
|
||||
? `text-${color}-400 hover:text-${color}-600 focus:text-${color}-600`
|
||||
? `text-${color}-500 hover:text-${color}-600 focus:text-${color}-600`
|
||||
: 'text-secondary hover:text-secondaryDark focus:text-secondaryDark',
|
||||
label ? 'px-3 rounded-lg' : 'px-2 rounded-full',
|
||||
rounded ? 'rounded-full' : 'rounded-lg',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
label ? 'px-4' : 'px-2',
|
||||
rounded ? 'rounded-full' : 'rounded',
|
||||
{ 'opacity-75 cursor-not-allowed': disabled },
|
||||
{ 'flex-row-reverse': reverse },
|
||||
{ 'px-6 py-4 text-lg': large },
|
||||
{
|
||||
'border border-divider hover:border-dividerDark focus:border-dividerDark':
|
||||
outline,
|
||||
@@ -29,22 +31,46 @@
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="material-icons"
|
||||
:class="[
|
||||
'material-icons',
|
||||
{ '!text-2xl': large },
|
||||
label ? (reverse ? 'ml-2' : 'mr-2') : '',
|
||||
]"
|
||||
>
|
||||
{{ icon }}
|
||||
</i>
|
||||
<SmartIcon
|
||||
v-if="svg"
|
||||
:name="svg"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="svg-icons"
|
||||
:class="[
|
||||
'svg-icons',
|
||||
{ '!h-6 !w-6': large },
|
||||
label ? (reverse ? 'ml-2' : 'mr-2') : '',
|
||||
]"
|
||||
/>
|
||||
{{ label }}
|
||||
<div v-if="shortcut.length && SHORTCUT_INDICATOR" class="ml-2">
|
||||
<kbd
|
||||
v-for="(key, index) in shortcut"
|
||||
:key="`key-${index}`"
|
||||
class="
|
||||
bg-dividerLight
|
||||
rounded
|
||||
text-secondaryLight
|
||||
ml-1
|
||||
px-1
|
||||
inline-flex
|
||||
"
|
||||
>
|
||||
{{ key }}
|
||||
</kbd>
|
||||
</div>
|
||||
</SmartLink>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
@@ -87,10 +113,28 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
large: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shortcut: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
SHORTCUT_INDICATOR: null,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
SHORTCUT_INDICATOR: getSettingSubject("SHORTCUT_INDICATOR"),
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("new_collection") }}</h3>
|
||||
<h3 class="heading">{{ $t("collection.new") }}</h3>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelAdd" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelAdd" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -14,7 +14,7 @@
|
||||
v-model="name"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_collection')"
|
||||
:placeholder="$t('collection.name')"
|
||||
@keyup.enter="addNewCollection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label
|
||||
for="selectLabelAddFolder"
|
||||
class="px-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelAddFolder" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -19,7 +16,7 @@
|
||||
v-model="name"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_folder')"
|
||||
:placeholder="$t('folder.new')"
|
||||
@keyup.enter="addFolder"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
type="text"
|
||||
autofocus
|
||||
class="
|
||||
flex
|
||||
w-full
|
||||
px-4
|
||||
text-xs
|
||||
py-3
|
||||
focus:outline-none
|
||||
border-b border-dividerLight
|
||||
font-medium
|
||||
bg-primaryLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
font-medium
|
||||
w-full
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
appearance-none
|
||||
"
|
||||
@change="updateSelectedTeam(myTeams[$event.target.value])"
|
||||
>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("edit_collection") }}</h3>
|
||||
<h3 class="heading">{{ $t("collection.edit") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelEdit" class="px-4 font-semibold pb-4 text-xs">{{
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelEdit" class="font-semibold px-4 pb-4">{{
|
||||
$t("label")
|
||||
}}</label>
|
||||
<input
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("edit_folder") }}</h3>
|
||||
<h3 class="heading">{{ $t("folder.edit") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label
|
||||
for="selectLabelEditFolder"
|
||||
class="px-4 font-semibold pb-4 text-xs"
|
||||
>{{ $t("label") }}</label
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelEditFolder" class="font-semibold px-4 pb-4">{{
|
||||
$t("label")
|
||||
}}</label>
|
||||
<input
|
||||
id="selectLabelEditFolder"
|
||||
v-model="name"
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelEditReq" class="px-4 font-semibold pb-4 text-xs">
|
||||
{{ $t("label") }}</label
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelEditReq" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
id="selectLabelEditReq"
|
||||
v-model="requestUpdateData.name"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="assignment_returned"
|
||||
:label="$t('import_from_gist')"
|
||||
:label="$t('import.from_gist')"
|
||||
@click.native="
|
||||
readCollectionGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -43,9 +43,9 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
!currentUser
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: currentUser.provider !== 'github.com'
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: null
|
||||
"
|
||||
:disabled="
|
||||
@@ -56,7 +56,7 @@
|
||||
: false
|
||||
"
|
||||
icon="assignment_turned_in"
|
||||
:label="$t('create_secret_gist')"
|
||||
:label="$t('export.create_secret_gist')"
|
||||
@click.native="
|
||||
createCollectionGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -87,7 +87,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('preserve_current')"
|
||||
icon="create_new_folder"
|
||||
:label="$t('import_json')"
|
||||
:label="$t('import.json')"
|
||||
@click.native="openDialogChooseFileToImportFrom"
|
||||
/>
|
||||
<input
|
||||
@@ -103,20 +103,20 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('preserve_current')"
|
||||
icon="folder_shared"
|
||||
:label="$t('import_from_my_collections')"
|
||||
:label="$t('import.from_my_collections')"
|
||||
@click.native="mode = 'import_from_my_collections'"
|
||||
/>
|
||||
<SmartItem
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('download_file')"
|
||||
icon="drive_file_move"
|
||||
:label="$t('export_as_json')"
|
||||
:label="$t('export.as_json')"
|
||||
@click.native="exportJSON"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="mode == 'import_from_my_collections'"
|
||||
class="flex px-2 flex-col"
|
||||
class="flex flex-col px-2"
|
||||
>
|
||||
<div class="select-wrapper">
|
||||
<select
|
||||
@@ -155,7 +155,7 @@
|
||||
<ButtonPrimary
|
||||
:disabled="mySelectedCollectionID == undefined"
|
||||
icon="create_new_folder"
|
||||
:label="$t('import')"
|
||||
:label="$t('import.title')"
|
||||
@click.native="importFromMyCollections"
|
||||
/>
|
||||
</span>
|
||||
@@ -213,7 +213,7 @@ export default {
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
this.$toast.success(this.$t("export.gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(res.html_url)
|
||||
@@ -226,7 +226,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readCollectionGist() {
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("import.gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -390,22 +390,22 @@ export default {
|
||||
},
|
||||
exportJSON() {
|
||||
this.getJSONCollection()
|
||||
let text = this.collectionJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-collection.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
anchor.style.display = "none"
|
||||
document.body.appendChild(anchor)
|
||||
anchor.click()
|
||||
document.body.removeChild(anchor)
|
||||
const dataToWrite = this.collectionJson
|
||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
}, 1000)
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
@@ -413,7 +413,7 @@ export default {
|
||||
})
|
||||
},
|
||||
failedImport() {
|
||||
this.$toast.error(this.$t("import_failed"), {
|
||||
this.$toast.error(this.$t("import.failed"), {
|
||||
icon: "error",
|
||||
})
|
||||
},
|
||||
|
||||
@@ -5,20 +5,17 @@
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelSaveReq" class="px-4 font-semibold pb-4 text-xs">
|
||||
{{ $t("token_req_name") }}</label
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelSaveReq" class="font-semibold px-4 pb-4">
|
||||
{{ $t("request_name") }}
|
||||
</label>
|
||||
<input
|
||||
id="selectLabelSaveReq"
|
||||
v-model="requestData.name"
|
||||
v-model="requestName"
|
||||
class="input"
|
||||
type="text"
|
||||
@keyup.enter="saveRequestAs"
|
||||
/>
|
||||
<label class="px-4 pt-4 font-semibold pb-4 text-xs">
|
||||
Select Location
|
||||
</label>
|
||||
<label class="font-semibold px-4 pt-4 pb-4"> Select Location </label>
|
||||
<CollectionsGraphql
|
||||
v-if="mode === 'graphql'"
|
||||
:doc="false"
|
||||
@@ -47,6 +44,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
import {
|
||||
saveRESTRequestAs,
|
||||
@@ -54,20 +52,23 @@ import {
|
||||
editGraphqlRequest,
|
||||
saveGraphqlRequestAs,
|
||||
} from "~/newstore/collections"
|
||||
import { getRESTRequest, useRESTRequestName } from "~/newstore/RESTSession"
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
props: {
|
||||
// mode can be either "graphql" or "rest"
|
||||
mode: { type: String, default: "rest" },
|
||||
show: Boolean,
|
||||
editingRequest: { type: Object, default: () => {} },
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
requestName: useRESTRequestName(),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRequestName: "Untitled Request",
|
||||
path: "Path will appear here",
|
||||
requestData: {
|
||||
name: undefined,
|
||||
name: this.requestName,
|
||||
collectionIndex: undefined,
|
||||
folderName: undefined,
|
||||
requestIndex: undefined,
|
||||
@@ -102,7 +103,7 @@ export default {
|
||||
},
|
||||
saveRequestAs() {
|
||||
if (this.picked == null) {
|
||||
this.$toast.error(this.$t("select_collection"), {
|
||||
this.$toast.error(this.$t("collection.select"), {
|
||||
icon: "error",
|
||||
})
|
||||
return
|
||||
@@ -114,10 +115,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
const requestUpdated = {
|
||||
...this.$props.editingRequest,
|
||||
name: this.$data.requestData.name,
|
||||
}
|
||||
const requestUpdated = getRESTRequest()
|
||||
|
||||
// Filter out all REST file inputs
|
||||
if (this.mode === "rest" && requestUpdated.bodyParams) {
|
||||
@@ -180,5 +178,5 @@ export default {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("new_collection") }}</h3>
|
||||
<h3 class="heading">{{ $t("collection.new") }}</h3>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelGqlAdd" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelGqlAdd" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -14,7 +14,7 @@
|
||||
v-model="name"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_collection')"
|
||||
:placeholder="$t('collection.name')"
|
||||
@keyup.enter="addNewCollection"
|
||||
/>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@ export default Vue.extend({
|
||||
methods: {
|
||||
addNewCollection() {
|
||||
if (!this.name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name").toString())
|
||||
this.$toast.info(this.$t("collection.invalid_name").toString())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label
|
||||
for="selectLabelGqlAddFolder"
|
||||
class="px-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelGqlAddFolder" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -19,7 +16,7 @@
|
||||
v-model="name"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$t('my_new_folder')"
|
||||
:placeholder="$t('folder.new')"
|
||||
@keyup.enter="addFolder"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,32 +10,24 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -44,7 +36,7 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
@@ -69,7 +61,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
path: `${collectionIndex}`,
|
||||
@@ -87,6 +79,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -95,11 +88,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsGraphqlFolder
|
||||
v-for="(folder, index) in collection.folders"
|
||||
:key="`folder-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:picked="picked"
|
||||
:saving-mode="savingMode"
|
||||
:folder="folder"
|
||||
@@ -116,7 +109,7 @@
|
||||
<CollectionsGraphqlRequest
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:picked="picked"
|
||||
:saving-mode="savingMode"
|
||||
:request="request"
|
||||
@@ -134,25 +127,24 @@
|
||||
collection.folders.length === 0 && collection.requests.length === 0
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("collection_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.collection") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_collection')"
|
||||
:title="$t('confirm.remove_collection')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeCollection"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("edit_collection") }}</h3>
|
||||
<h3 class="heading">{{ $t("collection.edit") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelGqlEdit" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelGqlEdit" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -48,7 +48,7 @@ export default Vue.extend({
|
||||
methods: {
|
||||
saveCollection() {
|
||||
if (!this.name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name").toString())
|
||||
this.$toast.info(this.$t("collection.invalid_name").toString())
|
||||
return
|
||||
}
|
||||
const collectionUpdated = {
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("edit_folder") }}</h3>
|
||||
<h3 class="heading">{{ $t("folder.edit") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label
|
||||
for="selectLabelGqlEditFolder"
|
||||
class="px-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelGqlEditFolder" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label
|
||||
for="selectLabelGqlEditReq"
|
||||
class="px-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelGqlEditReq" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
|
||||
@@ -10,32 +10,24 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -46,7 +38,7 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="$emit('add-folder', { folder, path: folderPath })"
|
||||
/>
|
||||
@@ -67,7 +59,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', { folder, path: folderPath })
|
||||
$refs.options.tippy().hide()
|
||||
@@ -83,6 +75,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -91,11 +84,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsGraphqlFolder
|
||||
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||
:key="`subFolder-${subFolderIndex}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:picked="picked"
|
||||
:saving-mode="savingMode"
|
||||
:folder="subFolder"
|
||||
@@ -112,7 +105,7 @@
|
||||
<CollectionsGraphqlRequest
|
||||
v-for="(request, index) in folder.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:picked="picked"
|
||||
:saving-mode="savingMode"
|
||||
:request="request"
|
||||
@@ -133,25 +126,24 @@
|
||||
folder.requests.length === 0
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("folder_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.folder") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_folder')"
|
||||
:title="$t('confirm.remove_folder')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeFolder"
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="assignment_returned"
|
||||
:label="$t('import_from_gist')"
|
||||
:label="$t('import.from_gist')"
|
||||
@click.native="
|
||||
readCollectionGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -30,9 +30,9 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
!currentUser
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: currentUser.provider !== 'github.com'
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: null
|
||||
"
|
||||
:disabled="
|
||||
@@ -43,7 +43,7 @@
|
||||
: false
|
||||
"
|
||||
icon="assignment_turned_in"
|
||||
:label="$t('create_secret_gist')"
|
||||
:label="$t('export.create_secret_gist')"
|
||||
@click.native="
|
||||
createCollectionGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -74,7 +74,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('preserve_current')"
|
||||
icon="create_new_folder"
|
||||
:label="$t('import_json')"
|
||||
:label="$t('import.json')"
|
||||
@click.native="openDialogChooseFileToImportFrom"
|
||||
/>
|
||||
<input
|
||||
@@ -89,7 +89,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('download_file')"
|
||||
icon="drive_file_move"
|
||||
:label="$t('export_as_json')"
|
||||
:label="$t('export.as_json')"
|
||||
@click.native="exportJSON"
|
||||
/>
|
||||
</div>
|
||||
@@ -140,7 +140,7 @@ export default {
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
this.$toast.success(this.$t("export.gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(res.html_url)
|
||||
@@ -153,7 +153,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readCollectionGist() {
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("import.gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -243,22 +243,22 @@ export default {
|
||||
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.collectionJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-collection.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
anchor.style.display = "none"
|
||||
document.body.appendChild(anchor)
|
||||
anchor.click()
|
||||
document.body.removeChild(anchor)
|
||||
const dataToWrite = this.collectionJson
|
||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
}, 1000)
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
@@ -266,7 +266,7 @@ export default {
|
||||
})
|
||||
},
|
||||
failedImport() {
|
||||
this.$toast.error(this.$t("import_failed"), {
|
||||
this.$toast.error(this.$t("import.failed"), {
|
||||
icon: "error",
|
||||
})
|
||||
},
|
||||
|
||||
@@ -10,33 +10,32 @@
|
||||
>
|
||||
<span
|
||||
class="
|
||||
font-mono font-bold
|
||||
cursor-pointer
|
||||
flex
|
||||
font-mono font-bold
|
||||
mx-2
|
||||
w-12
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-12
|
||||
mx-2
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ isSelected ? "check_circle" : "description" }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
>
|
||||
@@ -79,6 +78,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -89,7 +89,7 @@
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_request')"
|
||||
:title="$t('confirm.remove_request')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeRequest"
|
||||
/>
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
<template>
|
||||
<AppSection
|
||||
label="collections"
|
||||
:class="{ 'rounded-lg border-2 border-divider': savingMode }"
|
||||
:class="{ 'rounded border border-divider': savingMode }"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col sticky top-10 z-10"
|
||||
class="flex flex-col top-8 z-10 sticky"
|
||||
:class="{ 'bg-primary': !savingMode }"
|
||||
>
|
||||
<div v-if="showCollActions" class="search-wrapper">
|
||||
<input
|
||||
v-if="showCollActions"
|
||||
v-model="filterText"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
font-medium
|
||||
bg-primaryLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
w-full
|
||||
py-2
|
||||
pr-2
|
||||
pl-9
|
||||
focus:outline-none
|
||||
truncate
|
||||
"
|
||||
/>
|
||||
<div class="border-b flex justify-between flex-1 border-dividerLight">
|
||||
</div>
|
||||
<div class="border-b border-dividerLight flex flex-1 justify-between">
|
||||
<ButtonSecondary
|
||||
icon="add"
|
||||
:label="$t('new')"
|
||||
@@ -32,7 +35,7 @@
|
||||
<ButtonSecondary
|
||||
v-if="showCollActions"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('import_export')"
|
||||
:title="$t('modal.import_export')"
|
||||
icon="import_export"
|
||||
@click.native="displayModalImportExport(true)"
|
||||
/>
|
||||
@@ -59,19 +62,24 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="collections.length === 0"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">create_new_folder</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("create_new_collection") }}
|
||||
<i class="opacity-75 pb-2 material-icons">create_new_folder</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.collections") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
outline
|
||||
@click.native="displayModalAdd(true)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!(filteredCollections.length !== 0 || collections.length === 0)"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">manage_search</i>
|
||||
<span class="text-xs text-center">
|
||||
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||
<span class="text-center">
|
||||
{{ $t("nothing_found") }} "{{ filterText }}"
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
<template>
|
||||
<AppSection
|
||||
label="collections"
|
||||
:class="{ 'rounded-lg border-2 border-divider': saveRequest }"
|
||||
:class="{ 'rounded border border-divider': saveRequest }"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col sticky z-10 top-0 bg-primary"
|
||||
:class="{ '!top-10': !saveRequest && !doc }"
|
||||
class="bg-primary rounded-t flex flex-col top-0 z-10 sticky"
|
||||
:class="{ '!top-8': !saveRequest && !doc }"
|
||||
>
|
||||
<div v-if="!saveRequest" class="search-wrapper">
|
||||
<input
|
||||
v-if="!saveRequest"
|
||||
v-model="filterText"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
font-medium
|
||||
bg-primaryLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
w-full
|
||||
py-2
|
||||
pr-2
|
||||
pl-9
|
||||
focus:outline-none
|
||||
truncate
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<CollectionsChooseType
|
||||
:collections-type="collectionsType"
|
||||
:show="showTeamCollections"
|
||||
@@ -30,7 +33,7 @@
|
||||
@update-collection-type="updateCollectionType"
|
||||
@update-selected-team="updateSelectedTeam"
|
||||
/>
|
||||
<div class="border-b flex justify-between flex-1 border-dividerLight">
|
||||
<div class="border-b border-dividerLight flex flex-1 justify-between">
|
||||
<ButtonSecondary
|
||||
v-if="
|
||||
collectionsType.type == 'team-collections' &&
|
||||
@@ -40,7 +43,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
disabled
|
||||
icon="add"
|
||||
:title="$t('disable_new_collection')"
|
||||
:title="$t('team.no_access')"
|
||||
:label="$t('new')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
@@ -57,7 +60,7 @@
|
||||
collectionsType.selectedTeam == undefined
|
||||
"
|
||||
icon="import_export"
|
||||
:title="$t('import_export')"
|
||||
:title="$t('modal.import_export')"
|
||||
@click.native="displayModalImportExport(true)"
|
||||
/>
|
||||
</div>
|
||||
@@ -94,19 +97,37 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="collections.length === 0"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">create_new_folder</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("create_new_collection") }}
|
||||
<i class="opacity-75 pb-2 material-icons">create_new_folder</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.collections") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
v-if="
|
||||
collectionsType.type == 'team-collections' &&
|
||||
(collectionsType.selectedTeam == undefined ||
|
||||
collectionsType.selectedTeam.myRole == 'VIEWER')
|
||||
"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
disabled
|
||||
:title="$t('team.no_access')"
|
||||
:label="$t('add.new')"
|
||||
outline
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-else
|
||||
outline
|
||||
:label="$t('add.new')"
|
||||
@click.native="displayModalAdd(true)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!(filteredCollections.length !== 0 || collections.length === 0)"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">manage_search</i>
|
||||
<span class="text-xs text-center">
|
||||
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||
<span class="text-center">
|
||||
{{ $t("nothing_found") }} "{{ filterText }}"
|
||||
</span>
|
||||
</div>
|
||||
@@ -292,7 +313,7 @@ export default {
|
||||
// Intented to be called by the CollectionAdd modal submit event
|
||||
addNewRootCollection(name) {
|
||||
if (!name) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
this.$toast.info(this.$t("collection.invalid_name"))
|
||||
return
|
||||
}
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
@@ -312,7 +333,7 @@ export default {
|
||||
this.collectionsType.selectedTeam.id
|
||||
)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("collection_added"), {
|
||||
this.$toast.success(this.$t("collection.created"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
@@ -328,7 +349,7 @@ export default {
|
||||
// Intented to be called by CollectionEdit modal submit event
|
||||
updateEditingCollection(newName) {
|
||||
if (!newName) {
|
||||
this.$toast.info(this.$t("invalid_collection_name"))
|
||||
this.$toast.info(this.$t("collection.invalid_name"))
|
||||
return
|
||||
}
|
||||
if (this.collectionsType.type === "my-collections") {
|
||||
@@ -371,7 +392,7 @@ export default {
|
||||
.renameCollection(this.$apollo, name, this.editingFolder.id)
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("folder_renamed"), {
|
||||
this.$toast.success(this.$t("folder.renamed"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
@@ -486,7 +507,7 @@ export default {
|
||||
})
|
||||
.then(() => {
|
||||
// Result
|
||||
this.$toast.success(this.$t("folder_added"), {
|
||||
this.$toast.success(this.$t("folder.created"), {
|
||||
icon: "done",
|
||||
})
|
||||
this.$emit("update-team-collections")
|
||||
|
||||
@@ -10,32 +10,24 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -44,8 +36,9 @@
|
||||
<ButtonSecondary
|
||||
v-if="doc && !selected"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('import')"
|
||||
:title="$t('import.title')"
|
||||
icon="check_box_outline_blank"
|
||||
color="green"
|
||||
@click.native="$emit('select-collection')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
@@ -53,12 +46,14 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="check_box"
|
||||
color="green"
|
||||
@click.native="$emit('unselect-collection')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="!doc"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
@@ -84,7 +79,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
folder: collection,
|
||||
@@ -103,6 +98,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -111,11 +107,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsMyFolder
|
||||
v-for="(folder, index) in collection.folders"
|
||||
:key="`folder-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:folder="folder"
|
||||
:folder-index="index"
|
||||
:folder-path="`${collectionIndex}/${index}`"
|
||||
@@ -134,7 +130,7 @@
|
||||
<CollectionsMyRequest
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="-1"
|
||||
@@ -156,25 +152,24 @@
|
||||
(collection.requests == undefined || collection.requests.length === 0)
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("collection_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.collection") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_collection')"
|
||||
:title="$t('confirm.remove_collection')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeCollection"
|
||||
/>
|
||||
|
||||
@@ -10,32 +10,24 @@
|
||||
@dragend="dragging = false"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -46,7 +38,7 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="$emit('add-folder', { folder, path: folderPath })"
|
||||
/>
|
||||
@@ -67,7 +59,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', { folder, path: folderPath })
|
||||
$refs.options.tippy().hide()
|
||||
@@ -88,6 +80,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -96,11 +89,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsMyFolder
|
||||
v-for="(subFolder, subFolderIndex) in folder.folders"
|
||||
:key="`subFolder-${subFolderIndex}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:folder="subFolder"
|
||||
:folder-index="subFolderIndex"
|
||||
:collection-index="collectionIndex"
|
||||
@@ -119,7 +112,7 @@
|
||||
<CollectionsMyRequest
|
||||
v-for="(request, index) in folder.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="folderIndex"
|
||||
@@ -142,25 +135,24 @@
|
||||
folder.requests.length === 0
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("folder_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.folder") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_folder')"
|
||||
:title="$t('confirm.remove_folder')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeFolder"
|
||||
/>
|
||||
|
||||
@@ -10,15 +10,14 @@
|
||||
>
|
||||
<span
|
||||
class="
|
||||
font-mono font-bold
|
||||
cursor-pointer
|
||||
flex
|
||||
font-mono font-bold
|
||||
mx-2
|
||||
w-12
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-12
|
||||
mx-2
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
:class="getRequestLabelColor(request.method)"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
@@ -26,7 +25,7 @@
|
||||
<i
|
||||
v-if="isSelected"
|
||||
class="material-icons"
|
||||
:class="{ 'text-green-400': isSelected }"
|
||||
:class="{ 'text-green-500': isSelected }"
|
||||
>
|
||||
check_circle
|
||||
</i>
|
||||
@@ -36,22 +35,22 @@
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
>
|
||||
<span class="truncate"> {{ request.name }} </span>
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
v-if="!saveRequest"
|
||||
v-if="!saveRequest && !doc"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="replay"
|
||||
:title="$t('restore')"
|
||||
@@ -90,6 +89,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -100,7 +100,7 @@
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_request')"
|
||||
:title="$t('confirm.remove_request')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeRequest"
|
||||
/>
|
||||
@@ -108,6 +108,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { translateToNewRequest } from "~/helpers/types/HoppRESTRequest"
|
||||
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||
export default {
|
||||
props: {
|
||||
request: { type: Object, default: () => {} },
|
||||
@@ -126,11 +128,11 @@ export default {
|
||||
return {
|
||||
dragging: false,
|
||||
requestMethodLabels: {
|
||||
get: "text-green-400",
|
||||
post: "text-yellow-400",
|
||||
put: "text-blue-400",
|
||||
delete: "text-red-400",
|
||||
default: "text-gray-400",
|
||||
get: "text-green-500",
|
||||
post: "text-yellow-500",
|
||||
put: "text-blue-500",
|
||||
delete: "text-red-500",
|
||||
default: "text-gray-500",
|
||||
},
|
||||
confirmRemove: false,
|
||||
}
|
||||
@@ -157,8 +159,7 @@ export default {
|
||||
requestIndex: this.requestIndex,
|
||||
},
|
||||
})
|
||||
else
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
||||
else setRESTRequest(translateToNewRequest(this.request))
|
||||
},
|
||||
dragStart({ dataTransfer }) {
|
||||
this.dragging = !this.dragging
|
||||
|
||||
@@ -2,32 +2,24 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center group">
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -36,8 +28,9 @@
|
||||
<ButtonSecondary
|
||||
v-if="doc && !selected"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('import')"
|
||||
:title="$t('import.title')"
|
||||
icon="check_box_outline_blank"
|
||||
color="green"
|
||||
@click.native="$emit('select-collection')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
@@ -45,13 +38,14 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="check_box"
|
||||
color="green"
|
||||
@click.native="$emit('unselect-collection')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
@@ -79,7 +73,7 @@
|
||||
<SmartItem
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', {
|
||||
folder: collection,
|
||||
@@ -100,6 +94,7 @@
|
||||
<SmartItem
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -108,11 +103,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsTeamsFolder
|
||||
v-for="(folder, index) in collection.children"
|
||||
:key="`folder-${folder}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:folder="folder"
|
||||
:folder-index="index"
|
||||
:folder-path="`${collectionIndex}/${index}`"
|
||||
@@ -132,7 +127,7 @@
|
||||
<CollectionsTeamsRequest
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:request="request.request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="-1"
|
||||
@@ -153,25 +148,24 @@
|
||||
(collection.requests == undefined || collection.requests.length === 0)
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("collection_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.collection") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_collection')"
|
||||
:title="$t('confirm.remove_collection')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeCollection"
|
||||
/>
|
||||
|
||||
@@ -2,32 +2,24 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center group">
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
<i class="material-icons" :class="{ 'text-green-400': isSelected }">
|
||||
<i class="material-icons" :class="{ 'text-green-500': isSelected }">
|
||||
{{ getCollectionIcon }}
|
||||
</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="toggleShowChildren()"
|
||||
>
|
||||
@@ -39,7 +31,7 @@
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="create_new_folder"
|
||||
:title="$t('new_folder')"
|
||||
:title="$t('folder.new')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="$emit('add-folder', { folder, path: folderPath })"
|
||||
/>
|
||||
@@ -62,7 +54,7 @@
|
||||
<SmartItem
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
icon="create_new_folder"
|
||||
:label="$t('new_folder')"
|
||||
:label="$t('folder.new')"
|
||||
@click.native="
|
||||
$emit('add-folder', { folder, path: folderPath })
|
||||
$refs.options.tippy().hide()
|
||||
@@ -85,6 +77,7 @@
|
||||
<SmartItem
|
||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -93,11 +86,11 @@
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
<div v-show="showChildren || isFiltered">
|
||||
<div v-if="showChildren || isFiltered">
|
||||
<CollectionsTeamsFolder
|
||||
v-for="(subFolder, subFolderIndex) in folder.children"
|
||||
:key="`subFolder-${subFolderIndex}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:folder="subFolder"
|
||||
:folder-index="subFolderIndex"
|
||||
:collection-index="collectionIndex"
|
||||
@@ -117,7 +110,7 @@
|
||||
<CollectionsTeamsRequest
|
||||
v-for="(request, index) in folder.requests"
|
||||
:key="`request-${index}`"
|
||||
class="ml-5 border-l border-dividerLight"
|
||||
class="border-l border-dividerLight ml-5"
|
||||
:request="request.request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="folderIndex"
|
||||
@@ -137,25 +130,24 @@
|
||||
(folder.requests == undefined || folder.requests.length === 0)
|
||||
"
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
justify-center
|
||||
ml-5
|
||||
border-l border-dividerLight
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
ml-5
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">folder_open</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("folder_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">folder_open</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.folder") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_folder')"
|
||||
:title="$t('confirm.remove_folder')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeFolder"
|
||||
/>
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
<div class="flex items-center group">
|
||||
<span
|
||||
class="
|
||||
font-mono font-bold
|
||||
cursor-pointer
|
||||
flex
|
||||
font-mono font-bold
|
||||
mx-2
|
||||
w-12
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-12
|
||||
mx-2
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
:class="getRequestLabelColor(request.method)"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
@@ -19,7 +18,7 @@
|
||||
<i
|
||||
v-if="isSelected"
|
||||
class="material-icons"
|
||||
:class="{ 'text-green-400': isSelected }"
|
||||
:class="{ 'text-green-500': isSelected }"
|
||||
>
|
||||
check_circle
|
||||
</i>
|
||||
@@ -29,22 +28,22 @@
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="!doc ? selectRequest() : {}"
|
||||
>
|
||||
<span class="truncate"> {{ request.name }} </span>
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
v-if="!saveRequest"
|
||||
v-if="!saveRequest && !doc"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="replay"
|
||||
:title="$t('restore')"
|
||||
@@ -83,6 +82,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -93,7 +93,7 @@
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_request')"
|
||||
:title="$t('confirm.remove_request')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeRequest"
|
||||
/>
|
||||
@@ -101,6 +101,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { translateToNewRequest } from "~/helpers/types/HoppRESTRequest"
|
||||
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||
export default {
|
||||
props: {
|
||||
request: { type: Object, default: () => {} },
|
||||
@@ -117,11 +119,11 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
requestMethodLabels: {
|
||||
get: "text-green-400",
|
||||
post: "text-yellow-400",
|
||||
put: "text-blue-400",
|
||||
delete: "text-red-400",
|
||||
default: "text-gray-400",
|
||||
get: "text-green-500",
|
||||
post: "text-yellow-500",
|
||||
put: "text-blue-500",
|
||||
delete: "text-red-500",
|
||||
default: "text-gray-500",
|
||||
},
|
||||
confirmRemove: false,
|
||||
}
|
||||
@@ -144,8 +146,7 @@ export default {
|
||||
requestID: this.requestIndex,
|
||||
},
|
||||
})
|
||||
else
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
||||
else setRESTRequest(translateToNewRequest(this.request))
|
||||
},
|
||||
removeRequest() {
|
||||
this.$emit("remove-request", {
|
||||
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.collection {
|
||||
@apply flex flex-col flex-1;
|
||||
@apply justify-center;
|
||||
|
||||
@@ -24,13 +24,13 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.folder {
|
||||
@apply flex flex-col flex-1;
|
||||
@apply justify-center;
|
||||
@apply p-4;
|
||||
@apply border-l border-divider;
|
||||
@apply mt-4;
|
||||
@apply border-l border-divider;
|
||||
|
||||
.material-icons {
|
||||
@apply mr-4;
|
||||
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
@apply p-4;
|
||||
@apply mt-4;
|
||||
@apply border border-divider;
|
||||
@apply rounded-lg;
|
||||
@apply rounded;
|
||||
|
||||
h4 {
|
||||
@apply mt-4;
|
||||
@@ -137,7 +137,7 @@ export default {
|
||||
@apply p-4;
|
||||
@apply m-0;
|
||||
@apply text-secondaryLight;
|
||||
@apply border-b border-dashed border-divider;
|
||||
@apply border-b border-divider;
|
||||
|
||||
&:last-child {
|
||||
@apply border-b-0;
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelEnvAdd" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelEnvAdd" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="selectLabelEnvEdit" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="selectLabelEnvEdit" class="font-semibold px-4 pb-4">
|
||||
{{ $t("label") }}
|
||||
</label>
|
||||
<input
|
||||
@@ -19,11 +19,8 @@
|
||||
:placeholder="editingEnvironment.name"
|
||||
@keyup.enter="saveEnvironment"
|
||||
/>
|
||||
<div class="flex justify-between items-center flex-1">
|
||||
<label
|
||||
for="variableList"
|
||||
class="px-4 pt-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-1 justify-between items-center">
|
||||
<label for="variableList" class="font-semibold px-4 pt-4 pb-4">
|
||||
{{ $t("env_variable_list") }}
|
||||
</label>
|
||||
<div>
|
||||
@@ -36,49 +33,48 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="add"
|
||||
:title="$t('add_new')"
|
||||
:title="$t('add.new')"
|
||||
@click.native="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-2 border-divider">
|
||||
<div class="border-divider border rounded">
|
||||
<div
|
||||
v-for="(variable, index) in vars"
|
||||
:key="`variable-${index}`"
|
||||
class="
|
||||
divide-x divide-dividerLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
border-b
|
||||
divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<input
|
||||
v-model="variable.key"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('variable_count', { count: index + 1 })"
|
||||
:placeholder="$t('count.variable', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
/>
|
||||
<input
|
||||
v-model="variable.value"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
/>
|
||||
<div>
|
||||
@@ -87,10 +83,31 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="removeEnvironmentVariable(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="vars.length === 0"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">layers</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.environments") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
outline
|
||||
@click.native="addEnvironmentVariable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
<template>
|
||||
<div class="flex items-center group">
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-10
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
class="cursor-pointer flex w-10 justify-center items-center truncate"
|
||||
@click="$emit('edit-environment')"
|
||||
>
|
||||
<i class="material-icons">layers</i>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
@click="$emit('edit-environment')"
|
||||
>
|
||||
@@ -32,6 +24,7 @@
|
||||
{{ environment.name }}
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<tippy
|
||||
ref="options"
|
||||
interactive
|
||||
@@ -57,6 +50,7 @@
|
||||
/>
|
||||
<SmartItem
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('delete')"
|
||||
@click.native="
|
||||
confirmRemove = true
|
||||
@@ -64,9 +58,10 @@
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_environment')"
|
||||
:title="$t('confirm.remove_environment')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="removeEnvironment"
|
||||
/>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</template>
|
||||
<SmartItem
|
||||
icon="assignment_returned"
|
||||
:label="$t('import_from_gist')"
|
||||
:label="$t('import.from_gist')"
|
||||
@click.native="
|
||||
readEnvironmentGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -32,9 +32,9 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
!currentUser
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: currentUser.provider !== 'github.com'
|
||||
? $t('login_with_github_to') + $t('create_secret_gist')
|
||||
? $t('export.require_github')
|
||||
: null
|
||||
"
|
||||
:disabled="
|
||||
@@ -45,7 +45,7 @@
|
||||
: false
|
||||
"
|
||||
icon="assignment_turned_in"
|
||||
:label="$t('create_secret_gist')"
|
||||
:label="$t('export.create_secret_gist')"
|
||||
@click.native="
|
||||
createEnvironmentGist
|
||||
$refs.options.tippy().hide()
|
||||
@@ -76,7 +76,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('preserve_current')"
|
||||
icon="create_new_folder"
|
||||
:label="$t('import_json')"
|
||||
:label="$t('import.json')"
|
||||
@click.native="openDialogChooseFileToImportFrom"
|
||||
/>
|
||||
<input
|
||||
@@ -91,7 +91,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('download_file')"
|
||||
icon="drive_file_move"
|
||||
:label="$t('export_as_json')"
|
||||
:label="$t('export.as_json')"
|
||||
@click.native="exportJSON"
|
||||
/>
|
||||
</div>
|
||||
@@ -142,7 +142,7 @@ export default {
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("gist_created"), {
|
||||
this.$toast.success(this.$t("export.gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(res.html_url)
|
||||
@@ -155,7 +155,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async readEnvironmentGist() {
|
||||
const gist = prompt(this.$t("enter_gist_url"))
|
||||
const gist = prompt(this.$t("import.gist_url"))
|
||||
if (!gist) return
|
||||
await this.$axios
|
||||
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
|
||||
@@ -223,22 +223,22 @@ export default {
|
||||
this.importFromHoppscotch(environments)
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.environmentJson
|
||||
text = text.replace(/\n/g, "\r\n")
|
||||
const blob = new Blob([text], {
|
||||
type: "text/json",
|
||||
})
|
||||
const anchor = document.createElement("a")
|
||||
anchor.download = "hoppscotch-environment.json"
|
||||
anchor.href = window.URL.createObjectURL(blob)
|
||||
anchor.target = "_blank"
|
||||
anchor.style.display = "none"
|
||||
document.body.appendChild(anchor)
|
||||
anchor.click()
|
||||
document.body.removeChild(anchor)
|
||||
const dataToWrite = this.environmentJson
|
||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
}, 1000)
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<AppSection label="environments">
|
||||
<div class="flex flex-col sticky z-10 top-10 bg-primary">
|
||||
<div class="bg-primary rounded-t flex flex-col top-8 z-10 sticky">
|
||||
<div class="select-wrapper">
|
||||
<select
|
||||
v-model="selectedEnvironmentIndex"
|
||||
:disabled="environments.length == 0"
|
||||
class="
|
||||
flex
|
||||
w-full
|
||||
px-4
|
||||
text-xs
|
||||
py-3
|
||||
focus:outline-none
|
||||
border-b border-dividerLight
|
||||
font-medium
|
||||
bg-primaryLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
w-full
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
appearance-none
|
||||
"
|
||||
>
|
||||
<option :value="-1">No environment</option>
|
||||
@@ -30,7 +30,7 @@
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="border-b flex justify-between flex-1 border-dividerLight">
|
||||
<div class="border-b border-dividerLight flex flex-1 justify-between">
|
||||
<ButtonSecondary
|
||||
icon="add"
|
||||
:label="$t('new')"
|
||||
@@ -39,7 +39,7 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="import_export"
|
||||
:title="$t('import_export')"
|
||||
:title="$t('modal.import_export')"
|
||||
@click.native="displayModalImportExport(true)"
|
||||
/>
|
||||
</div>
|
||||
@@ -60,12 +60,17 @@
|
||||
/>
|
||||
<div
|
||||
v-if="environments.length === 0"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">library_add</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("create_new_environment") }}
|
||||
<i class="opacity-75 pb-2 material-icons">library_add</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.environments") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
outline
|
||||
@click.native="displayModalAdd(true)"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<EnvironmentsEnvironment
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<SmartModal v-if="show" dialog @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("login_to_hoppscotch") }}</h3>
|
||||
<div>
|
||||
@@ -27,17 +27,17 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-if="mode === 'email'" class="flex flex-col space-y-2">
|
||||
<div class="flex items-center">
|
||||
<label for="email" class="flex items-center px-4">
|
||||
<i class="material-icons opacity-75">mail</i>
|
||||
<div class="flex relative items-center">
|
||||
<label for="email" class="flex px-4 absolute items-center">
|
||||
<i class="opacity-75 material-icons">mail</i>
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
v-model="form.email"
|
||||
class="flex flex-1 rounded px-4 py-2 outline-none"
|
||||
class="input !pl-12"
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="enter your email"
|
||||
:placeholder="$t('email')"
|
||||
autocomplete="email"
|
||||
required
|
||||
spellcheck="false"
|
||||
@@ -56,30 +56,34 @@
|
||||
"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
:label="$t('send_magic_link')"
|
||||
:label="$t('auth.send_magic_link')"
|
||||
@click.native="signInWithEmail"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="mode === 'email-sent'" class="flex flex-col px-4">
|
||||
<div class="flex justify-center max-w-md items-center flex-col">
|
||||
<i class="material-icons text-accent text-4xl"> verified </i>
|
||||
<div class="flex flex-col max-w-md justify-center items-center">
|
||||
<i class="text-accent material-icons !text-4xl">
|
||||
mark_email_unread
|
||||
</i>
|
||||
<h3 class="font-bold my-2 text-center text-lg">
|
||||
{{ $t("we_sent_magic_link") }}
|
||||
{{ $t("auth.we_sent_magic_link") }}
|
||||
</h3>
|
||||
<p class="text-center">
|
||||
{{ $t("we_sent_magic_link_description", { email: form.email }) }}
|
||||
{{
|
||||
$t("auth.we_sent_magic_link_description", { email: form.email })
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<p v-if="mode === 'sign-in'" class="text-secondaryLight text-xs">
|
||||
<p v-if="mode === 'sign-in'" class="text-secondaryLight">
|
||||
By signing in, you are agreeing to our
|
||||
<SmartAnchor class="link" to="/index" label="Terms of Service" />
|
||||
and
|
||||
<SmartAnchor class="link" to="/index" label="Privacy Policy" />.
|
||||
</p>
|
||||
<p v-if="mode === 'email'" class="text-secondaryLight text-xs">
|
||||
<p v-if="mode === 'email'" class="text-secondaryLight">
|
||||
<SmartAnchor
|
||||
class="link"
|
||||
label="← All sign in options"
|
||||
@@ -88,14 +92,18 @@
|
||||
</p>
|
||||
<p
|
||||
v-if="mode === 'email-sent'"
|
||||
class="flex flex-1 justify-between text-secondaryLight text-xs"
|
||||
class="flex flex-1 text-secondaryLight justify-between"
|
||||
>
|
||||
<SmartAnchor
|
||||
class="link"
|
||||
label="← Re-enter email"
|
||||
@click.native="mode = 'email'"
|
||||
/>
|
||||
<SmartAnchor class="link" label="Dismiss" @click.native="hideModal" />
|
||||
<SmartAnchor
|
||||
class="link"
|
||||
:label="$t('action.dismiss')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</p>
|
||||
</template>
|
||||
</SmartModal>
|
||||
@@ -149,7 +157,7 @@ export default {
|
||||
const { additionalUserInfo } = await signInUserWithGoogle()
|
||||
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
this.$toast.info(`${this.$t("action.turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
@@ -195,7 +203,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.$toast.info(`${this.$t("account_exists")}`, {
|
||||
this.$toast.info(`${this.$t("auth.account_exists")}`, {
|
||||
icon: "vpn_key",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
@@ -225,7 +233,7 @@ export default {
|
||||
setProviderInfo(credential.providerId, credential.accessToken)
|
||||
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
this.$toast.info(`${this.$t("action.turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
@@ -271,7 +279,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.$toast.info(`${this.$t("account_exists")}`, {
|
||||
this.$toast.info(`${this.$t("auth.account_exists")}`, {
|
||||
icon: "vpn_key",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
<SmartConfirmModal
|
||||
:show="confirmLogout"
|
||||
:title="$t('are_you_sure_logout')"
|
||||
:title="$t('confirm.logout')"
|
||||
@hide-modal="confirmLogout = false"
|
||||
@resolve="logout"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="font-semibold text-xs field-title"
|
||||
class="font-semibold field-title"
|
||||
:class="{ 'field-highlighted': isHighlighted }"
|
||||
>
|
||||
{{ fieldName }}
|
||||
@@ -24,31 +24,31 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="gqlField.description"
|
||||
class="py-2 text-xs text-secondaryLight field-desc"
|
||||
class="text-secondaryLight py-2 field-desc"
|
||||
>
|
||||
{{ gqlField.description }}
|
||||
</div>
|
||||
<div
|
||||
v-if="gqlField.isDeprecated"
|
||||
class="
|
||||
inline-block
|
||||
px-2
|
||||
py-1
|
||||
my-1
|
||||
text-xs text-black
|
||||
bg-yellow-200
|
||||
rounded
|
||||
font-semibold
|
||||
bg-yellow-200
|
||||
my-1
|
||||
text-black
|
||||
py-1
|
||||
px-2
|
||||
inline-block
|
||||
field-deprecated
|
||||
"
|
||||
>
|
||||
{{ $t("deprecated") }}
|
||||
</div>
|
||||
<div v-if="fieldArgs.length > 0">
|
||||
<h5 class="my-2 text-xs">Arguments:</h5>
|
||||
<div class="pl-4 border-l-2 border-divider">
|
||||
<h5 class="my-2">Arguments:</h5>
|
||||
<div class="border-divider border-l-2 pl-4">
|
||||
<div v-for="(field, index) in fieldArgs" :key="`field-${index}`">
|
||||
<span class="font-semibold text-xs">
|
||||
<span class="font-semibold">
|
||||
{{ field.name }}:
|
||||
<GraphqlTypeLink
|
||||
:gql-type="field.type"
|
||||
@@ -57,7 +57,7 @@
|
||||
</span>
|
||||
<div
|
||||
v-if="field.description"
|
||||
class="py-2 text-xs text-secondaryLight field-desc"
|
||||
class="text-secondaryLight py-2 field-desc"
|
||||
>
|
||||
{{ field.description }}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div :id="`type_${gqlType.name}`" class="p-4">
|
||||
<div
|
||||
class="font-semibold text-xs type-title"
|
||||
class="font-semibold type-title"
|
||||
:class="{ 'text-accent': isHighlighted }"
|
||||
>
|
||||
<span v-if="isInput" class="text-accent">input </span>
|
||||
@@ -9,14 +9,11 @@
|
||||
<span v-else-if="isEnum" class="text-accent">enum </span>
|
||||
{{ gqlType.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="gqlType.description"
|
||||
class="py-2 text-xs text-secondaryLight type-desc"
|
||||
>
|
||||
<div v-if="gqlType.description" class="text-secondaryLight py-2 type-desc">
|
||||
{{ gqlType.description }}
|
||||
</div>
|
||||
<div v-if="interfaces.length > 0">
|
||||
<h5 class="my-2 text-xs">Interfaces:</h5>
|
||||
<h5 class="my-2">Interfaces:</h5>
|
||||
<div
|
||||
v-for="(gqlInterface, index) in interfaces"
|
||||
:key="`gqlInterface-${index}`"
|
||||
@@ -24,37 +21,37 @@
|
||||
<GraphqlTypeLink
|
||||
:gql-type="gqlInterface"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
class="pl-4 border-l-2 border-divider"
|
||||
class="border-divider border-l-2 pl-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="children.length > 0" class="mb-2">
|
||||
<h5 class="my-2 text-xs">Children:</h5>
|
||||
<h5 class="my-2">Children:</h5>
|
||||
<GraphqlTypeLink
|
||||
v-for="(child, index) in children"
|
||||
:key="`child-${index}`"
|
||||
:gql-type="child"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
class="pl-4 border-l-2 border-divider"
|
||||
class="border-divider border-l-2 pl-4"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="gqlType.getFields">
|
||||
<h5 class="my-2 text-xs">Fields:</h5>
|
||||
<h5 class="my-2">Fields:</h5>
|
||||
<GraphqlField
|
||||
v-for="(field, index) in gqlType.getFields()"
|
||||
:key="`field-${index}`"
|
||||
class="pl-4 border-l-2 border-divider"
|
||||
class="border-divider border-l-2 pl-4"
|
||||
:gql-field="field"
|
||||
:is-highlighted="isFieldHighlighted({ field })"
|
||||
:jump-type-callback="jumpTypeCallback"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="isEnum">
|
||||
<h5 class="my-2 text-xs">Values:</h5>
|
||||
<h5 class="my-2">Values:</h5>
|
||||
<div
|
||||
v-for="(value, index) in gqlType.getValues()"
|
||||
:key="`value-${index}`"
|
||||
class="pl-4 border-l-2 border-divider"
|
||||
class="border-divider border-l-2 pl-4"
|
||||
v-text="value.name"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<span
|
||||
:class="isScalar ? 'text-secondaryLight' : 'cursor-pointer text-accent'"
|
||||
class="font-mono text-xs"
|
||||
class="font-mono"
|
||||
@click="jumpToType"
|
||||
>
|
||||
{{ typeString }}
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
<div class="flex items-center">
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
pl-4
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
font-semibold
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
data-testid="restore_history_entry"
|
||||
@click="$emit('use-entry')"
|
||||
@@ -24,22 +24,22 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="delete"
|
||||
color="red"
|
||||
:title="$t('delete')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
color="red"
|
||||
data-testid="delete_history_entry"
|
||||
@click.native="$emit('delete-entry')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="expand ? $t('hide_more') : $t('show_more')"
|
||||
:title="expand ? $t('hide.more') : $t('show.more')"
|
||||
:icon="expand ? 'unfold_less' : 'unfold_more'"
|
||||
class="group-hover:inline-flex hidden"
|
||||
@click.native="expand = !expand"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="!entry.star ? $t('add_star') : $t('remove_star')"
|
||||
:title="!entry.star ? $t('add.star') : $t('remove.star')"
|
||||
:icon="entry.star ? 'star' : 'star_border'"
|
||||
color="yellow"
|
||||
:class="{ 'group-hover:inline-flex hidden': !entry.star }"
|
||||
@@ -51,14 +51,7 @@
|
||||
<span
|
||||
v-for="(line, index) in query"
|
||||
:key="`line-${index}`"
|
||||
class="
|
||||
text-xs
|
||||
cursor-pointer
|
||||
truncate
|
||||
px-4
|
||||
font-mono
|
||||
text-secondaryLight
|
||||
"
|
||||
class="cursor-pointer font-mono text-secondaryLight px-4 truncate"
|
||||
data-testid="restore_history_entry"
|
||||
@click="$emit('use-entry')"
|
||||
>
|
||||
|
||||
@@ -2,28 +2,32 @@
|
||||
<AppSection label="history">
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
sticky
|
||||
z-10
|
||||
bg-primaryLight
|
||||
top-10
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
top-8
|
||||
z-10
|
||||
sticky
|
||||
"
|
||||
>
|
||||
<div class="search-wrapper">
|
||||
<input
|
||||
v-model="filterText"
|
||||
type="search"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-medium
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
w-full
|
||||
py-2
|
||||
pr-2
|
||||
pl-9
|
||||
focus:outline-none
|
||||
truncate
|
||||
"
|
||||
:placeholder="$t('search')"
|
||||
/>
|
||||
</div>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
data-testid="clear_history"
|
||||
@@ -56,25 +60,25 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="!(filteredHistory.length !== 0 || history.length === 0)"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">manage_search</i>
|
||||
<span class="text-xs text-center">
|
||||
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||
<span class="text-center">
|
||||
{{ $t("nothing_found") }} "{{ filterText }}"
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="history.length === 0"
|
||||
class="flex items-center text-secondaryLight flex-col p-4 justify-center"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">schedule</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("history_empty") }}
|
||||
<i class="opacity-75 pb-2 material-icons">schedule</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.history") }}
|
||||
</span>
|
||||
</div>
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="$t('are_you_sure_remove_history')"
|
||||
:title="$t('confirm.remove_history')"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="clearHistory"
|
||||
/>
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
<div class="flex items-center group">
|
||||
<span
|
||||
class="
|
||||
font-mono font-bold
|
||||
cursor-pointer
|
||||
flex
|
||||
font-mono font-bold
|
||||
mx-2
|
||||
w-12
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
w-12
|
||||
mx-2
|
||||
truncate
|
||||
cursor-pointer
|
||||
"
|
||||
:class="entryStatus.className"
|
||||
data-testid="restore_history_entry"
|
||||
@@ -21,15 +20,15 @@
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
py-3
|
||||
cursor-pointer
|
||||
pr-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
py-2
|
||||
pr-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
data-testid="restore_history_entry"
|
||||
:title="duration"
|
||||
@@ -42,15 +41,15 @@
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="delete"
|
||||
color="red"
|
||||
:title="$t('delete')"
|
||||
class="group-hover:inline-flex hidden"
|
||||
color="red"
|
||||
data-testid="delete_history_entry"
|
||||
@click.native="$emit('delete-entry')"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="!entry.star ? $t('add_star') : $t('remove_star')"
|
||||
:title="!entry.star ? $t('add.star') : $t('remove.star')"
|
||||
:class="{ 'group-hover:inline-flex hidden': !entry.star }"
|
||||
:icon="entry.star ? 'star' : 'star_border'"
|
||||
color="yellow"
|
||||
|
||||
73
components/http/Body.vue
Normal file
73
components/http/Body.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-1 py-2 items-center justify-between">
|
||||
<tippy
|
||||
ref="contentTypeOptions"
|
||||
interactive
|
||||
tabindex="-1"
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<div class="flex">
|
||||
<span class="select-wrapper">
|
||||
<input
|
||||
id="contentType"
|
||||
v-model="contentType"
|
||||
class="
|
||||
bg-primary
|
||||
rounded
|
||||
flex
|
||||
font-semibold font-mono
|
||||
w-full
|
||||
py-2
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none
|
||||
"
|
||||
readonly
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<SmartItem
|
||||
v-for="(contentTypeItem, index) in validContentTypes"
|
||||
:key="`contentTypeItem-${index}`"
|
||||
:label="contentTypeItem"
|
||||
@click.native="
|
||||
contentType = contentTypeItem
|
||||
$refs.contentTypeOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
<SmartToggle :on="rawInput" class="px-4" @change="rawInput = !rawInput">
|
||||
{{ $t("raw_input") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
<HttpBodyParameters v-if="!rawInput" :content-type="contentType" />
|
||||
<HttpRawBody v-else :content-type="contentType" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { pluckRef } from "~/helpers/utils/composables"
|
||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
||||
import { knownContentTypes } from "~/helpers/utils/contenttypes"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
contentType: pluckRef(useRESTRequestBody(), "contentType"),
|
||||
rawInput: pluckRef(useRESTRequestBody(), "isRaw"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
validContentTypes: Object.keys(knownContentTypes),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -2,20 +2,21 @@
|
||||
<AppSection label="bodyParameters">
|
||||
<div
|
||||
class="
|
||||
sticky
|
||||
top-110px
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-24
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
pl-4
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="reqParamList" class="font-semibold text-xs">
|
||||
<label for="reqParamList" class="font-semibold">
|
||||
{{ $t("request_body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('clear')"
|
||||
@@ -23,29 +24,24 @@
|
||||
@click.native="clearContent('bodyParams', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(param, index) in bodyParams"
|
||||
:key="`param-${index}`"
|
||||
class="
|
||||
flex
|
||||
border-b border-dashed
|
||||
divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
"
|
||||
class="divide-x divide-dividerLight border-b border-dividerLight flex"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<input
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:placeholder="$t('count.parameter', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
autofocus
|
||||
@@ -55,15 +51,15 @@
|
||||
<input
|
||||
v-if="!requestBodyParamIsFile(index)"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="param.value"
|
||||
@change="
|
||||
@@ -90,9 +86,9 @@
|
||||
:title="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? $t('turn_off')
|
||||
: $t('turn_on')
|
||||
: $t('turn_off')
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
param.hasOwnProperty('active')
|
||||
@@ -101,10 +97,11 @@
|
||||
: 'check_box_outline_blank'
|
||||
: 'check_box'
|
||||
"
|
||||
color="green"
|
||||
@click.native="toggleActive(index, param)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="contentType === 'multipart/form-data'">
|
||||
<div>
|
||||
<label for="attachment" class="p-0">
|
||||
<ButtonSecondary
|
||||
class="w-full"
|
||||
@@ -126,6 +123,7 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="removeRequestBodyParam(index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label for="requestType" class="px-4 font-semibold pb-4 text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label for="requestType" class="font-semibold px-4 pb-4">
|
||||
{{ $t("choose_language") }}
|
||||
</label>
|
||||
<div class="flex flex-1">
|
||||
@@ -22,20 +22,19 @@
|
||||
<template #trigger>
|
||||
<span
|
||||
class="
|
||||
flex
|
||||
w-full
|
||||
px-4
|
||||
text-xs
|
||||
py-3
|
||||
rounded-lg
|
||||
font-semibold
|
||||
focus:outline-none
|
||||
border-b border-dividerLight
|
||||
bg-primaryLight
|
||||
border border-dividerLight
|
||||
rounded
|
||||
cursor-pointer
|
||||
flex
|
||||
font-semibold
|
||||
w-full
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
>
|
||||
{{ codegens.find((x) => x.id === requestType).name }}
|
||||
{{ codegens.find((x) => x.id === codegenType).name }}
|
||||
</span>
|
||||
</template>
|
||||
<SmartItem
|
||||
@@ -43,72 +42,109 @@
|
||||
:key="`gen-${index}`"
|
||||
:label="gen.name"
|
||||
@click.native="
|
||||
requestType = gen.id
|
||||
codegenType = gen.id
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between flex-1">
|
||||
<label
|
||||
for="generatedCode"
|
||||
class="px-4 pt-4 font-semibold pb-4 text-xs"
|
||||
>
|
||||
<div class="flex flex-1 justify-between">
|
||||
<label for="generatedCode" class="font-semibold px-4 pt-4 pb-4">
|
||||
{{ $t("generated_code") }}
|
||||
</label>
|
||||
<ButtonSecondary
|
||||
ref="copyRequestCode"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('copy_code')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyRequestCode"
|
||||
/>
|
||||
</div>
|
||||
<SmartAceEditor
|
||||
v-if="requestType"
|
||||
v-if="codegenType"
|
||||
ref="generatedCode"
|
||||
:value="requestCode"
|
||||
:lang="codegens.find((x) => x.id === requestType).language"
|
||||
:lang="codegens.find((x) => x.id === codegenType).language"
|
||||
:options="{
|
||||
maxLines: '10',
|
||||
minLines: '10',
|
||||
fontSize: '14px',
|
||||
maxLines: 16,
|
||||
minLines: 8,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="rounded-lg"
|
||||
styles="rounded"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<ButtonPrimary
|
||||
ref="copyRequestCode"
|
||||
:label="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyRequestCode"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
:label="$t('action.dismiss')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { codegens } from "~/helpers/codegen/codegen"
|
||||
import { getRESTRequest } from "~/newstore/RESTSession"
|
||||
import { getEffectiveRESTRequest } from "~/helpers/utils/EffectiveURL"
|
||||
import { getCurrentEnvironment } from "~/newstore/environments"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
requestCode: { type: String, default: null },
|
||||
requestTypeProp: { type: String, default: "curl" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codegens,
|
||||
copyIcon: "content_copy",
|
||||
request: getRESTRequest(),
|
||||
codegenType: "curl",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
requestType: {
|
||||
get() {
|
||||
return this.requestTypeProp
|
||||
requestCode(): string {
|
||||
const effectiveRequest = getEffectiveRESTRequest(
|
||||
this.request,
|
||||
getCurrentEnvironment()
|
||||
)
|
||||
|
||||
const urlObj = new URL(effectiveRequest.effectiveFinalURL)
|
||||
const baseURL = urlObj.origin
|
||||
const path = urlObj.pathname
|
||||
|
||||
// TODO: Solidify
|
||||
return codegens
|
||||
.find((x) => x.id === this.codegenType)!
|
||||
.generator({
|
||||
auth: "None",
|
||||
httpUser: null,
|
||||
httpPassword: null,
|
||||
method: effectiveRequest.method,
|
||||
url: baseURL,
|
||||
pathName: path,
|
||||
queryString: urlObj.searchParams.toString(),
|
||||
bearerToken: null,
|
||||
headers: effectiveRequest.effectiveFinalHeaders,
|
||||
rawInput: null,
|
||||
rawParams: null,
|
||||
rawRequestBody: "",
|
||||
contentType: effectiveRequest.effectiveFinalHeaders.find(
|
||||
(x) => x.key === "content-type"
|
||||
),
|
||||
})
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("set-request-type", val)
|
||||
},
|
||||
watch: {
|
||||
show(goingToShow) {
|
||||
if (goingToShow) {
|
||||
this.request = getRESTRequest()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -119,15 +155,13 @@ export default {
|
||||
this.$emit("handle-import")
|
||||
},
|
||||
copyRequestCode() {
|
||||
this.$refs.generatedCode.editor.selectAll()
|
||||
this.$refs.generatedCode.editor.focus()
|
||||
document.execCommand("copy")
|
||||
copyToClipboard(this.requestCode)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
this.$toast.success(this.$t("copied_to_clipboard").toString(), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
<AppSection label="headers">
|
||||
<div
|
||||
class="
|
||||
sticky
|
||||
top-110px
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-24
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
pl-4
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="headerList" class="font-semibold text-xs">
|
||||
<label for="headerList" class="font-semibold">
|
||||
{{ $t("header_list") }}
|
||||
</label>
|
||||
<div>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('clear')"
|
||||
@@ -25,7 +25,7 @@
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add_new')"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addHeader"
|
||||
/>
|
||||
@@ -34,21 +34,24 @@
|
||||
<div
|
||||
v-for="(header, index) in headers$"
|
||||
:key="`header-${index}`"
|
||||
class="
|
||||
flex
|
||||
border-b border-dashed
|
||||
divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
"
|
||||
class="divide-x divide-dividerLight border-b border-dividerLight flex"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<SmartAutoComplete
|
||||
:placeholder="$t('header_count', { count: index + 1 })"
|
||||
:placeholder="$t('count.header', { count: index + 1 })"
|
||||
:source="commonHeaders"
|
||||
:spellcheck="false"
|
||||
:value="header.key"
|
||||
autofocus
|
||||
styles="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
@input="
|
||||
updateHeader(index, {
|
||||
key: $event,
|
||||
@@ -57,17 +60,39 @@
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="header.value"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
styles="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
@change="
|
||||
updateHeader(index, {
|
||||
key: header.key,
|
||||
value: $event.target.value,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="header.value"
|
||||
@change="
|
||||
@@ -84,9 +109,9 @@
|
||||
:title="
|
||||
header.hasOwnProperty('active')
|
||||
? header.active
|
||||
? $t('turn_off')
|
||||
: $t('turn_on')
|
||||
: $t('turn_off')
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
header.hasOwnProperty('active')
|
||||
@@ -95,6 +120,7 @@
|
||||
: 'check_box_outline_blank'
|
||||
: 'check_box'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateHeader(index, {
|
||||
key: header.key,
|
||||
@@ -109,10 +135,25 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="deleteHeader(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="headers$.length === 0"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">post_add</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.headers") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
outline
|
||||
:label="$t('add.new')"
|
||||
@click.native="addHeader"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
@@ -126,30 +167,37 @@ import {
|
||||
} from "~/newstore/RESTSession"
|
||||
|
||||
import { commonHeaders } from "~/helpers/headers"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
commonHeaders,
|
||||
headers$: [],
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
headers$: restHeaders$,
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: getSettingSubject(
|
||||
"EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
),
|
||||
}
|
||||
},
|
||||
// watch: {
|
||||
// headers: {
|
||||
// handler(newValue) {
|
||||
// if (
|
||||
// newValue[newValue.length - 1]?.key !== "" ||
|
||||
// newValue[newValue.length - 1]?.value !== ""
|
||||
// )
|
||||
// this.addRequestHeader()
|
||||
// },
|
||||
// deep: true,
|
||||
// },
|
||||
// },
|
||||
watch: {
|
||||
headers$: {
|
||||
handler(newValue) {
|
||||
console.log("changed")
|
||||
if (
|
||||
(newValue[newValue.length - 1]?.key !== "" ||
|
||||
newValue[newValue.length - 1]?.value !== "") &&
|
||||
newValue.length
|
||||
)
|
||||
this.addHeader()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.headers$?.length) {
|
||||
this.addHeader()
|
||||
|
||||
@@ -1,41 +1,134 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="hideModal">
|
||||
<template #header>
|
||||
<h3 class="heading">{{ $t("import_curl") }}</h3>
|
||||
<h3 class="heading">{{ $t("import.curl") }}</h3>
|
||||
<div>
|
||||
<ButtonSecondary icon="close" @click.native="hideModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2">
|
||||
<textarea
|
||||
id="import-curl"
|
||||
v-model="curl"
|
||||
class="textarea"
|
||||
autofocus
|
||||
rows="8"
|
||||
:placeholder="$t('enter_curl')"
|
||||
></textarea>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span>
|
||||
<ButtonPrimary :label="$t('import')" @click.native="handleImport" />
|
||||
<ButtonPrimary
|
||||
:label="$t('import.title')"
|
||||
@click.native="handleImport"
|
||||
/>
|
||||
<ButtonSecondary :label="$t('cancel')" @click.native="hideModal" />
|
||||
</span>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import parseCurlCommand from "~/helpers/curlparser"
|
||||
import {
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
makeRESTRequest,
|
||||
} from "~/helpers/types/HoppRESTRequest"
|
||||
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
emits: ["hide-modal"],
|
||||
data() {
|
||||
return {
|
||||
curl: "",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
handleImport() {
|
||||
this.$emit("handle-import")
|
||||
const text = this.curl
|
||||
try {
|
||||
const parsedCurl = parseCurlCommand(text)
|
||||
const { origin, pathname } = new URL(
|
||||
parsedCurl.url.replace(/"/g, "").replace(/'/g, "")
|
||||
)
|
||||
const endpoint = origin + pathname
|
||||
const headers: HoppRESTHeader[] = []
|
||||
const params: HoppRESTParam[] = []
|
||||
if (parsedCurl.query) {
|
||||
for (const key of Object.keys(parsedCurl.query)) {
|
||||
const val = parsedCurl.query[key]!
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach((value) => {
|
||||
params.push({
|
||||
key,
|
||||
value,
|
||||
active: true,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
params.push({
|
||||
key,
|
||||
value: val!,
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parsedCurl.headers) {
|
||||
for (const key of Object.keys(parsedCurl.headers)) {
|
||||
headers.push({
|
||||
key,
|
||||
value: parsedCurl.headers[key],
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
const method = parsedCurl.method.toUpperCase()
|
||||
// let rawInput = false
|
||||
// let rawParams: any | null = null
|
||||
|
||||
// if (parsedCurl.data) {
|
||||
// rawInput = true
|
||||
// rawParams = parsedCurl.data
|
||||
// }
|
||||
|
||||
this.showCurlImportModal = false
|
||||
|
||||
setRESTRequest(
|
||||
makeRESTRequest({
|
||||
name: "Untitled request",
|
||||
endpoint,
|
||||
method,
|
||||
params,
|
||||
headers,
|
||||
preRequestScript: "",
|
||||
testScript: "",
|
||||
body: {
|
||||
contentType: "application/json",
|
||||
body: "",
|
||||
isRaw: false,
|
||||
},
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
this.showCurlImportModal = false
|
||||
this.$toast.error(this.$t("curl_invalid_format").toString(), {
|
||||
icon: "error",
|
||||
})
|
||||
}
|
||||
this.hideModal()
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
<AppSection label="parameters">
|
||||
<div
|
||||
class="
|
||||
sticky
|
||||
top-110px
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-24
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
pl-4
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="paramList" class="font-semibold text-xs">
|
||||
<label class="font-semibold">
|
||||
{{ $t("parameter_list") }}
|
||||
</label>
|
||||
<div>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('clear_all')"
|
||||
@@ -25,7 +25,7 @@
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add_new')"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
@@ -34,26 +34,42 @@
|
||||
<div
|
||||
v-for="(param, index) in params$"
|
||||
:key="`param-${index}`"
|
||||
class="
|
||||
flex
|
||||
border-b border-dashed
|
||||
divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
"
|
||||
class="divide-x divide-dividerLight border-b border-dividerLight flex"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<input
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="param.key"
|
||||
:placeholder="$t('count.parameter', { count: index + 1 })"
|
||||
styles="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: $event.target.value,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('count.parameter', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
autofocus
|
||||
@@ -65,17 +81,39 @@
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="param.value"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
styles="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: param.key,
|
||||
value: $event.target.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="param.value"
|
||||
@change="
|
||||
@@ -92,9 +130,9 @@
|
||||
:title="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? $t('turn_off')
|
||||
: $t('turn_on')
|
||||
: $t('turn_off')
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
param.hasOwnProperty('active')
|
||||
@@ -103,6 +141,7 @@
|
||||
: 'check_box_outline_blank'
|
||||
: 'check_box'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateParam(index, {
|
||||
key: param.key,
|
||||
@@ -117,10 +156,25 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="deleteParam(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="params$.length === 0"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">post_add</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.parameters") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
outline
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
@@ -132,6 +186,7 @@ import {
|
||||
deleteRESTParam,
|
||||
deleteAllRESTParams,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { getSettingSubject } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -142,20 +197,24 @@ export default {
|
||||
subscriptions() {
|
||||
return {
|
||||
params$: restParams$,
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: getSettingSubject(
|
||||
"EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
),
|
||||
}
|
||||
},
|
||||
// watch: {
|
||||
// params$: {
|
||||
// handler(newValue) {
|
||||
// if (
|
||||
// newValue[newValue.length - 1]?.key !== "" ||
|
||||
// newValue[newValue.length - 1]?.value !== ""
|
||||
// )
|
||||
// this.addParam()
|
||||
// },
|
||||
// deep: true,
|
||||
// },
|
||||
// },
|
||||
watch: {
|
||||
params$: {
|
||||
handler(newValue) {
|
||||
if (
|
||||
(newValue[newValue.length - 1]?.key !== "" ||
|
||||
newValue[newValue.length - 1]?.value !== "") &&
|
||||
newValue.length
|
||||
)
|
||||
this.addParam()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.params$?.length) {
|
||||
this.addParam()
|
||||
|
||||
53
components/http/PreRequestScript.vue
Normal file
53
components/http/PreRequestScript.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<AppSection label="preRequest">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-24
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold">
|
||||
{{ $t("javascript_code") }}
|
||||
</label>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://github.com/hoppscotch/hoppscotch/wiki/Pre-Request-Scripts"
|
||||
blank
|
||||
:title="$t('wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
</div>
|
||||
<SmartJsEditor
|
||||
v-model="preRequestScript"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
complete-mode="pre"
|
||||
/>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { usePreRequestScript } from "~/newstore/RESTSession"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
preRequestScript: usePreRequestScript(),
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -2,23 +2,23 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
sticky
|
||||
top-110px
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-24
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
pl-4
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="rawBody" class="font-semibold text-xs">
|
||||
<label for="rawBody" class="font-semibold">
|
||||
{{ $t("raw_request_body") }}
|
||||
</label>
|
||||
<div>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-if="rawInput && contentType.endsWith('json')"
|
||||
v-if="contentType.endsWith('json')"
|
||||
ref="prettifyRequest"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('prettify_body')"
|
||||
@@ -28,7 +28,7 @@
|
||||
<label for="payload">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('import_json')"
|
||||
:title="$t('import.json')"
|
||||
icon="post_add"
|
||||
@click.native="$refs.payload.click()"
|
||||
/>
|
||||
@@ -53,9 +53,9 @@
|
||||
v-model="rawParamsBody"
|
||||
:lang="rawInputEditorLang"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '14px',
|
||||
maxLines: 16,
|
||||
minLines: 8,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
@@ -66,43 +66,39 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { getEditorLangForMimeType } from "~/helpers/editorutils"
|
||||
import { pluckRef } from "~/helpers/utils/composables"
|
||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
props: {
|
||||
rawParams: { type: String, default: null },
|
||||
contentType: { type: String, default: null },
|
||||
rawInput: { type: Boolean, default: false },
|
||||
contentType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
rawParamsBody: pluckRef(useRESTRequestBody(), "body"),
|
||||
prettifyIcon: "photo_filter",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rawParamsBody: {
|
||||
get() {
|
||||
return this.rawParams
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update-raw-body", value)
|
||||
},
|
||||
},
|
||||
rawInputEditorLang() {
|
||||
return getEditorLangForMimeType(this.contentType)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearContent(bodyParams, $event) {
|
||||
this.$emit("clear-content", bodyParams, $event)
|
||||
clearContent() {
|
||||
this.rawParamsBody = ""
|
||||
},
|
||||
uploadPayload() {
|
||||
this.$emit("update-raw-input", true)
|
||||
const file = this.$refs.payload.files[0]
|
||||
if (file !== undefined && file !== null) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
this.$emit("update-raw-body", target.result)
|
||||
this.rawParamsBody = target.result
|
||||
}
|
||||
reader.readAsText(file)
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
@@ -128,5 +124,5 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="sticky top-0 z-10 bg-primary flex p-4">
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||
<div class="relative inline-flex">
|
||||
<span class="select-wrapper">
|
||||
<tippy
|
||||
ref="options"
|
||||
ref="methodOptions"
|
||||
interactive
|
||||
tabindex="-1"
|
||||
trigger="click"
|
||||
@@ -14,23 +14,23 @@
|
||||
<input
|
||||
id="method"
|
||||
class="
|
||||
flex
|
||||
rounded-l-lg
|
||||
bg-primaryLight
|
||||
font-mono
|
||||
w-32
|
||||
px-4
|
||||
py-2
|
||||
truncate
|
||||
text-secondaryDark
|
||||
font-semibold
|
||||
border border-divider
|
||||
transition
|
||||
focus:outline-none focus:border-accent
|
||||
rounded-l
|
||||
cursor-pointer
|
||||
flex
|
||||
font-semibold font-mono
|
||||
h-8
|
||||
text-secondaryDark
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
w-28
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
:value="newMethod$"
|
||||
autofocus
|
||||
:value="newMethod"
|
||||
readonly
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
@@ -38,79 +38,64 @@
|
||||
:key="`method-${index}`"
|
||||
:label="method"
|
||||
class="font-mono"
|
||||
@click.native="
|
||||
updateMethod(method)
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
@click.native="onSelectMethod(method)"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-1 inline-flex">
|
||||
<input
|
||||
id="url"
|
||||
v-model="newEndpoint$"
|
||||
class="
|
||||
w-full
|
||||
font-mono font-semibold
|
||||
truncate
|
||||
text-secondaryDark
|
||||
px-4
|
||||
py-2
|
||||
border border-divider
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="newEndpoint"
|
||||
:placeholder="$t('url')"
|
||||
styles="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
text-secondaryDark
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
@enter="newSendRequest()"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
id="url"
|
||||
v-model="newEndpoint"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
text-secondaryDark
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
name="url"
|
||||
type="text"
|
||||
spellcheck="false"
|
||||
:placeholder="$t('url')"
|
||||
autofocus
|
||||
@keyup.enter="newSendRequest()"
|
||||
/>
|
||||
<!-- <SmartUrlField v-else v-model="uri" /> -->
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span
|
||||
<ButtonPrimary
|
||||
id="send"
|
||||
class="
|
||||
px-4
|
||||
py-2
|
||||
border border-accent
|
||||
font-mono
|
||||
flex
|
||||
items-center
|
||||
truncate
|
||||
font-semibold
|
||||
bg-accent
|
||||
text-white
|
||||
cursor-pointer
|
||||
dark:text-accentDark
|
||||
"
|
||||
@click="newSendRequest"
|
||||
>
|
||||
{{ $t("send") }}
|
||||
</span>
|
||||
<!-- <span
|
||||
v-else
|
||||
id="cancel"
|
||||
class="
|
||||
px-4
|
||||
py-2
|
||||
border border-accent
|
||||
font-mono
|
||||
flex
|
||||
items-center
|
||||
truncate
|
||||
font-semibold
|
||||
bg-accent
|
||||
text-white
|
||||
cursor-pointer
|
||||
"
|
||||
@click="cancelRequest"
|
||||
>
|
||||
{{ $t("cancel") }}
|
||||
</span> -->
|
||||
class="rounded-none min-w-20"
|
||||
:label="!loading ? $t('send') : $t('cancel')"
|
||||
@click.native="!loading ? newSendRequest() : cancelRequest()"
|
||||
/>
|
||||
<span class="inline-flex">
|
||||
<tippy
|
||||
ref="sendOptions"
|
||||
interactive
|
||||
@@ -120,40 +105,22 @@
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span
|
||||
class="
|
||||
px-1
|
||||
py-2
|
||||
border border-accent
|
||||
font-mono
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
truncate
|
||||
font-semibold
|
||||
bg-accent
|
||||
text-white
|
||||
rounded-r-lg
|
||||
dark:text-accentDark
|
||||
"
|
||||
>
|
||||
<i class="material-icons">keyboard_arrow_down</i>
|
||||
</span>
|
||||
<ButtonPrimary class="rounded-l-none" icon="keyboard_arrow_down" />
|
||||
</template>
|
||||
<SmartItem
|
||||
:label="$t('import_curl')"
|
||||
:label="$t('import.curl')"
|
||||
icon="import_export"
|
||||
@click.native="
|
||||
showCurlImportModal = !showCurlImportModal
|
||||
$refs.sendOptions.tippy().hide()
|
||||
sendOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
:label="$t('show_code')"
|
||||
:label="$t('show.code')"
|
||||
icon="code"
|
||||
@click.native="
|
||||
showCodegenModal = !showCodegenModal
|
||||
$refs.sendOptions.tippy().hide()
|
||||
sendOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
@@ -161,30 +128,20 @@
|
||||
:label="$t('clear_all')"
|
||||
icon="clear_all"
|
||||
@click.native="
|
||||
clearContent('', $event)
|
||||
$refs.sendOptions.tippy().hide()
|
||||
clearContent()
|
||||
sendOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
<span
|
||||
class="
|
||||
ml-4
|
||||
px-4
|
||||
py-2
|
||||
border border-divider
|
||||
font-mono
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
truncate
|
||||
font-semibold
|
||||
rounded-l-lg
|
||||
cursor-pointer
|
||||
"
|
||||
@click="newSendRequest"
|
||||
>
|
||||
Save
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
class="rounded-r-none h-8 ml-2"
|
||||
:label="$t('save')"
|
||||
:shortcut="[getSpecialKey(), 'S']"
|
||||
outline
|
||||
@click.native="showSaveRequestModal = true"
|
||||
/>
|
||||
<span class="inline-flex">
|
||||
<tippy
|
||||
ref="saveOptions"
|
||||
interactive
|
||||
@@ -194,72 +151,76 @@
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span
|
||||
class="
|
||||
px-1
|
||||
py-2
|
||||
border border-divider
|
||||
font-mono
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
truncate
|
||||
font-semibold
|
||||
rounded-r-lg
|
||||
"
|
||||
>
|
||||
<i class="material-icons">keyboard_arrow_down</i>
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
icon="keyboard_arrow_down"
|
||||
outline
|
||||
class="rounded-l-none h-8"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem :description="$t('token_req_name')" />
|
||||
<input
|
||||
id="request-name"
|
||||
v-model="name"
|
||||
v-model="requestName"
|
||||
:placeholder="$t('request_name')"
|
||||
name="request-name"
|
||||
type="text"
|
||||
class="input text-sm"
|
||||
class="mb-2 input"
|
||||
/>
|
||||
<SmartItem
|
||||
ref="copyRequest"
|
||||
:label="$t('copy_request_link')"
|
||||
:icon="navigatorShare ? 'share' : 'content_copy'"
|
||||
:label="$t('request.copy_link')"
|
||||
:icon="hasNavigatorShare ? 'share' : 'content_copy'"
|
||||
@click.native="
|
||||
copyRequest()
|
||||
$refs.saveOptions.tippy().hide()
|
||||
saveOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
ref="saveRequest"
|
||||
:label="$t('save_to_collections')"
|
||||
:label="$t('request.save_as')"
|
||||
icon="create_new_folder"
|
||||
@click.native="
|
||||
saveRequest()
|
||||
$refs.saveOptions.tippy().hide()
|
||||
showSaveRequestModal = true
|
||||
saveOptions.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
<HttpImportCurl
|
||||
:show="showCurlImportModal"
|
||||
@hide-modal="showCurlImportModal = false"
|
||||
/>
|
||||
<HttpCodegenModal
|
||||
:show="showCodegenModal"
|
||||
@hide-modal="showCodegenModal = false"
|
||||
/>
|
||||
<CollectionsSaveRequest
|
||||
mode="rest"
|
||||
:show="showSaveRequestModal"
|
||||
@hide-modal="showSaveRequestModal = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
updateRESTResponse,
|
||||
restRequest$,
|
||||
restEndpoint$,
|
||||
setRESTEndpoint,
|
||||
restMethod$,
|
||||
updateRESTMethod,
|
||||
resetRESTRequest,
|
||||
useRESTRequestName,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { createRESTNetworkRequestStream } from "~/helpers/network"
|
||||
import { currentEnvironment$ } from "~/newstore/environments"
|
||||
import { getEffectiveRESTRequestStream } from "~/helpers/utils/EffectiveURL"
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
import { runRESTRequest$ } from "~/helpers/RequestRunner"
|
||||
import { useStreamSubscriber, useStream } from "~/helpers/utils/composables"
|
||||
import { defineActionHandler } from "~/helpers/actions"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
newMethod$: "",
|
||||
methods: [
|
||||
const methods = [
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST",
|
||||
@@ -270,47 +231,162 @@ export default {
|
||||
"TRACE",
|
||||
"PATCH",
|
||||
"CUSTOM",
|
||||
],
|
||||
name: "",
|
||||
newEndpoint$: "",
|
||||
showCurlImportModal: false,
|
||||
showCodegenModal: false,
|
||||
navigatorShare: navigator.share,
|
||||
effectiveStream$: null,
|
||||
}
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
newMethod$: restMethod$,
|
||||
newEndpoint$: restEndpoint$,
|
||||
effectiveStream$: getEffectiveRESTRequestStream(
|
||||
restRequest$,
|
||||
currentEnvironment$
|
||||
),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
newEndpoint$(newVal) {
|
||||
setRESTEndpoint(newVal)
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
updateMethod(method) {
|
||||
updateRESTMethod(method)
|
||||
},
|
||||
newSendRequest() {
|
||||
this.$subscribeTo(
|
||||
createRESTNetworkRequestStream(
|
||||
this.effectiveStream$,
|
||||
currentEnvironment$
|
||||
),
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const {
|
||||
$toast,
|
||||
app: { i18n },
|
||||
} = useContext()
|
||||
const t = i18n.t.bind(i18n)
|
||||
const { subscribeToStream } = useStreamSubscriber()
|
||||
|
||||
const newEndpoint = useStream(restEndpoint$, "", setRESTEndpoint)
|
||||
const newMethod = useStream(restMethod$, "", updateRESTMethod)
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const showCurlImportModal = ref(false)
|
||||
const showCodegenModal = ref(false)
|
||||
const showSaveRequestModal = ref(false)
|
||||
|
||||
const hasNavigatorShare = !!navigator.share
|
||||
|
||||
// Template refs
|
||||
//
|
||||
const methodOptions = ref<any | null>(null)
|
||||
const saveOptions = ref<any | null>(null)
|
||||
const sendOptions = ref<any | null>(null)
|
||||
|
||||
const newSendRequest = () => {
|
||||
loading.value = true
|
||||
|
||||
subscribeToStream(
|
||||
runRESTRequest$(),
|
||||
(responseState) => {
|
||||
console.log(responseState)
|
||||
if (loading.value) {
|
||||
// Check exists because, loading can be set to false
|
||||
// when cancelled
|
||||
updateRESTResponse(responseState)
|
||||
}
|
||||
},
|
||||
() => {
|
||||
loading.value = false
|
||||
},
|
||||
() => {
|
||||
loading.value = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const cancelRequest = () => {
|
||||
loading.value = false
|
||||
updateRESTResponse(null)
|
||||
}
|
||||
|
||||
const updateMethod = (method: string) => {
|
||||
updateRESTMethod(method)
|
||||
}
|
||||
|
||||
const onSelectMethod = (method: string) => {
|
||||
updateMethod(method)
|
||||
// Vue-tippy has no typescript support yet
|
||||
methodOptions.value.tippy().hide()
|
||||
}
|
||||
|
||||
const clearContent = () => {
|
||||
resetRESTRequest()
|
||||
}
|
||||
|
||||
const copyRequest = () => {
|
||||
if (navigator.share) {
|
||||
const time = new Date().toLocaleTimeString()
|
||||
const date = new Date().toLocaleDateString()
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text: `Hoppscotch • Open source API development ecosystem at ${time} on ${date}`,
|
||||
url: window.location.href,
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
copyToClipboard(window.location.href)
|
||||
$toast.info(t("copied_to_clipboard").toString(), {
|
||||
icon: "done",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const cycleUpMethod = () => {
|
||||
const currentIndex = methods.indexOf(newMethod.value)
|
||||
if (currentIndex === -1) {
|
||||
// Most probs we are in CUSTOM mode
|
||||
// Cycle up from CUSTOM is PATCH
|
||||
updateMethod("PATCH")
|
||||
} else if (currentIndex === 0) {
|
||||
updateMethod("CUSTOM")
|
||||
} else {
|
||||
updateMethod(methods[currentIndex - 1])
|
||||
}
|
||||
}
|
||||
const cycleDownMethod = () => {
|
||||
const currentIndex = methods.indexOf(newMethod.value)
|
||||
if (currentIndex === -1) {
|
||||
// Most probs we are in CUSTOM mode
|
||||
// Cycle down from CUSTOM is GET
|
||||
updateMethod("GET")
|
||||
} else if (currentIndex === methods.length - 1) {
|
||||
updateMethod("GET")
|
||||
} else {
|
||||
updateMethod(methods[currentIndex + 1])
|
||||
}
|
||||
}
|
||||
|
||||
defineActionHandler("request.send-cancel", () => {
|
||||
if (!loading.value) newSendRequest()
|
||||
else cancelRequest()
|
||||
})
|
||||
defineActionHandler("request.reset", clearContent)
|
||||
defineActionHandler("request.copy-link", copyRequest)
|
||||
defineActionHandler("request.method.next", cycleDownMethod)
|
||||
defineActionHandler("request.method.prev", cycleUpMethod)
|
||||
defineActionHandler(
|
||||
"request.save",
|
||||
() => (showSaveRequestModal.value = true)
|
||||
)
|
||||
defineActionHandler("request.method.get", () => updateMethod("GET"))
|
||||
defineActionHandler("request.method.post", () => updateMethod("POST"))
|
||||
defineActionHandler("request.method.put", () => updateMethod("PUT"))
|
||||
defineActionHandler("request.method.delete", () => updateMethod("DELETE"))
|
||||
defineActionHandler("request.method.head", () => updateMethod("HEAD"))
|
||||
|
||||
return {
|
||||
newEndpoint,
|
||||
newMethod,
|
||||
methods,
|
||||
loading,
|
||||
newSendRequest,
|
||||
requestName: useRESTRequestName(),
|
||||
getSpecialKey: getPlatformSpecialKey,
|
||||
showCurlImportModal,
|
||||
showCodegenModal,
|
||||
showSaveRequestModal,
|
||||
hasNavigatorShare,
|
||||
updateMethod,
|
||||
clearContent,
|
||||
copyRequest,
|
||||
onSelectMethod,
|
||||
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"),
|
||||
|
||||
// Template refs
|
||||
methodOptions,
|
||||
sendOptions,
|
||||
saveOptions,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,20 +1,46 @@
|
||||
<template>
|
||||
<div class="flex sticky top-0 z-10 bg-primary items-center p-4">
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky items-center">
|
||||
<div
|
||||
v-if="response == null"
|
||||
class="
|
||||
flex flex-1
|
||||
items-center
|
||||
flex flex-col flex-1
|
||||
text-secondaryLight
|
||||
flex-col
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="material-icons opacity-50 pb-2">send</i>
|
||||
<span class="text-xs text-center">
|
||||
{{ $t("waiting_send_req") }}
|
||||
<div class="flex space-x-2 pb-8">
|
||||
<div class="flex flex-col space-y-4 items-end">
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.send_request") }}
|
||||
</span>
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.reset_request") }}
|
||||
</span>
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.show_all") }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">{{ getSpecialKey() }}</span>
|
||||
<span class="shortcut-key">G</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">{{ getSpecialKey() }}</span>
|
||||
<span class="shortcut-key">I</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ButtonSecondary
|
||||
:label="$t('documentation')"
|
||||
to="https://docs.hoppscotch.io"
|
||||
blank
|
||||
outline
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<i v-if="response.type === 'loading'" class="animate-spin material-icons">
|
||||
@@ -29,11 +55,11 @@
|
||||
<span class="text-secondaryDark"> Status: </span>
|
||||
{{ response.statusCode || $t("waiting_send_req") }}
|
||||
</span>
|
||||
<span v-if="response.meta.responseDuration" class="text-xs">
|
||||
<span v-if="response.meta && response.meta.responseDuration">
|
||||
<span class="text-secondaryDark"> Time: </span>
|
||||
{{ `${response.meta.responseDuration} ms` }}
|
||||
</span>
|
||||
<span v-if="response.meta.responseSize" class="text-xs">
|
||||
<span v-if="response.meta && response.meta.responseSize">
|
||||
<span class="text-secondaryDark"> Size: </span>
|
||||
{{ `${response.meta.responseSize} B` }}
|
||||
</span>
|
||||
@@ -44,6 +70,7 @@
|
||||
|
||||
<script>
|
||||
import findStatusGroup from "~/helpers/findStatusGroup"
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -57,5 +84,19 @@ export default {
|
||||
return findStatusGroup(this.response.statusCode)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getSpecialKey: getPlatformSpecialKey,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-key {
|
||||
@apply bg-dividerLight;
|
||||
@apply rounded;
|
||||
@apply ml-2;
|
||||
@apply py-1;
|
||||
@apply px-2;
|
||||
@apply inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
63
components/http/TestResult.vue
Normal file
63
components/http/TestResult.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="divide-y divide-dividerLight">
|
||||
<div v-if="results.tests">
|
||||
<HttpTestResult
|
||||
v-for="(result, index) in results.tests"
|
||||
:key="`result-${index}`"
|
||||
:results="result"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
v-if="results.description"
|
||||
class="
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
font-semibold
|
||||
text-secondaryDark
|
||||
py-2
|
||||
px-4
|
||||
items-center
|
||||
"
|
||||
>
|
||||
{{ results.description }}
|
||||
</span>
|
||||
<div v-if="results.expectResults" class="divide-y divide-dividerLight">
|
||||
<div
|
||||
v-for="(result, index) in results.expectResults"
|
||||
:key="`result-${index}`"
|
||||
class="flex py-2 px-4 items-center"
|
||||
>
|
||||
<i
|
||||
class="mr-4 material-icons"
|
||||
:class="result.status === 'pass' ? 'text-green-500' : 'text-red-500'"
|
||||
>
|
||||
{{ result.status === "pass" ? "check" : "close" }}
|
||||
</i>
|
||||
<span v-if="result.message" class="font-semibold text-secondaryDark">
|
||||
{{ result.message }}
|
||||
</span>
|
||||
<span class="text-secondaryLight">
|
||||
{{
|
||||
` \xA0 — \xA0test ${
|
||||
result.status === "pass" ? $t("passed") : $t("failed")
|
||||
}`
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "@nuxtjs/composition-api"
|
||||
import { HoppTestResult } from "~/helpers/types/HoppTestResult"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
results: {
|
||||
type: Object as PropType<HoppTestResult>,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
148
components/http/Tests.vue
Normal file
148
components/http/Tests.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<SmartTabs styles="sticky top-24 z-20">
|
||||
<SmartTab id="script" :label="$t('test.script')" :selected="true">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-32
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold">
|
||||
{{ $t("javascript_code") }}
|
||||
</label>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://github.com/hoppscotch/hoppscotch/wiki/Post-Request-Tests"
|
||||
blank
|
||||
:title="$t('wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
</div>
|
||||
<SmartJsEditor
|
||||
v-model="testScript"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
complete-mode="test"
|
||||
/>
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
id="results"
|
||||
:label="$t('test.results')"
|
||||
:info="totalTests ? totalTests.toString() : ''"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
testResults &&
|
||||
(testResults.expectResults.length || testResults.tests.length)
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-32
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold"> Test Report </label>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('clear')"
|
||||
icon="clear_all"
|
||||
@click.native="clearContent()"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex p-2 items-center">
|
||||
<SmartProgressRing
|
||||
class="text-red-500"
|
||||
:radius="16"
|
||||
:stroke="4"
|
||||
:progress="(failedTests / totalTests) * 100"
|
||||
/>
|
||||
<div class="ml-2">
|
||||
<span v-if="failedTests" class="font-semibold text-red-500">
|
||||
{{ failedTests }} failing,
|
||||
</span>
|
||||
<span v-if="passedTests" class="font-semibold text-green-500">
|
||||
{{ passedTests }} successful,
|
||||
</span>
|
||||
<span class="font-semibold"> out of {{ totalTests }} tests. </span>
|
||||
</div>
|
||||
</div>
|
||||
<HttpTestResult :results="testResults" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">bug_report</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.tests") }}
|
||||
</span>
|
||||
</div>
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
useTestScript,
|
||||
restTestResults$,
|
||||
setRESTTestResults,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
testScript: useTestScript(),
|
||||
testResults: useReadonlyStream(restTestResults$, null),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalTests(): number | undefined {
|
||||
return this.testResults?.expectResults.length
|
||||
},
|
||||
failedTests(): number | undefined {
|
||||
return this.testResults?.expectResults.filter(
|
||||
(result: { status: string }) => result.status === "fail"
|
||||
).length
|
||||
},
|
||||
passedTests(): number | undefined {
|
||||
return this.testResults?.expectResults.filter(
|
||||
(result: { status: string }) => result.status === "pass"
|
||||
).length
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearContent() {
|
||||
setRESTTestResults(null)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -49,13 +49,14 @@
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="removeOAuthToken(index)"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<p v-if="tokens.length === 0">
|
||||
{{ $t("empty") }}
|
||||
{{ $t("empty.protocols") }}
|
||||
</p>
|
||||
</template>
|
||||
</SmartModal>
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<template>
|
||||
<div class="flex p-4 bg-primaryLight rounded">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
class="
|
||||
bg-primaryLight
|
||||
rounded
|
||||
flex
|
||||
grid
|
||||
p-4
|
||||
gap-4
|
||||
grid-cols-1
|
||||
md:grid-cols-2
|
||||
lg:grid-cols-3
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(cta, index) in ctas"
|
||||
:key="`cta-${index}`"
|
||||
class="inline-flex flex-col p-8"
|
||||
class="flex-col p-8 inline-flex"
|
||||
>
|
||||
<i class="text-3xl material-icons text-accent">{{ cta.icon }}</i>
|
||||
<i class="text-accent text-3xl material-icons">{{ cta.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
class="font-semibold mt-4 text-lg text-secondaryDark mb-2 transition"
|
||||
>
|
||||
{{ cta.title }}
|
||||
</h2>
|
||||
@@ -32,7 +36,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -42,32 +45,32 @@ export default {
|
||||
ctas: [
|
||||
{
|
||||
icon: "layers",
|
||||
title: "API Documentation",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Get up and running with Kooli in as little as 10 minutes.",
|
||||
link: {
|
||||
title: "API reference",
|
||||
target: "https://docs.kooli.tech/api",
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "local_library",
|
||||
title: "Guides",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Explore and start integrating Kooli's products and tools.",
|
||||
link: {
|
||||
title: "Guides and resources",
|
||||
target: "https://docs.kooli.tech/guides",
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/guides",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "local_library",
|
||||
title: "Guides",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Explore and start integrating Kooli's products and tools.",
|
||||
link: {
|
||||
title: "Guides and resources",
|
||||
target: "https://docs.kooli.tech/guides",
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/guides",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 font-semibold tracking-widest text-center text-accent">
|
||||
<p class="font-semibold my-4 text-center text-accent tracking-widest">
|
||||
FEATURES
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="(feature, index) in features"
|
||||
:key="`feature-${index}`"
|
||||
class="inline-flex flex-col p-8"
|
||||
class="flex-col p-8 inline-flex"
|
||||
>
|
||||
<i class="text-4xl material-icons text-accent">{{ feature.icon }}</i>
|
||||
<i class="text-accent text-4xl material-icons">{{ feature.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
mt-4
|
||||
text-lg text-secondaryDark
|
||||
mb-2
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
{{ feature.title }}
|
||||
@@ -47,44 +46,44 @@ export default {
|
||||
features: [
|
||||
{
|
||||
icon: "offline_bolt",
|
||||
title: "SaaS Executives",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Unblock your barriers to new markets to sell software globally, to companies of all sizes, at all different price points, and increase your revenue growth with Kooli’s SaaS Commerce Platform.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "stars",
|
||||
title: "Product Managers",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Don’t let your billing stack hold you back. Everything you need to grow your SaaS business. Subscription billing, payments, taxes and more, in one unified platform.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "supervised_user_circle",
|
||||
title: "Creators",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Kooli is for artists and creators: writers, designers, software developers, musicians, educators, filmmakers, and anyone in-between. If you make stuff, you can sell that stuff.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "build_circle",
|
||||
title: "Developers",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Focus on building, not billing. Save the blood, sweat and tears for your software development, not your billing stack, with Paddle’s subscription and commerce platform.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "monetization_on",
|
||||
title: "Finance",
|
||||
title: "Feature",
|
||||
description:
|
||||
"All your payments, subscriptions, taxes, invoices, SaaS metrics, and more in one place. Integrate your product, CRM, and accounting with only one tool, not dozens.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "group_work",
|
||||
title: "Enterprise",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Accept payments in 135+ currencies to better serve your international customers with a single integration. No international business entity required. Fees exclude GST.",
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
<template>
|
||||
<footer class="flex flex-col p-4">
|
||||
<nav class="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<footer class="flex flex-col p-6">
|
||||
<nav class="grid gap-4 grid-cols-2 md:grid-cols-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<span>
|
||||
<AppLogo class="h-8" />
|
||||
</span>
|
||||
<span class="font-bold"> Hoppscotch </span>
|
||||
<SmartChangeLanguage />
|
||||
<ul class="space-y-2">
|
||||
<h4 class="font-semibold my-2">Hoppscotch</h4>
|
||||
<ul class="space-y-4">
|
||||
<li>
|
||||
<SmartAnchor label="Terms" to="/about/terms" class="footer-nav" />
|
||||
<SmartChangeLanguage />
|
||||
</li>
|
||||
<li>
|
||||
<SmartAnchor
|
||||
label="Privacy"
|
||||
to="/about/privacy"
|
||||
class="footer-nav"
|
||||
/>
|
||||
<SmartColorModePicker />
|
||||
</li>
|
||||
</ul>
|
||||
<SmartColorModePicker />
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Solutions</h4>
|
||||
<h4 class="font-semibold my-2">Solutions</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.solutions"
|
||||
@@ -37,7 +28,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Platform</h4>
|
||||
<h4 class="font-semibold my-2">Platform</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.platform"
|
||||
@@ -52,7 +43,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Company</h4>
|
||||
<h4 class="font-semibold my-2">Company</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.company"
|
||||
@@ -77,72 +68,84 @@ export default {
|
||||
navigation: {
|
||||
solutions: [
|
||||
{
|
||||
name: "SaaS",
|
||||
link: "/settings",
|
||||
name: "RESTful",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Products",
|
||||
link: "/settings",
|
||||
name: "WebSocket",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "Creators",
|
||||
link: "/settings",
|
||||
name: "SSE",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "Developers",
|
||||
link: "/settings",
|
||||
name: "Socket.IO",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "Finance",
|
||||
link: "/settings",
|
||||
name: "MQTT",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "Enterprise",
|
||||
link: "/settings",
|
||||
name: "GraphQL",
|
||||
link: "/graphql",
|
||||
},
|
||||
],
|
||||
platform: [
|
||||
{
|
||||
name: "Payments",
|
||||
link: "/settings",
|
||||
name: "API Designing",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Subscriptions",
|
||||
link: "/settings",
|
||||
name: "API Development",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API",
|
||||
link: "https://docs.kooli.tech/api",
|
||||
name: "API Testing",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Guides",
|
||||
link: "https://docs.kooli.tech/guides",
|
||||
name: "API Deployment",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API Documentation",
|
||||
link: "/documentation",
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
link: "/",
|
||||
},
|
||||
],
|
||||
company: [
|
||||
{
|
||||
name: "About",
|
||||
link: "/about",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Jobs",
|
||||
link: "/about/jobs",
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
link: "/about/integrations",
|
||||
name: "Careers",
|
||||
link: "/careers",
|
||||
},
|
||||
{
|
||||
name: "Support",
|
||||
link: "",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Contact",
|
||||
link: "/about/contact",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Blog",
|
||||
link: "https://blog.kooli.tech",
|
||||
link: "https://blog.hoppscotch.io",
|
||||
},
|
||||
{
|
||||
name: "Community",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Open Source",
|
||||
link: "https://github.com/hoppscotch",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -151,11 +154,11 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.footer-nav {
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-secondaryDark;
|
||||
}
|
||||
@apply px-2 py-1;
|
||||
@apply -mx-2 -my-1;
|
||||
@apply hover:text-secondaryDark;
|
||||
@apply focus:text-secondaryDark;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
<template>
|
||||
<div ref="globe"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThreeGlobe from "three-globe"
|
||||
import * as THREE from "three"
|
||||
import TrackballControls from "three-trackballcontrols"
|
||||
import geojson from "~/assets/geojson/ne_110m_admin_0_countries.geojson"
|
||||
import texture from "~/assets/images/texture.png"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
globe: null,
|
||||
renderer: null,
|
||||
scene: null,
|
||||
camera: null,
|
||||
tbControls: null,
|
||||
arcsData: [...Array(20).keys()].map(() => ({
|
||||
startLat: (Math.random() - 0.5) * 180,
|
||||
startLng: (Math.random() - 0.5) * 360,
|
||||
endLat: (Math.random() - 0.5) * 180,
|
||||
endLng: (Math.random() - 0.5) * 360,
|
||||
color: [
|
||||
["#00acee", "#aceeff", "#00ffac", "#aaef3e"][
|
||||
Math.round(Math.random() * 3)
|
||||
],
|
||||
["#00acee", "#aceeff", "#00ffac", "#aaef3e"][
|
||||
Math.round(Math.random() * 3)
|
||||
],
|
||||
],
|
||||
})),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
labelsData() {
|
||||
const labelsData = []
|
||||
this.arcsData.forEach(
|
||||
({ startLat, startLng, endLat, endLng, color }, linkIdx) =>
|
||||
[
|
||||
[startLat, startLng],
|
||||
[endLat, endLng],
|
||||
].forEach(([lat, lng], edgeIdx) =>
|
||||
labelsData.push({
|
||||
lat,
|
||||
lng,
|
||||
color: color[edgeIdx],
|
||||
text: `${linkIdx} ${edgeIdx ? "tgt" : "src"}`,
|
||||
})
|
||||
)
|
||||
)
|
||||
return labelsData
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.init()
|
||||
this.animate()
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
() => {
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
this.globe = new ThreeGlobe()
|
||||
.globeImageUrl(texture)
|
||||
.atmosphereColor("#aaaaaa")
|
||||
// arcs layer
|
||||
.arcsData(this.arcsData)
|
||||
.arcColor("color")
|
||||
.arcDashLength(1)
|
||||
.arcDashGap(() => Math.random())
|
||||
.arcStroke(0.6)
|
||||
.arcDashInitialGap(() => Math.random() * 5)
|
||||
.arcDashAnimateTime(2000)
|
||||
|
||||
// hex layer
|
||||
.hexPolygonsData(geojson.features)
|
||||
.hexPolygonResolution(3)
|
||||
.hexPolygonMargin(0.5)
|
||||
.hexPolygonColor(() => `#aaaaaa`)
|
||||
|
||||
// labels layer
|
||||
.labelsData(this.labelsData)
|
||||
.labelLat("lat")
|
||||
.labelLng("lng")
|
||||
.labelText("text")
|
||||
.labelColor("color")
|
||||
.labelSize(1.2)
|
||||
.labelDotRadius(0.8)
|
||||
|
||||
// Setup renderer
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
alpha: true,
|
||||
})
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
this.$refs.globe.appendChild(this.renderer.domElement)
|
||||
|
||||
// Setup scene
|
||||
this.scene = new THREE.Scene()
|
||||
this.scene.background = null
|
||||
this.scene.add(this.globe)
|
||||
this.scene.add(new THREE.AmbientLight(0xffffff))
|
||||
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.6))
|
||||
|
||||
// Setup camera
|
||||
this.camera = new THREE.PerspectiveCamera()
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.camera.position.z = 300
|
||||
|
||||
// Add camera controls
|
||||
this.tbControls = new TrackballControls(
|
||||
this.camera,
|
||||
this.renderer.domElement
|
||||
)
|
||||
this.tbControls.noZoom = true
|
||||
this.tbControls.noPan = true
|
||||
},
|
||||
|
||||
animate() {
|
||||
this.renderer.render(this.scene, this.camera)
|
||||
this.tbControls.update()
|
||||
requestAnimationFrame(this.animate)
|
||||
this.globe.rotation.y -= 0.0025
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,51 +1,71 @@
|
||||
<template>
|
||||
<div class="flex p-4 relative">
|
||||
<div class="relative my-16 z-10 max-w-3xl">
|
||||
<div class="flex flex-col p-6 relative">
|
||||
<div class="flex flex-col mt-16 items-center justify-center">
|
||||
<h2
|
||||
class="
|
||||
font-extrabold
|
||||
text-accent text-center
|
||||
leading-none
|
||||
tracking-tighter
|
||||
font-semibold
|
||||
text-accent text-4xl
|
||||
md:text-5xl
|
||||
lg:text-6xl
|
||||
text-4xl
|
||||
md:text-6xl
|
||||
lg:text-8xl
|
||||
"
|
||||
>
|
||||
Open Source
|
||||
</h2>
|
||||
<h3
|
||||
class="
|
||||
text-3xl
|
||||
font-extrabold
|
||||
my-4
|
||||
font-mono
|
||||
text-secondaryDark
|
||||
text-center text-secondaryDark
|
||||
leading-none
|
||||
tracking-tighter
|
||||
text-3xl
|
||||
md:text-4xl
|
||||
lg:text-4xl
|
||||
font-semibold
|
||||
lg:text-5xl
|
||||
"
|
||||
>
|
||||
API Development Ecosystem
|
||||
</h3>
|
||||
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
|
||||
Millions of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the largest and most advanced development platform
|
||||
in the world.
|
||||
<p class="my-4 text-lg text-center max-w-2xl">
|
||||
Thousands of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the transparent and most flexible API development
|
||||
ecosystem in the world.
|
||||
</p>
|
||||
<div class="my-8 flex items-center">
|
||||
<div class="flex space-x-4 my-8 justify-center items-center">
|
||||
<ButtonPrimary
|
||||
label="Dashboard"
|
||||
icon="chevron_right"
|
||||
class="my-4"
|
||||
large
|
||||
label="Get Started"
|
||||
icon="arrow_forward"
|
||||
rounded
|
||||
reverse
|
||||
large
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
to="https://github.com/hoppscotch/hoppscotch"
|
||||
blank
|
||||
outline
|
||||
label="GitHub"
|
||||
svg="github"
|
||||
large
|
||||
rounded
|
||||
:shortcut="['30k Stars']"
|
||||
/>
|
||||
<ButtonSecondary label="Dashboard" icon="chevron_right" />
|
||||
<ButtonPrimary label="GitHub" svg="github" large rounded />
|
||||
<AppGitHubStarButton size="large" />
|
||||
</div>
|
||||
<!-- <LandingStats /> -->
|
||||
</div>
|
||||
<div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
||||
<LandingGlobe class="h-64 w-full sm:h-72 md:h-96 lg:h-full" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center justify-center"></div>
|
||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showLogin: false,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex p-4 font-mono space-x-16">
|
||||
<div class="flex font-mono space-x-16 p-6">
|
||||
<div v-for="(stat, index) in stats" :key="`stat-${index}`">
|
||||
<span class="text-xl font-bold">
|
||||
<span class="font-bold text-xl">
|
||||
{{ stat.count }}<span class="text-secondaryLight">+</span>
|
||||
</span>
|
||||
<br />
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
return {
|
||||
stats: [
|
||||
{ count: "350k", audience: "Developers" },
|
||||
{ count: "10k", audience: "Organizations" },
|
||||
{ count: "5k", audience: "Organizations" },
|
||||
{ count: "1m", audience: "Requests" },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4 mx-4 bg-primaryLight rounded">
|
||||
<div class="bg-primaryLight rounded flex flex-col mx-6 p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 font-semibold tracking-widest text-center">
|
||||
<p class="font-semibold my-4 text-center tracking-widest">
|
||||
EMPOWERING DEVELOPERS FROM
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 md:grid-cols-4 lg:grid-cols-6">
|
||||
<div class="grid gap-4 grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
|
||||
<div
|
||||
v-for="(user, index) in users"
|
||||
:key="`user-${index}`"
|
||||
class="inline-flex flex-col items-center justify-center px-4"
|
||||
class="flex-col px-4 inline-flex items-center justify-center"
|
||||
>
|
||||
<img
|
||||
:src="`/images/users/${user.image}`"
|
||||
alt="Profile picture"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center h-24 w-24"
|
||||
class="flex-col object-contain object-center h-24 w-24 inline-flex"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="p-2 font-mono">
|
||||
<div class="font-mono p-2">
|
||||
<div
|
||||
v-for="(header, index) in headers"
|
||||
:key="`header-${index}`"
|
||||
@@ -7,27 +7,25 @@
|
||||
>
|
||||
<span
|
||||
class="
|
||||
p-2
|
||||
flex
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
font-semibold
|
||||
min-w-0
|
||||
p-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
>
|
||||
<span class="truncate">
|
||||
<span class="rounded select-all truncate">
|
||||
{{ header.key }}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
font-mono font-bold
|
||||
flex
|
||||
font-mono font-bold
|
||||
mx-2
|
||||
justify-center
|
||||
items-center
|
||||
text-xs
|
||||
mx-2
|
||||
truncate
|
||||
"
|
||||
>
|
||||
@@ -35,16 +33,16 @@
|
||||
</span>
|
||||
<span
|
||||
class="
|
||||
p-2
|
||||
flex flex-1
|
||||
min-w-0
|
||||
text-xs
|
||||
group-hover:text-secondaryDark
|
||||
transition
|
||||
flex
|
||||
font-semibold
|
||||
flex-1
|
||||
min-w-0
|
||||
p-2
|
||||
transition
|
||||
group-hover:text-secondaryDark
|
||||
"
|
||||
>
|
||||
<span class="truncate">
|
||||
<span class="rounded select-all truncate">
|
||||
{{ header.value }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<SmartTabs styles="sticky z-10 top-13">
|
||||
<SmartTabs styles="sticky z-10 top-12">
|
||||
<SmartTab
|
||||
v-for="(lens, index) in validLenses"
|
||||
:id="lens.lensName"
|
||||
@@ -10,10 +10,10 @@
|
||||
<component :is="lens.renderer" :response="response" />
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
v-if="Object.keys(response.headers).length !== 0"
|
||||
v-if="headerLength"
|
||||
id="headers"
|
||||
:label="$t('Headers')"
|
||||
:info="Object.keys(response.headers).length.toString()"
|
||||
:info="headerLength.toString()"
|
||||
>
|
||||
<LensesHeadersRenderer :headers="response.headers" />
|
||||
</SmartTab>
|
||||
@@ -32,7 +32,14 @@ export default {
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
computed: {
|
||||
headerLength() {
|
||||
if (!this.response || !this.response.headers) return 0
|
||||
|
||||
return Object.keys(this.response.headers).length
|
||||
},
|
||||
validLenses() {
|
||||
if (!this.response) return []
|
||||
|
||||
return getSuitableLenses(this.response)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,24 +2,24 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
flex flex-1
|
||||
sticky
|
||||
top-23
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-20
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="body" class="px-4 font-semibold text-xs">
|
||||
<label for="body" class="font-semibold px-4">
|
||||
{{ $t("response_body") }}
|
||||
</label>
|
||||
<div>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="previewEnabled ? $t('hide_preview') : $t('preview_html')"
|
||||
:title="previewEnabled ? $t('hide.preview') : $t('preview_html')"
|
||||
:icon="!previewEnabled ? 'visibility' : 'visibility_off'"
|
||||
@click.native.prevent="togglePreview"
|
||||
/>
|
||||
@@ -35,7 +35,7 @@
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('copy_response')"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
@@ -47,8 +47,8 @@
|
||||
:lang="'html'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: '16',
|
||||
fontSize: '14px',
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
@@ -67,6 +67,7 @@
|
||||
|
||||
<script>
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default {
|
||||
mixins: [TextContentRendererMixin],
|
||||
@@ -97,18 +98,12 @@ export default {
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
flex flex-1
|
||||
sticky
|
||||
top-23
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-20
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="body" class="px-4 font-semibold text-xs">
|
||||
<label for="body" class="font-semibold px-4">
|
||||
{{ $t("response_body") }}
|
||||
</label>
|
||||
<div>
|
||||
@@ -98,7 +98,7 @@ export default {
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
flex flex-1
|
||||
sticky
|
||||
top-23
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-20
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="body" class="px-4 font-semibold text-xs">
|
||||
<label for="body" class="font-semibold px-4">
|
||||
{{ $t("response_body") }}
|
||||
</label>
|
||||
<div>
|
||||
<ButtonSecondary
|
||||
v-if="response.body && canDownloadResponse"
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('download_file')"
|
||||
@@ -28,7 +28,7 @@
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('copy_response')"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
@@ -41,8 +41,8 @@
|
||||
:provide-j-s-o-n-outline="true"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: '16',
|
||||
fontSize: '14px',
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
<script>
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default {
|
||||
mixins: [TextContentRendererMixin],
|
||||
@@ -82,19 +82,11 @@ export default {
|
||||
.split(";")[0]
|
||||
.toLowerCase()
|
||||
},
|
||||
canDownloadResponse() {
|
||||
return (
|
||||
this.response &&
|
||||
this.response.headers &&
|
||||
this.response.headers["content-type"] &&
|
||||
isJSONContentType(this.response.headers["content-type"])
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
@@ -108,18 +100,12 @@ export default {
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
flex flex-1
|
||||
sticky
|
||||
top-23
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-20
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="body" class="px-4 font-semibold text-xs">
|
||||
<label for="body" class="font-semibold px-4">
|
||||
{{ $t("response_body") }}
|
||||
</label>
|
||||
<div>
|
||||
<ButtonSecondary
|
||||
v-if="response.body && canDownloadResponse"
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('download_file')"
|
||||
@@ -28,7 +28,7 @@
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('copy_response')"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
@@ -40,8 +40,8 @@
|
||||
:lang="'plain_text'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: '16',
|
||||
fontSize: '14px',
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
<script>
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default {
|
||||
mixins: [TextContentRendererMixin],
|
||||
@@ -73,14 +73,6 @@ export default {
|
||||
.split(";")[0]
|
||||
.toLowerCase()
|
||||
},
|
||||
canDownloadResponse() {
|
||||
return (
|
||||
this.response &&
|
||||
this.response.headers &&
|
||||
this.response.headers["content-type"] &&
|
||||
isJSONContentType(this.response.headers["content-type"])
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
@@ -99,18 +91,12 @@ export default {
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
flex flex-1
|
||||
sticky
|
||||
top-23
|
||||
z-10
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-20
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
border-b border-dividerLight
|
||||
"
|
||||
>
|
||||
<label for="body" class="px-4 font-semibold text-xs">
|
||||
<label for="body" class="font-semibold px-4">
|
||||
{{ $t("response_body") }}
|
||||
</label>
|
||||
<div>
|
||||
@@ -28,7 +28,7 @@
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('copy_response')"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
@@ -40,8 +40,8 @@
|
||||
:lang="'xml'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: '16',
|
||||
fontSize: '14px',
|
||||
minLines: 16,
|
||||
fontSize: '12px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
<script>
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default {
|
||||
mixins: [TextContentRendererMixin],
|
||||
@@ -90,18 +91,12 @@ export default {
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
<template>
|
||||
<div class="relative flex items-center justify-center h-5 w-5 cursor-pointer">
|
||||
<div class="cursor-pointer flex h-5 w-5 relative items-center justify-center">
|
||||
<img
|
||||
class="
|
||||
absolute
|
||||
object-cover object-center
|
||||
transition
|
||||
bg-primaryDark bg-primaryLight
|
||||
rounded-full
|
||||
bg-primaryDark
|
||||
object-cover object-center
|
||||
h-5
|
||||
transition
|
||||
w-5
|
||||
bg-primaryLight
|
||||
absolute
|
||||
"
|
||||
:src="url"
|
||||
:alt="alt"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="absolute inset-0 rounded-lg shadow-inner"></div>
|
||||
<div class="rounded-full shadow-inner inset-0 absolute"></div>
|
||||
<span
|
||||
:class="[
|
||||
'border-primary rounded-full border-2 h-3 -top-1 -right-1 w-3 absolute',
|
||||
indicator,
|
||||
]"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -32,6 +37,10 @@ export default {
|
||||
type: String,
|
||||
default: "Profile picture",
|
||||
},
|
||||
indicator: {
|
||||
type: String,
|
||||
default: "bg-green-500",
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<label for="log">{{ title }}</label>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-0
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label for="log" class="font-semibold py-2">{{ title }}</label>
|
||||
</div>
|
||||
<div ref="log" name="log" class="realtime-log">
|
||||
<span v-if="log">
|
||||
<span v-if="log" class="space-y-2">
|
||||
<span
|
||||
v-for="(entry, index) in log"
|
||||
:key="`entry-${index}`"
|
||||
@@ -51,7 +65,7 @@ export default {
|
||||
|
||||
&,
|
||||
span {
|
||||
@apply font-mono;
|
||||
@apply font-mono font-semibold;
|
||||
@apply select-text;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,140 +1,150 @@
|
||||
<template>
|
||||
<div>
|
||||
<Splitpanes vertical :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes horizontal :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" vertical>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="mqtt-url">{{ $t("url") }}</label>
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||
<div class="flex-1 inline-flex">
|
||||
<input
|
||||
id="mqtt-url"
|
||||
v-model="url"
|
||||
type="url"
|
||||
spellcheck="false"
|
||||
class="input md:rounded-bl-lg"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
rounded-l
|
||||
font-semibold font-mono
|
||||
text-secondaryDark
|
||||
w-full
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
:placeholder="$t('url')"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="connect"
|
||||
:disabled="!validUrl"
|
||||
class="
|
||||
button
|
||||
rounded-b-lg
|
||||
md:rounded-bl-none md:rounded-br-lg
|
||||
"
|
||||
:icon="!connectionState ? 'sync' : 'sync_disabled'"
|
||||
:label="
|
||||
connectionState ? $t('disconnect') : $t('connect')
|
||||
"
|
||||
reverse
|
||||
class="rounded-l-none w-28"
|
||||
:label="connectionState ? $t('disconnect') : $t('connect')"
|
||||
:loading="connectingState"
|
||||
@click.native="toggleConnection"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
<Pane class="overflow-auto">
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="response">
|
||||
<ul>
|
||||
<li>
|
||||
<RealtimeLog :title="$t('log')" :log="log" />
|
||||
</li>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane max-size="35" min-size="20" class="overflow-auto">
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<AppSection label="messages">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="pub_topic">{{ $t("mqtt_topic") }}</label>
|
||||
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||
<label for="pub_topic" class="font-semibold">
|
||||
{{ $t("mqtt.topic") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex px-4">
|
||||
<input
|
||||
id="pub_topic"
|
||||
v-model="pub_topic"
|
||||
class="input"
|
||||
:placeholder="$t('mqtt.topic_name')"
|
||||
type="text"
|
||||
spellcheck="false"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="mqtt-message">{{ $t("message") }}</label>
|
||||
</div>
|
||||
<div class="bg-primary flex flex-1 p-4 items-center justify-between">
|
||||
<label for="mqtt-message" class="font-semibold">{{
|
||||
$t("communication")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="flex px-4">
|
||||
<input
|
||||
id="mqtt-message"
|
||||
v-model="msg"
|
||||
class="input !rounded-r-none"
|
||||
type="text"
|
||||
:placeholder="$t('message')"
|
||||
spellcheck="false"
|
||||
class="input border-dashed md:border-l border-divider"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="publish"
|
||||
class="button"
|
||||
name="get"
|
||||
class="rounded-l-none"
|
||||
:disabled="!canpublish"
|
||||
icon="send"
|
||||
:label="$t('mqtt_publish')"
|
||||
:label="$t('mqtt.publish')"
|
||||
@click.native="publish"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="sub_topic">{{ $t("mqtt_topic") }}</label>
|
||||
<div
|
||||
class="
|
||||
border-t border-dividerLight
|
||||
flex flex-col flex-1
|
||||
mt-4
|
||||
p-4
|
||||
inline-flex
|
||||
"
|
||||
>
|
||||
<label for="sub_topic" class="font-semibold">{{
|
||||
$t("mqtt.topic")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="flex px-4">
|
||||
<input
|
||||
id="sub_topic"
|
||||
v-model="sub_topic"
|
||||
type="text"
|
||||
:placeholder="$t('mqtt.topic_name')"
|
||||
spellcheck="false"
|
||||
class="input md:rounded-bl-lg"
|
||||
class="input !rounded-r-none"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="subscribe"
|
||||
name="get"
|
||||
:disabled="!cansubscribe"
|
||||
class="
|
||||
button
|
||||
rounded-b-lg
|
||||
md:rounded-bl-none md:rounded-br-lg
|
||||
"
|
||||
:icon="subscriptionState ? 'sync_disabled' : 'sync'"
|
||||
class="rounded-l-none"
|
||||
:label="
|
||||
subscriptionState
|
||||
? $t('mqtt_unsubscribe')
|
||||
: $t('mqtt_subscribe')
|
||||
subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe')
|
||||
"
|
||||
reverse
|
||||
@click.native="toggleSubscription"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import Paho from "paho-mqtt"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
return {
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url: "wss://test.mosquitto.org:8081",
|
||||
@@ -144,6 +154,7 @@ export default {
|
||||
sub_topic: "",
|
||||
msg: "",
|
||||
connectionState: false,
|
||||
connectingState: false,
|
||||
log: null,
|
||||
manualDisconnect: false,
|
||||
subscriptionState: false,
|
||||
@@ -182,6 +193,7 @@ export default {
|
||||
if (data.url === this.url) this.isUrlValid = data.result
|
||||
},
|
||||
connect() {
|
||||
this.connectingState = true
|
||||
this.log = [
|
||||
{
|
||||
payload: this.$t("connecting_to", { name: this.url }),
|
||||
@@ -209,6 +221,7 @@ export default {
|
||||
})
|
||||
},
|
||||
onConnectionFailure() {
|
||||
this.connectingState = false
|
||||
this.connectionState = false
|
||||
this.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
@@ -218,6 +231,7 @@ export default {
|
||||
})
|
||||
},
|
||||
onConnectionSuccess() {
|
||||
this.connectingState = false
|
||||
this.connectionState = true
|
||||
this.log.push({
|
||||
payload: this.$t("connected_to", { name: this.url }),
|
||||
@@ -255,6 +269,7 @@ export default {
|
||||
})
|
||||
},
|
||||
onConnectionLost() {
|
||||
this.connectingState = false
|
||||
this.connectionState = false
|
||||
if (this.manualDisconnect) {
|
||||
this.$toast.error(this.$t("disconnected"), {
|
||||
@@ -342,5 +357,5 @@ export default {
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,167 +1,173 @@
|
||||
<template>
|
||||
<div>
|
||||
<Splitpanes vertical :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes horizontal :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" vertical>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="socketio-url">{{ $t("url") }}</label>
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||
<div class="flex-1 inline-flex">
|
||||
<input
|
||||
id="socketio-url"
|
||||
v-model="url"
|
||||
type="url"
|
||||
spellcheck="false"
|
||||
:class="{ error: !urlValid }"
|
||||
class="input md:rounded-bl-lg"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
rounded-l
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
text-secondaryDark
|
||||
w-full
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
:placeholder="$t('url')"
|
||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<label for="socketio-path">{{ $t("path") }}</label>
|
||||
<input
|
||||
id="socketio-path"
|
||||
v-model="path"
|
||||
class="input"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
text-secondaryDark
|
||||
w-full
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
spellcheck="false"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="connect"
|
||||
:disabled="!urlValid"
|
||||
name="connect"
|
||||
class="
|
||||
button
|
||||
rounded-b-lg
|
||||
md:rounded-bl-none md:rounded-br-lg
|
||||
"
|
||||
:icon="!connectionState ? 'sync' : 'sync_disabled'"
|
||||
:label="
|
||||
!connectionState ? $t('connect') : $t('disconnect')
|
||||
"
|
||||
reverse
|
||||
class="rounded-l-none w-28"
|
||||
:label="!connectionState ? $t('connect') : $t('disconnect')"
|
||||
:loading="connectingState"
|
||||
@click.native="toggleConnection"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
<Pane class="overflow-auto">
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="response">
|
||||
<ul>
|
||||
<li>
|
||||
<RealtimeLog :title="$t('log')" :log="communication.log" />
|
||||
</li>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane max-size="35" min-size="20" class="overflow-auto">
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<AppSection label="messages">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="event_name">{{ $t("event_name") }}</label>
|
||||
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||
<label for="events" class="font-semibold">
|
||||
{{ $t("events") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex px-4">
|
||||
<input
|
||||
id="event_name"
|
||||
v-model="communication.eventName"
|
||||
class="input"
|
||||
name="event_name"
|
||||
:placeholder="$t('event_name')"
|
||||
type="text"
|
||||
:readonly="!connectionState"
|
||||
:disabled="!connectionState"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex flex-1">
|
||||
<label>{{ $t("message") }}s</label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
<div class="bg-primary flex flex-1 p-4 items-center justify-between">
|
||||
<label class="font-semibold">{{ $t("communication") }}</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addCommunicationInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2 px-4">
|
||||
<div
|
||||
v-for="(input, index) of communication.inputs"
|
||||
:key="`input-${index}`"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
md:divide-y-0
|
||||
"
|
||||
>
|
||||
<li>
|
||||
<div class="flex">
|
||||
<input
|
||||
v-model="communication.inputs[index]"
|
||||
class="input"
|
||||
class="input !rounded-r-none"
|
||||
name="message"
|
||||
:placeholder="$t('count.message', { count: index + 1 })"
|
||||
type="text"
|
||||
:readonly="!connectionState"
|
||||
:disabled="!connectionState"
|
||||
@keyup.enter="connectionState ? sendMessage() : null"
|
||||
/>
|
||||
</li>
|
||||
<div v-if="index + 1 !== communication.inputs.length">
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
v-if="index + 1 !== communication.inputs.length"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
class="rounded-l-none"
|
||||
color="red"
|
||||
outline
|
||||
@click.native="removeCommunicationInput({ index })"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<div v-if="index + 1 === communication.inputs.length">
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
v-if="index + 1 === communication.inputs.length"
|
||||
id="send"
|
||||
class="button"
|
||||
name="send"
|
||||
:disabled="!connectionState"
|
||||
icon="send"
|
||||
class="rounded-l-none"
|
||||
:label="$t('send')"
|
||||
@click.native="sendMessage"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
icon="add"
|
||||
:label="$t('add_new')"
|
||||
@click.native="addCommunicationInput"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import { io as Client } from "socket.io-client"
|
||||
import wildcard from "socketio-wildcard"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
return {
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url: "wss://main-daxrc78qyb411dls-gtw.qovery.io",
|
||||
path: "/socket.io",
|
||||
isUrlValid: true,
|
||||
connectingState: false,
|
||||
connectionState: false,
|
||||
io: null,
|
||||
communication: {
|
||||
@@ -210,6 +216,7 @@ export default {
|
||||
else return this.disconnect()
|
||||
},
|
||||
connect() {
|
||||
this.connectingState = true
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: this.$t("connecting_to", { name: this.url }),
|
||||
@@ -228,6 +235,7 @@ export default {
|
||||
// Add ability to listen to all events
|
||||
wildcard(Client.Manager)(this.io)
|
||||
this.io.on("connect", () => {
|
||||
this.connectingState = false
|
||||
this.connectionState = true
|
||||
this.communication.log = [
|
||||
{
|
||||
@@ -259,6 +267,7 @@ export default {
|
||||
this.handleError()
|
||||
})
|
||||
this.io.on("disconnect", () => {
|
||||
this.connectingState = false
|
||||
this.connectionState = false
|
||||
this.communication.log.push({
|
||||
payload: this.$t("disconnected_from", { name: this.url }),
|
||||
@@ -286,6 +295,7 @@ export default {
|
||||
},
|
||||
handleError(error) {
|
||||
this.disconnect()
|
||||
this.connectingState = false
|
||||
this.connectionState = false
|
||||
this.communication.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
@@ -332,5 +342,5 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<Splitpanes horizontal :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="server">{{ $t("server") }}</label>
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||
<div class="flex-1 inline-flex">
|
||||
<input
|
||||
id="server"
|
||||
v-model="server"
|
||||
type="url"
|
||||
:class="{ error: !serverValid }"
|
||||
class="input md:rounded-bl-lg"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
rounded-l
|
||||
font-semibold font-mono
|
||||
text-secondaryDark
|
||||
w-full
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
:placeholder="$t('url')"
|
||||
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="start"
|
||||
:disabled="!serverValid"
|
||||
name="start"
|
||||
class="
|
||||
button
|
||||
rounded-b-lg
|
||||
md:rounded-bl-none md:rounded-br-lg
|
||||
"
|
||||
:icon="!connectionSSEState ? 'sync' : 'sync_disabled'"
|
||||
class="rounded-l-none w-22"
|
||||
:label="!connectionSSEState ? $t('start') : $t('stop')"
|
||||
reverse
|
||||
:loading="connectingState"
|
||||
@click.native="toggleSSEConnection"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
<Pane class="overflow-auto">
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="response">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -48,7 +49,6 @@
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -61,6 +61,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
connectionSSEState: false,
|
||||
connectingState: false,
|
||||
server: "https://express-eventsource.herokuapp.com/events",
|
||||
isUrlValid: true,
|
||||
sse: null,
|
||||
@@ -103,6 +104,7 @@ export default {
|
||||
else return this.stop()
|
||||
},
|
||||
start() {
|
||||
this.connectingState = true
|
||||
this.events.log = [
|
||||
{
|
||||
payload: this.$t("connecting_to", { name: this.server }),
|
||||
@@ -114,6 +116,7 @@ export default {
|
||||
try {
|
||||
this.sse = new EventSource(this.server)
|
||||
this.sse.onopen = () => {
|
||||
this.connectingState = false
|
||||
this.connectionSSEState = true
|
||||
this.events.log = [
|
||||
{
|
||||
|
||||
@@ -1,183 +1,217 @@
|
||||
<template>
|
||||
<div>
|
||||
<Splitpanes vertical :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes horizontal :dbl-click-splitter="false">
|
||||
<Pane class="overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" vertical>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="websocket-url">{{ $t("url") }}</label>
|
||||
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||
<div class="flex-1 inline-flex">
|
||||
<input
|
||||
id="websocket-url"
|
||||
v-model="url"
|
||||
class="input"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-divider
|
||||
rounded-l
|
||||
font-semibold font-mono
|
||||
text-secondaryDark
|
||||
w-full
|
||||
py-1
|
||||
px-4
|
||||
transition
|
||||
truncate
|
||||
focus:outline-none focus:border-accent
|
||||
"
|
||||
type="url"
|
||||
spellcheck="false"
|
||||
:class="{ error: !urlValid }"
|
||||
:placeholder="$t('url')"
|
||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="connect"
|
||||
:disabled="!urlValid"
|
||||
class="button"
|
||||
class="rounded-l-none w-28"
|
||||
name="connect"
|
||||
:icon="!connectionState ? 'sync' : 'sync_disabled'"
|
||||
:label="
|
||||
!connectionState ? $t('connect') : $t('disconnect')
|
||||
"
|
||||
reverse
|
||||
:label="!connectionState ? $t('connect') : $t('disconnect')"
|
||||
:loading="connectingState"
|
||||
@click.native="toggleConnection"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex flex-1">
|
||||
<label>{{ $t("protocols") }}</label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-for="(protocol, index) of protocols"
|
||||
:key="`protocol-${index}`"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
</AppSection>
|
||||
<div
|
||||
class="
|
||||
border-b border-dashed
|
||||
divide-y
|
||||
md:divide-x
|
||||
border-divider
|
||||
divide-dashed divide-divider
|
||||
md:divide-y-0
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-16
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<li>
|
||||
<label class="font-semibold">
|
||||
{{ $t("websocket.protocols") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('clear_all')"
|
||||
icon="clear_all"
|
||||
@click.native="clearContent"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addProtocol"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(protocol, index) of protocols"
|
||||
:key="`protocol-${index}`"
|
||||
class="
|
||||
divide-x divide-dividerLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
"
|
||||
:class="{ 'border-t': index == 0 }"
|
||||
>
|
||||
<input
|
||||
v-model="protocol.value"
|
||||
class="input"
|
||||
:placeholder="$t('protocol_count', { count: index + 1 })"
|
||||
class="
|
||||
bg-primaryLight
|
||||
flex
|
||||
font-semibold font-mono
|
||||
flex-1
|
||||
py-2
|
||||
px-4
|
||||
focus:outline-none
|
||||
"
|
||||
:placeholder="$t('count.protocol', { count: index + 1 })"
|
||||
name="message"
|
||||
type="text"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
protocol.hasOwnProperty('active')
|
||||
? protocol.active
|
||||
? $t('turn_off')
|
||||
: $t('turn_on')
|
||||
: $t('turn_off')
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
protocol.hasOwnProperty('active')
|
||||
? protocol.active
|
||||
? 'check_box'
|
||||
: 'check_box_outline_blank'
|
||||
: 'check_box'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
protocol.active = protocol.hasOwnProperty('active')
|
||||
? !protocol.active
|
||||
: false
|
||||
"
|
||||
/>
|
||||
<i class="material-icons">
|
||||
{{
|
||||
protocol.hasOwnProperty("active")
|
||||
? protocol.active
|
||||
? "check_box"
|
||||
: "check_box_outline_blank"
|
||||
: "check_box"
|
||||
}}
|
||||
</i>
|
||||
</li>
|
||||
</div>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('delete')"
|
||||
icon="delete"
|
||||
color="red"
|
||||
@click.native="deleteProtocol({ index })"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
icon="add"
|
||||
:label="$t('add_new')"
|
||||
@click.native="addProtocol"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</div>
|
||||
<div
|
||||
v-if="protocols.length === 0"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">topic</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.protocols") }}
|
||||
</span>
|
||||
</div>
|
||||
</Pane>
|
||||
<Pane class="overflow-auto">
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="response">
|
||||
<ul>
|
||||
<li>
|
||||
<RealtimeLog :title="$t('log')" :log="communication.log" />
|
||||
</li>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane max-size="35" min-size="20" class="overflow-auto">
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<AppSection label="messages">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="websocket-message">{{ $t("message") }}</label>
|
||||
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||
<label for="websocket-message" class="font-semibold">
|
||||
{{ $t("communication") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex px-4">
|
||||
<input
|
||||
id="websocket-message"
|
||||
v-model="communication.input"
|
||||
name="message"
|
||||
type="text"
|
||||
:readonly="!connectionState"
|
||||
class="input md:rounded-bl-lg"
|
||||
:disabled="!connectionState"
|
||||
:placeholder="$t('message')"
|
||||
class="input !rounded-r-none"
|
||||
@keyup.enter="connectionState ? sendMessage() : null"
|
||||
@keyup.up="connectionState ? walkHistory('up') : null"
|
||||
@keyup.down="connectionState ? walkHistory('down') : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<ButtonSecondary
|
||||
<ButtonPrimary
|
||||
id="send"
|
||||
name="send"
|
||||
:disabled="!connectionState"
|
||||
class="
|
||||
button
|
||||
rounded-b-lg
|
||||
md:rounded-bl-none md:rounded-br-lg
|
||||
"
|
||||
icon="send"
|
||||
class="rounded-l-none"
|
||||
:label="$t('send')"
|
||||
@click.native="sendMessage"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
import "splitpanes/dist/splitpanes.css"
|
||||
|
||||
export default {
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
return {
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
connectionState: false,
|
||||
connectingState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
isUrlValid: true,
|
||||
socket: null,
|
||||
@@ -222,6 +256,9 @@ export default {
|
||||
this.worker.terminate()
|
||||
},
|
||||
methods: {
|
||||
clearContent() {
|
||||
this.protocols = []
|
||||
},
|
||||
debouncer: debounce(function () {
|
||||
this.worker.postMessage({ type: "ws", url: this.url })
|
||||
}, 1000),
|
||||
@@ -243,8 +280,10 @@ export default {
|
||||
},
|
||||
]
|
||||
try {
|
||||
this.connectingState = true
|
||||
this.socket = new WebSocket(this.url, this.activeProtocols)
|
||||
this.socket.onopen = () => {
|
||||
this.connectingState = false
|
||||
this.connectionState = true
|
||||
this.communication.log = [
|
||||
{
|
||||
@@ -294,6 +333,8 @@ export default {
|
||||
disconnect() {
|
||||
if (this.socket) {
|
||||
this.socket.close()
|
||||
this.connectionState = false
|
||||
this.connectingState = false
|
||||
}
|
||||
},
|
||||
handleError(error) {
|
||||
@@ -378,5 +419,5 @@ export default {
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<!-- text-blue-400 -->
|
||||
<!-- text-green-400 -->
|
||||
<!-- text-teal-400 -->
|
||||
<!-- text-indigo-400 -->
|
||||
<!-- text-purple-400 -->
|
||||
<!-- text-orange-400 -->
|
||||
<!-- text-pink-400 -->
|
||||
<!-- text-red-400 -->
|
||||
<!-- text-yellow-400 -->
|
||||
<!-- text-green-500 -->
|
||||
<!-- text-teal-500 -->
|
||||
<!-- text-blue-500 -->
|
||||
<!-- text-indigo-500 -->
|
||||
<!-- text-purple-500 -->
|
||||
<!-- text-yellow-500 -->
|
||||
<!-- text-orange-500 -->
|
||||
<!-- text-red-500 -->
|
||||
<!-- text-pink-500 -->
|
||||
<ButtonSecondary
|
||||
v-for="(color, index) of accentColors"
|
||||
:key="`color-${index}`"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="`${color.charAt(0).toUpperCase()}${color.slice(1)}`"
|
||||
:class="[`text-${color}-400`, { 'bg-primary': color === active }]"
|
||||
:class="[{ 'bg-primaryLight': color === active }]"
|
||||
icon="lens"
|
||||
:color="color"
|
||||
@click.native="setActiveColor(color)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
<div class="show-if-initialized" :class="{ initialized }">
|
||||
<pre ref="editor" :class="styles"></pre>
|
||||
<div
|
||||
v-if="lang == 'json'"
|
||||
v-if="provideJSONOutline"
|
||||
class="
|
||||
sticky
|
||||
bottom-0
|
||||
z-10
|
||||
flex flex-nowrap flex-1
|
||||
overflow-auto
|
||||
font-mono
|
||||
shadow-lg
|
||||
px-4
|
||||
bg-primaryLight
|
||||
border-t border-divider
|
||||
flex flex-nowrap
|
||||
font-mono
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
bottom-0
|
||||
z-10
|
||||
sticky
|
||||
overflow-auto
|
||||
hide-scrollbar
|
||||
"
|
||||
>
|
||||
@@ -21,20 +22,19 @@
|
||||
v-for="(p, index) in currentPath"
|
||||
:key="`p-${index}`"
|
||||
class="
|
||||
inline-flex
|
||||
items-center
|
||||
flex-grow-0 flex-shrink-0
|
||||
text-secondaryLight
|
||||
hover:text-secondary
|
||||
cursor-pointer
|
||||
font-semibold
|
||||
text-xs
|
||||
flex-grow-0 flex-shrink-0
|
||||
text-secondaryLight
|
||||
inline-flex
|
||||
items-center
|
||||
hover:text-secondary
|
||||
"
|
||||
>
|
||||
<span @click="onBlockClick(index)">
|
||||
{{ p }}
|
||||
</span>
|
||||
<i v-if="index + 1 !== currentPath.length" class="material-icons mx-2">
|
||||
<i v-if="index + 1 !== currentPath.length" class="mx-2 material-icons">
|
||||
chevron_right
|
||||
</i>
|
||||
<tippy
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
color
|
||||
? `text-${color}-500 hover:text-${color}-600 focus:text-${color}-600`
|
||||
: 'hover:text-secondaryDark focus:text-secondaryDark',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
{ 'opacity-75 cursor-not-allowed': disabled },
|
||||
{ 'flex-row-reverse': reverse },
|
||||
]"
|
||||
:disabled="disabled"
|
||||
|
||||
@@ -3,15 +3,6 @@
|
||||
<input
|
||||
ref="acInput"
|
||||
v-model="text"
|
||||
class="
|
||||
px-4
|
||||
py-3
|
||||
text-xs
|
||||
flex flex-1
|
||||
font-semibold
|
||||
bg-primaryLight
|
||||
focus:outline-none
|
||||
"
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
:spellcheck="spellcheck"
|
||||
@@ -197,7 +188,7 @@ export default {
|
||||
.autocomplete-wrapper {
|
||||
@apply relative;
|
||||
@apply flex;
|
||||
@apply flex-1;
|
||||
@apply w-full;
|
||||
|
||||
input:focus + ul.suggestions,
|
||||
ul.suggestions:hover {
|
||||
@@ -213,15 +204,15 @@ export default {
|
||||
@apply z-50;
|
||||
@apply shadow-lg;
|
||||
|
||||
top: calc(100% - 8px);
|
||||
top: calc(100% - 4px);
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
li {
|
||||
@apply w-full;
|
||||
@apply block;
|
||||
@apply py-2 px-4;
|
||||
@apply text-xs;
|
||||
@apply font-mono;
|
||||
@apply text-secondaryLight;
|
||||
@apply font-semibold font-mono;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span>
|
||||
<span class="inline-flex">
|
||||
<tippy
|
||||
ref="language"
|
||||
interactive
|
||||
@@ -10,23 +10,26 @@
|
||||
:animate-fill="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<TabPrimary
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('choose_language')"
|
||||
class="font-medium focus:outline-none"
|
||||
outline
|
||||
:label="`${
|
||||
$i18n.locales.find(({ code }) => code == $i18n.locale).country
|
||||
} ${$i18n.locales.find(({ code }) => code == $i18n.locale).name}`"
|
||||
$i18n.locales.find(({ code }) => code == $i18n.locale).name
|
||||
}`"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
<nuxt-link
|
||||
v-for="(locale, index) in $i18n.locales.filter(
|
||||
({ code }) => code !== $i18n.locale
|
||||
)"
|
||||
:key="`locale-${index}`"
|
||||
:to="switchLocalePath(locale.code).toString()"
|
||||
:label="`${locale.country} ${locale.name}`"
|
||||
@click.native="$refs.language.tippy().hide()"
|
||||
/>
|
||||
:to="switchLocalePath(locale.code)"
|
||||
@click="$refs.language.tippy().hide()"
|
||||
>
|
||||
<SmartItem :label="locale.name" />
|
||||
</nuxt-link>
|
||||
</tippy>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
:key="`color-${index}`"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="`${color.charAt(0).toUpperCase()}${color.slice(1)}`"
|
||||
:class="[
|
||||
{ 'bg-primary': color === activeColor },
|
||||
{ 'text-accent hover:text-accent': color === activeColor },
|
||||
]"
|
||||
:class="{
|
||||
'bg-primaryLight !text-accent hover:text-accent': color === active,
|
||||
}"
|
||||
:icon="getIcon(color)"
|
||||
@click.native="setBGMode(color)"
|
||||
/>
|
||||
@@ -32,7 +31,7 @@ export default Vue.extend({
|
||||
},
|
||||
subscriptions() {
|
||||
return {
|
||||
activeColor: getSettingSubject("BG_COLOR"),
|
||||
active: getSettingSubject("BG_COLOR"),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="px-2 flex flex-col">
|
||||
<label class="font-semibold text-xs">
|
||||
<div class="flex flex-col px-2">
|
||||
<label class="font-semibold">
|
||||
{{ title }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
@apply inline-flex;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@apply rounded-lg;
|
||||
@apply rounded;
|
||||
@apply m-1;
|
||||
@apply pl-4;
|
||||
@apply bg-primaryDark;
|
||||
@apply text-secondary;
|
||||
@apply font-mono;
|
||||
@apply font-mono font-semibold;
|
||||
@apply border border-divider;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,32 @@
|
||||
|
||||
<template>
|
||||
<div class="url-field-container">
|
||||
<div ref="editor" class="url-field" contenteditable="true"></div>
|
||||
<div
|
||||
ref="editor"
|
||||
:placeholder="placeholder"
|
||||
class="url-field"
|
||||
:class="styles"
|
||||
contenteditable="true"
|
||||
@keydown.enter.prevent="$emit('enter', $event)"
|
||||
@change="$emit('change', $event)"
|
||||
@keyup="$emit('keyup', $event)"
|
||||
@click="$emit('click', $event)"
|
||||
@keydown="$emit('keydown', $event)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import IntervalTree from "node-interval-tree"
|
||||
import debounce from "lodash/debounce"
|
||||
import isUndefined from "lodash/isUndefined"
|
||||
import { tippy } from "vue-tippy"
|
||||
import {
|
||||
currentEnvironment$,
|
||||
getCurrentEnvironment,
|
||||
} from "~/newstore/environments"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
|
||||
const tagsToReplace = {
|
||||
"&": "&",
|
||||
@@ -20,12 +38,30 @@ const tagsToReplace = {
|
||||
">": ">",
|
||||
}
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const currentEnvironment = useReadonlyStream(
|
||||
currentEnvironment$,
|
||||
getCurrentEnvironment()
|
||||
)
|
||||
|
||||
return {
|
||||
currentEnvironment,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -35,7 +71,8 @@ export default {
|
||||
highlight: [
|
||||
{
|
||||
text: /(<<\w+>>)/g,
|
||||
style: "VAR",
|
||||
style:
|
||||
"text-white cursor-help rounded px-1 focus:outline-none mx-0.5",
|
||||
},
|
||||
],
|
||||
highlightEnabled: true,
|
||||
@@ -45,7 +82,11 @@ export default {
|
||||
fireOnEnabled: true,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentEnvironment() {
|
||||
this.processHighlights()
|
||||
},
|
||||
highlightStyle() {
|
||||
this.processHighlights()
|
||||
},
|
||||
@@ -70,6 +111,7 @@ export default {
|
||||
this.restoreSelection(this.$refs.editor, selection)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.fireOnEnabled)
|
||||
this.$refs.editor.addEventListener(this.fireOn, this.handleChange)
|
||||
@@ -155,14 +197,21 @@ export default {
|
||||
result += this.safe_tags_replace(
|
||||
this.internalValue.substring(startingPosition, position.start)
|
||||
)
|
||||
result +=
|
||||
"<span class='" +
|
||||
highlightPositions[k].style +
|
||||
"'>" +
|
||||
this.safe_tags_replace(
|
||||
const envVar = this.internalValue
|
||||
.substring(position.start, position.end + 1)
|
||||
.slice(2, -2)
|
||||
result += `<span class="${highlightPositions[k].style} ${
|
||||
this.currentEnvironment.variables.find((k) => k.key === envVar)
|
||||
?.value === undefined
|
||||
? "bg-red-500"
|
||||
: "bg-accentDark"
|
||||
}" v-tippy data-tippy-content="environment: ${
|
||||
this.currentEnvironment.name
|
||||
} • value: ${
|
||||
this.currentEnvironment.variables.find((k) => k.key === envVar)?.value
|
||||
}">${this.safe_tags_replace(
|
||||
this.internalValue.substring(position.start, position.end + 1)
|
||||
) +
|
||||
"</span>"
|
||||
)}</span>`
|
||||
startingPosition = position.end + 1
|
||||
}
|
||||
if (startingPosition < this.internalValue.length)
|
||||
@@ -177,8 +226,32 @@ export default {
|
||||
result += " "
|
||||
}
|
||||
this.htmlOutput = result
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.renderTippy()
|
||||
})
|
||||
|
||||
this.$emit("input", this.internalValue)
|
||||
},
|
||||
renderTippy() {
|
||||
const tippable = document.querySelectorAll("[v-tippy]")
|
||||
tippable.forEach((t) => {
|
||||
tippy(t, {
|
||||
content: t.dataset["tippy-content"],
|
||||
theme: "tooltip",
|
||||
popperOptions: {
|
||||
modifiers: {
|
||||
preventOverflow: {
|
||||
enabled: false,
|
||||
},
|
||||
hide: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
},
|
||||
insertRange(start, end, highlightObj, intervalTree) {
|
||||
const overlap = intervalTree.search(start, end)
|
||||
const maxLengthOverlap = overlap.reduce((max, o) => {
|
||||
@@ -386,25 +459,37 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.VAR {
|
||||
@apply font-bold;
|
||||
@apply text-accent;
|
||||
<style lang="scss" scoped>
|
||||
[contenteditable="true"] {
|
||||
&:empty {
|
||||
line-height: 1.9;
|
||||
|
||||
&::before {
|
||||
@apply text-secondary;
|
||||
@apply opacity-75;
|
||||
@apply pointer-events-none;
|
||||
|
||||
content: attr(placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.url-field-container {
|
||||
@apply inline-grid;
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.url-field {
|
||||
@apply border-dashed border-divider;
|
||||
@apply flex;
|
||||
@apply items-center;
|
||||
@apply justify-items-start;
|
||||
@apply whitespace-nowrap;
|
||||
@apply overflow-x-auto;
|
||||
@apply overflow-y-hidden;
|
||||
@apply resize-none;
|
||||
@apply md:border-l;
|
||||
}
|
||||
|
||||
.url-field::-webkit-scrollbar {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user