classcard-ui 0.2.1497 → 0.2.1498-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/README.md +24 -24
  2. package/dist/classcard-ui.common.js +181 -142
  3. package/dist/classcard-ui.common.js.map +1 -1
  4. package/dist/classcard-ui.css +1 -1
  5. package/dist/classcard-ui.umd.js +181 -142
  6. package/dist/classcard-ui.umd.js.map +1 -1
  7. package/dist/classcard-ui.umd.min.js +2 -2
  8. package/dist/classcard-ui.umd.min.js.map +1 -1
  9. package/package.json +84 -84
  10. package/src/App.vue +16 -16
  11. package/src/colorConfig.js +52 -52
  12. package/src/components/CAlertModal/CAlertModal.vue +179 -179
  13. package/src/components/CAlertModal/index.js +3 -3
  14. package/src/components/CAlerts/CAlerts.vue +114 -114
  15. package/src/components/CAlerts/index.js +2 -2
  16. package/src/components/CAnchorTabs/CAnchorTabs.vue +100 -100
  17. package/src/components/CAnchorTabs/index.js +2 -2
  18. package/src/components/CAnchorTag/CAnchorTag.vue +84 -84
  19. package/src/components/CAnchorTag/index.js +2 -2
  20. package/src/components/CAvatar/CAvatar.vue +232 -232
  21. package/src/components/CAvatar/index.js +2 -2
  22. package/src/components/CAvatarGroup/CAvatarGroup.vue +213 -213
  23. package/src/components/CAvatarGroup/index.js +2 -2
  24. package/src/components/CBasicTable/CBasicTable.vue +184 -184
  25. package/src/components/CBasicTable/index.js +2 -2
  26. package/src/components/CBreadcrumbs/CBreadcrumbs.vue +38 -38
  27. package/src/components/CBreadcrumbs/index.js +2 -2
  28. package/src/components/CButton/CButton.vue +239 -239
  29. package/src/components/CButton/index.js +2 -2
  30. package/src/components/CButtonGroup/CButtonGroup.vue +155 -155
  31. package/src/components/CButtonGroup/index.js +2 -2
  32. package/src/components/CButtonIcon/CButtonIcon.vue +166 -166
  33. package/src/components/CButtonIcon/index.js +2 -2
  34. package/src/components/CButtonLink/CButtonLink.vue +43 -43
  35. package/src/components/CButtonLink/index.js +2 -2
  36. package/src/components/CButtonSelect/CButtonSelect.vue +186 -186
  37. package/src/components/CButtonSelect/index.js +2 -2
  38. package/src/components/CButtonSelectBorder/CButtonSelectBorder.vue +265 -265
  39. package/src/components/CButtonSelectBorder/index.js +3 -3
  40. package/src/components/CButtonWithDropdown/CButtonWithDropdown.vue +208 -208
  41. package/src/components/CButtonWithDropdown/index.js +2 -2
  42. package/src/components/CCalendar/CCalendar.vue +443 -443
  43. package/src/components/CCalendar/index.js +3 -3
  44. package/src/components/CCard/CCard.vue +53 -53
  45. package/src/components/CCard/index.js +2 -2
  46. package/src/components/CCheckbox/CCheckbox.vue +200 -200
  47. package/src/components/CCheckbox/index.js +2 -2
  48. package/src/components/CCircularButton/CCircularButton.vue +57 -57
  49. package/src/components/CCircularButton/index.js +2 -2
  50. package/src/components/CCollapsibleSection/CCollapsibleSection.vue +121 -121
  51. package/src/components/CCollapsibleSection/index.js +2 -2
  52. package/src/components/CColorDots/CColorDots.vue +52 -52
  53. package/src/components/CColorDots/index.js +3 -3
  54. package/src/components/CConfirmActionModal/CConfirmActionModal.vue +253 -253
  55. package/src/components/CConfirmActionModal/index.js +3 -3
  56. package/src/components/CDatepicker/CDatepicker.vue +235 -235
  57. package/src/components/CDatepicker/index.js +2 -2
  58. package/src/components/CDualSelect/CDualSelect.vue +193 -193
  59. package/src/components/CDualSelect/index.js +2 -2
  60. package/src/components/CEditor/CEditor.vue +137 -137
  61. package/src/components/CEditor/index.js +6 -6
  62. package/src/components/CEditor/quill-font-setup.js +133 -133
  63. package/src/components/CFormSectionHeading/CFormSectionHeading.vue +76 -76
  64. package/src/components/CFormSectionHeading/index.js +2 -2
  65. package/src/components/CGroupedFilterDropdown/CGroupedFilterDropdown.vue +331 -331
  66. package/src/components/CGroupedFilterDropdown/index.js +2 -2
  67. package/src/components/CGroupedSelect/CGroupedSelect.vue +393 -393
  68. package/src/components/CGroupedSelect/index.js +3 -3
  69. package/src/components/CIcon/CIcon.vue +112 -112
  70. package/src/components/CIcon/index.js +2 -2
  71. package/src/components/CIconDropdown/CIconDropdown.vue +206 -206
  72. package/src/components/CIconDropdown/index.js +2 -2
  73. package/src/components/CIconSelect/CIconSelect.vue +182 -182
  74. package/src/components/CIconSelect/index.js +3 -3
  75. package/src/components/CInput/CInput.vue +173 -173
  76. package/src/components/CInput/index.js +2 -2
  77. package/src/components/CInputAddon/CInputAddon.vue +302 -297
  78. package/src/components/CInputAddon/index.js +2 -2
  79. package/src/components/CInputEmail/CInputEmail.vue +107 -107
  80. package/src/components/CInputEmail/index.js +2 -2
  81. package/src/components/CInsetTabs/CInsetTabs.vue +141 -141
  82. package/src/components/CInsetTabs/index.js +3 -3
  83. package/src/components/CLinearProgressBar/CLinearProgressBar.vue +35 -35
  84. package/src/components/CLinearProgressBar/index.js +2 -2
  85. package/src/components/CModalHeading/CModalHeading.vue +22 -22
  86. package/src/components/CModalHeading/index.js +2 -2
  87. package/src/components/CModuleHelpLinks/CModuleHelpLinks.vue +88 -88
  88. package/src/components/CModuleHelpLinks/index.js +3 -3
  89. package/src/components/CMultiselect/CMultiselect.vue +1307 -1307
  90. package/src/components/CMultiselect/index.js +2 -2
  91. package/src/components/CMultiselectr/CMultiselectr.vue +44 -44
  92. package/src/components/CMultiselectr/index.js +2 -2
  93. package/src/components/CPageHeading/CPageHeading.vue +83 -83
  94. package/src/components/CPageHeading/index.js +2 -2
  95. package/src/components/CPagination/CPagination.vue +239 -239
  96. package/src/components/CPagination/index.js +2 -2
  97. package/src/components/CPhoneNumber/CPhoneNumber.vue +213 -213
  98. package/src/components/CPhoneNumber/index.js +2 -2
  99. package/src/components/CProgress/CProgress.vue +91 -91
  100. package/src/components/CProgress/index.js +2 -2
  101. package/src/components/CRadio/CRadio.vue +197 -197
  102. package/src/components/CRadio/index.js +2 -2
  103. package/src/components/CRadioGroup/CRadioGroup.vue +96 -96
  104. package/src/components/CRadioGroup/index.js +2 -2
  105. package/src/components/CRangeSlider/CRangeSlider.vue +55 -55
  106. package/src/components/CRangeSlider/index.js +2 -2
  107. package/src/components/CReorderableStackedList/CReorderableStackedList.vue +94 -94
  108. package/src/components/CReorderableStackedList/index.js +2 -2
  109. package/src/components/CSelect/CSelect.vue +1216 -1216
  110. package/src/components/CSelect/index.js +2 -2
  111. package/src/components/CSmallTimeline/CSmallTimeline.vue +40 -40
  112. package/src/components/CSmallTimeline/index.js +2 -2
  113. package/src/components/CStackedList/CStackedList.vue +162 -162
  114. package/src/components/CStackedList/index.js +2 -2
  115. package/src/components/CStats/CStats.vue +157 -157
  116. package/src/components/CStats/index.js +2 -2
  117. package/src/components/CSwitch/CSwitch.vue +200 -200
  118. package/src/components/CSwitch/index.js +2 -2
  119. package/src/components/CTabLazy/CTabLazy.vue +83 -83
  120. package/src/components/CTabLazy/index.js +2 -2
  121. package/src/components/CTable/CTable.vue +1114 -1114
  122. package/src/components/CTable/index.js +2 -2
  123. package/src/components/CTabs/CTabs.vue +250 -250
  124. package/src/components/CTabs/index.js +2 -2
  125. package/src/components/CTag/CTag.vue +109 -109
  126. package/src/components/CTag/index.js +2 -2
  127. package/src/components/CTextarea/CTextarea.vue +118 -118
  128. package/src/components/CTextarea/index.js +2 -2
  129. package/src/components/CTimeline/CTimeline.vue +237 -237
  130. package/src/components/CTimeline/index.js +2 -2
  131. package/src/components/CTokenEditor/CTokenEditor.vue +804 -804
  132. package/src/components/CTokenEditor/index.js +3 -3
  133. package/src/components/CToolTip/CToolTip.vue +108 -108
  134. package/src/components/CToolTip/index.js +3 -3
  135. package/src/components/CUpload/CUpload.vue +332 -332
  136. package/src/components/CUpload/index.js +2 -2
  137. package/src/components/NumberAnimator.vue +112 -112
  138. package/src/components/index.js +60 -60
  139. package/src/helper.js +8 -8
  140. package/src/icons.js +833 -833
  141. package/src/main.js +22 -22
  142. package/src/stories/CAlertModal.stories.js +30 -30
  143. package/src/stories/CAlerts.stories.js +39 -39
  144. package/src/stories/CAnchorTabs.stories.js +29 -29
  145. package/src/stories/CAnchorTag.stories.js +38 -38
  146. package/src/stories/CAvatar.stories.js +38 -38
  147. package/src/stories/CAvatarGroup.stories.js +136 -136
  148. package/src/stories/CBasicTable.stories.js +316 -316
  149. package/src/stories/CBreadcrumbs.stories.js +24 -24
  150. package/src/stories/CButton.stories.js +49 -49
  151. package/src/stories/CButtonGroup.stories.js +43 -43
  152. package/src/stories/CButtonIcon.stories.js +27 -27
  153. package/src/stories/CButtonLink.stories.js +24 -24
  154. package/src/stories/CButtonSelect.stories.js +44 -44
  155. package/src/stories/CButtonSelectBorder.stories.js +56 -56
  156. package/src/stories/CButtonWithDropdown.stories.js +41 -41
  157. package/src/stories/CCalendar.stories.js +16 -16
  158. package/src/stories/CCard.stories.js +30 -30
  159. package/src/stories/CCheckbox.stories.js +38 -38
  160. package/src/stories/CCircularButton.stories.js +29 -29
  161. package/src/stories/CCollapsibleSection.stories.js +29 -29
  162. package/src/stories/CColorDots.stories.js +37 -37
  163. package/src/stories/CConfirmActionModal.stories.js +67 -67
  164. package/src/stories/CDatepicker.stories.js +31 -31
  165. package/src/stories/CDualSelect.stories.js +29 -29
  166. package/src/stories/CEditor.stories.js +36 -36
  167. package/src/stories/CFormSectionHeading.stories.js +37 -37
  168. package/src/stories/CGroupedFilterDropdown.stories.js +131 -131
  169. package/src/stories/CGroupedSelect.stories.js +103 -103
  170. package/src/stories/CIcon.stories.js +31 -31
  171. package/src/stories/CIconDropdown.stories.js +52 -52
  172. package/src/stories/CIconSelect.stories.js +45 -45
  173. package/src/stories/CInput.stories.js +36 -36
  174. package/src/stories/CInputAddon.stories.js +37 -37
  175. package/src/stories/CInputEmail.stories.js +27 -27
  176. package/src/stories/CInsetTabs.stories.js +48 -48
  177. package/src/stories/CLinearProgressBar.stories.js +22 -22
  178. package/src/stories/CModalHeading.stories.js +25 -25
  179. package/src/stories/CModuleHelpLinks.stories.js +25 -25
  180. package/src/stories/CMultiselect.stories.js +146 -146
  181. package/src/stories/CMultiselectr.stories.js +23 -23
  182. package/src/stories/CPageHeading.stories.js +32 -32
  183. package/src/stories/CPagination.stories.js +30 -30
  184. package/src/stories/CPhoneNumber.stories.js +37 -37
  185. package/src/stories/CProgress.stories.js +23 -23
  186. package/src/stories/CRadio.stories.js +44 -44
  187. package/src/stories/CRadioGroup.stories.js +51 -51
  188. package/src/stories/CRangeSlider.stories.js +23 -23
  189. package/src/stories/CReorderableStackedList.stories.js +23 -23
  190. package/src/stories/CSelect.stories.js +157 -157
  191. package/src/stories/CSmallTimeline.stories.js +26 -26
  192. package/src/stories/CStackedList.stories.js +37 -37
  193. package/src/stories/CStats.stories.js +53 -53
  194. package/src/stories/CSwitch.stories.js +28 -28
  195. package/src/stories/CTabLazy.stories.js +42 -42
  196. package/src/stories/CTable.stories.js +203 -203
  197. package/src/stories/CTabs.stories.js +36 -36
  198. package/src/stories/CTag.stories.js +37 -37
  199. package/src/stories/CTextarea.stories.js +32 -32
  200. package/src/stories/CTimeline.stories.js +26 -26
  201. package/src/stories/CTokenEditor.stories.js +136 -136
  202. package/src/stories/CToolTip.stories.js +27 -27
  203. package/src/stories/CUpload.stories.js +36 -36
  204. package/src/stories/Introduction.stories.mdx +207 -207
  205. package/src/stories/Page.vue +88 -88
  206. package/src/stories/assets/code-brackets.svg +0 -0
  207. package/src/stories/assets/colors.svg +0 -0
  208. package/src/stories/assets/comments.svg +0 -0
  209. package/src/stories/assets/direction.svg +0 -0
  210. package/src/stories/assets/flow.svg +0 -0
  211. package/src/stories/assets/plugin.svg +0 -0
  212. package/src/stories/assets/repo.svg +0 -0
  213. package/src/stories/assets/stackalt.svg +0 -0
  214. package/src/stories/header.css +26 -26
  215. package/src/stories/page.css +69 -69
  216. package/src/stories/utils.css +32 -32
@@ -1,393 +1,393 @@
1
- <template>
2
- <div>
3
- <div
4
- v-if="showDropDown && options.length > 0"
5
- class="fixed inset-0"
6
- @click="closeDropDown(false)"
7
- aria-hidden="true"
8
- ></div>
9
- <div class="flex items-center" v-if="label">
10
- <!-- label of select field -->
11
- <label class="block text-sm font-medium text-gray-900">
12
- {{ label }}
13
- </label>
14
- <!-- asterisk sign to render if field is required -->
15
- <p v-if="isRequired" class="ml-1 text-red-600">*</p>
16
- </div>
17
- <div class="relative mt-1" ref="inputContainer">
18
- <div class="relative">
19
- <input
20
- type="text"
21
- class="relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:pointer-events-none disabled:opacity-50 sm:text-sm"
22
- aria-haspopup="listbox"
23
- aria-expanded="true"
24
- aria-labelledby="listbox-label"
25
- :value="inputValue"
26
- :placeholder="placeholder"
27
- autocomplete="off"
28
- @focus="openDropDown()"
29
- @input="onInput"
30
- :disabled="disabled"
31
- :id="id + '_input_search'"
32
- />
33
- <span
34
- class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
35
- >
36
- <svg
37
- class="h-5 w-5 animate-spin text-gray-400"
38
- xmlns="http://www.w3.org/2000/svg"
39
- fill="none"
40
- viewBox="0 0 24 24"
41
- v-if="isSearching"
42
- >
43
- <circle
44
- class="opacity-25"
45
- cx="12"
46
- cy="12"
47
- r="10"
48
- stroke="currentColor"
49
- stroke-width="4"
50
- ></circle>
51
- <path
52
- class="opacity-75"
53
- fill="currentColor"
54
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
55
- ></path>
56
- </svg>
57
- <!-- Heroicon name: solid/selector -->
58
- <svg
59
- class="h-5 w-5 text-gray-400"
60
- xmlns="http://www.w3.org/2000/svg"
61
- viewBox="0 0 20 20"
62
- fill="currentColor"
63
- aria-hidden="true"
64
- v-if="!isSearching"
65
- >
66
- <path
67
- fill-rule="evenodd"
68
- d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
69
- clip-rule="evenodd"
70
- />
71
- </svg>
72
- </span>
73
- <button
74
- class="z-100 absolute top-2.5 right-10"
75
- v-if="
76
- showCloseButton && (inputValue == null || inputValue.length == 0)
77
- "
78
- @click.stop="handleCrossClick"
79
- :id="id + '_button_close_dropdown'"
80
- >
81
- <c-icon
82
- name="close"
83
- type="outline-v2"
84
- class="h-4 w-4 text-gray-400"
85
- cursorType="cursor-pointer"
86
- />
87
- </button>
88
- </div>
89
- <!--
90
- Select popover, show/hide based on select state.
91
-
92
- Entering: ""
93
- From: ""
94
- To: ""
95
- Leaving: "transition ease-in duration-100"
96
- From: "opacity-100"
97
- To: "opacity-0"
98
- -->
99
- <div
100
- v-if="showDropDown"
101
- :class="[
102
- shouldOpenAbove ? 'bottom-full mb-1' : 'top-full mt-1',
103
- 'absolute z-10 max-h-60 w-full',
104
- ]"
105
- >
106
- <ul
107
- tabindex="-1"
108
- role="listbox"
109
- aria-labelledby="listbox-label"
110
- aria-activedescendant="listbox-option-3"
111
- v-if="options.length > 0"
112
- class="max-h-60 overflow-auto overscroll-contain rounded-md bg-white text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
113
- >
114
- <!--
115
- Select option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
116
-
117
- Highlighted: "text-white bg-indigo-600", Not Highlighted: "text-gray-900"
118
- -->
119
-
120
- <template v-for="option in options">
121
- <li
122
- :key="option.id"
123
- class="sticky top-0 z-20 cursor-default select-none bg-gray-100 px-3 py-2 text-left text-sm text-gray-700"
124
- role="option"
125
- >
126
- <span class="block truncate"> {{ option.label }} </span>
127
- </li>
128
- <li
129
- v-for="subOption in option.options"
130
- :class="[
131
- 'group relative flex select-none justify-between gap-3 px-3 py-2 text-left text-gray-900',
132
- customiseCSS ? 'flex' : '',
133
- subOption.isDisabled
134
- ? 'cursor-not-allowed opacity-50'
135
- : 'cursor-default hover:bg-gray-100 hover:text-gray-900',
136
- ]"
137
- :key="subOption.id"
138
- role="option"
139
- @click="handleOptionClick(subOption)"
140
- >
141
- <!-- custom slot for option ui -->
142
- <slot
143
- v-if="$scopedSlots['custom-option']"
144
- name="custom-option"
145
- :subOption="subOption"
146
- :option="option"
147
- :keyField="keyField"
148
- ></slot>
149
-
150
- <!-- default option ui -->
151
- <template v-else>
152
- <div class="flex w-full items-center">
153
- <img
154
- v-if="subOption.image_url"
155
- :src="subOption.image_url"
156
- :class="[
157
- 'mr-3 h-10 w-10 flex-shrink-0 object-cover',
158
- subOption.type === 'user' ? 'rounded-full' : 'rounded-md',
159
- ]"
160
- loading="lazy"
161
- :alt="subOption.label"
162
- />
163
- <div
164
- v-if="
165
- !subOption.image_url &&
166
- subOption.type === 'user' &&
167
- subOption.initials
168
- "
169
- class="mr-3 flex h-10 w-10 items-center justify-center rounded-full bg-gray-500 text-white"
170
- >
171
- <span class="text-base text-white">{{
172
- subOption.initials
173
- }}</span>
174
- </div>
175
- <div
176
- v-if="!subOption.image_url && subOption.icon"
177
- class="mr-3 flex h-10 w-10 items-center justify-center rounded-md bg-gray-100"
178
- >
179
- <c-icon
180
- :name="subOption.icon.name"
181
- :type="subOption.icon.type"
182
- :class="subOption.icon.class"
183
- :viewBox="subOption.icon.viewBox"
184
- ></c-icon>
185
- </div>
186
- <span class="flex-1">
187
- <div class="flex items-center justify-between">
188
- <span
189
- class="flex"
190
- :class="[
191
- subOption[keyField] === value[keyField]
192
- ? 'font-semibold'
193
- : 'font-normal',
194
- ' block break-words',
195
- ]"
196
- >
197
- {{ subOption.label }}
198
- <span
199
- v-if="subOption.labelExtra"
200
- class="ml-1 text-sm text-gray-400 group-hover:text-gray-400"
201
- >{{ subOption.labelExtra }}</span
202
- >
203
- <div
204
- v-if="
205
- subOption.stock_level ||
206
- (subOption.stock_level <= 0 &&
207
- subOption.stock_level !== null)
208
- "
209
- >
210
- <p
211
- class="ml-2 text-sm"
212
- :class="[quantityLabelColor(subOption)]"
213
- >
214
- {{ subOption.stock_level }} units
215
- </p>
216
- </div>
217
- </span>
218
- <slot name="custom-tag" :subOption="subOption"></slot>
219
- </div>
220
- <span
221
- class="mt-1 text-sm font-normal text-gray-500 group-hover:text-gray-500"
222
- v-if="subOption.description"
223
- >
224
- {{ subOption.description }}
225
- </span>
226
- </span>
227
- </div>
228
- <p
229
- v-if="subOption.price"
230
- class="relative flex items-center whitespace-nowrap text-sm text-gray-900"
231
- >
232
- {{ subOption.price }}
233
- </p>
234
- <span
235
- class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-hover:text-white"
236
- v-if="subOption[keyField] === value[keyField]"
237
- >
238
- <svg
239
- class="h-5 w-5"
240
- xmlns="http://www.w3.org/2000/svg"
241
- viewBox="0 0 20 20"
242
- fill="currentColor"
243
- aria-hidden="true"
244
- >
245
- <path
246
- fill-rule="evenodd"
247
- d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
248
- clip-rule="evenodd"
249
- />
250
- </svg>
251
- </span>
252
- </template>
253
- </li>
254
- </template>
255
- </ul>
256
- <p
257
- v-else
258
- class="rounded-md bg-white py-2 text-center text-sm shadow-lg ring-1 ring-black ring-opacity-5"
259
- >
260
- No match found. Try a different search.
261
- </p>
262
- </div>
263
- </div>
264
- <p v-if="isError" class="mt-2 text-left text-sm text-red-600">
265
- {{ errorMessage }}
266
- </p>
267
- </div>
268
- </template>
269
-
270
- <script>
271
- import { debounce } from "lodash-es";
272
- import CIcon from "../CIcon/CIcon.vue";
273
-
274
- export default {
275
- name: "CGroupedSelect",
276
- components: {
277
- CIcon,
278
- },
279
- props: {
280
- label: String,
281
- options: Array,
282
- value: Object,
283
- placeholder: String,
284
- isSearching: Boolean,
285
- disabled: { type: Boolean, default: false },
286
- isError: { type: Boolean, default: false },
287
- errorMessage: {
288
- type: String,
289
- default: "This field cannot be empty",
290
- },
291
- keyField: { type: String, default: "label" },
292
- // whether the select field is mandatory or not
293
- isRequired: {
294
- type: Boolean,
295
- },
296
- customiseCSS: {
297
- type: Boolean,
298
- default: false,
299
- },
300
- showCloseButton: {
301
- type: Boolean,
302
- default: false,
303
- },
304
- // whether to search when the select field is focused
305
- searchOnFocus: {
306
- type: Boolean,
307
- default: true,
308
- },
309
- },
310
- data() {
311
- return {
312
- showDropDown: false,
313
- inputValue: "",
314
- shouldOpenAbove: false,
315
- };
316
- },
317
- methods: {
318
- updateDropdownPlacement() {
319
- if (!this.$refs.inputContainer) {
320
- this.shouldOpenAbove = false;
321
- return;
322
- }
323
-
324
- const rect = this.$refs.inputContainer.getBoundingClientRect();
325
- const dropdownMaxHeight = 240; // max height of the dropdown in pixels - max-h-60
326
- const bottomSpace = window.innerHeight - rect.bottom;
327
- const topSpace = rect.top;
328
-
329
- this.shouldOpenAbove =
330
- bottomSpace < dropdownMaxHeight && topSpace > bottomSpace;
331
- },
332
- handleCrossClick(event) {
333
- event.preventDefault();
334
- this.$emit("hide-grouped-select-dropdown");
335
- },
336
- onInput(e) {
337
- this.inputValue = e.target.value;
338
- this.emitSearch(e.target.value);
339
- },
340
- emitSearch: debounce(function (value) {
341
- if (value !== this.value.label) {
342
- this.$emit("search", value);
343
- } else {
344
- this.$emit("search", "");
345
- }
346
- }, 500),
347
- handleOptionClick(option) {
348
- this.inputValue = option.label;
349
- this.closeDropDown(true);
350
- this.$emit("input", option);
351
- },
352
- closeDropDown(hasData) {
353
- this.inputValue = this.value.label;
354
- this.showDropDown = false;
355
- this.$emit("blur", { hasData: hasData });
356
- },
357
- openDropDown() {
358
- this.showDropDown = true;
359
- if (this.searchOnFocus) {
360
- this.emitSearch("");
361
- }
362
- },
363
- quantityLabelColor(data) {
364
- if (data.stock_level <= 0) {
365
- return "text-red-300";
366
- } else if (data.stock_level < data.alert_level) {
367
- return "text-orange-300";
368
- } else {
369
- return "text-gray-300";
370
- }
371
- },
372
- },
373
- watch: {
374
- value(newValue) {
375
- this.inputValue = newValue.label;
376
- },
377
- showDropDown(newValue) {
378
- if (newValue) {
379
- this.$nextTick(this.updateDropdownPlacement);
380
- }
381
- },
382
- },
383
- mounted() {
384
- this.inputValue = this.value.label;
385
- },
386
- };
387
- </script>
388
-
389
- <style scoped>
390
- input::placeholder {
391
- @apply text-gray-500;
392
- }
393
- </style>
1
+ <template>
2
+ <div>
3
+ <div
4
+ v-if="showDropDown && options.length > 0"
5
+ class="fixed inset-0"
6
+ @click="closeDropDown(false)"
7
+ aria-hidden="true"
8
+ ></div>
9
+ <div class="flex items-center" v-if="label">
10
+ <!-- label of select field -->
11
+ <label class="block text-sm font-medium text-gray-900">
12
+ {{ label }}
13
+ </label>
14
+ <!-- asterisk sign to render if field is required -->
15
+ <p v-if="isRequired" class="ml-1 text-red-600">*</p>
16
+ </div>
17
+ <div class="relative mt-1" ref="inputContainer">
18
+ <div class="relative">
19
+ <input
20
+ type="text"
21
+ class="relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:pointer-events-none disabled:opacity-50 sm:text-sm"
22
+ aria-haspopup="listbox"
23
+ aria-expanded="true"
24
+ aria-labelledby="listbox-label"
25
+ :value="inputValue"
26
+ :placeholder="placeholder"
27
+ autocomplete="off"
28
+ @focus="openDropDown()"
29
+ @input="onInput"
30
+ :disabled="disabled"
31
+ :id="id + '_input_search'"
32
+ />
33
+ <span
34
+ class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
35
+ >
36
+ <svg
37
+ class="h-5 w-5 animate-spin text-gray-400"
38
+ xmlns="http://www.w3.org/2000/svg"
39
+ fill="none"
40
+ viewBox="0 0 24 24"
41
+ v-if="isSearching"
42
+ >
43
+ <circle
44
+ class="opacity-25"
45
+ cx="12"
46
+ cy="12"
47
+ r="10"
48
+ stroke="currentColor"
49
+ stroke-width="4"
50
+ ></circle>
51
+ <path
52
+ class="opacity-75"
53
+ fill="currentColor"
54
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
55
+ ></path>
56
+ </svg>
57
+ <!-- Heroicon name: solid/selector -->
58
+ <svg
59
+ class="h-5 w-5 text-gray-400"
60
+ xmlns="http://www.w3.org/2000/svg"
61
+ viewBox="0 0 20 20"
62
+ fill="currentColor"
63
+ aria-hidden="true"
64
+ v-if="!isSearching"
65
+ >
66
+ <path
67
+ fill-rule="evenodd"
68
+ d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
69
+ clip-rule="evenodd"
70
+ />
71
+ </svg>
72
+ </span>
73
+ <button
74
+ class="z-100 absolute top-2.5 right-10"
75
+ v-if="
76
+ showCloseButton && (inputValue == null || inputValue.length == 0)
77
+ "
78
+ @click.stop="handleCrossClick"
79
+ :id="id + '_button_close_dropdown'"
80
+ >
81
+ <c-icon
82
+ name="close"
83
+ type="outline-v2"
84
+ class="h-4 w-4 text-gray-400"
85
+ cursorType="cursor-pointer"
86
+ />
87
+ </button>
88
+ </div>
89
+ <!--
90
+ Select popover, show/hide based on select state.
91
+
92
+ Entering: ""
93
+ From: ""
94
+ To: ""
95
+ Leaving: "transition ease-in duration-100"
96
+ From: "opacity-100"
97
+ To: "opacity-0"
98
+ -->
99
+ <div
100
+ v-if="showDropDown"
101
+ :class="[
102
+ shouldOpenAbove ? 'bottom-full mb-1' : 'top-full mt-1',
103
+ 'absolute z-10 max-h-60 w-full',
104
+ ]"
105
+ >
106
+ <ul
107
+ tabindex="-1"
108
+ role="listbox"
109
+ aria-labelledby="listbox-label"
110
+ aria-activedescendant="listbox-option-3"
111
+ v-if="options.length > 0"
112
+ class="max-h-60 overflow-auto overscroll-contain rounded-md bg-white text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
113
+ >
114
+ <!--
115
+ Select option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
116
+
117
+ Highlighted: "text-white bg-indigo-600", Not Highlighted: "text-gray-900"
118
+ -->
119
+
120
+ <template v-for="option in options">
121
+ <li
122
+ :key="option.id"
123
+ class="sticky top-0 z-20 cursor-default select-none bg-gray-100 px-3 py-2 text-left text-sm text-gray-700"
124
+ role="option"
125
+ >
126
+ <span class="block truncate"> {{ option.label }} </span>
127
+ </li>
128
+ <li
129
+ v-for="subOption in option.options"
130
+ :class="[
131
+ 'group relative flex select-none justify-between gap-3 px-3 py-2 text-left text-gray-900',
132
+ customiseCSS ? 'flex' : '',
133
+ subOption.isDisabled
134
+ ? 'cursor-not-allowed opacity-50'
135
+ : 'cursor-default hover:bg-gray-100 hover:text-gray-900',
136
+ ]"
137
+ :key="subOption.id"
138
+ role="option"
139
+ @click="handleOptionClick(subOption)"
140
+ >
141
+ <!-- custom slot for option ui -->
142
+ <slot
143
+ v-if="$scopedSlots['custom-option']"
144
+ name="custom-option"
145
+ :subOption="subOption"
146
+ :option="option"
147
+ :keyField="keyField"
148
+ ></slot>
149
+
150
+ <!-- default option ui -->
151
+ <template v-else>
152
+ <div class="flex w-full items-center">
153
+ <img
154
+ v-if="subOption.image_url"
155
+ :src="subOption.image_url"
156
+ :class="[
157
+ 'mr-3 h-10 w-10 flex-shrink-0 object-cover',
158
+ subOption.type === 'user' ? 'rounded-full' : 'rounded-md',
159
+ ]"
160
+ loading="lazy"
161
+ :alt="subOption.label"
162
+ />
163
+ <div
164
+ v-if="
165
+ !subOption.image_url &&
166
+ subOption.type === 'user' &&
167
+ subOption.initials
168
+ "
169
+ class="mr-3 flex h-10 w-10 items-center justify-center rounded-full bg-gray-500 text-white"
170
+ >
171
+ <span class="text-base text-white">{{
172
+ subOption.initials
173
+ }}</span>
174
+ </div>
175
+ <div
176
+ v-if="!subOption.image_url && subOption.icon"
177
+ class="mr-3 flex h-10 w-10 items-center justify-center rounded-md bg-gray-100"
178
+ >
179
+ <c-icon
180
+ :name="subOption.icon.name"
181
+ :type="subOption.icon.type"
182
+ :class="subOption.icon.class"
183
+ :viewBox="subOption.icon.viewBox"
184
+ ></c-icon>
185
+ </div>
186
+ <span class="flex-1">
187
+ <div class="flex items-center justify-between">
188
+ <span
189
+ class="flex"
190
+ :class="[
191
+ subOption[keyField] === value[keyField]
192
+ ? 'font-semibold'
193
+ : 'font-normal',
194
+ ' block break-words',
195
+ ]"
196
+ >
197
+ {{ subOption.label }}
198
+ <span
199
+ v-if="subOption.labelExtra"
200
+ class="ml-1 text-sm text-gray-400 group-hover:text-gray-400"
201
+ >{{ subOption.labelExtra }}</span
202
+ >
203
+ <div
204
+ v-if="
205
+ subOption.stock_level ||
206
+ (subOption.stock_level <= 0 &&
207
+ subOption.stock_level !== null)
208
+ "
209
+ >
210
+ <p
211
+ class="ml-2 text-sm"
212
+ :class="[quantityLabelColor(subOption)]"
213
+ >
214
+ {{ subOption.stock_level }} units
215
+ </p>
216
+ </div>
217
+ </span>
218
+ <slot name="custom-tag" :subOption="subOption"></slot>
219
+ </div>
220
+ <span
221
+ class="mt-1 text-sm font-normal text-gray-500 group-hover:text-gray-500"
222
+ v-if="subOption.description"
223
+ >
224
+ {{ subOption.description }}
225
+ </span>
226
+ </span>
227
+ </div>
228
+ <p
229
+ v-if="subOption.price"
230
+ class="relative flex items-center whitespace-nowrap text-sm text-gray-900"
231
+ >
232
+ {{ subOption.price }}
233
+ </p>
234
+ <span
235
+ class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 group-hover:text-white"
236
+ v-if="subOption[keyField] === value[keyField]"
237
+ >
238
+ <svg
239
+ class="h-5 w-5"
240
+ xmlns="http://www.w3.org/2000/svg"
241
+ viewBox="0 0 20 20"
242
+ fill="currentColor"
243
+ aria-hidden="true"
244
+ >
245
+ <path
246
+ fill-rule="evenodd"
247
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
248
+ clip-rule="evenodd"
249
+ />
250
+ </svg>
251
+ </span>
252
+ </template>
253
+ </li>
254
+ </template>
255
+ </ul>
256
+ <p
257
+ v-else
258
+ class="rounded-md bg-white py-2 text-center text-sm shadow-lg ring-1 ring-black ring-opacity-5"
259
+ >
260
+ No match found. Try a different search.
261
+ </p>
262
+ </div>
263
+ </div>
264
+ <p v-if="isError" class="mt-2 text-left text-sm text-red-600">
265
+ {{ errorMessage }}
266
+ </p>
267
+ </div>
268
+ </template>
269
+
270
+ <script>
271
+ import { debounce } from "lodash-es";
272
+ import CIcon from "../CIcon/CIcon.vue";
273
+
274
+ export default {
275
+ name: "CGroupedSelect",
276
+ components: {
277
+ CIcon,
278
+ },
279
+ props: {
280
+ label: String,
281
+ options: Array,
282
+ value: Object,
283
+ placeholder: String,
284
+ isSearching: Boolean,
285
+ disabled: { type: Boolean, default: false },
286
+ isError: { type: Boolean, default: false },
287
+ errorMessage: {
288
+ type: String,
289
+ default: "This field cannot be empty",
290
+ },
291
+ keyField: { type: String, default: "label" },
292
+ // whether the select field is mandatory or not
293
+ isRequired: {
294
+ type: Boolean,
295
+ },
296
+ customiseCSS: {
297
+ type: Boolean,
298
+ default: false,
299
+ },
300
+ showCloseButton: {
301
+ type: Boolean,
302
+ default: false,
303
+ },
304
+ // whether to search when the select field is focused
305
+ searchOnFocus: {
306
+ type: Boolean,
307
+ default: true,
308
+ },
309
+ },
310
+ data() {
311
+ return {
312
+ showDropDown: false,
313
+ inputValue: "",
314
+ shouldOpenAbove: false,
315
+ };
316
+ },
317
+ methods: {
318
+ updateDropdownPlacement() {
319
+ if (!this.$refs.inputContainer) {
320
+ this.shouldOpenAbove = false;
321
+ return;
322
+ }
323
+
324
+ const rect = this.$refs.inputContainer.getBoundingClientRect();
325
+ const dropdownMaxHeight = 240; // max height of the dropdown in pixels - max-h-60
326
+ const bottomSpace = window.innerHeight - rect.bottom;
327
+ const topSpace = rect.top;
328
+
329
+ this.shouldOpenAbove =
330
+ bottomSpace < dropdownMaxHeight && topSpace > bottomSpace;
331
+ },
332
+ handleCrossClick(event) {
333
+ event.preventDefault();
334
+ this.$emit("hide-grouped-select-dropdown");
335
+ },
336
+ onInput(e) {
337
+ this.inputValue = e.target.value;
338
+ this.emitSearch(e.target.value);
339
+ },
340
+ emitSearch: debounce(function (value) {
341
+ if (value !== this.value.label) {
342
+ this.$emit("search", value);
343
+ } else {
344
+ this.$emit("search", "");
345
+ }
346
+ }, 500),
347
+ handleOptionClick(option) {
348
+ this.inputValue = option.label;
349
+ this.closeDropDown(true);
350
+ this.$emit("input", option);
351
+ },
352
+ closeDropDown(hasData) {
353
+ this.inputValue = this.value.label;
354
+ this.showDropDown = false;
355
+ this.$emit("blur", { hasData: hasData });
356
+ },
357
+ openDropDown() {
358
+ this.showDropDown = true;
359
+ if (this.searchOnFocus) {
360
+ this.emitSearch("");
361
+ }
362
+ },
363
+ quantityLabelColor(data) {
364
+ if (data.stock_level <= 0) {
365
+ return "text-red-300";
366
+ } else if (data.stock_level < data.alert_level) {
367
+ return "text-orange-300";
368
+ } else {
369
+ return "text-gray-300";
370
+ }
371
+ },
372
+ },
373
+ watch: {
374
+ value(newValue) {
375
+ this.inputValue = newValue.label;
376
+ },
377
+ showDropDown(newValue) {
378
+ if (newValue) {
379
+ this.$nextTick(this.updateDropdownPlacement);
380
+ }
381
+ },
382
+ },
383
+ mounted() {
384
+ this.inputValue = this.value.label;
385
+ },
386
+ };
387
+ </script>
388
+
389
+ <style scoped>
390
+ input::placeholder {
391
+ @apply text-gray-500;
392
+ }
393
+ </style>