pgo-uiux2 1.0.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 (180) hide show
  1. package/.env +1 -0
  2. package/.env.production +1 -0
  3. package/.prettierrc +13 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/BUTTON_GUIDE.md +257 -0
  6. package/README.md +49 -0
  7. package/THEME_REFERENCE.md +310 -0
  8. package/eslint.config.ts +27 -0
  9. package/index.html +13 -0
  10. package/package.json +85 -0
  11. package/public/favicon.ico +0 -0
  12. package/src/App.vue +368 -0
  13. package/src/assets/fonts/Faruma.ttf +0 -0
  14. package/src/components/examples/AppBarExample.vue +101 -0
  15. package/src/components/examples/AvatarExample.vue +47 -0
  16. package/src/components/examples/BannerExample.vue +287 -0
  17. package/src/components/examples/BaseInputExample.vue +25 -0
  18. package/src/components/examples/BreadcrumbExample.vue +53 -0
  19. package/src/components/examples/CardExample.vue +77 -0
  20. package/src/components/examples/ChipExample.vue +225 -0
  21. package/src/components/examples/DatePickerExample.vue +31 -0
  22. package/src/components/examples/DropdownExample.vue +84 -0
  23. package/src/components/examples/EditorExample.vue +200 -0
  24. package/src/components/examples/ExpansionPanelExample.vue +42 -0
  25. package/src/components/examples/FileUploadExample.vue +40 -0
  26. package/src/components/examples/FormExample.vue +121 -0
  27. package/src/components/examples/HugeTest.vue +8 -0
  28. package/src/components/examples/LayoutContainerExample.vue +80 -0
  29. package/src/components/examples/ModalExample.vue +82 -0
  30. package/src/components/examples/NavDrawerExample.vue +170 -0
  31. package/src/components/examples/NumberFieldExample.vue +145 -0
  32. package/src/components/examples/RadioButtonExample.vue +161 -0
  33. package/src/components/examples/SearchExample.vue +322 -0
  34. package/src/components/examples/SelectExample.vue +121 -0
  35. package/src/components/examples/StackedTableViewExample.vue +53 -0
  36. package/src/components/examples/TabExample.vue +336 -0
  37. package/src/components/examples/TableExample.vue +228 -0
  38. package/src/components/examples/TextFieldExample.vue +181 -0
  39. package/src/components/examples/TextareaExample.vue +173 -0
  40. package/src/components/examples/ThemeToggle.vue +50 -0
  41. package/src/components/examples/TimelineExample.vue +66 -0
  42. package/src/components/examples/TipTapEditorExample.vue +20 -0
  43. package/src/components/examples/TooltipExample.vue +53 -0
  44. package/src/components/examples/VueDatePickerShowcase.vue +214 -0
  45. package/src/components/examples/_DatePickerExample.vue +33 -0
  46. package/src/components/examples/__FormExample.vue +77 -0
  47. package/src/components/index.ts +25 -0
  48. package/src/components/pgo/AppBar.vue +347 -0
  49. package/src/components/pgo/Avatar.vue +139 -0
  50. package/src/components/pgo/Banner.vue +300 -0
  51. package/src/components/pgo/Breadcrumb.vue +101 -0
  52. package/src/components/pgo/Button.vue +171 -0
  53. package/src/components/pgo/Card.vue +178 -0
  54. package/src/components/pgo/ConfirmationModel.vue +32 -0
  55. package/src/components/pgo/DataTable.vue +845 -0
  56. package/src/components/pgo/DatePicker/CalendarPanel.vue +43 -0
  57. package/src/components/pgo/DatePicker/__DatePicker.vue +122 -0
  58. package/src/components/pgo/DatePicker/types.ts +11 -0
  59. package/src/components/pgo/DatePicker/useCalendar.ts +39 -0
  60. package/src/components/pgo/DatePicker/useDatePicker.ts +31 -0
  61. package/src/components/pgo/Deprecated/ToastContainer.vue +51 -0
  62. package/src/components/pgo/Deprecated/ToastItem.vue +55 -0
  63. package/src/components/pgo/Dropdown.vue +296 -0
  64. package/src/components/pgo/DropdownItem.vue +40 -0
  65. package/src/components/pgo/Editor.vue +511 -0
  66. package/src/components/pgo/ExpansionPanel.vue +185 -0
  67. package/src/components/pgo/Footer.vue +39 -0
  68. package/src/components/pgo/HeroIcon.vue +124 -0
  69. package/src/components/pgo/InputSearch.vue +194 -0
  70. package/src/components/pgo/LayoutContainer.vue +104 -0
  71. package/src/components/pgo/Main.vue +37 -0
  72. package/src/components/pgo/Modal.vue +273 -0
  73. package/src/components/pgo/NavDrawer.vue +127 -0
  74. package/src/components/pgo/NavDrawerItem.vue +161 -0
  75. package/src/components/pgo/NavigationDrawer.vue +849 -0
  76. package/src/components/pgo/OLDNavDrawer.vue +661 -0
  77. package/src/components/pgo/OldAppBar.vue +223 -0
  78. package/src/components/pgo/PApp.vue +102 -0
  79. package/src/components/pgo/Pagination.vue +242 -0
  80. package/src/components/pgo/Search copy.vue +310 -0
  81. package/src/components/pgo/Search.vue +411 -0
  82. package/src/components/pgo/StackedTableView.vue +167 -0
  83. package/src/components/pgo/Tab.vue +617 -0
  84. package/src/components/pgo/TestInput.vue +395 -0
  85. package/src/components/pgo/Timeline.vue +367 -0
  86. package/src/components/pgo/TimelineItem.vue +80 -0
  87. package/src/components/pgo/TipTapEditor.vue +315 -0
  88. package/src/components/pgo/Tooltip.NOTES.md +12 -0
  89. package/src/components/pgo/Tooltip.PROPS.md +21 -0
  90. package/src/components/pgo/Tooltip.vue +281 -0
  91. package/src/components/pgo/base/Base.vue +444 -0
  92. package/src/components/pgo/buttons/Chip.vue +324 -0
  93. package/src/components/pgo/buttons/ChipGroup.vue +224 -0
  94. package/src/components/pgo/buttons/Radio.vue +424 -0
  95. package/src/components/pgo/filters/FilterSection.vue +188 -0
  96. package/src/components/pgo/filters/Searchbar.vue +216 -0
  97. package/src/components/pgo/forms/DynamicForm.vue +45 -0
  98. package/src/components/pgo/forms/Form.vue +132 -0
  99. package/src/components/pgo/index.ts +15 -0
  100. package/src/components/pgo/inputs/Checkbox.vue +320 -0
  101. package/src/components/pgo/inputs/DatePicker.vue +395 -0
  102. package/src/components/pgo/inputs/FileUpload.vue +326 -0
  103. package/src/components/pgo/inputs/NumberField.vue +243 -0
  104. package/src/components/pgo/inputs/Radio.vue +162 -0
  105. package/src/components/pgo/inputs/RadioGroup.vue +188 -0
  106. package/src/components/pgo/inputs/Select.vue +535 -0
  107. package/src/components/pgo/inputs/TextField.vue +194 -0
  108. package/src/components/pgo/inputs/Textarea.vue +181 -0
  109. package/src/main.js +12 -0
  110. package/src/pgo-components/_index.js +31 -0
  111. package/src/pgo-components/assets/fonts/Faruma.ttf +0 -0
  112. package/src/pgo-components/assets/fonts/logo.png +0 -0
  113. package/src/pgo-components/composables/useTheme.js +10 -0
  114. package/src/pgo-components/directives/tooltip-directive.ts +393 -0
  115. package/src/pgo-components/index.js +96 -0
  116. package/src/pgo-components/lib/componentConfig.js +147 -0
  117. package/src/pgo-components/lib/core/composables/_useCalendar.ts +127 -0
  118. package/src/pgo-components/lib/core/composables/useDefaults.ts +15 -0
  119. package/src/pgo-components/lib/core/composables/useLanguageSelect.js +0 -0
  120. package/src/pgo-components/lib/core/composables/useRtl.ts +12 -0
  121. package/src/pgo-components/lib/core/defaults/createDefaults.ts +5 -0
  122. package/src/pgo-components/lib/core/defaults/defaults.ts +7 -0
  123. package/src/pgo-components/lib/core/rtl/rtl.ts +3 -0
  124. package/src/pgo-components/lib/core/rtl/setRtl.ts +19 -0
  125. package/src/pgo-components/lib/drawerState.ts +3 -0
  126. package/src/pgo-components/lib/i18n/defaultLables.js +71 -0
  127. package/src/pgo-components/lib/i18n/i18nPlugin.js +52 -0
  128. package/src/pgo-components/lib/i18n/useI18n.js +35 -0
  129. package/src/pgo-components/lib/index.ts +38 -0
  130. package/src/pgo-components/pages/Component.vue +7 -0
  131. package/src/pgo-components/pages/ComponentRenderer.vue +85 -0
  132. package/src/pgo-components/pages/Home.vue +130 -0
  133. package/src/pgo-components/pages/ListView.vue +370 -0
  134. package/src/pgo-components/pages/Page1.vue +296 -0
  135. package/src/pgo-components/pages/_Page1.vue +180 -0
  136. package/src/pgo-components/plugins/SnackBar.vue +251 -0
  137. package/src/pgo-components/plugins/SnackBarContainer.vue +53 -0
  138. package/src/pgo-components/plugins/SnackBarPlugin.ts +136 -0
  139. package/src/pgo-components/plugins/theme-plugin.js +114 -0
  140. package/src/pgo-components/plugins/types.ts +46 -0
  141. package/src/pgo-components/plugins/useSnackBar.js +11 -0
  142. package/src/pgo-components/plugins/useSnackBar.ts +21 -0
  143. package/src/pgo-components/plugins/validation-plugin.js +11 -0
  144. package/src/pgo-components/services/Entry.json +813 -0
  145. package/src/pgo-components/services/axios.js +54 -0
  146. package/src/pgo-components/services/data.json +90 -0
  147. package/src/pgo-components/services/person.json +260 -0
  148. package/src/pgo-components/services/toast.ts +44 -0
  149. package/src/pgo-components/styles/global.css +234 -0
  150. package/src/pgo-components/styles/reset.css +96 -0
  151. package/src/pgo-components/styles/tokens.css +18 -0
  152. package/src/pgo-components/styles/utilities/border-radius.css +57 -0
  153. package/src/pgo-components/styles/utilities/borders.css +85 -0
  154. package/src/pgo-components/styles/utilities/colors.css +38 -0
  155. package/src/pgo-components/styles/utilities/cursor.css +19 -0
  156. package/src/pgo-components/styles/utilities/display.css +78 -0
  157. package/src/pgo-components/styles/utilities/elevation.css +33 -0
  158. package/src/pgo-components/styles/utilities/flex.css +403 -0
  159. package/src/pgo-components/styles/utilities/float.css +41 -0
  160. package/src/pgo-components/styles/utilities/hover.css +9 -0
  161. package/src/pgo-components/styles/utilities/index.css +18 -0
  162. package/src/pgo-components/styles/utilities/opacity.css +27 -0
  163. package/src/pgo-components/styles/utilities/overflow.css +26 -0
  164. package/src/pgo-components/styles/utilities/palette.css +515 -0
  165. package/src/pgo-components/styles/utilities/position.css +14 -0
  166. package/src/pgo-components/styles/utilities/sizing.css +70 -0
  167. package/src/pgo-components/styles/utilities/spacing.css +578 -0
  168. package/src/pgo-components/styles/utilities/transitions.css +58 -0
  169. package/src/pgo-components/styles/utilities/typography.css +91 -0
  170. package/src/pgo-components/styles/utilities/z-index.css +11 -0
  171. package/src/pgo-components/tokens/index.js +337 -0
  172. package/src/router/index.js +88 -0
  173. package/src/shims-vue.d.ts +14 -0
  174. package/src/validations/validationRules.js +50 -0
  175. package/tailwind.config.js +73 -0
  176. package/test.php +5 -0
  177. package/tsconfig.json +25 -0
  178. package/ui +31 -0
  179. package/ui.pgo.mv.conf +18 -0
  180. package/vite.config.js +42 -0
@@ -0,0 +1,444 @@
1
+ <template>
2
+ <div :dir="selectedDirection !== '' ? selectedDirection : ''" :class="[faruma, margin, width, 'group/select overflow-visible ']">
3
+ <div
4
+ :class="[
5
+ `relative vts-border focus:outline-none transition cursor-pointer flex items-center justify-between gap-2 `,
6
+ width,
7
+ props.height || heights[size],
8
+ sizes[size],
9
+ isErrored ? 'vts-border-color-error' : 'vts-border-color',
10
+ disabled ? 'bg-input-hover cursor-not-allowed' : `${bg}`,
11
+ isOpen && !isErrored ? 'vts-border-color-heavy' : '',
12
+ !isOpen && !isErrored ? 'vts-border-color-light' : '',
13
+ rounded ? roundedMap[rounded] : '',
14
+ textColor
15
+ ]"
16
+ >
17
+ <div v-if="label" :class="labelClasses" aria-hidden="true">
18
+ {{ selectedlabels ? selectedlabels ?? selectedlabels.label : label }}
19
+ </div>
20
+ <!-- main -->
21
+ <div class="flex items-center w-full gap-2">
22
+ <div v-if="$slots.prepend || prepend" class="">
23
+ <slot name="prepend">
24
+ <HeroIcon :size="iconSizes[size]" :name="prepend" :type="iconType == 'outline' ? 'outline' : 'solid'" />
25
+ </slot>
26
+ </div>
27
+ <div class="flex-1 w-full">
28
+ <slot name="control" :attrs="controlAttrs" :events="controlEvents" />
29
+ </div>
30
+ </div>
31
+ <!-- Icons -->
32
+ <div class="flex items-center gap-2">
33
+ <button
34
+ v-if="clearable && hasValue && !disabled"
35
+ class="hover:text-input-focus-border text-input-text flex items-center invisible group-hover/select:visible transition-all cursor-pointer"
36
+ title="Clear selection"
37
+ type="button"
38
+ @click.stop="clearSelection"
39
+ >
40
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
41
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
42
+ </svg>
43
+ </button>
44
+ <div v-if="$slots.append || append" class="">
45
+ <slot name="append">
46
+ <HeroIcon :size="iconSizes[size]" :name="append" :type="iconType == 'outline' ? 'outline' : 'solid'" />
47
+ </slot>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <div v-if="messagesToShow.length" :class="['.vts-mt-1 text-xs', selectLanguage === 'dv' ? 'text-right' : 'text-left', 'text-input-text']">
52
+ <div v-for="(msg, i) in messagesToShow" :key="i" :class="isErrored ? 'text-error' : ''">
53
+ {{ useLanguageSelected(msg, selectLanguage, '') }}
54
+ <!-- {{ msg }} -->
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
59
+ <script setup>
60
+ import { computed, inject, ref, watch, onMounted, nextTick, onBeforeUnmount, getCurrentInstance } from 'vue'
61
+ const { language } = inject('i18n')
62
+ import { useLanguageSelected, roundedMap, sizes, iconSizes, heights } from '../../../pgo-components/lib/componentConfig.js'
63
+ import HeroIcon from '../HeroIcon.vue'
64
+ import { vOnClickOutside } from '@vueuse/components'
65
+
66
+ const emit = defineEmits(['update:modelValue', 'focus', 'blur', 'change', 'click:prepend', 'click:append', 'click:clear'])
67
+
68
+ const props = defineProps({
69
+ modelValue: [String, Number, Boolean, Object, Array, null],
70
+ label: { type: [Object, String], default: '' },
71
+ items: { type: Array, default: () => [] },
72
+ itemText: { type: String, default: 'text' },
73
+ itemValue: { type: String, default: 'value' },
74
+ disabled: { type: Boolean, default: false },
75
+ required: { type: Boolean, default: false },
76
+ readonly: { type: Boolean, default: false },
77
+ clearable: { type: Boolean, default: true },
78
+ dir: { type: String, default: '' },
79
+ lang: { type: String, default: '' },
80
+ error: { type: Boolean, default: false },
81
+ errorMessage: { type: [String, Array], default: '' },
82
+ errorMessages: { type: Array, default: () => [] },
83
+ rules: { type: Array, default: () => [] },
84
+ messages: { type: [String, Array], default: '' },
85
+ hint: { type: String, default: '' },
86
+ persistentHint: { type: Boolean, default: false },
87
+ hideDetails: { type: Boolean, default: false },
88
+ isOpen: { type: Boolean, default: false },
89
+ prepend: { type: String, default: '' },
90
+ append: { type: String, default: '' },
91
+ size: { type: String, default: 'md' }, // xs | sm | md | lg | xl
92
+ id: { type: String, default: '' },
93
+ width: { type: String, default: 'w-full' },
94
+ border: { type: String, default: ' border-input-border' },
95
+ textColor: { type: String, default: 'text-input-text' },
96
+ margin: { type: String, default: '' },
97
+ bg: { type: String, default: 'bg-surface-elevated' },
98
+ hover: { type: String, default: 'hover:border-input-hover-border' },
99
+ focus: { type: String, default: 'focus:border-input-focus-border' },
100
+ rounded: { type: String, default: 'sm' },
101
+ multiple: { type: Boolean, default: false },
102
+ searchable: { type: Boolean, default: false },
103
+ bgColorVariant: { type: String, default: '' },
104
+ borderColorVariant: { type: String, default: '' },
105
+ textColorVariant: { type: String, default: '' },
106
+ labelTextSize: { type: String, default: 'text-base' },
107
+ iconType: { type: String, default: 'outline' },
108
+ labelTextColor: { type: String, default: 'text-input-border' },
109
+ height: { type: String, default: '' }
110
+ })
111
+
112
+ // Form integration
113
+ const formContext = inject('formContext', null)
114
+ const instance = getCurrentInstance()
115
+ const uid = instance?.uid
116
+
117
+ const focused = ref(false)
118
+ const dirty = ref(false)
119
+ const internalErrorMessages = ref([])
120
+ const hasBlurred = ref(false)
121
+
122
+ /**
123
+ * MAIN LOGIC
124
+ */
125
+ const isErrored = computed(() => {
126
+ if (props.error === true) return true
127
+ if (props.errorMessages && Array.isArray(props.errorMessages) && props.errorMessages.length > 0) return true
128
+ if (props.errorMessage && props.errorMessage !== '') {
129
+ if (Array.isArray(props.errorMessage)) {
130
+ return props.errorMessage.length > 0
131
+ }
132
+ if (typeof props.errorMessage === 'string') {
133
+ return props.errorMessage.trim() !== ''
134
+ }
135
+ }
136
+ // Only show internal errors after field has been blurred
137
+ if (hasBlurred.value && internalErrorMessages.value && internalErrorMessages.value.length > 0) return true
138
+ return false
139
+ })
140
+
141
+ // FIXED: Better hasValue logic to handle empty arrays and empty strings
142
+ const hasValue = computed(() => {
143
+ if (props.modelValue === null || props.modelValue === undefined) return false
144
+ if (props.modelValue === '') return false
145
+ if (Array.isArray(props.modelValue)) return props.modelValue.length > 0
146
+ if (typeof props.modelValue === 'boolean') return true // booleans are always considered having value
147
+ return true
148
+ })
149
+
150
+ const validationMessages = computed(() => {
151
+ const messages = []
152
+ if (props.errorMessages && Array.isArray(props.errorMessages) && props.errorMessages.length > 0) {
153
+ messages.push(...props.errorMessages)
154
+ }
155
+ if (props.errorMessage) {
156
+ if (Array.isArray(props.errorMessage) && props.errorMessage.length > 0) {
157
+ messages.push(...props.errorMessage)
158
+ } else if (typeof props.errorMessage === 'string' && props.errorMessage.trim() !== '') {
159
+ messages.push(props.errorMessage)
160
+ }
161
+ }
162
+ // CHANGED: Only include internal errors after field has been blurred
163
+ if (hasBlurred.value && internalErrorMessages.value && internalErrorMessages.value.length > 0) {
164
+ messages.push(...internalErrorMessages.value)
165
+ }
166
+ return messages
167
+ })
168
+
169
+ function validate() {
170
+ internalErrorMessages.value = []
171
+ if (!props.rules || props.rules.length === 0) return true
172
+
173
+ for (const rule of props.rules) {
174
+ const res = rule(props.modelValue)
175
+ if (res !== true) {
176
+ internalErrorMessages.value.push(res)
177
+ }
178
+ }
179
+
180
+ return internalErrorMessages.value.length === 0
181
+ }
182
+
183
+ const selectedDirection = computed(() => {
184
+ let selected = ''
185
+ if (props.dir == 'rtl') selected = 'rtl'
186
+ else if (props.lang == 'dv') selected = 'rtl'
187
+ else if (props.dir == 'ltr') selected = 'ltr'
188
+ else if (props.lang == 'en') selected = 'ltr'
189
+ else selected = ''
190
+ return selected
191
+ })
192
+
193
+ const selectLanguage = computed(() => {
194
+ let Selected = ''
195
+ if (props.lang != '') {
196
+ Selected = props.lang
197
+ } else if (props.dir == 'rtl') {
198
+ Selected = 'dv'
199
+ } else if (props.dir == 'ltr') {
200
+ Selected = 'en'
201
+ } else {
202
+ Selected = language.value
203
+ }
204
+ return Selected === 'dv' ? 'dv' : 'en'
205
+ })
206
+
207
+ const faruma = computed(() => {
208
+ if (selectLanguage.value == 'dv') return 'faruma'
209
+ return ''
210
+ })
211
+
212
+ const selectedlabels = computed(() => {
213
+ // console.log('selectLanguage in Base:', useLanguageSelected(props.label, selectLanguage.value, ''))
214
+ let selected = useLanguageSelected(props.label, selectLanguage.value, '')
215
+ // console.log('selected in Base:', selected)
216
+ if ((typeof selected === 'object' || Array.isArray(selected)) && selected !== null) {
217
+ return selected
218
+ }else if (selected !== '' && selected !== null) {
219
+ console.log('selectedlabels in Base:', selected)
220
+ return selected
221
+ }else {
222
+ return null
223
+ }
224
+ })
225
+
226
+ function clear() {
227
+ emit('update:modelValue', '')
228
+ dirty.value = true
229
+ validate()
230
+ emit('click:clear')
231
+ }
232
+
233
+ /**
234
+ * CONTROL BINDING
235
+ */
236
+ const controlAttrs = computed(() => ({
237
+ id: props.id,
238
+ disabled: props.disabled,
239
+ readonly: props.readonly,
240
+ value: props.modelValue,
241
+ required: props.required,
242
+ 'aria-invalid': isErrored.value,
243
+ dir: selectedDirection.value
244
+ }))
245
+
246
+ const controlEvents = {
247
+ input: e => onInput(e.target.value),
248
+ focus: onFocus,
249
+ blur: onBlur,
250
+ change: e => emit('change', e.target.value)
251
+ }
252
+
253
+ // FIXED: Label positioning logic
254
+ const labelY = computed(() => {
255
+ // Label should be at top if focused, has value, or is open
256
+ if (focused.value || hasValue.value || props.isOpen) {
257
+ return '-top-[1px]'
258
+ } else {
259
+ return 'top-[48%]'
260
+ }
261
+ })
262
+
263
+ const LabelX = computed(() => {
264
+ let positionX = ''
265
+ let fontSize = ''
266
+
267
+ if (props.size === 'xs') {
268
+ fontSize = 'text-xs'
269
+ positionX = props.prepend ? 'inset-4' : 'inset-3'
270
+ } else if (props.size === 'sm') {
271
+ fontSize = 'text-sm'
272
+ positionX = props.prepend ? 'inset-8' : 'inset-3'
273
+ } else if (props.size === 'md') {
274
+ fontSize = 'text-md'
275
+ positionX = props.prepend ? 'inset-10' : 'inset-3'
276
+ } else if (props.size === 'lg') {
277
+ fontSize = 'text-lg'
278
+ positionX = props.prepend ? 'inset-12' : 'inset-3'
279
+ } else if (props.size === 'xl') {
280
+ fontSize = 'text-lg'
281
+ positionX = props.prepend ? 'inset-14' : 'inset-3'
282
+ } else {
283
+ fontSize = 'text-sm'
284
+ positionX = props.prepend ? 'inset-8' : 'inset-3'
285
+ }
286
+
287
+ // When label is floating (focused, has value, or is open)
288
+ if (focused.value || hasValue.value || props.isOpen) {
289
+ if (selectLanguage.value === 'dv') {
290
+ positionX = 'right-8'
291
+ // console.log('Floating RTL positionX:', positionX)
292
+ } else {
293
+ positionX = 'left-8'
294
+ // console.log('Floating RTL positionX:', positionX)
295
+ }
296
+ fontSize = 'text-xs'
297
+ }
298
+
299
+ return positionX + ' ' + fontSize
300
+ })
301
+
302
+ const labelClasses = computed(() => {
303
+ const classes = ['absolute flex px-2 items-center transition-all pointer-events-none h-[1px]', LabelX.value, labelY.value]
304
+
305
+ // Add background only when label is floating
306
+ if (focused.value || hasValue.value || props.isOpen) {
307
+ classes.push('bg-inherit')
308
+ } else {
309
+ classes.push('bg-transparent')
310
+ }
311
+
312
+ // Color based on error state
313
+ if (isErrored.value) {
314
+ classes.push('text-error')
315
+ } else if (focused.value) {
316
+ classes.push('border-input-focus-border')
317
+ } else {
318
+ classes.push('text-input-text')
319
+ }
320
+
321
+ return classes
322
+ })
323
+
324
+ const messagesToShow = computed(() => {
325
+ if (props.hideDetails) return []
326
+ if (isErrored.value) return validationMessages.value
327
+ if (props.messages) {
328
+ if (Array.isArray(props.messages)) {
329
+ return props.messages.filter(msg => msg && msg.trim && msg.trim() !== '')
330
+ }
331
+ if (typeof props.messages === 'string' && props.messages.trim() !== '') {
332
+ return [props.messages]
333
+ }
334
+ }
335
+ if (props.hint && props.hint.trim() !== '' && (props.persistentHint || focused.value)) {
336
+ return [props.hint]
337
+ }
338
+ return []
339
+ })
340
+
341
+ function onFocus(e) {
342
+ focused.value = true
343
+ emit('focus', e)
344
+ }
345
+
346
+ function onBlur(e) {
347
+ focused.value = false
348
+ hasBlurred.value = true
349
+
350
+ // console.log('onBlur called, hasBlurred:', hasBlurred.value)
351
+
352
+ if (formContext?.validateOn?.value) {
353
+ const validateOn = formContext.validateOn.value
354
+ const shouldValidate = validateOn === 'blur' || (Array.isArray(validateOn) && validateOn.includes('blur'))
355
+
356
+ if (shouldValidate) {
357
+ const isValid = validate()
358
+ if (formContext.validate) {
359
+ formContext.validate(uid, isValid, internalErrorMessages.value)
360
+ }
361
+ }
362
+ } else {
363
+ const isValid = validate()
364
+ }
365
+
366
+ emit('blur', e)
367
+ }
368
+
369
+ function onInput(val) {
370
+ emit('update:modelValue', val)
371
+ }
372
+
373
+ function reset() {
374
+ const newValue = props.multiple ? [] : ''
375
+ emit('update:modelValue', newValue)
376
+ internalErrorMessages.value = []
377
+ dirty.value = false
378
+ hasBlurred.value = false // ADD THIS LINE
379
+ }
380
+
381
+ function resetValidation() {
382
+ internalErrorMessages.value = []
383
+ dirty.value = false
384
+ hasBlurred.value = false // ADD THIS LINE
385
+ }
386
+
387
+ // FIXED: Clear selection function
388
+ const clearSelection = () => {
389
+ const newValue = props.multiple ? [] : ''
390
+ emit('update:modelValue', newValue)
391
+ emit('change', newValue)
392
+ emit('click:clear')
393
+ }
394
+ const registration = {
395
+ validate,
396
+ reset,
397
+ resetValidation
398
+ }
399
+
400
+ // Register with form on mount
401
+ onMounted(() => {
402
+ if (formContext) {
403
+ formContext.register(uid, {
404
+ validate: () => {
405
+ const isValid = validate()
406
+ const errors = isValid ? [] : internalErrorMessages.value
407
+ if (formContext.validate) {
408
+ formContext.validate(uid, isValid, errors)
409
+ }
410
+ return isValid
411
+ },
412
+ reset: () => {
413
+ emit('update:modelValue', props.multiple ? [] : '')
414
+ resetValidation()
415
+ },
416
+ resetValidation: () => {
417
+ internalErrorMessages.value = []
418
+ dirty.value = false
419
+ }
420
+ })
421
+ }
422
+ })
423
+
424
+ watch(
425
+ () => props.isOpen,
426
+ (newVal, oldVal) => {
427
+ if (oldVal === true && newVal === false && formContext) {
428
+ // Dropdown closed, act as blur
429
+ hasBlurred.value = true
430
+ const isValid = validate()
431
+ if (formContext.validate) {
432
+ formContext.validate(uid, isValid, internalErrorMessages.value)
433
+ }
434
+ }
435
+ }
436
+ )
437
+
438
+ // Unregister on unmount
439
+ onBeforeUnmount(() => {
440
+ if (formContext) {
441
+ formContext.unregister(uid)
442
+ }
443
+ })
444
+ </script>