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,324 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'inline-flex items-center gap-2 transition-all duration-200 ease-in-out select-none',
5
+ 'leading-none',
6
+ sizeClasses,
7
+ variantClasses,
8
+ roundedClasses,
9
+ {
10
+ 'cursor-pointer hover:shadow-md': clickable,
11
+ 'opacity-60': disabled,
12
+ 'cursor-not-allowed': disabled
13
+ },
14
+ chipClass
15
+ ]"
16
+ :tabindex="clickable && !disabled ? 0 : -1"
17
+ @click="handleClick"
18
+ @keydown.enter="handleClick"
19
+ @keydown.space.prevent="handleClick"
20
+ >
21
+ <!-- Leading icon/avatar -->
22
+ <div v-if="$slots.prepend || avatar || prependIcon" class="flex items-center">
23
+ <slot name="prepend">
24
+ <!-- Avatar -->
25
+ <img
26
+ v-if="avatar"
27
+ :src="avatar"
28
+ :alt="avatarAlt"
29
+ :class="avatarClasses"
30
+ />
31
+ <!-- Prepend Icon -->
32
+ <HeroIcon v-if="prependIcon" :name="prependIcon" type="outline" :size="iconSizes['sm']" color="text-current" />
33
+ <!-- Default prepend icon -->
34
+ <svg
35
+ v-else
36
+ :class="iconClasses"
37
+ fill="none"
38
+ stroke="currentColor"
39
+ viewBox="0 0 24 24"
40
+ >
41
+ <circle cx="12" cy="12" r="10" />
42
+ <path d="M12 6v6l4 2" />
43
+ </svg>
44
+ </slot>
45
+ </div>
46
+
47
+ <!-- Chip content -->
48
+ <div class="flex items-center min-w-0 leading-none">
49
+ <span :class="['truncate leading-none', textClasses]">
50
+ <slot>{{ label }}</slot>
51
+ </span>
52
+ </div>
53
+
54
+ <!-- Trailing icon/close button -->
55
+ <div v-if="$slots.append || appendIcon || closable" class="flex items-center">
56
+ <slot name="append">
57
+ <!-- Close button -->
58
+ <button
59
+ v-if="closable"
60
+ type="button"
61
+ :class="[
62
+ 'inline-flex items-center justify-center rounded-full transition-colors duration-200 hover:bg-black/10 focus:outline-none focus:ring-2 focus:ring-offset-1',
63
+ closeButtonClasses
64
+ ]"
65
+ :disabled="disabled"
66
+ @click.stop="handleClose"
67
+ @keydown.enter.stop="handleClose"
68
+ @keydown.space.stop.prevent="handleClose"
69
+ >
70
+ <svg
71
+ :class="closeIconClasses"
72
+ fill="none"
73
+ stroke="currentColor"
74
+ viewBox="0 0 24 24"
75
+ >
76
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
77
+ </svg>
78
+ </button>
79
+ <!-- Append Icon -->
80
+ <component
81
+ v-else-if="appendIcon"
82
+ :is="appendIcon"
83
+ :class="iconClasses"
84
+ />
85
+ </slot>
86
+ </div>
87
+ </div>
88
+ </template>
89
+
90
+ <script setup>
91
+ import { computed } from 'vue'
92
+ import { sizes, iconSizes, roundedMap, colorMap } from '../../../pgo-components/lib/componentConfig'
93
+ import HeroIcon from '../HeroIcon.vue'
94
+
95
+ const props = defineProps({
96
+ // Content
97
+ label: {
98
+ type: String,
99
+ default: ''
100
+ },
101
+
102
+ // Behavior
103
+ clickable: {
104
+ type: Boolean,
105
+ default: false
106
+ },
107
+ closable: {
108
+ type: Boolean,
109
+ default: false
110
+ },
111
+ disabled: {
112
+ type: Boolean,
113
+ default: false
114
+ },
115
+
116
+ // Styling
117
+ variant: {
118
+ type: String,
119
+ default: 'default',
120
+ validator: (value) => ['default', 'outlined', 'filled', 'tonal'].includes(value)
121
+ },
122
+ color: {
123
+ type: String,
124
+ default: 'primary'
125
+ },
126
+ size: {
127
+ type: String,
128
+ default: 'default',
129
+ validator: (value) => ['small', 'default', 'large'].includes(value)
130
+ },
131
+ rounded: {
132
+ type: String,
133
+ default: 'full'
134
+ },
135
+
136
+ // Icons and avatars
137
+
138
+ prependIcon: {
139
+ type: [String, Object],
140
+ default: null
141
+ },
142
+ appendIcon: {
143
+ type: [String, Object],
144
+ default: null
145
+ },
146
+ avatar: {
147
+ type: String,
148
+ default: null
149
+ },
150
+ avatarAlt: {
151
+ type: String,
152
+ default: 'Avatar'
153
+ },
154
+
155
+ // Custom classes
156
+ chipClass: {
157
+ type: String,
158
+ default: ''
159
+ },
160
+ textClass: {
161
+ type: String,
162
+ default: ''
163
+ },
164
+
165
+ // Color customization
166
+ bgColor: {
167
+ type: String,
168
+ default: ''
169
+ },
170
+ textColor: {
171
+ type: String,
172
+ default: ''
173
+ },
174
+ borderColor: {
175
+ type: String,
176
+ default: ''
177
+ }
178
+ })
179
+
180
+ const emit = defineEmits(['click', 'close'])
181
+
182
+ // Computed classes
183
+ const sizeClasses = computed(() => {
184
+ const sizes = {
185
+ small: 'px-2 py-1 text-xs min-h-6',
186
+ default: 'px-3 py-1.5 text-sm min-h-8',
187
+ large: 'px-4 py-2 text-base min-h-10'
188
+ }
189
+ return sizes[props.size] || sizes.default
190
+ })
191
+
192
+ const roundedClasses = computed(() => {
193
+ if (props.rounded === 'none') return 'rounded-none'
194
+ if (props.rounded === 'sm') return 'rounded-sm'
195
+ if (props.rounded === 'md') return 'rounded-md'
196
+ if (props.rounded === 'lg') return 'rounded-lg'
197
+ if (props.rounded === 'xl') return 'rounded-xl'
198
+ return 'rounded-full' // default
199
+ })
200
+
201
+ const variantClasses = computed(() => {
202
+ // If custom colors are provided, use them
203
+ if (props.bgColor || props.textColor || props.borderColor) {
204
+ const bg = props.bgColor || 'bg-gray-100'
205
+ const text = props.textColor || 'text-gray-800'
206
+ const border = props.borderColor ? `border ${props.borderColor}` : ''
207
+
208
+ if (props.variant === 'outlined') {
209
+ return `bg-transparent ${text} border ${border || 'border-gray-300'}`
210
+ }
211
+ return `${bg} ${text} ${border}`
212
+ }
213
+
214
+ // Default color variants
215
+ const colorVariants = {
216
+ primary: {
217
+ default: 'bg-blue-100 text-blue-800 hover:bg-blue-200',
218
+ outlined: 'bg-transparent text-blue-600 border border-blue-300 hover:bg-blue-50',
219
+ filled: 'bg-blue-600 text-white hover:bg-blue-700',
220
+ tonal: 'bg-blue-50 text-blue-700 hover:bg-blue-100'
221
+ },
222
+ secondary: {
223
+ default: 'bg-gray-100 text-gray-800 hover:bg-gray-200',
224
+ outlined: 'bg-transparent text-gray-600 border border-gray-300 hover:bg-gray-50',
225
+ filled: 'bg-gray-600 text-white hover:bg-gray-700',
226
+ tonal: 'bg-gray-50 text-gray-700 hover:bg-gray-100'
227
+ },
228
+ success: {
229
+ default: 'bg-green-100 text-green-800 hover:bg-green-200',
230
+ outlined: 'bg-transparent text-green-600 border border-green-300 hover:bg-green-50',
231
+ filled: 'bg-green-600 text-white hover:bg-green-700',
232
+ tonal: 'bg-green-50 text-green-700 hover:bg-green-100'
233
+ },
234
+ warning: {
235
+ default: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-200',
236
+ outlined: 'bg-transparent text-yellow-600 border border-yellow-300 hover:bg-yellow-50',
237
+ filled: 'bg-yellow-600 text-white hover:bg-yellow-700',
238
+ tonal: 'bg-yellow-50 text-yellow-700 hover:bg-yellow-100'
239
+ },
240
+ error: {
241
+ default: 'bg-red-100 text-red-800 hover:bg-red-200',
242
+ outlined: 'bg-transparent text-red-600 border border-red-300 hover:bg-red-50',
243
+ filled: 'bg-red-600 text-white hover:bg-red-700',
244
+ tonal: 'bg-red-50 text-red-700 hover:bg-red-100'
245
+ },
246
+ info: {
247
+ default: 'bg-cyan-100 text-cyan-800 hover:bg-cyan-200',
248
+ outlined: 'bg-transparent text-cyan-600 border border-cyan-300 hover:bg-cyan-50',
249
+ filled: 'bg-cyan-600 text-white hover:bg-cyan-700',
250
+ tonal: 'bg-cyan-50 text-cyan-700 hover:bg-cyan-100'
251
+ }
252
+ }
253
+
254
+ const colorScheme = colorVariants[props.color] || colorVariants.primary
255
+ return colorScheme[props.variant] || colorScheme.default
256
+ })
257
+
258
+ const textClasses = computed(() => {
259
+ const base = 'font-medium'
260
+ return props.textClass ? `${base} ${props.textClass}` : base
261
+ })
262
+
263
+ const iconClasses = computed(() => {
264
+ const sizes = {
265
+ small: 'w-3 h-3',
266
+ default: 'w-4 h-4',
267
+ large: 'w-5 h-5'
268
+ }
269
+ return sizes[props.size] || sizes.default
270
+ })
271
+
272
+ const avatarClasses = computed(() => {
273
+ const sizes = {
274
+ small: 'w-4 h-4',
275
+ default: 'w-5 h-5',
276
+ large: 'w-6 h-6'
277
+ }
278
+ return `${sizes[props.size] || sizes.default} rounded-full object-cover`
279
+ })
280
+
281
+ const closeButtonClasses = computed(() => {
282
+ const sizes = {
283
+ small: 'w-4 h-4',
284
+ default: 'w-5 h-5',
285
+ large: 'w-6 h-6'
286
+ }
287
+ return sizes[props.size] || sizes.default
288
+ })
289
+
290
+ const closeIconClasses = computed(() => {
291
+ const sizes = {
292
+ small: 'w-2.5 h-2.5',
293
+ default: 'w-3 h-3',
294
+ large: 'w-3.5 h-3.5'
295
+ }
296
+ return sizes[props.size] || sizes.default
297
+ })
298
+
299
+ // Event handlers
300
+ const handleClick = () => {
301
+ if (!props.disabled && props.clickable) {
302
+ emit('click')
303
+ }
304
+ }
305
+
306
+ const handleClose = () => {
307
+ if (!props.disabled) {
308
+ emit('close')
309
+ }
310
+ }
311
+ </script>
312
+
313
+ <style scoped>
314
+ /* Minimal styles for focus states */
315
+ .focus\:ring-2:focus {
316
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
317
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
318
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
319
+ }
320
+
321
+ .focus\:ring-offset-1:focus {
322
+ --tw-ring-offset-width: 1px;
323
+ }
324
+ </style>
@@ -0,0 +1,224 @@
1
+ <template>
2
+ <div class="flex items-center ">
3
+ <div
4
+ :class="[
5
+ 'inline-flex ',
6
+ gapClasses,
7
+ justifyClasses,
8
+ containerClass
9
+ ]"
10
+ role="group"
11
+ :aria-label="ariaLabel"
12
+ >
13
+ <Chip
14
+ v-for="(chip, index) in chips"
15
+ :key="getChipKey(chip, index)"
16
+ :label="getChipLabel(chip)"
17
+ :variant="chip.variant || variant"
18
+ :color="chip.color || color"
19
+ :size="chip.size || size"
20
+ :rounded="chip.rounded || rounded"
21
+ :clickable="chip.clickable !== undefined ? chip.clickable : clickable"
22
+ :closable="chip.closable !== undefined ? chip.closable : closable"
23
+ :disabled="chip.disabled || disabled"
24
+ :avatar="chip.avatar"
25
+ :avatar-alt="chip.avatarAlt"
26
+ :prepend-icon="chip.prependIcon"
27
+ :append-icon="chip.appendIcon"
28
+ :chip-class="chip.chipClass || chipClass"
29
+ :text-class="chip.textClass || textClass"
30
+ :bg-color="chip.bgColor || bgColor"
31
+ :text-color="chip.textColor || textColor"
32
+ :border-color="chip.borderColor || borderColor"
33
+ @click="handleChipClick(chip, index)"
34
+ @close="handleChipClose(chip, index)"
35
+ >
36
+ <template v-if="$slots.chip" #default="slotProps">
37
+ <slot name="chip" v-bind="{ ...slotProps, chip, index }" />
38
+ </template>
39
+ <template v-if="$slots.prepend" #prepend="slotProps">
40
+ <slot name="prepend" v-bind="{ ...slotProps, chip, index }" />
41
+ </template>
42
+ <template v-if="$slots.append" #append="slotProps">
43
+ <slot name="append" v-bind="{ ...slotProps, chip, index }" />
44
+ </template>
45
+ </Chip>
46
+ </div>
47
+ </div>
48
+ </template>
49
+
50
+ <script setup>
51
+ import { computed } from 'vue'
52
+ import Chip from './Chip.vue'
53
+
54
+ const props = defineProps({
55
+ // Data
56
+ chips: {
57
+ type: Array,
58
+ default: () => []
59
+ },
60
+
61
+ // Default properties for all chips
62
+ variant: {
63
+ type: String,
64
+ default: 'default'
65
+ },
66
+ color: {
67
+ type: String,
68
+ default: 'primary'
69
+ },
70
+ size: {
71
+ type: String,
72
+ default: 'default'
73
+ },
74
+ rounded: {
75
+ type: String,
76
+ default: 'full'
77
+ },
78
+
79
+ // Default behavior
80
+ clickable: {
81
+ type: Boolean,
82
+ default: false
83
+ },
84
+ closable: {
85
+ type: Boolean,
86
+ default: false
87
+ },
88
+ disabled: {
89
+ type: Boolean,
90
+ default: false
91
+ },
92
+
93
+ // Layout
94
+ gap: {
95
+ type: String,
96
+ default: 'md'
97
+ },
98
+ justify: {
99
+ type: String,
100
+ default: 'start'
101
+ },
102
+
103
+ // Custom classes
104
+ containerClass: {
105
+ type: String,
106
+ default: ''
107
+ },
108
+ chipClass: {
109
+ type: String,
110
+ default: ''
111
+ },
112
+ textClass: {
113
+ type: String,
114
+ default: ''
115
+ },
116
+
117
+ // Default color customization
118
+ bgColor: {
119
+ type: String,
120
+ default: ''
121
+ },
122
+ textColor: {
123
+ type: String,
124
+ default: ''
125
+ },
126
+ borderColor: {
127
+ type: String,
128
+ default: ''
129
+ },
130
+
131
+ // Accessibility
132
+ ariaLabel: {
133
+ type: String,
134
+ default: 'Chip group'
135
+ },
136
+
137
+ // Key field for tracking chips
138
+ itemKey: {
139
+ type: String,
140
+ default: 'id'
141
+ },
142
+ itemText: {
143
+ type: String,
144
+ default: 'text'
145
+ },
146
+ groupName: {
147
+ type: String,
148
+ default: ''
149
+ }
150
+ })
151
+
152
+ const emit = defineEmits(['chip-click', 'chip-close', 'update:chips', 'update:modelValue'])
153
+
154
+ // Computed classes
155
+ const gapClasses = computed(() => {
156
+ const gaps = {
157
+ none: 'gap-0',
158
+ sm: 'gap-1',
159
+ md: 'gap-2',
160
+ lg: 'gap-3',
161
+ xl: 'gap-4'
162
+ }
163
+ return gaps[props.gap] || gaps.md
164
+ })
165
+
166
+ const justifyClasses = computed(() => {
167
+ const justifications = {
168
+ start: 'justify-start',
169
+ center: 'justify-center',
170
+ end: 'justify-end',
171
+ between: 'justify-between',
172
+ around: 'justify-around',
173
+ evenly: 'justify-evenly'
174
+ }
175
+ return justifications[props.justify] || justifications.start
176
+ })
177
+
178
+ // Helper functions
179
+ const getChipKey = (chip, index) => {
180
+ if (typeof chip === 'object' && chip[props.itemKey]) {
181
+ return chip[props.itemKey]
182
+ }
183
+ if (typeof chip === 'object' && chip.id) {
184
+ return chip.id
185
+ }
186
+ return index
187
+ }
188
+
189
+ const getChipLabel = (chip) => {
190
+ if (typeof chip === 'string') {
191
+ return chip
192
+ }
193
+ if (typeof chip === 'object') {
194
+ return chip[props.itemText] || chip.text || chip.label || chip.name || ''
195
+ }
196
+ return String(chip)
197
+ }
198
+
199
+ // Event handlers
200
+ // const handleChipClick = (chip, index) => {
201
+ // emit('chip-click', { groupName: props.groupName, chip, index })
202
+ // }
203
+
204
+ const handleChipClick = (chip) => {
205
+ emit('chip-click', chip) // Emits { value: 'active', label: 'Active' }
206
+ // OR if you want to emit just the value:
207
+ emit('update:modelValue', chip.value)
208
+ }
209
+
210
+ const handleChipClose = (chip, index) => {
211
+ const updatedChips = [...props.chips]
212
+ updatedChips.splice(index, 1)
213
+
214
+ emit('chip-close', { chip, index })
215
+ emit('update:chips', updatedChips)
216
+ }
217
+ </script>
218
+
219
+ <style scoped>
220
+ /* Ensure proper wrapping */
221
+ /* .flex-wrap {
222
+ flex-wrap: wrap;
223
+ } */
224
+ </style>