nuxt-crud-modals 1.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Tobias Scheibling
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ [![MIT License][license-src]][license-href]
2
+ [![NuxtModules][modules-src]][modules-href]
3
+
4
+ # Nuxt CRUD Modals
5
+
6
+ Nuxt CRUD Modals is a lightweight module designed to streamline the creation, viewing and editing of (database) records through modal interfaces in Nuxt applications. It provides a simple and consistent way to handle CRUD-related UI patterns without repetitive boilerplate.
7
+
8
+ Built on top of Nuxt UI’s modal system, it leverages `UModal` and `useOverlay` under the hood to deliver a flexible and extensible modal experience. The module abstracts common interaction patterns, allowing developers to quickly scaffold modals for different record types while maintaining full control over behavior and presentation.
9
+
10
+ Whether you're building admin panels, dashboards or data-driven applications, Nuxt CRUD Modals module helps you reduce complexity and focus on your business logic by handling the modal lifecycle, state management and integration patterns for you.
11
+
12
+ ## Features
13
+
14
+ - Open, view or edit modals based on record ID
15
+ - Loading overlay for long running data fetching operations
16
+ - `<UFormModal>` component for creating and editing data records
17
+ - Coming soon: delete modals
18
+
19
+ ## Peer Dependencies
20
+
21
+ Requires Nuxt UI and Zod 4
22
+
23
+ ## Quick Setup
24
+
25
+ Install the module to your Nuxt application with one command:
26
+
27
+ ```bash
28
+ npx nuxt module add nuxt-crud-modals
29
+ ```
30
+
31
+ That's it! You can now use CRUD Modals in your Nuxt app
32
+
33
+ ## Options
34
+
35
+ ```ts
36
+ export default defineNuxtConfig({
37
+ modules: ['@nuxt/ui', 'nuxt-crud-modals'],
38
+
39
+ //...
40
+
41
+ modals: {
42
+ loadingDelay: 700, // hides the loading overlay for 700 ms
43
+ prefix: 'U', // prefix for the components, e.g. UFormModal
44
+ },
45
+ })
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ First, define a modal for your specific record type. You configure which components should be used for creating, editing, and viewing records, and optionally provide a data-fetching function:
51
+
52
+ ```ts
53
+ import JobForm from '~/components/JobForm.vue'
54
+ import JobView from '~/components/Job.vue'
55
+
56
+ export const useJobModal = defineCrudModals({
57
+ components: {
58
+ create: JobForm,
59
+ edit: JobForm,
60
+ view: JobView
61
+ },
62
+ fetchData: async (id) => {
63
+ // your data fetching logic
64
+ return {
65
+ id,
66
+ title: 'Sample job',
67
+ status: 'In progress',
68
+ comment: 'Additional briefing needed'
69
+ }
70
+ }
71
+ })
72
+ ```
73
+
74
+ ### Open modal to edit a record
75
+ To open the modal in edit mode, call `openToEdit` with the record ID:
76
+
77
+ ```ts
78
+ const jobModal = useJobModal()
79
+ jobModal.openToEdit(123)
80
+ ```
81
+
82
+ ### Open modal with initial state to create a new record
83
+ To create a new record, use `openToCreate`. You can optionally pass an initial state:
84
+
85
+ ```ts
86
+ const jobModal = useJobModal()
87
+ jobModal.openToCreate({ status: 'New' })
88
+ ```
89
+
90
+ This will open the modal providing the initial state in the modal to prefill the form.
91
+
92
+ ## Local development
93
+
94
+ ```bash
95
+ # Install dependencies
96
+ npm install
97
+
98
+ # Generate type stubs
99
+ npm run dev:prepare
100
+
101
+ # Develop with the playground
102
+ npm run dev
103
+
104
+ # Build the playground
105
+ npm run dev:build
106
+
107
+ # Run ESLint
108
+ npm run lint
109
+
110
+ # Run Vitest
111
+ npm run test
112
+ npm run test:watch
113
+
114
+ # Release new version
115
+ npm run release
116
+ ```
117
+
118
+ [license-src]: https://img.shields.io/github/license/tosling/nuxt-crud-modals
119
+ [license-href]: ./LICENSE
120
+
121
+ [modules-src]: https://img.shields.io/badge/Nuxt%20Module-gray?logo=nuxt
122
+ [modules-href]: https://nuxt.com/modules
@@ -0,0 +1,17 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ declare module '@nuxt/schema' {
4
+ interface PublicRuntimeConfig {
5
+ modals: {
6
+ loadingDelay: number;
7
+ };
8
+ }
9
+ }
10
+ interface ModuleOptions {
11
+ loadingDelay: number;
12
+ prefix: string;
13
+ }
14
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
15
+
16
+ export { _default as default };
17
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "nuxt-crud-modals",
3
+ "configKey": "modals",
4
+ "compatibility": {
5
+ "nuxt": ">=4.0.0"
6
+ },
7
+ "version": "1.1.0",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "3.6.1"
11
+ }
12
+ }
@@ -0,0 +1,41 @@
1
+ import { defineNuxtModule, createResolver, addComponentsDir, addImports } from '@nuxt/kit';
2
+ import { defu } from 'defu';
3
+
4
+ const module$1 = defineNuxtModule({
5
+ meta: {
6
+ name: "nuxt-crud-modals",
7
+ configKey: "modals",
8
+ compatibility: {
9
+ nuxt: ">=4.0.0"
10
+ }
11
+ },
12
+ // default configuration options of the module
13
+ defaults: {
14
+ prefix: "U",
15
+ loadingDelay: 500
16
+ // ms
17
+ },
18
+ setup(options, nuxt) {
19
+ const { resolve } = createResolver(import.meta.url);
20
+ nuxt.options.runtimeConfig.public.modals = defu(
21
+ nuxt.options.runtimeConfig.public.modals,
22
+ {
23
+ loadingDelay: options.loadingDelay
24
+ }
25
+ );
26
+ addComponentsDir({
27
+ path: resolve("runtime/app/components"),
28
+ prefix: options.prefix
29
+ });
30
+ addImports({
31
+ name: "useLoadingOverlay",
32
+ from: resolve("runtime/app/composables/useLoadingOverlay")
33
+ });
34
+ addImports({
35
+ name: "defineCrudModals",
36
+ from: resolve("runtime/app/utils/modalFactory")
37
+ });
38
+ }
39
+ });
40
+
41
+ export { module$1 as default };
@@ -0,0 +1,38 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { FormSubmitEvent } from '@nuxt/ui';
3
+ import type { CrudModalResult } from '../utils/modalFactory.js';
4
+ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
5
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<({
6
+ schema: ZodType<T>;
7
+ initialState: T;
8
+ onSubmit: (event: FormSubmitEvent<T>) => void | Promise<void>;
9
+ } & {
10
+ open?: boolean;
11
+ }) & {
12
+ onClose?: ((value: CrudModalResult) => any) | undefined;
13
+ "onUpdate:open"?: ((value: boolean | undefined) => any) | undefined;
14
+ }> & (typeof globalThis extends {
15
+ __VLS_PROPS_FALLBACK: infer P;
16
+ } ? P : {});
17
+ expose: (exposed: import("vue").ShallowUnwrapRef<{
18
+ setError: (message: string) => void;
19
+ }>) => void;
20
+ attrs: any;
21
+ slots: {
22
+ fields?: (props: {
23
+ state: import("@vue/reactivity").DistributeRef<import("vue").Reactive<T>>;
24
+ }) => any;
25
+ } & {
26
+ buttons?: (props: {}) => any;
27
+ };
28
+ emit: ((evt: "close", value: CrudModalResult) => void) & ((event: "update:open", value: boolean | undefined) => void);
29
+ }>) => import("vue").VNode & {
30
+ __ctx?: Awaited<typeof __VLS_setup>;
31
+ };
32
+ declare const _default: typeof __VLS_export;
33
+ export default _default;
34
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
35
+ [K in keyof T]: T[K];
36
+ } : {
37
+ [K in keyof T as K]: T[K];
38
+ }) & {};
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <UModal v-model:open="open">
3
+ <template #body>
4
+ <UForm
5
+ ref="form"
6
+ class="space-y-4"
7
+ :state="state"
8
+ :schema="schema"
9
+ @submit="submit"
10
+ >
11
+ <slot
12
+ name="fields"
13
+ :state="state"
14
+ />
15
+
16
+ <UAlert
17
+ v-if="error"
18
+ :description="error"
19
+ variant="subtle"
20
+ color="error"
21
+ icon="i-lucide-triangle-alert"
22
+ />
23
+
24
+ <div class="flex flex-row-reverse gap-2">
25
+ <slot name="buttons">
26
+ <UButton
27
+ type="submit"
28
+ loading-auto
29
+ >
30
+ Submit
31
+ </UButton>
32
+ </slot>
33
+ </div>
34
+ </UForm>
35
+ </template>
36
+ </UModal>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { reactive, ref, useTemplateRef, watch } from "vue";
41
+ const props = defineProps({
42
+ schema: { type: Object, required: true },
43
+ initialState: { type: null, required: true },
44
+ onSubmit: { type: Function, required: true }
45
+ });
46
+ const emit = defineEmits(["close"]);
47
+ const error = ref();
48
+ const open = defineModel("open", { type: Boolean });
49
+ const form = useTemplateRef("form");
50
+ const state = reactive({ ...props.initialState });
51
+ function setError(message) {
52
+ error.value = message;
53
+ }
54
+ defineExpose({ setError });
55
+ async function submit(event) {
56
+ try {
57
+ await props.onSubmit(event);
58
+ emit("close", { success: true });
59
+ open.value = false;
60
+ } catch (err) {
61
+ error.value = "Unknown error";
62
+ if (err instanceof Error)
63
+ error.value = err.message;
64
+ }
65
+ }
66
+ watch(open, (newVal) => {
67
+ if (newVal) {
68
+ Object.assign(state, props.initialState);
69
+ error.value = void 0;
70
+ }
71
+ });
72
+ watch(
73
+ () => form.value?.dirty,
74
+ (newVal) => {
75
+ if (newVal) {
76
+ error.value = void 0;
77
+ }
78
+ }
79
+ );
80
+ </script>
@@ -0,0 +1,38 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { FormSubmitEvent } from '@nuxt/ui';
3
+ import type { CrudModalResult } from '../utils/modalFactory.js';
4
+ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
5
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<({
6
+ schema: ZodType<T>;
7
+ initialState: T;
8
+ onSubmit: (event: FormSubmitEvent<T>) => void | Promise<void>;
9
+ } & {
10
+ open?: boolean;
11
+ }) & {
12
+ onClose?: ((value: CrudModalResult) => any) | undefined;
13
+ "onUpdate:open"?: ((value: boolean | undefined) => any) | undefined;
14
+ }> & (typeof globalThis extends {
15
+ __VLS_PROPS_FALLBACK: infer P;
16
+ } ? P : {});
17
+ expose: (exposed: import("vue").ShallowUnwrapRef<{
18
+ setError: (message: string) => void;
19
+ }>) => void;
20
+ attrs: any;
21
+ slots: {
22
+ fields?: (props: {
23
+ state: import("@vue/reactivity").DistributeRef<import("vue").Reactive<T>>;
24
+ }) => any;
25
+ } & {
26
+ buttons?: (props: {}) => any;
27
+ };
28
+ emit: ((evt: "close", value: CrudModalResult) => void) & ((event: "update:open", value: boolean | undefined) => void);
29
+ }>) => import("vue").VNode & {
30
+ __ctx?: Awaited<typeof __VLS_setup>;
31
+ };
32
+ declare const _default: typeof __VLS_export;
33
+ export default _default;
34
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
35
+ [K in keyof T]: T[K];
36
+ } : {
37
+ [K in keyof T as K]: T[K];
38
+ }) & {};
@@ -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,51 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <Transition
4
+ appear
5
+ enter-active-class="transition-opacity duration-150 ease-out"
6
+ enter-from-class="opacity-0"
7
+ enter-to-class="opacity-100"
8
+ leave-active-class="transition-opacity duration-100 ease-in"
9
+ leave-from-class="opacity-100"
10
+ leave-to-class="opacity-0"
11
+ >
12
+ <div
13
+ v-if="state.isActive && state.blocking"
14
+ class="fixed inset-0 z-100 flex items-center justify-center cursor-progress"
15
+ :class="{
16
+ 'bg-elevated/75': state.backdrop
17
+ }"
18
+ >
19
+ <Transition
20
+ enter-active-class="transition-all duration-300 ease-out"
21
+ leave-active-class="transition-all duration-200 ease-in"
22
+ enter-from-class="opacity-0 scale-95"
23
+ enter-to-class="opacity-100 scale-100"
24
+ leave-from-class="opacity-100 scale-100"
25
+ leave-to-class="opacity-0 scale-95"
26
+ >
27
+ <div
28
+ v-if="state.isVisible"
29
+ class="bg-default rounded-lg shadow-lg ring ring-default p-4 sm:p-6 flex flex-col items-center gap-2 min-w-30"
30
+ >
31
+ <UIcon
32
+ name="i-lucide-loader-circle"
33
+ class="size-8 text-primary animate-spin"
34
+ />
35
+ <span
36
+ v-if="state.label"
37
+ class="text-sm text-muted font-medium"
38
+ >
39
+ {{ state.label }}
40
+ </span>
41
+ </div>
42
+ </Transition>
43
+ </div>
44
+ </Transition>
45
+ </Teleport>
46
+ </template>
47
+
48
+ <script setup>
49
+ import { useLoadingOverlay } from "../composables/useLoadingOverlay";
50
+ const { state } = useLoadingOverlay();
51
+ </script>
@@ -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,24 @@
1
+ export declare const useLoadingOverlay: () => {
2
+ state: Readonly<import("vue").Ref<{
3
+ readonly isActive: boolean;
4
+ readonly isVisible: boolean;
5
+ readonly backdrop: boolean;
6
+ readonly blocking: boolean;
7
+ readonly delay: number;
8
+ readonly label: string | null;
9
+ }, {
10
+ readonly isActive: boolean;
11
+ readonly isVisible: boolean;
12
+ readonly backdrop: boolean;
13
+ readonly blocking: boolean;
14
+ readonly delay: number;
15
+ readonly label: string | null;
16
+ }>>;
17
+ show: (options?: {
18
+ backdrop?: boolean;
19
+ blocking?: boolean;
20
+ delay?: number;
21
+ label?: string | null;
22
+ }) => void;
23
+ hide: () => void;
24
+ };
@@ -0,0 +1,36 @@
1
+ import { useRuntimeConfig, useState } from "#app";
2
+ import { readonly } from "vue";
3
+ let timer = null;
4
+ export const useLoadingOverlay = () => {
5
+ const config = useRuntimeConfig();
6
+ const loadingDelay = config.public.modals.loadingDelay;
7
+ const state = useState("loading-overlay", () => ({
8
+ isActive: false,
9
+ isVisible: false,
10
+ backdrop: false,
11
+ blocking: true,
12
+ delay: loadingDelay,
13
+ label: "Loading..."
14
+ }));
15
+ function show(options) {
16
+ state.value.backdrop = options?.backdrop === true ? true : false;
17
+ state.value.blocking = options?.blocking === false ? false : true;
18
+ state.value.delay = options?.delay ?? loadingDelay;
19
+ state.value.label = options?.label === null ? null : "Loading...";
20
+ state.value.isActive = true;
21
+ timer = setTimeout(() => state.value.isVisible = true, state.value.delay);
22
+ }
23
+ function hide() {
24
+ if (timer) {
25
+ clearTimeout(timer);
26
+ timer = null;
27
+ }
28
+ state.value.isActive = false;
29
+ state.value.isVisible = false;
30
+ }
31
+ return {
32
+ state: readonly(state),
33
+ show,
34
+ hide
35
+ };
36
+ };
@@ -0,0 +1,26 @@
1
+ import { type Component } from 'vue';
2
+ type RecordId = string | number;
3
+ type CrudModalMode = 'create' | 'read' | 'update';
4
+ export type CrudModalResult = {
5
+ success: boolean;
6
+ };
7
+ type CrudModalConfig<TRecord extends {
8
+ id: RecordId;
9
+ }> = {
10
+ components: Partial<Record<CrudModalMode, Component>>;
11
+ fetchData?: (id: RecordId) => Promise<TRecord>;
12
+ };
13
+ export declare function defineCrudModals<TRecord extends {
14
+ id: RecordId;
15
+ }>(config: CrudModalConfig<TRecord>): () => {
16
+ data: [TRecord | null] extends [import("vue").Ref<any, any>] ? import("@vue/shared").IfAny<import("vue").Ref<any, any> & TRecord, import("vue").Ref<import("vue").Ref<any, any> & TRecord, import("vue").Ref<any, any> & TRecord>, import("vue").Ref<any, any> & TRecord> : import("vue").Ref<import("vue").UnwrapRef<TRecord> | null, TRecord | import("vue").UnwrapRef<TRecord> | null>;
17
+ error: import("vue").Ref<string | null, string | null>;
18
+ isLoading: import("vue").Ref<boolean, boolean>;
19
+ isOpen: import("vue").Ref<boolean, boolean>;
20
+ openToCreate: (initialState?: Partial<TRecord>) => Promise<CrudModalResult | undefined>;
21
+ openToRead: (id: RecordId) => Promise<CrudModalResult | undefined>;
22
+ openToUpdate: (id: RecordId) => Promise<CrudModalResult | undefined>;
23
+ refresh: () => Promise<void>;
24
+ reset: () => void;
25
+ };
26
+ export {};
@@ -0,0 +1,90 @@
1
+ import { createError } from "#app";
2
+ import { useLoadingOverlay } from "#imports";
3
+ import { useOverlay } from "@nuxt/ui/runtime/composables/useOverlay.js";
4
+ import { useToast } from "@nuxt/ui/runtime/composables/useToast.js";
5
+ import { ref } from "vue";
6
+ export function defineCrudModals(config) {
7
+ const overlay = useOverlay();
8
+ const data = ref(null);
9
+ const error = ref(null);
10
+ const isLoading = ref(false);
11
+ const isOpen = ref(false);
12
+ function resolveComponent(mode) {
13
+ if (!config.components[mode])
14
+ throw createError({
15
+ status: 500,
16
+ statusText: `No component registered for modal mode '${mode}'`
17
+ });
18
+ return config.components[mode];
19
+ }
20
+ async function fetchData(id) {
21
+ if (!config.fetchData) return;
22
+ setLoading(true);
23
+ try {
24
+ data.value = await config.fetchData(id);
25
+ } catch (err) {
26
+ const toast = useToast();
27
+ toast.add({
28
+ title: "Failed to fetch record",
29
+ description: err instanceof Error ? err.message : "Unknown error",
30
+ color: "error"
31
+ });
32
+ } finally {
33
+ setLoading(false);
34
+ }
35
+ }
36
+ async function open(mode, payload) {
37
+ reset();
38
+ const component = resolveComponent(mode);
39
+ if (mode !== "create") {
40
+ const { id } = payload;
41
+ if (!id) throw new Error(`Mode '${mode}' requires an id`);
42
+ await fetchData(id);
43
+ if (!data.value) return;
44
+ } else if (mode === "create") {
45
+ const { initialState } = payload;
46
+ data.value = initialState;
47
+ }
48
+ isOpen.value = true;
49
+ const promise = overlay.create(component).open({ mode, ...payload });
50
+ promise.finally(() => isOpen.value = false);
51
+ return promise;
52
+ }
53
+ function openToRead(id) {
54
+ return open("read", { id });
55
+ }
56
+ function openToUpdate(id) {
57
+ return open("update", { id });
58
+ }
59
+ function openToCreate(initialState) {
60
+ return open("create", { initialState });
61
+ }
62
+ async function refresh() {
63
+ if (!data.value?.id) return;
64
+ return fetchData(data.value.id);
65
+ }
66
+ function reset() {
67
+ data.value = null;
68
+ error.value = null;
69
+ isOpen.value = false;
70
+ }
71
+ function setLoading(loading) {
72
+ isLoading.value = loading;
73
+ if (!isOpen.value) {
74
+ const loadingOverlay = useLoadingOverlay();
75
+ if (loading) loadingOverlay.show();
76
+ else loadingOverlay.hide();
77
+ }
78
+ }
79
+ return () => ({
80
+ data,
81
+ error,
82
+ isLoading,
83
+ isOpen,
84
+ openToCreate,
85
+ openToRead,
86
+ openToUpdate,
87
+ refresh,
88
+ reset
89
+ });
90
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../.nuxt/tsconfig.server.json",
3
+ }
@@ -0,0 +1,3 @@
1
+ export { default } from './module.mjs'
2
+
3
+ export { type ModuleOptions } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "nuxt-crud-modals",
3
+ "version": "1.1.0",
4
+ "description": "Nuxt UI compatible module to handle modals for creating, editing and viewing id based records",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/tosling/nuxt-crud-modals.git"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/types.d.mts",
14
+ "import": "./dist/module.mjs"
15
+ }
16
+ },
17
+ "main": "./dist/module.mjs",
18
+ "typesVersions": {
19
+ "*": {
20
+ ".": [
21
+ "./dist/types.d.mts"
22
+ ]
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "workspaces": [
29
+ "playground"
30
+ ],
31
+ "scripts": {
32
+ "prepack": "nuxt-module-build build",
33
+ "dev": "npm run dev:prepare && nuxt dev playground",
34
+ "dev:build": "nuxt build playground",
35
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
36
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
37
+ "lint": "eslint .",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest watch",
40
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
41
+ },
42
+ "dependencies": {
43
+ "@nuxt/kit": "^4.4.2",
44
+ "defu": "^6.1.6"
45
+ },
46
+ "devDependencies": {
47
+ "@nuxt/devtools": "^3.2.4",
48
+ "@nuxt/eslint-config": "^1.15.2",
49
+ "@nuxt/module-builder": "^1.0.2",
50
+ "@nuxt/schema": "^4.4.2",
51
+ "@nuxt/test-utils": "^4.0.0",
52
+ "@types/node": "latest",
53
+ "changelogen": "^0.6.2",
54
+ "eslint": "^10.1.0",
55
+ "nuxt": "^4.4.2",
56
+ "typescript": "~6.0.2",
57
+ "vitest": "^4.1.2",
58
+ "vue-tsc": "^3.2.6"
59
+ },
60
+ "peerDependencies": {
61
+ "@nuxt/ui": "^4.6.0",
62
+ "nuxt": "^4.4.2",
63
+ "zod": "^4.0.0"
64
+ }
65
+ }