bge-ui 1.3.4 → 1.3.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.
Files changed (135) hide show
  1. package/dist/datePicker/components/ActionRow.vue.d.ts +1051 -0
  2. package/dist/datePicker/components/Common/ArrowBtn.vue.d.ts +29 -0
  3. package/dist/datePicker/components/Common/InstanceWrap.vue.d.ts +29 -0
  4. package/dist/datePicker/components/Common/SelectionOverlay.vue.d.ts +55 -0
  5. package/dist/datePicker/components/DatePicker/DatePicker.vue.d.ts +1114 -0
  6. package/dist/datePicker/components/DatePicker/DpCalendar.vue.d.ts +1085 -0
  7. package/dist/datePicker/components/DatePicker/DpHeader.vue.d.ts +1103 -0
  8. package/dist/datePicker/components/DatePicker/date-picker.d.ts +35 -0
  9. package/dist/datePicker/components/DatepickerInput.vue.d.ts +1008 -0
  10. package/dist/datePicker/components/DatepickerMenu.vue.d.ts +1061 -0
  11. package/dist/datePicker/components/Icons/CalendarIcon.d.ts +9 -0
  12. package/dist/datePicker/components/Icons/CancelIcon.d.ts +9 -0
  13. package/dist/datePicker/components/Icons/ChevronDownIcon.d.ts +9 -0
  14. package/dist/datePicker/components/Icons/ChevronLeftIcon.d.ts +9 -0
  15. package/dist/datePicker/components/Icons/ChevronRightIcon.d.ts +9 -0
  16. package/dist/datePicker/components/Icons/ChevronUpIcon.d.ts +9 -0
  17. package/dist/datePicker/components/Icons/ClockIcon.d.ts +9 -0
  18. package/dist/datePicker/components/Icons/index.d.ts +7 -0
  19. package/dist/datePicker/components/MonthPicker/MonthPicker.vue.d.ts +1071 -0
  20. package/dist/datePicker/components/MonthPicker/month-picker.d.ts +34 -0
  21. package/dist/datePicker/components/QuarterPicker/QuarterPicker.vue.d.ts +1043 -0
  22. package/dist/datePicker/components/QuarterPicker/quarter-picker.d.ts +25 -0
  23. package/dist/datePicker/components/TimePicker/TimeInput.vue.d.ts +1116 -0
  24. package/dist/datePicker/components/TimePicker/TimePicker.vue.d.ts +1087 -0
  25. package/dist/datePicker/components/TimePicker/TimePickerSolo.vue.d.ts +1036 -0
  26. package/dist/datePicker/components/TimePicker/time-picker-utils.d.ts +15 -0
  27. package/dist/datePicker/components/TimePicker/time-picker.d.ts +13 -0
  28. package/dist/datePicker/components/YearPicker/YearPicker.vue.d.ts +1040 -0
  29. package/dist/datePicker/components/YearPicker/year-picker.d.ts +9 -0
  30. package/dist/datePicker/components/shared/YearModePicker.vue.d.ts +1075 -0
  31. package/dist/datePicker/components/shared/month-quarter-picker.d.ts +29 -0
  32. package/dist/datePicker/composables/arrow-navigate.d.ts +26 -0
  33. package/dist/datePicker/composables/calendar-class.d.ts +8 -0
  34. package/dist/datePicker/composables/common.d.ts +6 -0
  35. package/dist/datePicker/composables/defaults.d.ts +37 -0
  36. package/dist/datePicker/composables/external-internal-mapper.d.ts +14 -0
  37. package/dist/datePicker/composables/flow.d.ts +10 -0
  38. package/dist/datePicker/composables/index.d.ts +14 -0
  39. package/dist/datePicker/composables/model.d.ts +17 -0
  40. package/dist/datePicker/composables/month-year.d.ts +10 -0
  41. package/dist/datePicker/composables/position.d.ts +25 -0
  42. package/dist/datePicker/composables/shared.d.ts +12 -0
  43. package/dist/datePicker/composables/slots.d.ts +10 -0
  44. package/dist/datePicker/composables/state.d.ts +8 -0
  45. package/dist/datePicker/composables/transition.d.ts +7 -0
  46. package/dist/datePicker/composables/validation.d.ts +12 -0
  47. package/dist/datePicker/constants/index.d.ts +43 -0
  48. package/dist/datePicker/datePicker.vue.d.ts +1006 -0
  49. package/dist/datePicker/directives/clickOutside.d.ts +2 -0
  50. package/dist/datePicker/index.vue.d.ts +1012 -0
  51. package/dist/datePicker/interfaces.d.ts +323 -0
  52. package/dist/datePicker/props.d.ts +865 -0
  53. package/dist/datePicker/utils/date-utils.d.ts +45 -0
  54. package/dist/datePicker/utils/defaults.d.ts +42 -0
  55. package/dist/datePicker/utils/timezone.d.ts +8 -0
  56. package/dist/datePicker/utils/type-guard.d.ts +1 -0
  57. package/dist/datePicker/utils/util.d.ts +57 -0
  58. package/dist/dialog/index.vue.d.ts +6 -3
  59. package/dist/index.d.ts +2 -1
  60. package/dist/index.js +13008 -1250
  61. package/dist/style.css +1188 -1
  62. package/dist/tooltip/index.vue.d.ts +2 -2
  63. package/dist/tooltip/usePopper.d.ts +5 -5
  64. package/package.json +3 -2
  65. package/src/datePicker/components/ActionRow.vue +216 -0
  66. package/src/datePicker/components/Common/ArrowBtn.vue +42 -0
  67. package/src/datePicker/components/Common/InstanceWrap.vue +28 -0
  68. package/src/datePicker/components/Common/SelectionOverlay.vue +320 -0
  69. package/src/datePicker/components/DatePicker/DatePicker.vue +302 -0
  70. package/src/datePicker/components/DatePicker/DpCalendar.vue +405 -0
  71. package/src/datePicker/components/DatePicker/DpHeader.vue +332 -0
  72. package/src/datePicker/components/DatePicker/date-picker.ts +674 -0
  73. package/src/datePicker/components/DatepickerInput.vue +334 -0
  74. package/src/datePicker/components/DatepickerMenu.vue +424 -0
  75. package/src/datePicker/components/Icons/CalendarIcon.ts +40 -0
  76. package/src/datePicker/components/Icons/CancelIcon.ts +32 -0
  77. package/src/datePicker/components/Icons/ChevronDownIcon.ts +29 -0
  78. package/src/datePicker/components/Icons/ChevronLeftIcon.ts +29 -0
  79. package/src/datePicker/components/Icons/ChevronRightIcon.ts +29 -0
  80. package/src/datePicker/components/Icons/ChevronUpIcon.ts +29 -0
  81. package/src/datePicker/components/Icons/ClockIcon.ts +32 -0
  82. package/src/datePicker/components/Icons/index.ts +8 -0
  83. package/src/datePicker/components/MonthPicker/MonthPicker.vue +130 -0
  84. package/src/datePicker/components/MonthPicker/month-picker.ts +232 -0
  85. package/src/datePicker/components/QuarterPicker/QuarterPicker.vue +111 -0
  86. package/src/datePicker/components/QuarterPicker/quarter-picker.ts +153 -0
  87. package/src/datePicker/components/TimePicker/TimeInput.vue +477 -0
  88. package/src/datePicker/components/TimePicker/TimePicker.vue +265 -0
  89. package/src/datePicker/components/TimePicker/TimePickerSolo.vue +79 -0
  90. package/src/datePicker/components/TimePicker/time-picker-utils.ts +179 -0
  91. package/src/datePicker/components/TimePicker/time-picker.ts +112 -0
  92. package/src/datePicker/components/YearPicker/YearPicker.vue +70 -0
  93. package/src/datePicker/components/YearPicker/year-picker.ts +109 -0
  94. package/src/datePicker/components/shared/YearModePicker.vue +105 -0
  95. package/src/datePicker/components/shared/month-quarter-picker.ts +199 -0
  96. package/src/datePicker/composables/arrow-navigate.ts +191 -0
  97. package/src/datePicker/composables/calendar-class.ts +383 -0
  98. package/src/datePicker/composables/common.ts +25 -0
  99. package/src/datePicker/composables/defaults.ts +123 -0
  100. package/src/datePicker/composables/external-internal-mapper.ts +442 -0
  101. package/src/datePicker/composables/flow.ts +70 -0
  102. package/src/datePicker/composables/index.ts +14 -0
  103. package/src/datePicker/composables/model.ts +89 -0
  104. package/src/datePicker/composables/month-year.ts +72 -0
  105. package/src/datePicker/composables/position.ts +297 -0
  106. package/src/datePicker/composables/shared.ts +98 -0
  107. package/src/datePicker/composables/slots.ts +84 -0
  108. package/src/datePicker/composables/state.ts +25 -0
  109. package/src/datePicker/composables/transition.ts +18 -0
  110. package/src/datePicker/composables/validation.ts +312 -0
  111. package/src/datePicker/constants/index.ts +49 -0
  112. package/src/datePicker/datePicker.vue +554 -0
  113. package/src/datePicker/directives/clickOutside.ts +79 -0
  114. package/src/datePicker/index.vue +158 -0
  115. package/src/datePicker/interfaces.ts +404 -0
  116. package/src/datePicker/props.ts +173 -0
  117. package/src/datePicker/style/components/_ActionRow.scss +73 -0
  118. package/src/datePicker/style/components/_Calendar.scss +284 -0
  119. package/src/datePicker/style/components/_DatepickerInput.scss +109 -0
  120. package/src/datePicker/style/components/_DatepickerMenu.scss +213 -0
  121. package/src/datePicker/style/components/_MonthYearInput.scss +97 -0
  122. package/src/datePicker/style/components/_QuarterPicker.scss +53 -0
  123. package/src/datePicker/style/components/_SelectionOverlay.scss +142 -0
  124. package/src/datePicker/style/components/_TimeInput.scss +181 -0
  125. package/src/datePicker/style/components/_shared.scss +15 -0
  126. package/src/datePicker/style/main.scss +259 -0
  127. package/src/datePicker/utils/date-utils.ts +440 -0
  128. package/src/datePicker/utils/defaults.ts +327 -0
  129. package/src/datePicker/utils/timezone.ts +38 -0
  130. package/src/datePicker/utils/type-guard.ts +3 -0
  131. package/src/datePicker/utils/util.ts +322 -0
  132. package/src/dialog/index.vue +9 -0
  133. package/src/form/index.vue +2 -1
  134. package/src/index.ts +6 -2
  135. package/src/slider/index.vue +1 -1
@@ -114,8 +114,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
114
114
  toggleTooltip: import("vue").Ref<any>;
115
115
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
116
116
  transition: (...args: any[]) => void;
117
- opened: (...args: any[]) => void;
118
117
  closed: (...args: any[]) => void;
118
+ opened: (...args: any[]) => void;
119
119
  }, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
120
120
  /**
121
121
  * title
@@ -225,9 +225,9 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
225
225
  validator(value: unknown): boolean;
226
226
  };
227
227
  }>> & {
228
+ onClosed?: ((...args: any[]) => any) | undefined;
228
229
  onTransition?: ((...args: any[]) => any) | undefined;
229
230
  onOpened?: ((...args: any[]) => any) | undefined;
230
- onClosed?: ((...args: any[]) => any) | undefined;
231
231
  }, {
232
232
  size: string;
233
233
  type: string;
@@ -172,23 +172,23 @@ declare const usePopper: (triggerReference: any, suppliedOptions?: Options) => {
172
172
  };
173
173
  } | undefined;
174
174
  offset?: {
175
- auto?: {
175
+ left?: {
176
176
  y: number;
177
177
  x: number;
178
178
  } | undefined;
179
- top?: {
179
+ right?: {
180
180
  y: number;
181
181
  x: number;
182
182
  } | undefined;
183
- bottom?: {
183
+ top?: {
184
184
  y: number;
185
185
  x: number;
186
186
  } | undefined;
187
- left?: {
187
+ bottom?: {
188
188
  y: number;
189
189
  x: number;
190
190
  } | undefined;
191
- right?: {
191
+ auto?: {
192
192
  y: number;
193
193
  x: number;
194
194
  } | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bge-ui",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -27,7 +27,8 @@
27
27
  "tsx": "^4.6.2",
28
28
  "vite-plugin-dts": "^3.6.4",
29
29
  "vue": "^3.3.9",
30
- "bignumber.js": "^9.1.2"
30
+ "bignumber.js": "^9.1.2",
31
+ "date-fns": "^3.6.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/node": "^20.10.1"
@@ -0,0 +1,216 @@
1
+ <template>
2
+ <div ref="actionRowRef" class="dp__action_row">
3
+ <template v-if="$slots['action-row']">
4
+ <slot
5
+ name="action-row"
6
+ v-bind="{
7
+ internalModelValue,
8
+ disabled,
9
+ selectDate: () => $emit('select-date'),
10
+ closePicker: () => $emit('close-picker'),
11
+ }"
12
+ />
13
+ </template>
14
+ <template v-else>
15
+ <div
16
+ v-if="defaultedActionRow.showPreview"
17
+ class="dp__selection_preview"
18
+ :title="formatValue"
19
+ :style="previewStyle"
20
+ >
21
+ <slot
22
+ v-if="$slots['action-preview'] && showPreview"
23
+ name="action-preview"
24
+ :value="internalModelValue"
25
+ />
26
+ <template v-if="!$slots['action-preview'] && showPreview">
27
+ {{ formatValue }}
28
+ </template>
29
+ </div>
30
+ <div ref="actionBtnContainer" class="dp__action_buttons" data-dp-element="action-row">
31
+ <slot v-if="$slots['action-buttons']" name="action-buttons" :value="internalModelValue" />
32
+ <template v-if="!$slots['action-buttons']">
33
+ <button
34
+ v-if="!defaultedInline.enabled && defaultedActionRow.showCancel"
35
+ ref="cancelButtonRef"
36
+ type="button"
37
+ class="dp__action_button dp__action_cancel"
38
+ @click="$emit('close-picker')"
39
+ @keydown="checkKeyDown($event, () => $emit('close-picker'))"
40
+ >
41
+ {{ cancelText }}
42
+ </button>
43
+ <button
44
+ v-if="defaultedActionRow.showNow"
45
+ type="button"
46
+ class="dp__action_button dp__action_cancel"
47
+ @click="$emit('select-now')"
48
+ @keydown="checkKeyDown($event, () => $emit('select-now'))"
49
+ >
50
+ {{ nowButtonLabel }}
51
+ </button>
52
+ <button
53
+ v-if="defaultedActionRow.showSelect"
54
+ ref="selectButtonRef"
55
+ type="button"
56
+ class="dp__action_button dp__action_select"
57
+ :disabled="disabled"
58
+ data-test="select-button"
59
+ @keydown="checkKeyDown($event, () => selectDate())"
60
+ @click="selectDate"
61
+ >
62
+ {{ selectText }}
63
+ </button>
64
+ </template>
65
+ </div>
66
+ </template>
67
+ </div>
68
+ </template>
69
+
70
+ <script lang="ts" setup>
71
+ import { computed, onMounted, onUnmounted, ref } from 'vue';
72
+
73
+ import { checkKeyDown, convertType, unrefElement } from '../utils/util';
74
+ import { useArrowNavigation, useDefaults, useValidation } from '../composables';
75
+ import { PickerBaseProps } from '../props';
76
+ import { formatDate } from '../utils/date-utils';
77
+
78
+ import type { PropType } from 'vue';
79
+
80
+ defineOptions({
81
+ compatConfig: {
82
+ MODE: 3,
83
+ },
84
+ });
85
+
86
+ const emit = defineEmits(['close-picker', 'select-date', 'select-now', 'invalid-select']);
87
+
88
+ const props = defineProps({
89
+ menuMount: { type: Boolean as PropType<boolean>, default: false },
90
+ calendarWidth: { type: Number as PropType<number>, default: 0 },
91
+ ...PickerBaseProps,
92
+ });
93
+
94
+ const {
95
+ defaultedActionRow,
96
+ defaultedPreviewFormat,
97
+ defaultedMultiCalendars,
98
+ defaultedTextInput,
99
+ defaultedInline,
100
+ defaultedRange,
101
+ defaultedMultiDates,
102
+ } = useDefaults(props);
103
+ const { isTimeValid, isMonthValid } = useValidation(props);
104
+ const { buildMatrix } = useArrowNavigation();
105
+
106
+ const cancelButtonRef = ref(null);
107
+ const selectButtonRef = ref(null);
108
+ const showPreview = ref(false);
109
+ const previewStyle = ref<any>({});
110
+ const actionBtnContainer = ref<HTMLElement | null>(null);
111
+ const actionRowRef = ref<HTMLElement | null>(null);
112
+
113
+ onMounted(() => {
114
+ if (props.arrowNavigation) {
115
+ buildMatrix([unrefElement(cancelButtonRef), unrefElement(selectButtonRef)] as HTMLElement[], 'actionRow');
116
+ }
117
+ getPreviewAvailableSpace();
118
+ window.addEventListener('resize', getPreviewAvailableSpace);
119
+ });
120
+
121
+ onUnmounted(() => {
122
+ window.removeEventListener('resize', getPreviewAvailableSpace);
123
+ });
124
+
125
+ const getPreviewAvailableSpace = () => {
126
+ showPreview.value = false;
127
+ setTimeout(() => {
128
+ const rect = actionBtnContainer.value?.getBoundingClientRect();
129
+ const rowRect = actionRowRef.value?.getBoundingClientRect();
130
+ if (rect && rowRect) {
131
+ previewStyle.value.maxWidth = `${rowRect.width - rect.width - 20}px`;
132
+ }
133
+ showPreview.value = true;
134
+ }, 0);
135
+ };
136
+
137
+ const validDateRange = computed(() => {
138
+ return defaultedRange.value.enabled && !defaultedRange.value.partialRange && props.internalModelValue
139
+ ? (props.internalModelValue as Date[]).length === 2
140
+ : true;
141
+ });
142
+
143
+ const disabled = computed(
144
+ () =>
145
+ !isTimeValid.value(props.internalModelValue) ||
146
+ !isMonthValid.value(props.internalModelValue) ||
147
+ !validDateRange.value,
148
+ );
149
+
150
+ const handleCustomPreviewFormat = () => {
151
+ const formatFn = defaultedPreviewFormat.value as (val: Date | Date[]) => string | string[];
152
+
153
+ if (props.timePicker) return formatFn(convertType(props.internalModelValue));
154
+
155
+ if (props.monthPicker) return formatFn(convertType(props.internalModelValue as Date));
156
+
157
+ return formatFn(convertType(props.internalModelValue));
158
+ };
159
+
160
+ const formatRangeDate = () => {
161
+ const dates = props.internalModelValue as Date[];
162
+ if (defaultedMultiCalendars.value.count > 0) {
163
+ return `${formatDatePreview(dates[0])} - ${formatDatePreview(dates[1])}`;
164
+ }
165
+ return [formatDatePreview(dates[0]), formatDatePreview(dates[1])];
166
+ };
167
+
168
+ const formatDatePreview = (date: Date) => {
169
+ return formatDate(
170
+ date,
171
+ defaultedPreviewFormat.value as string,
172
+ props.formatLocale,
173
+ defaultedTextInput.value.rangeSeparator,
174
+ props.modelAuto,
175
+ defaultedPreviewFormat.value as string,
176
+ );
177
+ };
178
+
179
+ const previewValue = computed((): string | string[] => {
180
+ if (!props.internalModelValue || !props.menuMount) return '';
181
+ if (typeof defaultedPreviewFormat.value === 'string') {
182
+ if (Array.isArray(props.internalModelValue)) {
183
+ if (props.internalModelValue.length === 2 && props.internalModelValue[1]) {
184
+ return formatRangeDate();
185
+ }
186
+ if (defaultedMultiDates.value.enabled) {
187
+ return props.internalModelValue.map((date) => `${formatDatePreview(date)}`);
188
+ }
189
+ if (props.modelAuto) {
190
+ return `${formatDatePreview(props.internalModelValue[0])}`;
191
+ }
192
+ return `${formatDatePreview(props.internalModelValue[0])} -`;
193
+ }
194
+ return formatDatePreview(props.internalModelValue);
195
+ }
196
+ return handleCustomPreviewFormat();
197
+ });
198
+
199
+ const dateSeparator = () => (defaultedMultiDates.value.enabled ? '; ' : ' - ');
200
+
201
+ const formatValue = computed(() =>
202
+ !Array.isArray(previewValue.value) ? previewValue.value : previewValue.value.join(dateSeparator()),
203
+ );
204
+
205
+ const selectDate = (): void => {
206
+ if (
207
+ isTimeValid.value(props.internalModelValue) &&
208
+ isMonthValid.value(props.internalModelValue) &&
209
+ validDateRange.value
210
+ ) {
211
+ emit('select-date');
212
+ } else {
213
+ emit('invalid-select');
214
+ }
215
+ };
216
+ </script>
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <button
3
+ ref="elRef"
4
+ type="button"
5
+ :data-dp-element="elName"
6
+ class="dp__btn dp--arrow-btn-nav"
7
+ tabindex="0"
8
+ :aria-label="ariaLabel"
9
+ :aria-disabled="disabled || undefined"
10
+ @click="$emit('activate')"
11
+ @keydown="checkKeyDown($event, () => $emit('activate'), true)"
12
+ >
13
+ <span class="dp__inner_nav" :class="{ dp__inner_nav_disabled: disabled }">
14
+ <slot />
15
+ </span>
16
+ </button>
17
+ </template>
18
+
19
+ <script lang="ts" setup>
20
+ import { onMounted, ref } from 'vue';
21
+ import { checkKeyDown } from '../../utils/util';
22
+
23
+ defineOptions({
24
+ compatConfig: {
25
+ MODE: 3,
26
+ },
27
+ });
28
+
29
+ const emit = defineEmits(['activate', 'set-ref']);
30
+
31
+ interface Props {
32
+ ariaLabel?: string;
33
+ elName?: string;
34
+ disabled: boolean;
35
+ }
36
+
37
+ defineProps<Props>();
38
+
39
+ const elRef = ref(null);
40
+
41
+ onMounted(() => emit('set-ref', elRef));
42
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <div
3
+ :class="{
4
+ dp__menu_inner: !stretch,
5
+ 'dp--menu--inner-stretched': stretch,
6
+ dp__flex_display: multiCalendars > 0,
7
+ 'dp--flex-display-collapsed': collapse,
8
+ }"
9
+ >
10
+ <div v-for="(instance, i) in instances" :key="instance" :class="calendarInstanceClassWrapper">
11
+ <slot :instance="instance" :index="i"></slot>
12
+ </div>
13
+ </div>
14
+ </template>
15
+
16
+ <script lang="ts" setup>
17
+ import { computed } from 'vue';
18
+
19
+ const props = defineProps<{ multiCalendars: number; stretch?: boolean; collapse?: boolean }>();
20
+
21
+ const instances = computed((): number[] =>
22
+ props.multiCalendars > 0 ? [...Array(props.multiCalendars).keys()] : [0],
23
+ );
24
+
25
+ const calendarInstanceClassWrapper = computed(() => ({
26
+ dp__instance_calendar: props.multiCalendars > 0,
27
+ }));
28
+ </script>
@@ -0,0 +1,320 @@
1
+ <template>
2
+ <div
3
+ ref="gridWrapRef"
4
+ :class="dpOverlayClass"
5
+ :style="dpOverlayStyle"
6
+ :role="useRelative ? undefined : 'dialog'"
7
+ :aria-label="overlayLabel"
8
+ :tabindex="useRelative ? undefined : '0'"
9
+ @keydown="onKeyDown"
10
+ @click.prevent
11
+ >
12
+ <div
13
+ ref="containerRef"
14
+ :class="containerClass"
15
+ :style="{ '--dp-overlay-height': `${containerHeight}px` }"
16
+ role="grid"
17
+ >
18
+ <div class="dp__selection_grid_header"><slot name="header"></slot></div>
19
+ <template v-if="$slots.overlay">
20
+ <slot name="overlay" />
21
+ </template>
22
+ <template v-else>
23
+ <div
24
+ v-for="(row, i) in items"
25
+ :key="i"
26
+ class="dp__overlay_row"
27
+ role="row"
28
+ >
29
+ <div
30
+ v-for="(col, ind) in row"
31
+ :key="col.value"
32
+ :ref="(el) => assignRef(el, col, i, ind)"
33
+ role="gridcell"
34
+ :class="cellClassName"
35
+ :aria-selected="col.active || undefined"
36
+ :aria-disabled="col.disabled || undefined"
37
+ tabindex="0"
38
+ :data-test="col.text"
39
+ @click.prevent="onClick(col)"
40
+ @keydown="checkKeyDown($event, () => onClick(col), true)"
41
+ @mouseover="setHoverValue(col.value)"
42
+ >
43
+ <div :class="col.className">
44
+ <slot v-if="$slots.item" name="item" :item="col" />
45
+ <template v-if="!$slots.item">{{ col.text }}</template>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </template>
50
+ </div>
51
+ <button
52
+ v-if="$slots['button-icon']"
53
+ v-show="!hideNavigationButtons(hideNavigation, type)"
54
+ ref="toggleButton"
55
+ type="button"
56
+ :aria-label="defaultedAriaLabels?.toggleOverlay"
57
+ :class="actionButtonClass"
58
+ tabindex="0"
59
+ @click="toggle"
60
+ @keydown="onBtnKeyDown"
61
+ >
62
+ <slot name="button-icon" />
63
+ </button>
64
+ </div>
65
+ </template>
66
+
67
+ <script lang="ts" setup>
68
+ import { computed, nextTick, onBeforeUpdate, onMounted, onUnmounted, ref, watch } from 'vue';
69
+
70
+ import {
71
+ checkKeyDown,
72
+ checkStopPropagation,
73
+ convertType,
74
+ findFocusableEl,
75
+ getElWithin,
76
+ unrefElement,
77
+ } from '../../utils/util';
78
+ import { useArrowNavigation, useCommon, useDefaults } from '../../composables';
79
+
80
+ import type { AriaLabels, Config, DynamicClass, Flow, OverlayGridItem, TextInputProp } from '../../interfaces';
81
+ import type { PickerBasePropsType } from '../../props';
82
+ import { EventKey } from '../../constants';
83
+
84
+ const { setSelectionGrid, buildMultiLevelMatrix, setMonthPicker } = useArrowNavigation();
85
+
86
+ const emit = defineEmits(['selected', 'toggle', 'reset-flow', 'hover-value']);
87
+
88
+ interface Props {
89
+ items: OverlayGridItem[][];
90
+ type: Flow;
91
+ isLast: boolean;
92
+ arrowNavigation?: boolean;
93
+ skipButtonRef?: boolean;
94
+ headerRefs?: (HTMLElement | null)[];
95
+ hideNavigation?: Flow[];
96
+ escClose?: boolean;
97
+ useRelative?: boolean;
98
+ height?: number | string;
99
+ textInput?: TextInputProp;
100
+ config?: Partial<Config>;
101
+ noOverlayFocus?: boolean;
102
+ focusValue?: number;
103
+ menuWrapRef?: HTMLElement | null;
104
+ ariaLabels?: Partial<AriaLabels>;
105
+ overlayLabel?: string;
106
+ }
107
+
108
+ const props = defineProps<Props>();
109
+
110
+ const { defaultedAriaLabels, defaultedTextInput, defaultedConfig } = useDefaults(
111
+ props as unknown as PickerBasePropsType,
112
+ );
113
+ const { hideNavigationButtons } = useCommon();
114
+
115
+ const scrollable = ref(false);
116
+ const selectionActiveRef = ref<HTMLElement | null>(null);
117
+ const gridWrapRef = ref(null);
118
+ const elementRefs = ref<Array<HTMLElement | null>[]>([]);
119
+ const hoverValue = ref();
120
+ const toggleButton = ref<HTMLElement | null>(null);
121
+ const containerHeight = ref(0);
122
+ const containerRef = ref<HTMLElement | null>(null);
123
+
124
+ onBeforeUpdate(() => {
125
+ selectionActiveRef.value = null;
126
+ });
127
+
128
+ /**
129
+ * On mounted hook, set the scroll position, if any to a selected value when opening overlay
130
+ */
131
+ onMounted(() => {
132
+ nextTick().then(() => setContainerHeightAndScroll());
133
+ if (!props.noOverlayFocus) {
134
+ focusGrid();
135
+ }
136
+ handleArrowNav(true);
137
+ });
138
+
139
+ onUnmounted(() => handleArrowNav(false));
140
+
141
+ const handleArrowNav = (value: boolean): void => {
142
+ if (props.arrowNavigation) {
143
+ if (props.headerRefs?.length) {
144
+ setMonthPicker(value);
145
+ } else {
146
+ setSelectionGrid(value);
147
+ }
148
+ }
149
+ };
150
+
151
+ const focusGrid = (): void => {
152
+ const elm = unrefElement(gridWrapRef);
153
+ if (elm) {
154
+ if (!defaultedTextInput.value.enabled) {
155
+ if (selectionActiveRef.value) {
156
+ selectionActiveRef.value?.focus({ preventScroll: true });
157
+ } else {
158
+ elm.focus({ preventScroll: true });
159
+ }
160
+ }
161
+
162
+ scrollable.value = elm.clientHeight < elm.scrollHeight;
163
+ }
164
+ };
165
+
166
+ // Dynamic class for the overlay
167
+ const dpOverlayClass = computed(
168
+ (): DynamicClass => ({
169
+ dp__overlay: true,
170
+ 'dp--overlay-absolute': !props.useRelative,
171
+ 'dp--overlay-relative': props.useRelative,
172
+ }),
173
+ );
174
+
175
+ const dpOverlayStyle = computed(() =>
176
+ props.useRelative ? { height: `${props.height}px`, width: `var(--dp-menu-min-width)` } : undefined,
177
+ );
178
+
179
+ const cellClassName = computed(() => ({
180
+ dp__overlay_col: true,
181
+ }));
182
+
183
+ /**
184
+ * Dynamic class for action button
185
+ */
186
+ const actionButtonClass = computed(
187
+ (): DynamicClass => ({
188
+ dp__btn: true,
189
+ dp__button: true,
190
+ dp__overlay_action: true,
191
+ dp__over_action_scroll: scrollable.value,
192
+ dp__button_bottom: props.isLast,
193
+ }),
194
+ );
195
+
196
+ const containerClass = computed(() => ({
197
+ dp__overlay_container: true,
198
+ dp__container_flex: props.items?.length <= 6,
199
+ dp__container_block: props.items?.length > 6,
200
+ }));
201
+
202
+ watch(
203
+ () => props.items,
204
+ () => setContainerHeightAndScroll(false),
205
+ { deep: true },
206
+ );
207
+
208
+ const setContainerHeightAndScroll = (setScroll = true) => {
209
+ nextTick().then(() => {
210
+ const el = unrefElement(selectionActiveRef);
211
+ const parent = unrefElement(gridWrapRef);
212
+ const btn = unrefElement(toggleButton);
213
+ const container = unrefElement(containerRef);
214
+ const toggleBtnHeight = btn ? btn.getBoundingClientRect().height : 0;
215
+ if (parent) {
216
+ if (!parent.getBoundingClientRect().height) {
217
+ containerHeight.value = defaultedConfig.value.modeHeight - toggleBtnHeight;
218
+ } else {
219
+ containerHeight.value = parent.getBoundingClientRect().height - toggleBtnHeight;
220
+ }
221
+ }
222
+ if (el && container && setScroll) {
223
+ container.scrollTop =
224
+ el.offsetTop -
225
+ container.offsetTop -
226
+ (containerHeight.value / 2 - el.getBoundingClientRect().height) -
227
+ toggleBtnHeight;
228
+ }
229
+ });
230
+ };
231
+
232
+ /**
233
+ * Handle click on cell, if value is enabled (not in filters), emit value back to parent
234
+ */
235
+ const onClick = (val: OverlayGridItem): void => {
236
+ if (!val.disabled) {
237
+ emit('selected', val.value);
238
+ }
239
+ };
240
+
241
+ const toggle = () => {
242
+ emit('toggle');
243
+ emit('reset-flow');
244
+ };
245
+
246
+ const handleEsc = () => {
247
+ if (props.escClose) {
248
+ toggle();
249
+ }
250
+ };
251
+
252
+ const assignRef = (el: any, col: OverlayGridItem, rowInd: number, colInd: number): void => {
253
+ if (el) {
254
+ if (col.active || col.value === props.focusValue) {
255
+ selectionActiveRef.value = el;
256
+ }
257
+ if (props.arrowNavigation) {
258
+ if (Array.isArray(elementRefs.value[rowInd])) {
259
+ elementRefs.value[rowInd][colInd] = el;
260
+ } else {
261
+ elementRefs.value[rowInd] = [el];
262
+ }
263
+ buildMatrix();
264
+ }
265
+ }
266
+ };
267
+
268
+ const buildMatrix = () => {
269
+ const refs = props.headerRefs?.length
270
+ ? [props.headerRefs].concat(elementRefs.value)
271
+ : elementRefs.value.concat([props.skipButtonRef ? [] : [toggleButton.value]]);
272
+
273
+ buildMultiLevelMatrix(convertType(refs), props.headerRefs?.length ? 'monthPicker' : 'selectionGrid');
274
+ };
275
+
276
+ const handleArrowKey = (ev: KeyboardEvent) => {
277
+ if (props.arrowNavigation) return;
278
+ checkStopPropagation(ev, defaultedConfig.value, true);
279
+ };
280
+
281
+ const setHoverValue = (val: number) => {
282
+ hoverValue.value = val;
283
+ emit('hover-value', val);
284
+ };
285
+
286
+ const onTab = () => {
287
+ toggle();
288
+ if (!props.isLast) {
289
+ const actionRow = getElWithin(props.menuWrapRef ?? null, 'action-row');
290
+ if (actionRow) {
291
+ const focusable = findFocusableEl(actionRow);
292
+ focusable?.focus();
293
+ }
294
+ }
295
+ };
296
+
297
+ const onKeyDown = (ev: KeyboardEvent) => {
298
+ switch (ev.key) {
299
+ case EventKey.esc:
300
+ return handleEsc();
301
+ case EventKey.arrowLeft:
302
+ return handleArrowKey(ev);
303
+ case EventKey.arrowRight:
304
+ return handleArrowKey(ev);
305
+ case EventKey.arrowUp:
306
+ return handleArrowKey(ev);
307
+ case EventKey.arrowDown:
308
+ return handleArrowKey(ev);
309
+ default:
310
+ return;
311
+ }
312
+ };
313
+
314
+ const onBtnKeyDown = (ev: KeyboardEvent) => {
315
+ if (ev.key === EventKey.enter) return toggle();
316
+ if (ev.key === EventKey.tab) return onTab();
317
+ };
318
+
319
+ defineExpose({ focusGrid });
320
+ </script>