ion-datetime-button.cjs.entry.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. 'use strict';
  5. Object.defineProperty(exports, '__esModule', { value: true });
  6. const index = require('./index-2e236a04.js');
  7. const helpers = require('./helpers-8a48fdea.js');
  8. const index$1 = require('./index-cc858e97.js');
  9. const theme = require('./theme-d1c573d2.js');
  10. const ionicGlobal = require('./ionic-global-6dea5a96.js');
  11. const data = require('./data-94e8d392.js');
  12. const datetimeButtonIosCss = ":host{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}:host button{border-radius:8px;-webkit-margin-start:2px;margin-inline-start:2px;-webkit-margin-end:2px;margin-inline-end:2px;margin-top:0px;margin-bottom:0px;position:relative;-webkit-transition:150ms color ease-in-out;transition:150ms color ease-in-out;border:none;background:var(--ion-color-step-300, var(--ion-background-color-step-300, #edeef0));color:var(--ion-text-color, #000);font-family:inherit;font-size:1rem;cursor:pointer;overflow:hidden;-webkit-appearance:none;-moz-appearance:none;appearance:none}:host(.time-active) #time-button,:host(.date-active) #date-button{color:var(--ion-color-base)}:host(.datetime-button-disabled){pointer-events:none}:host(.datetime-button-disabled) button{opacity:0.4}:host button{-webkit-padding-start:13px;padding-inline-start:13px;-webkit-padding-end:13px;padding-inline-end:13px;padding-top:7px;padding-bottom:7px}:host button.ion-activated{color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}";
  13. const IonDatetimeButtonIosStyle0 = datetimeButtonIosCss;
  14. const datetimeButtonMdCss = ":host{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}:host button{border-radius:8px;-webkit-margin-start:2px;margin-inline-start:2px;-webkit-margin-end:2px;margin-inline-end:2px;margin-top:0px;margin-bottom:0px;position:relative;-webkit-transition:150ms color ease-in-out;transition:150ms color ease-in-out;border:none;background:var(--ion-color-step-300, var(--ion-background-color-step-300, #edeef0));color:var(--ion-text-color, #000);font-family:inherit;font-size:1rem;cursor:pointer;overflow:hidden;-webkit-appearance:none;-moz-appearance:none;appearance:none}:host(.time-active) #time-button,:host(.date-active) #date-button{color:var(--ion-color-base)}:host(.datetime-button-disabled){pointer-events:none}:host(.datetime-button-disabled) button{opacity:0.4}:host button{-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px;padding-top:6px;padding-bottom:6px}";
  15. const IonDatetimeButtonMdStyle0 = datetimeButtonMdCss;
  16. const DatetimeButton = class {
  17. constructor(hostRef) {
  18. index.registerInstance(this, hostRef);
  19. this.datetimeEl = null;
  20. this.overlayEl = null;
  21. /**
  22. * Accepts one or more string values and converts
  23. * them to DatetimeParts. This is done so datetime-button
  24. * can work with an array internally and not need
  25. * to keep checking if the datetime value is `string` or `string[]`.
  26. */
  27. this.getParsedDateValues = (value) => {
  28. if (value === undefined || value === null) {
  29. return [];
  30. }
  31. if (Array.isArray(value)) {
  32. return value;
  33. }
  34. return [value];
  35. };
  36. /**
  37. * Check the value property on the linked
  38. * ion-datetime and then format it according
  39. * to the locale specified on ion-datetime.
  40. */
  41. this.setDateTimeText = () => {
  42. var _a, _b, _c, _d, _e;
  43. const { datetimeEl, datetimePresentation } = this;
  44. if (!datetimeEl) {
  45. return;
  46. }
  47. const { value, locale, formatOptions, hourCycle, preferWheel, multiple, titleSelectedDatesFormatter } = datetimeEl;
  48. const parsedValues = this.getParsedDateValues(value);
  49. /**
  50. * Both ion-datetime and ion-datetime-button default
  51. * to today's date and time if no value is set.
  52. */
  53. const parsedDatetimes = data.parseDate(parsedValues.length > 0 ? parsedValues : [data.getToday()]);
  54. if (!parsedDatetimes) {
  55. return;
  56. }
  57. /**
  58. * If developers incorrectly use multiple="true"
  59. * with non "date" datetimes, then just select
  60. * the first value so the interface does
  61. * not appear broken. Datetime will provide a
  62. * warning in the console.
  63. */
  64. const firstParsedDatetime = parsedDatetimes[0];
  65. const computedHourCycle = data.getHourCycle(locale, hourCycle);
  66. this.dateText = this.timeText = undefined;
  67. switch (datetimePresentation) {
  68. case 'date-time':
  69. case 'time-date':
  70. const dateText = data.getLocalizedDateTime(locale, firstParsedDatetime, (_a = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _a !== void 0 ? _a : { month: 'short', day: 'numeric', year: 'numeric' });
  71. const timeText = data.getLocalizedTime(locale, firstParsedDatetime, computedHourCycle, formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time);
  72. if (preferWheel) {
  73. this.dateText = `${dateText} ${timeText}`;
  74. }
  75. else {
  76. this.dateText = dateText;
  77. this.timeText = timeText;
  78. }
  79. break;
  80. case 'date':
  81. if (multiple && parsedValues.length !== 1) {
  82. let headerText = `${parsedValues.length} days`; // default/fallback for multiple selection
  83. if (titleSelectedDatesFormatter !== undefined) {
  84. try {
  85. headerText = titleSelectedDatesFormatter(parsedValues);
  86. }
  87. catch (e) {
  88. index$1.printIonError('[ion-datetime-button] - Exception in provided `titleSelectedDatesFormatter`:', e);
  89. }
  90. }
  91. this.dateText = headerText;
  92. }
  93. else {
  94. this.dateText = data.getLocalizedDateTime(locale, firstParsedDatetime, (_b = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _b !== void 0 ? _b : { month: 'short', day: 'numeric', year: 'numeric' });
  95. }
  96. break;
  97. case 'time':
  98. this.timeText = data.getLocalizedTime(locale, firstParsedDatetime, computedHourCycle, formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time);
  99. break;
  100. case 'month-year':
  101. this.dateText = data.getLocalizedDateTime(locale, firstParsedDatetime, (_c = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _c !== void 0 ? _c : { month: 'long', year: 'numeric' });
  102. break;
  103. case 'month':
  104. this.dateText = data.getLocalizedDateTime(locale, firstParsedDatetime, (_d = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) !== null && _d !== void 0 ? _d : { month: 'long' });
  105. break;
  106. case 'year':
  107. this.dateText = data.getLocalizedDateTime(locale, firstParsedDatetime, (_e = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) !== null && _e !== void 0 ? _e : { year: 'numeric' });
  108. break;
  109. }
  110. };
  111. /**
  112. * Waits for the ion-datetime to re-render.
  113. * This is needed in order to correctly position
  114. * a popover relative to the trigger element.
  115. */
  116. this.waitForDatetimeChanges = async () => {
  117. const { datetimeEl } = this;
  118. if (!datetimeEl) {
  119. return Promise.resolve();
  120. }
  121. return new Promise((resolve) => {
  122. helpers.addEventListener(datetimeEl, 'ionRender', resolve, { once: true });
  123. });
  124. };
  125. this.handleDateClick = async (ev) => {
  126. const { datetimeEl, datetimePresentation } = this;
  127. if (!datetimeEl) {
  128. return;
  129. }
  130. let needsPresentationChange = false;
  131. /**
  132. * When clicking the date button,
  133. * we need to make sure that only a date
  134. * picker is displayed. For presentation styles
  135. * that display content other than a date picker,
  136. * we need to update the presentation style.
  137. */
  138. switch (datetimePresentation) {
  139. case 'date-time':
  140. case 'time-date':
  141. const needsChange = datetimeEl.presentation !== 'date';
  142. /**
  143. * The date+time wheel picker
  144. * shows date and time together,
  145. * so do not adjust the presentation
  146. * in that case.
  147. */
  148. if (!datetimeEl.preferWheel && needsChange) {
  149. datetimeEl.presentation = 'date';
  150. needsPresentationChange = true;
  151. }
  152. break;
  153. }
  154. /**
  155. * Track which button was clicked
  156. * so that it can have the correct
  157. * activated styles applied when
  158. * the modal/popover containing
  159. * the datetime is opened.
  160. */
  161. this.selectedButton = 'date';
  162. this.presentOverlay(ev, needsPresentationChange, this.dateTargetEl);
  163. };
  164. this.handleTimeClick = (ev) => {
  165. const { datetimeEl, datetimePresentation } = this;
  166. if (!datetimeEl) {
  167. return;
  168. }
  169. let needsPresentationChange = false;
  170. /**
  171. * When clicking the time button,
  172. * we need to make sure that only a time
  173. * picker is displayed. For presentation styles
  174. * that display content other than a time picker,
  175. * we need to update the presentation style.
  176. */
  177. switch (datetimePresentation) {
  178. case 'date-time':
  179. case 'time-date':
  180. const needsChange = datetimeEl.presentation !== 'time';
  181. if (needsChange) {
  182. datetimeEl.presentation = 'time';
  183. needsPresentationChange = true;
  184. }
  185. break;
  186. }
  187. /**
  188. * Track which button was clicked
  189. * so that it can have the correct
  190. * activated styles applied when
  191. * the modal/popover containing
  192. * the datetime is opened.
  193. */
  194. this.selectedButton = 'time';
  195. this.presentOverlay(ev, needsPresentationChange, this.timeTargetEl);
  196. };
  197. /**
  198. * If the datetime is presented in an
  199. * overlay, the datetime and overlay
  200. * should be appropriately sized.
  201. * These classes provide default sizing values
  202. * that developers can customize.
  203. * The goal is to provide an overlay that is
  204. * reasonably sized with a datetime that
  205. * fills the entire container.
  206. */
  207. this.presentOverlay = async (ev, needsPresentationChange, triggerEl) => {
  208. const { overlayEl } = this;
  209. if (!overlayEl) {
  210. return;
  211. }
  212. if (overlayEl.tagName === 'ION-POPOVER') {
  213. /**
  214. * When the presentation on datetime changes,
  215. * we need to wait for the component to re-render
  216. * otherwise the computed width/height of the
  217. * popover content will be wrong, causing
  218. * the popover to not align with the trigger element.
  219. */
  220. if (needsPresentationChange) {
  221. await this.waitForDatetimeChanges();
  222. }
  223. /**
  224. * We pass the trigger button element
  225. * so that the popover aligns with the individual
  226. * button that was clicked, not the component container.
  227. */
  228. overlayEl.present(Object.assign(Object.assign({}, ev), { detail: {
  229. ionShadowTarget: triggerEl,
  230. } }));
  231. }
  232. else {
  233. overlayEl.present();
  234. }
  235. };
  236. this.datetimePresentation = 'date-time';
  237. this.dateText = undefined;
  238. this.timeText = undefined;
  239. this.datetimeActive = false;
  240. this.selectedButton = undefined;
  241. this.color = 'primary';
  242. this.disabled = false;
  243. this.datetime = undefined;
  244. }
  245. async componentWillLoad() {
  246. const { datetime } = this;
  247. if (!datetime) {
  248. index$1.printIonError('[ion-datetime-button] - An ID associated with an ion-datetime instance is required to function properly.', this.el);
  249. return;
  250. }
  251. const datetimeEl = (this.datetimeEl = document.getElementById(datetime));
  252. if (!datetimeEl) {
  253. index$1.printIonError(`[ion-datetime-button] - No ion-datetime instance found for ID '${datetime}'.`, this.el);
  254. return;
  255. }
  256. /**
  257. * The element reference must be an ion-datetime. Print an error
  258. * if a non-datetime element was provided.
  259. */
  260. if (datetimeEl.tagName !== 'ION-DATETIME') {
  261. index$1.printIonError(`[ion-datetime-button] - Expected an ion-datetime instance for ID '${datetime}' but received '${datetimeEl.tagName.toLowerCase()}' instead.`, datetimeEl);
  262. return;
  263. }
  264. /**
  265. * Since the datetime can be used in any context (overlays, accordion, etc)
  266. * we track when it is visible to determine when it is active.
  267. * This informs which button is highlighted as well as the
  268. * aria-expanded state.
  269. */
  270. const io = new IntersectionObserver((entries) => {
  271. const ev = entries[0];
  272. this.datetimeActive = ev.isIntersecting;
  273. }, {
  274. threshold: 0.01,
  275. });
  276. io.observe(datetimeEl);
  277. /**
  278. * Get a reference to any modal/popover
  279. * the datetime is being used in so we can
  280. * correctly size it when it is presented.
  281. */
  282. const overlayEl = (this.overlayEl = datetimeEl.closest('ion-modal, ion-popover'));
  283. /**
  284. * The .ion-datetime-button-overlay class contains
  285. * styles that allow any modal/popover to be
  286. * sized according to the dimensions of the datetime.
  287. * If developers want a smaller/larger overlay all they need
  288. * to do is change the width/height of the datetime.
  289. * Additionally, this lets us avoid having to set
  290. * explicit widths on each variant of datetime.
  291. */
  292. if (overlayEl) {
  293. overlayEl.classList.add('ion-datetime-button-overlay');
  294. }
  295. helpers.componentOnReady(datetimeEl, () => {
  296. const datetimePresentation = (this.datetimePresentation = datetimeEl.presentation || 'date-time');
  297. /**
  298. * Set the initial display
  299. * in the rendered buttons.
  300. *
  301. * From there, we need to listen
  302. * for ionChange to be emitted
  303. * from datetime so we know when
  304. * to re-render the displayed
  305. * text in the buttons.
  306. */
  307. this.setDateTimeText();
  308. helpers.addEventListener(datetimeEl, 'ionValueChange', this.setDateTimeText);
  309. /**
  310. * Configure the initial selected button
  311. * in the event that the datetime is displayed
  312. * without clicking one of the datetime buttons.
  313. * For example, a datetime could be expanded
  314. * in an accordion. In this case users only
  315. * need to click the accordion header to show
  316. * the datetime.
  317. */
  318. switch (datetimePresentation) {
  319. case 'date-time':
  320. case 'date':
  321. case 'month-year':
  322. case 'month':
  323. case 'year':
  324. this.selectedButton = 'date';
  325. break;
  326. case 'time-date':
  327. case 'time':
  328. this.selectedButton = 'time';
  329. break;
  330. }
  331. });
  332. }
  333. render() {
  334. const { color, dateText, timeText, selectedButton, datetimeActive, disabled } = this;
  335. const mode = ionicGlobal.getIonMode(this);
  336. return (index.h(index.Host, { key: '11d037e6ab061e5116842970760b04850b42f2c7', class: theme.createColorClasses(color, {
  337. [mode]: true,
  338. [`${selectedButton}-active`]: datetimeActive,
  339. ['datetime-button-disabled']: disabled,
  340. }) }, dateText && (index.h("button", { key: '08ecb62da0fcbf7466a1f2403276712a3ff17fbc', class: "ion-activatable", id: "date-button", "aria-expanded": datetimeActive ? 'true' : 'false', onClick: this.handleDateClick, disabled: disabled, part: "native", ref: (el) => (this.dateTargetEl = el) }, index.h("slot", { key: '1c04853d4d23c0f1a594602bde44511c98355644', name: "date-target" }, dateText), mode === 'md' && index.h("ion-ripple-effect", { key: '5fc566cd4bc885bcf983ce99e3dc65d7f485bf9b' }))), timeText && (index.h("button", { key: 'c9c5c34ac338badf8659da22bea5829d62c51169', class: "ion-activatable", id: "time-button", "aria-expanded": datetimeActive ? 'true' : 'false', onClick: this.handleTimeClick, disabled: disabled, part: "native", ref: (el) => (this.timeTargetEl = el) }, index.h("slot", { key: '147a9d2069dbf737f6fc64787823d6d5af5aa653', name: "time-target" }, timeText), mode === 'md' && index.h("ion-ripple-effect", { key: '70a5e25b75ed90ac6bba003468435f67aa9d8f0a' })))));
  341. }
  342. get el() { return index.getElement(this); }
  343. };
  344. DatetimeButton.style = {
  345. ios: IonDatetimeButtonIosStyle0,
  346. md: IonDatetimeButtonMdStyle0
  347. };
  348. exports.ion_datetime_button = DatetimeButton;