time-picker.js 37 KB

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