UNPKG

11.7 kBJavaScriptView Raw
1/*!
2 * (C) Ionic http://ionicframework.com - MIT License
3 */
4import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client';
5import { b as getIonMode } from './ionic-global.js';
6import { r as raf, g as getElementRoot } from './helpers.js';
7import { a as hapticSelectionStart, b as hapticSelectionChanged, h as hapticSelectionEnd } from './haptic.js';
8import { c as createColorClasses } from './theme.js';
9
10const pickerColumnInternalIosCss = ":host{padding-left:16px;padding-right:16px;padding-top:0px;padding-bottom:0px;height:200px;outline:none;font-size:22px;-webkit-scroll-snap-type:y mandatory;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;overflow-x:hidden;overflow-y:scroll;scrollbar-width:none;text-align:center}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}}:host::-webkit-scrollbar{display:none}:host .picker-item{height:34px;line-height:34px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;scroll-snap-align:center}:host .picker-item-empty{scroll-snap-align:none}:host(.picker-column-active) .picker-item.picker-item-active{color:var(--ion-color-base)}@media (any-hover: hover){:host(:focus){outline:none;background:rgba(var(--ion-color-base-rgb), 0.2)}}";
11
12const pickerColumnInternalMdCss = ":host{padding-left:16px;padding-right:16px;padding-top:0px;padding-bottom:0px;height:200px;outline:none;font-size:22px;-webkit-scroll-snap-type:y mandatory;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;overflow-x:hidden;overflow-y:scroll;scrollbar-width:none;text-align:center}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}}:host::-webkit-scrollbar{display:none}:host .picker-item{height:34px;line-height:34px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;scroll-snap-align:center}:host .picker-item-empty{scroll-snap-align:none}:host(.picker-column-active) .picker-item.picker-item-active{color:var(--ion-color-base)}@media (any-hover: hover){:host(:focus){outline:none;background:rgba(var(--ion-color-base-rgb), 0.2)}}:host .picker-item-active{color:var(--ion-color-base)}";
13
14const PickerColumnInternal = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
15 constructor() {
16 super();
17 this.__registerHost();
18 this.__attachShadow();
19 this.ionChange = createEvent(this, "ionChange", 7);
20 this.hapticsStarted = false;
21 this.isColumnVisible = false;
22 this.isActive = false;
23 /**
24 * A list of options to be displayed in the picker
25 */
26 this.items = [];
27 /**
28 * The color to use from your application's color palette.
29 * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
30 * For more information on colors, see [theming](/docs/theming/basics).
31 */
32 this.color = 'primary';
33 /**
34 * If `true`, tapping the picker will
35 * reveal a number input keyboard that lets
36 * the user type in values for each picker
37 * column. This is useful when working
38 * with time pickers.
39 *
40 * @internal
41 */
42 this.numericInput = false;
43 this.centerPickerItemInView = (target, smooth = true) => {
44 const { el, isColumnVisible } = this;
45 if (isColumnVisible) {
46 // (Vertical offset from parent) - (three empty picker rows) + (half the height of the target to ensure the scroll triggers)
47 const top = target.offsetTop - (3 * target.clientHeight) + (target.clientHeight / 2);
48 if (el.scrollTop !== top) {
49 el.scroll({
50 top,
51 left: 0,
52 behavior: smooth ? 'smooth' : undefined
53 });
54 }
55 }
56 };
57 /**
58 * When ionInputModeChange is emitted, each column
59 * needs to check if it is the one being made available
60 * for text entry.
61 */
62 this.inputModeChange = (ev) => {
63 if (!this.numericInput) {
64 return;
65 }
66 const { useInputMode, inputModeColumn } = ev.detail;
67 /**
68 * If inputModeColumn is undefined then this means
69 * all numericInput columns are being selected.
70 */
71 const isColumnActive = inputModeColumn === undefined || inputModeColumn === this.el;
72 if (!useInputMode || !isColumnActive) {
73 this.isActive = false;
74 return;
75 }
76 this.isActive = true;
77 };
78 /**
79 * When the column scrolls, the component
80 * needs to determine which item is centered
81 * in the view and will emit an ionChange with
82 * the item object.
83 */
84 this.initializeScrollListener = () => {
85 const { el } = this;
86 let timeout;
87 let activeEl = this.activeItem;
88 const scrollCallback = () => {
89 raf(() => {
90 if (timeout) {
91 clearTimeout(timeout);
92 timeout = undefined;
93 }
94 if (!this.hapticsStarted) {
95 hapticSelectionStart();
96 this.hapticsStarted = true;
97 }
98 /**
99 * Select item in the center of the column
100 * which is the month/year that we want to select
101 */
102 const bbox = el.getBoundingClientRect();
103 const centerX = bbox.x + (bbox.width / 2);
104 const centerY = bbox.y + (bbox.height / 2);
105 const activeElement = el.shadowRoot.elementFromPoint(centerX, centerY);
106 if (activeEl !== null) {
107 activeEl.classList.remove(PICKER_COL_ACTIVE);
108 }
109 /**
110 * If we are selecting a new value,
111 * we need to run haptics again.
112 */
113 if (activeElement !== activeEl) {
114 hapticSelectionChanged();
115 }
116 activeEl = activeElement;
117 activeElement.classList.add(PICKER_COL_ACTIVE);
118 timeout = setTimeout(() => {
119 const dataIndex = activeElement.getAttribute('data-index');
120 /**
121 * If no value it is
122 * possible we hit one of the
123 * empty padding columns.
124 */
125 if (dataIndex === null) {
126 return;
127 }
128 const index = parseInt(dataIndex, 10);
129 const selectedItem = this.items[index];
130 if (selectedItem.value !== this.value) {
131 this.value = selectedItem.value;
132 hapticSelectionEnd();
133 this.hapticsStarted = false;
134 }
135 }, 250);
136 });
137 };
138 /**
139 * Wrap this in an raf so that the scroll callback
140 * does not fire when component is initially shown.
141 */
142 raf(() => {
143 el.addEventListener('scroll', scrollCallback);
144 this.destroyScrollListener = () => {
145 el.removeEventListener('scroll', scrollCallback);
146 };
147 });
148 };
149 }
150 valueChange() {
151 if (this.isColumnVisible) {
152 /**
153 * Only scroll the active item into view and emit the value
154 * change, when the picker column is actively visible to the user.
155 */
156 const { items, value } = this;
157 this.scrollActiveItemIntoView();
158 const findItem = items.find(item => item.value === value);
159 if (findItem) {
160 this.ionChange.emit(findItem);
161 }
162 }
163 }
164 /**
165 * Only setup scroll listeners
166 * when the picker is visible, otherwise
167 * the container will have a scroll
168 * height of 0px.
169 */
170 componentWillLoad() {
171 const visibleCallback = (entries) => {
172 var _a;
173 const ev = entries[0];
174 if (ev.isIntersecting) {
175 this.isColumnVisible = true;
176 /**
177 * Because this initial call to scrollActiveItemIntoView has to fire before
178 * the scroll listener is set up, we need to manage the active class manually.
179 */
180 const oldActive = getElementRoot(this.el).querySelector(`.${PICKER_COL_ACTIVE}`);
181 oldActive === null || oldActive === void 0 ? void 0 : oldActive.classList.remove(PICKER_COL_ACTIVE);
182 this.scrollActiveItemIntoView();
183 (_a = this.activeItem) === null || _a === void 0 ? void 0 : _a.classList.add(PICKER_COL_ACTIVE);
184 this.initializeScrollListener();
185 }
186 else {
187 this.isColumnVisible = false;
188 if (this.destroyScrollListener) {
189 this.destroyScrollListener();
190 this.destroyScrollListener = undefined;
191 }
192 }
193 };
194 new IntersectionObserver(visibleCallback, { threshold: 0.01 }).observe(this.el);
195 const parentEl = this.el.closest('ion-picker-internal');
196 if (parentEl !== null) {
197 parentEl.addEventListener('ionInputModeChange', (ev) => this.inputModeChange(ev));
198 }
199 }
200 componentDidRender() {
201 var _a;
202 const { activeItem, items, isColumnVisible, value } = this;
203 if (isColumnVisible) {
204 if (activeItem) {
205 this.scrollActiveItemIntoView();
206 }
207 else if (((_a = items[0]) === null || _a === void 0 ? void 0 : _a.value) !== value) {
208 /**
209 * If the picker column does not have an active item and the current value
210 * does not match the first item in the picker column, that means
211 * the value is out of bounds. In this case, we assign the value to the
212 * first item to match the scroll position of the column.
213 *
214 */
215 this.value = items[0].value;
216 }
217 }
218 }
219 /** @internal */
220 async scrollActiveItemIntoView() {
221 const activeEl = this.activeItem;
222 if (activeEl) {
223 this.centerPickerItemInView(activeEl, false);
224 }
225 }
226 get activeItem() {
227 return getElementRoot(this.el).querySelector(`.picker-item[data-value="${this.value}"]`);
228 }
229 render() {
230 const { items, color, isActive, numericInput } = this;
231 const mode = getIonMode(this);
232 return (h(Host, { tabindex: 0, class: createColorClasses(color, {
233 [mode]: true,
234 ['picker-column-active']: isActive,
235 ['picker-column-numeric-input']: numericInput
236 }) }, h("div", { class: "picker-item picker-item-empty" }, "\u00A0"), h("div", { class: "picker-item picker-item-empty" }, "\u00A0"), h("div", { class: "picker-item picker-item-empty" }, "\u00A0"), items.map((item, index) => {
237 return (h("div", { class: "picker-item", "data-value": item.value, "data-index": index, onClick: (ev) => {
238 this.centerPickerItemInView(ev.target);
239 } }, item.text));
240 }), h("div", { class: "picker-item picker-item-empty" }, "\u00A0"), h("div", { class: "picker-item picker-item-empty" }, "\u00A0"), h("div", { class: "picker-item picker-item-empty" }, "\u00A0")));
241 }
242 get el() { return this; }
243 static get watchers() { return {
244 "value": ["valueChange"]
245 }; }
246 static get style() { return {
247 ios: pickerColumnInternalIosCss,
248 md: pickerColumnInternalMdCss
249 }; }
250}, [33, "ion-picker-column-internal", {
251 "items": [16],
252 "value": [1032],
253 "color": [513],
254 "numericInput": [4, "numeric-input"],
255 "isActive": [32],
256 "scrollActiveItemIntoView": [64]
257 }]);
258const PICKER_COL_ACTIVE = 'picker-item-active';
259function defineCustomElement() {
260 if (typeof customElements === "undefined") {
261 return;
262 }
263 const components = ["ion-picker-column-internal"];
264 components.forEach(tagName => { switch (tagName) {
265 case "ion-picker-column-internal":
266 if (!customElements.get(tagName)) {
267 customElements.define(tagName, PickerColumnInternal);
268 }
269 break;
270 } });
271}
272
273export { PickerColumnInternal as P, defineCustomElement as d };