UNPKG

10.2 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 { c as componentOnReady } from './helpers.js';
7import { a as hapticSelectionStart, b as hapticSelectionChanged, h as hapticSelectionEnd } from './haptic.js';
8
9const reorderGroupCss = ".reorder-list-active>*{-webkit-transition:-webkit-transform 300ms;transition:-webkit-transform 300ms;transition:transform 300ms;transition:transform 300ms, -webkit-transform 300ms;will-change:transform}.reorder-enabled{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.reorder-enabled ion-reorder{display:block;cursor:-webkit-grab;cursor:grab;pointer-events:all;-ms-touch-action:none;touch-action:none}.reorder-selected,.reorder-selected ion-reorder{cursor:-webkit-grabbing;cursor:grabbing}.reorder-selected{position:relative;-webkit-transition:none !important;transition:none !important;-webkit-box-shadow:0 0 10px rgba(0, 0, 0, 0.4);box-shadow:0 0 10px rgba(0, 0, 0, 0.4);opacity:0.8;z-index:100}.reorder-visible ion-reorder .reorder-icon{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}";
10
11const ReorderGroup = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
12 constructor() {
13 super();
14 this.__registerHost();
15 this.ionItemReorder = createEvent(this, "ionItemReorder", 7);
16 this.lastToIndex = -1;
17 this.cachedHeights = [];
18 this.scrollElTop = 0;
19 this.scrollElBottom = 0;
20 this.scrollElInitial = 0;
21 this.containerTop = 0;
22 this.containerBottom = 0;
23 this.state = 0 /* Idle */;
24 /**
25 * If `true`, the reorder will be hidden.
26 */
27 this.disabled = true;
28 }
29 disabledChanged() {
30 if (this.gesture) {
31 this.gesture.enable(!this.disabled);
32 }
33 }
34 async connectedCallback() {
35 const contentEl = this.el.closest('ion-content');
36 if (contentEl) {
37 await new Promise(resolve => componentOnReady(contentEl, resolve));
38 this.scrollEl = await contentEl.getScrollElement();
39 }
40 this.gesture = (await import('./index2.js')).createGesture({
41 el: this.el,
42 gestureName: 'reorder',
43 gesturePriority: 110,
44 threshold: 0,
45 direction: 'y',
46 passive: false,
47 canStart: detail => this.canStart(detail),
48 onStart: ev => this.onStart(ev),
49 onMove: ev => this.onMove(ev),
50 onEnd: () => this.onEnd(),
51 });
52 this.disabledChanged();
53 }
54 disconnectedCallback() {
55 this.onEnd();
56 if (this.gesture) {
57 this.gesture.destroy();
58 this.gesture = undefined;
59 }
60 }
61 /**
62 * Completes the reorder operation. Must be called by the `ionItemReorder` event.
63 *
64 * If a list of items is passed, the list will be reordered and returned in the
65 * proper order.
66 *
67 * If no parameters are passed or if `true` is passed in, the reorder will complete
68 * and the item will remain in the position it was dragged to. If `false` is passed,
69 * the reorder will complete and the item will bounce back to its original position.
70 *
71 * @param listOrReorder A list of items to be sorted and returned in the new order or a
72 * boolean of whether or not the reorder should reposition the item.
73 */
74 complete(listOrReorder) {
75 return Promise.resolve(this.completeSync(listOrReorder));
76 }
77 canStart(ev) {
78 if (this.selectedItemEl || this.state !== 0 /* Idle */) {
79 return false;
80 }
81 const target = ev.event.target;
82 const reorderEl = target.closest('ion-reorder');
83 if (!reorderEl) {
84 return false;
85 }
86 const item = findReorderItem(reorderEl, this.el);
87 if (!item) {
88 return false;
89 }
90 ev.data = item;
91 return true;
92 }
93 onStart(ev) {
94 ev.event.preventDefault();
95 const item = this.selectedItemEl = ev.data;
96 const heights = this.cachedHeights;
97 heights.length = 0;
98 const el = this.el;
99 const children = el.children;
100 if (!children || children.length === 0) {
101 return;
102 }
103 let sum = 0;
104 for (let i = 0; i < children.length; i++) {
105 const child = children[i];
106 sum += child.offsetHeight;
107 heights.push(sum);
108 child.$ionIndex = i;
109 }
110 const box = el.getBoundingClientRect();
111 this.containerTop = box.top;
112 this.containerBottom = box.bottom;
113 if (this.scrollEl) {
114 const scrollBox = this.scrollEl.getBoundingClientRect();
115 this.scrollElInitial = this.scrollEl.scrollTop;
116 this.scrollElTop = scrollBox.top + AUTO_SCROLL_MARGIN;
117 this.scrollElBottom = scrollBox.bottom - AUTO_SCROLL_MARGIN;
118 }
119 else {
120 this.scrollElInitial = 0;
121 this.scrollElTop = 0;
122 this.scrollElBottom = 0;
123 }
124 this.lastToIndex = indexForItem(item);
125 this.selectedItemHeight = item.offsetHeight;
126 this.state = 1 /* Active */;
127 item.classList.add(ITEM_REORDER_SELECTED);
128 hapticSelectionStart();
129 }
130 onMove(ev) {
131 const selectedItem = this.selectedItemEl;
132 if (!selectedItem) {
133 return;
134 }
135 // Scroll if we reach the scroll margins
136 const scroll = this.autoscroll(ev.currentY);
137 // // Get coordinate
138 const top = this.containerTop - scroll;
139 const bottom = this.containerBottom - scroll;
140 const currentY = Math.max(top, Math.min(ev.currentY, bottom));
141 const deltaY = scroll + currentY - ev.startY;
142 const normalizedY = currentY - top;
143 const toIndex = this.itemIndexForTop(normalizedY);
144 if (toIndex !== this.lastToIndex) {
145 const fromIndex = indexForItem(selectedItem);
146 this.lastToIndex = toIndex;
147 hapticSelectionChanged();
148 this.reorderMove(fromIndex, toIndex);
149 }
150 // Update selected item position
151 selectedItem.style.transform = `translateY(${deltaY}px)`;
152 }
153 onEnd() {
154 const selectedItemEl = this.selectedItemEl;
155 this.state = 2 /* Complete */;
156 if (!selectedItemEl) {
157 this.state = 0 /* Idle */;
158 return;
159 }
160 const toIndex = this.lastToIndex;
161 const fromIndex = indexForItem(selectedItemEl);
162 if (toIndex === fromIndex) {
163 this.completeSync();
164 }
165 else {
166 this.ionItemReorder.emit({
167 from: fromIndex,
168 to: toIndex,
169 complete: this.completeSync.bind(this)
170 });
171 }
172 hapticSelectionEnd();
173 }
174 completeSync(listOrReorder) {
175 const selectedItemEl = this.selectedItemEl;
176 if (selectedItemEl && this.state === 2 /* Complete */) {
177 const children = this.el.children;
178 const len = children.length;
179 const toIndex = this.lastToIndex;
180 const fromIndex = indexForItem(selectedItemEl);
181 if (toIndex !== fromIndex && (listOrReorder === undefined || listOrReorder === true)) {
182 const ref = (fromIndex < toIndex)
183 ? children[toIndex + 1]
184 : children[toIndex];
185 this.el.insertBefore(selectedItemEl, ref);
186 }
187 if (Array.isArray(listOrReorder)) {
188 listOrReorder = reorderArray(listOrReorder, fromIndex, toIndex);
189 }
190 for (let i = 0; i < len; i++) {
191 children[i].style['transform'] = '';
192 }
193 selectedItemEl.style.transition = '';
194 selectedItemEl.classList.remove(ITEM_REORDER_SELECTED);
195 this.selectedItemEl = undefined;
196 this.state = 0 /* Idle */;
197 }
198 return listOrReorder;
199 }
200 itemIndexForTop(deltaY) {
201 const heights = this.cachedHeights;
202 // TODO: since heights is a sorted array of integers, we can do
203 // speed up the search using binary search. Remember that linear-search is still
204 // faster than binary-search for small arrays (<64) due CPU branch misprediction.
205 for (let i = 0; i < heights.length; i++) {
206 if (heights[i] > deltaY) {
207 return i;
208 }
209 }
210 return heights.length - 1;
211 }
212 /********* DOM WRITE ********* */
213 reorderMove(fromIndex, toIndex) {
214 const itemHeight = this.selectedItemHeight;
215 const children = this.el.children;
216 for (let i = 0; i < children.length; i++) {
217 const style = children[i].style;
218 let value = '';
219 if (i > fromIndex && i <= toIndex) {
220 value = `translateY(${-itemHeight}px)`;
221 }
222 else if (i < fromIndex && i >= toIndex) {
223 value = `translateY(${itemHeight}px)`;
224 }
225 style['transform'] = value;
226 }
227 }
228 autoscroll(posY) {
229 if (!this.scrollEl) {
230 return 0;
231 }
232 let amount = 0;
233 if (posY < this.scrollElTop) {
234 amount = -SCROLL_JUMP;
235 }
236 else if (posY > this.scrollElBottom) {
237 amount = SCROLL_JUMP;
238 }
239 if (amount !== 0) {
240 this.scrollEl.scrollBy(0, amount);
241 }
242 return this.scrollEl.scrollTop - this.scrollElInitial;
243 }
244 render() {
245 const mode = getIonMode(this);
246 return (h(Host, { class: {
247 [mode]: true,
248 'reorder-enabled': !this.disabled,
249 'reorder-list-active': this.state !== 0 /* Idle */,
250 } }));
251 }
252 get el() { return this; }
253 static get watchers() { return {
254 "disabled": ["disabledChanged"]
255 }; }
256 static get style() { return reorderGroupCss; }
257}, [0, "ion-reorder-group", {
258 "disabled": [4],
259 "state": [32],
260 "complete": [64]
261 }]);
262const indexForItem = (element) => {
263 return element['$ionIndex'];
264};
265const findReorderItem = (node, container) => {
266 let parent;
267 while (node) {
268 parent = node.parentElement;
269 if (parent === container) {
270 return node;
271 }
272 node = parent;
273 }
274 return undefined;
275};
276const AUTO_SCROLL_MARGIN = 60;
277const SCROLL_JUMP = 10;
278const ITEM_REORDER_SELECTED = 'reorder-selected';
279const reorderArray = (array, from, to) => {
280 const element = array[from];
281 array.splice(from, 1);
282 array.splice(to, 0, element);
283 return array.slice();
284};
285function defineCustomElement$1() {
286 if (typeof customElements === "undefined") {
287 return;
288 }
289 const components = ["ion-reorder-group"];
290 components.forEach(tagName => { switch (tagName) {
291 case "ion-reorder-group":
292 if (!customElements.get(tagName)) {
293 customElements.define(tagName, ReorderGroup);
294 }
295 break;
296 } });
297}
298
299const IonReorderGroup = ReorderGroup;
300const defineCustomElement = defineCustomElement$1;
301
302export { IonReorderGroup, defineCustomElement };