Parameters.vue 7.3 KB

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