@maas/vue-equipment 0.14.4 → 0.15.1

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@maas/vue-equipment/nuxt",
3
3
  "configKey": "vueEquipment",
4
- "version": "0.14.3"
4
+ "version": "0.15.0"
5
5
  }
@@ -24,13 +24,13 @@
24
24
  <div
25
25
  v-show="innerActive"
26
26
  class="magic-drawer__backdrop"
27
- @click.self="close"
27
+ @click.self="closeIfAllowed"
28
28
  >
29
29
  <slot name="backdrop" />
30
30
  </div>
31
31
  </transition>
32
32
 
33
- <div class="magic-drawer__wrapper">
33
+ <div class="magic-drawer__wrapper" ref="wrapperRef">
34
34
  <transition
35
35
  :name="contentTransition"
36
36
  @before-leave="onBeforeLeave"
@@ -40,20 +40,21 @@
40
40
  @enter="onEnter"
41
41
  @after-enter="onAfterEnter"
42
42
  >
43
- <div
44
- ref="elRef"
45
- v-show="innerActive"
46
- class="magic-drawer__content"
47
- @pointerdown="onPointerdown"
48
- :style="style"
49
- >
50
- <component
51
- v-if="component"
52
- v-bind="props"
53
- :is="component"
54
- @close="close"
55
- />
56
- <slot v-else />
43
+ <div v-show="innerActive" class="magic-drawer__content">
44
+ <div
45
+ ref="elRef"
46
+ class="magic-drawer__drag"
47
+ :style="style"
48
+ @pointerdown="onPointerdown"
49
+ >
50
+ <component
51
+ v-if="component"
52
+ v-bind="props"
53
+ :is="component"
54
+ @close="closeIfAllowed"
55
+ />
56
+ <slot v-else />
57
+ </div>
57
58
  </div>
58
59
  </transition>
59
60
  </div>
@@ -94,9 +95,9 @@ import '@maas/vue-equipment/utils/css/animations/slide-rtl-out.css'
94
95
  import '@maas/vue-equipment/utils/css/animations/slide-ttb-out.css'
95
96
  import '@maas/vue-equipment/utils/css/animations/slide-btt-out.css'
96
97
 
97
- // Prevent keys array from being merged with default
98
+ // Prevent deep merge of certain options
98
99
  const customDefu = createDefu((obj, key, value) => {
99
- if (key === 'keys') {
100
+ if (key === 'keys' || key === 'snapPoints') {
100
101
  obj[key] = value
101
102
  return true
102
103
  }
@@ -116,6 +117,7 @@ const props = withDefaults(defineProps<MagicDrawerProps>(), {
116
117
 
117
118
  const elRef = ref<HTMLDivElement | undefined>(undefined)
118
119
  const drawerRef = ref<HTMLElement | undefined>(undefined)
120
+ const wrapperRef = ref<HTMLDivElement | undefined>(undefined)
119
121
  const drawerApi = useDrawerApi(props.id, { focusTarget: drawerRef })
120
122
 
121
123
  const mappedOptions: typeof defaultOptions = customDefu(
@@ -124,7 +126,7 @@ const mappedOptions: typeof defaultOptions = customDefu(
124
126
  )
125
127
 
126
128
  const overshoot = ref(0)
127
- const { position, threshold } = mappedOptions
129
+ const { position, threshold, snapPoints, snapPoint, canClose } = mappedOptions
128
130
 
129
131
  const {
130
132
  isActive,
@@ -139,10 +141,14 @@ const {
139
141
  } = drawerApi
140
142
 
141
143
  const { onPointerdown, dragging, style } = useDrawerDrag({
144
+ elRef,
145
+ wrapperRef,
142
146
  position,
143
147
  threshold,
144
148
  overshoot,
145
- elRef,
149
+ snapPoints,
150
+ snapPoint,
151
+ canClose,
146
152
  close,
147
153
  })
148
154
 
@@ -193,17 +199,7 @@ const contentTransition = computed(() => {
193
199
  : mappedOptions.transitions?.content
194
200
  })
195
201
 
196
- // Handle state
197
- async function onOpen() {
198
- wrapperActive.value = true
199
- await nextTick()
200
- innerActive.value = true
201
- }
202
-
203
- function onClose() {
204
- innerActive.value = false
205
- }
206
-
202
+ // Private functions
207
203
  function convertToPixels(value: string) {
208
204
  const regex = /^(\d*\.?\d+)\s*(rem|px)$/
209
205
 
@@ -229,6 +225,23 @@ function convertToPixels(value: string) {
229
225
  }
230
226
  }
231
227
 
228
+ async function onOpen() {
229
+ wrapperActive.value = true
230
+ await nextTick()
231
+ innerActive.value = true
232
+ }
233
+
234
+ function onClose() {
235
+ innerActive.value = false
236
+ }
237
+
238
+ // Public functions
239
+ function closeIfAllowed() {
240
+ if (canClose) {
241
+ close()
242
+ }
243
+ }
244
+
232
245
  function saveOvershoot() {
233
246
  const element = unrefElement(drawerRef)
234
247
  const overshootVar = getComputedStyle(element!).getPropertyValue(
@@ -238,7 +251,8 @@ function saveOvershoot() {
238
251
  overshoot.value = convertToPixels(overshootVar) || 0
239
252
  }
240
253
 
241
- if (mappedOptions.keys) {
254
+ // Lifecycle hooks and listeners
255
+ if (mappedOptions.keys && canClose) {
242
256
  for (const key of mappedOptions.keys) {
243
257
  onKeyStroke(key, (e) => {
244
258
  e.preventDefault()
@@ -260,7 +274,8 @@ watch(innerActive, () => {
260
274
  saveOvershoot()
261
275
  })
262
276
 
263
- onBeforeMount(() => {
277
+ onBeforeMount(async () => {
278
+ // Force open
264
279
  if (mappedOptions.beforeMount.open) {
265
280
  open()
266
281
  }
@@ -273,5 +288,5 @@ onBeforeUnmount(() => {
273
288
  </script>
274
289
 
275
290
  <style>
276
- :root{--magic-drawer-z-index:999;--magic-drawer-justify-content:center;--magic-drawer-align-items:flex-end;--magic-drawer-backdrop-color:rgba(0,0,0,.5);--magic-drawer-backdrop-filter:unset;--magic-drawer-content-overflow-y:auto;--magic-drawer-handle-wrapper-height:2rem;--magic-drawer-handle-width:3rem;--magic-drawer-handle-height:0.375rem;--magic-drawer-handle-color:#d4d4d8;--magic-drawer-handle-border-radius:0.25rem;--magic-drawer-enter-animation:slide-btt-in 300ms ease;--magic-drawer-leave-animation:slide-btt-out 300ms ease;--magic-drawer-drag-overshoot:4rem;--magic-drawer-drag-overshoot-x:0;--magic-drawer-drag-overshoot-y:0}.magic-drawer{align-items:var(--magic-drawer-align-items);background:transparent;border:none;color:inherit;display:flex;height:100%;inset:0;justify-content:var(--magic-drawer-justify-content);padding:0;position:fixed;width:100%;z-index:var(--magic-drawer-z-index)}.magic-drawer.-bottom{--magic-drawer-drag-overshoot-y:var(--magic-drawer-drag-overshoot)}.magic-drawer.-top{--magic-drawer-enter-animation:slide-ttb-in 300ms ease;--magic-drawer-leave-animation:slide-ttb-out 300ms ease;--magic-drawer-align-items:flex-start;--magic-drawer-drag-overshoot-y:calc(var(--magic-drawer-drag-overshoot)*-1)}.magic-drawer.-right{--magic-drawer-enter-animation:slide-rtl-in 300ms ease;--magic-drawer-leave-animation:slide-rtl-out 300ms ease;--magic-drawer-align-items:center;--magic-drawer-justify-content:flex-end;--magic-drawer-drag-overshoot-x:var(--magic-drawer-drag-overshoot)}.magic-drawer.-left{--magic-drawer-enter-animation:slide-ltr-in 300ms ease;--magic-drawer-leave-animation:slide-ltr-out 300ms ease;--magic-drawer-align-items:center;--magic-drawer-justify-content:flex-start;--magic-drawer-drag-overshoot-x:calc(var(--magic-drawer-drag-overshoot)*-1)}.magic-drawer__wrapper{transform:translate(var(--magic-drawer-drag-overshoot-x),var(--magic-drawer-drag-overshoot-y));width:100%}.magic-drawer__content{-webkit-overflow-scrolling:touch;align-items:var(--magic-drawer-align-items);cursor:grab;display:flex;justify-content:var(--magic-drawer-justify-content);max-height:100%;overflow-y:var(--magic-drawer-content-overflow-y);position:relative;scroll-behavior:smooth;width:100%}.magic-drawer.-dragging .magic-drawer__content{cursor:grabbing}.magic-drawer__backdrop{-webkit-backdrop-filter:var(--magic-drawer-backdrop-filter);backdrop-filter:var(--magic-drawer-backdrop-filter);background-color:var(--magic-drawer-backdrop-color);bottom:0;height:100%;left:0;position:fixed;right:0;top:0;width:100%;z-index:-1}.magic-drawer--content-enter-active{animation:var(--magic-drawer-enter-animation)}.magic-drawer--content-leave-active{animation:var(--magic-drawer-leave-animation)}.magic-drawer--backdrop-enter-active{animation:fade-in .3s ease}.magic-drawer--backdrop-leave-active{animation:fade-out .3s ease}
291
+ :root{--magic-drawer-height:75svh;--magic-drawer-z-index:999;--magic-drawer-justify-content:center;--magic-drawer-align-items:flex-end;--magic-drawer-backdrop-color:rgba(0,0,0,.5);--magic-drawer-backdrop-filter:unset;--magic-drawer-content-overflow-y:auto;--magic-drawer-handle-wrapper-height:2rem;--magic-drawer-handle-width:3rem;--magic-drawer-handle-height:0.375rem;--magic-drawer-handle-color:#d4d4d8;--magic-drawer-handle-border-radius:0.25rem;--magic-drawer-enter-animation:slide-btt-in 300ms ease;--magic-drawer-leave-animation:slide-btt-out 300ms ease;--magic-drawer-drag-overshoot:4rem;--magic-drawer-drag-overshoot-x:0;--magic-drawer-drag-overshoot-y:0}.magic-drawer{align-items:var(--magic-drawer-align-items);background:transparent;border:none;color:inherit;display:flex;height:100%;inset:0;justify-content:var(--magic-drawer-justify-content);padding:0;position:fixed;width:100%;z-index:var(--magic-drawer-z-index)}.magic-drawer.-bottom{--magic-drawer-drag-overshoot-y:var(--magic-drawer-drag-overshoot)}.magic-drawer.-top{--magic-drawer-enter-animation:slide-ttb-in 300ms ease;--magic-drawer-leave-animation:slide-ttb-out 300ms ease;--magic-drawer-align-items:flex-start;--magic-drawer-drag-overshoot-y:calc(var(--magic-drawer-drag-overshoot)*-1)}.magic-drawer.-right{--magic-drawer-enter-animation:slide-rtl-in 300ms ease;--magic-drawer-leave-animation:slide-rtl-out 300ms ease;--magic-drawer-align-items:center;--magic-drawer-justify-content:flex-end;--magic-drawer-drag-overshoot-x:var(--magic-drawer-drag-overshoot)}.magic-drawer.-left{--magic-drawer-enter-animation:slide-ltr-in 300ms ease;--magic-drawer-leave-animation:slide-ltr-out 300ms ease;--magic-drawer-align-items:center;--magic-drawer-justify-content:flex-start;--magic-drawer-drag-overshoot-x:calc(var(--magic-drawer-drag-overshoot)*-1)}.magic-drawer__wrapper{height:calc(var(--magic-drawer-height) + var(--magic-drawer-drag-overshoot));pointer-events:none;transform:translate(var(--magic-drawer-drag-overshoot-x),var(--magic-drawer-drag-overshoot-y));width:100%}.magic-drawer__content{height:100%;max-height:100%;position:relative;width:100%}.magic-drawer__drag{-webkit-overflow-scrolling:touch;align-items:var(--magic-drawer-align-items);cursor:grab;display:flex;height:100%;justify-content:var(--magic-drawer-justify-content);overflow-y:var(--magic-drawer-content-overflow-y);pointer-events:auto;position:relative;scroll-behavior:smooth;width:100%}.magic-drawer.-dragging .magic-drawer__content{cursor:grabbing}.magic-drawer__backdrop{-webkit-backdrop-filter:var(--magic-drawer-backdrop-filter);backdrop-filter:var(--magic-drawer-backdrop-filter);background-color:var(--magic-drawer-backdrop-color);bottom:0;height:100%;left:0;position:fixed;right:0;top:0;width:100%;z-index:-1}.magic-drawer--content-enter-active{animation:var(--magic-drawer-enter-animation)}.magic-drawer--content-leave-active{animation:var(--magic-drawer-leave-animation)}.magic-drawer--backdrop-enter-active{animation:fade-in .3s ease}.magic-drawer--backdrop-leave-active{animation:fade-out .3s ease}
277
292
  </style>
@@ -2,13 +2,19 @@ import { type MaybeRef } from 'vue';
2
2
  import { type DefaultOptions } from '../../utils/defaultOptions.js';
3
3
  type UseDrawerDragArgs = {
4
4
  elRef: MaybeRef<HTMLDivElement | undefined>;
5
+ wrapperRef: MaybeRef<HTMLDivElement | undefined>;
5
6
  position: MaybeRef<DefaultOptions['position']>;
6
7
  threshold: MaybeRef<DefaultOptions['threshold']>;
8
+ snapPoints: MaybeRef<DefaultOptions['snapPoints']>;
9
+ snapPoint: MaybeRef<DefaultOptions['snapPoint']>;
10
+ canClose: MaybeRef<DefaultOptions['canClose']>;
7
11
  overshoot: MaybeRef<number>;
8
12
  close: () => void;
9
13
  };
10
14
  export declare function useDrawerDrag(args: UseDrawerDragArgs): {
11
15
  style: import("vue").ComputedRef<string>;
16
+ draggedX: import("vue").Ref<number>;
17
+ draggedY: import("vue").Ref<number>;
12
18
  dragging: import("vue").Ref<boolean>;
13
19
  onPointerdown: (e: PointerEvent) => void;
14
20
  };
@@ -4,75 +4,176 @@ import {
4
4
  onMounted,
5
5
  watch,
6
6
  onBeforeUnmount,
7
- toValue
7
+ toValue,
8
+ nextTick
8
9
  } from "vue";
9
10
  import { useEventListener, unrefElement } from "@vueuse/core";
10
11
  import { interpolate } from "@maas/vue-equipment/utils";
11
12
  import { useDrawerEmitter } from "../useDrawerEmitter.mjs";
13
+ import { useDrawerSnap } from "./useDrawerSnap.mjs";
12
14
  export function useDrawerDrag(args) {
13
- const { elRef, position, overshoot, threshold, close } = args;
15
+ const {
16
+ elRef,
17
+ wrapperRef,
18
+ snapPoints,
19
+ position,
20
+ overshoot,
21
+ threshold,
22
+ snapPoint,
23
+ canClose,
24
+ close
25
+ } = args;
26
+ const elRect = ref(void 0);
27
+ const wrapperRect = ref(void 0);
28
+ const { findClosestSnapPoint, mapSnapPoint, drawerHeight, drawerWidth } = useDrawerSnap({
29
+ wrapperRect,
30
+ snapPoints,
31
+ canClose,
32
+ position,
33
+ overshoot
34
+ });
14
35
  const dragStart = ref(void 0);
15
- const originX = ref(0);
16
- const originY = ref(0);
17
36
  const dragging = ref(false);
18
37
  const shouldClose = ref(false);
19
- const elRect = ref(void 0);
38
+ const interpolateTo = ref(void 0);
39
+ let cancelPointerup = void 0;
20
40
  let cancelPointermove = void 0;
41
+ const originX = ref(0);
42
+ const originY = ref(0);
43
+ const snappedY = ref(0);
44
+ const snappedX = ref(0);
45
+ const directionY = ref("absolute");
46
+ const directionX = ref("absolute");
47
+ const hasSnapPoints = computed(() => toValue(snapPoints).length > 1);
21
48
  const draggedX = ref(0);
22
49
  const draggedY = ref(0);
23
50
  const style = computed(
24
51
  () => `transform: translate(${draggedX.value}px, ${draggedY.value}px)`
25
52
  );
26
- function getSizes() {
53
+ async function getSizes() {
27
54
  elRect.value = unrefElement(elRef)?.getBoundingClientRect();
55
+ wrapperRect.value = unrefElement(wrapperRef)?.getBoundingClientRect();
56
+ await nextTick();
28
57
  }
29
- function checkPosition() {
58
+ async function checkPosition() {
30
59
  switch (position) {
31
60
  case "bottom":
32
- if (draggedY.value > toValue(threshold).distance) {
33
- shouldClose.value = true;
61
+ const snapPointB = await findClosestSnapPoint({
62
+ draggedX,
63
+ draggedY,
64
+ direction: directionY.value
65
+ });
66
+ if (draggedY.value > toValue(threshold).distance || hasSnapPoints.value) {
67
+ if (snapPointB === drawerHeight.value) {
68
+ shouldClose.value = true;
69
+ } else {
70
+ interpolateTo.value = snapPointB;
71
+ }
34
72
  }
35
73
  break;
36
74
  case "top":
37
- if (draggedY.value < toValue(threshold).distance * -1) {
38
- shouldClose.value = true;
75
+ const snapPointT = await findClosestSnapPoint({
76
+ draggedX,
77
+ draggedY,
78
+ direction: directionY.value
79
+ });
80
+ if (draggedY.value < toValue(threshold).distance * -1 || hasSnapPoints.value) {
81
+ if (snapPointT === drawerHeight.value * -1) {
82
+ shouldClose.value = true;
83
+ } else {
84
+ interpolateTo.value = snapPointT;
85
+ }
39
86
  }
40
87
  break;
41
88
  case "right":
42
- if (draggedX.value > toValue(threshold).distance) {
43
- shouldClose.value = true;
89
+ const snapPointR = await findClosestSnapPoint({
90
+ draggedX,
91
+ draggedY,
92
+ direction: directionX.value
93
+ });
94
+ if (draggedX.value > toValue(threshold).distance || hasSnapPoints.value) {
95
+ if (snapPointR === drawerWidth.value) {
96
+ shouldClose.value = true;
97
+ } else {
98
+ interpolateTo.value = snapPointR;
99
+ }
44
100
  }
45
101
  break;
46
102
  case "left":
47
- if (draggedX.value < toValue(threshold).distance * -1) {
48
- shouldClose.value = true;
103
+ const snapPointL = await findClosestSnapPoint({
104
+ draggedX,
105
+ draggedY,
106
+ direction: directionX.value
107
+ });
108
+ if (draggedX.value < toValue(threshold).distance * -1 || hasSnapPoints.value) {
109
+ if (snapPointL === drawerWidth.value * -1) {
110
+ shouldClose.value = true;
111
+ } else {
112
+ interpolateTo.value = snapPointL;
113
+ }
49
114
  }
50
115
  break;
51
116
  }
52
117
  }
53
- function checkMomentum({ x, y }) {
118
+ async function checkMomentum({ x, y }) {
54
119
  const elapsed = Date.now() - dragStart.value.getTime();
55
120
  const velocityX = (x - originX.value) / elapsed;
56
121
  const velocityY = (y - originY.value) / elapsed;
57
122
  switch (position) {
58
123
  case "bottom":
124
+ const snapPointB = await findClosestSnapPoint({
125
+ draggedX,
126
+ draggedY,
127
+ direction: directionY.value
128
+ });
59
129
  if (velocityY > toValue(threshold).momentum) {
60
- shouldClose.value = true;
130
+ if (snapPointB === drawerHeight.value) {
131
+ shouldClose.value = true;
132
+ } else {
133
+ interpolateTo.value = snapPointB;
134
+ }
61
135
  }
62
136
  break;
63
137
  case "top":
138
+ const snapPointT = await findClosestSnapPoint({
139
+ draggedX,
140
+ draggedY,
141
+ direction: directionY.value
142
+ });
64
143
  if (velocityY < toValue(threshold).momentum * -1) {
65
- shouldClose.value = true;
144
+ if (snapPointT === drawerHeight.value) {
145
+ shouldClose.value = true;
146
+ } else {
147
+ interpolateTo.value = snapPointT;
148
+ }
66
149
  }
67
150
  break;
68
151
  case "right":
152
+ const snapPointR = await findClosestSnapPoint({
153
+ draggedX,
154
+ draggedY,
155
+ direction: directionX.value
156
+ });
69
157
  if (velocityX > toValue(threshold).momentum) {
70
- shouldClose.value = true;
158
+ if (snapPointR === drawerWidth.value) {
159
+ shouldClose.value = true;
160
+ } else {
161
+ interpolateTo.value = snapPointR;
162
+ }
71
163
  }
72
164
  break;
73
165
  case "left":
74
- if (velocityX < toValue(threshold).momentum * -1) {
75
- shouldClose.value = true;
166
+ const snapPointL = await findClosestSnapPoint({
167
+ draggedX,
168
+ draggedY,
169
+ direction: directionX.value
170
+ });
171
+ if (velocityX > toValue(threshold).momentum) {
172
+ if (snapPointL === drawerWidth.value) {
173
+ shouldClose.value = true;
174
+ } else {
175
+ interpolateTo.value = snapPointL;
176
+ }
76
177
  }
77
178
  break;
78
179
  }
@@ -99,59 +200,117 @@ export function useDrawerDrag(args) {
99
200
  function setDragged({ x, y }) {
100
201
  switch (position) {
101
202
  case "bottom":
102
- draggedY.value = clamp(y - originY.value, 0, toValue(overshoot) * -1);
203
+ const newDraggedB = clamp(y - originY.value, 0, toValue(overshoot) * -1);
204
+ directionY.value = newDraggedB < draggedY.value ? "below" : "above";
205
+ draggedY.value = newDraggedB;
103
206
  break;
104
207
  case "top":
105
- draggedY.value = clamp(y - originY.value, 0, toValue(overshoot));
208
+ const newDraggedT = clamp(y - originY.value, 0, toValue(overshoot));
209
+ directionY.value = newDraggedT < draggedY.value ? "below" : "above";
210
+ draggedY.value = newDraggedT;
106
211
  break;
107
212
  case "right":
108
- draggedX.value = clamp(x - originX.value, 0, toValue(overshoot) * -1);
213
+ const newDraggedR = clamp(x - originX.value, 0, toValue(overshoot) * -1);
214
+ directionX.value = newDraggedR < draggedX.value ? "below" : "above";
215
+ draggedX.value = newDraggedR;
109
216
  break;
110
217
  case "left":
111
- draggedX.value = clamp(x - originX.value, 0, toValue(overshoot));
218
+ const newDraggedL = clamp(x - originX.value, 0, toValue(overshoot));
219
+ directionX.value = newDraggedL < draggedX.value ? "below" : "above";
220
+ draggedX.value = newDraggedL;
221
+ break;
222
+ }
223
+ }
224
+ async function setInitial() {
225
+ await nextTick();
226
+ switch (position) {
227
+ case "top":
228
+ case "bottom":
229
+ const mappedSnapPointY = mapSnapPoint(toValue(snapPoint));
230
+ console.log("toValue(snapPoint):", toValue(snapPoint));
231
+ console.log("mappedSnapPointY:", mappedSnapPointY);
232
+ if (!mappedSnapPointY)
233
+ return;
234
+ draggedY.value = await findClosestSnapPoint({
235
+ draggedX,
236
+ draggedY: mappedSnapPointY
237
+ }) || 0;
238
+ break;
239
+ case "left":
240
+ case "right":
241
+ const mappedSnapPointX = mapSnapPoint(toValue(snapPoint));
242
+ if (!mappedSnapPointX)
243
+ return;
244
+ draggedX.value = await findClosestSnapPoint({
245
+ draggedX: mappedSnapPointX,
246
+ draggedY
247
+ }) || 0;
112
248
  break;
113
249
  }
114
250
  }
115
251
  function resetStateAndListeners() {
116
252
  dragging.value = false;
117
253
  shouldClose.value = false;
254
+ interpolateTo.value = void 0;
255
+ cancelPointerup?.();
118
256
  cancelPointermove?.();
119
257
  }
120
258
  function resetDragged() {
121
259
  draggedX.value = 0;
122
260
  draggedY.value = 0;
123
261
  }
262
+ function resetSnapped() {
263
+ snappedX.value = 0;
264
+ snappedY.value = 0;
265
+ }
124
266
  function emitterCallback(event, _payload) {
125
267
  if (event === "afterLeave") {
126
268
  resetDragged();
269
+ resetSnapped();
270
+ }
271
+ }
272
+ async function interpolateDragged(target) {
273
+ switch (position) {
274
+ case "bottom":
275
+ case "top":
276
+ snappedY.value = target;
277
+ interpolate({
278
+ from: draggedY.value,
279
+ to: target,
280
+ duration: 300,
281
+ callback: (value) => {
282
+ draggedY.value = value;
283
+ }
284
+ });
285
+ break;
286
+ case "right":
287
+ case "left":
288
+ snappedX.value = target;
289
+ interpolate({
290
+ from: draggedX.value,
291
+ to: target,
292
+ duration: 300,
293
+ callback: (value) => {
294
+ draggedX.value = value;
295
+ }
296
+ });
297
+ break;
127
298
  }
128
299
  }
129
300
  function onPointerup(e) {
130
301
  if (shouldClose.value) {
131
302
  close();
303
+ } else if (interpolateTo.value || interpolateTo.value === 0) {
304
+ interpolateDragged(interpolateTo.value);
132
305
  } else {
133
306
  switch (position) {
134
307
  case "bottom":
135
308
  case "top":
136
- interpolate({
137
- from: draggedY.value,
138
- to: 0,
139
- duration: 50,
140
- callback: (value) => {
141
- draggedY.value = value;
142
- }
143
- });
309
+ interpolateDragged(snappedY.value);
144
310
  break;
145
311
  case "right":
146
312
  case "left":
147
- interpolate({
148
- from: draggedX.value,
149
- to: 0,
150
- duration: 50,
151
- callback: (value) => {
152
- draggedX.value = value;
153
- }
154
- });
313
+ interpolateDragged(snappedX.value);
155
314
  break;
156
315
  }
157
316
  }
@@ -173,22 +332,23 @@ export function useDrawerDrag(args) {
173
332
  }
174
333
  ;
175
334
  e.target.setPointerCapture(e.pointerId);
176
- useEventListener(document, "pointerup", onPointerup);
335
+ cancelPointerup = useEventListener(document, "pointerup", onPointerup);
177
336
  cancelPointermove = useEventListener(document, "pointermove", onPointermove);
178
- originX.value = e.screenX;
179
- originY.value = e.screenY;
337
+ originX.value = e.screenX - draggedX.value;
338
+ originY.value = e.screenY - draggedY.value;
180
339
  dragStart.value = /* @__PURE__ */ new Date();
181
340
  onPointermove(e);
182
341
  e.preventDefault();
183
342
  }
184
- onMounted(() => {
185
- getSizes();
343
+ onMounted(async () => {
344
+ await getSizes();
186
345
  useDrawerEmitter().on("*", emitterCallback);
187
346
  });
188
347
  watch(
189
- () => unrefElement(elRef),
190
- () => {
191
- getSizes();
348
+ () => [unrefElement(elRef), unrefElement(wrapperRef)],
349
+ async () => {
350
+ await getSizes();
351
+ setInitial();
192
352
  }
193
353
  );
194
354
  onBeforeUnmount(() => {
@@ -196,6 +356,8 @@ export function useDrawerDrag(args) {
196
356
  });
197
357
  return {
198
358
  style,
359
+ draggedX,
360
+ draggedY,
199
361
  dragging,
200
362
  onPointerdown
201
363
  };
@@ -0,0 +1,22 @@
1
+ import { type MaybeRef } from 'vue';
2
+ import { type DefaultOptions } from '../../utils/defaultOptions.js';
3
+ import { type SnapPoint } from '../../types.js';
4
+ type UseDrawerSnapArgs = {
5
+ wrapperRect: MaybeRef<DOMRect | undefined>;
6
+ position: MaybeRef<DefaultOptions['position']>;
7
+ snapPoints: MaybeRef<DefaultOptions['snapPoints']>;
8
+ canClose: MaybeRef<DefaultOptions['canClose']>;
9
+ overshoot: MaybeRef<number>;
10
+ };
11
+ type FindClosestSnapPointArgs = {
12
+ draggedY: MaybeRef<number>;
13
+ draggedX: MaybeRef<number>;
14
+ direction?: 'below' | 'above' | 'absolute';
15
+ };
16
+ export declare function useDrawerSnap(args: UseDrawerSnapArgs): {
17
+ findClosestSnapPoint: (args: FindClosestSnapPointArgs) => Promise<number | undefined>;
18
+ mapSnapPoint: (snapPoint: SnapPoint) => number | undefined;
19
+ drawerHeight: import("vue").ComputedRef<number>;
20
+ drawerWidth: import("vue").ComputedRef<number>;
21
+ };
22
+ export {};
@@ -0,0 +1,138 @@
1
+ import { computed, toValue } from "vue";
2
+ import { mapValue } from "@maas/vue-equipment/utils";
3
+ export function useDrawerSnap(args) {
4
+ const { snapPoints, position, wrapperRect, overshoot, canClose } = args;
5
+ const mappedSnapPoints = computed(() => {
6
+ const extended = toValue(canClose) ? [...toValue(snapPoints), 0] : toValue(snapPoints);
7
+ const mapped = extended.map((snapPoint) => {
8
+ return mapSnapPoint(snapPoint);
9
+ });
10
+ const filtered = mapped.filter(
11
+ (snapPoint) => !!snapPoint || snapPoint === 0
12
+ ).sort((a, b) => a - b);
13
+ return filtered;
14
+ });
15
+ const drawerHeight = computed(() => {
16
+ if (toValue(wrapperRect) === void 0) {
17
+ return 0;
18
+ } else {
19
+ return toValue(wrapperRect)?.height - toValue(overshoot);
20
+ }
21
+ });
22
+ const drawerWidth = computed(() => {
23
+ if (toValue(wrapperRect) === void 0) {
24
+ return 0;
25
+ } else {
26
+ return toValue(wrapperRect)?.width - toValue(overshoot);
27
+ }
28
+ });
29
+ function findClosestNumber(args2) {
30
+ const { number, numbers, direction } = args2;
31
+ let filtered = numbers;
32
+ switch (direction) {
33
+ case "above":
34
+ filtered = numbers.filter((num) => num > number);
35
+ break;
36
+ case "below":
37
+ filtered = numbers.filter((num) => num < number);
38
+ break;
39
+ }
40
+ if (filtered.length === 0) {
41
+ switch (direction) {
42
+ case "above":
43
+ return Math.max(...numbers);
44
+ case "below":
45
+ return Math.min(...numbers);
46
+ default:
47
+ return void 0;
48
+ }
49
+ }
50
+ const closestNumber = filtered.reduce(
51
+ (closest, current) => Math.abs(current - number) < Math.abs(closest - number) ? current : closest
52
+ );
53
+ return closestNumber;
54
+ }
55
+ function mapSnapPoint(snapPoint) {
56
+ if (typeof snapPoint === "number") {
57
+ const reversedSnapPoint = mapValue(snapPoint, 0, 1, 1, 0);
58
+ const vh = window.innerHeight;
59
+ const vw = window.innerWidth;
60
+ if (toValue(wrapperRect) === void 0) {
61
+ return void 0;
62
+ }
63
+ switch (position) {
64
+ case "bottom":
65
+ if (reversedSnapPoint === 1)
66
+ return drawerHeight.value;
67
+ else if (reversedSnapPoint === 0)
68
+ return 0;
69
+ else
70
+ return vh * reversedSnapPoint - toValue(wrapperRect)?.top;
71
+ case "top":
72
+ if (reversedSnapPoint === 1)
73
+ return drawerHeight.value * -1;
74
+ else if (reversedSnapPoint === 0)
75
+ return 0;
76
+ else
77
+ return vh * reversedSnapPoint - toValue(wrapperRect)?.bottom;
78
+ case "right":
79
+ if (reversedSnapPoint === 1)
80
+ return drawerWidth.value;
81
+ else if (reversedSnapPoint === 0)
82
+ return 0;
83
+ else
84
+ return vw * reversedSnapPoint - toValue(wrapperRect)?.left;
85
+ case "left":
86
+ if (reversedSnapPoint === 1)
87
+ return drawerWidth.value * -1;
88
+ else if (reversedSnapPoint === 0)
89
+ return 0;
90
+ else
91
+ return vw * reversedSnapPoint - toValue(wrapperRect)?.right;
92
+ default:
93
+ return 0;
94
+ }
95
+ } else {
96
+ const parsedSnapPoint = parseFloat(snapPoint);
97
+ if (!drawerHeight.value || !drawerWidth.value) {
98
+ return void 0;
99
+ }
100
+ switch (position) {
101
+ case "bottom":
102
+ return drawerHeight.value - parsedSnapPoint;
103
+ case "top":
104
+ return (drawerHeight.value - parsedSnapPoint) * -1;
105
+ case "right":
106
+ return drawerWidth.value - parsedSnapPoint;
107
+ case "left":
108
+ return (drawerWidth.value - parsedSnapPoint) * -1;
109
+ default:
110
+ return parseFloat(snapPoint);
111
+ }
112
+ }
113
+ }
114
+ async function findClosestSnapPoint(args2) {
115
+ const { draggedY, draggedX, direction = "absolute" } = args2;
116
+ let closest = 0;
117
+ switch (position) {
118
+ case "bottom":
119
+ case "top":
120
+ closest = findClosestNumber({
121
+ number: toValue(draggedY),
122
+ numbers: mappedSnapPoints.value,
123
+ direction
124
+ });
125
+ break;
126
+ case "right":
127
+ case "left":
128
+ closest = findClosestNumber({
129
+ number: toValue(draggedX),
130
+ numbers: mappedSnapPoints.value,
131
+ direction
132
+ });
133
+ break;
134
+ }
135
+ return closest;
136
+ }
137
+ return { findClosestSnapPoint, mapSnapPoint, drawerHeight, drawerWidth };
138
+ }
@@ -1,3 +1,4 @@
1
+ export type SnapPoint = number | `${string}px`;
1
2
  export type DrawerOptions = {
2
3
  position?: 'top' | 'right' | 'bottom' | 'left';
3
4
  backdrop?: boolean;
@@ -22,6 +23,9 @@ export type DrawerOptions = {
22
23
  open: boolean;
23
24
  animate: boolean;
24
25
  };
26
+ snapPoints?: SnapPoint[];
27
+ snapPoint?: SnapPoint;
28
+ canClose?: boolean;
25
29
  };
26
30
  export type DrawerEvents = {
27
31
  beforeEnter: string;
@@ -1,7 +1,7 @@
1
1
  const defaultOptions = {
2
2
  position: "bottom",
3
3
  backdrop: true,
4
- focusTrap: true,
4
+ focusTrap: false,
5
5
  scrollLock: true,
6
6
  scrollLockPadding: true,
7
7
  teleport: {
@@ -21,6 +21,9 @@ const defaultOptions = {
21
21
  beforeMount: {
22
22
  open: false,
23
23
  animate: false
24
- }
24
+ },
25
+ snapPoints: [1],
26
+ snapPoint: 1,
27
+ canClose: true
25
28
  };
26
29
  export { defaultOptions };
@@ -5,7 +5,7 @@
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
- import { ref, inject, computed, onMounted, toRaw, watch } from 'vue'
8
+ import { ref, inject, computed, onMounted, watch } from 'vue'
9
9
  import { unrefElement } from '@vueuse/core'
10
10
  import { animate, type Easing } from 'motion'
11
11
  import { ScrollProgressKey } from '../symbols'
@@ -37,26 +37,21 @@ function clampValue(value, min, max) {
37
37
 
38
38
  // src/functions/interpolate.ts
39
39
  function interpolate(args) {
40
- const {
41
- from,
42
- to,
43
- duration,
44
- callback,
45
- easing = (t) => t * (2 - t),
46
- interval = 1
47
- } = args;
48
- const steps = Math.ceil(duration / interval);
49
- const stepSize = 1 / steps;
50
- let currentStep = 0;
51
- const intervalId = setInterval(() => {
52
- const progress = currentStep * stepSize;
53
- const value = from + (to - from) * easing(progress);
40
+ const { from, to, duration, callback, easing = (t) => t * (2 - t) } = args;
41
+ let startTime;
42
+ const speed = 1;
43
+ function animate(timestamp) {
44
+ if (!startTime)
45
+ startTime = timestamp;
46
+ const progress = Math.min(1, (timestamp - startTime) / (duration * speed));
47
+ const easedProgress = easing(progress);
48
+ const value = from + (to - from) * easedProgress;
54
49
  callback(value);
55
- currentStep++;
56
- if (currentStep >= steps) {
57
- clearInterval(intervalId);
50
+ if (progress < 1) {
51
+ requestAnimationFrame(animate);
58
52
  }
59
- }, interval);
53
+ }
54
+ requestAnimationFrame(animate);
60
55
  }
61
56
 
62
57
  // src/functions/isIOS.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../packages/utils/index.ts","../../packages/utils/src/functions/clampValue.ts","../../packages/utils/src/functions/interpolate.ts","../../packages/utils/src/functions/isIOS.ts","../../packages/utils/src/functions/mapValue.ts","../../packages/utils/src/functions/uuid.ts","../../packages/utils/src/functions/uniq.ts","../../packages/utils/src/functions/slugify.ts"],"sourcesContent":["export * from './src/functions/clampValue'\nexport * from './src/functions/interpolate'\nexport * from './src/functions/isIOS'\nexport * from './src/functions/mapValue'\nexport * from './src/functions/uuid'\nexport * from './src/functions/uniq'\nexport * from './src/functions/slugify'\n\nexport type * from './src/types'\n","export function clampValue(value: number, min: number, max: number) {\n return value <= min ? min : value >= max ? max : value\n}\n","export type InterpolateArgs = {\n from: number\n to: number\n duration: number\n interval?: number\n easing?: (t: number) => number\n callback: (result: number) => void\n}\n\nexport function interpolate(args: InterpolateArgs) {\n const {\n from,\n to,\n duration,\n callback,\n easing = (t: number) => t * (2 - t),\n interval = 1,\n } = args\n\n const steps = Math.ceil(duration / interval)\n const stepSize = 1 / steps\n\n let currentStep = 0\n\n const intervalId = setInterval(() => {\n const progress = currentStep * stepSize\n const value = from + (to - from) * easing(progress)\n\n callback(value)\n currentStep++\n\n if (currentStep >= steps) {\n clearInterval(intervalId)\n }\n }, interval)\n}\n","export function isIOS() {\n if (typeof window === 'undefined') return false\n return /iPad|iPhone|iPod/.test(navigator?.userAgent)\n}\n","export function mapValue(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number,\n) {\n return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin\n}\n","// This implementation is meant for internal use only.\n// It is only used to generate a unique IDs for the `key` props.\n// It should not replace crypto.randomUUID() or window.crypto.randomUUID().\n\nexport function uuid() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'\n .split('')\n .reduce(\n (c, i) =>\n c +\n (i === 'x'\n ? Math.floor(Math.random() * 0xf).toString(16)\n : i === 'y'\n ? Math.floor(Math.random() * 4 + 8).toString(16)\n : i),\n '',\n )\n}\n","export function uniq<T extends any[]>(a: T) {\n return Array.from(new Set(a))\n}\n","export interface SlugifyOptions {\n separator?: string\n trim?: boolean\n remove?: RegExp\n strict?: boolean\n lowercase?: boolean\n}\n\nconst defaultOptions: SlugifyOptions = {\n separator: '-',\n trim: true,\n remove: undefined,\n strict: true,\n lowercase: true,\n}\n\nexport function slugify(string: string, options?: SlugifyOptions): string {\n if (typeof string !== 'string') {\n throw new Error('slugify: string argument expected')\n }\n\n // Merge provided options with default options\n const _options = { ...defaultOptions, ...options }\n\n const charMap: { [key: string]: string } = {}\n\n let slug = string\n .normalize()\n .split('')\n .reduce(function (result, ch) {\n let appendChar = charMap[ch]\n if (appendChar === undefined) appendChar = ch\n if (appendChar === _options?.separator) appendChar = ' '\n return (\n result +\n appendChar.replace(_options?.remove || /[^\\w\\s$*_+~.()'\"!\\-:@]+/g, '')\n )\n }, '')\n\n if (_options.strict) {\n slug = slug.replace(/[^A-Za-z0-9\\s]/g, '')\n }\n\n if (_options.trim) {\n slug = slug.trim()\n }\n\n if (_options.separator) {\n slug = slug.replace(/ +/g, _options.separator)\n }\n\n if (_options.lowercase) {\n slug = slug.toLocaleLowerCase()\n }\n\n return slug\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,WAAW,OAAe,KAAa,KAAa;AAClE,SAAO,SAAS,MAAM,MAAM,SAAS,MAAM,MAAM;AACnD;;;ACOO,SAAS,YAAY,MAAuB;AACjD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,CAAC,MAAc,KAAK,IAAI;AAAA,IACjC,WAAW;AAAA,EACb,IAAI;AAEJ,QAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ;AAC3C,QAAM,WAAW,IAAI;AAErB,MAAI,cAAc;AAElB,QAAM,aAAa,YAAY,MAAM;AACnC,UAAM,WAAW,cAAc;AAC/B,UAAM,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ;AAElD,aAAS,KAAK;AACd;AAEA,QAAI,eAAe,OAAO;AACxB,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,QAAQ;AACb;;;ACnCO,SAAS,QAAQ;AACtB,MAAI,OAAO,WAAW;AAAa,WAAO;AAC1C,SAAO,mBAAmB,KAAK,uCAAW,SAAS;AACrD;;;ACHO,SAAS,SACd,OACA,OACA,OACA,QACA,QACA;AACA,UAAS,QAAQ,UAAU,SAAS,WAAY,QAAQ,SAAS;AACnE;;;ACJO,SAAS,OAAO;AACrB,SAAO,uCACJ,MAAM,EAAE,EACR;AAAA,IACC,CAAC,GAAG,MACF,KACC,MAAM,MACH,KAAK,MAAM,KAAK,OAAO,IAAI,EAAG,EAAE,SAAS,EAAE,IAC3C,MAAM,MACN,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,SAAS,EAAE,IAC7C;AAAA,IACN;AAAA,EACF;AACJ;;;ACjBO,SAAS,KAAsB,GAAM;AAC1C,SAAO,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC;AAC9B;;;ACMA,IAAM,iBAAiC;AAAA,EACrC,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AACb;AAEO,SAAS,QAAQ,QAAgB,SAAkC;AACxE,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AAGA,QAAM,WAAW,EAAE,GAAG,gBAAgB,GAAG,QAAQ;AAEjD,QAAM,UAAqC,CAAC;AAE5C,MAAI,OAAO,OACR,UAAU,EACV,MAAM,EAAE,EACR,OAAO,SAAU,QAAQ,IAAI;AAC5B,QAAI,aAAa,QAAQ,EAAE;AAC3B,QAAI,eAAe;AAAW,mBAAa;AAC3C,QAAI,gBAAe,qCAAU;AAAW,mBAAa;AACrD,WACE,SACA,WAAW,SAAQ,qCAAU,WAAU,4BAA4B,EAAE;AAAA,EAEzE,GAAG,EAAE;AAEP,MAAI,SAAS,QAAQ;AACnB,WAAO,KAAK,QAAQ,mBAAmB,EAAE;AAAA,EAC3C;AAEA,MAAI,SAAS,MAAM;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,QAAQ,OAAO,SAAS,SAAS;AAAA,EAC/C;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../packages/utils/index.ts","../../packages/utils/src/functions/clampValue.ts","../../packages/utils/src/functions/interpolate.ts","../../packages/utils/src/functions/isIOS.ts","../../packages/utils/src/functions/mapValue.ts","../../packages/utils/src/functions/uuid.ts","../../packages/utils/src/functions/uniq.ts","../../packages/utils/src/functions/slugify.ts"],"sourcesContent":["export * from './src/functions/clampValue'\nexport * from './src/functions/interpolate'\nexport * from './src/functions/isIOS'\nexport * from './src/functions/mapValue'\nexport * from './src/functions/uuid'\nexport * from './src/functions/uniq'\nexport * from './src/functions/slugify'\n\nexport type * from './src/types'\n","export function clampValue(value: number, min: number, max: number) {\n return value <= min ? min : value >= max ? max : value\n}\n","export type InterpolateArgs = {\n from: number\n to: number\n duration: number\n interval?: number\n easing?: (t: number) => number\n callback: (result: number) => void\n}\n\nexport function interpolate(args: InterpolateArgs) {\n const { from, to, duration, callback, easing = (t) => t * (2 - t) } = args\n\n let startTime: number\n const speed = 1\n\n function animate(timestamp: number) {\n if (!startTime) startTime = timestamp\n\n const progress = Math.min(1, (timestamp - startTime) / (duration * speed))\n const easedProgress = easing(progress)\n const value = from + (to - from) * easedProgress\n\n callback(value)\n\n if (progress < 1) {\n requestAnimationFrame(animate)\n }\n }\n\n requestAnimationFrame(animate)\n}\n","export function isIOS() {\n if (typeof window === 'undefined') return false\n return /iPad|iPhone|iPod/.test(navigator?.userAgent)\n}\n","export function mapValue(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number,\n) {\n return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin\n}\n","// This implementation is meant for internal use only.\n// It is only used to generate a unique IDs for the `key` props.\n// It should not replace crypto.randomUUID() or window.crypto.randomUUID().\n\nexport function uuid() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'\n .split('')\n .reduce(\n (c, i) =>\n c +\n (i === 'x'\n ? Math.floor(Math.random() * 0xf).toString(16)\n : i === 'y'\n ? Math.floor(Math.random() * 4 + 8).toString(16)\n : i),\n '',\n )\n}\n","export function uniq<T extends any[]>(a: T) {\n return Array.from(new Set(a))\n}\n","export interface SlugifyOptions {\n separator?: string\n trim?: boolean\n remove?: RegExp\n strict?: boolean\n lowercase?: boolean\n}\n\nconst defaultOptions: SlugifyOptions = {\n separator: '-',\n trim: true,\n remove: undefined,\n strict: true,\n lowercase: true,\n}\n\nexport function slugify(string: string, options?: SlugifyOptions): string {\n if (typeof string !== 'string') {\n throw new Error('slugify: string argument expected')\n }\n\n // Merge provided options with default options\n const _options = { ...defaultOptions, ...options }\n\n const charMap: { [key: string]: string } = {}\n\n let slug = string\n .normalize()\n .split('')\n .reduce(function (result, ch) {\n let appendChar = charMap[ch]\n if (appendChar === undefined) appendChar = ch\n if (appendChar === _options?.separator) appendChar = ' '\n return (\n result +\n appendChar.replace(_options?.remove || /[^\\w\\s$*_+~.()'\"!\\-:@]+/g, '')\n )\n }, '')\n\n if (_options.strict) {\n slug = slug.replace(/[^A-Za-z0-9\\s]/g, '')\n }\n\n if (_options.trim) {\n slug = slug.trim()\n }\n\n if (_options.separator) {\n slug = slug.replace(/ +/g, _options.separator)\n }\n\n if (_options.lowercase) {\n slug = slug.toLocaleLowerCase()\n }\n\n return slug\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,WAAW,OAAe,KAAa,KAAa;AAClE,SAAO,SAAS,MAAM,MAAM,SAAS,MAAM,MAAM;AACnD;;;ACOO,SAAS,YAAY,MAAuB;AACjD,QAAM,EAAE,MAAM,IAAI,UAAU,UAAU,SAAS,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI;AAEtE,MAAI;AACJ,QAAM,QAAQ;AAEd,WAAS,QAAQ,WAAmB;AAClC,QAAI,CAAC;AAAW,kBAAY;AAE5B,UAAM,WAAW,KAAK,IAAI,IAAI,YAAY,cAAc,WAAW,MAAM;AACzE,UAAM,gBAAgB,OAAO,QAAQ;AACrC,UAAM,QAAQ,QAAQ,KAAK,QAAQ;AAEnC,aAAS,KAAK;AAEd,QAAI,WAAW,GAAG;AAChB,4BAAsB,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,wBAAsB,OAAO;AAC/B;;;AC9BO,SAAS,QAAQ;AACtB,MAAI,OAAO,WAAW;AAAa,WAAO;AAC1C,SAAO,mBAAmB,KAAK,uCAAW,SAAS;AACrD;;;ACHO,SAAS,SACd,OACA,OACA,OACA,QACA,QACA;AACA,UAAS,QAAQ,UAAU,SAAS,WAAY,QAAQ,SAAS;AACnE;;;ACJO,SAAS,OAAO;AACrB,SAAO,uCACJ,MAAM,EAAE,EACR;AAAA,IACC,CAAC,GAAG,MACF,KACC,MAAM,MACH,KAAK,MAAM,KAAK,OAAO,IAAI,EAAG,EAAE,SAAS,EAAE,IAC3C,MAAM,MACN,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,SAAS,EAAE,IAC7C;AAAA,IACN;AAAA,EACF;AACJ;;;ACjBO,SAAS,KAAsB,GAAM;AAC1C,SAAO,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC;AAC9B;;;ACMA,IAAM,iBAAiC;AAAA,EACrC,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AACb;AAEO,SAAS,QAAQ,QAAgB,SAAkC;AACxE,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AAGA,QAAM,WAAW,EAAE,GAAG,gBAAgB,GAAG,QAAQ;AAEjD,QAAM,UAAqC,CAAC;AAE5C,MAAI,OAAO,OACR,UAAU,EACV,MAAM,EAAE,EACR,OAAO,SAAU,QAAQ,IAAI;AAC5B,QAAI,aAAa,QAAQ,EAAE;AAC3B,QAAI,eAAe;AAAW,mBAAa;AAC3C,QAAI,gBAAe,qCAAU;AAAW,mBAAa;AACrD,WACE,SACA,WAAW,SAAQ,qCAAU,WAAU,4BAA4B,EAAE;AAAA,EAEzE,GAAG,EAAE;AAEP,MAAI,SAAS,QAAQ;AACnB,WAAO,KAAK,QAAQ,mBAAmB,EAAE;AAAA,EAC3C;AAEA,MAAI,SAAS,MAAM;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,QAAQ,OAAO,SAAS,SAAS;AAAA,EAC/C;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAEA,SAAO;AACT;","names":[]}
@@ -5,26 +5,21 @@ function clampValue(value, min, max) {
5
5
 
6
6
  // src/functions/interpolate.ts
7
7
  function interpolate(args) {
8
- const {
9
- from,
10
- to,
11
- duration,
12
- callback,
13
- easing = (t) => t * (2 - t),
14
- interval = 1
15
- } = args;
16
- const steps = Math.ceil(duration / interval);
17
- const stepSize = 1 / steps;
18
- let currentStep = 0;
19
- const intervalId = setInterval(() => {
20
- const progress = currentStep * stepSize;
21
- const value = from + (to - from) * easing(progress);
8
+ const { from, to, duration, callback, easing = (t) => t * (2 - t) } = args;
9
+ let startTime;
10
+ const speed = 1;
11
+ function animate(timestamp) {
12
+ if (!startTime)
13
+ startTime = timestamp;
14
+ const progress = Math.min(1, (timestamp - startTime) / (duration * speed));
15
+ const easedProgress = easing(progress);
16
+ const value = from + (to - from) * easedProgress;
22
17
  callback(value);
23
- currentStep++;
24
- if (currentStep >= steps) {
25
- clearInterval(intervalId);
18
+ if (progress < 1) {
19
+ requestAnimationFrame(animate);
26
20
  }
27
- }, interval);
21
+ }
22
+ requestAnimationFrame(animate);
28
23
  }
29
24
 
30
25
  // src/functions/isIOS.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../packages/utils/src/functions/clampValue.ts","../../packages/utils/src/functions/interpolate.ts","../../packages/utils/src/functions/isIOS.ts","../../packages/utils/src/functions/mapValue.ts","../../packages/utils/src/functions/uuid.ts","../../packages/utils/src/functions/uniq.ts","../../packages/utils/src/functions/slugify.ts"],"sourcesContent":["export function clampValue(value: number, min: number, max: number) {\n return value <= min ? min : value >= max ? max : value\n}\n","export type InterpolateArgs = {\n from: number\n to: number\n duration: number\n interval?: number\n easing?: (t: number) => number\n callback: (result: number) => void\n}\n\nexport function interpolate(args: InterpolateArgs) {\n const {\n from,\n to,\n duration,\n callback,\n easing = (t: number) => t * (2 - t),\n interval = 1,\n } = args\n\n const steps = Math.ceil(duration / interval)\n const stepSize = 1 / steps\n\n let currentStep = 0\n\n const intervalId = setInterval(() => {\n const progress = currentStep * stepSize\n const value = from + (to - from) * easing(progress)\n\n callback(value)\n currentStep++\n\n if (currentStep >= steps) {\n clearInterval(intervalId)\n }\n }, interval)\n}\n","export function isIOS() {\n if (typeof window === 'undefined') return false\n return /iPad|iPhone|iPod/.test(navigator?.userAgent)\n}\n","export function mapValue(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number,\n) {\n return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin\n}\n","// This implementation is meant for internal use only.\n// It is only used to generate a unique IDs for the `key` props.\n// It should not replace crypto.randomUUID() or window.crypto.randomUUID().\n\nexport function uuid() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'\n .split('')\n .reduce(\n (c, i) =>\n c +\n (i === 'x'\n ? Math.floor(Math.random() * 0xf).toString(16)\n : i === 'y'\n ? Math.floor(Math.random() * 4 + 8).toString(16)\n : i),\n '',\n )\n}\n","export function uniq<T extends any[]>(a: T) {\n return Array.from(new Set(a))\n}\n","export interface SlugifyOptions {\n separator?: string\n trim?: boolean\n remove?: RegExp\n strict?: boolean\n lowercase?: boolean\n}\n\nconst defaultOptions: SlugifyOptions = {\n separator: '-',\n trim: true,\n remove: undefined,\n strict: true,\n lowercase: true,\n}\n\nexport function slugify(string: string, options?: SlugifyOptions): string {\n if (typeof string !== 'string') {\n throw new Error('slugify: string argument expected')\n }\n\n // Merge provided options with default options\n const _options = { ...defaultOptions, ...options }\n\n const charMap: { [key: string]: string } = {}\n\n let slug = string\n .normalize()\n .split('')\n .reduce(function (result, ch) {\n let appendChar = charMap[ch]\n if (appendChar === undefined) appendChar = ch\n if (appendChar === _options?.separator) appendChar = ' '\n return (\n result +\n appendChar.replace(_options?.remove || /[^\\w\\s$*_+~.()'\"!\\-:@]+/g, '')\n )\n }, '')\n\n if (_options.strict) {\n slug = slug.replace(/[^A-Za-z0-9\\s]/g, '')\n }\n\n if (_options.trim) {\n slug = slug.trim()\n }\n\n if (_options.separator) {\n slug = slug.replace(/ +/g, _options.separator)\n }\n\n if (_options.lowercase) {\n slug = slug.toLocaleLowerCase()\n }\n\n return slug\n}\n"],"mappings":";AAAO,SAAS,WAAW,OAAe,KAAa,KAAa;AAClE,SAAO,SAAS,MAAM,MAAM,SAAS,MAAM,MAAM;AACnD;;;ACOO,SAAS,YAAY,MAAuB;AACjD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,CAAC,MAAc,KAAK,IAAI;AAAA,IACjC,WAAW;AAAA,EACb,IAAI;AAEJ,QAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ;AAC3C,QAAM,WAAW,IAAI;AAErB,MAAI,cAAc;AAElB,QAAM,aAAa,YAAY,MAAM;AACnC,UAAM,WAAW,cAAc;AAC/B,UAAM,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ;AAElD,aAAS,KAAK;AACd;AAEA,QAAI,eAAe,OAAO;AACxB,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,QAAQ;AACb;;;ACnCO,SAAS,QAAQ;AACtB,MAAI,OAAO,WAAW;AAAa,WAAO;AAC1C,SAAO,mBAAmB,KAAK,uCAAW,SAAS;AACrD;;;ACHO,SAAS,SACd,OACA,OACA,OACA,QACA,QACA;AACA,UAAS,QAAQ,UAAU,SAAS,WAAY,QAAQ,SAAS;AACnE;;;ACJO,SAAS,OAAO;AACrB,SAAO,uCACJ,MAAM,EAAE,EACR;AAAA,IACC,CAAC,GAAG,MACF,KACC,MAAM,MACH,KAAK,MAAM,KAAK,OAAO,IAAI,EAAG,EAAE,SAAS,EAAE,IAC3C,MAAM,MACN,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,SAAS,EAAE,IAC7C;AAAA,IACN;AAAA,EACF;AACJ;;;ACjBO,SAAS,KAAsB,GAAM;AAC1C,SAAO,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC;AAC9B;;;ACMA,IAAM,iBAAiC;AAAA,EACrC,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AACb;AAEO,SAAS,QAAQ,QAAgB,SAAkC;AACxE,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AAGA,QAAM,WAAW,EAAE,GAAG,gBAAgB,GAAG,QAAQ;AAEjD,QAAM,UAAqC,CAAC;AAE5C,MAAI,OAAO,OACR,UAAU,EACV,MAAM,EAAE,EACR,OAAO,SAAU,QAAQ,IAAI;AAC5B,QAAI,aAAa,QAAQ,EAAE;AAC3B,QAAI,eAAe;AAAW,mBAAa;AAC3C,QAAI,gBAAe,qCAAU;AAAW,mBAAa;AACrD,WACE,SACA,WAAW,SAAQ,qCAAU,WAAU,4BAA4B,EAAE;AAAA,EAEzE,GAAG,EAAE;AAEP,MAAI,SAAS,QAAQ;AACnB,WAAO,KAAK,QAAQ,mBAAmB,EAAE;AAAA,EAC3C;AAEA,MAAI,SAAS,MAAM;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,QAAQ,OAAO,SAAS,SAAS;AAAA,EAC/C;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../packages/utils/src/functions/clampValue.ts","../../packages/utils/src/functions/interpolate.ts","../../packages/utils/src/functions/isIOS.ts","../../packages/utils/src/functions/mapValue.ts","../../packages/utils/src/functions/uuid.ts","../../packages/utils/src/functions/uniq.ts","../../packages/utils/src/functions/slugify.ts"],"sourcesContent":["export function clampValue(value: number, min: number, max: number) {\n return value <= min ? min : value >= max ? max : value\n}\n","export type InterpolateArgs = {\n from: number\n to: number\n duration: number\n interval?: number\n easing?: (t: number) => number\n callback: (result: number) => void\n}\n\nexport function interpolate(args: InterpolateArgs) {\n const { from, to, duration, callback, easing = (t) => t * (2 - t) } = args\n\n let startTime: number\n const speed = 1\n\n function animate(timestamp: number) {\n if (!startTime) startTime = timestamp\n\n const progress = Math.min(1, (timestamp - startTime) / (duration * speed))\n const easedProgress = easing(progress)\n const value = from + (to - from) * easedProgress\n\n callback(value)\n\n if (progress < 1) {\n requestAnimationFrame(animate)\n }\n }\n\n requestAnimationFrame(animate)\n}\n","export function isIOS() {\n if (typeof window === 'undefined') return false\n return /iPad|iPhone|iPod/.test(navigator?.userAgent)\n}\n","export function mapValue(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number,\n) {\n return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin\n}\n","// This implementation is meant for internal use only.\n// It is only used to generate a unique IDs for the `key` props.\n// It should not replace crypto.randomUUID() or window.crypto.randomUUID().\n\nexport function uuid() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'\n .split('')\n .reduce(\n (c, i) =>\n c +\n (i === 'x'\n ? Math.floor(Math.random() * 0xf).toString(16)\n : i === 'y'\n ? Math.floor(Math.random() * 4 + 8).toString(16)\n : i),\n '',\n )\n}\n","export function uniq<T extends any[]>(a: T) {\n return Array.from(new Set(a))\n}\n","export interface SlugifyOptions {\n separator?: string\n trim?: boolean\n remove?: RegExp\n strict?: boolean\n lowercase?: boolean\n}\n\nconst defaultOptions: SlugifyOptions = {\n separator: '-',\n trim: true,\n remove: undefined,\n strict: true,\n lowercase: true,\n}\n\nexport function slugify(string: string, options?: SlugifyOptions): string {\n if (typeof string !== 'string') {\n throw new Error('slugify: string argument expected')\n }\n\n // Merge provided options with default options\n const _options = { ...defaultOptions, ...options }\n\n const charMap: { [key: string]: string } = {}\n\n let slug = string\n .normalize()\n .split('')\n .reduce(function (result, ch) {\n let appendChar = charMap[ch]\n if (appendChar === undefined) appendChar = ch\n if (appendChar === _options?.separator) appendChar = ' '\n return (\n result +\n appendChar.replace(_options?.remove || /[^\\w\\s$*_+~.()'\"!\\-:@]+/g, '')\n )\n }, '')\n\n if (_options.strict) {\n slug = slug.replace(/[^A-Za-z0-9\\s]/g, '')\n }\n\n if (_options.trim) {\n slug = slug.trim()\n }\n\n if (_options.separator) {\n slug = slug.replace(/ +/g, _options.separator)\n }\n\n if (_options.lowercase) {\n slug = slug.toLocaleLowerCase()\n }\n\n return slug\n}\n"],"mappings":";AAAO,SAAS,WAAW,OAAe,KAAa,KAAa;AAClE,SAAO,SAAS,MAAM,MAAM,SAAS,MAAM,MAAM;AACnD;;;ACOO,SAAS,YAAY,MAAuB;AACjD,QAAM,EAAE,MAAM,IAAI,UAAU,UAAU,SAAS,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI;AAEtE,MAAI;AACJ,QAAM,QAAQ;AAEd,WAAS,QAAQ,WAAmB;AAClC,QAAI,CAAC;AAAW,kBAAY;AAE5B,UAAM,WAAW,KAAK,IAAI,IAAI,YAAY,cAAc,WAAW,MAAM;AACzE,UAAM,gBAAgB,OAAO,QAAQ;AACrC,UAAM,QAAQ,QAAQ,KAAK,QAAQ;AAEnC,aAAS,KAAK;AAEd,QAAI,WAAW,GAAG;AAChB,4BAAsB,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,wBAAsB,OAAO;AAC/B;;;AC9BO,SAAS,QAAQ;AACtB,MAAI,OAAO,WAAW;AAAa,WAAO;AAC1C,SAAO,mBAAmB,KAAK,uCAAW,SAAS;AACrD;;;ACHO,SAAS,SACd,OACA,OACA,OACA,QACA,QACA;AACA,UAAS,QAAQ,UAAU,SAAS,WAAY,QAAQ,SAAS;AACnE;;;ACJO,SAAS,OAAO;AACrB,SAAO,uCACJ,MAAM,EAAE,EACR;AAAA,IACC,CAAC,GAAG,MACF,KACC,MAAM,MACH,KAAK,MAAM,KAAK,OAAO,IAAI,EAAG,EAAE,SAAS,EAAE,IAC3C,MAAM,MACN,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,SAAS,EAAE,IAC7C;AAAA,IACN;AAAA,EACF;AACJ;;;ACjBO,SAAS,KAAsB,GAAM;AAC1C,SAAO,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC;AAC9B;;;ACMA,IAAM,iBAAiC;AAAA,EACrC,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AACb;AAEO,SAAS,QAAQ,QAAgB,SAAkC;AACxE,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AAGA,QAAM,WAAW,EAAE,GAAG,gBAAgB,GAAG,QAAQ;AAEjD,QAAM,UAAqC,CAAC;AAE5C,MAAI,OAAO,OACR,UAAU,EACV,MAAM,EAAE,EACR,OAAO,SAAU,QAAQ,IAAI;AAC5B,QAAI,aAAa,QAAQ,EAAE;AAC3B,QAAI,eAAe;AAAW,mBAAa;AAC3C,QAAI,gBAAe,qCAAU;AAAW,mBAAa;AACrD,WACE,SACA,WAAW,SAAQ,qCAAU,WAAU,4BAA4B,EAAE;AAAA,EAEzE,GAAG,EAAE;AAEP,MAAI,SAAS,QAAQ;AACnB,WAAO,KAAK,QAAQ,mBAAmB,EAAE;AAAA,EAC3C;AAEA,MAAI,SAAS,MAAM;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,QAAQ,OAAO,SAAS,SAAS;AAAA,EAC/C;AAEA,MAAI,SAAS,WAAW;AACtB,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAEA,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maas/vue-equipment",
3
3
  "description": "A magic collection of Vue composables, plugins, components and directives",
4
- "version": "0.14.4",
4
+ "version": "0.15.1",
5
5
  "author": "Robin Scholz <https://github.com/robinscholz>, Christoph Jeworutzki <https://github.com/ChristophJeworutzki>",
6
6
  "devDependencies": {
7
7
  "@antfu/ni": "^0.21.12",