FieldToggleInput.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. <!-- Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import stopEvent from '@shared/utils/events'
  4. import { computed, nextTick, toRef, watch } from 'vue'
  5. import useValue from '../../composables/useValue'
  6. import type { FormFieldContext } from '../../types/field'
  7. const props = defineProps<{
  8. context: FormFieldContext<{
  9. // TODO: need to be changed to "options", because otherwise core workflow can not handle this
  10. variants?: {
  11. true?: string
  12. false?: string
  13. }
  14. }>
  15. }>()
  16. const context = toRef(props, 'context')
  17. const { localValue } = useValue(context)
  18. const variants = computed(() => props.context.variants || {})
  19. watch(
  20. () => props.context.variants,
  21. (variants) => {
  22. if (!variants) {
  23. console.warn(
  24. 'FieldToggleInput: variants prop is required, but not provided',
  25. )
  26. return
  27. }
  28. if (localValue.value === undefined) {
  29. const options = Object.keys(variants)
  30. if (options.length === 1) {
  31. nextTick(() => {
  32. localValue.value = options[0] === 'true'
  33. })
  34. }
  35. return
  36. }
  37. const valueString = localValue.value ? 'true' : 'false'
  38. // if current value is not removed from options, we don't need to reset it
  39. if (valueString in variants) return
  40. // current value was removed from options, so we need reset it
  41. // if other value exists, fallback to it, otherwise set to undefined
  42. const newValueString = localValue.value ? 'false' : 'true'
  43. const newValue = newValueString in variants ? !localValue.value : undefined
  44. localValue.value = newValue
  45. },
  46. { immediate: true },
  47. )
  48. const disabled = computed(() => {
  49. if (props.context.disabled) return true
  50. const nextValueString = localValue.value ? 'false' : 'true'
  51. // if ca't select next value, disable the toggle
  52. return !(nextValueString in variants.value)
  53. })
  54. const updateLocalValue = (e: Event) => {
  55. stopEvent(e)
  56. if (disabled.value) return
  57. const newValue = localValue.value ? 'false' : 'true'
  58. if (newValue in variants.value) {
  59. localValue.value = newValue === 'true'
  60. }
  61. }
  62. </script>
  63. <template>
  64. <input
  65. :id="context.id"
  66. class="hidden"
  67. type="checkbox"
  68. tabindex="-1"
  69. :disabled="disabled"
  70. :checked="localValue"
  71. @change="updateLocalValue"
  72. />
  73. <div
  74. class="relative inline-flex h-6 w-10 flex-shrink-0 cursor-pointer rounded-full border border-transparent bg-gray-300 transition-colors duration-200 ease-in-out focus-within:ring-1 focus-within:ring-white focus-within:ring-opacity-75 focus:outline-none formkit-invalid:border-solid formkit-invalid:border-red"
  75. :class="{
  76. '!bg-blue': localValue,
  77. }"
  78. aria-hidden="true"
  79. :tabindex="props.context.disabled ? '-1' : '0'"
  80. @click="updateLocalValue"
  81. @keydown.space="updateLocalValue"
  82. >
  83. <div
  84. class="pointer-events-none inline-block h-[22px] w-[22px] translate-x-0 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out"
  85. :class="{
  86. 'translate-x-4': localValue,
  87. }"
  88. ></div>
  89. </div>
  90. </template>