@vonage/vivid 3.49.0 → 3.50.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 (197) hide show
  1. package/accordion/index.cjs +1 -1
  2. package/accordion/index.js +1 -1
  3. package/accordion-item/index.cjs +1 -1
  4. package/accordion-item/index.js +1 -1
  5. package/alert/index.cjs +2 -2
  6. package/alert/index.js +2 -2
  7. package/audio-player/index.cjs +1 -1
  8. package/audio-player/index.js +1 -1
  9. package/banner/index.cjs +1 -1
  10. package/banner/index.js +1 -1
  11. package/breadcrumb-item/index.cjs +1 -1
  12. package/breadcrumb-item/index.js +1 -1
  13. package/button/index.cjs +1 -1
  14. package/button/index.js +1 -1
  15. package/card/index.cjs +1 -1
  16. package/card/index.js +1 -1
  17. package/checkbox/index.cjs +1 -2
  18. package/checkbox/index.js +1 -2
  19. package/combobox/index.cjs +3 -3
  20. package/combobox/index.js +3 -3
  21. package/custom-elements.json +797 -124
  22. package/date-picker/index.cjs +4 -3
  23. package/date-picker/index.js +4 -3
  24. package/date-range-picker/index.cjs +3 -2
  25. package/date-range-picker/index.js +3 -2
  26. package/dialog/index.cjs +2 -2
  27. package/dialog/index.js +2 -2
  28. package/elevation/index.cjs +1 -1
  29. package/elevation/index.js +1 -1
  30. package/fab/index.cjs +1 -1
  31. package/fab/index.js +1 -1
  32. package/file-picker/index.cjs +1 -1
  33. package/file-picker/index.js +1 -1
  34. package/focus/index.cjs +1 -1
  35. package/focus/index.js +1 -1
  36. package/header/index.cjs +1 -1
  37. package/header/index.js +1 -1
  38. package/index.cjs +21 -16
  39. package/index.js +10 -8
  40. package/lib/components.d.ts +1 -0
  41. package/lib/time-picker/definition.d.ts +3 -0
  42. package/lib/time-picker/locale.d.ts +12 -0
  43. package/lib/time-picker/time/picker.d.ts +9 -0
  44. package/lib/time-picker/time/presentationTime.d.ts +5 -0
  45. package/lib/time-picker/time/time.d.ts +15 -0
  46. package/lib/time-picker/time-picker.d.ts +16 -0
  47. package/lib/time-picker/time-picker.form-associated.d.ts +10 -0
  48. package/lib/time-picker/time-picker.template.d.ts +4 -0
  49. package/listbox/index.cjs +1 -1
  50. package/listbox/index.js +1 -1
  51. package/locales/en-GB.cjs +15 -0
  52. package/locales/en-GB.js +15 -0
  53. package/locales/en-US.cjs +15 -0
  54. package/locales/en-US.js +15 -0
  55. package/locales/ja-JP.cjs +15 -0
  56. package/locales/ja-JP.js +15 -0
  57. package/locales/zh-CN.cjs +15 -0
  58. package/locales/zh-CN.js +15 -0
  59. package/menu/index.cjs +3 -3
  60. package/menu/index.js +3 -3
  61. package/menu-item/index.cjs +1 -1
  62. package/menu-item/index.js +1 -1
  63. package/nav-disclosure/index.cjs +1 -1
  64. package/nav-disclosure/index.js +1 -1
  65. package/nav-item/index.cjs +1 -1
  66. package/nav-item/index.js +1 -1
  67. package/number-field/index.cjs +1 -1
  68. package/number-field/index.js +1 -1
  69. package/option/index.cjs +1 -1
  70. package/option/index.js +1 -1
  71. package/package.json +1 -1
  72. package/pagination/index.cjs +1 -1
  73. package/pagination/index.js +1 -1
  74. package/popup/index.cjs +3 -3
  75. package/popup/index.js +3 -3
  76. package/radio/index.cjs +1 -2
  77. package/radio/index.js +1 -2
  78. package/select/index.cjs +3 -3
  79. package/select/index.js +3 -3
  80. package/selectable-box/index.cjs +2 -2
  81. package/selectable-box/index.js +2 -2
  82. package/shared/date-picker/date-picker-base.d.ts +2 -2
  83. package/shared/definition.cjs +1 -1
  84. package/shared/definition.js +1 -1
  85. package/shared/definition11.cjs +1 -1
  86. package/shared/definition11.js +1 -1
  87. package/shared/definition14.cjs +1 -1
  88. package/shared/definition14.js +1 -1
  89. package/shared/definition15.cjs +2 -5
  90. package/shared/definition15.js +2 -5
  91. package/shared/definition16.cjs +2 -2
  92. package/shared/definition16.js +2 -2
  93. package/shared/definition18.cjs +2 -2
  94. package/shared/definition18.js +2 -2
  95. package/shared/definition19.cjs +2 -2
  96. package/shared/definition19.js +2 -2
  97. package/shared/definition20.cjs +1 -1
  98. package/shared/definition20.js +1 -1
  99. package/shared/definition23.cjs +1 -1
  100. package/shared/definition23.js +1 -1
  101. package/shared/definition24.cjs +1 -1
  102. package/shared/definition24.js +1 -1
  103. package/shared/definition25.cjs +1 -1
  104. package/shared/definition25.js +1 -1
  105. package/shared/definition28.cjs +1 -1
  106. package/shared/definition28.js +1 -1
  107. package/shared/definition29.cjs +1 -1
  108. package/shared/definition29.js +1 -1
  109. package/shared/definition30.cjs +1 -1
  110. package/shared/definition30.js +1 -1
  111. package/shared/definition31.cjs +1 -1
  112. package/shared/definition31.js +1 -1
  113. package/shared/definition35.cjs +1 -1
  114. package/shared/definition35.js +1 -1
  115. package/shared/definition4.cjs +1 -1
  116. package/shared/definition4.js +1 -1
  117. package/shared/definition40.cjs +3 -6
  118. package/shared/definition40.js +3 -6
  119. package/shared/definition41.cjs +2 -2
  120. package/shared/definition41.js +2 -2
  121. package/shared/definition42.cjs +1 -1
  122. package/shared/definition42.js +1 -1
  123. package/shared/definition44.cjs +1 -1
  124. package/shared/definition44.js +1 -1
  125. package/shared/definition45.cjs +1 -1
  126. package/shared/definition45.js +1 -1
  127. package/shared/definition46.cjs +1 -1
  128. package/shared/definition46.js +1 -1
  129. package/shared/definition48.cjs +1 -1
  130. package/shared/definition48.js +1 -1
  131. package/shared/definition5.cjs +1 -1
  132. package/shared/definition5.js +1 -1
  133. package/shared/definition51.cjs +1 -1
  134. package/shared/definition51.js +1 -1
  135. package/shared/definition52.cjs +1 -1
  136. package/shared/definition52.js +1 -1
  137. package/shared/definition53.cjs +1 -1
  138. package/shared/definition53.js +1 -1
  139. package/shared/definition54.cjs +769 -91
  140. package/shared/definition54.js +768 -90
  141. package/shared/definition55.cjs +76 -58
  142. package/shared/definition55.js +75 -57
  143. package/shared/definition56.cjs +100 -71
  144. package/shared/definition56.js +99 -70
  145. package/shared/definition57.cjs +80 -291
  146. package/shared/definition57.js +81 -292
  147. package/shared/definition58.cjs +303 -13
  148. package/shared/definition58.js +302 -13
  149. package/shared/definition59.cjs +11 -42
  150. package/shared/definition59.js +11 -41
  151. package/shared/definition60.cjs +23 -1782
  152. package/shared/definition60.js +22 -1781
  153. package/shared/definition61.cjs +1810 -0
  154. package/shared/definition61.js +1806 -0
  155. package/shared/definition9.cjs +1 -1
  156. package/shared/definition9.js +1 -1
  157. package/shared/localization/Locale.d.ts +2 -0
  158. package/shared/patterns/index.d.ts +1 -0
  159. package/shared/patterns/trapped-focus.d.ts +4 -0
  160. package/shared/presentationDate.cjs +11 -25
  161. package/shared/presentationDate.js +11 -25
  162. package/shared/trapped-focus.cjs +29 -0
  163. package/shared/trapped-focus.js +27 -0
  164. package/slider/index.cjs +1 -1
  165. package/slider/index.js +1 -1
  166. package/split-button/index.cjs +1 -1
  167. package/split-button/index.js +1 -1
  168. package/style.css +1 -1
  169. package/styles/core/all.css +1 -1
  170. package/styles/core/theme.css +1 -1
  171. package/styles/core/typography.css +1 -1
  172. package/styles/tokens/theme-dark.css +4 -4
  173. package/styles/tokens/theme-light.css +4 -4
  174. package/styles/tokens/vivid-2-compat.css +1 -1
  175. package/switch/index.cjs +1 -1
  176. package/switch/index.js +1 -1
  177. package/tab/index.cjs +1 -1
  178. package/tab/index.js +1 -1
  179. package/tabs/index.cjs +1 -1
  180. package/tabs/index.js +1 -1
  181. package/tag/index.cjs +1 -1
  182. package/tag/index.js +1 -1
  183. package/text-area/index.cjs +1 -1
  184. package/text-area/index.js +1 -1
  185. package/text-field/index.cjs +1 -1
  186. package/text-field/index.js +1 -1
  187. package/time-picker/index.cjs +38 -0
  188. package/time-picker/index.js +36 -0
  189. package/toggletip/index.cjs +4 -4
  190. package/toggletip/index.js +4 -4
  191. package/tooltip/index.cjs +4 -4
  192. package/tooltip/index.js +4 -4
  193. package/tree-item/index.cjs +2 -2
  194. package/tree-item/index.js +2 -2
  195. package/tree-view/index.cjs +1 -1
  196. package/tree-view/index.js +1 -1
  197. package/vivid.api.json +105 -0
@@ -1,10 +1,215 @@
1
- import { a as attr, F as FoundationElement, D as DOM, h as html, r as registerFactory } from './index.js';
2
- import { P as Popup, p as popupRegistries } from './definition60.js';
3
- import { a as anchored } from './anchored.js';
1
+ import { F as FoundationElement, a as attr, D as DOM, n as nullableNumberConverter, o as observable, h as html, r as registerFactory } from './index.js';
2
+ import { T as TextField, a as textFieldRegistries } from './definition53.js';
3
+ import { P as Popup, p as popupRegistries } from './definition61.js';
4
+ import { B as Button, a as buttonRegistries } from './definition11.js';
5
+ import './affix.js';
6
+ import { e as errorText, f as formElements, a as FormElementHelperText } from './index2.js';
7
+ import { L as Localized } from './localized.js';
8
+ import { T as TrappedFocus } from './trapped-focus.js';
9
+ import { F as FormAssociated } from './form-associated.js';
10
+ import { a as applyMixins } from './apply-mixins.js';
11
+ import { r as ref } from './ref.js';
4
12
  import { w as when } from './when.js';
13
+ import { r as repeat } from './repeat.js';
5
14
  import { c as classNames } from './class-names.js';
6
15
 
7
- const styles = ".control{display:inline-block}.content-wrapper{width:var(--toggletip-inline-size, auto);padding:16px}.headline{font:var(--vvd-typography-base-bold)}.action-items{display:flex;justify-content:flex-end;gap:10px}::slotted([slot=action-items]){margin-block-start:16px}\n";
16
+ const styles = ":host{display:inline-block}.base,.control{inline-size:100%}.dialog{display:inline-flex;flex-direction:column}.dialog .footer{display:flex;align-items:center;justify-content:flex-end;padding:8px;border-top:1px solid var(--vvd-color-neutral-200);gap:8px}.time-pickers{display:flex;overflow:hidden;padding:4px;block-size:188px;gap:4px}.time-pickers .picker{position:relative;display:flex;flex-direction:column;padding:0;margin:0;border-radius:4px;gap:4px;inline-size:50px;list-style:none;overflow-x:hidden;overflow-y:hidden;scrollbar-width:thin}.time-pickers .picker:hover{overflow-y:auto}.time-pickers .picker:after{display:block;flex-shrink:0;block-size:156px;content:\"\"}.time-pickers .picker:focus-visible{--focus-stroke-gap-color: transparent;--focus-inset: -2px;box-shadow:inset 0 0 0 3px var(--focus-stroke-gap-color, currentColor);outline:2px solid var(--focus-stroke-color, var(--vvd-color-canvas-text));outline-offset:calc(-2px - var(--focus-inset, 0px))}.time-pickers .item{display:flex;flex-shrink:0;align-items:center;justify-content:center;background-color:var(--_appearance-color-fill);block-size:28px;border-radius:4px;color:var(--_appearance-color-text);cursor:pointer;font:var(--vvd-typography-base);inline-size:50px}.time-pickers .item{--_connotation-color-primary: var(--vvd-time-picker-accent-primary, var(--vvd-color-canvas-text));--_connotation-color-faint: var(--vvd-time-picker-accent-faint, var(--vvd-color-neutral-50));--_connotation-color-soft: var(--vvd-time-picker-accent-soft, var(--vvd-color-neutral-100));--_connotation-color-dim: var(--vvd-time-picker-accent-dim, var(--vvd-color-neutral-200));--_connotation-color-pale: var(--vvd-time-picker-accent-pale, var(--vvd-color-neutral-300))}.time-pickers .item{--_appearance-color-text: var(--_connotation-color-primary);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.time-pickers .item:where(:hover,.hover):where(:not(:disabled,.disabled,.readonly)){--_appearance-color-text: var(--_connotation-color-primary);--_appearance-color-fill: var(--_connotation-color-faint);--_appearance-color-outline: transparent}.time-pickers .item:where(:active,.active):where(:not(:disabled,.disabled)){--_appearance-color-text: var(--_connotation-color-primary);--_appearance-color-fill: var(--_connotation-color-soft);--_appearance-color-outline: transparent}.time-pickers .item:where(.selected,[aria-current]):where(:not(:disabled,.disabled)){--_appearance-color-text: var(--_connotation-color-primary);--_appearance-color-fill: var(--_connotation-color-dim);--_appearance-color-outline: transparent}.time-pickers .item:where(.selected,[aria-current]):where(:hover,.hover):where(:not(:disabled,.disabled,.readonly)){--_appearance-color-text: var(--_connotation-color-primary);--_appearance-color-fill: var(--_connotation-color-pale);--_appearance-color-outline: transparent}.time-pickers .item:where(.selected,[aria-current]):where(:disabled,.disabled){--_appearance-color-text: var(--vvd-color-neutral-300);--_appearance-color-fill: var(--vvd-color-neutral-200);--_appearance-color-outline: transparent}\n";
17
+
18
+ class _TimePicker extends FoundationElement {
19
+ }
20
+ class FormAssociatedTimePicker extends FormAssociated(_TimePicker) {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.proxy = document.createElement("input");
24
+ }
25
+ }
26
+
27
+ const isValidTimeStr = (timeStr) => {
28
+ const parts = timeStr.split(":");
29
+ if (parts.length !== 3) {
30
+ return false;
31
+ }
32
+ const [hours, minutes, seconds] = parts;
33
+ if (hours.length !== 2 || minutes.length !== 2 || seconds.length !== 2) {
34
+ return false;
35
+ }
36
+ const hoursNum = parseInt(hours, 10);
37
+ const minutesNum = parseInt(minutes, 10);
38
+ const secondsNum = parseInt(seconds, 10);
39
+ if (isNaN(hoursNum) || isNaN(minutesNum) || isNaN(secondsNum)) {
40
+ return false;
41
+ }
42
+ if (hoursNum < 0 || hoursNum > 23) {
43
+ return false;
44
+ }
45
+ if (minutesNum < 0 || minutesNum > 59) {
46
+ return false;
47
+ }
48
+ if (secondsNum < 0 || secondsNum > 59) {
49
+ return false;
50
+ }
51
+ return true;
52
+ };
53
+ const parseTimePart = (partStr) => Number.parseInt(partStr, 10);
54
+ const formatTimePart = (part) => part.toString().padStart(2, "0");
55
+ const parseTimeStr = (timeStr) => {
56
+ const [hoursStr, minutesStr, secondsStr] = timeStr.split(":");
57
+ const hours = parseTimePart(hoursStr);
58
+ const minutes = parseTimePart(minutesStr);
59
+ const seconds = parseTimePart(secondsStr);
60
+ return {
61
+ hourStr: hoursStr,
62
+ hours,
63
+ minuteStr: minutesStr,
64
+ minutes,
65
+ secondStr: secondsStr,
66
+ seconds,
67
+ meridiem: hours < 12 ? "AM" : "PM"
68
+ };
69
+ };
70
+ const hoursAs12hClock = (hour) => hour % 12 || 12;
71
+
72
+ const formatPresentationTime = (timeStr, includeSeconds, use12HourClock) => {
73
+ const time = parseTimeStr(timeStr);
74
+ const hoursStr = formatTimePart(use12HourClock ? hoursAs12hClock(time.hours) : time.hours);
75
+ let result = `${hoursStr}:${time.minuteStr}`;
76
+ if (includeSeconds) {
77
+ result += `:${time.secondStr}`;
78
+ }
79
+ if (use12HourClock) {
80
+ result += ` ${time.meridiem}`;
81
+ }
82
+ return result;
83
+ };
84
+ const isDigit = (char) => char >= "0" && char <= "9";
85
+ const parsePresentationTime = (input, use12HourClock) => {
86
+ const cleanedInput = input.toLowerCase();
87
+ const numerals = [];
88
+ let meridiem;
89
+ for (let i = 0; i < cleanedInput.length; i++) {
90
+ const char = cleanedInput[i];
91
+ if (char === "a" && cleanedInput[i + 1] === "m") {
92
+ i++;
93
+ meridiem = "AM";
94
+ }
95
+ if (char === "p" && cleanedInput[i + 1] === "m") {
96
+ i++;
97
+ meridiem = "PM";
98
+ }
99
+ if (isDigit(char)) {
100
+ let numeral = char;
101
+ while (isDigit(cleanedInput[i + 1])) {
102
+ i++;
103
+ numeral += cleanedInput[i];
104
+ }
105
+ numerals.push(Number.parseInt(numeral, 10));
106
+ }
107
+ }
108
+ if (numerals.length === 0 || numerals.length > 3) {
109
+ throw new Error("Invalid time format");
110
+ }
111
+ if (meridiem && (numerals[0] < 1 || numerals[0] > 12)) {
112
+ throw new Error("Invalid time format");
113
+ }
114
+ if (meridiem || use12HourClock) {
115
+ if (numerals[0] === 12) {
116
+ numerals[0] = 0;
117
+ }
118
+ }
119
+ if (meridiem === "PM") {
120
+ numerals[0] = numerals[0] + 12;
121
+ }
122
+ const [hours, minutes = 0, seconds = 0] = numerals;
123
+ if (hours > 23 || minutes > 59 || seconds > 59) {
124
+ throw new Error("Invalid value");
125
+ }
126
+ return [hours, minutes, seconds].map(formatTimePart).join(":");
127
+ };
128
+
129
+ const fallsIntoMeridiem = (meridiem, hour) => meridiem === "AM" && hour < 12 || meridiem === "PM" && hour >= 12;
130
+ const getHoursOptions = (min, max, forMeridiem) => {
131
+ const result = [];
132
+ const minHour = min ? parseTimeStr(min).hours : 0;
133
+ const maxHour = max ? parseTimeStr(max).hours : 23;
134
+ for (let i = minHour; i <= maxHour; i++) {
135
+ if (forMeridiem && !fallsIntoMeridiem(forMeridiem, i)) {
136
+ continue;
137
+ }
138
+ result.push({
139
+ value: formatTimePart(i),
140
+ label: formatTimePart(forMeridiem ? hoursAs12hClock(i) : i)
141
+ });
142
+ }
143
+ return result;
144
+ };
145
+ const getMinutesOptions = (step, value, min, max) => {
146
+ const result = [];
147
+ let minMinute = 0;
148
+ let maxMinute = 59;
149
+ if (min) {
150
+ const { hourStr: minHourStr, minutes: minMinutes } = parseTimeStr(min);
151
+ if (value && parseTimeStr(value).hourStr === minHourStr) {
152
+ minMinute = minMinutes;
153
+ }
154
+ }
155
+ if (max) {
156
+ const { hourStr: maxHourStr, minutes: maxMinutes } = parseTimeStr(max);
157
+ if (value && parseTimeStr(value).hourStr === maxHourStr) {
158
+ maxMinute = maxMinutes;
159
+ }
160
+ }
161
+ for (let i = minMinute; i <= maxMinute; i += Math.max(1, step ?? 1)) {
162
+ const minutes = formatTimePart(i);
163
+ result.push({
164
+ value: minutes,
165
+ label: minutes
166
+ });
167
+ }
168
+ return result;
169
+ };
170
+ const getSecondsOptions = (step, value, min, max) => {
171
+ const result = [];
172
+ let minSecond = 0;
173
+ let maxSecond = 59;
174
+ if (min) {
175
+ const minTime = parseTimeStr(min);
176
+ if (value && (parseTimeStr(value).hourStr === minTime.hourStr && parseTimeStr(value).minuteStr) === minTime.minuteStr) {
177
+ minSecond = minTime.seconds;
178
+ }
179
+ }
180
+ if (max) {
181
+ const maxTime = parseTimeStr(max);
182
+ if (value && (parseTimeStr(value).hourStr === maxTime.hourStr && parseTimeStr(value).minuteStr) === maxTime.minuteStr) {
183
+ maxSecond = maxTime.seconds;
184
+ }
185
+ }
186
+ for (let i = minSecond; i <= maxSecond; i += Math.max(1, step)) {
187
+ const seconds = formatTimePart(i);
188
+ result.push({
189
+ value: seconds,
190
+ label: seconds
191
+ });
192
+ }
193
+ return result;
194
+ };
195
+ const getMeridiesOptions = (min, max) => {
196
+ const result = [];
197
+ const hideAM = min ? parseTimeStr(min).meridiem === "PM" : false;
198
+ if (!hideAM) {
199
+ result.push({
200
+ value: "AM",
201
+ label: "AM"
202
+ });
203
+ }
204
+ const hidePM = max ? parseTimeStr(max).meridiem === "AM" : false;
205
+ if (!hidePM) {
206
+ result.push({
207
+ value: "PM",
208
+ label: "PM"
209
+ });
210
+ }
211
+ return result;
212
+ };
8
213
 
9
214
  var __defProp = Object.defineProperty;
10
215
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -17,126 +222,599 @@ var __decorateClass = (decorators, target, key, kind) => {
17
222
  __defProp(target, key, result);
18
223
  return result;
19
224
  };
20
- let Toggletip = class extends FoundationElement {
225
+ const ValidTimeFilter = {
226
+ fromView: (value) => {
227
+ if (value && isValidTimeStr(value)) {
228
+ return value;
229
+ }
230
+ return "";
231
+ },
232
+ toView(value) {
233
+ return value;
234
+ }
235
+ };
236
+ let TimePicker = class extends FormAssociatedTimePicker {
21
237
  constructor() {
22
- super(...arguments);
23
- this.#ANCHOR_ARIA_LABEL_SUFFIX = " ; Show more information";
24
- this.alternate = false;
25
- this.placement = "right";
26
- this.open = false;
27
- this.#openIfClosed = () => {
28
- if (!this.open)
29
- DOM.queueUpdate(() => this.open = true);
238
+ super();
239
+ this.readOnly = false;
240
+ this.minutesStep = null;
241
+ this.secondsStep = null;
242
+ this.#getFocusableEls = () => this.shadowRoot.querySelectorAll(`
243
+ .dialog [tabindex="0"],
244
+ .dialog .vwc-button:not(:disabled)
245
+ `);
246
+ this.#onFocusIn = () => {
247
+ this.$emit("focus", void 0, { bubbles: false });
30
248
  };
31
- this.#closeOnClickOutside = (e) => {
32
- if (!this.contains(e.target))
33
- this.open = false;
249
+ this.#onFocusOut = () => {
250
+ this.$emit("blur", void 0, { bubbles: false });
34
251
  };
35
- this.#closeOnEscape = (e) => {
36
- if (e.key === "Escape")
37
- this.open = false;
252
+ this._popupOpen = false;
253
+ this.#dismissOnClickOutside = (event) => {
254
+ if (!this._popupOpen) {
255
+ return;
256
+ }
257
+ const path = event.composedPath();
258
+ const elementsToIgnoreClicksOn = [this._dialogEl, this._clockButtonEl];
259
+ if (!elementsToIgnoreClicksOn.some((element) => path.includes(element))) {
260
+ this._closePopup(false);
261
+ }
38
262
  };
263
+ this._presentationValue = "";
264
+ this.value = "";
265
+ this.min = "";
266
+ this.max = "";
267
+ this.proxy.type = "time";
268
+ this.proxy.step = "1";
39
269
  }
40
- #ANCHOR_ARIA_LABEL_SUFFIX;
41
- openChanged(oldValue, newValue) {
42
- if (oldValue === void 0)
43
- return;
44
- if (newValue) {
45
- this.setAttribute("role", "status");
46
- } else {
47
- this.removeAttribute("role");
270
+ /**
271
+ * @internal
272
+ */
273
+ readOnlyChanged() {
274
+ if (this.proxy instanceof HTMLInputElement) {
275
+ this.proxy.readOnly = this.readOnly;
276
+ this.validate();
277
+ }
278
+ }
279
+ /**
280
+ * @internal
281
+ */
282
+ minChanged(_, newMin) {
283
+ if (this.proxy instanceof HTMLInputElement) {
284
+ this.proxy.min = newMin;
285
+ this.validate();
286
+ }
287
+ }
288
+ /**
289
+ * @internal
290
+ */
291
+ maxChanged(_, newMax) {
292
+ if (this.proxy instanceof HTMLInputElement) {
293
+ this.proxy.max = newMax;
294
+ this.validate();
48
295
  }
49
- this.#updateListeners();
50
- if (this._anchorEl) {
51
- this.#updateAnchor(this._anchorEl);
296
+ }
297
+ // --- Core ---
298
+ /**
299
+ * @internal
300
+ */
301
+ get _displaySeconds() {
302
+ return this.secondsStep !== null;
303
+ }
304
+ /**
305
+ * @internal
306
+ */
307
+ get _use12hClock() {
308
+ return this.clock ? this.clock === "12h" : this.locale.timePicker.defaultTo12HourClock;
309
+ }
310
+ #getFocusableEls;
311
+ /**
312
+ * @internal
313
+ */
314
+ valueChanged(previous, next) {
315
+ super.valueChanged(previous, next);
316
+ if (this.value) {
317
+ if (!isValidTimeStr(this.value)) {
318
+ this.value = "";
319
+ return;
320
+ }
321
+ this._presentationValue = formatPresentationTime(
322
+ this.value,
323
+ this._displaySeconds,
324
+ this._use12hClock
325
+ );
326
+ } else {
327
+ this._presentationValue = "";
52
328
  }
53
329
  }
330
+ #updateValueDueToUserInteraction(newValue) {
331
+ this.value = newValue;
332
+ this.$emit("change");
333
+ this.$emit("input");
334
+ }
54
335
  connectedCallback() {
55
336
  super.connectedCallback();
56
- this.#updateListeners();
337
+ document.addEventListener("click", this.#dismissOnClickOutside);
338
+ this.addEventListener("focusin", this.#onFocusIn);
339
+ this.addEventListener("focusout", this.#onFocusOut);
57
340
  }
58
341
  disconnectedCallback() {
59
342
  super.disconnectedCallback();
60
- this.#updateListeners();
343
+ document.removeEventListener("click", this.#dismissOnClickOutside);
344
+ this.removeEventListener("focusin", this.#onFocusIn);
345
+ this.removeEventListener("focusout", this.#onFocusOut);
346
+ }
347
+ #onFocusIn;
348
+ #onFocusOut;
349
+ #dismissOnClickOutside;
350
+ #openPopupIfPossible() {
351
+ if (!this.readOnly) {
352
+ this._popupOpen = true;
353
+ }
354
+ }
355
+ /**
356
+ * @internal
357
+ */
358
+ _closePopup(restoreFocusToTextField = true) {
359
+ this._popupOpen = false;
360
+ if (restoreFocusToTextField) {
361
+ this._textFieldEl.focus();
362
+ }
363
+ }
364
+ /**
365
+ * On keydown anywhere in the time picker.
366
+ * @internal
367
+ */
368
+ _onBaseKeyDown(event) {
369
+ if (event.key === "Escape") {
370
+ this._closePopup();
371
+ return false;
372
+ }
373
+ if (this._trappedFocus(event, this.#getFocusableEls)) {
374
+ return false;
375
+ }
376
+ return true;
377
+ }
378
+ /**
379
+ * @internal
380
+ */
381
+ _presentationValueChanged() {
382
+ this.dirtyValue = true;
383
+ this.validate();
384
+ }
385
+ /**
386
+ * @internal
387
+ */
388
+ get _textFieldPlaceholder() {
389
+ let format = "hh:mm";
390
+ if (this._displaySeconds) {
391
+ format += ":ss";
392
+ }
393
+ if (this._use12hClock) {
394
+ format += " aa";
395
+ }
396
+ return format;
397
+ }
398
+ /**
399
+ * @internal
400
+ */
401
+ _onTextFieldInput(event) {
402
+ const textField = event.currentTarget;
403
+ this._presentationValue = textField.value;
404
+ }
405
+ /**
406
+ * @internal
407
+ */
408
+ _onTextFieldChange() {
409
+ if (this._presentationValue === "") {
410
+ this.#updateValueDueToUserInteraction("");
411
+ return;
412
+ }
413
+ try {
414
+ this.#updateValueDueToUserInteraction(
415
+ parsePresentationTime(this._presentationValue, this._use12hClock)
416
+ );
417
+ } catch (_) {
418
+ return;
419
+ }
61
420
  }
421
+ // --- Clock button ---
62
422
  /**
63
423
  * @internal
64
424
  */
65
- _anchorElChanged(oldValue, newValue) {
66
- if (oldValue)
67
- this.#cleanupAnchor(oldValue);
68
- if (newValue)
69
- this.#setupAnchor(newValue);
425
+ get _clockButtonLabel() {
426
+ if (this.value) {
427
+ return this.locale.timePicker.changeTimeLabel(formatPresentationTime(this.value, this._displaySeconds, this._use12hClock));
428
+ }
429
+ return this.locale.timePicker.chooseTimeLabel;
70
430
  }
71
- #setupAnchor(a) {
72
- a.addEventListener("click", this.#openIfClosed, true);
73
- a.ariaLabel = (a.ariaLabel ?? "") + this.#ANCHOR_ARIA_LABEL_SUFFIX;
74
- this.#updateAnchor(a);
431
+ /**
432
+ * @internal
433
+ */
434
+ _onClockButtonClick() {
435
+ if (this._popupOpen) {
436
+ this._closePopup();
437
+ } else {
438
+ this.#openPopupIfPossible();
439
+ DOM.processUpdates();
440
+ if (this._selectedHour) {
441
+ this.#scrollToItem("hours", this._selectedHour, "start");
442
+ }
443
+ if (this._selectedMinute) {
444
+ this.#scrollToItem("minutes", this._selectedMinute, "start");
445
+ }
446
+ if (this._displaySeconds && this._selectedSecond) {
447
+ this.#scrollToItem("seconds", this._selectedSecond, "start");
448
+ }
449
+ if (this._use12hClock && this._selectedMeridiem) {
450
+ this.#scrollToItem("meridies", this._selectedMeridiem, "start");
451
+ }
452
+ this.#getFocusableEls()[0].focus();
453
+ }
75
454
  }
76
- #updateAnchor(a) {
77
- a.setAttribute("aria-expanded", this.open.toString());
455
+ // --- Pickers ---
456
+ /**
457
+ * @internal
458
+ */
459
+ get _hours() {
460
+ return getHoursOptions(this.min, this.max, this._use12hClock ? this._selectedMeridiem ?? this._meridies[0].value : void 0);
78
461
  }
79
- #cleanupAnchor(a) {
80
- a.removeEventListener("click", this.#openIfClosed, true);
81
- if (a.ariaLabel)
82
- a.ariaLabel = a.ariaLabel.replace(this.#ANCHOR_ARIA_LABEL_SUFFIX, "");
462
+ /**
463
+ * @internal
464
+ */
465
+ get _selectedHour() {
466
+ return this.value ? parseTimeStr(this.value).hourStr : void 0;
83
467
  }
84
- #openIfClosed;
85
- #updateListeners() {
86
- document.removeEventListener("click", this.#closeOnClickOutside);
87
- document.removeEventListener("keydown", this.#closeOnEscape);
88
- if (this.open && this.isConnected) {
89
- document.addEventListener("click", this.#closeOnClickOutside);
90
- document.addEventListener("keydown", this.#closeOnEscape);
468
+ /**
469
+ * @internal
470
+ */
471
+ set _selectedHour(value) {
472
+ if (this.value) {
473
+ const { minuteStr, secondStr } = parseTimeStr(this.value);
474
+ this.value = `${value}:${minuteStr}:${secondStr}`;
475
+ } else {
476
+ this.value = `${value}:00:00`;
477
+ }
478
+ }
479
+ /**
480
+ * @internal
481
+ */
482
+ get _minutes() {
483
+ return getMinutesOptions(this.minutesStep, this.value, this.min, this.max);
484
+ }
485
+ /**
486
+ * @internal
487
+ */
488
+ get _selectedMinute() {
489
+ return this.value ? parseTimeStr(this.value).minuteStr : void 0;
490
+ }
491
+ /**
492
+ * @internal
493
+ */
494
+ set _selectedMinute(value) {
495
+ if (this.value) {
496
+ const { hourStr, secondStr } = parseTimeStr(this.value);
497
+ this.value = `${hourStr}:${value}:${secondStr}`;
498
+ } else {
499
+ this.value = `00:${value}:00`;
500
+ }
501
+ }
502
+ /**
503
+ * @internal
504
+ */
505
+ get _seconds() {
506
+ return getSecondsOptions(this.secondsStep, this.value, this.min, this.max);
507
+ }
508
+ /**
509
+ * @internal
510
+ */
511
+ get _selectedSecond() {
512
+ return this.value ? parseTimeStr(this.value).secondStr : void 0;
513
+ }
514
+ /**
515
+ * @internal
516
+ */
517
+ set _selectedSecond(value) {
518
+ if (this.value) {
519
+ const { hourStr, minuteStr } = parseTimeStr(this.value);
520
+ this.value = `${hourStr}:${minuteStr}:${value}`;
521
+ } else {
522
+ this.value = `00:00:${value}`;
523
+ }
524
+ }
525
+ /**
526
+ * @internal
527
+ */
528
+ get _meridies() {
529
+ return getMeridiesOptions(this.min, this.max);
530
+ }
531
+ /**
532
+ * @internal
533
+ */
534
+ get _selectedMeridiem() {
535
+ return this.value ? parseTimeStr(this.value).meridiem : void 0;
536
+ }
537
+ /**
538
+ * @internal
539
+ */
540
+ set _selectedMeridiem(value) {
541
+ if (this.value) {
542
+ const { hours, minuteStr, secondStr } = parseTimeStr(this.value);
543
+ let adjustedHours = hours;
544
+ if (value === "AM" && hours >= 12) {
545
+ adjustedHours -= 12;
546
+ } else if (value === "PM" && hours < 12) {
547
+ adjustedHours += 12;
548
+ }
549
+ this.value = `${formatTimePart(adjustedHours)}:${minuteStr}:${secondStr}`;
550
+ } else {
551
+ if (value === "AM") {
552
+ this.value = "00:00:00";
553
+ } else {
554
+ this.value = "12:00:00";
555
+ }
556
+ }
557
+ }
558
+ /**
559
+ * @internal
560
+ */
561
+ _onPickerKeyDown(picker, options, selectedValue, setSelectedValue, event) {
562
+ const offset = {
563
+ ArrowUp: -1,
564
+ ArrowDown: 1
565
+ }[event.key];
566
+ if (offset) {
567
+ event.preventDefault();
568
+ const index = options.findIndex((h) => h.value === selectedValue);
569
+ const newRawIndex = index === -1 ? 0 : index + offset;
570
+ const newIndex = (newRawIndex + options.length) % options.length;
571
+ const newValue = options[newIndex].value;
572
+ setSelectedValue(newValue);
573
+ this.#scrollToItem(picker, newValue, "nearest");
574
+ this.#updateValueDueToUserInteraction(this.value);
575
+ }
576
+ return true;
577
+ }
578
+ #scrollToItem(picker, selectedValue, position) {
579
+ const element = this.shadowRoot.querySelector(`#${picker}-${selectedValue}`);
580
+ if (!element) {
581
+ return;
582
+ }
583
+ const parent = element.parentElement;
584
+ switch (position) {
585
+ case "start":
586
+ parent.scrollTop = element.offsetTop;
587
+ break;
588
+ case "nearest":
589
+ if (element.offsetTop < parent.scrollTop) {
590
+ parent.scrollTop = element.offsetTop;
591
+ }
592
+ if (element.offsetTop + element.offsetHeight > parent.scrollTop + parent.offsetHeight) {
593
+ parent.scrollTop = element.offsetTop + element.offsetHeight - parent.offsetHeight;
594
+ }
595
+ break;
596
+ }
597
+ }
598
+ /**
599
+ * @internal
600
+ */
601
+ _onPickerItemClick(picker, setSelectedValue, value) {
602
+ setSelectedValue(value);
603
+ this.#scrollToItem(picker, value, "start");
604
+ this.#updateValueDueToUserInteraction(this.value);
605
+ const nextPickerEl = this.shadowRoot.querySelector(`#${picker} + .picker`);
606
+ if (nextPickerEl) {
607
+ nextPickerEl.focus();
608
+ } else {
609
+ this._closePopup();
610
+ }
611
+ }
612
+ // --- Dialog footer ---
613
+ /**
614
+ * @internal
615
+ */
616
+ _onOkClick() {
617
+ this._closePopup();
618
+ }
619
+ /**
620
+ * @internal
621
+ */
622
+ _onClearClick() {
623
+ this.#updateValueDueToUserInteraction("");
624
+ this._closePopup();
625
+ }
626
+ // --- Validation ---
627
+ /**
628
+ * @internal
629
+ */
630
+ validate() {
631
+ if (this.proxy) {
632
+ this.proxy.setCustomValidity(this._getCustomValidationError() ?? "");
633
+ }
634
+ super.validate(this._textFieldEl?.querySelector("input") ?? void 0);
635
+ }
636
+ /**
637
+ * @internal
638
+ */
639
+ _getCustomValidationError() {
640
+ if (this._isPresentationValueInvalid()) {
641
+ return this.locale.timePicker.invalidTimeError;
642
+ }
643
+ return null;
644
+ }
645
+ /**
646
+ * @internal
647
+ */
648
+ _isPresentationValueInvalid() {
649
+ if (this._presentationValue === "") {
650
+ return false;
651
+ }
652
+ try {
653
+ parsePresentationTime(this._presentationValue, this._use12hClock);
654
+ return false;
655
+ } catch (_) {
656
+ return true;
91
657
  }
92
658
  }
93
- #closeOnClickOutside;
94
- #closeOnEscape;
95
659
  };
660
+ __decorateClass([
661
+ attr({ attribute: "readonly", mode: "boolean" })
662
+ ], TimePicker.prototype, "readOnly", 2);
663
+ __decorateClass([
664
+ attr({ attribute: "minutes-step", converter: nullableNumberConverter })
665
+ ], TimePicker.prototype, "minutesStep", 2);
666
+ __decorateClass([
667
+ attr({ attribute: "seconds-step", converter: nullableNumberConverter })
668
+ ], TimePicker.prototype, "secondsStep", 2);
96
669
  __decorateClass([
97
670
  attr
98
- ], Toggletip.prototype, "headline", 2);
671
+ ], TimePicker.prototype, "clock", 2);
672
+ __decorateClass([
673
+ attr({ converter: ValidTimeFilter })
674
+ ], TimePicker.prototype, "min", 2);
99
675
  __decorateClass([
100
- attr({ mode: "boolean" })
101
- ], Toggletip.prototype, "alternate", 2);
676
+ attr({ converter: ValidTimeFilter })
677
+ ], TimePicker.prototype, "max", 2);
102
678
  __decorateClass([
103
- attr({ mode: "fromView" })
104
- ], Toggletip.prototype, "placement", 2);
679
+ observable
680
+ ], TimePicker.prototype, "_popupOpen", 2);
105
681
  __decorateClass([
106
- attr({ mode: "boolean" })
107
- ], Toggletip.prototype, "open", 2);
108
- Toggletip = __decorateClass([
109
- anchored
110
- ], Toggletip);
682
+ observable
683
+ ], TimePicker.prototype, "_presentationValue", 2);
684
+ TimePicker = __decorateClass([
685
+ errorText,
686
+ formElements
687
+ ], TimePicker);
688
+ applyMixins(TimePicker, Localized, FormElementHelperText, TrappedFocus);
111
689
 
112
- const getClasses = (_) => classNames("control");
113
- const ToggletipTemplate = (context) => {
114
- const popup = context.tagFor(Popup);
690
+ const renderPicker = (id, getLabel, getSelected, setSelected, getOptions) => {
115
691
  return html`
116
- <${popup}
117
- class="${getClasses}"
118
- arrow
119
- :anchor="${(x) => x._anchorEl}"
120
- :open="${(x) => x.open}"
121
- ?alternate="${(x) => !x.alternate}"
122
- placement="${(x) => x.placement}"
123
- exportparts="vvd-theme-alternate"
692
+ <ul
693
+ id="${id}"
694
+ class="picker"
695
+ role="listbox"
696
+ tabindex="0"
697
+ aria-label="${getLabel}"
698
+ aria-activedescendant="${(x) => getSelected(x) ? `${id}-${getSelected(x)}` : void 0}"
699
+ @keydown="${(x, c) => x._onPickerKeyDown(id, getOptions(x), getSelected(x), setSelected(x), c.event)}"
124
700
  >
125
- <div class="content-wrapper">
126
- ${when((x) => x.headline, html`<header class="headline">${(x) => x.headline}</header>`)}
127
- <slot></slot>
128
- <footer class="action-items"><slot name="action-items"></slot></footer>
129
- </div>
130
- </${popup}>
701
+ ${repeat(
702
+ getOptions,
703
+ html`
704
+ <li
705
+ id="${(x) => `${id}-${x.value}`}"
706
+ class="${(x, c) => classNames("item", [
707
+ "selected",
708
+ getSelected(c.parent) === x.value
709
+ ])}"
710
+ role="option"
711
+ aria-selected="${(x, c) => getSelected(c.parent) === x.value}"
712
+ @click="${(x, c) => c.parent._onPickerItemClick(id, setSelected(c.parent), x.value)}"
713
+ >
714
+ ${(x) => x.label}
715
+ </li>
716
+ `
717
+ )}
718
+ </ul>
131
719
  `;
132
720
  };
721
+ const TimePickerTemplate = (context, _) => {
722
+ const popupTag = context.tagFor(Popup);
723
+ const textFieldTag = context.tagFor(TextField);
724
+ const buttonTag = context.tagFor(Button);
725
+ return html`<div class="base" @keydown="${(x, { event }) => x._onBaseKeyDown(event)}">
726
+ <${textFieldTag} id="text-field"
727
+ ${ref("_textFieldEl")}
728
+ class="control"
729
+ label="${(x) => x.label}"
730
+ helper-text="${(x) => x.helperText}"
731
+ error-text="${(x) => x.errorValidationMessage}"
732
+ placeholder="${(x) => x._textFieldPlaceholder}"
733
+ current-value="${(x) => x._presentationValue}"
734
+ ?disabled="${(x) => x.disabled}"
735
+ ?readonly="${(x) => x.readOnly}"
736
+ @input="${(x, c) => x._onTextFieldInput(c.event)}"
737
+ @change="${(x) => x._onTextFieldChange()}"
738
+ >
739
+ <${buttonTag}
740
+ id="clock-button"
741
+ ${ref("_clockButtonEl")}
742
+ slot="action-items"
743
+ size="condensed"
744
+ icon="clock-line"
745
+ appearance="ghost"
746
+ ?disabled="${(x) => x.disabled || x.readOnly}"
747
+ aria-label="${(x) => x._clockButtonLabel}"
748
+ @click="${(x) => x._onClockButtonClick()}"
749
+ ></${buttonTag}>
750
+ </${textFieldTag}>
751
+ <${popupTag}
752
+ ?open="${(x) => x._popupOpen}"
753
+ :anchor="${(x) => x._textFieldEl}"
754
+ placement="bottom-start"
755
+ class="popup">
756
+ <div class="dialog" role="dialog" ${ref(
757
+ "_dialogEl"
758
+ )} aria-modal="true" aria-label="${(x) => x.locale.timePicker.chooseTimeLabel}">
759
+ <div class="time-pickers">
760
+ ${renderPicker(
761
+ "hours",
762
+ (x) => x.locale.timePicker.hoursLabel,
763
+ (x) => x._selectedHour,
764
+ (x) => (v) => x._selectedHour = v,
765
+ (x) => x._hours
766
+ )}
767
+ ${renderPicker(
768
+ "minutes",
769
+ (x) => x.locale.timePicker.minutesLabel,
770
+ (x) => x._selectedMinute,
771
+ (x) => (v) => x._selectedMinute = v,
772
+ (x) => x._minutes
773
+ )}
774
+ ${when((x) => x._displaySeconds, renderPicker(
775
+ "seconds",
776
+ (x) => x.locale.timePicker.secondsLabel,
777
+ (x) => x._selectedSecond,
778
+ (x) => (v) => x._selectedSecond = v,
779
+ (x) => x._seconds
780
+ ))}
781
+ ${when((x) => x._use12hClock, renderPicker(
782
+ "meridies",
783
+ (x) => x.locale.timePicker.meridiesLabel,
784
+ (x) => x._selectedMeridiem,
785
+ (x) => (v) => x._selectedMeridiem = v,
786
+ (x) => x._meridies
787
+ ))}
788
+ </div>
789
+ <div class="footer">
790
+ <${buttonTag}
791
+ class="vwc-button"
792
+ size="condensed"
793
+ label="${(x) => x.locale.timePicker.clearLabel}"
794
+ @click="${(x) => x._onClearClick()}"
795
+ ></${buttonTag}>
796
+ <${buttonTag}
797
+ class="vwc-button"
798
+ size="condensed"
799
+ appearance="filled"
800
+ label="${(x) => x.locale.timePicker.okLabel}"
801
+ @click="${(x) => x._onOkClick()}"
802
+ ></${buttonTag}>
803
+ </div>
804
+ </div>
805
+ </${popupTag}>
806
+ </div>`;
807
+ };
133
808
 
134
- const toggletipDefinition = Toggletip.compose({
135
- baseName: "toggletip",
136
- template: ToggletipTemplate,
137
- styles
809
+ const timePickerDefinition = TimePicker.compose({
810
+ baseName: "time-picker",
811
+ template: TimePickerTemplate,
812
+ styles,
813
+ shadowOptions: {
814
+ delegatesFocus: true
815
+ }
138
816
  });
139
- const toggletipRegistries = [toggletipDefinition(), ...popupRegistries];
140
- const registerToggletip = registerFactory(toggletipRegistries);
817
+ const timePickerRegistries = [timePickerDefinition(), ...textFieldRegistries, ...popupRegistries, ...buttonRegistries];
818
+ const registerTimePicker = registerFactory(timePickerRegistries);
141
819
 
142
- export { toggletipRegistries as a, registerToggletip as r, toggletipDefinition as t };
820
+ export { timePickerRegistries as a, registerTimePicker as r, timePickerDefinition as t };