@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 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.37";
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 | Bindings |
20
- | ------- | ------------ | ------------------------------------- |
21
- | default | Slot content | **close** `Function` - close function |
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 | 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` | - |
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 | 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
- | **padding** | If the textarea has a padding | `boolean` | No | `true` | - |
22
- | **transparent** | If the textarea has a transparent background | `boolean` | No | `false` | - |
23
- | **border** | If the textarea has no border | `boolean` | No | `false` | - |
24
- | **autogrow** | If the textarea should autogrow based on its content | `boolean` | No | `true` | - |
25
- | **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'` |
26
- | **top-label** | Static label displayed above the textarea. Unlike the floating label, this remains fixed | `string` | No | `undefined` | - |
27
- | **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` | - |
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
- ## Interactive Demo
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
- <template>
179
- <div class="maz:flex maz:flex-col maz:gap-4">
180
- <!-- Product Card -->
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
- <!-- Product Options Bottom Sheet -->
215
- <MazBottomSheet v-model="isProductOpen" title="Customize Your Shoes">
216
- <div class="maz:space-y-6 maz:p-6">
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
- <!-- Color Selection with Visual Swatches -->
236
- <div>
237
- <h4 class="maz:mb-3 maz:text-lg maz:font-semibold">
238
- Select Color
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
- <!-- Quantity Input -->
258
- <MazInputNumber
259
- v-model="quantity"
260
- label="Quantity"
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="isUserOpen" title="Profile Settings">
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
- <div class="maz:flex maz:items-center maz:gap-4 maz:p-4 maz:bg-secondary/10 maz:rounded-lg">
280
- <MazAvatar src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100" size="xl" />
281
- <div>
282
- <h4 class="maz:font-semibold">John Doe</h4>
283
- <p class="maz:text-muted maz:text-sm">john.doe@example.com</p>
284
- <MazBadge color="success" size="xs">Premium Member</MazBadge>
285
- </div>
286
- </div>
287
- <div class="maz:space-y-4">
288
- <MazInput
289
- v-model="userForm.name"
290
- label="Full Name"
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
- (You should use a real device or a mobile simulator to test the swipe functionality)
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
- (You should use a real device or a mobile simulator to test the swipe functionality)
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: TouchEvent) => void
164
+ onLeft?: (event: PointerEvent) => void
157
165
  /** Callback executed when a right swipe is detected. */
158
- onRight?: (event: TouchEvent) => void
166
+ onRight?: (event: PointerEvent) => void
159
167
  /** Callback executed when an up swipe is detected. */
160
- onUp?: (event: TouchEvent) => void
168
+ onUp?: (event: PointerEvent) => void
161
169
  /** Callback executed when a down swipe is detected. */
162
- onDown?: (event: TouchEvent) => void
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
- * Whether to prevent the default behavior of the touchmove event.
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
- preventDefaultOnTouchMove?: boolean
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 trigger the swipe event immediately on touchstart/mousedown.
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 touchend/mouseup.
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 touchmove or mousewheel events, you can set the `preventDefaultOnTouchMove` and `preventDefaultOnMouseWheel` options, respectively.
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.37",
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.28",
46
- "maz-ui": "5.0.0-beta.37"
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/node": "5.0.0-beta.26",
56
- "@maz-ui/eslint-config": "5.0.0-beta.28"
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"