@itfin/components 1.2.85 → 1.2.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/package.json +4 -3
  2. package/src/components/customize/PropertiesEditMenu.vue +53 -0
  3. package/src/components/customize/PropertiesItemsMenu.vue +33 -0
  4. package/src/components/customize/PropertiesList.vue +179 -0
  5. package/src/components/customize/PropertiesPopupMenu.vue +46 -0
  6. package/src/components/customize/PropertyInlineEdit.vue +46 -0
  7. package/src/components/customize/index.stories.js +65 -0
  8. package/src/components/dropdown/Dropdown.vue +8 -19
  9. package/src/components/icon/components/type_checkbox.vue +2 -0
  10. package/src/components/icon/components/type_date.vue +2 -0
  11. package/src/components/icon/components/type_email.vue +2 -0
  12. package/src/components/icon/components/type_file.vue +2 -0
  13. package/src/components/icon/components/type_formula.vue +2 -0
  14. package/src/components/icon/components/type_id.vue +2 -0
  15. package/src/components/icon/components/type_multiselect.vue +2 -0
  16. package/src/components/icon/components/type_number.vue +2 -0
  17. package/src/components/icon/components/type_person.vue +2 -0
  18. package/src/components/icon/components/type_phone.vue +2 -0
  19. package/src/components/icon/components/type_relation.vue +2 -0
  20. package/src/components/icon/components/type_select.vue +2 -0
  21. package/src/components/icon/components/type_status.vue +2 -0
  22. package/src/components/icon/components/type_text.vue +2 -0
  23. package/src/components/icon/components/type_time.vue +2 -0
  24. package/src/components/icon/components/type_url.vue +2 -0
  25. package/src/components/icon/components/type_user.vue +2 -0
  26. package/src/components/icon/icons/type_checkbox.svg +1 -0
  27. package/src/components/icon/icons/type_date.svg +1 -0
  28. package/src/components/icon/icons/type_email.svg +1 -0
  29. package/src/components/icon/icons/type_file.svg +1 -0
  30. package/src/components/icon/icons/type_formula.svg +1 -0
  31. package/src/components/icon/icons/type_id.svg +1 -0
  32. package/src/components/icon/icons/type_multiselect.svg +1 -0
  33. package/src/components/icon/icons/type_number.svg +1 -0
  34. package/src/components/icon/icons/type_person.svg +1 -0
  35. package/src/components/icon/icons/type_phone.svg +1 -0
  36. package/src/components/icon/icons/type_relation.svg +1 -0
  37. package/src/components/icon/icons/type_select.svg +1 -0
  38. package/src/components/icon/icons/type_status.svg +1 -0
  39. package/src/components/icon/icons/type_text.svg +1 -0
  40. package/src/components/icon/icons/type_time.svg +1 -0
  41. package/src/components/icon/icons/type_url.svg +1 -0
  42. package/src/components/icon/icons/type_user.svg +1 -0
  43. package/src/components/icon/icons.js +295 -278
  44. package/src/components/modal/DeleteConfirmModal.vue +78 -0
  45. package/src/components/modal/ItemEditor.vue +217 -0
  46. package/src/components/modal/Modal.vue +15 -13
  47. package/src/components/modal/index.stories.js +15 -10
  48. package/src/components/popover/ConfirmPopover.vue +1 -1
  49. package/src/components/popover/DeleteConfirmPopover.vue +39 -0
  50. package/src/components/popover/IconPopover.vue +136 -0
  51. package/src/components/popover/SelectPopover.vue +127 -0
  52. package/src/components/popover/index.stories.js +26 -10
  53. package/src/components/sortable/AutoScroll.vue +142 -0
  54. package/src/components/sortable/Sortable.scss +93 -0
  55. package/src/components/sortable/Sortable.vue +399 -0
  56. package/src/components/sortable/sortable-item-list/axis.js +9 -0
  57. package/src/components/sortable/sortable-item-list/mocked-sortable-item-list.js +415 -0
  58. package/src/components/sortable/sortable-item-list/original-sortable-item-list.js +105 -0
  59. package/src/components/sortable/utils/event-outside.js +30 -0
  60. package/src/components/sortable/utils/get-relative-position.js +41 -0
  61. package/src/components/sortable/utils/sort-item-list.js +13 -0
  62. package/src/components/sortable/utils/stop-event.js +16 -0
  63. package/src/components/sortable/utils/vibrate.js +18 -0
  64. package/src/components/text-field/MoneyField.vue +112 -0
  65. package/src/components/text-field/index.stories.js +53 -0
  66. package/src/directives/tooltip.js +1 -1
  67. package/src/locales/en.js +28 -0
@@ -0,0 +1,415 @@
1
+ import getRelativePosition from '../utils/get-relative-position';
2
+ import { AXIS } from './axis';
3
+
4
+ export const CURSOR_ITEM_OFFSET = {
5
+ x: 5,
6
+ y: 5
7
+ };
8
+
9
+ export const ATTRIBUTE_SKIP_SORTING = 'sortable-skip';
10
+
11
+ /**
12
+ * Logic for cloned original item list to perform sorting
13
+ */
14
+ export default class MockedSortableItemList {
15
+ /**
16
+ * @param {HTMLElement} element
17
+ */
18
+ setContainer (element) {
19
+ this.elContainer = element;
20
+ /**
21
+ * @type {number}
22
+ */
23
+ this.fromPosition = null;
24
+ /**
25
+ * @type {number}
26
+ */
27
+ this.toPosition = null;
28
+ /**
29
+ * @type {string}
30
+ */
31
+ this.axis = null;
32
+ /**
33
+ * @type {HTMLElement}
34
+ */
35
+ this.sourceItem = null;
36
+ /**
37
+ * @type {HTMLElement}
38
+ */
39
+ this.cursorItem = null;
40
+ /**
41
+ * @type {object}
42
+ */
43
+ this.cursorOffset = null;
44
+ }
45
+
46
+ /**
47
+ * @param {string} axis
48
+ */
49
+ setAxis (axis) {
50
+ this.axis = axis;
51
+ }
52
+
53
+ hide () {
54
+ this.elContainer.classList.add('hidden');
55
+ }
56
+
57
+ reset () {
58
+ this.elContainer.classList.remove('hidden');
59
+ while (this.elContainer.hasChildNodes()) {
60
+ this.elContainer.removeChild(this.elContainer.lastChild);
61
+ }
62
+ this.toPosition = null;
63
+ this.fromPosition = null;
64
+ }
65
+
66
+ /**
67
+ * @returns {boolean}
68
+ */
69
+ isSorted () {
70
+ return (
71
+ this.toPosition !== null &&
72
+ this.fromPosition !== null &&
73
+ this.toPosition !== this.fromPosition
74
+ );
75
+ }
76
+
77
+ /**
78
+ * @returns {number}
79
+ */
80
+ getFromPosition () {
81
+ return this.fromPosition;
82
+ }
83
+
84
+ /**
85
+ * @returns {number}
86
+ */
87
+ getToPosition () {
88
+ return this.toPosition;
89
+ }
90
+
91
+ /**
92
+ * @return {HTMLElement}
93
+ */
94
+ getContainer () {
95
+ return this.elContainer;
96
+ }
97
+
98
+ /**
99
+ * @param position
100
+ * @returns {Element}
101
+ */
102
+ getItem (position) {
103
+ return this.elContainer.children[position + 1];
104
+ }
105
+
106
+ getItemCount () {
107
+ return this.elContainer.children.length - 1;
108
+ }
109
+
110
+ /**
111
+ * @param {number} x
112
+ * @param {number} from
113
+ * @returns {number}
114
+ */
115
+ getItemIndexByX (x, from = 0) {
116
+ let i = from;
117
+ let skip = 0;
118
+ while (i + skip < this.getItemCount() - 1) {
119
+ const currentItem = this.getItem(i + skip);
120
+ const nextItem = this.getItem(i + skip + 1);
121
+ if (this.isToSkip(currentItem) || this.isToSkip(nextItem)) {
122
+ skip += 1;
123
+ // eslint-disable-next-line no-continue
124
+ continue;
125
+ } else {
126
+ i += skip;
127
+ skip = 0;
128
+ }
129
+ if (
130
+ currentItem.offsetWidth > nextItem.offsetWidth &&
131
+ currentItem !== this.sourceItem
132
+ ) {
133
+ const keepItemLeft = currentItem.classList.contains('keep-item-left');
134
+ const width = keepItemLeft ? 0 : nextItem.offsetWidth;
135
+ if (currentItem.offsetLeft + width > x) {
136
+ break;
137
+ }
138
+ } else if (nextItem.offsetLeft > x) {
139
+ break;
140
+ }
141
+ if (
142
+ currentItem.offsetLeft + currentItem.offsetWidth / 2 >=
143
+ nextItem.offsetLeft
144
+ ) {
145
+ break;
146
+ }
147
+ i += 1;
148
+ }
149
+ return i;
150
+ }
151
+
152
+ /**
153
+ * @param {number} y
154
+ * @returns {number}
155
+ */
156
+ getItemIndexByY (y) {
157
+ let i = 0;
158
+ let result = 0;
159
+ let skip = 0;
160
+ while (i + skip < this.getItemCount() - 1) {
161
+ const currentItem = this.getItem(i + skip);
162
+ const nextItem = this.getItem(i + skip + 1);
163
+ if (this.isToSkip(currentItem) || this.isToSkip(nextItem)) {
164
+ skip += 1;
165
+ // eslint-disable-next-line no-continue
166
+ continue;
167
+ } else {
168
+ i += skip;
169
+ skip = 0;
170
+ }
171
+ if (
172
+ currentItem.offsetHeight > nextItem.offsetHeight &&
173
+ currentItem !== this.sourceItem
174
+ ) {
175
+ if (currentItem.offsetTop + nextItem.offsetHeight > y) {
176
+ break;
177
+ }
178
+ } else if (nextItem.offsetTop > y) {
179
+ break;
180
+ }
181
+ i += 1;
182
+ // catch next row border
183
+ result =
184
+ currentItem.offsetTop + currentItem.offsetHeight / 2 <
185
+ nextItem.offsetTop
186
+ ? i
187
+ : result;
188
+ }
189
+ while (
190
+ result < this.getItemCount() - 1 &&
191
+ this.isToSkip(this.getItem(result))
192
+ ) {
193
+ result += 1;
194
+ }
195
+ return result;
196
+ }
197
+
198
+ /**
199
+ * @param {number} x
200
+ * @param {number} y
201
+ * @returns {number}
202
+ */
203
+ getItemIndexByXY (x, y) {
204
+ const from = this.getItemIndexByY(y);
205
+ return this.getItemIndexByX(x, from);
206
+ }
207
+
208
+ /**
209
+ * @param {number} fromPosition
210
+ * @param {Event} event
211
+ * @param {string} cursorClass
212
+ * @param {string} cursorSelector
213
+ */
214
+ createCursorItem (fromPosition, event, cursorClass, cursorSelector) {
215
+ this.fromPosition = fromPosition;
216
+ this.toPosition = fromPosition;
217
+ this.sourceItem = this.elContainer.children[fromPosition];
218
+ this.cursorOffset = getRelativePosition(event, this.sourceItem);
219
+ this.cursorItem = this.sourceItem.cloneNode(true);
220
+ const style = getComputedStyle(this.sourceItem);
221
+ if (style.display === 'block') {
222
+ this.cursorItem.style.setProperty(
223
+ 'width',
224
+ `${this.sourceItem.clientWidth}px`
225
+ );
226
+ this.cursorItem.style.setProperty('top', '0');
227
+ this.cursorItem.style.setProperty('margin-top', '0');
228
+ }
229
+ this.sourceItem.classList.add('up-source-item');
230
+ this.cursorItem.classList.add('up-cursor-item');
231
+ const cursorItemPresentation = cursorSelector
232
+ ? this.cursorItem.querySelector(cursorSelector)
233
+ : this.cursorItem;
234
+ cursorItemPresentation.classList.add('up-cursor-item-presentation');
235
+ if (cursorClass) {
236
+ cursorItemPresentation.classList.add(cursorClass);
237
+ }
238
+ this.sourceItem.setAttribute('aria-hidden', 'true');
239
+ this.cursorItem.setAttribute('aria-role', 'button');
240
+ this.cursorItem.setAttribute('aria-pressed', 'true');
241
+ this.cursorItem.setAttribute('tabindex', '0');
242
+ this.elContainer.prepend(this.cursorItem);
243
+ if (event.type.indexOf('key') === 0) {
244
+ this.applyMovement();
245
+ this.cursorItem.focus();
246
+ this.stickCursorItemToSourceItem();
247
+ } else {
248
+ this.move(event);
249
+ }
250
+ }
251
+
252
+ /**
253
+ * @param {Event} event
254
+ */
255
+ move (event) {
256
+ if (this.getItemCount() > 0) {
257
+ const containerPosition = getRelativePosition(event, this.elContainer);
258
+ let offsetX =
259
+ containerPosition.x - this.cursorOffset.x - containerPosition.pl;
260
+ let offsetY =
261
+ containerPosition.y - this.cursorOffset.y - containerPosition.pt;
262
+ switch (this.axis) {
263
+ case AXIS.x:
264
+ offsetY = 0;
265
+ this.toPosition = this.getItemIndexByX(containerPosition.x);
266
+ break;
267
+ case AXIS.y:
268
+ offsetX = 0;
269
+ this.toPosition = this.getItemIndexByY(containerPosition.y);
270
+ break;
271
+ case AXIS.xy:
272
+ this.toPosition = this.getItemIndexByXY(
273
+ containerPosition.x,
274
+ containerPosition.y
275
+ );
276
+ break;
277
+ default:
278
+ // eslint-disable-next-line no-throw-literal
279
+ throw `Unknown axis was passed '${this.axis}'`;
280
+ }
281
+ this.cursorItem.style.setProperty(
282
+ 'transform',
283
+ `translate(${offsetX + 5}px, ${offsetY + 5}px)`
284
+ );
285
+ this.applyMovement();
286
+ }
287
+ }
288
+
289
+ moveUp () {
290
+ const prevPosition = this.searchPosition(this.toPosition, -1);
291
+ if (prevPosition !== false) {
292
+ this.toPosition = prevPosition;
293
+ this.applyMovement();
294
+ this.stickCursorItemToSourceItem();
295
+ }
296
+ }
297
+
298
+ moveDown () {
299
+ const nextPosition = this.searchPosition(this.toPosition, 1);
300
+ if (nextPosition !== false) {
301
+ this.toPosition = nextPosition;
302
+ this.applyMovement();
303
+ this.stickCursorItemToSourceItem();
304
+ return true;
305
+ }
306
+ return false;
307
+ }
308
+
309
+ searchPosition (position, step) {
310
+ let newPosition = position;
311
+ while (
312
+ newPosition + step >= 0 &&
313
+ newPosition + step < this.getItemCount()
314
+ ) {
315
+ newPosition += step;
316
+ if (!this.isToSkip(this.getItem(newPosition))) {
317
+ return newPosition;
318
+ }
319
+ }
320
+ return false;
321
+ }
322
+
323
+ /**
324
+ * @private
325
+ */
326
+ stickCursorItemToSourceItem () {
327
+ const style = window.getComputedStyle(this.elContainer);
328
+ const x =
329
+ this.axis !== AXIS.y
330
+ ? this.sourceItem.offsetLeft - parseFloat(style.paddingLeft)
331
+ : CURSOR_ITEM_OFFSET.x;
332
+ const y =
333
+ this.axis !== AXIS.x
334
+ ? this.sourceItem.offsetTop - parseFloat(style.paddingTop)
335
+ : CURSOR_ITEM_OFFSET.y;
336
+ this.cursorItem.style.setProperty('transform', `translate(${x}px, ${y}px)`);
337
+ }
338
+
339
+ /**
340
+ * @private
341
+ */
342
+ applyMovement () {
343
+ let targetItem = this.getItem(this.toPosition);
344
+ if (targetItem !== this.sourceItem) {
345
+ if (targetItem && this.toPosition < this.getItemCount() - 1) {
346
+ if (this.toPosition > 0) {
347
+ const previousItem = this.getItem(this.toPosition - 1);
348
+ if (previousItem === this.sourceItem) {
349
+ targetItem = this.getItem(this.toPosition + 1);
350
+ }
351
+ }
352
+ const targetItemBefore = targetItem.getBoundingClientRect();
353
+ const isMoveBack =
354
+ targetItem.offsetTop < this.sourceItem.offsetTop ||
355
+ (targetItem.offsetTop === this.sourceItem.offsetTop &&
356
+ targetItem.offsetLeft < this.sourceItem.offsetLeft);
357
+ this.elContainer.insertBefore(this.sourceItem, targetItem);
358
+ if (isMoveBack) {
359
+ const targetItemAfter = targetItem.getBoundingClientRect();
360
+ /* istanbul ignore next */
361
+ if (
362
+ targetItemBefore.left === targetItemAfter.left &&
363
+ targetItemBefore.top === targetItemAfter.top
364
+ ) {
365
+ targetItem.classList.add('keep-item-left');
366
+ }
367
+ }
368
+ } else {
369
+ this.elContainer.appendChild(this.sourceItem);
370
+ }
371
+ }
372
+ }
373
+
374
+ /**
375
+ * @param {HTMLElement} item
376
+ * @returns {boolean}
377
+ */
378
+ /* eslint class-methods-use-this: "off" */
379
+ isToSkip (item) {
380
+ return (
381
+ item.hasAttribute(ATTRIBUTE_SKIP_SORTING) &&
382
+ [true, 'true', ATTRIBUTE_SKIP_SORTING].includes(item.getAttribute(ATTRIBUTE_SKIP_SORTING))
383
+ );
384
+ }
385
+
386
+ getCursorPageX () {
387
+ if (this.cursorItem) {
388
+ const rect = this.cursorItem.getBoundingClientRect();
389
+ /* istanbul ignore next */
390
+ const scrollLeft = document.documentElement.scrollLeft
391
+ ? document.documentElement.scrollLeft
392
+ : document.body.scrollLeft;
393
+ return rect.left + scrollLeft;
394
+ }
395
+ return 0;
396
+ }
397
+
398
+ getCursorPageY () {
399
+ if (this.cursorItem) {
400
+ const rect = this.cursorItem.getBoundingClientRect();
401
+ /* istanbul ignore next */
402
+ const scrollTop = document.documentElement.scrollTop
403
+ ? document.documentElement.scrollTop
404
+ : document.body.scrollTop;
405
+ return rect.top + scrollTop;
406
+ }
407
+ return 0;
408
+ }
409
+
410
+ finishSorting () {
411
+ this.elContainer.querySelectorAll('.keep-item-left').forEach((item) => {
412
+ item.classList.remove('keep-item-left');
413
+ });
414
+ }
415
+ }
@@ -0,0 +1,105 @@
1
+ const SORTABLE_ATTRIBUTE = 'sortable';
2
+ const SORTABLE_DATA_ATTRIBUTE = 'data-sortable';
3
+ /**
4
+ * Original item list from default slot - clone to specified container,
5
+ * show after sorting, detect if item is sortable
6
+ */
7
+ export default class OriginalSortableItemList {
8
+ constructor () {
9
+ /**
10
+ * @type {HTMLElement}
11
+ */
12
+ this.container = null;
13
+ }
14
+
15
+ /**
16
+ * @param {HTMLElement} element
17
+ */
18
+ setContainer (element) {
19
+ this.container = element;
20
+ }
21
+
22
+ hide () {
23
+ this.container.classList.add('hidden');
24
+ }
25
+
26
+ /**
27
+ * @param {number} positionToFocus
28
+ */
29
+ restore (positionToFocus) {
30
+ this.container.classList.remove('hidden');
31
+ if (positionToFocus !== null) {
32
+ const item = this.container.children.item(positionToFocus);
33
+ const focusedItem =
34
+ item && !item.hasAttribute('tabindex')
35
+ ? item.querySelector('[tabindex]')
36
+ : item;
37
+ if (focusedItem) {
38
+ focusedItem.focus();
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @param {HTMLElement} element
45
+ */
46
+ cloneTo (element) {
47
+ for (let i = 0; i < this.container.children.length; i += 1) {
48
+ const originalElement = this.container.children.item(i);
49
+ const clonedElement = originalElement.cloneNode(true);
50
+ clonedElement.removeAttribute('v-for');
51
+ element.appendChild(clonedElement);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * @param {HTMLElement} target
57
+ * @param {boolean} requireSortableAttribute
58
+ * @returns {boolean}
59
+ */
60
+ isSortable (target, requireSortableAttribute) {
61
+ if (
62
+ (target.tagName === 'A' ||
63
+ target.tagName === 'BUTTON' ||
64
+ target.tagName === 'INPUT') &&
65
+ !requireSortableAttribute
66
+ ) {
67
+ return false;
68
+ }
69
+ if (requireSortableAttribute) {
70
+ const attribute = SORTABLE_ATTRIBUTE;
71
+ if (
72
+ target.getAttribute(attribute) === 'true' ||
73
+ target.getAttribute(attribute) === attribute ||
74
+ target.getAttribute(SORTABLE_DATA_ATTRIBUTE) === 'true'
75
+ ) {
76
+ return true;
77
+ }
78
+ return target.parentElement
79
+ ? this.isSortable(target.parentElement, requireSortableAttribute)
80
+ : false;
81
+ }
82
+ return true;
83
+ }
84
+
85
+ /**
86
+ * @param {HTMLElement} element
87
+ * @returns {number}
88
+ */
89
+ getItemIndexByElement (element) {
90
+ return Array.prototype.indexOf.call(this.container.children, element);
91
+ }
92
+
93
+ /**
94
+ * @param {HTMLElement} element
95
+ * @returns {object|null}
96
+ */
97
+ getSortItem (element) {
98
+ if (element.parentElement === this.container) {
99
+ return element;
100
+ }
101
+ return element.parentElement
102
+ ? this.getSortItem(element.parentElement)
103
+ : null;
104
+ }
105
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * On any event which is not addressed to element
3
+ * @param element
4
+ * @param eventList
5
+ * @param callback
6
+ */
7
+ export function onEventOutside (element, eventList, callback) {
8
+ function handler (event) {
9
+ if (!element.contains(event.target)) {
10
+ return callback(event);
11
+ }
12
+ return null;
13
+ }
14
+ eventList.forEach((event) => {
15
+ document.body.addEventListener(event, handler);
16
+ });
17
+ return () => {
18
+ eventList.forEach((event) => {
19
+ document.body.removeEventListener(event, handler);
20
+ });
21
+ };
22
+ }
23
+
24
+ export function onClickOutside (element, callback) {
25
+ return onEventOutside(element, ['click'], callback);
26
+ }
27
+
28
+ export function onDropOutside (element, callback) {
29
+ return onEventOutside(element, ['mouseup', 'touchend'], callback);
30
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Get `event.target` relative position to `element`. Provide `element` paddings
3
+ * @param {Event} event
4
+ * @param {HTMLElement} element
5
+ * @returns {object}
6
+ */
7
+ export default function getRelativePosition (event, element) {
8
+ const rect = element.getBoundingClientRect();
9
+ /* istanbul ignore next */
10
+ const scrollTop = document.documentElement.scrollTop
11
+ ? document.documentElement.scrollTop
12
+ : document.body.scrollTop;
13
+ /* istanbul ignore next */
14
+ const scrollLeft = document.documentElement.scrollLeft
15
+ ? document.documentElement.scrollLeft
16
+ : document.body.scrollLeft;
17
+ const elementLeft = rect.left + scrollLeft;
18
+ const elementTop = rect.top + scrollTop;
19
+ let x = 0;
20
+ let y = 0;
21
+ if (event.type.indexOf('mouse') === 0) {
22
+ const mouseEvent = event;
23
+ x = mouseEvent.pageX - elementLeft;
24
+ y = mouseEvent.pageY - elementTop;
25
+ } else if (event.type.indexOf('touch') === 0) {
26
+ const touchEvent = event;
27
+ x = touchEvent.targetTouches[0].pageX - elementLeft;
28
+ y = touchEvent.targetTouches[0].pageY - elementTop;
29
+ }
30
+ x = x < 0 ? 0 : x;
31
+ y = y < 0 ? 0 : y;
32
+ x = x > rect.width ? rect.width : x;
33
+ y = y > rect.height ? rect.height : y;
34
+ const style = window.getComputedStyle(element);
35
+ return {
36
+ x,
37
+ y,
38
+ pl: parseFloat(style.paddingLeft),
39
+ pt: parseFloat(style.paddingTop)
40
+ };
41
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Apply sorting from `fromPosition` to `toPosition`
3
+ * @param {object[]} itemList
4
+ * @param {number} fromPosition
5
+ * @param {number}
6
+ * @returns {object[]}
7
+ */
8
+ export default function sortItemList (itemList, fromPosition, toPosition) {
9
+ const items = [...itemList];
10
+ const item = items.splice(fromPosition, 1)[0];
11
+ items.splice(toPosition, 0, item);
12
+ return items;
13
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Stop `event` when `condition` is true
3
+ * @param {Event} event
4
+ * @param {boolean} condition
5
+ * @returns {false|null}
6
+ */
7
+ export default function stopEvent (event, condition = true) {
8
+ if (condition) {
9
+ if (event.cancelable) {
10
+ event.preventDefault();
11
+ }
12
+ event.stopPropagation();
13
+ return false;
14
+ }
15
+ return undefined;
16
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Vibrate when sorting started/stopped
3
+ * @param {number} duration
4
+ * @returns boolean
5
+ */
6
+ export default function vibrate (duration = 50) {
7
+ /* istanbul ignore next */
8
+ if (navigator.vibrate) {
9
+ navigator.vibrate(duration);
10
+ return true;
11
+ }
12
+ /* istanbul ignore next */
13
+ if (navigator.mozVibrate) {
14
+ navigator.mozVibrate(duration);
15
+ return true;
16
+ }
17
+ return false;
18
+ }