time-picker.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /*!
  2. * All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
  4. * v1.0.0-beta.82
  5. */
  6. import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/internal/client';
  7. import { i as isValidNumber, n as numberKeys, c as isActivationKey } from './number.js';
  8. import { d as defineCustomElement$1 } from './icon.js';
  9. const maxTenthForMinuteAndSecond = 5;
  10. function createLocaleDateTimeFormatter(locale, includeSeconds = true) {
  11. try {
  12. const options = {
  13. hour: "2-digit",
  14. minute: "2-digit",
  15. timeZone: "UTC"
  16. };
  17. if (includeSeconds) {
  18. options.second = "2-digit";
  19. }
  20. return new Intl.DateTimeFormat(locale, options);
  21. }
  22. catch (e) {
  23. throw new Error(`Invalid locale supplied while attempting to create a DateTime formatter: ${locale}`);
  24. }
  25. }
  26. function formatTimePart(number) {
  27. const numberAsString = number.toString();
  28. return number >= 0 && number <= 9 ? numberAsString.padStart(2, "0") : numberAsString;
  29. }
  30. function formatTimeString(value) {
  31. if (!isValidTime(value)) {
  32. return null;
  33. }
  34. const [hourString, minuteString, secondString] = value.split(":");
  35. const hour = formatTimePart(parseInt(hourString));
  36. const minute = formatTimePart(parseInt(minuteString));
  37. if (secondString) {
  38. const second = formatTimePart(parseInt(secondString));
  39. return `${hour}:${minute}:${second}`;
  40. }
  41. return `${hour}:${minute}`;
  42. }
  43. function getLocaleHourCycle(locale) {
  44. const formatter = createLocaleDateTimeFormatter(locale);
  45. const parts = formatter.formatToParts(new Date(Date.UTC(0, 0, 0, 0, 0, 0)));
  46. return getLocalizedTimePart("meridiem", parts) ? "12" : "24";
  47. }
  48. function getLocalizedTimePart(part, parts) {
  49. var _a, _b, _c, _d;
  50. if (!part || !parts) {
  51. return null;
  52. }
  53. if (part === "hourSuffix") {
  54. const hourIndex = parts.indexOf(parts.find(({ type }) => type === "hour"));
  55. const minuteIndex = parts.indexOf(parts.find(({ type }) => type === "minute"));
  56. const hourSuffix = parts[hourIndex + 1];
  57. return hourSuffix && hourSuffix.type === "literal" && minuteIndex - hourIndex === 2
  58. ? ((_a = hourSuffix.value) === null || _a === void 0 ? void 0 : _a.trim()) || null
  59. : null;
  60. }
  61. if (part === "minuteSuffix") {
  62. const minuteIndex = parts.indexOf(parts.find(({ type }) => type === "minute"));
  63. const secondIndex = parts.indexOf(parts.find(({ type }) => type === "second"));
  64. const minuteSuffix = parts[minuteIndex + 1];
  65. return minuteSuffix && minuteSuffix.type === "literal" && secondIndex - minuteIndex === 2
  66. ? ((_b = minuteSuffix.value) === null || _b === void 0 ? void 0 : _b.trim()) || null
  67. : null;
  68. }
  69. if (part === "secondSuffix") {
  70. const secondIndex = parts.indexOf(parts.find(({ type }) => type === "second"));
  71. const secondSuffix = parts[secondIndex + 1];
  72. return secondSuffix && secondSuffix.type === "literal" ? ((_c = secondSuffix.value) === null || _c === void 0 ? void 0 : _c.trim()) || null : null;
  73. }
  74. return ((_d = parts.find(({ type }) => (part == "meridiem" ? type === "dayPeriod" : type === part))) === null || _d === void 0 ? void 0 : _d.value) || null;
  75. }
  76. function getMeridiem(hour) {
  77. if (!isValidNumber(hour)) {
  78. return null;
  79. }
  80. const hourAsNumber = parseInt(hour);
  81. return hourAsNumber >= 0 && hourAsNumber <= 11 ? "AM" : "PM";
  82. }
  83. function isValidTime(value) {
  84. if (!value || value.startsWith(":") || value.endsWith(":")) {
  85. return false;
  86. }
  87. const splitValue = value.split(":");
  88. const validLength = splitValue.length > 1 && splitValue.length < 4;
  89. if (!validLength) {
  90. return false;
  91. }
  92. const [hour, minute, second] = splitValue;
  93. const hourAsNumber = parseInt(splitValue[0]);
  94. const minuteAsNumber = parseInt(splitValue[1]);
  95. const secondAsNumber = parseInt(splitValue[2]);
  96. const hourValid = isValidNumber(hour) && hourAsNumber >= 0 && hourAsNumber < 24;
  97. const minuteValid = isValidNumber(minute) && minuteAsNumber >= 0 && minuteAsNumber < 60;
  98. const secondValid = isValidNumber(second) && secondAsNumber >= 0 && secondAsNumber < 60;
  99. if ((hourValid && minuteValid && !second) || (hourValid && minuteValid && secondValid)) {
  100. return true;
  101. }
  102. }
  103. function isValidTimePart(value, part) {
  104. if (part === "meridiem") {
  105. return value === "AM" || value === "PM";
  106. }
  107. if (!isValidNumber(value)) {
  108. return false;
  109. }
  110. const valueAsNumber = Number(value);
  111. return part === "hour" ? valueAsNumber >= 0 && valueAsNumber < 24 : valueAsNumber >= 0 && valueAsNumber < 60;
  112. }
  113. function localizeTimePart(value, part, locale) {
  114. if (!isValidTimePart(value, part)) {
  115. return;
  116. }
  117. const valueAsNumber = parseInt(value);
  118. const date = new Date(Date.UTC(0, 0, 0, part === "hour" ? valueAsNumber : part === "meridiem" ? (value === "AM" ? 0 : 12) : 0, part === "minute" ? valueAsNumber : 0, part === "second" ? valueAsNumber : 0));
  119. if (!date) {
  120. return;
  121. }
  122. const formatter = createLocaleDateTimeFormatter(locale);
  123. const parts = formatter.formatToParts(date);
  124. return getLocalizedTimePart(part, parts);
  125. }
  126. function localizeTimeString(value, locale = "en", includeSeconds = true) {
  127. if (!isValidTime(value)) {
  128. return null;
  129. }
  130. const { hour, minute, second = "0" } = parseTimeString(value);
  131. const dateFromTimeString = new Date(Date.UTC(0, 0, 0, parseInt(hour), parseInt(minute), parseInt(second)));
  132. const formatter = createLocaleDateTimeFormatter(locale, includeSeconds);
  133. return (formatter === null || formatter === void 0 ? void 0 : formatter.format(dateFromTimeString)) || null;
  134. }
  135. function localizeTimeStringToParts(value, locale = "en") {
  136. if (!isValidTime(value)) {
  137. return null;
  138. }
  139. const { hour, minute, second = "0" } = parseTimeString(value);
  140. const dateFromTimeString = new Date(Date.UTC(0, 0, 0, parseInt(hour), parseInt(minute), parseInt(second)));
  141. if (dateFromTimeString) {
  142. const formatter = createLocaleDateTimeFormatter(locale);
  143. const parts = formatter.formatToParts(dateFromTimeString);
  144. return {
  145. localizedHour: getLocalizedTimePart("hour", parts),
  146. localizedHourSuffix: getLocalizedTimePart("hourSuffix", parts),
  147. localizedMinute: getLocalizedTimePart("minute", parts),
  148. localizedMinuteSuffix: getLocalizedTimePart("minuteSuffix", parts),
  149. localizedSecond: getLocalizedTimePart("second", parts),
  150. localizedSecondSuffix: getLocalizedTimePart("secondSuffix", parts),
  151. localizedMeridiem: getLocalizedTimePart("meridiem", parts)
  152. };
  153. }
  154. return null;
  155. }
  156. function getTimeParts(value, locale = "en") {
  157. if (!isValidTime(value)) {
  158. return null;
  159. }
  160. const { hour, minute, second = "0" } = parseTimeString(value);
  161. const dateFromTimeString = new Date(Date.UTC(0, 0, 0, parseInt(hour), parseInt(minute), parseInt(second)));
  162. if (dateFromTimeString) {
  163. const formatter = createLocaleDateTimeFormatter(locale);
  164. const parts = formatter.formatToParts(dateFromTimeString);
  165. return parts;
  166. }
  167. return null;
  168. }
  169. function parseTimeString(value) {
  170. if (isValidTime(value)) {
  171. const [hour, minute, second] = value.split(":");
  172. return {
  173. hour,
  174. minute,
  175. second
  176. };
  177. }
  178. return {
  179. hour: null,
  180. minute: null,
  181. second: null
  182. };
  183. }
  184. const CSS = {
  185. button: "button",
  186. buttonBottomLeft: "button--bottom-left",
  187. buttonBottomRight: "button--bottom-right",
  188. buttonHourDown: "button--hour-down",
  189. buttonHourUp: "button--hour-up",
  190. buttonMeridiemDown: "button--meridiem-down",
  191. buttonMeridiemUp: "button--meridiem-up",
  192. buttonMinuteDown: "button--minute-down",
  193. buttonMinuteUp: "button--minute-up",
  194. buttonSecondDown: "button--second-down",
  195. buttonSecondUp: "button--second-up",
  196. buttonTopLeft: "button--top-left",
  197. buttonTopRight: "button--top-right",
  198. column: "column",
  199. delimiter: "delimiter",
  200. hour: "hour",
  201. input: "input",
  202. meridiem: "meridiem",
  203. minute: "minute",
  204. second: "second",
  205. showMeridiem: "show-meridiem",
  206. showSecond: "show-second",
  207. "scale-s": "scale-s",
  208. "scale-m": "scale-m",
  209. "scale-l": "scale-l",
  210. timePicker: "time-picker",
  211. meridiemStart: "meridiem--start"
  212. };
  213. const TEXT = {
  214. hour: "Hour",
  215. hourDown: "Decrease hour",
  216. hourUp: "Increase hour",
  217. meridiem: "AM/PM",
  218. meridiemDown: "Decrease AM/PM",
  219. meridiemUp: "Increase AM/PM",
  220. minute: "Minute",
  221. minuteDown: "Decrease minute",
  222. minuteUp: "Increase minute",
  223. second: "Second",
  224. secondDown: "Decrease second",
  225. secondUp: "Increase second"
  226. };
  227. const timePickerCss = "@-webkit-keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes in-down{0%{opacity:0;-webkit-transform:translate3D(0, -5px, 0);transform:translate3D(0, -5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@keyframes in-down{0%{opacity:0;-webkit-transform:translate3D(0, -5px, 0);transform:translate3D(0, -5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@-webkit-keyframes in-up{0%{opacity:0;-webkit-transform:translate3D(0, 5px, 0);transform:translate3D(0, 5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;-webkit-transform:translate3D(0, 5px, 0);transform:translate3D(0, 5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@-webkit-keyframes in-scale{0%{opacity:0;-webkit-transform:scale3D(0.95, 0.95, 1);transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;-webkit-transform:scale3D(1, 1, 1);transform:scale3D(1, 1, 1)}}@keyframes in-scale{0%{opacity:0;-webkit-transform:scale3D(0.95, 0.95, 1);transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;-webkit-transform:scale3D(1, 1, 1);transform:scale3D(1, 1, 1)}}:root{--calcite-animation-timing:calc(150ms * var(--calcite-internal-duration-factor));--calcite-internal-duration-factor:var(--calcite-duration-factor, 1);--calcite-internal-animation-timing-fast:calc(100ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-medium:calc(200ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-slow:calc(300ms * var(--calcite-internal-duration-factor))}.calcite-animate{opacity:0;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:var(--calcite-animation-timing);animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{-webkit-animation-name:in;animation-name:in}.calcite-animate__in-down{-webkit-animation-name:in-down;animation-name:in-down}.calcite-animate__in-up{-webkit-animation-name:in-up;animation-name:in-up}.calcite-animate__in-scale{-webkit-animation-name:in-scale;animation-name:in-scale}:root{--calcite-popper-transition:var(--calcite-animation-timing)}:host([hidden]){display:none}:host{display:inline-block}.time-picker{display:-ms-flexbox;display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-ms-flex-align:center;align-items:center;background-color:var(--calcite-ui-foreground-1);font-weight:var(--calcite-font-weight-medium);color:var(--calcite-ui-text-1);--tw-shadow:0 6px 20px -4px rgba(0, 0, 0, 0.1), 0 4px 12px -2px rgba(0, 0, 0, 0.08);--tw-shadow-colored:0 6px 20px -4px var(--tw-shadow-color), 0 4px 12px -2px var(--tw-shadow-color);-webkit-box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);border-radius:var(--calcite-border-radius)}.time-picker .column{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.time-picker .meridiem--start{-ms-flex-order:-1;order:-1}.time-picker .button{display:-ms-inline-flexbox;display:inline-flex;cursor:pointer;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:var(--calcite-ui-foreground-1)}.time-picker .button:hover,.time-picker .button:focus{background-color:var(--calcite-ui-foreground-2);outline:2px solid transparent;outline-offset:2px}.time-picker .button:active{background-color:var(--calcite-ui-foreground-3)}.time-picker .button.top-left{border-top-left-radius:var(--calcite-border-radius)}.time-picker .button.bottom-left{border-bottom-left-radius:var(--calcite-border-radius)}.time-picker .button.top-right{border-top-right-radius:var(--calcite-border-radius)}.time-picker .button.bottom-right{border-bottom-right-radius:var(--calcite-border-radius)}.time-picker .button calcite-icon{color:var(--calcite-ui-text-3)}.time-picker .input{display:-ms-inline-flexbox;display:inline-flex;cursor:pointer;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:var(--calcite-ui-foreground-1);font-weight:var(--calcite-font-weight-medium)}.time-picker .input:hover{-webkit-box-shadow:inset 0 0 0 2px var(--calcite-ui-foreground-2);box-shadow:inset 0 0 0 2px var(--calcite-ui-foreground-2)}.time-picker .input:focus,.time-picker .input:hover:focus{outline:2px solid transparent;outline-offset:2px;-webkit-box-shadow:inset 0 0 0 2px var(--calcite-ui-brand);box-shadow:inset 0 0 0 2px var(--calcite-ui-brand)}.time-picker.scale-s{font-size:var(--calcite-font-size--1)}.time-picker.scale-s .button,.time-picker.scale-s .input{padding-left:0.75rem;padding-right:0.75rem;padding-top:0.25rem;padding-bottom:0.25rem}.time-picker.scale-s:not(.show-meridiem) .delimiter:last-child{-webkit-padding-end:0.75rem;padding-inline-end:0.75rem}.time-picker.scale-m{font-size:var(--calcite-font-size-0)}.time-picker.scale-m .button,.time-picker.scale-m .input{padding-left:1rem;padding-right:1rem;padding-top:0.5rem;padding-bottom:0.5rem}.time-picker.scale-m:not(.show-meridiem) .delimiter:last-child{-webkit-padding-end:1rem;padding-inline-end:1rem}.time-picker.scale-l{font-size:var(--calcite-font-size-1)}.time-picker.scale-l .button,.time-picker.scale-l .input{padding-left:1.25rem;padding-right:1.25rem;padding-top:0.75rem;padding-bottom:0.75rem}.time-picker.scale-l:not(.show-meridiem) .delimiter:last-child{-webkit-padding-end:1.25rem;padding-inline-end:1.25rem}";
  228. function capitalize(str) {
  229. return str.charAt(0).toUpperCase() + str.slice(1);
  230. }
  231. const TimePicker = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
  232. constructor() {
  233. super();
  234. this.__registerHost();
  235. this.__attachShadow();
  236. this.calciteTimePickerBlur = createEvent(this, "calciteTimePickerBlur", 7);
  237. this.calciteTimePickerChange = createEvent(this, "calciteTimePickerChange", 7);
  238. this.calciteTimePickerFocus = createEvent(this, "calciteTimePickerFocus", 7);
  239. //--------------------------------------------------------------------------
  240. //
  241. // Properties
  242. //
  243. //--------------------------------------------------------------------------
  244. /** aria-label for the hour input
  245. * @default "Hour"
  246. */
  247. this.intlHour = TEXT.hour;
  248. /** aria-label for the hour down button
  249. * @default "Decrease hour"
  250. */
  251. this.intlHourDown = TEXT.hourDown;
  252. /** aria-label for the hour up button
  253. * @default "Increase hour"
  254. */
  255. this.intlHourUp = TEXT.hourUp;
  256. /** aria-label for the meridiem (am/pm) input
  257. * @default "AM/PM"
  258. */
  259. this.intlMeridiem = TEXT.meridiem;
  260. /** aria-label for the meridiem (am/pm) down button
  261. * @default "Decrease AM/PM"
  262. */
  263. this.intlMeridiemDown = TEXT.meridiemDown;
  264. /** aria-label for the meridiem (am/pm) up button
  265. * @default "Increase AM/PM"
  266. */
  267. this.intlMeridiemUp = TEXT.meridiemUp;
  268. /** aria-label for the minute input
  269. * @default "Minute"
  270. */
  271. this.intlMinute = TEXT.minute;
  272. /** aria-label for the minute down button
  273. * @default "Decrease minute"
  274. */
  275. this.intlMinuteDown = TEXT.minuteDown;
  276. /** aria-label for the minute up button
  277. * @default "Increase minute"
  278. */
  279. this.intlMinuteUp = TEXT.minuteUp;
  280. /** aria-label for the second input
  281. * @default "Second"
  282. */
  283. this.intlSecond = TEXT.second;
  284. /** aria-label for the second down button
  285. * @default "Decrease second"
  286. */
  287. this.intlSecondDown = TEXT.secondDown;
  288. /** aria-label for the second up button
  289. * @default "Increase second"
  290. */
  291. this.intlSecondUp = TEXT.secondUp;
  292. /**
  293. * BCP 47 language tag for desired language and country format
  294. * @internal
  295. */
  296. this.locale = document.documentElement.lang || navigator.language || "en";
  297. /** The scale (size) of the time picker */
  298. this.scale = "m";
  299. /** number (seconds) that specifies the granularity that the value must adhere to */
  300. this.step = 60;
  301. /** The selected time in UTC (always 24-hour format) */
  302. this.value = null;
  303. this.showSecond = this.step < 60;
  304. this.decrementHour = () => {
  305. const newHour = !this.hour ? 0 : this.hour === "00" ? 23 : parseInt(this.hour) - 1;
  306. this.setValuePart("hour", newHour);
  307. };
  308. this.decrementMeridiem = () => {
  309. const newMeridiem = this.meridiem === "PM" ? "AM" : "PM";
  310. this.setValuePart("meridiem", newMeridiem);
  311. };
  312. this.decrementMinuteOrSecond = (key) => {
  313. let newValue;
  314. if (isValidNumber(this[key])) {
  315. const valueAsNumber = parseInt(this[key]);
  316. newValue = valueAsNumber === 0 ? 59 : valueAsNumber - 1;
  317. }
  318. else {
  319. newValue = 59;
  320. }
  321. this.setValuePart(key, newValue);
  322. };
  323. this.decrementMinute = () => {
  324. this.decrementMinuteOrSecond("minute");
  325. };
  326. this.decrementSecond = () => {
  327. this.decrementMinuteOrSecond("second");
  328. };
  329. this.focusHandler = (event) => {
  330. this.activeEl = event.currentTarget;
  331. };
  332. this.hourDownButtonKeyDownHandler = (event) => {
  333. if (this.buttonActivated(event)) {
  334. this.decrementHour();
  335. }
  336. };
  337. this.hourKeyDownHandler = (event) => {
  338. const key = event.key;
  339. if (numberKeys.includes(key)) {
  340. const keyAsNumber = parseInt(key);
  341. let newHour;
  342. if (isValidNumber(this.hour)) {
  343. switch (this.hourCycle) {
  344. case "12":
  345. newHour =
  346. this.hour === "01" && keyAsNumber >= 0 && keyAsNumber <= 2
  347. ? `1${keyAsNumber}`
  348. : keyAsNumber;
  349. break;
  350. case "24":
  351. if (this.hour === "01") {
  352. newHour = `1${keyAsNumber}`;
  353. }
  354. else if (this.hour === "02" && keyAsNumber >= 0 && keyAsNumber <= 3) {
  355. newHour = `2${keyAsNumber}`;
  356. }
  357. else {
  358. newHour = keyAsNumber;
  359. }
  360. break;
  361. }
  362. }
  363. else {
  364. newHour = keyAsNumber;
  365. }
  366. this.setValuePart("hour", newHour);
  367. }
  368. else {
  369. switch (key) {
  370. case "Backspace":
  371. case "Delete":
  372. this.setValuePart("hour", null);
  373. break;
  374. case "ArrowDown":
  375. event.preventDefault();
  376. this.decrementHour();
  377. break;
  378. case "ArrowUp":
  379. event.preventDefault();
  380. this.incrementHour();
  381. break;
  382. case " ":
  383. case "Spacebar":
  384. event.preventDefault();
  385. break;
  386. }
  387. }
  388. };
  389. this.hourUpButtonKeyDownHandler = (event) => {
  390. if (this.buttonActivated(event)) {
  391. this.incrementHour();
  392. }
  393. };
  394. this.incrementMeridiem = () => {
  395. const newMeridiem = this.meridiem === "AM" ? "PM" : "AM";
  396. this.setValuePart("meridiem", newMeridiem);
  397. };
  398. this.incrementHour = () => {
  399. const newHour = isValidNumber(this.hour)
  400. ? this.hour === "23"
  401. ? 0
  402. : parseInt(this.hour) + 1
  403. : 1;
  404. this.setValuePart("hour", newHour);
  405. };
  406. this.incrementMinuteOrSecond = (key) => {
  407. const newValue = isValidNumber(this[key])
  408. ? this[key] === "59"
  409. ? 0
  410. : parseInt(this[key]) + 1
  411. : 0;
  412. this.setValuePart(key, newValue);
  413. };
  414. this.incrementMinute = () => {
  415. this.incrementMinuteOrSecond("minute");
  416. };
  417. this.incrementSecond = () => {
  418. this.incrementMinuteOrSecond("second");
  419. };
  420. this.meridiemDownButtonKeyDownHandler = (event) => {
  421. if (this.buttonActivated(event)) {
  422. this.decrementMeridiem();
  423. }
  424. };
  425. this.meridiemKeyDownHandler = (event) => {
  426. switch (event.key) {
  427. case "a":
  428. this.setValuePart("meridiem", "AM");
  429. break;
  430. case "p":
  431. this.setValuePart("meridiem", "PM");
  432. break;
  433. case "Backspace":
  434. case "Delete":
  435. this.setValuePart("meridiem", null);
  436. break;
  437. case "ArrowUp":
  438. event.preventDefault();
  439. this.incrementMeridiem();
  440. break;
  441. case "ArrowDown":
  442. event.preventDefault();
  443. this.decrementMeridiem();
  444. break;
  445. case " ":
  446. case "Spacebar":
  447. event.preventDefault();
  448. break;
  449. }
  450. };
  451. this.meridiemUpButtonKeyDownHandler = (event) => {
  452. if (this.buttonActivated(event)) {
  453. this.incrementMeridiem();
  454. }
  455. };
  456. this.minuteDownButtonKeyDownHandler = (event) => {
  457. if (this.buttonActivated(event)) {
  458. this.decrementMinute();
  459. }
  460. };
  461. this.minuteKeyDownHandler = (event) => {
  462. const key = event.key;
  463. if (numberKeys.includes(key)) {
  464. const keyAsNumber = parseInt(key);
  465. let newMinute;
  466. if (isValidNumber(this.minute) && this.minute.startsWith("0")) {
  467. const minuteAsNumber = parseInt(this.minute);
  468. newMinute =
  469. minuteAsNumber > maxTenthForMinuteAndSecond
  470. ? keyAsNumber
  471. : `${minuteAsNumber}${keyAsNumber}`;
  472. }
  473. else {
  474. newMinute = keyAsNumber;
  475. }
  476. this.setValuePart("minute", newMinute);
  477. }
  478. else {
  479. switch (key) {
  480. case "Backspace":
  481. case "Delete":
  482. this.setValuePart("minute", null);
  483. break;
  484. case "ArrowDown":
  485. event.preventDefault();
  486. this.decrementMinute();
  487. break;
  488. case "ArrowUp":
  489. event.preventDefault();
  490. this.incrementMinute();
  491. break;
  492. case " ":
  493. case "Spacebar":
  494. event.preventDefault();
  495. break;
  496. }
  497. }
  498. };
  499. this.minuteUpButtonKeyDownHandler = (event) => {
  500. if (this.buttonActivated(event)) {
  501. this.incrementMinute();
  502. }
  503. };
  504. this.secondDownButtonKeyDownHandler = (event) => {
  505. if (this.buttonActivated(event)) {
  506. this.decrementSecond();
  507. }
  508. };
  509. this.secondKeyDownHandler = (event) => {
  510. const key = event.key;
  511. if (numberKeys.includes(key)) {
  512. const keyAsNumber = parseInt(key);
  513. let newSecond;
  514. if (isValidNumber(this.second) && this.second.startsWith("0")) {
  515. const secondAsNumber = parseInt(this.second);
  516. newSecond =
  517. secondAsNumber > maxTenthForMinuteAndSecond
  518. ? keyAsNumber
  519. : `${secondAsNumber}${keyAsNumber}`;
  520. }
  521. else {
  522. newSecond = keyAsNumber;
  523. }
  524. this.setValuePart("second", newSecond);
  525. }
  526. else {
  527. switch (key) {
  528. case "Backspace":
  529. case "Delete":
  530. this.setValuePart("second", null);
  531. break;
  532. case "ArrowDown":
  533. event.preventDefault();
  534. this.decrementSecond();
  535. break;
  536. case "ArrowUp":
  537. event.preventDefault();
  538. this.incrementSecond();
  539. break;
  540. case " ":
  541. case "Spacebar":
  542. event.preventDefault();
  543. break;
  544. }
  545. }
  546. };
  547. this.secondUpButtonKeyDownHandler = (event) => {
  548. if (this.buttonActivated(event)) {
  549. this.incrementSecond();
  550. }
  551. };
  552. this.setHourEl = (el) => (this.hourEl = el);
  553. this.setMeridiemEl = (el) => (this.meridiemEl = el);
  554. this.setMinuteEl = (el) => (this.minuteEl = el);
  555. this.setSecondEl = (el) => (this.secondEl = el);
  556. this.setValue = (value, emit = true) => {
  557. if (isValidTime(value)) {
  558. const { hour, minute, second } = parseTimeString(value);
  559. const { localizedHour, localizedHourSuffix, localizedMinute, localizedMinuteSuffix, localizedSecond, localizedSecondSuffix, localizedMeridiem } = localizeTimeStringToParts(value, this.locale);
  560. this.localizedHour = localizedHour;
  561. this.localizedHourSuffix = localizedHourSuffix;
  562. this.localizedMinute = localizedMinute;
  563. this.localizedMinuteSuffix = localizedMinuteSuffix;
  564. this.localizedSecond = localizedSecond;
  565. this.localizedSecondSuffix = localizedSecondSuffix;
  566. this.hour = hour;
  567. this.minute = minute;
  568. this.second = second;
  569. if (localizedMeridiem) {
  570. this.localizedMeridiem = localizedMeridiem;
  571. this.meridiem = getMeridiem(this.hour);
  572. const formatParts = getTimeParts(value, this.locale);
  573. this.meridiemOrder = this.getMeridiemOrder(formatParts);
  574. }
  575. }
  576. else {
  577. this.hour = null;
  578. this.localizedHour = null;
  579. this.localizedHourSuffix = null;
  580. this.localizedMeridiem = null;
  581. this.localizedMinute = null;
  582. this.localizedMinuteSuffix = null;
  583. this.localizedSecond = null;
  584. this.localizedSecondSuffix = null;
  585. this.meridiem = null;
  586. this.minute = null;
  587. this.second = null;
  588. this.value = null;
  589. }
  590. if (emit) {
  591. this.calciteTimePickerChange.emit();
  592. }
  593. };
  594. this.setValuePart = (key, value, emit = true) => {
  595. var _a;
  596. if (key === "meridiem") {
  597. this.meridiem = value;
  598. if (isValidNumber(this.hour)) {
  599. const hourAsNumber = parseInt(this.hour);
  600. switch (value) {
  601. case "AM":
  602. if (hourAsNumber >= 12) {
  603. this.hour = formatTimePart(hourAsNumber - 12);
  604. }
  605. break;
  606. case "PM":
  607. if (hourAsNumber < 12) {
  608. this.hour = formatTimePart(hourAsNumber + 12);
  609. }
  610. break;
  611. }
  612. this.localizedHour = localizeTimePart(this.hour, "hour", this.locale);
  613. }
  614. }
  615. else {
  616. this[key] = typeof value === "number" ? formatTimePart(value) : value;
  617. this[`localized${capitalize(key)}`] = localizeTimePart(this[key], key, this.locale);
  618. }
  619. if (this.hour && this.minute) {
  620. const showSeconds = this.second && this.showSecond;
  621. this.value = `${this.hour}:${this.minute}:${showSeconds ? this.second : "00"}`;
  622. }
  623. else {
  624. this.value = null;
  625. }
  626. this.localizedMeridiem = this.value
  627. ? ((_a = localizeTimeStringToParts(this.value, this.locale)) === null || _a === void 0 ? void 0 : _a.localizedMeridiem) || null
  628. : localizeTimePart(this.meridiem, "meridiem", this.locale);
  629. if (emit) {
  630. this.calciteTimePickerChange.emit();
  631. }
  632. };
  633. }
  634. localeWatcher(newLocale) {
  635. this.hourCycle = getLocaleHourCycle(newLocale);
  636. this.setValue(this.value, false);
  637. }
  638. valueWatcher(newValue) {
  639. this.setValue(newValue, false);
  640. }
  641. //--------------------------------------------------------------------------
  642. //
  643. // Event Listeners
  644. //
  645. //--------------------------------------------------------------------------
  646. hostBlurHandler() {
  647. this.calciteTimePickerBlur.emit();
  648. }
  649. hostFocusHandler() {
  650. this.calciteTimePickerFocus.emit();
  651. }
  652. keyDownHandler(event) {
  653. const key = event.key;
  654. switch (this.activeEl) {
  655. case this.hourEl:
  656. if (key === "ArrowRight") {
  657. this.setFocus("minute");
  658. }
  659. break;
  660. case this.minuteEl:
  661. switch (key) {
  662. case "ArrowLeft":
  663. this.setFocus("hour");
  664. break;
  665. case "ArrowRight":
  666. if (this.step !== 60) {
  667. this.setFocus("second");
  668. }
  669. else if (this.hourCycle === "12") {
  670. this.setFocus("meridiem");
  671. }
  672. break;
  673. }
  674. break;
  675. case this.secondEl:
  676. switch (key) {
  677. case "ArrowLeft":
  678. this.setFocus("minute");
  679. break;
  680. case "ArrowRight":
  681. if (this.hourCycle === "12") {
  682. this.setFocus("meridiem");
  683. }
  684. break;
  685. }
  686. break;
  687. case this.meridiemEl:
  688. switch (key) {
  689. case "ArrowLeft":
  690. if (this.step !== 60) {
  691. this.setFocus("second");
  692. }
  693. else {
  694. this.setFocus("minute");
  695. }
  696. break;
  697. }
  698. break;
  699. }
  700. }
  701. //--------------------------------------------------------------------------
  702. //
  703. // Public Methods
  704. //
  705. //--------------------------------------------------------------------------
  706. /** Sets focus on the component. */
  707. async setFocus(target) {
  708. var _a;
  709. (_a = this[`${target || "hour"}El`]) === null || _a === void 0 ? void 0 : _a.focus();
  710. }
  711. // --------------------------------------------------------------------------
  712. //
  713. // Private Methods
  714. //
  715. // --------------------------------------------------------------------------
  716. buttonActivated(event) {
  717. const key = event.key;
  718. if (key === " ") {
  719. event.preventDefault();
  720. }
  721. return isActivationKey(key);
  722. }
  723. getMeridiemOrder(formatParts) {
  724. const isRTLKind = this.locale === "ar" || this.locale === "he";
  725. if (formatParts && !isRTLKind) {
  726. const index = formatParts.findIndex((parts) => {
  727. return parts.value === this.localizedMeridiem;
  728. });
  729. return index;
  730. }
  731. return 0;
  732. }
  733. // --------------------------------------------------------------------------
  734. //
  735. // Lifecycle
  736. //
  737. // --------------------------------------------------------------------------
  738. connectedCallback() {
  739. this.setValue(this.value, false);
  740. this.hourCycle = getLocaleHourCycle(this.locale);
  741. }
  742. // --------------------------------------------------------------------------
  743. //
  744. // Render Methods
  745. //
  746. // --------------------------------------------------------------------------
  747. render() {
  748. const hourIsNumber = isValidNumber(this.hour);
  749. const iconScale = this.scale === "s" || this.scale === "m" ? "s" : "m";
  750. const minuteIsNumber = isValidNumber(this.minute);
  751. const secondIsNumber = isValidNumber(this.second);
  752. const showMeridiem = this.hourCycle === "12";
  753. return (h("div", { class: {
  754. [CSS.timePicker]: true,
  755. [CSS.showMeridiem]: showMeridiem,
  756. [CSS.showSecond]: this.showSecond,
  757. [CSS[`scale-${this.scale}`]]: true
  758. }, dir: "ltr" }, h("div", { class: CSS.column, role: "group" }, h("span", { "aria-label": this.intlHourUp, class: {
  759. [CSS.button]: true,
  760. [CSS.buttonHourUp]: true,
  761. [CSS.buttonTopLeft]: true
  762. }, onClick: this.incrementHour, onKeyDown: this.hourUpButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-up", scale: iconScale })), h("span", { "aria-label": this.intlHour, "aria-valuemax": "23", "aria-valuemin": "1", "aria-valuenow": (hourIsNumber && parseInt(this.hour)) || "0", "aria-valuetext": this.hour, class: {
  763. [CSS.input]: true,
  764. [CSS.hour]: true
  765. }, onFocus: this.focusHandler, onKeyDown: this.hourKeyDownHandler, ref: this.setHourEl, role: "spinbutton", tabIndex: 0 }, this.localizedHour || "--"), h("span", { "aria-label": this.intlHourDown, class: {
  766. [CSS.button]: true,
  767. [CSS.buttonHourDown]: true,
  768. [CSS.buttonBottomLeft]: true
  769. }, onClick: this.decrementHour, onKeyDown: this.hourDownButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-down", scale: iconScale }))), h("span", { class: CSS.delimiter }, this.localizedHourSuffix), h("div", { class: CSS.column, role: "group" }, h("span", { "aria-label": this.intlMinuteUp, class: {
  770. [CSS.button]: true,
  771. [CSS.buttonMinuteUp]: true
  772. }, onClick: this.incrementMinute, onKeyDown: this.minuteUpButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-up", scale: iconScale })), h("span", { "aria-label": this.intlMinute, "aria-valuemax": "12", "aria-valuemin": "1", "aria-valuenow": (minuteIsNumber && parseInt(this.minute)) || "0", "aria-valuetext": this.minute, class: {
  773. [CSS.input]: true,
  774. [CSS.minute]: true
  775. }, onFocus: this.focusHandler, onKeyDown: this.minuteKeyDownHandler, ref: this.setMinuteEl, role: "spinbutton", tabIndex: 0 }, this.localizedMinute || "--"), h("span", { "aria-label": this.intlMinuteDown, class: {
  776. [CSS.button]: true,
  777. [CSS.buttonMinuteDown]: true
  778. }, onClick: this.decrementMinute, onKeyDown: this.minuteDownButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-down", scale: iconScale }))), this.showSecond && h("span", { class: CSS.delimiter }, this.localizedMinuteSuffix), this.showSecond && (h("div", { class: CSS.column, role: "group" }, h("span", { "aria-label": this.intlSecondUp, class: {
  779. [CSS.button]: true,
  780. [CSS.buttonSecondUp]: true
  781. }, onClick: this.incrementSecond, onKeyDown: this.secondUpButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-up", scale: iconScale })), h("span", { "aria-label": this.intlSecond, "aria-valuemax": "59", "aria-valuemin": "0", "aria-valuenow": (secondIsNumber && parseInt(this.second)) || "0", "aria-valuetext": this.second, class: {
  782. [CSS.input]: true,
  783. [CSS.second]: true
  784. }, onFocus: this.focusHandler, onKeyDown: this.secondKeyDownHandler, ref: this.setSecondEl, role: "spinbutton", tabIndex: 0 }, this.localizedSecond || "--"), h("span", { "aria-label": this.intlSecondDown, class: {
  785. [CSS.button]: true,
  786. [CSS.buttonSecondDown]: true
  787. }, onClick: this.decrementSecond, onKeyDown: this.secondDownButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-down", scale: iconScale })))), this.localizedSecondSuffix && (h("span", { class: CSS.delimiter }, this.localizedSecondSuffix)), showMeridiem && (h("div", { class: {
  788. [CSS.column]: true,
  789. [CSS.meridiemStart]: this.meridiemOrder === 0
  790. }, role: "group" }, h("span", { "aria-label": this.intlMeridiemUp, class: {
  791. [CSS.button]: true,
  792. [CSS.buttonMeridiemUp]: true,
  793. [CSS.buttonTopRight]: true
  794. }, onClick: this.incrementMeridiem, onKeyDown: this.meridiemUpButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-up", scale: iconScale })), h("span", { "aria-label": this.intlMeridiem, "aria-valuemax": "2", "aria-valuemin": "1", "aria-valuenow": (this.meridiem === "PM" && "2") || "1", "aria-valuetext": this.meridiem, class: {
  795. [CSS.input]: true,
  796. [CSS.meridiem]: true
  797. }, onFocus: this.focusHandler, onKeyDown: this.meridiemKeyDownHandler, ref: this.setMeridiemEl, role: "spinbutton", tabIndex: 0 }, this.localizedMeridiem || "--"), h("span", { "aria-label": this.intlMeridiemDown, class: {
  798. [CSS.button]: true,
  799. [CSS.buttonMeridiemDown]: true,
  800. [CSS.buttonBottomRight]: true
  801. }, onClick: this.decrementMeridiem, onKeyDown: this.meridiemDownButtonKeyDownHandler, role: "button", tabIndex: -1 }, h("calcite-icon", { icon: "chevron-down", scale: iconScale }))))));
  802. }
  803. get el() { return this; }
  804. static get watchers() { return {
  805. "locale": ["localeWatcher"],
  806. "value": ["valueWatcher"]
  807. }; }
  808. static get style() { return timePickerCss; }
  809. }, [1, "calcite-time-picker", {
  810. "intlHour": [1, "intl-hour"],
  811. "intlHourDown": [1, "intl-hour-down"],
  812. "intlHourUp": [1, "intl-hour-up"],
  813. "intlMeridiem": [1, "intl-meridiem"],
  814. "intlMeridiemDown": [1, "intl-meridiem-down"],
  815. "intlMeridiemUp": [1, "intl-meridiem-up"],
  816. "intlMinute": [1, "intl-minute"],
  817. "intlMinuteDown": [1, "intl-minute-down"],
  818. "intlMinuteUp": [1, "intl-minute-up"],
  819. "intlSecond": [1, "intl-second"],
  820. "intlSecondDown": [1, "intl-second-down"],
  821. "intlSecondUp": [1, "intl-second-up"],
  822. "locale": [1025, "lang"],
  823. "scale": [1],
  824. "step": [2],
  825. "value": [1025],
  826. "hour": [32],
  827. "hourCycle": [32],
  828. "localizedHour": [32],
  829. "localizedHourSuffix": [32],
  830. "localizedMeridiem": [32],
  831. "localizedMinute": [32],
  832. "localizedMinuteSuffix": [32],
  833. "localizedSecond": [32],
  834. "localizedSecondSuffix": [32],
  835. "meridiem": [32],
  836. "minute": [32],
  837. "second": [32],
  838. "showSecond": [32],
  839. "setFocus": [64]
  840. }, [[0, "blur", "hostBlurHandler"], [0, "focus", "hostFocusHandler"], [0, "keydown", "keyDownHandler"]]]);
  841. function defineCustomElement() {
  842. if (typeof customElements === "undefined") {
  843. return;
  844. }
  845. const components = ["calcite-time-picker", "calcite-icon"];
  846. components.forEach(tagName => { switch (tagName) {
  847. case "calcite-time-picker":
  848. if (!customElements.get(tagName)) {
  849. customElements.define(tagName, TimePicker);
  850. }
  851. break;
  852. case "calcite-icon":
  853. if (!customElements.get(tagName)) {
  854. defineCustomElement$1();
  855. }
  856. break;
  857. } });
  858. }
  859. defineCustomElement();
  860. export { TimePicker as T, defineCustomElement as d, formatTimeString as f, isValidTime as i, localizeTimeString as l };