Parameters.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <template>
  2. <AppSection label="parameters">
  3. <div
  4. class="
  5. bg-primary
  6. border-b border-dividerLight
  7. flex flex-1
  8. top-upperSecondaryStickyFold
  9. pl-4
  10. z-10
  11. sticky
  12. items-center
  13. justify-between
  14. "
  15. >
  16. <label class="font-semibold text-secondaryLight">
  17. {{ $t("request.parameter_list") }}
  18. </label>
  19. <div class="flex">
  20. <ButtonSecondary
  21. v-tippy="{ theme: 'tooltip' }"
  22. to="https://docs.hoppscotch.io/features/parameters"
  23. blank
  24. :title="$t('app.wiki')"
  25. svg="help-circle"
  26. />
  27. <ButtonSecondary
  28. v-tippy="{ theme: 'tooltip' }"
  29. :title="$t('action.clear_all')"
  30. svg="trash-2"
  31. @click.native="bulkMode ? clearBulkEditor() : clearContent()"
  32. />
  33. <ButtonSecondary
  34. v-tippy="{ theme: 'tooltip' }"
  35. :title="$t('state.bulk_mode')"
  36. svg="edit"
  37. :class="{ '!text-accent': bulkMode }"
  38. @click.native="bulkMode = !bulkMode"
  39. />
  40. <ButtonSecondary
  41. v-tippy="{ theme: 'tooltip' }"
  42. :title="$t('add.new')"
  43. svg="plus"
  44. :disabled="bulkMode"
  45. @click.native="addParam"
  46. />
  47. </div>
  48. </div>
  49. <div v-if="bulkMode" ref="bulkEditor"></div>
  50. <div v-else>
  51. <div
  52. v-for="(param, index) in params$"
  53. :key="`param-${index}`"
  54. class="divide-x divide-dividerLight border-b border-dividerLight flex"
  55. >
  56. <SmartEnvInput
  57. v-model="param.key"
  58. :placeholder="`${$t('count.parameter', { count: index + 1 })}`"
  59. styles="
  60. bg-transparent
  61. flex
  62. flex-1
  63. py-1
  64. px-4
  65. "
  66. @change="
  67. updateParam(index, {
  68. key: $event,
  69. value: param.value,
  70. active: param.active,
  71. })
  72. "
  73. />
  74. <SmartEnvInput
  75. v-model="param.value"
  76. :placeholder="`${$t('count.value', { count: index + 1 })}`"
  77. styles="
  78. bg-transparent
  79. flex
  80. flex-1
  81. py-1
  82. px-4
  83. "
  84. @change="
  85. updateParam(index, {
  86. key: param.key,
  87. value: $event,
  88. active: param.active,
  89. })
  90. "
  91. />
  92. <span>
  93. <ButtonSecondary
  94. v-tippy="{ theme: 'tooltip' }"
  95. :title="
  96. param.hasOwnProperty('active')
  97. ? param.active
  98. ? $t('action.turn_off')
  99. : $t('action.turn_on')
  100. : $t('action.turn_off')
  101. "
  102. :svg="
  103. param.hasOwnProperty('active')
  104. ? param.active
  105. ? 'check-circle'
  106. : 'circle'
  107. : 'check-circle'
  108. "
  109. color="green"
  110. @click.native="
  111. updateParam(index, {
  112. key: param.key,
  113. value: param.value,
  114. active: param.hasOwnProperty('active') ? !param.active : false,
  115. })
  116. "
  117. />
  118. </span>
  119. <span>
  120. <ButtonSecondary
  121. v-tippy="{ theme: 'tooltip' }"
  122. :title="$t('action.remove')"
  123. svg="trash"
  124. color="red"
  125. @click.native="deleteParam(index)"
  126. />
  127. </span>
  128. </div>
  129. <div
  130. v-if="params$.length === 0"
  131. class="
  132. flex flex-col
  133. text-secondaryLight
  134. p-4
  135. items-center
  136. justify-center
  137. "
  138. >
  139. <img
  140. :src="`/images/states/${$colorMode.value}/add_files.svg`"
  141. loading="lazy"
  142. class="
  143. flex-col
  144. my-4
  145. object-contain object-center
  146. h-16
  147. w-16
  148. inline-flex
  149. "
  150. :alt="$t('empty.parameters')"
  151. />
  152. <span class="text-center pb-4">
  153. {{ $t("empty.parameters") }}
  154. </span>
  155. <ButtonSecondary
  156. :label="`${$t('add.new')}`"
  157. svg="plus"
  158. filled
  159. class="mb-4"
  160. @click.native="addParam"
  161. />
  162. </div>
  163. </div>
  164. </AppSection>
  165. </template>
  166. <script setup lang="ts">
  167. import { ref, useContext, watch, onBeforeUpdate } from "@nuxtjs/composition-api"
  168. import { useCodemirror } from "~/helpers/editor/codemirror"
  169. import { HoppRESTParam } from "~/helpers/types/HoppRESTRequest"
  170. import { useReadonlyStream } from "~/helpers/utils/composables"
  171. import {
  172. restParams$,
  173. addRESTParam,
  174. updateRESTParam,
  175. deleteRESTParam,
  176. deleteAllRESTParams,
  177. setRESTParams,
  178. } from "~/newstore/RESTSession"
  179. const {
  180. $toast,
  181. app: { i18n },
  182. } = useContext()
  183. const t = i18n.t.bind(i18n)
  184. const bulkMode = ref(false)
  185. const bulkParams = ref("")
  186. watch(bulkParams, () => {
  187. try {
  188. const transformation = bulkParams.value.split("\n").map((item) => ({
  189. key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""),
  190. value: item.substring(item.indexOf(":") + 1).trim(),
  191. active: !item.trim().startsWith("//"),
  192. }))
  193. setRESTParams(transformation)
  194. } catch (e) {
  195. $toast.error(`${t("error.something_went_wrong")}`, {
  196. icon: "error_outline",
  197. })
  198. console.error(e)
  199. }
  200. })
  201. const bulkEditor = ref<any | null>(null)
  202. useCodemirror(bulkEditor, bulkParams, {
  203. extendedEditorConfig: {
  204. mode: "text/x-yaml",
  205. placeholder: `${t("state.bulk_mode_placeholder")}`,
  206. },
  207. linter: null,
  208. completer: null,
  209. })
  210. const params$ = useReadonlyStream(restParams$, [])
  211. watch(
  212. params$,
  213. (newValue) => {
  214. if (
  215. (newValue[newValue.length - 1]?.key !== "" ||
  216. newValue[newValue.length - 1]?.value !== "") &&
  217. newValue.length
  218. )
  219. addParam()
  220. },
  221. { deep: true }
  222. )
  223. onBeforeUpdate(() => editBulkParamsLine(-1, null))
  224. const editBulkParamsLine = (index: number, item?: HoppRESTParam | null) => {
  225. const params = params$.value
  226. bulkParams.value = params
  227. .reduce((all, param, pIndex) => {
  228. const current =
  229. index === pIndex && item != null
  230. ? `${item.active ? "" : "//"}${item.key}: ${item.value}`
  231. : `${param.active ? "" : "//"}${param.key}: ${param.value}`
  232. return [...all, current]
  233. }, [])
  234. .join("\n")
  235. }
  236. const clearBulkEditor = () => {
  237. bulkParams.value = ""
  238. }
  239. const addParam = () => {
  240. const empty = { key: "", value: "", active: true }
  241. const index = params$.value.length
  242. addRESTParam(empty)
  243. editBulkParamsLine(index, empty)
  244. }
  245. const updateParam = (index: number, item: HoppRESTParam) => {
  246. updateRESTParam(index, item)
  247. editBulkParamsLine(index, item)
  248. }
  249. const deleteParam = (index: number) => {
  250. const parametersBeforeDeletion = params$.value
  251. deleteRESTParam(index)
  252. editBulkParamsLine(index, null)
  253. const deletedItem = parametersBeforeDeletion[index]
  254. if (deletedItem.key || deletedItem.value) {
  255. $toast.success(t("state.deleted").toString(), {
  256. icon: "delete",
  257. action: [
  258. {
  259. text: t("action.undo").toString(),
  260. onClick: (_, toastObject) => {
  261. setRESTParams(parametersBeforeDeletion as HoppRESTParam[])
  262. editBulkParamsLine(index, deletedItem)
  263. toastObject.goAway(0)
  264. },
  265. },
  266. ],
  267. })
  268. }
  269. }
  270. const clearContent = () => {
  271. deleteAllRESTParams()
  272. clearBulkEditor()
  273. }
  274. </script>