@maz-ui/mcp 5.0.0-beta.4 → 5.0.0-beta.40
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.
- package/README.md +1 -1
- package/dist/mcp.d.mts +22 -9
- package/dist/mcp.d.ts +22 -9
- package/dist/mcp.mjs +160 -83
- package/docs/generated-docs/maz-avatar.doc.md +30 -30
- package/docs/generated-docs/maz-bottom-sheet.doc.md +16 -13
- package/docs/generated-docs/maz-btn.doc.md +21 -19
- package/docs/generated-docs/maz-checkbox.doc.md +16 -17
- package/docs/generated-docs/maz-circular-progress-bar.doc.md +1 -1
- package/docs/generated-docs/maz-code-highlight.doc.md +11 -0
- package/docs/generated-docs/maz-container.doc.md +1 -0
- package/docs/generated-docs/maz-date-picker.doc.md +41 -41
- package/docs/generated-docs/maz-drawer.doc.md +7 -8
- package/docs/generated-docs/maz-dropdown.doc.md +3 -0
- package/docs/generated-docs/maz-expand-animation.doc.md +4 -4
- package/docs/generated-docs/maz-fullscreen-loader.doc.md +5 -5
- package/docs/generated-docs/maz-gallery.doc.md +15 -15
- package/docs/generated-docs/maz-input-code.doc.md +16 -16
- package/docs/generated-docs/maz-input-price.doc.md +14 -14
- package/docs/generated-docs/maz-input-tags.doc.md +16 -16
- package/docs/generated-docs/maz-input.doc.md +33 -33
- package/docs/generated-docs/maz-lazy-img.doc.md +14 -14
- package/docs/generated-docs/maz-loading-bar.doc.md +4 -4
- package/docs/generated-docs/maz-markdown-editor.doc.md +65 -0
- package/docs/generated-docs/maz-popover.doc.md +1 -1
- package/docs/generated-docs/maz-pull-to-refresh.doc.md +10 -10
- package/docs/generated-docs/maz-radio-buttons.doc.md +17 -17
- package/docs/generated-docs/maz-radio.doc.md +16 -16
- package/docs/generated-docs/maz-reading-progress-bar.doc.md +4 -4
- package/docs/generated-docs/maz-sidebar-content.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar-footer.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar-group.doc.md +11 -0
- package/docs/generated-docs/maz-sidebar-header.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar-menu-button.doc.md +27 -0
- package/docs/generated-docs/maz-sidebar-menu-item.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar-menu-sub.doc.md +16 -0
- package/docs/generated-docs/maz-sidebar-menu.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar-separator.doc.md +0 -0
- package/docs/generated-docs/maz-sidebar-trigger.doc.md +5 -0
- package/docs/generated-docs/maz-sidebar.doc.md +36 -0
- package/docs/generated-docs/maz-slider.doc.md +1 -1
- package/docs/generated-docs/maz-spinner.doc.md +4 -4
- package/docs/generated-docs/maz-switch.doc.md +14 -14
- package/docs/generated-docs/maz-table.doc.md +39 -37
- package/docs/generated-docs/maz-tabs-bar.doc.md +20 -9
- package/docs/generated-docs/maz-tabs.doc.md +6 -3
- package/docs/generated-docs/maz-textarea.doc.md +27 -25
- package/docs/generated-docs/maz-ticker.doc.md +1 -1
- package/docs/generated-docs/maz-timeline.doc.md +4 -4
- package/docs/generated-docs/maz-window-mockup.doc.md +23 -0
- package/docs/src/blog/v4.md +1 -1
- package/docs/src/blog/v5.md +5 -7
- package/docs/src/components/maz-avatar.md +52 -4
- package/docs/src/components/maz-bottom-sheet.md +300 -169
- package/docs/src/components/maz-btn.md +1 -1
- package/docs/src/components/maz-code-highlight.md +233 -0
- package/docs/src/components/maz-date-picker.md +1 -1
- package/docs/src/components/maz-dialog.md +46 -0
- package/docs/src/components/maz-dropdown.md +32 -16
- package/docs/src/components/maz-icon.md +2 -2
- package/docs/src/components/maz-lazy-img.md +64 -0
- package/docs/src/components/maz-markdown-editor.md +369 -0
- package/docs/src/components/maz-pull-to-refresh.md +1 -1
- package/docs/src/components/maz-sidebar.md +719 -0
- package/docs/src/components/maz-table.md +74 -0
- package/docs/src/components/maz-tabs.md +110 -3
- package/docs/src/components/maz-textarea.md +58 -0
- package/docs/src/components/maz-timeline.md +60 -0
- package/docs/src/components/maz-window-mockup.md +249 -0
- package/docs/src/composables/use-drag.md +239 -0
- package/docs/src/composables/use-form-validator.md +50 -11
- package/docs/src/composables/use-swipe.md +26 -11
- package/docs/src/directives/click-outside.md +8 -15
- package/docs/src/directives/fullscreen-img.md +1 -1
- package/docs/src/directives/lazy-img.md +44 -11
- package/docs/src/directives/tooltip.md +24 -1
- package/docs/src/directives/zoom-img.md +1 -1
- package/docs/src/ecosystem/eslint-config.md +95 -1
- package/docs/src/{guide/icons.md → ecosystem/icons/index.md} +1 -1
- package/docs/src/ecosystem/node/exec-promise.md +87 -0
- package/docs/src/ecosystem/node/index.md +53 -0
- package/docs/src/ecosystem/node/logger.md +146 -0
- package/docs/src/ecosystem/node/print-banner.md +93 -0
- package/docs/src/{guide → ecosystem}/nuxt.md +81 -47
- package/docs/src/{guide → ecosystem}/themes.md +152 -71
- package/docs/src/{guide → ecosystem}/translations.md +1 -1
- package/docs/src/ecosystem/utils/camel-case.md +31 -0
- package/docs/src/{helpers → ecosystem/utils}/capitalize.md +2 -3
- package/docs/src/ecosystem/utils/check-availability.md +79 -0
- package/docs/src/ecosystem/utils/cookie.md +80 -0
- package/docs/src/ecosystem/utils/debounce-callback.md +38 -0
- package/docs/src/ecosystem/utils/debounce-id.md +69 -0
- package/docs/src/ecosystem/utils/debounce.md +65 -0
- package/docs/src/ecosystem/utils/fetch-locale-ip.md +33 -0
- package/docs/src/ecosystem/utils/format-json.md +33 -0
- package/docs/src/ecosystem/utils/format-phone-number.md +37 -0
- package/docs/src/ecosystem/utils/get-browser-locale.md +29 -0
- package/docs/src/ecosystem/utils/get-error-message.md +39 -0
- package/docs/src/ecosystem/utils/idle-timeout.md +90 -0
- package/docs/src/ecosystem/utils/index.md +60 -0
- package/docs/src/ecosystem/utils/is-client.md +32 -0
- package/docs/src/ecosystem/utils/is-equal.md +38 -0
- package/docs/src/ecosystem/utils/is-server.md +31 -0
- package/docs/src/ecosystem/utils/is-standalone-mode.md +43 -0
- package/docs/src/ecosystem/utils/kebab-case.md +36 -0
- package/docs/src/ecosystem/utils/normalize-string.md +77 -0
- package/docs/src/ecosystem/utils/pascal-case.md +35 -0
- package/docs/src/ecosystem/utils/script-loader.md +77 -0
- package/docs/src/ecosystem/utils/sleep.md +59 -0
- package/docs/src/ecosystem/utils/snake-case.md +36 -0
- package/docs/src/ecosystem/utils/swipe-handler.md +91 -0
- package/docs/src/ecosystem/utils/textarea-autogrow.md +41 -0
- package/docs/src/ecosystem/utils/throttle-id.md +48 -0
- package/docs/src/ecosystem/utils/throttle.md +57 -0
- package/docs/src/ecosystem/utils/truthy-filter.md +31 -0
- package/docs/src/ecosystem/utils/types/deep-key-of.md +48 -0
- package/docs/src/ecosystem/utils/types/deep-partial.md +42 -0
- package/docs/src/ecosystem/utils/types/deep-required.md +39 -0
- package/docs/src/ecosystem/utils/types/flatten-object-keys.md +44 -0
- package/docs/src/ecosystem/utils/types/generic-instance-type.md +42 -0
- package/docs/src/ecosystem/utils/types/infer-maybe-ref.md +35 -0
- package/docs/src/ecosystem/utils/upper-first.md +32 -0
- package/docs/src/ecosystem/utils/user-visibility.md +69 -0
- package/docs/src/guide/getting-started.md +15 -13
- package/docs/src/guide/global-defaults.md +101 -0
- package/docs/src/guide/maz-ui-provider.md +6 -3
- package/docs/src/guide/migration-v4.md +13 -9
- package/docs/src/guide/migration-v5.md +112 -13
- package/docs/src/guide/resolvers.md +7 -7
- package/docs/src/guide/tailwind.md +4 -0
- package/docs/src/guide/vue.md +4 -4
- package/docs/src/index.md +12 -12
- package/docs/src/plugins/aos.md +1 -1
- package/package.json +8 -7
- package/docs/src/helpers/camel-case.md +0 -14
- package/docs/src/helpers/check-availability.md +0 -14
- package/docs/src/helpers/debounce-callback.md +0 -14
- package/docs/src/helpers/debounce-id.md +0 -14
- package/docs/src/helpers/debounce.md +0 -14
- package/docs/src/helpers/is-client.md +0 -14
- package/docs/src/helpers/is-equal.md +0 -14
- package/docs/src/helpers/is-standalone-mode.md +0 -14
- package/docs/src/helpers/kebab-case.md +0 -14
- package/docs/src/helpers/normalize-string.md +0 -14
- package/docs/src/helpers/pascal-case.md +0 -14
- package/docs/src/helpers/script-loader.md +0 -14
- package/docs/src/helpers/sleep.md +0 -14
- package/docs/src/helpers/snake-case.md +0 -14
- package/docs/src/helpers/throttle-id.md +0 -14
- package/docs/src/helpers/throttle.md +0 -14
- /package/docs/src/{guide → ecosystem/icons}/icon-set.md +0 -0
- /package/docs/src/{guide → ecosystem}/mcp.md +0 -0
- /package/docs/src/{helpers → ecosystem/utils}/country-code-to-unicode-flag.md +0 -0
- /package/docs/src/{helpers → ecosystem/utils}/currency.md +0 -0
- /package/docs/src/{helpers → ecosystem/utils}/date.md +0 -0
- /package/docs/src/{helpers → ecosystem/utils}/get-country-flag-url.md +0 -0
- /package/docs/src/{helpers → ecosystem/utils}/number.md +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useDrag
|
|
3
|
+
description: useDrag is a Vue composable that tracks a pointer drag gesture (touch, mouse and pen) in real time, exposing the live offset, distance and direction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# {{ $frontmatter.title }}
|
|
7
|
+
|
|
8
|
+
{{ $frontmatter.description }}
|
|
9
|
+
|
|
10
|
+
## Introduction
|
|
11
|
+
|
|
12
|
+
`useDrag` follows a pointer drag on an element and gives you the **live** offset, distance and direction, plus `onStart` / `onMove` / `onEnd` callbacks. It is the low-level gesture primitive behind components like [MazBottomSheet](../components/maz-bottom-sheet.md) (drag-to-dismiss), and it's useful to build sliders, sortable handles, swipeable cards, bottom sheets, custom carousels, etc.
|
|
13
|
+
|
|
14
|
+
It is built on top of [Pointer Events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events), so it works with **touch, mouse and pen**.
|
|
15
|
+
|
|
16
|
+
::: tip
|
|
17
|
+
For discrete directional swipe detection (left / right / up / down with a threshold) rather than real-time tracking, use [useSwipe](./use-swipe.md).
|
|
18
|
+
:::
|
|
19
|
+
|
|
20
|
+
## Key Features
|
|
21
|
+
|
|
22
|
+
- Real-time pointer tracking (touch, mouse and pen)
|
|
23
|
+
- Live reactive state: `isDragging`, `offsetX`, `offsetY`, `distance`, `direction`
|
|
24
|
+
- `onStart` / `onMove` / `onEnd` callbacks with a full state snapshot
|
|
25
|
+
- Axis locking (`x`, `y` or `both`)
|
|
26
|
+
- Activation threshold, pointer-type filtering and reactive `disabled`
|
|
27
|
+
- Listeners are followed on the document, so the drag keeps working when the pointer leaves the element
|
|
28
|
+
- Automatically attaches/detaches when the target appears/disappears, and cleans up on unmount
|
|
29
|
+
|
|
30
|
+
## Basic Usage
|
|
31
|
+
|
|
32
|
+
Drag the card down: it follows your pointer and snaps back on release.
|
|
33
|
+
|
|
34
|
+
<div class="drag-demo">
|
|
35
|
+
<div
|
|
36
|
+
ref="dragHandle"
|
|
37
|
+
class="drag-card"
|
|
38
|
+
:class="{ '--dragging': dragging }"
|
|
39
|
+
:style="{ transform: dragOffset ? `translateY(${dragOffset}px)` : undefined }"
|
|
40
|
+
>
|
|
41
|
+
{{ dragging ? `Dragging — ${Math.round(dragOffset)}px ${dragDirection ?? ''}` : 'Drag me down' }}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<script lang="ts" setup>
|
|
47
|
+
import { useDrag } from 'maz-ui/composables'
|
|
48
|
+
import { ref } from 'vue'
|
|
49
|
+
|
|
50
|
+
const dragHandle = ref<HTMLElement>()
|
|
51
|
+
const dragOffset = ref(0)
|
|
52
|
+
|
|
53
|
+
const { isDragging: dragging, direction: dragDirection } = useDrag(dragHandle, {
|
|
54
|
+
axis: 'y',
|
|
55
|
+
onMove: ({ offsetY }) => {
|
|
56
|
+
dragOffset.value = Math.max(0, offsetY)
|
|
57
|
+
},
|
|
58
|
+
onEnd: ({ offsetY }) => {
|
|
59
|
+
if (offsetY > 150) {
|
|
60
|
+
// trigger an action when dragged far enough
|
|
61
|
+
}
|
|
62
|
+
dragOffset.value = 0
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<div
|
|
69
|
+
ref="dragHandle"
|
|
70
|
+
class="drag-card"
|
|
71
|
+
:class="{ '--dragging': dragging }"
|
|
72
|
+
:style="{ transform: dragOffset ? `translateY(${dragOffset}px)` : undefined }"
|
|
73
|
+
>
|
|
74
|
+
{{ dragging ? `Dragging — ${Math.round(dragOffset)}px ${dragDirection ?? ''}` : 'Drag me down' }}
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<style>
|
|
79
|
+
.drag-card {
|
|
80
|
+
touch-action: none;
|
|
81
|
+
cursor: grab;
|
|
82
|
+
user-select: none;
|
|
83
|
+
}
|
|
84
|
+
.drag-card:not(.--dragging) {
|
|
85
|
+
transition: transform 0.25s ease;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Options
|
|
91
|
+
|
|
92
|
+
`useDrag(target, options)` - the `target` is a `MaybeRefOrGetter<HTMLElement | string | null | undefined>` and `options` accepts:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface UseDragOptions {
|
|
96
|
+
/**
|
|
97
|
+
* Restrict the tracking to a single axis. Off-axis movement is ignored.
|
|
98
|
+
* @default 'both'
|
|
99
|
+
*/
|
|
100
|
+
axis?: 'x' | 'y' | 'both'
|
|
101
|
+
/**
|
|
102
|
+
* Minimum distance (px) the pointer must travel before the drag becomes active.
|
|
103
|
+
* @default 0
|
|
104
|
+
*/
|
|
105
|
+
threshold?: number
|
|
106
|
+
/**
|
|
107
|
+
* Pointer types allowed to initiate the drag.
|
|
108
|
+
* @default ['mouse', 'touch', 'pen']
|
|
109
|
+
*/
|
|
110
|
+
pointerTypes?: ('mouse' | 'touch' | 'pen')[]
|
|
111
|
+
/**
|
|
112
|
+
* Reactively disable the gesture.
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
disabled?: MaybeRefOrGetter<boolean>
|
|
116
|
+
/**
|
|
117
|
+
* Call `event.preventDefault()` on each pointer move while dragging.
|
|
118
|
+
* @default false
|
|
119
|
+
*/
|
|
120
|
+
preventDefault?: boolean
|
|
121
|
+
/**
|
|
122
|
+
* Call `event.stopPropagation()` on the pointer events.
|
|
123
|
+
* @default false
|
|
124
|
+
*/
|
|
125
|
+
stopPropagation?: boolean
|
|
126
|
+
/**
|
|
127
|
+
* Attach the listeners as soon as the target is available. When `false`, call `start()` manually.
|
|
128
|
+
* @default true
|
|
129
|
+
*/
|
|
130
|
+
immediate?: boolean
|
|
131
|
+
/** Called once when the drag becomes active (threshold reached). */
|
|
132
|
+
onStart?: (state: DragState) => void
|
|
133
|
+
/** Called on every pointer move while dragging. */
|
|
134
|
+
onMove?: (state: DragState) => void
|
|
135
|
+
/** Called when the drag ends (pointer up or cancel). */
|
|
136
|
+
onEnd?: (state: DragState) => void
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Each callback receives a `DragState` snapshot:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
interface DragState {
|
|
144
|
+
/** Pointer offset from the drag start on the X axis (`0` when axis is `'y'`). */
|
|
145
|
+
offsetX: number
|
|
146
|
+
/** Pointer offset from the drag start on the Y axis (`0` when axis is `'x'`). */
|
|
147
|
+
offsetY: number
|
|
148
|
+
/** Absolute distance dragged from the start. */
|
|
149
|
+
distance: number
|
|
150
|
+
/** Dominant direction of the drag. */
|
|
151
|
+
direction: 'up' | 'down' | 'left' | 'right' | undefined
|
|
152
|
+
/** Pointer position when the drag started. */
|
|
153
|
+
startX: number
|
|
154
|
+
startY: number
|
|
155
|
+
/** Current pointer position. */
|
|
156
|
+
x: number
|
|
157
|
+
y: number
|
|
158
|
+
/** The underlying pointer event. */
|
|
159
|
+
event: PointerEvent
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Composable Return
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
interface UseDragReturn {
|
|
167
|
+
/** Whether a drag is currently active (threshold reached). */
|
|
168
|
+
isDragging: Readonly<Ref<boolean>>
|
|
169
|
+
/** Reactive pointer offset from the drag start on the X axis. */
|
|
170
|
+
offsetX: Readonly<Ref<number>>
|
|
171
|
+
/** Reactive pointer offset from the drag start on the Y axis. */
|
|
172
|
+
offsetY: Readonly<Ref<number>>
|
|
173
|
+
/** Reactive absolute distance dragged from the start. */
|
|
174
|
+
distance: Readonly<Ref<number>>
|
|
175
|
+
/** Reactive dominant direction of the drag. */
|
|
176
|
+
direction: Readonly<Ref<'up' | 'down' | 'left' | 'right' | undefined>>
|
|
177
|
+
/** Manually attach the listeners. */
|
|
178
|
+
start: () => void
|
|
179
|
+
/** Manually detach the listeners (also cancels an ongoing drag). */
|
|
180
|
+
stop: () => void
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Notes
|
|
185
|
+
|
|
186
|
+
- Add `touch-action: none` on the draggable element to prevent the browser from scrolling while dragging.
|
|
187
|
+
- The composable attaches automatically on mount (`immediate: true` by default) and cleans up on unmount; use `start()` / `stop()` for manual control.
|
|
188
|
+
- `onEnd` only fires when an actual drag happened (the threshold was reached), so a simple tap will not trigger it.
|
|
189
|
+
- The target can be a ref, a getter or a CSS selector string, and the listeners re-attach automatically when it changes.
|
|
190
|
+
|
|
191
|
+
<script lang="ts" setup>
|
|
192
|
+
import { useDrag } from 'maz-ui/composables/useDrag'
|
|
193
|
+
import { ref } from 'vue'
|
|
194
|
+
|
|
195
|
+
const dragHandle = ref()
|
|
196
|
+
const dragOffset = ref(0)
|
|
197
|
+
|
|
198
|
+
const { isDragging: dragging, direction: dragDirection } = useDrag(dragHandle, {
|
|
199
|
+
axis: 'y',
|
|
200
|
+
onMove: ({ offsetY }) => {
|
|
201
|
+
dragOffset.value = Math.max(0, offsetY)
|
|
202
|
+
},
|
|
203
|
+
onEnd: () => {
|
|
204
|
+
dragOffset.value = 0
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<style>
|
|
210
|
+
.drag-demo {
|
|
211
|
+
border: 1px solid var(--vp-c-divider);
|
|
212
|
+
border-radius: 12px;
|
|
213
|
+
padding: 1rem;
|
|
214
|
+
height: 220px;
|
|
215
|
+
display: flex;
|
|
216
|
+
justify-content: center;
|
|
217
|
+
align-items: flex-start;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
}
|
|
220
|
+
.drag-card {
|
|
221
|
+
touch-action: none;
|
|
222
|
+
cursor: grab;
|
|
223
|
+
user-select: none;
|
|
224
|
+
width: 100%;
|
|
225
|
+
max-width: 320px;
|
|
226
|
+
padding: 2.5rem 1rem;
|
|
227
|
+
border-radius: 12px;
|
|
228
|
+
text-align: center;
|
|
229
|
+
font-size: 1.1rem;
|
|
230
|
+
color: white;
|
|
231
|
+
background: var(--vp-c-brand-1, #6366f1);
|
|
232
|
+
}
|
|
233
|
+
.drag-card:not(.--dragging) {
|
|
234
|
+
transition: transform 0.25s ease;
|
|
235
|
+
}
|
|
236
|
+
.drag-card.--dragging {
|
|
237
|
+
cursor: grabbing;
|
|
238
|
+
}
|
|
239
|
+
</style>
|
|
@@ -151,7 +151,7 @@ Each field in `fieldsStates` contains:
|
|
|
151
151
|
v-model="stateModel.age"
|
|
152
152
|
label="Age (18-100)"
|
|
153
153
|
type="number"
|
|
154
|
-
:hint="stateErrors.age"
|
|
154
|
+
:hint="stateErrors.age"
|
|
155
155
|
:error="!!stateErrors.age"
|
|
156
156
|
:success="stateFields.age.valid"
|
|
157
157
|
/>
|
|
@@ -365,7 +365,7 @@ Requires `useFormField` with `ref` option or `validationEvents`.
|
|
|
365
365
|
:success="eagerEmailValid"
|
|
366
366
|
:class="{ 'has-error-eager': eagerEmailHasError }"
|
|
367
367
|
/>
|
|
368
|
-
<MazBtn type="submit" :loading="eagerSubmitting">Submit</MazBtn>
|
|
368
|
+
<MazBtn type="submit" :loading="eagerSubmitting" :disabled="!eagerIsValid">Submit</MazBtn>
|
|
369
369
|
</form>
|
|
370
370
|
|
|
371
371
|
<template #code>
|
|
@@ -381,7 +381,7 @@ const schema = {
|
|
|
381
381
|
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
const { isSubmitting, handleSubmit } = useFormValidator({
|
|
384
|
+
const { isSubmitting, handleSubmit, isValid } = useFormValidator({
|
|
385
385
|
schema,
|
|
386
386
|
options: {
|
|
387
387
|
mode: 'eager',
|
|
@@ -430,6 +430,8 @@ const {
|
|
|
430
430
|
:error="emailHasError"
|
|
431
431
|
:success="emailValid"
|
|
432
432
|
/>
|
|
433
|
+
|
|
434
|
+
<MazBtn type="submit" :loading="isSubmitting" :disabled="!isValid">Submit</MazBtn>
|
|
433
435
|
</form>
|
|
434
436
|
</template>
|
|
435
437
|
```
|
|
@@ -614,6 +616,8 @@ const { value: name, hasError, errorMessage, isValid } = useFormField<string>('n
|
|
|
614
616
|
|
|
615
617
|
Pass a template ref to `useFormField`. It will automatically detect interactive elements and attach blur listeners.
|
|
616
618
|
|
|
619
|
+
The `ref` option is **reactive**: if the field is rendered conditionally (e.g. with `v-if`), the blur listeners are attached automatically as soon as the element appears in the DOM, and removed when it is unmounted. You don't need to handle anything special.
|
|
620
|
+
|
|
617
621
|
```vue
|
|
618
622
|
<script setup>
|
|
619
623
|
import { useFormField } from 'maz-ui/composables'
|
|
@@ -635,6 +639,41 @@ const { value, errorMessage, hasError } = useFormField<string>('email', {
|
|
|
635
639
|
</template>
|
|
636
640
|
```
|
|
637
641
|
|
|
642
|
+
Because it is reactive, the same code works even when the input is wrapped in a `v-if`:
|
|
643
|
+
|
|
644
|
+
```vue
|
|
645
|
+
<script setup>
|
|
646
|
+
import { useFormField } from 'maz-ui/composables'
|
|
647
|
+
import { useTemplateRef, ref } from 'vue'
|
|
648
|
+
|
|
649
|
+
const isVisible = ref(false)
|
|
650
|
+
|
|
651
|
+
const { value, errorMessage, hasError } = useFormField<string>('email', {
|
|
652
|
+
ref: useTemplateRef('emailRef'),
|
|
653
|
+
formIdentifier: 'my-form',
|
|
654
|
+
})
|
|
655
|
+
</script>
|
|
656
|
+
|
|
657
|
+
<template>
|
|
658
|
+
<MazInput
|
|
659
|
+
v-if="isVisible"
|
|
660
|
+
ref="emailRef"
|
|
661
|
+
v-model="value"
|
|
662
|
+
:hint="errorMessage"
|
|
663
|
+
:error="hasError"
|
|
664
|
+
/>
|
|
665
|
+
</template>
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
You can also pass a raw `HTMLElement` directly. In that case it won't be reactive, so the element must already exist in the DOM:
|
|
669
|
+
|
|
670
|
+
```ts
|
|
671
|
+
const { value } = useFormField<string>('email', {
|
|
672
|
+
ref: document.querySelector('input'),
|
|
673
|
+
formIdentifier: 'my-form',
|
|
674
|
+
})
|
|
675
|
+
```
|
|
676
|
+
|
|
638
677
|
#### Option 2: Using `validationEvents`
|
|
639
678
|
|
|
640
679
|
If your component emits a `blur` event, you can use `v-bind` with `validationEvents`.
|
|
@@ -1074,7 +1113,7 @@ useFormField<FieldType>(
|
|
|
1074
1113
|
options?: {
|
|
1075
1114
|
defaultValue?: FieldType, // Default value for this field
|
|
1076
1115
|
mode?: 'lazy' | 'aggressive' | 'eager' | 'blur' | 'progressive', // Override form mode
|
|
1077
|
-
ref?: Ref<HTMLElement | ComponentInstance
|
|
1116
|
+
ref?: Ref<HTMLElement | ComponentInstance> | HTMLElement, // Reactive template ref (v-if friendly) or raw element
|
|
1078
1117
|
formIdentifier?: string | symbol, // Must match useFormValidator's identifier
|
|
1079
1118
|
}
|
|
1080
1119
|
)
|
|
@@ -1113,7 +1152,7 @@ interface FormValidatorOptions<Model> {
|
|
|
1113
1152
|
interface FormFieldOptions<FieldType> {
|
|
1114
1153
|
defaultValue?: FieldType
|
|
1115
1154
|
mode?: 'eager' | 'lazy' | 'aggressive' | 'blur' | 'progressive'
|
|
1116
|
-
ref?: Ref<HTMLElement | ComponentInstance>
|
|
1155
|
+
ref?: Ref<HTMLElement | ComponentInstance> | HTMLElement
|
|
1117
1156
|
formIdentifier?: string | symbol
|
|
1118
1157
|
}
|
|
1119
1158
|
|
|
@@ -1166,14 +1205,13 @@ const { value, validationEvents } = useFormField<string>('name')
|
|
|
1166
1205
|
// Then: v-bind="validationEvents" on your input
|
|
1167
1206
|
```
|
|
1168
1207
|
|
|
1169
|
-
###
|
|
1208
|
+
### Conditionally Rendered Fields (`v-if`)
|
|
1170
1209
|
|
|
1171
|
-
|
|
1210
|
+
The `ref` option is reactive, so a field wrapped in `v-if` works out of the box: blur listeners are attached as soon as the element is rendered and removed when it is unmounted.
|
|
1172
1211
|
|
|
1173
|
-
|
|
1174
|
-
1. Ensure the ref is bound to an HTML element or Vue component
|
|
1175
|
-
2.
|
|
1176
|
-
3. For custom components, add `data-interactive` attribute
|
|
1212
|
+
If blur validation still doesn't trigger:
|
|
1213
|
+
1. Ensure the `ref` is bound to an HTML element or a Vue component exposing `$el`
|
|
1214
|
+
2. For custom components, add the `data-interactive` attribute
|
|
1177
1215
|
|
|
1178
1216
|
### Mismatched Form Identifiers
|
|
1179
1217
|
|
|
@@ -1310,6 +1348,7 @@ const eagerSchema = {
|
|
|
1310
1348
|
const {
|
|
1311
1349
|
isSubmitting: eagerSubmitting,
|
|
1312
1350
|
handleSubmit: handleEager,
|
|
1351
|
+
isValid: eagerIsValid,
|
|
1313
1352
|
} = useFormValidator({
|
|
1314
1353
|
schema: eagerSchema,
|
|
1315
1354
|
options: { mode: 'eager', scrollToError: '.has-error-eager', identifier: 'form-eager' },
|
|
@@ -12,11 +12,19 @@ description: useSwipe is a Vue composable that simplifies the management of "swi
|
|
|
12
12
|
`useSwipe` allows you to detect and react to swiping movements on an HTML element. It provides you with various information about the swipe movement, such as the direction, distance, start, and end coordinates.
|
|
13
13
|
You can use this information to implement specific interactions in your application, such as scrolling a carousel, opening a side menu, etc.
|
|
14
14
|
|
|
15
|
+
It is built on top of [Pointer Events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events), so it works with **touch, mouse and pen** out of the box.
|
|
16
|
+
|
|
17
|
+
::: tip
|
|
18
|
+
For a real-time drag gesture (follow the pointer, snap back, drag-to-dismiss…) rather than discrete directional detection, use [useDrag](./use-drag.md).
|
|
19
|
+
:::
|
|
20
|
+
|
|
15
21
|
## Key Features
|
|
16
22
|
|
|
17
23
|
- Detects swipes in all 4 directions (left, right, up, down)
|
|
24
|
+
- Works with touch, mouse and pen (Pointer Events)
|
|
18
25
|
- Provides key information about the swipe movement (start/end coordinates, horizontal/vertical distance)
|
|
19
26
|
- Allows you to configure callbacks for each swipe direction
|
|
27
|
+
- Restrict the gesture to specific pointer types
|
|
20
28
|
- Possibility to customize the swipe detection threshold
|
|
21
29
|
- Automatically handles the addition and removal of event listeners
|
|
22
30
|
- Can be used with any HTML element
|
|
@@ -27,7 +35,7 @@ You can use this information to implement specific interactions in your applicat
|
|
|
27
35
|
<p>
|
|
28
36
|
Swipe in any direction<br>
|
|
29
37
|
<span class="maz:text-xs maz:text-muted">
|
|
30
|
-
(
|
|
38
|
+
(works with touch, mouse and pen)
|
|
31
39
|
</span>
|
|
32
40
|
<br><br>
|
|
33
41
|
Last swipe direction: {{lastSwipeDirection || 'None'}}
|
|
@@ -74,7 +82,7 @@ onUnmounted(() => {
|
|
|
74
82
|
<p>
|
|
75
83
|
Swipe in any direction<br>
|
|
76
84
|
<span class="maz:text-sm maz:text-muted">
|
|
77
|
-
(
|
|
85
|
+
(works with touch, mouse and pen)
|
|
78
86
|
</span>
|
|
79
87
|
<br><br>
|
|
80
88
|
Last swipe direction: {{ lastSwipeDirection || 'None' }}
|
|
@@ -153,35 +161,41 @@ interface UseSwipeOptions {
|
|
|
153
161
|
*/
|
|
154
162
|
element: HTMLElement | string | Ref<HTMLElement>
|
|
155
163
|
/** Callback executed when a left swipe is detected. */
|
|
156
|
-
onLeft?: (event:
|
|
164
|
+
onLeft?: (event: PointerEvent) => void
|
|
157
165
|
/** Callback executed when a right swipe is detected. */
|
|
158
|
-
onRight?: (event:
|
|
166
|
+
onRight?: (event: PointerEvent) => void
|
|
159
167
|
/** Callback executed when an up swipe is detected. */
|
|
160
|
-
onUp?: (event:
|
|
168
|
+
onUp?: (event: PointerEvent) => void
|
|
161
169
|
/** Callback executed when a down swipe is detected. */
|
|
162
|
-
onDown?: (event:
|
|
170
|
+
onDown?: (event: PointerEvent) => void
|
|
163
171
|
/**
|
|
164
172
|
* The minimum distance the swipe must travel to be considered valid.
|
|
165
173
|
* @default 50
|
|
166
174
|
*/
|
|
167
175
|
threshold?: number
|
|
168
176
|
/**
|
|
169
|
-
*
|
|
177
|
+
* Pointer types that can trigger the swipe.
|
|
178
|
+
* @default ['mouse', 'touch', 'pen']
|
|
179
|
+
*/
|
|
180
|
+
pointerTypes?: ('mouse' | 'touch' | 'pen')[]
|
|
181
|
+
/**
|
|
182
|
+
* Whether to prevent the default behavior of the pointer move event
|
|
183
|
+
* (the move listener becomes non-passive when enabled).
|
|
170
184
|
* @default false
|
|
171
185
|
*/
|
|
172
|
-
|
|
186
|
+
preventDefaultOnMove?: boolean
|
|
173
187
|
/**
|
|
174
188
|
* Whether to prevent the default behavior of the mousewheel event.
|
|
175
189
|
* @default false
|
|
176
190
|
*/
|
|
177
191
|
preventDefaultOnMouseWheel?: boolean
|
|
178
192
|
/**
|
|
179
|
-
* Whether to
|
|
193
|
+
* Whether to start listening immediately on instantiation.
|
|
180
194
|
* @default false
|
|
181
195
|
*/
|
|
182
196
|
immediate?: boolean
|
|
183
197
|
/**
|
|
184
|
-
* Whether to trigger the swipe event only on
|
|
198
|
+
* Whether to trigger the swipe event only on pointer up.
|
|
185
199
|
* @default false
|
|
186
200
|
*/
|
|
187
201
|
triggerOnEnd?: boolean
|
|
@@ -220,4 +234,5 @@ interface UseSwipeReturn {
|
|
|
220
234
|
- If you use the composable in a Vue component, make sure to call it in the `setup()` and clean up the event listeners in the `onUnmounted()`.
|
|
221
235
|
- The composable automatically handles the addition and removal of event listeners based on the provided options.
|
|
222
236
|
- You can customize the swipe detection threshold by modifying the `threshold` option.
|
|
223
|
-
- If you want to prevent the default behavior of
|
|
237
|
+
- If you want to prevent the default behavior of pointer move or mousewheel events, you can set the `preventDefaultOnMove` and `preventDefaultOnMouseWheel` options, respectively.
|
|
238
|
+
- Use the `pointerTypes` option to restrict the gesture to specific input types (e.g. `['touch']` for touch-only).
|
|
@@ -11,8 +11,7 @@ description: vClickOutside is a Vue directive to trigger a function when the use
|
|
|
11
11
|
|
|
12
12
|
<ComponentDemo>
|
|
13
13
|
<div
|
|
14
|
-
|
|
15
|
-
class="maz:flex maz:flex-center maz:rounded-md"
|
|
14
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:p-12 maz:bg-surface-300"
|
|
16
15
|
>
|
|
17
16
|
<MazCard v-click-outside="clikedOutside">
|
|
18
17
|
Click outside me
|
|
@@ -21,8 +20,7 @@ description: vClickOutside is a Vue directive to trigger a function when the use
|
|
|
21
20
|
|
|
22
21
|
<div
|
|
23
22
|
v-if="hasClikedOutside"
|
|
24
|
-
|
|
25
|
-
class="maz:flex maz:flex-center maz:rounded-md"
|
|
23
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:bg-sucess maz:text-success-foreground maz:p-4 maz:mt-4"
|
|
26
24
|
>
|
|
27
25
|
You clicked outside
|
|
28
26
|
</div>
|
|
@@ -44,8 +42,7 @@ function clikedOutside() {
|
|
|
44
42
|
|
|
45
43
|
<template>
|
|
46
44
|
<div
|
|
47
|
-
|
|
48
|
-
class="flex flex-center rounded"
|
|
45
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:p-12 maz:bg-surface-300"
|
|
49
46
|
>
|
|
50
47
|
<MazCard v-click-outside="clikedOutside">
|
|
51
48
|
Click outside me
|
|
@@ -54,8 +51,7 @@ function clikedOutside() {
|
|
|
54
51
|
|
|
55
52
|
<div
|
|
56
53
|
v-if="hasClikedOutside"
|
|
57
|
-
|
|
58
|
-
class="flex flex-center rounded"
|
|
54
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:bg-sucess maz:text-success-foreground maz:p-4 maz:mt-4"
|
|
59
55
|
>
|
|
60
56
|
You clicked outside
|
|
61
57
|
</div>
|
|
@@ -71,8 +67,7 @@ The directive can accept an options object to customize its behavior:
|
|
|
71
67
|
|
|
72
68
|
<ComponentDemo>
|
|
73
69
|
<div
|
|
74
|
-
|
|
75
|
-
class="maz:flex maz:flex-center maz:rounded-md"
|
|
70
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:p-12 maz:bg-surface-300"
|
|
76
71
|
>
|
|
77
72
|
<MazCard v-click-outside="{ callback: clickedOutsideWithIgnore, ignore: ['.ignore-me'] }">
|
|
78
73
|
<div class="maz:p-4">
|
|
@@ -86,8 +81,7 @@ The directive can accept an options object to customize its behavior:
|
|
|
86
81
|
|
|
87
82
|
<div
|
|
88
83
|
v-if="hasClickedOutsideWithIgnore"
|
|
89
|
-
|
|
90
|
-
class="maz:flex maz:flex-center maz:rounded-md"
|
|
84
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:bg-sucess maz:text-success-foreground maz:p-4 maz:mt-4"
|
|
91
85
|
>
|
|
92
86
|
You clicked outside (button clicks are ignored)
|
|
93
87
|
</div>
|
|
@@ -128,8 +122,7 @@ The directive can be configured to trigger only once:
|
|
|
128
122
|
|
|
129
123
|
<ComponentDemo>
|
|
130
124
|
<div
|
|
131
|
-
|
|
132
|
-
class="maz:flex maz:flex-center maz:rounded-md"
|
|
125
|
+
class="maz:flex maz:flex-center maz:rounded-md maz:p-12 maz:bg-surface-300"
|
|
133
126
|
>
|
|
134
127
|
<MazCard v-click-outside="{ callback: clickedOnce, once: true }">
|
|
135
128
|
Click outside me (works only once)
|
|
@@ -249,7 +242,7 @@ app.mount('#app')
|
|
|
249
242
|
|
|
250
243
|
### Nuxt
|
|
251
244
|
|
|
252
|
-
Please refer to the [Nuxt module documentation](./../
|
|
245
|
+
Please refer to the [Nuxt module documentation](./../ecosystem/nuxt.md) for more information.
|
|
253
246
|
|
|
254
247
|
<script lang="ts" setup>
|
|
255
248
|
import { ref } from 'vue'
|