@demos-europe/demosplan-ui 0.0.1

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/buildTokens.js +59 -0
  3. package/components/DpButton/DpButton.stories.mdx +136 -0
  4. package/components/DpButton/DpButton.vue +118 -0
  5. package/components/DpDetails/DpDetails.stories.mdx +55 -0
  6. package/components/DpDetails/DpDetails.vue +58 -0
  7. package/components/DpIcon/DpIcon.stories.mdx +396 -0
  8. package/components/DpIcon/DpIcon.vue +51 -0
  9. package/components/DpIcon/util/iconVariables.js +148 -0
  10. package/components/DpInput/DpInput.stories.mdx +127 -0
  11. package/components/DpInput/DpInput.vue +284 -0
  12. package/components/DpLabel/DpLabel.stories.mdx +103 -0
  13. package/components/DpLabel/DpLabel.vue +112 -0
  14. package/components/DpLoading/DpLoading.stories.mdx +63 -0
  15. package/components/DpLoading/DpLoading.vue +63 -0
  16. package/components/core/DpAccordion.vue +108 -0
  17. package/components/core/DpAnonymizeText.vue +136 -0
  18. package/components/core/DpAutocomplete.vue +133 -0
  19. package/components/core/DpBulkEditHeader.vue +53 -0
  20. package/components/core/DpButtonIcon.vue +47 -0
  21. package/components/core/DpButtonRow.vue +155 -0
  22. package/components/core/DpCard.vue +54 -0
  23. package/components/core/DpChangeStateAtDate.vue +223 -0
  24. package/components/core/DpCheckboxGroup.vue +93 -0
  25. package/components/core/DpContextualHelp.vue +54 -0
  26. package/components/core/DpCopyPasteButton.vue +47 -0
  27. package/components/core/DpDashboardTaskCard.vue +123 -0
  28. package/components/core/DpDataTable/DataTableSearch.js +44 -0
  29. package/components/core/DpDataTable/DpColumnSelector.vue +133 -0
  30. package/components/core/DpDataTable/DpDataTable.vue +647 -0
  31. package/components/core/DpDataTable/DpDataTableExtended.vue +377 -0
  32. package/components/core/DpDataTable/DpResizeHandle.vue +37 -0
  33. package/components/core/DpDataTable/DpSelectPageItemCount.vue +70 -0
  34. package/components/core/DpDataTable/DpTableHeader.vue +197 -0
  35. package/components/core/DpDataTable/DpTableRow.vue +355 -0
  36. package/components/core/DpDataTable/DpWrapTrigger.vue +48 -0
  37. package/components/core/DpDataTable/lib/ResizableColumns.js +83 -0
  38. package/components/core/DpEditableList.vue +161 -0
  39. package/components/core/DpEditor/DpBoilerPlate.vue +140 -0
  40. package/components/core/DpEditor/DpBoilerPlateModal.vue +166 -0
  41. package/components/core/DpEditor/DpEditor.vue +1281 -0
  42. package/components/core/DpEditor/DpLinkModal.vue +117 -0
  43. package/components/core/DpEditor/DpRecommendationModal/DpInsertableRecommendation.vue +137 -0
  44. package/components/core/DpEditor/DpRecommendationModal.vue +283 -0
  45. package/components/core/DpEditor/DpResizableImage.vue +121 -0
  46. package/components/core/DpEditor/DpUploadModal.vue +121 -0
  47. package/components/core/DpEditor/libs/Decoration.js +66 -0
  48. package/components/core/DpEditor/libs/SegmentRangeChangePlugin.js +35 -0
  49. package/components/core/DpEditor/libs/editorAnonymize.js +66 -0
  50. package/components/core/DpEditor/libs/editorBuildSuggestion.js +269 -0
  51. package/components/core/DpEditor/libs/editorCustomDelete.js +32 -0
  52. package/components/core/DpEditor/libs/editorCustomImage.js +100 -0
  53. package/components/core/DpEditor/libs/editorCustomInsert.js +32 -0
  54. package/components/core/DpEditor/libs/editorCustomLink.js +46 -0
  55. package/components/core/DpEditor/libs/editorCustomMark.js +32 -0
  56. package/components/core/DpEditor/libs/editorInsertAtCursorPos.js +41 -0
  57. package/components/core/DpEditor/libs/editorObscure.js +60 -0
  58. package/components/core/DpEditor/libs/editorUnAnonymize.js +56 -0
  59. package/components/core/DpEditor/libs/handleWordPaste.js +360 -0
  60. package/components/core/DpEditor/libs/preventDrop.js +31 -0
  61. package/components/core/DpEditor/libs/preventKeyboardInput.js +27 -0
  62. package/components/core/DpEditor/libs/preventPaste.js +28 -0
  63. package/components/core/DpFlyout.vue +119 -0
  64. package/components/core/DpInlineNotification.vue +116 -0
  65. package/components/core/DpModal.vue +208 -0
  66. package/components/core/DpObscure.vue +29 -0
  67. package/components/core/DpPager.vue +139 -0
  68. package/components/core/DpProgressBar.vue +67 -0
  69. package/components/core/DpRegisterFlyout.vue +58 -0
  70. package/components/core/DpResettableInput.vue +140 -0
  71. package/components/core/DpSkeletonBox.vue +32 -0
  72. package/components/core/DpSlidebar.vue +86 -0
  73. package/components/core/DpSlidingPagination.vue +45 -0
  74. package/components/core/DpSplitButton.vue +77 -0
  75. package/components/core/DpSwitcher.vue +62 -0
  76. package/components/core/DpTableCardList/DpTableCard.vue +61 -0
  77. package/components/core/DpTableCardList/DpTableCardListHeader.vue +83 -0
  78. package/components/core/DpTabs/DpTab.vue +52 -0
  79. package/components/core/DpTabs/DpTabs.vue +165 -0
  80. package/components/core/DpTextWrapper.vue +65 -0
  81. package/components/core/DpToggleForm.vue +72 -0
  82. package/components/core/DpTooltipIcon.vue +52 -0
  83. package/components/core/DpTransitionExpand.vue +87 -0
  84. package/components/core/DpTreeList/DpTreeList.vue +334 -0
  85. package/components/core/DpTreeList/DpTreeListCheckbox.vue +79 -0
  86. package/components/core/DpTreeList/DpTreeListNode.vue +348 -0
  87. package/components/core/DpTreeList/DpTreeListToggle.vue +71 -0
  88. package/components/core/DpTreeList/utils/constants.js +14 -0
  89. package/components/core/DpUpload/DpUpload.vue +223 -0
  90. package/components/core/DpUpload/DpUploadFiles.vue +269 -0
  91. package/components/core/DpUpload/DpUploadedFile.vue +80 -0
  92. package/components/core/DpUpload/DpUploadedFileList.vue +56 -0
  93. package/components/core/DpUpload/utils/GetFileIdsByHash.js +42 -0
  94. package/components/core/DpUpload/utils/UppyTranslations.js +31 -0
  95. package/components/core/DpVideoPlayer.vue +115 -0
  96. package/components/core/HeightLimit.vue +121 -0
  97. package/components/core/MultistepNav.vue +89 -0
  98. package/components/core/form/DpCheckbox.vue +108 -0
  99. package/components/core/form/DpDateRangePicker.vue +186 -0
  100. package/components/core/form/DpDatepicker.vue +160 -0
  101. package/components/core/form/DpDatetimePicker.vue +194 -0
  102. package/components/core/form/DpFormRow.vue +79 -0
  103. package/components/core/form/DpMultiselect.vue +164 -0
  104. package/components/core/form/DpRadio.vue +128 -0
  105. package/components/core/form/DpSearchField.vue +110 -0
  106. package/components/core/form/DpSelect.vue +149 -0
  107. package/components/core/form/DpTextArea.vue +152 -0
  108. package/components/core/form/DpTimePicker.vue +374 -0
  109. package/components/core/form/DpToggle.vue +78 -0
  110. package/components/core/index.js +132 -0
  111. package/components/core/notify/DpNotifyContainer.vue +122 -0
  112. package/components/core/notify/DpNotifyMessage.vue +95 -0
  113. package/components/core/shared/DpStickyElement.vue +95 -0
  114. package/components/index.js +24 -0
  115. package/components/shared/translations.js +15 -0
  116. package/directives/CleanHtml/CleanHtml.js +50 -0
  117. package/directives/CleanHtml/CleanHtml.stories.mdx +64 -0
  118. package/directives/Tooltip/Tooltip.js +40 -0
  119. package/directives/Tooltip/Tooltip.stories.mdx +42 -0
  120. package/directives/index.js +17 -0
  121. package/lib/index.js +14 -0
  122. package/lib/prefixClass.js +47 -0
  123. package/mixins/index.js +14 -0
  124. package/mixins/prefixClassMixin.js +22 -0
  125. package/package.json +52 -0
  126. package/shared/props.js +86 -0
  127. package/style/index.css +7 -0
  128. package/tailwind.config.js +24 -0
  129. package/tokens/color.json +358 -0
  130. package/tokens/color.stories.mdx +45 -0
  131. package/tokens/fontSize.json +100 -0
  132. package/tokens/space.json +33 -0
  133. package/utils/lengthHint.js +69 -0
@@ -0,0 +1,149 @@
1
+ <license>
2
+ (c) 2010-present DEMOS E-Partizipation GmbH.
3
+
4
+ This file is part of the package @demos-europe/demosplan-ui,
5
+ for more information see the license file.
6
+
7
+ All rights reserved
8
+ </license>
9
+
10
+ <template>
11
+ <div>
12
+ <dp-label
13
+ v-if="label.text"
14
+ v-bind="{
15
+ ...label,
16
+ for: nameOrId,
17
+ required: required
18
+ }" /><!--
19
+ --><select
20
+ :id="nameOrId"
21
+ :required="required"
22
+ :name="name !== '' ? name : false"
23
+ class="o-form__control-select"
24
+ :class="[disabled ? ' bg-color--grey-light-2' : '', classes]"
25
+ :disabled="disabled"
26
+ @change="update">
27
+ <option
28
+ v-if="showPlaceholder"
29
+ data-id="placeholder"
30
+ disabled
31
+ value=""
32
+ :selected="selected === ''">
33
+ {{ Translator.trans(placeholder) }}
34
+ </option>
35
+ <option
36
+ v-for="(option, idx) in options"
37
+ :selected="option.value === selected"
38
+ :value="option.value"
39
+ :key="idx">
40
+ {{ Translator.trans(option.label) }}
41
+ </option>
42
+ </select>
43
+ </div>
44
+ </template>
45
+
46
+ <script>
47
+ import { DpLabel } from 'demosplan-ui/components'
48
+ import { prefixClassMixin } from 'demosplan-ui/mixins'
49
+
50
+ export default {
51
+ name: 'DpSelect',
52
+
53
+ components: {
54
+ DpLabel
55
+ },
56
+
57
+ mixins: [prefixClassMixin],
58
+
59
+ /*
60
+ * Customize the v-model binding names
61
+ * see https://learn.adamwathan.com/advanced-vue/customizing-controlled-component-bindings
62
+ */
63
+ model: {
64
+ prop: 'selected',
65
+ event: 'select'
66
+ },
67
+
68
+ props: {
69
+ classes: {
70
+ type: [Array, String],
71
+ required: false,
72
+ default: ''
73
+ },
74
+
75
+ disabled: {
76
+ type: Boolean,
77
+ required: false,
78
+ default: false
79
+ },
80
+
81
+ id: {
82
+ type: String,
83
+ required: false,
84
+ default: () => ''
85
+ },
86
+
87
+ label: {
88
+ type: Object,
89
+ required: false,
90
+ default: () => ({}),
91
+ validator: (prop) => {
92
+ return Object.keys(prop).every(key => ['bold', 'hint', 'text', 'tooltip'].includes(key))
93
+ }
94
+ },
95
+
96
+ name: {
97
+ type: String,
98
+ required: false,
99
+ default: ''
100
+ },
101
+
102
+ // Need label and value
103
+ options: {
104
+ required: true,
105
+ type: Array
106
+ },
107
+
108
+ placeholder: {
109
+ type: String,
110
+ required: false,
111
+ default: 'warning.select.entry'
112
+ },
113
+
114
+ required: {
115
+ type: Boolean,
116
+ required: false,
117
+ default: false
118
+ },
119
+
120
+ showPlaceholder: {
121
+ type: Boolean,
122
+ required: false,
123
+ default: true
124
+ },
125
+
126
+ selected: {
127
+ type: String,
128
+ required: false,
129
+ default: ''
130
+ }
131
+ },
132
+
133
+ computed: {
134
+ nameOrId () {
135
+ /*
136
+ * As long as there is no necessity of having the id to differ from name,
137
+ * it should not be required to specify it.
138
+ */
139
+ return this.id === '' ? this.name : this.id
140
+ }
141
+ },
142
+
143
+ methods: {
144
+ update (event) {
145
+ this.$emit('select', event.target.value)
146
+ }
147
+ }
148
+ }
149
+ </script>
@@ -0,0 +1,152 @@
1
+ <license>
2
+ (c) 2010-present DEMOS E-Partizipation GmbH.
3
+
4
+ This file is part of the package @demos-europe/demosplan-ui,
5
+ for more information see the license file.
6
+
7
+ All rights reserved
8
+ </license>
9
+
10
+ <template>
11
+ <div :class="{ 'flex flex-column': growToParent }">
12
+ <dp-label
13
+ v-if="label !== ''"
14
+ v-bind="labelProps" /><!--
15
+ --><textarea
16
+ :name="name"
17
+ :id="id"
18
+ class="o-form__control-textarea"
19
+ :class="{ 'flex-grow': growToParent, 'height-60': reducedHeight }"
20
+ :data-dp-validate-if="dataDpValidateIf"
21
+ :disabled="disabled"
22
+ :maxlength="maxlength"
23
+ v-bind="allowedAttributes"
24
+ v-model="currentValue"
25
+ @input="$emit('input', currentValue)"
26
+ :required="required" />
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ import { attributes, length } from 'demosplan-ui/shared/props'
32
+ import { maxlengthHint } from 'demosplan-ui/utils/lengthHint'
33
+
34
+ export default {
35
+ name: 'DpTextArea',
36
+
37
+ components: {
38
+ DpLabel: async () => {
39
+ const { DpLabel } = await import('demosplan-ui/components')
40
+ return DpLabel
41
+ }
42
+ },
43
+
44
+ props: {
45
+ attributes: attributes('textarea'),
46
+
47
+ /**
48
+ * Use to conditionally validate a required textarea field.
49
+ */
50
+ dataDpValidateIf: {
51
+ type: [Boolean, String],
52
+ required: false,
53
+ default: false
54
+ },
55
+
56
+ disabled: {
57
+ type: Boolean,
58
+ required: false,
59
+ default: false
60
+ },
61
+
62
+ /**
63
+ * If enabled, classes are applied to let element grow to 100% height of its parent element.
64
+ */
65
+ growToParent: {
66
+ type: Boolean,
67
+ required: false,
68
+ default: false
69
+ },
70
+
71
+ hint: {
72
+ type: String,
73
+ required: false,
74
+ default: ''
75
+ },
76
+
77
+ id: {
78
+ type: String,
79
+ required: false,
80
+ default: ''
81
+ },
82
+
83
+ label: {
84
+ type: String,
85
+ required: false,
86
+ default: ''
87
+ },
88
+
89
+ maxlength: length,
90
+
91
+ name: {
92
+ type: String,
93
+ required: false,
94
+ default: ''
95
+ },
96
+
97
+ /**
98
+ * Display the form field with reduced height (60px).
99
+ */
100
+ reducedHeight: {
101
+ type: Boolean,
102
+ required: false,
103
+ default: false
104
+ },
105
+
106
+ required: {
107
+ type: Boolean,
108
+ required: false,
109
+ default: false
110
+ },
111
+
112
+ value: {
113
+ type: String,
114
+ required: false,
115
+ default: ''
116
+ }
117
+ },
118
+
119
+ data () {
120
+ return {
121
+ currentValue: this.value
122
+ }
123
+ },
124
+
125
+ computed: {
126
+ allowedAttributes () {
127
+ const attrs = {}
128
+ this.attributes.forEach(attr => {
129
+ attr = attr.split('=')
130
+ attrs[attr[0]] = attr[1]
131
+ })
132
+ return attrs
133
+ },
134
+
135
+ labelProps () {
136
+ return {
137
+ for: this.id,
138
+ hint: this.maxlength ? [this.hint, maxlengthHint(this.currentValue.length, this.maxlength)] : this.hint,
139
+ required: this.required,
140
+ text: this.label,
141
+ tooltip: this.tooltip
142
+ }
143
+ }
144
+ },
145
+
146
+ watch: {
147
+ value () {
148
+ this.currentValue = this.value
149
+ }
150
+ }
151
+ }
152
+ </script>
@@ -0,0 +1,374 @@
1
+ <license>
2
+ (c) 2010-present DEMOS E-Partizipation GmbH.
3
+
4
+ This file is part of the package @demos-europe/demosplan-ui,
5
+ for more information see the license file.
6
+
7
+ All rights reserved
8
+ </license>
9
+
10
+ <template>
11
+ <div
12
+ class="c-timepicker display--inline-block"
13
+ v-click-outside="closeFlyout"
14
+ @keydown.esc="closeFlyout"
15
+ @keydown.enter="e => handleEnter(e)">
16
+ <dp-label
17
+ v-if="label !== ''"
18
+ for="timeInput"
19
+ :text="label" />
20
+ <dp-resettable-input
21
+ v-if="!isMobileDevice"
22
+ :id="`timeInput:${id}`"
23
+ :ref="`timeInput:${id}`"
24
+ class="width-100"
25
+ button-variant="small"
26
+ default-value="00:00"
27
+ :input-attributes="{ disabled: disabled, autocomplete: 'off' }"
28
+ @reset="handleReset"
29
+ @enter="val => handleEnter(val)"
30
+ @focus="handleFocus"
31
+ @blur="handleBlur"
32
+ @input="val => handleInput(val)"
33
+ pattern="([01]?[0-9]|2[0-3]):[0-5][0-9]"
34
+ :value="currentTime" />
35
+ <dp-input
36
+ v-else
37
+ :id="`timeInput:${id}`"
38
+ class="width-100"
39
+ type="time"
40
+ pattern="([01]?[0-9]|2[0-3]):[0-5][0-9]"
41
+ :value="currentTime"
42
+ @input="val => handleInput(val)"
43
+ autocomplete="off" />
44
+
45
+ <div
46
+ ref="flyout"
47
+ v-show="showFlyout"
48
+ class="flex flex-items-start flex-content-evenly c-timepicker__flyout font-size-small"
49
+ tabindex="0">
50
+ <ul class="u-m-0_25 u-mr-0 u-pr-0_25 overflow-y-scroll height-130">
51
+ <li
52
+ v-for="hour in availableHours"
53
+ :key="`${id}:hour:${hour}`"
54
+ class="c-timepicker__flyout-item"
55
+ :class="{'is-selected': currentHour === hour}">
56
+ <button
57
+ @click.prevent="handleInput(hour, 'hour')"
58
+ class="btn--blank u-ph-0_125 u-pv-0_125"
59
+ tabindex="0"
60
+ :value="hour"
61
+ :id="`${id}:hour:${hour}`">
62
+ {{ hour }}
63
+ </button>
64
+ </li>
65
+ </ul>
66
+ <ul
67
+ class="u-m-0_25 height-130"
68
+ :class="minutes.length > 5 ? 'overflow-y-scroll' : ''">
69
+ <li
70
+ v-for="minute in availableMinutes"
71
+ :key="`${id}:minute:${minute}`"
72
+ class="c-timepicker__flyout-item"
73
+ :class="{'is-selected': currentMinutes === minute}">
74
+ <button
75
+ @click.prevent="handleInput(minute, 'minute')"
76
+ tabindex="0"
77
+ class="btn--blank u-ph-0_125 u-pv-0_125"
78
+ :value="minute"
79
+ :id="`${id}:minute:${minute}`">
80
+ {{ minute }}
81
+ </button>
82
+ </li>
83
+ </ul>
84
+ </div>
85
+ </div>
86
+ </template>
87
+
88
+ <script>
89
+ import ClickOutside from 'vue-click-outside'
90
+ import isMobile from 'ismobilejs'
91
+
92
+ const DEFAULT_TIME = '00:00'
93
+
94
+ export default {
95
+ name: 'DpTimePicker',
96
+
97
+ components: {
98
+ DpInput: async () => {
99
+ const { DpInput } = await import('demosplan-ui/components')
100
+ return DpInput
101
+ },
102
+ DpLabel: async () => {
103
+ const { DpLabel } = await import('demosplan-ui/components')
104
+ return DpLabel
105
+ },
106
+ DpResettableInput: () => import('../DpResettableInput')
107
+ },
108
+
109
+ directives: {
110
+ ClickOutside
111
+ },
112
+
113
+ props: {
114
+ disabled: {
115
+ type: Boolean,
116
+ required: false,
117
+ default: false
118
+ },
119
+
120
+ id: {
121
+ type: String,
122
+ required: true
123
+ },
124
+
125
+ label: {
126
+ type: String,
127
+ required: false,
128
+ default: ''
129
+ },
130
+
131
+ minuteSteps: {
132
+ type: Number,
133
+ required: false,
134
+ default: 15
135
+ },
136
+
137
+ /**
138
+ * Minimum allowed value in the format 'hh:mm'
139
+ */
140
+ minValue: {
141
+ type: String,
142
+ required: false,
143
+ default: ''
144
+ },
145
+
146
+ // Expects ISO datetime
147
+ value: {
148
+ type: String,
149
+ required: false,
150
+ default: DEFAULT_TIME
151
+ }
152
+ },
153
+
154
+ data: () => ({
155
+ currentHour: '00',
156
+ currentMinutes: '00',
157
+ isInputFocused: false,
158
+ showFlyout: false
159
+ }),
160
+
161
+ computed: {
162
+ availableHours () {
163
+ if (this.minValue !== '') {
164
+ const values = this.minValue.split(':')
165
+ const minHour = values[0]
166
+ return this.hours.filter(h => this.availableMinutes.length ? h >= minHour : h > minHour)
167
+ }
168
+ return this.hours
169
+ },
170
+
171
+ availableMinutes () {
172
+ if (this.minValue !== '') {
173
+ const values = this.minValue.split(':')
174
+ const minHour = values[0]
175
+ const minMinutes = values[1]
176
+ if (this.currentHour === minHour) {
177
+ return this.minutes.filter(m => m >= minMinutes)
178
+ } else {
179
+ return this.minutes
180
+ }
181
+ }
182
+ return this.minutes
183
+ },
184
+
185
+ currentTime () {
186
+ return `${this.currentHour}:${this.currentMinutes}`
187
+ },
188
+
189
+ hours () {
190
+ let hours = Array.from({ length: 24 }, (v, k) => k)
191
+ hours = hours.map(h => {
192
+ return h.toString().padStart(2, '0')
193
+ })
194
+ return hours
195
+ },
196
+
197
+ isHour () {
198
+ return e => e.target.id.includes('hour')
199
+ },
200
+
201
+ isMinute () {
202
+ return e => e.target.id.includes('minute')
203
+ },
204
+
205
+ isMobileDevice () {
206
+ return isMobile(window.navigator).any
207
+ },
208
+
209
+ inputAttributes () {
210
+ return this.isMobileDevice ? { disabled: this.disabled, autocomplete: 'off', type: 'time' } : { disabled: this.disabled, autocomplete: 'off' }
211
+ },
212
+
213
+ minutes () {
214
+ let minutes = Array.from({ length: 60 }, (v, k) => k)
215
+ minutes = minutes.filter(m => !(m % this.minuteSteps))
216
+ minutes = minutes.map(h => {
217
+ return h.toString().padStart(2, '0')
218
+ })
219
+ return minutes
220
+ }
221
+ },
222
+
223
+ watch: {
224
+ isInputFocused () {
225
+ const input = this.$refs[`timeInput:${this.id}`].$el
226
+ if (this.isInputFocused) {
227
+ input.addEventListener('keydown', this.handleShiftDown)
228
+ } else {
229
+ input.removeEventListener('keydown', this.handleShiftDown)
230
+ }
231
+ },
232
+
233
+ showFlyout () {
234
+ if (this.showFlyout) {
235
+ document.body.addEventListener('keydown', this.handleKeyDown)
236
+ } else {
237
+ document.body.removeEventListener('keydown', this.handleKeyDown)
238
+ }
239
+ },
240
+
241
+ value () {
242
+ this.updateTime(this.value)
243
+ }
244
+ },
245
+
246
+ methods: {
247
+ closeFlyout () {
248
+ if (this.showFlyout) {
249
+ this.toggleFlyout()
250
+ }
251
+ },
252
+
253
+ focusHour () {
254
+ const hourId = `hour:${this.currentHour}`
255
+ const hourEl = document.getElementById(hourId)
256
+ hourEl.focus()
257
+ },
258
+
259
+ handleBlur () {
260
+ if (this.isInputFocused) {
261
+ this.toggleInputFocus()
262
+ }
263
+ },
264
+
265
+ /**
266
+ * If a minValue is set, and the minHour is selected, and the currently selected minutes are not included in the
267
+ * available minutes, we set the selected minutes to the first value from availableMinutes
268
+ * @param hour
269
+ * @param minutes
270
+ * @return {*}
271
+ */
272
+ handleCurrentMinutesUnavailable (hour, minutes) {
273
+ if (this.minValue !== '') {
274
+ const minValues = this.minValue.split(':')
275
+ const minHour = minValues[0]
276
+
277
+ if (hour === minHour && !this.availableMinutes.includes(minutes)) {
278
+ minutes = this.availableMinutes[0]
279
+ }
280
+ }
281
+
282
+ return minutes
283
+ },
284
+
285
+ handleEnter (val) {
286
+ if (val.target) {
287
+ // On enter, there is a pointer event setting the value of the currently active element (why tho), so we only set the complementary value here
288
+ if (this.isHour(val)) {
289
+ this.handleInput(this.currentMinutes, 'minute')
290
+ }
291
+ if (this.isMinute(val)) {
292
+ this.handleInput(this.currentHour, 'hour')
293
+ }
294
+ } else {
295
+ this.handleInput(val)
296
+ }
297
+
298
+ if (this.showFlyout) {
299
+ this.toggleFlyout()
300
+ }
301
+ },
302
+
303
+ handleFocus () {
304
+ if (!this.showFlyout) {
305
+ this.toggleFlyout()
306
+ }
307
+
308
+ if (!this.isInputFocused) {
309
+ this.toggleInputFocus()
310
+ }
311
+ },
312
+
313
+ handleInput (val, type = '') {
314
+ if (type === '') {
315
+ this.updateTime(val)
316
+ }
317
+ if (type === 'hour') {
318
+ this.setHour(val)
319
+ }
320
+ if (type === 'minute') {
321
+ this.setMinutes(val)
322
+ }
323
+ this.$emit('input', this.currentTime)
324
+ },
325
+
326
+ handleKeyDown (e) {
327
+ if (e.key === 'Escape') {
328
+ this.toggleFlyout()
329
+ }
330
+
331
+ if ((e.key === ' ')) {
332
+ e.preventDefault()
333
+ this.handleEnter()
334
+ }
335
+ },
336
+
337
+ handleReset () {
338
+ this.handleInput(DEFAULT_TIME)
339
+ },
340
+
341
+ handleShiftDown (e) {
342
+ if (e.shiftKey && (e.key === 'Down' || e.key === 'ArrowDown')) {
343
+ this.focusHour()
344
+ }
345
+ },
346
+
347
+ setHour (hour) {
348
+ this.currentHour = hour
349
+ },
350
+
351
+ setMinutes (minutes) {
352
+ this.currentMinutes = minutes
353
+ },
354
+
355
+ toggleFlyout () {
356
+ this.showFlyout = !this.showFlyout
357
+ },
358
+
359
+ toggleInputFocus () {
360
+ this.isInputFocused = !this.isInputFocused
361
+ },
362
+
363
+ updateTime (val) {
364
+ const values = val.split(':')
365
+ const hour = values[0]
366
+
367
+ const minutes = this.handleCurrentMinutesUnavailable(hour, values[1])
368
+
369
+ this.setHour(hour)
370
+ this.setMinutes(minutes)
371
+ }
372
+ }
373
+ }
374
+ </script>