@a-vision-software/vue-input-components 1.2.4 → 1.2.6

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.
@@ -0,0 +1,634 @@
1
+ <template>
2
+ <nav
3
+ :class="[
4
+ 'navigation',
5
+ `navigation--${type}`,
6
+ `navigation--${orientation}`,
7
+ { 'navigation--large-icons': iconSize === 'large' },
8
+ ]"
9
+ :style="{
10
+ '--navigation-color': color,
11
+ '--navigation-hover-color': hoverColor,
12
+ '--navigation-active-color': activeColor,
13
+ '--navigation-disabled-color': disabledColor,
14
+ '--navigation-gap': gap,
15
+ '--navigation-padding': padding,
16
+ '--navigation-border-radius': borderRadius,
17
+ '--navigation-height': height,
18
+ '--navigation-width': width,
19
+ '--navigation-background-color': backgroundColor,
20
+ '--navigation-active-background-color': activeBackgroundColor || 'rgba(0, 0, 0, 0.1)',
21
+ '--navigation-bottom-border': showBottomBorder
22
+ ? `1px solid ${bottomBorderColor || 'rgba(0, 0, 0, 0.2)'}`
23
+ : 'none',
24
+ '--navigation-tiles-grid': navigationGrid,
25
+ }"
26
+ >
27
+ <template v-if="type === 'tiles'">
28
+ <div class="navigation__tiles">
29
+ <div
30
+ v-for="(item, index) in sortedItems"
31
+ :key="item.id"
32
+ class="navigation__tile"
33
+ :class="[
34
+ { 'navigation__tile--active': item.id === activeItem },
35
+ { 'navigation__tile--disabled': item.disabled },
36
+ { 'navigation__tile--right': item.alignment === 'right' },
37
+ { 'navigation__tile--open': isDropdownOpen(item.id) },
38
+ { 'navigation__tile--spacer': item.id.includes('spacer') },
39
+ ]"
40
+ :style="{
41
+ '--item-alignment': item.alignment || activeItemAlignment,
42
+ width: item.width || '150px',
43
+ 'min-width': item.width || '150px',
44
+ 'max-width': item.width || '150px',
45
+ 'grid-column': item.alignment === 'right' ? `${index - items.length}` : `auto`,
46
+ }"
47
+ @click="(e) => !item.id.includes('spacer') && handleItemClick(item, e)"
48
+ >
49
+ <div
50
+ class="navigation__tile-content"
51
+ :class="{
52
+ 'navigation__tile-content--icon-only': !item.label,
53
+ 'navigation__tile-content--large-icon': iconSize === 'large' && item.icon,
54
+ }"
55
+ >
56
+ <div v-if="item.icon" class="navigation__icon">
57
+ <img
58
+ v-if="item.icon.startsWith('img:')"
59
+ :src="item.icon.substring(4)"
60
+ :alt="item.label || 'Icon'"
61
+ class="navigation__icon-image"
62
+ />
63
+ <font-awesome-icon v-else :icon="item.icon" />
64
+ </div>
65
+ <div v-if="item.label" class="navigation__label">
66
+ <span>{{ item.label }}</span>
67
+ <div v-if="item.children" class="navigation__dropdown-arrow">
68
+ <font-awesome-icon icon="chevron-down" />
69
+ </div>
70
+ </div>
71
+ </div>
72
+ <div
73
+ v-if="item.url && parseInt(height || '0') >= 80"
74
+ class="navigation__external-link"
75
+ @click.stop="openUrl(item.url)"
76
+ >
77
+ <font-awesome-icon icon="square-up-right" />
78
+ </div>
79
+ <div
80
+ v-if="item.children && isDropdownOpen(item.id)"
81
+ class="navigation__dropdown-content"
82
+ :class="{
83
+ 'navigation__dropdown-content--start': item.alignment === 'start',
84
+ 'navigation__dropdown-content--end': item.alignment === 'end',
85
+ }"
86
+ >
87
+ <div
88
+ v-for="child in item.children"
89
+ :key="child.id"
90
+ class="navigation__dropdown-item"
91
+ :class="{
92
+ 'navigation__dropdown-item--disabled': child.disabled,
93
+ }"
94
+ @click="(e) => handleItemClick(child, e)"
95
+ >
96
+ <div v-if="child.icon" class="navigation__icon">
97
+ <img
98
+ v-if="child.icon.startsWith('img:')"
99
+ :src="child.icon.substring(4)"
100
+ :alt="child.label || 'Icon'"
101
+ class="navigation__icon-image"
102
+ />
103
+ <font-awesome-icon v-else :icon="child.icon" />
104
+ </div>
105
+ <div v-if="child.label" class="navigation__label">{{ child.label }}</div>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </template>
111
+
112
+ <template v-else>
113
+ <div class="navigation__dropdowns">
114
+ <div
115
+ v-for="item in items"
116
+ :key="item.id"
117
+ class="navigation__dropdown"
118
+ :class="[
119
+ { 'navigation__dropdown--active': item.id === activeItem },
120
+ { 'navigation__dropdown--disabled': item.disabled },
121
+ { 'navigation__dropdown--start': item.alignment === 'start' },
122
+ { 'navigation__dropdown--end': item.alignment === 'end' },
123
+ { 'navigation__dropdown--open': isDropdownOpen(item.id) },
124
+ ]"
125
+ :style="{
126
+ '--item-alignment': item.alignment || activeItemAlignment,
127
+ }"
128
+ >
129
+ <div
130
+ class="navigation__dropdown-header"
131
+ :class="{
132
+ 'navigation__dropdown-header--icon-only': !item.label,
133
+ 'navigation__dropdown-header--large-icon': iconSize === 'large' && item.icon,
134
+ }"
135
+ @click="(e) => handleItemClick(item, e)"
136
+ >
137
+ <div v-if="item.icon" class="navigation__icon">
138
+ <img
139
+ v-if="item.icon.startsWith('img:')"
140
+ :src="item.icon.substring(4)"
141
+ :alt="item.label || 'Icon'"
142
+ class="navigation__icon-image"
143
+ />
144
+ <font-awesome-icon v-else :icon="item.icon" />
145
+ </div>
146
+ <div v-if="item.label" class="navigation__label">
147
+ <span>{{ item.label }}</span>
148
+ <div v-if="item.children" class="navigation__dropdown-arrow">
149
+ <font-awesome-icon icon="chevron-down" />
150
+ </div>
151
+ </div>
152
+ </div>
153
+ <div
154
+ v-if="item.url && parseInt(height || '0') >= 80"
155
+ class="navigation__external-link"
156
+ @click.stop="openUrl(item.url)"
157
+ >
158
+ <font-awesome-icon icon="square-up-right" />
159
+ </div>
160
+ <div
161
+ v-if="item.children && isDropdownOpen(item.id)"
162
+ class="navigation__dropdown-content"
163
+ :class="{
164
+ 'navigation__dropdown-content--start': item.alignment === 'start',
165
+ 'navigation__dropdown-content--end': item.alignment === 'end',
166
+ }"
167
+ >
168
+ <div
169
+ v-for="child in item.children"
170
+ :key="child.id"
171
+ class="navigation__dropdown-item"
172
+ :class="{
173
+ 'navigation__dropdown-item--disabled': child.disabled,
174
+ }"
175
+ @click="(e) => handleItemClick(child, e)"
176
+ >
177
+ <div v-if="child.icon" class="navigation__icon">
178
+ <img
179
+ v-if="child.icon.startsWith('img:')"
180
+ :src="child.icon.substring(4)"
181
+ :alt="child.label || 'Icon'"
182
+ class="navigation__icon-image"
183
+ />
184
+ <font-awesome-icon v-else :icon="child.icon" />
185
+ </div>
186
+ <div v-if="child.label" class="navigation__label">{{ child.label }}</div>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </template>
192
+ </nav>
193
+ </template>
194
+
195
+ <script setup lang="ts">
196
+ import { ref, computed, onMounted, onUnmounted } from 'vue'
197
+ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
198
+ import { NavigationProps, NavigationItem } from '../types/navigation'
199
+
200
+ const props = defineProps<NavigationProps>()
201
+ const emit = defineEmits<{
202
+ (e: 'update:activeItem', value: string): void
203
+ (e: 'item-click', item: NavigationItem): void
204
+ }>()
205
+
206
+ const openDropdownId = ref<string | null>(null)
207
+
208
+ const sortedItems = computed(() => {
209
+ return [...props.items].sort((a, b) => {
210
+ if (a.alignment === 'right' && b.alignment !== 'right') return 1
211
+ if (a.alignment !== 'right' && b.alignment === 'right') return -1
212
+ return 0
213
+ })
214
+ })
215
+
216
+ const navigationGrid = computed(() => {
217
+ return sortedItems.value.map((item) => item.width || '150px').join(' ')
218
+ })
219
+
220
+ const handleItemClick = (item: NavigationItem, event: MouseEvent) => {
221
+ if (item.disabled) return
222
+
223
+ // Prevent event propagation to avoid closing the dropdown immediately
224
+ event.stopPropagation()
225
+
226
+ emit('item-click', item)
227
+ emit('update:activeItem', item.id)
228
+
229
+ if (item.children) {
230
+ openDropdownId.value = openDropdownId.value === item.id ? null : item.id
231
+ } else {
232
+ openDropdownId.value = null
233
+ }
234
+ }
235
+
236
+ const openUrl = (url: string) => {
237
+ window.open(url, '_blank')
238
+ }
239
+
240
+ const isDropdownOpen = (itemId: string) => {
241
+ return openDropdownId.value === itemId
242
+ }
243
+
244
+ const closeDropdowns = () => {
245
+ openDropdownId.value = null
246
+ }
247
+
248
+ // Close dropdowns when clicking outside
249
+ const handleClickOutside = (event: MouseEvent) => {
250
+ const target = event.target as HTMLElement
251
+ if (!target.closest('.navigation__tile') && !target.closest('.navigation__dropdown')) {
252
+ closeDropdowns()
253
+ }
254
+ }
255
+
256
+ onMounted(() => {
257
+ document.addEventListener('click', handleClickOutside)
258
+ })
259
+
260
+ onUnmounted(() => {
261
+ document.removeEventListener('click', handleClickOutside)
262
+ })
263
+ </script>
264
+
265
+ <style scoped>
266
+ .navigation {
267
+ display: flex;
268
+ font-family: Arial, sans-serif;
269
+ width: var(--navigation-width, 100%);
270
+ background-color: var(--navigation-background-color, transparent);
271
+ }
272
+
273
+ .navigation--horizontal {
274
+ flex-direction: row;
275
+ }
276
+
277
+ .navigation--vertical {
278
+ flex-direction: column;
279
+ }
280
+
281
+ /* Tiles */
282
+ .navigation__tiles {
283
+ display: grid;
284
+ grid-template-columns: var(--navigation-tiles-grid);
285
+ gap: var(--navigation-gap);
286
+ width: 100%;
287
+ height: var(--navigation-height, auto);
288
+ border-bottom: var(--navigation-bottom-border);
289
+ }
290
+
291
+ .navigation__tile {
292
+ position: relative;
293
+ display: flex;
294
+ flex-direction: column;
295
+ align-items: center;
296
+ justify-content: center;
297
+ padding: var(--navigation-padding);
298
+ border: 1px solid var(--navigation-color);
299
+ border-radius: var(--navigation-border-radius);
300
+ cursor: pointer;
301
+ transition: all 0.2s ease;
302
+ background: transparent;
303
+ height: 100%;
304
+ }
305
+
306
+ .navigation__tile:hover {
307
+ border-color: var(--navigation-hover-color);
308
+ color: var(--navigation-hover-color);
309
+
310
+ .navigation__tile-content {
311
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
312
+ transform: scale(1.05);
313
+ }
314
+ }
315
+
316
+ .navigation__tile--active {
317
+ border-color: var(--navigation-active-color);
318
+ color: var(--navigation-active-color);
319
+ background-color: var(--navigation-active-background-color);
320
+ box-shadow:
321
+ -5px 0 3px -3px rgba(0, 0, 0, 0.1),
322
+ 5px 0 3px -3px rgba(0, 0, 0, 0.1);
323
+ }
324
+
325
+ .navigation__tile--disabled {
326
+ opacity: 0.5;
327
+ cursor: not-allowed;
328
+ color: var(--navigation-disabled-color);
329
+ border-color: var(--navigation-disabled-color);
330
+ }
331
+
332
+ .navigation__tile--right {
333
+ grid-column: auto;
334
+ margin-left: auto;
335
+ }
336
+
337
+ .navigation__tile-content {
338
+ display: flex;
339
+ align-items: center;
340
+ width: 100%;
341
+ flex-direction: row;
342
+ justify-content: center;
343
+ }
344
+
345
+ /* Dropdowns */
346
+ .navigation__dropdowns {
347
+ display: flex;
348
+ flex-direction: column;
349
+ gap: var(--navigation-gap);
350
+ width: 100%;
351
+ }
352
+
353
+ .navigation__dropdown {
354
+ position: relative;
355
+ }
356
+
357
+ .navigation__dropdown--start {
358
+ align-self: flex-start;
359
+ }
360
+
361
+ .navigation__dropdown--end {
362
+ align-self: flex-end;
363
+ }
364
+
365
+ .navigation__dropdown-header {
366
+ display: flex;
367
+ align-items: center;
368
+ padding: var(--navigation-padding);
369
+ cursor: pointer;
370
+ transition: all 0.2s ease;
371
+ flex-direction: row;
372
+ justify-content: flex-start;
373
+ }
374
+
375
+ .navigation__dropdown-header:hover {
376
+ color: var(--navigation-hover-color);
377
+ }
378
+
379
+ .navigation__dropdown--active .navigation__dropdown-header {
380
+ color: var(--navigation-active-color);
381
+ background-color: var(--navigation-active-background-color);
382
+ }
383
+
384
+ .navigation__dropdown--disabled .navigation__dropdown-header {
385
+ opacity: 0.5;
386
+ cursor: not-allowed;
387
+ color: var(--navigation-disabled-color);
388
+ }
389
+
390
+ .navigation__dropdown-arrow {
391
+ margin-left: 0.5rem;
392
+ margin-right: 0;
393
+ transition: transform 0.2s ease;
394
+ display: flex;
395
+ align-items: center;
396
+ font-size: 0.8em;
397
+ }
398
+
399
+ .navigation--large-icons .navigation__dropdown-arrow {
400
+ margin-left: 0.5rem;
401
+ margin-right: 0;
402
+ margin-bottom: 0;
403
+ }
404
+
405
+ .navigation__dropdown-content {
406
+ position: absolute;
407
+ top: 100%;
408
+ left: 0;
409
+ min-width: 150px;
410
+ background: white;
411
+ border: 1px solid var(--navigation-color);
412
+ border-radius: var(--navigation-border-radius);
413
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
414
+ z-index: 10;
415
+ opacity: 0;
416
+ visibility: hidden;
417
+ transform: translateY(-10px);
418
+ transition: all 0.2s ease;
419
+ }
420
+
421
+ .navigation__dropdown-content--end {
422
+ left: auto;
423
+ right: 0;
424
+ }
425
+
426
+ .navigation__dropdown-item {
427
+ display: flex;
428
+ align-items: center;
429
+ padding: var(--navigation-padding);
430
+ cursor: pointer;
431
+ transition: all 0.2s ease;
432
+ justify-content: center;
433
+ }
434
+
435
+ .navigation__dropdown-item .navigation__label {
436
+ text-align: center;
437
+ justify-content: center;
438
+ }
439
+
440
+ .navigation__dropdown-item .navigation__icon {
441
+ margin-left: 0;
442
+ margin-right: 1rem;
443
+ }
444
+
445
+ .navigation__dropdown-item:hover {
446
+ background: rgba(0, 0, 0, 0.05);
447
+ color: var(--navigation-hover-color);
448
+ }
449
+
450
+ .navigation__dropdown-item--disabled {
451
+ opacity: 0.5;
452
+ cursor: not-allowed;
453
+ color: var(--navigation-disabled-color);
454
+ }
455
+
456
+ /* Common styles */
457
+ .navigation__icon {
458
+ margin-left: 1rem;
459
+ margin-right: 1rem;
460
+ font-size: 1.2em;
461
+ display: flex;
462
+ align-items: center;
463
+ justify-content: center;
464
+ }
465
+
466
+ .navigation__icon-image {
467
+ width: 1.2em;
468
+ height: 1.2em;
469
+ object-fit: contain;
470
+ }
471
+
472
+ .navigation--large-icons .navigation__icon {
473
+ font-size: 2.5rem;
474
+ margin-bottom: 0.5rem;
475
+ margin-left: 0;
476
+ margin-right: 0;
477
+ }
478
+
479
+ .navigation--large-icons .navigation__icon-image {
480
+ width: 2.5rem;
481
+ height: 2.5rem;
482
+ }
483
+
484
+ .navigation--large-icons .navigation__tile-content,
485
+ .navigation--large-icons .navigation__dropdown-header {
486
+ flex-direction: column;
487
+ align-items: center;
488
+ text-align: center;
489
+ }
490
+
491
+ .navigation--large-icons .navigation__label {
492
+ text-align: center;
493
+ }
494
+
495
+ /* Icon-only styles */
496
+ .navigation__tile-content--icon-only,
497
+ .navigation__dropdown-header--icon-only {
498
+ justify-content: center;
499
+ }
500
+
501
+ .navigation__tile-content--icon-only .navigation__icon,
502
+ .navigation__dropdown-header--icon-only .navigation__icon {
503
+ margin-left: 0;
504
+ margin-right: 0;
505
+ margin-bottom: 0;
506
+ }
507
+
508
+ /* Update content layouts */
509
+ .navigation__tile-content,
510
+ .navigation__dropdown-header {
511
+ display: flex;
512
+ align-items: center;
513
+ width: 100%;
514
+ justify-content: flex-start;
515
+ }
516
+
517
+ .navigation__tile-content {
518
+ display: flex;
519
+ align-items: center;
520
+ width: 100%;
521
+ flex-direction: row;
522
+ justify-content: center;
523
+ }
524
+
525
+ .navigation__dropdown-header {
526
+ flex-direction: row;
527
+ justify-content: flex-start;
528
+ }
529
+
530
+ .navigation__tile--spacer {
531
+ cursor: default;
532
+ pointer-events: none;
533
+ border-color: transparent;
534
+ background: transparent;
535
+ }
536
+
537
+ .navigation__tile--spacer:hover {
538
+ border-color: transparent;
539
+ color: inherit;
540
+ background: transparent;
541
+ }
542
+
543
+ .navigation__label {
544
+ white-space: nowrap;
545
+ text-align: center;
546
+ display: flex;
547
+ align-items: center;
548
+ justify-content: center;
549
+ }
550
+
551
+ .navigation__label span {
552
+ flex: 1;
553
+ text-align: center;
554
+ }
555
+
556
+ .navigation__dropdown-item .navigation__label {
557
+ text-align: center;
558
+ justify-content: center;
559
+ }
560
+
561
+ .navigation__dropdown-item .navigation__icon {
562
+ margin-left: 0;
563
+ margin-right: 1rem;
564
+ }
565
+
566
+ .navigation__tile--open .navigation__dropdown-arrow,
567
+ .navigation__dropdown--open .navigation__dropdown-arrow {
568
+ transform: rotate(180deg);
569
+ }
570
+
571
+ /* Update dropdown styles */
572
+ .navigation__tile--open .navigation__dropdown-content,
573
+ .navigation__dropdown--open .navigation__dropdown-content {
574
+ opacity: 1;
575
+ visibility: visible;
576
+ transform: translateY(0);
577
+ position: absolute;
578
+ top: 100%;
579
+ left: 0;
580
+ min-width: 100%;
581
+ background: white;
582
+ border: 1px solid var(--navigation-color);
583
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
584
+ z-index: 10;
585
+ }
586
+
587
+ .navigation__tile--right .navigation__dropdown-content {
588
+ left: auto;
589
+ right: 0;
590
+ }
591
+
592
+ .navigation__tile--open .navigation__dropdown-content,
593
+ .navigation__dropdown--open .navigation__dropdown-content {
594
+ border-radius: var(--navigation-border-radius);
595
+ }
596
+
597
+ .navigation__tile .navigation__dropdown-item {
598
+ min-height: 3rem;
599
+ display: flex;
600
+ align-items: center;
601
+ padding: var(--navigation-padding);
602
+ cursor: pointer;
603
+ transition: all 0.2s ease;
604
+ }
605
+
606
+ .navigation__tile .navigation__dropdown-item:hover {
607
+ background: rgba(0, 0, 0, 0.05);
608
+ color: var(--navigation-hover-color);
609
+ }
610
+
611
+ .navigation__tile .navigation__dropdown-item--disabled {
612
+ opacity: 0.5;
613
+ cursor: not-allowed;
614
+ color: var(--navigation-disabled-color);
615
+ }
616
+
617
+ .navigation__external-link {
618
+ position: absolute;
619
+ top: 0.5rem;
620
+ right: 0.5rem;
621
+ font-size: 1.6em;
622
+ cursor: pointer;
623
+ color: rgba(0, 0, 0, 0.2);
624
+ transition: all 0.2s ease;
625
+ }
626
+
627
+ .navigation__external-link:hover {
628
+ color: rgba(0, 0, 0, 0.9);
629
+ }
630
+
631
+ .navigation__tile:hover .navigation__external-link {
632
+ color: rgba(0, 0, 0, 0.3);
633
+ }
634
+ </style>