FieldSelectInput.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { toRef, useTemplateRef } from 'vue'
  4. import useValue from '#shared/components/Form/composables/useValue.ts'
  5. import type { SelectContext } from '#shared/components/Form/fields/FieldSelect/types.ts'
  6. import useSelectOptions from '#shared/composables/useSelectOptions.ts'
  7. import useSelectPreselect from '#shared/composables/useSelectPreselect.ts'
  8. import { useFormBlock } from '#shared/form/useFormBlock.ts'
  9. import { EnumTicketStateColorCode } from '#shared/graphql/types.ts'
  10. import { i18n } from '#shared/i18n.ts'
  11. import CommonSelect from '#mobile/components/CommonSelect/CommonSelect.vue'
  12. import CommonTicketStateIndicator from '#mobile/components/CommonTicketStateIndicator/CommonTicketStateIndicator.vue'
  13. interface Props {
  14. context: SelectContext
  15. }
  16. const props = defineProps<Props>()
  17. const contextReactive = toRef(props, 'context')
  18. const { hasValue, valueContainer, currentValue, clearValue } =
  19. useValue(contextReactive)
  20. const {
  21. hasStatusProperty,
  22. sortedOptions,
  23. selectOption,
  24. getSelectedOptionIcon,
  25. getSelectedOptionLabel,
  26. getSelectedOptionStatus,
  27. setupMissingOrDisabledOptionHandling,
  28. } = useSelectOptions(toRef(props.context, 'options'), contextReactive)
  29. const select = useTemplateRef('select')
  30. const openSelectDialog = () => {
  31. if (
  32. select.value?.isOpen ||
  33. !props.context.options?.length ||
  34. props.context.disabled
  35. )
  36. return
  37. select.value?.openDialog()
  38. }
  39. useFormBlock(contextReactive, openSelectDialog)
  40. useSelectPreselect(sortedOptions, contextReactive)
  41. setupMissingOrDisabledOptionHandling()
  42. </script>
  43. <template>
  44. <div
  45. :class="[
  46. context.classes.input,
  47. 'flex h-auto',
  48. {
  49. 'ltr:pr-9 rtl:pl-9': context.clearable && hasValue && !context.disabled,
  50. },
  51. ]"
  52. data-test-id="field-select"
  53. >
  54. <CommonSelect
  55. ref="select"
  56. #default="{ state: expanded }"
  57. :model-value="currentValue"
  58. :options="sortedOptions"
  59. :multiple="context.multiple"
  60. :owner="context.id"
  61. no-options-label-translation
  62. passive
  63. @select="selectOption"
  64. >
  65. <output
  66. :id="context.id"
  67. ref="outputElement"
  68. role="combobox"
  69. aria-controls="common-select"
  70. aria-owns="common-select"
  71. aria-haspopup="dialog"
  72. :aria-expanded="expanded"
  73. :name="context.node.name"
  74. class="formkit-disabled:pointer-events-none flex grow items-center focus:outline-none"
  75. :aria-labelledby="`label-${context.id}`"
  76. :aria-disabled="context.disabled"
  77. :data-multiple="context.multiple"
  78. tabindex="0"
  79. v-bind="context.attrs"
  80. @keyup.shift.down.prevent="openSelectDialog()"
  81. @keypress.space.prevent="openSelectDialog()"
  82. @blur="context.handlers.blur"
  83. >
  84. <div v-if="hasValue" class="flex grow flex-wrap gap-1" role="list">
  85. <template v-if="hasValue && hasStatusProperty">
  86. <CommonTicketStateIndicator
  87. v-for="selectedValue in valueContainer"
  88. :key="selectedValue"
  89. :color-code="
  90. getSelectedOptionStatus(
  91. selectedValue,
  92. ) as EnumTicketStateColorCode
  93. "
  94. :label="
  95. getSelectedOptionLabel(selectedValue) ||
  96. i18n.t('%s (unknown)', selectedValue)
  97. "
  98. :data-test-status="getSelectedOptionStatus(selectedValue)"
  99. role="listitem"
  100. pill
  101. />
  102. </template>
  103. <template v-else-if="hasValue">
  104. <div
  105. v-for="(selectedValue, idx) in valueContainer"
  106. :key="selectedValue"
  107. class="flex items-center text-base leading-[19px]"
  108. role="listitem"
  109. >
  110. <CommonIcon
  111. v-if="getSelectedOptionIcon(selectedValue)"
  112. :name="getSelectedOptionIcon(selectedValue)"
  113. size="tiny"
  114. class="ltr:mr-1 rtl:ml-1"
  115. decorative
  116. />{{
  117. getSelectedOptionLabel(selectedValue) ||
  118. i18n.t('%s (unknown)', selectedValue)
  119. }}{{ idx === valueContainer.length - 1 ? '' : ',' }}
  120. </div>
  121. </template>
  122. </div>
  123. <CommonIcon
  124. v-if="context.clearable && hasValue && !context.disabled"
  125. :aria-label="i18n.t('Clear Selection')"
  126. class="text-gray absolute -mt-5 shrink-0 ltr:right-2 rtl:left-2"
  127. name="close-small"
  128. size="base"
  129. role="button"
  130. tabindex="0"
  131. @click.stop="clearValue()"
  132. @keypress.space.prevent.stop="clearValue()"
  133. />
  134. </output>
  135. </CommonSelect>
  136. </div>
  137. </template>