@maz-ui/mcp 5.0.0-beta.37 → 5.0.0-beta.39
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/dist/mcp.mjs +1 -1
- package/docs/generated-docs/maz-bottom-sheet.doc.md +16 -13
- package/docs/generated-docs/maz-btn.doc.md +21 -20
- package/docs/generated-docs/maz-textarea.doc.md +27 -25
- package/docs/src/components/maz-bottom-sheet.md +300 -169
- package/docs/src/components/maz-textarea.md +58 -0
- package/docs/src/composables/use-drag.md +239 -0
- package/docs/src/composables/use-swipe.md +26 -11
- package/package.json +5 -5
package/dist/mcp.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
|
7
7
|
import { resolve, join, dirname } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
|
|
10
|
-
const version = "5.0.0-beta.
|
|
10
|
+
const version = "5.0.0-beta.39";
|
|
11
11
|
|
|
12
12
|
class MetadataExtractor {
|
|
13
13
|
extract(name, type, content, manualTags = []) {
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
## Props
|
|
2
|
-
|
|
3
|
-
| Name | Description | Type | Required |
|
|
4
|
-
| --------------------- | ----------------------------------- | --------- | -------- |
|
|
5
|
-
| **model-value** | `v-model` <br/> | `boolean` | No |
|
|
6
|
-
| **hide-close-button** | Remove the close button | `boolean` | No |
|
|
7
|
-
| **padding** | Remove the padding on the container | `boolean` | No |
|
|
8
|
-
|
|
9
1
|
## Events
|
|
10
2
|
|
|
11
3
|
| Event name | Properties | Description |
|
|
12
4
|
| ------------------ | ---------- | --------------------------------------- |
|
|
13
|
-
| close | | Emitted when the component is closed |
|
|
14
|
-
| open | | Emitted when the component is opened |
|
|
15
5
|
| update:model-value | | Emitted when the model value is updated |
|
|
6
|
+
| open | | Emitted when the component is opened |
|
|
7
|
+
| close | | Emitted when the component is closed |
|
|
16
8
|
|
|
17
9
|
## Slots
|
|
18
10
|
|
|
19
|
-
| Name | Description
|
|
20
|
-
| ------- |
|
|
21
|
-
|
|
|
11
|
+
| Name | Description | Bindings |
|
|
12
|
+
| ------- | -------------------------------------------------------------------------- | ------------------------------------- |
|
|
13
|
+
| handle | Drag handle (grab bar) displayed at the top when `swipeToClose` is enabled | |
|
|
14
|
+
| header | Header slot | **close** `Function` - close function |
|
|
15
|
+
| icon | Icon slot in the header | |
|
|
16
|
+
| title | Title slot in the header | |
|
|
17
|
+
| default | Default content | **close** `Function` - close function |
|
|
18
|
+
| footer | Footer slot | **close** `Function` - close function |
|
|
19
|
+
|
|
20
|
+
## Expose
|
|
21
|
+
|
|
22
|
+
### close
|
|
23
|
+
|
|
24
|
+
> Close the bottom sheet <br/>`@description` This is used to close the bottom sheet
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
## Props
|
|
2
2
|
|
|
3
|
-
| Name
|
|
4
|
-
|
|
|
5
|
-
| **text**
|
|
6
|
-
| **size**
|
|
7
|
-
| **color**
|
|
8
|
-
| **text-color**
|
|
9
|
-
| **type**
|
|
10
|
-
| **rounded-size**
|
|
11
|
-
| **outlined**
|
|
12
|
-
| **pastel**
|
|
13
|
-
| **block**
|
|
14
|
-
| **loading**
|
|
15
|
-
| **disabled**
|
|
16
|
-
| **fab**
|
|
17
|
-
| **icon**
|
|
18
|
-
| **start-icon**
|
|
19
|
-
| **end-icon**
|
|
20
|
-
| **padding**
|
|
21
|
-
| **justify**
|
|
22
|
-
| **active**
|
|
3
|
+
| Name | Description | Type | Required | Default | Possible values |
|
|
4
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
5
|
+
| **text** | The text of the button, if not provided, the slot will be used | `string` | No | `undefined` | - |
|
|
6
|
+
| **size** | Predifined sizes of the button | `MazSize` | No | `undefined` | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs' \| 'mini'` |
|
|
7
|
+
| **color** | The color of the button | `MazColor \| "surface"` | No | `undefined` | `'primary' \| 'secondary' \| 'accent' \| 'info' \| 'success' \| 'warning' \| 'destructive' \| 'contrast' \| 'transparent' \| 'surface'` |
|
|
8
|
+
| **text-color** | The text color of the button | `MazColor \| "muted"` | No | `undefined` | `primary' \| 'secondary' \| 'accent' \| 'info' \| 'success' \| 'warning' \| 'destructive' \| 'contrast' \| 'transparent' \| 'muted'` |
|
|
9
|
+
| **type** | The type of the button | `"submit" \| "reset" \| "button"` | No | `undefined` | `'submit' \| 'reset' \| 'button'` |
|
|
10
|
+
| **rounded-size** | Size of the rounded | `MazRoundedSize` | No | `md` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` |
|
|
11
|
+
| **outlined** | If true, the button have the "border" style | `boolean` | No | `false` | - |
|
|
12
|
+
| **pastel** | If true, the button will have a pastel color | `boolean` | No | `false` | - |
|
|
13
|
+
| **block** | If true, the button will have a full width | `boolean` | No | `false` | - |
|
|
14
|
+
| **loading** | Enable the button loader | `boolean` | No | `false` | - |
|
|
15
|
+
| **disabled** | Disable the button | `boolean` | No | `false` | - |
|
|
16
|
+
| **fab** | If true, the button will have a fab style | `boolean` | No | `false` | - |
|
|
17
|
+
| **icon** | The icon to display in the fab variant. Accepts a bare value (Vue component, raw SVG string, URL or `data:` URI) for the common case, or a full `MazIconProps` object for fine-grained control (size, title, svgAttributes, fallback, flipIconForRtl, …). | `MazIconLike` | No | `undefined` | - |
|
|
18
|
+
| **start-icon** | The icon to display on the inline-start edge (left in LTR, right in RTL). Accepts a bare value or a full `MazIconProps` object. | `MazIconLike` | No | `undefined` | - |
|
|
19
|
+
| **end-icon** | The icon to display on the inline-end edge (right in LTR, left in RTL). Accepts a bare value or a full `MazIconProps` object. | `MazIconLike` | No | `undefined` | - |
|
|
20
|
+
| **padding** | If true, the button will have no padding | `boolean` | No | `true` | - |
|
|
21
|
+
| **justify** | Choose how the elements are aligned in the button | `"start" \| "end" \| "center" \| "space-between" \| "space-around" \| "space-evenly"` | No | `undefined` | - |
|
|
22
|
+
| **active** | If true, the button will have an active state | `boolean` | No | `false` | - |
|
|
23
|
+
| **overflow-hidden** | If true, the button will have an overflow-hidden style | `boolean` | No | `false` | - |
|
|
23
24
|
|
|
24
25
|
## Slots
|
|
25
26
|
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
## Props
|
|
2
2
|
|
|
3
|
-
| Name | Description
|
|
4
|
-
| ------------------ |
|
|
5
|
-
| **style** | Style attribut of the component root element
|
|
6
|
-
| **class** | Class attribut of the component root element
|
|
7
|
-
| **model-value** | `v-model` <br/>
|
|
8
|
-
| **id** | The id of the textarea
|
|
9
|
-
| **name** | The name of the textarea
|
|
10
|
-
| **label** | The label of the textarea
|
|
11
|
-
| **placeholder** | The placeholder of the textarea
|
|
12
|
-
| **required** | If the textarea is required
|
|
13
|
-
| **disabled** | If the textarea is disabled
|
|
14
|
-
| **readonly** | If the textarea is readonly
|
|
15
|
-
| **error** | If the textarea has an error
|
|
16
|
-
| **success** | If the textarea has a success
|
|
17
|
-
| **warning** | If the textarea has a warning
|
|
18
|
-
| **hint** | The hint of the textarea
|
|
19
|
-
| **color** | The color of the textarea
|
|
20
|
-
| **rounded-size** | Size radius of the component's border
|
|
21
|
-
| **
|
|
22
|
-
| **
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
25
|
-
| **
|
|
26
|
-
| **
|
|
27
|
-
| **
|
|
3
|
+
| Name | Description | Type | Required | Default | Possible values |
|
|
4
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | -------- | ----------- | ------------------------------------------------------------------------------------- |
|
|
5
|
+
| **style** | Style attribut of the component root element | `Native type` | No | `undefined` | - |
|
|
6
|
+
| **class** | Class attribut of the component root element | `Native type` | No | `undefined` | - |
|
|
7
|
+
| **model-value** | `v-model` <br/> | `T` | No | `undefined` | - |
|
|
8
|
+
| **id** | The id of the textarea | `string` | No | `undefined` | - |
|
|
9
|
+
| **name** | The name of the textarea | `string` | No | `undefined` | - |
|
|
10
|
+
| **label** | The label of the textarea | `string` | No | `undefined` | - |
|
|
11
|
+
| **placeholder** | The placeholder of the textarea | `string` | No | `undefined` | - |
|
|
12
|
+
| **required** | If the textarea is required | `boolean` | No | `undefined` | - |
|
|
13
|
+
| **disabled** | If the textarea is disabled | `boolean` | No | `undefined` | - |
|
|
14
|
+
| **readonly** | If the textarea is readonly | `boolean` | No | `undefined` | - |
|
|
15
|
+
| **error** | If the textarea has an error | `boolean` | No | `undefined` | - |
|
|
16
|
+
| **success** | If the textarea has a success | `boolean` | No | `undefined` | - |
|
|
17
|
+
| **warning** | If the textarea has a warning | `boolean` | No | `undefined` | - |
|
|
18
|
+
| **hint** | The hint of the textarea | `string` | No | `undefined` | - |
|
|
19
|
+
| **color** | The color of the textarea | `MazColor` | No | `undefined` | - |
|
|
20
|
+
| **rounded-size** | Size radius of the component's border | `MazRoundedSize` | No | `md` | - |
|
|
21
|
+
| **size** | Controls the padding (height) and text size of the textarea, mirroring MazInput sizes. Combined with `minRows`, e.g. `size="md" :min-rows="1"` matches a regular input height. | `MazSize` | No | `md` | `mini, xs, sm, md, lg, xl` |
|
|
22
|
+
| **padding** | If the textarea has a padding | `boolean` | No | `true` | - |
|
|
23
|
+
| **transparent** | If the textarea has a transparent background | `boolean` | No | `false` | - |
|
|
24
|
+
| **border** | If the textarea has no border | `boolean` | No | `false` | - |
|
|
25
|
+
| **autogrow** | If the textarea should autogrow based on its content | `boolean` | No | `true` | - |
|
|
26
|
+
| **min-rows** | Minimum number of rows displayed by the textarea (initial/minimum height). With autogrow enabled, the textarea still grows beyond this with its content. | `number` | No | `3` | - |
|
|
27
|
+
| **append-justify** | The alignment of the append slot | `"start" \| "end" \| "center" \| "space-between" \| "space-around" \| "space-evenly"` | No | `end` | `'start' \| 'end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly'` |
|
|
28
|
+
| **top-label** | Static label displayed above the textarea. Unlike the floating label, this remains fixed | `string` | No | `undefined` | - |
|
|
29
|
+
| **assistive-text** | Helper text displayed below the input to provide additional context or validation feedback<br/>**Example:** `"Must contain at least 8 characters"` | `string` | No | `undefined` | - |
|
|
28
30
|
|
|
29
31
|
## Events
|
|
30
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: MazBottomSheet
|
|
3
|
-
description: MazBottomSheet is a standalone component like a simple dialog but at the bottom of screen. Useful for mobile UX.
|
|
3
|
+
description: MazBottomSheet is a standalone component like a simple dialog but anchored at the bottom of the screen. Full-width on mobile and constrained/centered on desktop, with a header (icon, title, close button), a footer and many replaceable slots. Useful for mobile UX.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# {{ $frontmatter.title }}
|
|
@@ -10,14 +10,250 @@ description: MazBottomSheet is a standalone component like a simple dialog but a
|
|
|
10
10
|
<!--@include: ./../../.vitepress/mixins/getting-started.md-->
|
|
11
11
|
|
|
12
12
|
::: tip
|
|
13
|
-
This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop.md), so you can implement this component anywhere and it inherits all its props
|
|
13
|
+
This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop.md), so you can implement this component anywhere and it inherits all its props (`persistent`, `closeOnEscape`, etc.)
|
|
14
14
|
:::
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Basic usage
|
|
17
|
+
|
|
18
|
+
The sheet has a header (with the `title` and a close button) and an optional `footer` slot. It opens full-width on mobile and is constrained/centered on desktop (see [Max width](#max-width)).
|
|
19
|
+
|
|
20
|
+
<ComponentDemo expanded>
|
|
21
|
+
<MazBtn @click="basicOpened = true">Open Bottom Sheet</MazBtn>
|
|
22
|
+
|
|
23
|
+
<MazBottomSheet v-model="basicOpened" title="Bottom Sheet Title">
|
|
24
|
+
<p>Your content goes here.</p>
|
|
25
|
+
<template #footer="{ close }">
|
|
26
|
+
<MazBtn color="transparent" @click="close">Cancel</MazBtn>
|
|
27
|
+
<MazBtn @click="close">Confirm</MazBtn>
|
|
28
|
+
</template>
|
|
29
|
+
</MazBottomSheet>
|
|
30
|
+
|
|
31
|
+
<template #code>
|
|
32
|
+
|
|
33
|
+
```vue
|
|
34
|
+
<script setup>
|
|
35
|
+
import MazBottomSheet from 'maz-ui/components/MazBottomSheet'
|
|
36
|
+
import { ref } from 'vue'
|
|
37
|
+
|
|
38
|
+
const basicOpened = ref(false)
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template>
|
|
42
|
+
<MazBtn @click="basicOpened = true">
|
|
43
|
+
Open Bottom Sheet
|
|
44
|
+
</MazBtn>
|
|
45
|
+
|
|
46
|
+
<MazBottomSheet v-model="basicOpened" title="Bottom Sheet Title">
|
|
47
|
+
<p>Your content goes here.</p>
|
|
48
|
+
|
|
49
|
+
<template #footer="{ close }">
|
|
50
|
+
<MazBtn color="transparent" @click="close">
|
|
51
|
+
Cancel
|
|
52
|
+
</MazBtn>
|
|
53
|
+
<MazBtn @click="close">
|
|
54
|
+
Confirm
|
|
55
|
+
</MazBtn>
|
|
56
|
+
</template>
|
|
57
|
+
</MazBottomSheet>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
</template>
|
|
62
|
+
</ComponentDemo>
|
|
63
|
+
|
|
64
|
+
## Swipe to close (iOS-like)
|
|
65
|
+
|
|
66
|
+
By default a drag handle (grab bar) is shown at the top of the sheet, and you can **close the sheet by dragging that handle down** - the sheet follows your pointer and either dismisses (past the threshold) or snaps back. This works with both touch and mouse.
|
|
67
|
+
|
|
68
|
+
Disable it with `:swipe-to-close="false"` (the handle is then hidden too), or replace the handle with the `#handle` slot. A `persistent` sheet never dismisses on swipe: it snaps back instead.
|
|
69
|
+
|
|
70
|
+
<ComponentDemo>
|
|
71
|
+
<MazBtn @click="swipeOpened = true">Open swipeable sheet</MazBtn>
|
|
72
|
+
|
|
73
|
+
<MazBottomSheet v-model="swipeOpened" title="Drag me down">
|
|
74
|
+
<p>Grab the bar at the top and drag down to close, or release before the threshold to snap back.</p>
|
|
75
|
+
</MazBottomSheet>
|
|
76
|
+
|
|
77
|
+
<template #code>
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<!-- enabled by default -->
|
|
81
|
+
<MazBottomSheet v-model="swipeOpened" title="Drag me down">
|
|
82
|
+
<p>Grab the bar at the top and drag down to close.</p>
|
|
83
|
+
</MazBottomSheet>
|
|
84
|
+
|
|
85
|
+
<!-- disable the handle and the gesture -->
|
|
86
|
+
<MazBottomSheet v-model="swipeOpened" :swipe-to-close="false" title="No swipe" />
|
|
87
|
+
|
|
88
|
+
<!-- custom handle -->
|
|
89
|
+
<MazBottomSheet v-model="swipeOpened" title="Custom handle">
|
|
90
|
+
<template #handle>
|
|
91
|
+
<span class="maz:h-1.5 maz:w-12 maz:rounded-full maz:bg-primary" />
|
|
92
|
+
</template>
|
|
93
|
+
</MazBottomSheet>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
</template>
|
|
97
|
+
</ComponentDemo>
|
|
98
|
+
|
|
99
|
+
## Header with an icon
|
|
100
|
+
|
|
101
|
+
Pass an `icon` (an icon value or a full `MazIconProps` object) to display it on the left of the title.
|
|
102
|
+
|
|
103
|
+
<ComponentDemo>
|
|
104
|
+
<MazBtn @click="iconOpened = true">Open with icon</MazBtn>
|
|
105
|
+
|
|
106
|
+
<MazBottomSheet v-model="iconOpened" title="Notifications" icon="/bell.svg">
|
|
107
|
+
<p>You have 3 new notifications.</p>
|
|
108
|
+
<template #footer="{ close }">
|
|
109
|
+
<MazBtn @click="close">Mark all as read</MazBtn>
|
|
110
|
+
</template>
|
|
111
|
+
</MazBottomSheet>
|
|
112
|
+
|
|
113
|
+
<template #code>
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<MazBottomSheet v-model="iconOpened" title="Notifications" icon="/bell.svg">
|
|
117
|
+
<p>You have 3 new notifications.</p>
|
|
118
|
+
|
|
119
|
+
<template #footer="{ close }">
|
|
120
|
+
<MazBtn @click="close">Mark all as read</MazBtn>
|
|
121
|
+
</template>
|
|
122
|
+
</MazBottomSheet>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
</template>
|
|
126
|
+
</ComponentDemo>
|
|
127
|
+
|
|
128
|
+
## Replaceable slots
|
|
129
|
+
|
|
130
|
+
Everything in the header is replaceable: use `#icon` and `#title` to customize parts of the default header, or `#header` to replace the whole header (you get the `close` function as a binding).
|
|
131
|
+
|
|
132
|
+
<ComponentDemo>
|
|
133
|
+
<MazBtn @click="slotsOpened = true">Open custom header</MazBtn>
|
|
134
|
+
|
|
135
|
+
<MazBottomSheet v-model="slotsOpened">
|
|
136
|
+
<template #icon>
|
|
137
|
+
<MazAvatar src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100" size="sm" />
|
|
138
|
+
</template>
|
|
139
|
+
<template #title>
|
|
140
|
+
<span class="maz:text-primary">Custom title</span>
|
|
141
|
+
</template>
|
|
142
|
+
<p>The icon and the title slots are fully replaceable.</p>
|
|
143
|
+
</MazBottomSheet>
|
|
144
|
+
|
|
145
|
+
<template #code>
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<MazBottomSheet v-model="slotsOpened">
|
|
149
|
+
<template #icon>
|
|
150
|
+
<MazAvatar src="/avatar.jpg" size="sm" />
|
|
151
|
+
</template>
|
|
152
|
+
|
|
153
|
+
<template #title>
|
|
154
|
+
<span class="maz:text-primary">Custom title</span>
|
|
155
|
+
</template>
|
|
156
|
+
|
|
157
|
+
<p>The icon and the title slots are fully replaceable.</p>
|
|
158
|
+
</MazBottomSheet>
|
|
159
|
+
|
|
160
|
+
<!-- or replace the whole header -->
|
|
161
|
+
<MazBottomSheet v-model="slotsOpened">
|
|
162
|
+
<template #header="{ close }">
|
|
163
|
+
<div class="maz:flex maz:items-center maz:justify-between maz:p-4">
|
|
164
|
+
<strong>My header</strong>
|
|
165
|
+
<MazBtn size="sm" color="transparent" @click="close">Close</MazBtn>
|
|
166
|
+
</div>
|
|
167
|
+
</template>
|
|
168
|
+
</MazBottomSheet>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
</template>
|
|
172
|
+
</ComponentDemo>
|
|
173
|
+
|
|
174
|
+
## Max width
|
|
175
|
+
|
|
176
|
+
On mobile the sheet is always **full-width**. From the tablet breakpoint and up, it is **constrained** to `max-width` and centered horizontally. The default `max-width` is `35rem`; pass the `max-width` prop (a [`MazSizeUnit`](#props)) to override it.
|
|
177
|
+
|
|
178
|
+
<ComponentDemo>
|
|
179
|
+
<MazBtn @click="wideOpened = true">Open wide sheet</MazBtn>
|
|
180
|
+
|
|
181
|
+
<MazBottomSheet v-model="wideOpened" title="Wide sheet" max-width="60rem">
|
|
182
|
+
<p>This sheet is constrained to 60rem on desktop and full-width on mobile.</p>
|
|
183
|
+
</MazBottomSheet>
|
|
184
|
+
|
|
185
|
+
<template #code>
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<MazBottomSheet v-model="wideOpened" title="Wide sheet" max-width="60rem">
|
|
189
|
+
<p>This sheet is constrained to 60rem on desktop and full-width on mobile.</p>
|
|
190
|
+
</MazBottomSheet>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
</template>
|
|
194
|
+
</ComponentDemo>
|
|
195
|
+
|
|
196
|
+
## Without header
|
|
197
|
+
|
|
198
|
+
Hide the whole header with `hide-header`, or only the close button with `hide-close-button`.
|
|
199
|
+
|
|
200
|
+
<ComponentDemo>
|
|
201
|
+
<MazBtn @click="noHeaderOpened = true">Open without header</MazBtn>
|
|
202
|
+
|
|
203
|
+
<MazBottomSheet v-model="noHeaderOpened" hide-header>
|
|
204
|
+
<div class="maz:flex maz:flex-col maz:gap-3 maz:text-center">
|
|
205
|
+
<p>No header here, you control the whole layout.</p>
|
|
206
|
+
<MazBtn @click="noHeaderOpened = false">Got it</MazBtn>
|
|
207
|
+
</div>
|
|
208
|
+
</MazBottomSheet>
|
|
209
|
+
|
|
210
|
+
<template #code>
|
|
211
|
+
|
|
212
|
+
```html
|
|
213
|
+
<MazBottomSheet v-model="noHeaderOpened" hide-header>
|
|
214
|
+
<div class="maz:flex maz:flex-col maz:gap-3 maz:text-center">
|
|
215
|
+
<p>No header here, you control the whole layout.</p>
|
|
216
|
+
<MazBtn @click="noHeaderOpened = false">Got it</MazBtn>
|
|
217
|
+
</div>
|
|
218
|
+
</MazBottomSheet>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
</template>
|
|
222
|
+
</ComponentDemo>
|
|
223
|
+
|
|
224
|
+
## Persistent
|
|
225
|
+
|
|
226
|
+
A `persistent` sheet cannot be closed by clicking outside or pressing escape, and the close button is removed. Provide your own action to close it.
|
|
227
|
+
|
|
228
|
+
<ComponentDemo>
|
|
229
|
+
<MazBtn @click="persistentOpened = true">Open persistent sheet</MazBtn>
|
|
230
|
+
|
|
231
|
+
<MazBottomSheet v-model="persistentOpened" title="Action required" icon="/exclamation-triangle.svg" persistent>
|
|
232
|
+
<p>You must confirm before closing this sheet.</p>
|
|
233
|
+
<template #footer>
|
|
234
|
+
<MazBtn @click="persistentOpened = false">I understand</MazBtn>
|
|
235
|
+
</template>
|
|
236
|
+
</MazBottomSheet>
|
|
237
|
+
|
|
238
|
+
<template #code>
|
|
239
|
+
|
|
240
|
+
```html
|
|
241
|
+
<MazBottomSheet v-model="persistentOpened" title="Action required" persistent>
|
|
242
|
+
<p>You must confirm before closing this sheet.</p>
|
|
243
|
+
|
|
244
|
+
<template #footer>
|
|
245
|
+
<MazBtn @click="persistentOpened = false">I understand</MazBtn>
|
|
246
|
+
</template>
|
|
247
|
+
</MazBottomSheet>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
</template>
|
|
251
|
+
</ComponentDemo>
|
|
252
|
+
|
|
253
|
+
## Full example
|
|
17
254
|
|
|
18
255
|
<ComponentDemo expanded>
|
|
19
256
|
<div class="maz:flex maz:flex-col maz:gap-4">
|
|
20
|
-
<!-- Product Selection Demo -->
|
|
21
257
|
<MazCard>
|
|
22
258
|
<template #title>
|
|
23
259
|
<div class="maz:flex maz:items-center maz:gap-3">
|
|
@@ -60,9 +296,8 @@ This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop
|
|
|
60
296
|
</div>
|
|
61
297
|
</MazCard>
|
|
62
298
|
</div>
|
|
63
|
-
<MazBottomSheet v-model="isProductOpen" title="Customize Your Shoes">
|
|
299
|
+
<MazBottomSheet v-model="isProductOpen" title="Customize Your Shoes" icon="/cog.svg" :padding="false">
|
|
64
300
|
<div class="maz:space-y-6 maz:p-6">
|
|
65
|
-
<!-- Size Selection -->
|
|
66
301
|
<div>
|
|
67
302
|
<h4 class="maz:text-lg maz:font-semibold maz:mb-3">Select Size</h4>
|
|
68
303
|
<div class="maz:grid maz:grid-cols-4 maz:gap-2">
|
|
@@ -103,19 +338,17 @@ This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop
|
|
|
103
338
|
:max="10"
|
|
104
339
|
/>
|
|
105
340
|
</div>
|
|
106
|
-
<div class="maz:flex maz:gap-3 maz:pt-4">
|
|
107
|
-
<MazBtn color="primary" class="maz:flex-1" @click="addToCart">
|
|
108
|
-
<MazIcon icon="/shopping-cart.svg" class="maz:me-2" />
|
|
109
|
-
Add to Cart (${{ (129.99 * quantity).toFixed(2) }})
|
|
110
|
-
</MazBtn>
|
|
111
|
-
<MazBtn color="secondary" @click="isProductOpen = false">
|
|
112
|
-
Cancel
|
|
113
|
-
</MazBtn>
|
|
114
|
-
</div>
|
|
115
341
|
</div>
|
|
342
|
+
<template #footer="{ close }">
|
|
343
|
+
<MazBtn color="transparent" @click="close">Cancel</MazBtn>
|
|
344
|
+
<MazBtn color="primary" @click="addToCart">
|
|
345
|
+
<MazIcon icon="/shopping-cart.svg" class="maz:me-2" />
|
|
346
|
+
Add to Cart (${{ (129.99 * quantity).toFixed(2) }})
|
|
347
|
+
</MazBtn>
|
|
348
|
+
</template>
|
|
116
349
|
</MazBottomSheet>
|
|
117
350
|
|
|
118
|
-
<MazBottomSheet v-model="isUserOpen" title="Profile Settings">
|
|
351
|
+
<MazBottomSheet v-model="isUserOpen" title="Profile Settings" icon="/user.svg" max-width="48rem" :padding="false">
|
|
119
352
|
<div class="maz:space-y-6 maz:p-6">
|
|
120
353
|
<div class="maz:flex maz:items-center maz:gap-4 maz:p-4 maz:bg-secondary/10 maz:rounded-lg">
|
|
121
354
|
<MazAvatar src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100" size="xl" />
|
|
@@ -160,173 +393,63 @@ This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop
|
|
|
160
393
|
<MazSwitch v-model="userForm.darkMode" />
|
|
161
394
|
</div>
|
|
162
395
|
</div>
|
|
163
|
-
<div class="maz:flex maz:gap-3 maz:pt-4">
|
|
164
|
-
<MazBtn color="primary" class="maz:flex-1" @click="saveSettings">
|
|
165
|
-
<MazIcon icon="/check.svg" class="maz:me-2" />
|
|
166
|
-
Save Changes
|
|
167
|
-
</MazBtn>
|
|
168
|
-
<MazBtn color="secondary" @click="isUserOpen = false">
|
|
169
|
-
Cancel
|
|
170
|
-
</MazBtn>
|
|
171
|
-
</div>
|
|
172
396
|
</div>
|
|
397
|
+
<template #footer="{ close }">
|
|
398
|
+
<MazBtn color="transparent" @click="close">Cancel</MazBtn>
|
|
399
|
+
<MazBtn color="primary" @click="saveSettings">
|
|
400
|
+
<MazIcon icon="/check.svg" class="maz:me-2" />
|
|
401
|
+
Save Changes
|
|
402
|
+
</MazBtn>
|
|
403
|
+
</template>
|
|
173
404
|
</MazBottomSheet>
|
|
174
405
|
|
|
175
406
|
<template #code>
|
|
176
407
|
|
|
177
408
|
```vue
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<MazCard>
|
|
182
|
-
<template #title>
|
|
183
|
-
<div class="maz:flex maz:items-center maz:gap-3">
|
|
184
|
-
<MazAvatar src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=100" size="lg" />
|
|
185
|
-
<div>
|
|
186
|
-
<h3 class="maz:text-lg maz:font-semibold">
|
|
187
|
-
Nike Air Max
|
|
188
|
-
</h3>
|
|
189
|
-
<p class="maz:text-sm maz:text-muted">
|
|
190
|
-
Premium Running Shoes
|
|
191
|
-
</p>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
</template>
|
|
195
|
-
|
|
196
|
-
<div class="maz:space-y-4">
|
|
197
|
-
<div class="maz:flex maz:items-center maz:justify-between">
|
|
198
|
-
<span class="maz:font-medium">Price:</span>
|
|
199
|
-
<span class="maz:text-xl maz:font-bold maz:text-primary">$129.99</span>
|
|
200
|
-
</div>
|
|
201
|
-
|
|
202
|
-
<div class="maz:flex maz:gap-2">
|
|
203
|
-
<MazBtn color="primary" @click="openProductOptions">
|
|
204
|
-
Customize Options
|
|
205
|
-
</MazBtn>
|
|
206
|
-
<MazBtn color="secondary" @click="openUserSettings">
|
|
207
|
-
Profile Settings
|
|
208
|
-
</MazBtn>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
</MazCard>
|
|
212
|
-
</div>
|
|
409
|
+
<script setup>
|
|
410
|
+
import MazBottomSheet from 'maz-ui/components/MazBottomSheet'
|
|
411
|
+
import { reactive, ref } from 'vue'
|
|
213
412
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
<!-- Size Selection -->
|
|
218
|
-
<div>
|
|
219
|
-
<h4 class="maz:mb-3 maz:text-lg maz:font-semibold">
|
|
220
|
-
Select Size
|
|
221
|
-
</h4>
|
|
222
|
-
<div class="maz:grid maz:grid-cols-4 maz:gap-2">
|
|
223
|
-
<MazBtn
|
|
224
|
-
v-for="size in sizes"
|
|
225
|
-
:key="size"
|
|
226
|
-
:color="selectedOptions.size === size ? 'primary' : 'secondary'"
|
|
227
|
-
size="sm"
|
|
228
|
-
@click="selectedOptions.size = size"
|
|
229
|
-
>
|
|
230
|
-
{{ size }}
|
|
231
|
-
</MazBtn>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
413
|
+
const isProductOpen = ref(false)
|
|
414
|
+
const quantity = ref(1)
|
|
415
|
+
const selectedOptions = reactive({ size: '', color: '' })
|
|
234
416
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
</h4>
|
|
240
|
-
<div class="maz:grid maz:grid-cols-3 maz:gap-3">
|
|
241
|
-
<div
|
|
242
|
-
v-for="color in colors"
|
|
243
|
-
:key="color.name"
|
|
244
|
-
class="maz:flex maz:cursor-pointer maz:flex-col maz:items-center maz:rounded-lg maz:border-2 maz:p-3"
|
|
245
|
-
:class="selectedOptions.color === color.name ? 'maz:border-primary' : 'maz:border-divider'"
|
|
246
|
-
@click="selectedOptions.color = color.name"
|
|
247
|
-
>
|
|
248
|
-
<div
|
|
249
|
-
class="maz:mb-2 maz:size-8 maz:rounded-full"
|
|
250
|
-
:style="{ backgroundColor: color.value }"
|
|
251
|
-
/>
|
|
252
|
-
<span class="maz:text-sm">{{ color.name }}</span>
|
|
253
|
-
</div>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
417
|
+
function openProductOptions() {
|
|
418
|
+
isProductOpen.value = true
|
|
419
|
+
}
|
|
420
|
+
</script>
|
|
256
421
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
:min="1"
|
|
262
|
-
:max="10"
|
|
263
|
-
/>
|
|
264
|
-
|
|
265
|
-
<!-- Actions -->
|
|
266
|
-
<div class="maz:flex maz:gap-3 maz:pt-4">
|
|
267
|
-
<MazBtn color="primary" class="maz:flex-1" @click="addToCart">
|
|
268
|
-
Add to Cart (${{ (129.99 * quantity).toFixed(2) }})
|
|
269
|
-
</MazBtn>
|
|
270
|
-
<MazBtn color="secondary" @click="isProductOpen = false">
|
|
271
|
-
Cancel
|
|
272
|
-
</MazBtn>
|
|
273
|
-
</div>
|
|
274
|
-
</div>
|
|
275
|
-
</MazBottomSheet>
|
|
422
|
+
<template>
|
|
423
|
+
<MazBtn color="primary" @click="openProductOptions">
|
|
424
|
+
Customize Options
|
|
425
|
+
</MazBtn>
|
|
276
426
|
|
|
277
|
-
<MazBottomSheet v-model="
|
|
427
|
+
<MazBottomSheet v-model="isProductOpen" title="Customize Your Shoes" icon="/cog.svg" :padding="false">
|
|
278
428
|
<div class="maz:space-y-6 maz:p-6">
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
placeholder="Enter your name"
|
|
292
|
-
/>
|
|
293
|
-
<MazInput
|
|
294
|
-
v-model="userForm.email"
|
|
295
|
-
label="Email"
|
|
296
|
-
type="email"
|
|
297
|
-
placeholder="Enter your email"
|
|
298
|
-
/>
|
|
299
|
-
<MazSelect
|
|
300
|
-
v-model="userForm.country"
|
|
301
|
-
label="Country"
|
|
302
|
-
:options="countries"
|
|
303
|
-
placeholder="Select your country"
|
|
304
|
-
/>
|
|
305
|
-
<div class="maz:flex maz:items-center maz:justify-between maz:p-4 maz:border maz:border-divider maz:rounded-lg">
|
|
306
|
-
<div>
|
|
307
|
-
<p class="maz:font-medium">Email Notifications</p>
|
|
308
|
-
<p class="maz:text-sm maz:text-muted">Receive updates about your orders</p>
|
|
309
|
-
</div>
|
|
310
|
-
<MazSwitch v-model="userForm.notifications" />
|
|
311
|
-
</div>
|
|
312
|
-
<div class="maz:flex maz:items-center maz:justify-between maz:p-4 maz:border maz:border-divider maz:rounded-lg">
|
|
313
|
-
<div>
|
|
314
|
-
<p class="maz:font-medium">Dark Mode</p>
|
|
315
|
-
<p class="maz:text-sm maz:text-muted">Switch to dark theme</p>
|
|
316
|
-
</div>
|
|
317
|
-
<MazSwitch v-model="userForm.darkMode" />
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
<div class="maz:flex maz:gap-3 maz:pt-4">
|
|
321
|
-
<MazBtn color="primary" class="maz:flex-1" @click="saveSettings">
|
|
322
|
-
<MazIcon icon="/check.svg" class="maz:me-2" />
|
|
323
|
-
Save Changes
|
|
324
|
-
</MazBtn>
|
|
325
|
-
<MazBtn color="secondary" @click="isUserOpen = false">
|
|
326
|
-
Cancel
|
|
429
|
+
<h4 class="maz:mb-3 maz:text-lg maz:font-semibold">
|
|
430
|
+
Select Size
|
|
431
|
+
</h4>
|
|
432
|
+
<div class="maz:grid maz:grid-cols-4 maz:gap-2">
|
|
433
|
+
<MazBtn
|
|
434
|
+
v-for="size in sizes"
|
|
435
|
+
:key="size"
|
|
436
|
+
:color="selectedOptions.size === size ? 'primary' : 'secondary'"
|
|
437
|
+
size="sm"
|
|
438
|
+
@click="selectedOptions.size = size"
|
|
439
|
+
>
|
|
440
|
+
{{ size }}
|
|
327
441
|
</MazBtn>
|
|
328
442
|
</div>
|
|
329
443
|
</div>
|
|
444
|
+
|
|
445
|
+
<template #footer="{ close }">
|
|
446
|
+
<MazBtn color="transparent" @click="close">
|
|
447
|
+
Cancel
|
|
448
|
+
</MazBtn>
|
|
449
|
+
<MazBtn color="primary" @click="addToCart">
|
|
450
|
+
Add to Cart (${{ (129.99 * quantity).toFixed(2) }})
|
|
451
|
+
</MazBtn>
|
|
452
|
+
</template>
|
|
330
453
|
</MazBottomSheet>
|
|
331
454
|
</template>
|
|
332
455
|
```
|
|
@@ -339,6 +462,14 @@ This component uses the `<Teleport to="body">` with [MazBackdrop](./maz-backdrop
|
|
|
339
462
|
<script setup>
|
|
340
463
|
import { ref, reactive } from 'vue'
|
|
341
464
|
|
|
465
|
+
const basicOpened = ref(false)
|
|
466
|
+
const swipeOpened = ref(false)
|
|
467
|
+
const iconOpened = ref(false)
|
|
468
|
+
const slotsOpened = ref(false)
|
|
469
|
+
const wideOpened = ref(false)
|
|
470
|
+
const noHeaderOpened = ref(false)
|
|
471
|
+
const persistentOpened = ref(false)
|
|
472
|
+
|
|
342
473
|
const isProductOpen = ref(false)
|
|
343
474
|
const isUserOpen = ref(false)
|
|
344
475
|
const quantity = ref(1)
|
|
@@ -303,6 +303,64 @@ By default, the textarea automatically expands as the user types. You can disabl
|
|
|
303
303
|
</template>
|
|
304
304
|
</ComponentDemo>
|
|
305
305
|
|
|
306
|
+
## Minimum rows (compact / input-like)
|
|
307
|
+
|
|
308
|
+
Use the `min-rows` prop to control the initial (minimum) height of the textarea. It defaults to `3` lines. Set it to `1` to make the textarea start at the height of a regular input while keeping the autogrow behavior: it grows line by line as the user types.
|
|
309
|
+
|
|
310
|
+
<ComponentDemo>
|
|
311
|
+
<MazTextarea
|
|
312
|
+
v-model="value"
|
|
313
|
+
name="comment"
|
|
314
|
+
:min-rows="1"
|
|
315
|
+
rounded-size="xl"
|
|
316
|
+
placeholder="Send your message"
|
|
317
|
+
/>
|
|
318
|
+
|
|
319
|
+
<template #code>
|
|
320
|
+
|
|
321
|
+
```vue
|
|
322
|
+
<template>
|
|
323
|
+
<MazTextarea
|
|
324
|
+
v-model="value"
|
|
325
|
+
name="comment"
|
|
326
|
+
:min-rows="1"
|
|
327
|
+
rounded-size="xl"
|
|
328
|
+
placeholder="Send your message"
|
|
329
|
+
/>
|
|
330
|
+
</template>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
</template>
|
|
334
|
+
</ComponentDemo>
|
|
335
|
+
|
|
336
|
+
## Sizes
|
|
337
|
+
|
|
338
|
+
The `size` prop controls the padding (height) and text size of the textarea, mirroring the `MazInput` sizes (`mini`, `xs`, `sm`, `md`, `lg`, `xl`). Combined with `:min-rows="1"`, a given size matches the height of a `MazInput` of the same size, while keeping the autogrow behavior.
|
|
339
|
+
|
|
340
|
+
<ComponentDemo>
|
|
341
|
+
<div class="maz:flex maz:flex-col maz:gap-4">
|
|
342
|
+
<MazTextarea v-model="value" name="comment" size="xs" :min-rows="1" placeholder="Size xs" />
|
|
343
|
+
<MazTextarea v-model="value" name="comment" size="sm" :min-rows="1" placeholder="Size sm" />
|
|
344
|
+
<MazTextarea v-model="value" name="comment" size="md" :min-rows="1" placeholder="Size md" />
|
|
345
|
+
<MazTextarea v-model="value" name="comment" size="lg" :min-rows="1" placeholder="Size lg" />
|
|
346
|
+
<MazTextarea v-model="value" name="comment" size="xl" :min-rows="1" placeholder="Size xl" />
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<template #code>
|
|
350
|
+
|
|
351
|
+
```vue
|
|
352
|
+
<template>
|
|
353
|
+
<MazTextarea v-model="value" name="comment" size="xs" :min-rows="1" placeholder="Size xs" />
|
|
354
|
+
<MazTextarea v-model="value" name="comment" size="sm" :min-rows="1" placeholder="Size sm" />
|
|
355
|
+
<MazTextarea v-model="value" name="comment" size="md" :min-rows="1" placeholder="Size md" />
|
|
356
|
+
<MazTextarea v-model="value" name="comment" size="lg" :min-rows="1" placeholder="Size lg" />
|
|
357
|
+
<MazTextarea v-model="value" name="comment" size="xl" :min-rows="1" placeholder="Size xl" />
|
|
358
|
+
</template>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
</template>
|
|
362
|
+
</ComponentDemo>
|
|
363
|
+
|
|
306
364
|
## Disabled
|
|
307
365
|
|
|
308
366
|
<ComponentDemo>
|
|
@@ -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>
|
|
@@ -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).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maz-ui/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.0.0-beta.
|
|
4
|
+
"version": "5.0.0-beta.39",
|
|
5
5
|
"description": "Maz-UI ModelContextProtocol Client",
|
|
6
6
|
"author": "Louis Mazel <me@loicmazuel.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
|
-
"@maz-ui/utils": "5.0.0-beta.
|
|
46
|
-
"maz-ui": "5.0.0-beta.
|
|
45
|
+
"@maz-ui/utils": "5.0.0-beta.39",
|
|
46
|
+
"maz-ui": "5.0.0-beta.39"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@modelcontextprotocol/inspector": "^0.21.2",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"ts-node-maintained": "^10.9.5",
|
|
53
53
|
"tsx": "^4.22.3",
|
|
54
54
|
"unbuild": "^3.6.1",
|
|
55
|
-
"@maz-ui/
|
|
56
|
-
"@maz-ui/
|
|
55
|
+
"@maz-ui/eslint-config": "5.0.0-beta.39",
|
|
56
|
+
"@maz-ui/node": "5.0.0-beta.26"
|
|
57
57
|
},
|
|
58
58
|
"lint-staged": {
|
|
59
59
|
"*.{js,ts,mjs,mts,cjs,md,yml,json}": "cross-env NODE_ENV=production eslint --fix"
|