@inertia-vuetify/modal 0.0.0-stage → 3.0.0
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/LICENSE +50 -0
- package/README.md +371 -2
- package/dist/CloseButton.vue.d.ts +3 -0
- package/dist/Deferred.vue.d.ts +16 -0
- package/dist/ModalBtn.vue.d.ts +17 -0
- package/dist/ModalContent.vue.d.ts +33 -0
- package/dist/ModalLink.vue.d.ts +201 -0
- package/dist/ModalRenderer.vue.d.ts +6 -0
- package/dist/ModalRoot.vue.d.ts +13 -0
- package/dist/WhenVisible.vue.d.ts +24 -0
- package/dist/app.d.ts +5 -0
- package/dist/cache.d.ts +12 -0
- package/dist/config.d.ts +16 -0
- package/dist/helpers.d.ts +5 -0
- package/dist/index.d.ts +14 -0
- package/dist/inertia-modal-vuetify.js +1140 -0
- package/dist/keys.d.ts +2 -0
- package/dist/modalStack.d.ts +64 -0
- package/dist/transitions.d.ts +10 -0
- package/dist/types.d.ts +109 -0
- package/dist/useModal.d.ts +3 -0
- package/package.json +71 -4
- package/src/CloseButton.vue +13 -0
- package/src/Deferred.vue +21 -0
- package/src/HeadlessModal.vue +174 -0
- package/src/Modal.vue +120 -0
- package/src/ModalBtn.vue +28 -0
- package/src/ModalContent.vue +238 -0
- package/src/ModalLink.vue +189 -0
- package/src/ModalRenderer.vue +21 -0
- package/src/ModalRoot.vue +152 -0
- package/src/WhenVisible.vue +90 -0
- package/src/app.ts +17 -0
- package/src/cache.ts +67 -0
- package/src/config.ts +116 -0
- package/src/helpers.ts +18 -0
- package/src/index.ts +15 -0
- package/src/keys.ts +3 -0
- package/src/modalStack.ts +525 -0
- package/src/transitions.ts +30 -0
- package/src/types.ts +121 -0
- package/src/useModal.ts +7 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 gigerIT
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The modal runtime in src/ (the modal stack, the Inertia integration,
|
|
26
|
+
HeadlessModal, ModalLink, ModalRoot, ModalRenderer, Deferred, WhenVisible, and
|
|
27
|
+
their helpers) and parts of docs/ are derived from InertiaUI Modal
|
|
28
|
+
(https://github.com/inertiaui/modal), which is licensed as follows:
|
|
29
|
+
|
|
30
|
+
The MIT License (MIT)
|
|
31
|
+
|
|
32
|
+
Copyright (c) Protone Media B.V. <pascal@protone.media>
|
|
33
|
+
|
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
35
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
36
|
+
in the Software without restriction, including without limitation the rights
|
|
37
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
38
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
39
|
+
furnished to do so, subject to the following conditions:
|
|
40
|
+
|
|
41
|
+
The above copyright notice and this permission notice shall be included in
|
|
42
|
+
all copies or substantial portions of the Software.
|
|
43
|
+
|
|
44
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
45
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
46
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
47
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
48
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
49
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
50
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,372 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @inertia-vuetify/modal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Open any Inertia.js route in a Vuetify modal, slideover, or bottom sheet without changing your routes or controllers. It works with the [`inertiaui/modal`](https://inertiaui.com/inertia-modal/docs) Laravel package and has the same API as its Vue frontend, [`@inertiaui/modal-vue`](https://github.com/inertiaui/modal), so switching is a matter of changing imports.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Drop-in replacement** - Same API as `@inertiaui/modal-vue`, no Tailwind needed
|
|
8
|
+
- ✅ **Vuetify 4 styling** - `v-dialog`, `v-card` and your Vuetify theme, icons, locale and defaults
|
|
9
|
+
- ✅ **Zero backend configuration** - Works with existing Inertia.js routes
|
|
10
|
+
- ✅ **Modals, slideovers and bottom sheets** - Including fullscreen modals on phones
|
|
11
|
+
- ✅ **Card layout** - Title and actions that stay in place while the content scrolls
|
|
12
|
+
- ✅ **Unsaved-changes guard** - Veto closing with `before-close`
|
|
13
|
+
- ✅ **`ModalBtn`** - A Vuetify button that opens a modal and shows a loading state
|
|
14
|
+
- ✅ **Nested/stacked modals** - Covered modals are blurred and hidden from assistive technology
|
|
15
|
+
- ✅ **TypeScript** - Typed props, slots, events and exposed methods
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @inertia-vuetify/modal
|
|
21
|
+
# or
|
|
22
|
+
bun add @inertia-vuetify/modal
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Requirements
|
|
26
|
+
|
|
27
|
+
This package requires these peer dependencies:
|
|
28
|
+
|
|
29
|
+
- `vue` ^3.5.0
|
|
30
|
+
- `vuetify` ^4.2.0
|
|
31
|
+
- `@inertiajs/vue3` ^3.0.0
|
|
32
|
+
|
|
33
|
+
On the Laravel side, install the [`inertiaui/modal`](https://inertiaui.com/inertia-modal/docs) package (3.x) with Inertia Laravel 3:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
composer require inertiaui/modal
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The close button uses Vuetify's `$close` icon alias, so it follows whichever icon set you configured (Material Design Icons by default).
|
|
40
|
+
|
|
41
|
+
The package ships as ESM only and does not need a stylesheet import.
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### 1. Configure Inertia.js
|
|
46
|
+
|
|
47
|
+
Mount your app with `renderApp`:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import { createInertiaApp } from "@inertiajs/vue3";
|
|
51
|
+
import { renderApp } from "@inertia-vuetify/modal";
|
|
52
|
+
import { createApp } from "vue";
|
|
53
|
+
import { createVuetify } from "vuetify";
|
|
54
|
+
|
|
55
|
+
createInertiaApp({
|
|
56
|
+
setup({ el, App, props, plugin }) {
|
|
57
|
+
createApp({ render: renderApp(App, props) })
|
|
58
|
+
.use(plugin)
|
|
59
|
+
.use(createVuetify())
|
|
60
|
+
.mount(el);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Link to the modal
|
|
66
|
+
|
|
67
|
+
`ModalBtn` is a Vuetify button that opens a route in a modal. It accepts every `v-btn` and `ModalLink` prop, shows a loading spinner while the modal loads, and still opens a new tab on Ctrl/Cmd-click:
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<script setup>
|
|
71
|
+
import { ModalBtn, ModalLink } from "@inertia-vuetify/modal";
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<ModalBtn href="/users/create" color="primary">Create User</ModalBtn>
|
|
76
|
+
|
|
77
|
+
<!-- Or a plain link -->
|
|
78
|
+
<ModalLink href="/users/create">Create User</ModalLink>
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. Wrap the page content with Modal
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<script setup>
|
|
86
|
+
import { Modal } from "@inertia-vuetify/modal";
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<template>
|
|
90
|
+
<Modal title="Create User">
|
|
91
|
+
<v-form id="create-user">
|
|
92
|
+
<!-- Form fields -->
|
|
93
|
+
</v-form>
|
|
94
|
+
|
|
95
|
+
<template #actions="{ close }">
|
|
96
|
+
<v-btn variant="text" @click="close">Cancel</v-btn>
|
|
97
|
+
<v-btn type="submit" form="create-user" color="primary">Save</v-btn>
|
|
98
|
+
</template>
|
|
99
|
+
</Modal>
|
|
100
|
+
</template>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
With a `title` or `#title` slot and an `#actions` slot, the header and actions stay in place while the content scrolls, and the dialog is labelled by its title. Without them, the content fills the card and the close button floats in the corner.
|
|
104
|
+
|
|
105
|
+
That's it! No backend changes needed.
|
|
106
|
+
|
|
107
|
+
## Usage Examples
|
|
108
|
+
|
|
109
|
+
### Slideovers and bottom sheets
|
|
110
|
+
|
|
111
|
+
```vue
|
|
112
|
+
<ModalBtn href="/settings" slideover>Settings</ModalBtn>
|
|
113
|
+
<ModalBtn href="/settings" slideover position="left">Settings</ModalBtn>
|
|
114
|
+
<ModalBtn href="/settings" slideover position="bottom">Settings</ModalBtn>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Size, position and fullscreen
|
|
118
|
+
|
|
119
|
+
```vue
|
|
120
|
+
<ModalBtn href="/users/create" max-width="4xl">Create User</ModalBtn>
|
|
121
|
+
<ModalBtn href="/users/create" position="top">Create User</ModalBtn>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Centered modals turn fullscreen on phones (below Vuetify's `sm` breakpoint). Set `fullscreen` on the `Modal` to change that: `true`, `false`, a breakpoint name (`sm`, `md`, `lg`, `xl`, `xxl`) or pixel width to go fullscreen below, or `'mobile'` to follow your Vuetify `display.mobileBreakpoint`.
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<Modal fullscreen="md">...</Modal>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Unsaved changes
|
|
131
|
+
|
|
132
|
+
`before-close` runs before the close button, Escape, a backdrop click or `requestClose()` closes the modal. Return `false` (or a promise resolving to `false`) to keep it open; the dialog bounces to show the close was refused. `close()` skips the check, and so does the automatic close after a successful save:
|
|
133
|
+
|
|
134
|
+
```vue
|
|
135
|
+
<script setup>
|
|
136
|
+
import { Modal } from "@inertia-vuetify/modal";
|
|
137
|
+
import { useForm } from "@inertiajs/vue3";
|
|
138
|
+
|
|
139
|
+
const form = useForm({ name: "" });
|
|
140
|
+
|
|
141
|
+
const confirmDiscard = () => !form.isDirty || confirm("Discard your changes?");
|
|
142
|
+
</script>
|
|
143
|
+
|
|
144
|
+
<template>
|
|
145
|
+
<Modal title="Edit" :before-close="confirmDiscard">
|
|
146
|
+
<v-form id="edit" @submit.prevent="form.put('/users/1')">
|
|
147
|
+
<v-text-field v-model="form.name" label="Name" :error-messages="form.errors.name" />
|
|
148
|
+
</v-form>
|
|
149
|
+
|
|
150
|
+
<template #actions="{ requestClose }">
|
|
151
|
+
<v-btn variant="text" @click="requestClose">Cancel</v-btn>
|
|
152
|
+
<v-btn type="submit" form="edit" color="primary">Save</v-btn>
|
|
153
|
+
</template>
|
|
154
|
+
</Modal>
|
|
155
|
+
</template>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
When a form submission redirects back to the page under the modal, the modals close by themselves once the request finishes. Events emitted from the submission's `onSuccess` still reach their listeners.
|
|
159
|
+
|
|
160
|
+
### Local modal (no route required)
|
|
161
|
+
|
|
162
|
+
```vue
|
|
163
|
+
<script setup>
|
|
164
|
+
import { Modal, ModalBtn } from "@inertia-vuetify/modal";
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<template>
|
|
168
|
+
<ModalBtn href="#confirm-delete" color="error">Delete Item</ModalBtn>
|
|
169
|
+
|
|
170
|
+
<Modal name="confirm-delete" title="Delete item?">
|
|
171
|
+
<p>This cannot be undone.</p>
|
|
172
|
+
|
|
173
|
+
<template #actions="{ close }">
|
|
174
|
+
<v-btn variant="text" @click="close">Cancel</v-btn>
|
|
175
|
+
<v-btn color="error" @click="deleteItem">Delete</v-btn>
|
|
176
|
+
</template>
|
|
177
|
+
</Modal>
|
|
178
|
+
</template>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Components rendered inside a modal can call `useModal()` to get the modal instance, including inside local modals.
|
|
182
|
+
|
|
183
|
+
### Programmatic usage
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
import { visitModal } from "@inertia-vuetify/modal";
|
|
187
|
+
|
|
188
|
+
visitModal("/users/create", {
|
|
189
|
+
config: { maxWidth: "lg", fullscreen: false },
|
|
190
|
+
onClose: () => console.log("Modal closed"),
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Configuration
|
|
195
|
+
|
|
196
|
+
### Modal props
|
|
197
|
+
|
|
198
|
+
| Prop | Type | Default (modal / slideover) | Description |
|
|
199
|
+
| ------------------------ | ---------------------------- | --------------------------- | ------------------------------------------------------------------------- |
|
|
200
|
+
| `close-button` | Boolean | `true` | Show the close button |
|
|
201
|
+
| `close-explicitly` | Boolean | `false` | Only close via the close button or programmatically |
|
|
202
|
+
| `close-on-click-outside` | Boolean | `true` | Close when the backdrop is clicked |
|
|
203
|
+
| `max-width` | String | `'2xl'` / `'md'` | Maximum width, see below |
|
|
204
|
+
| `padding-classes` | String/Boolean | `'pa-4 pa-sm-6'` | Padding classes for the content; `false` removes padding |
|
|
205
|
+
| `panel-classes` | String/Boolean | `''` | Extra classes for the content |
|
|
206
|
+
| `position` | String | `'center'` / `'right'` | `top`, `center`, `bottom` for modals; `left`, `right`, `bottom` for slideovers |
|
|
207
|
+
| `slideover` | Boolean | `false` | Render as a slideover instead of a modal |
|
|
208
|
+
| `fullscreen` | Boolean/String/Number | `'sm'` / `false` | Fullscreen always, never, or below a breakpoint or width |
|
|
209
|
+
| `title` | String | - | Header title; enables the card layout |
|
|
210
|
+
| `before-close` | Function | - | Return `false` to keep the modal open |
|
|
211
|
+
| `dialog-props` | Object | - | Extra `v-dialog` props, e.g. `{ scrim: 'primary' }` |
|
|
212
|
+
| `card-props` | Object | - | Extra `v-card` props, e.g. `{ color: 'surface-variant' }` |
|
|
213
|
+
|
|
214
|
+
All props except `title`, `before-close`, `dialog-props` and `card-props` also work on `ModalLink`, `ModalBtn`, in `visitModal`'s `config`, and in `putConfig`. Props set on a link or visit win over the ones on the `Modal`.
|
|
215
|
+
|
|
216
|
+
`max-width` takes the same tokens as `@inertiaui/modal-vue` (Tailwind's `max-w-*` scale) and maps them to the same widths: `sm` 384px, `md` 448px, `lg` 512px, `xl` 576px, `2xl` 672px, `3xl` 768px, `4xl` 896px, `5xl` 1024px, `6xl` 1152px, `7xl` 1280px. Unknown values fall back to `2xl`.
|
|
217
|
+
|
|
218
|
+
### Default configuration
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
import { putConfig } from "@inertia-vuetify/modal";
|
|
222
|
+
|
|
223
|
+
putConfig({
|
|
224
|
+
modal: {
|
|
225
|
+
maxWidth: "lg",
|
|
226
|
+
fullscreen: "md",
|
|
227
|
+
},
|
|
228
|
+
slideover: {
|
|
229
|
+
position: "left",
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
putConfig("modal.closeButton", false);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Keys you leave out keep their defaults. `resetConfig()` restores them.
|
|
237
|
+
|
|
238
|
+
### Vuetify defaults
|
|
239
|
+
|
|
240
|
+
Style every modal through Vuetify's [global defaults](https://vuetifyjs.com/en/features/global-configuration/) under the `InertiaModal` key, without touching other dialogs. Content defaults nest under `VDialog`, like Vuetify's own dialogs:
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
createVuetify({
|
|
244
|
+
defaults: {
|
|
245
|
+
InertiaModal: {
|
|
246
|
+
VDialog: {
|
|
247
|
+
scrim: "primary",
|
|
248
|
+
VCard: { rounded: "xl" },
|
|
249
|
+
VBtn: { variant: "tonal" },
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## API Reference
|
|
257
|
+
|
|
258
|
+
### `<Modal>`
|
|
259
|
+
|
|
260
|
+
**Slots:** `default`, `title` and `actions`. Each receives the modal's page props plus `close()`, `requestClose()`, `reload()`, `setOpen(open)`, `emit(event, ...args)`, `getParentModal()`, `getChildModal()`, `isOpen`, `onTopOfStack` and `config`.
|
|
261
|
+
|
|
262
|
+
**Events:** `@close`, `@focus`, `@blur`, `@success`, `@after-leave`, plus any event emitted from the modal (for example `@user-created`).
|
|
263
|
+
|
|
264
|
+
**Methods (via ref):** `close()`, `reload()`, `setOpen(open)`, `emit(event, ...args)`, `getParentModal()`, `getChildModal()`, and the `isOpen`, `onTopOfStack`, `config`, `id` and `index` getters.
|
|
265
|
+
|
|
266
|
+
### `<ModalBtn>`
|
|
267
|
+
|
|
268
|
+
A `v-btn` rendered by `ModalLink`, with a loading state. Accepts all `v-btn` props and the `ModalLink` props below.
|
|
269
|
+
|
|
270
|
+
### `<ModalLink>`
|
|
271
|
+
|
|
272
|
+
Besides the modal props it accepts `href`, `method`, `data`, `headers`, `as`, `navigate`, `prefetch` and `cache-for`, and emits `@start`, `@success`, `@error`, `@close`, `@after-leave`, `@prefetching` and `@prefetched`. Ctrl/Cmd/Shift/Alt-clicks on a link are left to the browser, so they still open a new tab or window.
|
|
273
|
+
|
|
274
|
+
### Functions
|
|
275
|
+
|
|
276
|
+
- `useModal()` - The current modal instance
|
|
277
|
+
- `visitModal(url, options)` - Open a modal programmatically
|
|
278
|
+
- `putConfig(key, value)`, `getConfig(key)`, `resetConfig()` - Default configuration
|
|
279
|
+
- `getMaxWidth(token)`, `getDialogLocation(position)` - The width and location mapping this package uses
|
|
280
|
+
|
|
281
|
+
The package also exports `HeadlessModal`, `ModalRoot`, `Deferred`, `WhenVisible`, `useModalStack`, `prefetch`, `withInertiaModal`, `modalPropNames`, `ModalInstance` and `dialogUtils` with the same API as `@inertiaui/modal-vue`.
|
|
282
|
+
|
|
283
|
+
## Styling
|
|
284
|
+
|
|
285
|
+
Modals use your Vuetify theme, so colors, elevation and typography follow it. Use `panel-classes` and `padding-classes` for utility classes, `card-props` and `dialog-props` for one modal, and the `InertiaModal` defaults for all of them.
|
|
286
|
+
|
|
287
|
+
For further customization, target the `im-` classes: `im-modal-dialog`, `im-modal-wrapper`, `im-modal-title`, `im-modal-content`, `im-modal-actions`, the matching `im-slideover-*` classes, `im-slideover-left`, `im-slideover-bottom` and `im-close-button`. Dialogs are teleported, so use global (unscoped) selectors.
|
|
288
|
+
|
|
289
|
+
## Server-side rendering
|
|
290
|
+
|
|
291
|
+
Vuetify components import their own CSS, so let Vite bundle this package and Vuetify for the SSR build, and pass `ssr: true` to Vuetify:
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
// vite.config.js
|
|
295
|
+
export default defineConfig({
|
|
296
|
+
ssr: {
|
|
297
|
+
noExternal: ["vuetify", "@inertia-vuetify/modal"],
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// ssr.js
|
|
304
|
+
createSSRApp({ render: renderApp(App, props) })
|
|
305
|
+
.use(plugin)
|
|
306
|
+
.use(createVuetify({ ssr: true }));
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The base page renders on the server, but the modal itself appears once the app hydrates: Vuetify renders overlays on the client only.
|
|
310
|
+
|
|
311
|
+
## Migration
|
|
312
|
+
|
|
313
|
+
### From @inertiaui/modal-vue
|
|
314
|
+
|
|
315
|
+
1. Replace the imports, everywhere:
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
// Before
|
|
319
|
+
import { Modal, ModalLink } from "@inertiaui/modal-vue";
|
|
320
|
+
|
|
321
|
+
// After
|
|
322
|
+
import { Modal, ModalLink } from "@inertia-vuetify/modal";
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
2. Uninstall `@inertiaui/modal-vue`. Importing from both packages would give you two separate modal stacks.
|
|
326
|
+
|
|
327
|
+
3. Replace Tailwind classes in `padding-classes`, `panel-classes` and `putConfig` with Vuetify utility classes. `useNativeDialog` has no effect.
|
|
328
|
+
|
|
329
|
+
4. Ensure Vuetify 4.2+ is installed and configured.
|
|
330
|
+
|
|
331
|
+
### From @gigerit/inertia-modal-vuetify 2.x
|
|
332
|
+
|
|
333
|
+
- The package is now `@inertia-vuetify/modal`. Uninstall `@gigerit/inertia-modal-vuetify`, install `@inertia-vuetify/modal`, and update your imports.
|
|
334
|
+
- Vuetify `^4.2.0` is required, and the package is ESM only.
|
|
335
|
+
- The package no longer depends on `@inertiaui/modal-vue`. Import everything from `@inertia-vuetify/modal` and uninstall it.
|
|
336
|
+
- `max-width` tokens use the same widths as `@inertiaui/modal-vue` (`2xl` is now 672px instead of 896px).
|
|
337
|
+
- Centered modals are fullscreen on phones; pass `fullscreen: false` to `putConfig({ modal: { ... } })` to opt out.
|
|
338
|
+
- The close button floats in the top corner, or sits in the header when the modal has a title, instead of taking its own row.
|
|
339
|
+
- `ModalLink :as="VBtn"` can become `ModalBtn`.
|
|
340
|
+
|
|
341
|
+
## Development
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
bun install
|
|
345
|
+
bunx playwright install chromium-headless-shell # once, for the browser tests
|
|
346
|
+
bun run test # Vitest: unit tests and real-browser component tests
|
|
347
|
+
bun run lint # oxlint
|
|
348
|
+
bun run format # oxfmt
|
|
349
|
+
bun run typecheck # vue-tsc
|
|
350
|
+
bun run build # Library and type declarations in dist/
|
|
351
|
+
bun run dev # Local-modal playground in example/ at http://localhost:3000
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`demo-app/` is a Laravel 13 app for testing routed modals against the package source, with Playwright end-to-end tests; see [AGENTS.md](AGENTS.md) for its commands.
|
|
355
|
+
|
|
356
|
+
## Contributing
|
|
357
|
+
|
|
358
|
+
Contributions are welcome! Use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, ...); release-please derives versions and the changelog from them.
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT
|
|
363
|
+
|
|
364
|
+
## Credits
|
|
365
|
+
|
|
366
|
+
The modal runtime (stack, routing, `HeadlessModal`, `ModalLink`, ...) started as a port of [InertiaUI Modal](https://github.com/inertiaui/modal) by Pascal Baljet / Protone Media, which also provides the Laravel package this one works with. See [LICENSE](LICENSE).
|
|
367
|
+
|
|
368
|
+
## Related Packages
|
|
369
|
+
|
|
370
|
+
- [InertiaUI Modal](https://inertiaui.com/inertia-modal/docs) - The Laravel package and the original Tailwind frontends
|
|
371
|
+
- [Inertia.js](https://inertiajs.com/)
|
|
372
|
+
- [Vuetify](https://vuetifyjs.com/)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
data: string | string[];
|
|
3
|
+
};
|
|
4
|
+
type __VLS_Slots = {
|
|
5
|
+
default?(): unknown;
|
|
6
|
+
fallback?(): unknown;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
12
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
13
|
+
new (): {
|
|
14
|
+
$slots: S;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
href: string;
|
|
3
|
+
};
|
|
4
|
+
type __VLS_Slots = {
|
|
5
|
+
default?(props: {
|
|
6
|
+
loading: boolean;
|
|
7
|
+
}): unknown;
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
13
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
14
|
+
new (): {
|
|
15
|
+
$slots: S;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BeforeClose, ModalConfig, ModalContext } from './types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
modalContext: ModalContext;
|
|
4
|
+
config: ModalConfig;
|
|
5
|
+
title?: string;
|
|
6
|
+
beforeClose?: BeforeClose;
|
|
7
|
+
dialogProps?: Record<string, unknown>;
|
|
8
|
+
cardProps?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
type __VLS_Slots = {
|
|
11
|
+
default(props: {
|
|
12
|
+
requestClose: () => Promise<void>;
|
|
13
|
+
}): unknown;
|
|
14
|
+
title?(props: {
|
|
15
|
+
requestClose: () => Promise<void>;
|
|
16
|
+
}): unknown;
|
|
17
|
+
actions?(props: {
|
|
18
|
+
requestClose: () => Promise<void>;
|
|
19
|
+
}): unknown;
|
|
20
|
+
};
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
22
|
+
"after-leave": () => any;
|
|
23
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
24
|
+
"onAfter-leave"?: (() => any) | undefined;
|
|
25
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
26
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
29
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
30
|
+
new (): {
|
|
31
|
+
$slots: S;
|
|
32
|
+
};
|
|
33
|
+
};
|