nuxt-ui-elements 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,22 +30,32 @@ A collection of beautiful, animated UI components for Nuxt applications. Built w
30
30
 
31
31
  ## Quick Setup
32
32
 
33
- Install the module to your Nuxt application with one command:
33
+ 1. Install the module to your Nuxt application:
34
34
 
35
35
  ```bash
36
36
  pnpm add nuxt-ui-elements
37
37
  ```
38
38
 
39
- Add the module to your `nuxt.config.ts`:
39
+ 2. Add the module to your `nuxt.config.ts`:
40
40
 
41
41
  ```ts
42
42
  export default defineNuxtConfig({
43
- modules: ['nuxt-ui-elements']
43
+ modules: ["@nuxt/ui", "nuxt-ui-elements"],
44
44
  })
45
45
  ```
46
46
 
47
+ 3. Import the module's CSS in your main CSS file (e.g., `assets/css/main.css`):
48
+
49
+ ```css
50
+ @import "tailwindcss";
51
+ @import "@nuxt/ui";
52
+ @import "nuxt-ui-elements";
53
+ ```
54
+
47
55
  That's it! You can now use Nuxt UI Elements in your Nuxt app ✨
48
56
 
57
+ > **Note:** This module requires `@nuxt/ui` v4.x as a peer dependency. The CSS import is required for Tailwind v4 to properly scan and generate styles from the component themes.
58
+
49
59
  ## Usage
50
60
 
51
61
  ### FlickeringGrid
@@ -61,8 +71,7 @@ A beautiful animated grid background component perfect for hero sections and lan
61
71
  gradient-direction="left-right"
62
72
  gradient-end-color="white"
63
73
  :flicker-speed="0.2"
64
- class="z-0"
65
- />
74
+ class="z-0" />
66
75
  <div class="relative z-10">
67
76
  <!-- Your content here -->
68
77
  </div>
@@ -113,24 +122,23 @@ An animated button component with an elegant shimmer effect, perfect for call-to
113
122
  shimmer-color="#60a5fa"
114
123
  background="rgba(59, 130, 246, 1)"
115
124
  :speed="2.5"
116
- @click="handleClick"
117
- />
125
+ @click="handleClick" />
118
126
  </template>
119
127
  ```
120
128
 
121
129
  #### Props
122
130
 
123
- | Prop | Type | Default | Description |
124
- | --------------- | -------- | -------------------- | ------------------------------------------------ |
125
- | `label` | `string` | - | Button label text |
126
- | `shimmerColor` | `string` | `'#ffffff'` | Color of the shimmer effect |
127
- | `shimmerSize` | `string` | `'0.05em'` | Size of the shimmer effect |
128
- | `speed` | `number` | `3` | Animation speed in seconds (lower = faster) |
129
- | `background` | `string` | `'rgba(0, 0, 0, 1)'` | Background color of the button |
130
- | `radius` | `string` | `'100px'` | Border radius of the button |
131
- | `size` | `string` | `'md'` | Button size: `xs`, `sm`, `md`, `lg`, `xl` |
132
- | `class` | `string` | - | Additional CSS classes |
133
- | `ui` | `object` | - | UI slot customization for advanced styling |
131
+ | Prop | Type | Default | Description |
132
+ | -------------- | -------- | -------------------- | ------------------------------------------- |
133
+ | `label` | `string` | - | Button label text |
134
+ | `shimmerColor` | `string` | `'#ffffff'` | Color of the shimmer effect |
135
+ | `shimmerSize` | `string` | `'0.05em'` | Size of the shimmer effect |
136
+ | `speed` | `number` | `3` | Animation speed in seconds (lower = faster) |
137
+ | `background` | `string` | `'rgba(0, 0, 0, 1)'` | Background color of the button |
138
+ | `radius` | `string` | `'100px'` | Border radius of the button |
139
+ | `size` | `string` | `'md'` | Button size: `xs`, `sm`, `md`, `lg`, `xl` |
140
+ | `class` | `string` | - | Additional CSS classes |
141
+ | `ui` | `object` | - | UI slot customization for advanced styling |
134
142
 
135
143
  #### Slots
136
144
 
@@ -145,20 +153,19 @@ An animated button component with an elegant shimmer effect, perfect for call-to
145
153
  #### Examples
146
154
 
147
155
  **Basic Usage:**
156
+
148
157
  ```vue
149
158
  <UEButtonShimmer label="Click Me" />
150
159
  ```
151
160
 
152
161
  **With Custom Colors:**
162
+
153
163
  ```vue
154
- <UEButtonShimmer
155
- label="Primary Action"
156
- shimmer-color="#a78bfa"
157
- background="rgba(139, 92, 246, 1)"
158
- />
164
+ <UEButtonShimmer label="Primary Action" shimmer-color="#a78bfa" background="rgba(139, 92, 246, 1)" />
159
165
  ```
160
166
 
161
167
  **Different Sizes:**
168
+
162
169
  ```vue
163
170
  <UEButtonShimmer label="Small" size="sm" />
164
171
  <UEButtonShimmer label="Medium" size="md" />
@@ -167,6 +174,7 @@ An animated button component with an elegant shimmer effect, perfect for call-to
167
174
  ```
168
175
 
169
176
  **With Slots:**
177
+
170
178
  ```vue
171
179
  <UEButtonShimmer>
172
180
  <template #leading>
@@ -180,11 +188,9 @@ An animated button component with an elegant shimmer effect, perfect for call-to
180
188
  ```
181
189
 
182
190
  **Fast Animation:**
191
+
183
192
  ```vue
184
- <UEButtonShimmer
185
- label="Fast Shimmer"
186
- :speed="1.5"
187
- />
193
+ <UEButtonShimmer label="Fast Shimmer" :speed="1.5" />
188
194
  ```
189
195
 
190
196
  ## Configuration
@@ -193,10 +199,10 @@ You can customize the component prefix in your `nuxt.config.ts`:
193
199
 
194
200
  ```ts
195
201
  export default defineNuxtConfig({
196
- modules: ['nuxt-ui-elements'],
202
+ modules: ["nuxt-ui-elements"],
197
203
  uiElements: {
198
- prefix: 'Custom' // Components will be named CustomFlickeringGrid
199
- }
204
+ prefix: "Custom", // Components will be named CustomFlickeringGrid
205
+ },
200
206
  })
201
207
  ```
202
208
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-ui-elements",
3
3
  "configKey": "uiElements",
4
- "version": "0.1.10",
4
+ "version": "0.1.12",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,4 +1,115 @@
1
- import { defineNuxtModule, createResolver, useLogger, addImportsDir } from '@nuxt/kit';
1
+ import { addTemplate, defineNuxtModule, createResolver, addImportsDir } from '@nuxt/kit';
2
+ import { kebabCase } from 'scule';
3
+
4
+ const dialogConfirm = (options) => ({
5
+ slots: {
6
+ content: "divide-y-0 bg-default ring-0",
7
+ header: "relative flex items-center gap-1.5 p-4 sm:px-6 min-h-16",
8
+ icon: "shrink-0 mt-1 size-6",
9
+ title: "text-lg font-semibold",
10
+ description: "text-sm opacity-90",
11
+ body: "p-2 sm:p-5 sm:pt-0",
12
+ footer: "justify-end gap-3",
13
+ close: ""
14
+ },
15
+ variants: {
16
+ size: {
17
+ sm: {
18
+ content: "sm:max-w-md"
19
+ },
20
+ md: {
21
+ content: "sm:max-w-3xl"
22
+ }
23
+ },
24
+ color: {
25
+ ...Object.fromEntries((options.theme.colors || []).map((color) => [color, ""])),
26
+ neutral: ""
27
+ },
28
+ variant: {
29
+ solid: "",
30
+ outline: ""
31
+ }
32
+ },
33
+ compoundVariants: [
34
+ // Solid variants - dynamically generated for all theme colors
35
+ ...(options.theme.colors || []).map((color) => ({
36
+ color,
37
+ variant: "solid",
38
+ class: {
39
+ content: `bg-${color} text-inverted`,
40
+ close: "!text-white hover:bg-white/10 active:bg-white/20"
41
+ }
42
+ })),
43
+ {
44
+ color: "neutral",
45
+ variant: "solid",
46
+ class: {
47
+ content: "text-inverted bg-inverted",
48
+ close: "!text-white hover:bg-white/10 active:bg-white/20"
49
+ }
50
+ },
51
+ // Outline variants - dynamically generated for all theme colors
52
+ ...(options.theme.colors || []).map((color) => ({
53
+ color,
54
+ variant: "outline",
55
+ class: {
56
+ content: `bg-gradient-to-b from-${color}/5 to-${color}/5 bg-default text-${color} !ring !ring-inset !ring-${color}`
57
+ }
58
+ })),
59
+ {
60
+ color: "neutral",
61
+ variant: "outline",
62
+ class: {
63
+ content: "text-highlighted bg-elevated !ring !ring-inset !ring-accented"
64
+ }
65
+ }
66
+ ],
67
+ defaultVariants: {
68
+ color: "primary",
69
+ variant: "solid",
70
+ size: "sm"
71
+ }
72
+ });
73
+
74
+ const theme = {
75
+ __proto__: null,
76
+ dialogConfirm: dialogConfirm
77
+ };
78
+
79
+ function addTemplates(options, _nuxt) {
80
+ const templates = [];
81
+ function writeThemeTemplate(themeModules) {
82
+ for (const component in themeModules) {
83
+ templates.push({
84
+ filename: `ui-elements/${kebabCase(component)}.ts`,
85
+ write: true,
86
+ getContents: () => {
87
+ const template = themeModules[component];
88
+ const result = typeof template === "function" ? template(options) : template;
89
+ const variants = Object.entries(result.variants || {}).filter(([_, values]) => {
90
+ const keys = Object.keys(values);
91
+ return keys.some((key) => key !== "true" && key !== "false");
92
+ }).map(([key]) => key);
93
+ let json = JSON.stringify(result, null, 2);
94
+ for (const variant of variants) {
95
+ json = json.replace(new RegExp(`("${variant}": "[^"]+")`, "g"), `$1 as typeof ${variant}[number]`);
96
+ json = json.replace(new RegExp(`("${variant}": \\[\\s*)((?:"[^"]+",?\\s*)+)(\\])`, "g"), (_, before, match, after) => {
97
+ const replaced = match.replace(/("[^"]+")/g, `$1 as typeof ${variant}[number]`);
98
+ return `${before}${replaced}${after}`;
99
+ });
100
+ }
101
+ const variantDeclarations = variants.filter((variant) => json.includes(`as typeof ${variant}`)).map((variant) => {
102
+ const keys = Object.keys(result.variants[variant]);
103
+ return `const ${variant} = ${JSON.stringify(keys, null, 2)} as const`;
104
+ });
105
+ return [...variantDeclarations, `export default ${json}`].join("\n\n");
106
+ }
107
+ });
108
+ }
109
+ }
110
+ writeThemeTemplate(theme);
111
+ templates.forEach((template) => addTemplate(template));
112
+ }
2
113
 
3
114
  const module$1 = defineNuxtModule({
4
115
  meta: {
@@ -11,9 +122,18 @@ const module$1 = defineNuxtModule({
11
122
  moduleDependencies: {
12
123
  "@nuxt/ui": {}
13
124
  },
14
- setup(_options, _nuxt) {
125
+ setup(options, nuxt) {
15
126
  const resolver = createResolver(import.meta.url);
16
- useLogger("nuxt-ui-elements");
127
+ const uiOptions = nuxt.options.ui;
128
+ const themeColors = uiOptions?.theme?.colors || ["primary", "secondary", "success", "info", "warning", "error"];
129
+ const optionsWithTheme = {
130
+ ...options,
131
+ theme: {
132
+ colors: themeColors
133
+ }
134
+ };
135
+ addTemplates(optionsWithTheme);
136
+ nuxt.options.css.push(resolver.resolve("./runtime/index.css"));
17
137
  addImportsDir(resolver.resolve("./runtime/composables"));
18
138
  }
19
139
  });
@@ -1,25 +1,49 @@
1
- interface Props {
2
- open?: boolean;
3
- size?: "sm" | "md";
1
+ import type { AppConfig } from "@nuxt/schema";
2
+ import theme from "#build/ui-elements/dialog-confirm";
3
+ import type { ComponentConfig } from "../types/tv.js";
4
+ type DialogConfirm = ComponentConfig<typeof theme, AppConfig, "dialogConfirm">;
5
+ export interface DialogConfirmEmits {
6
+ "update:open": [value: boolean];
7
+ close: [value?: any];
8
+ "after:leave": [];
9
+ }
10
+ export interface DialogConfirmSlots {
11
+ title(props?: {}): any;
12
+ description(props?: {}): any;
13
+ actions(props?: {}): any;
14
+ }
15
+ export interface DialogConfirmProps {
4
16
  title: string;
5
17
  description?: string;
18
+ icon?: boolean;
6
19
  confirmLabel?: string;
7
20
  dismissLabel?: string;
8
- clickToClose?: boolean;
9
- async?: boolean;
10
- successDelay?: number;
21
+ close?: boolean;
22
+ color?: DialogConfirm["variants"]["color"];
23
+ variant?: DialogConfirm["variants"]["variant"];
11
24
  onConfirm?: (() => void) | (() => Promise<void>);
12
25
  onDismiss?: () => void;
13
- onClose?: () => void;
26
+ ui?: Partial<DialogConfirm["slots"]>;
14
27
  }
15
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {
16
- "update:open": (value: boolean) => any;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<DialogConfirmProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
31
  close: (value?: any) => any;
32
+ "update:open": (value: boolean) => any;
18
33
  "after:leave": () => any;
19
- }, string, import("@vue/runtime-core").PublicProps, Readonly<Props> & Readonly<{
20
- "onUpdate:open"?: ((value: boolean) => any) | undefined;
34
+ }, string, import("vue").PublicProps, Readonly<DialogConfirmProps> & Readonly<{
21
35
  onClose?: ((value?: any) => any) | undefined;
36
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
22
37
  "onAfter:leave"?: (() => any) | undefined;
23
- }>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
24
- declare const _default: typeof __VLS_export;
25
- export default _default;
38
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
39
+ title?: (props: {}) => any;
40
+ } & {
41
+ description?: (props: {}) => any;
42
+ } & {
43
+ actions?: (props: {}) => any;
44
+ }>;
45
+ type __VLS_WithSlots<T, S> = T & {
46
+ new (): {
47
+ $slots: S;
48
+ };
49
+ };
@@ -1,119 +1,170 @@
1
- <script setup>
1
+ <script>
2
+ import theme from "#build/ui-elements/dialog-confirm";
2
3
  import { computed, ref } from "vue";
4
+ import { DialogTitle, DialogDescription } from "reka-ui";
5
+ import { tv } from "../utils/tv";
6
+ </script>
7
+
8
+ <script setup>
3
9
  const {
4
- open = false,
5
- size = "sm",
10
+ title,
11
+ description = "",
12
+ icon = true,
6
13
  confirmLabel = "Yes",
7
14
  dismissLabel = "No",
8
- async = false,
9
- successDelay = 1200,
10
- ...props
15
+ close = false,
16
+ color = "neutral",
17
+ variant = "solid",
18
+ onConfirm,
19
+ onDismiss,
20
+ ui: uiProps
11
21
  } = defineProps({
12
- open: { type: Boolean, required: false },
13
- size: { type: String, required: false },
14
22
  title: { type: String, required: true },
15
23
  description: { type: String, required: false },
24
+ icon: { type: Boolean, required: false },
16
25
  confirmLabel: { type: String, required: false },
17
26
  dismissLabel: { type: String, required: false },
18
- clickToClose: { type: Boolean, required: false },
19
- async: { type: Boolean, required: false },
20
- successDelay: { type: Number, required: false },
27
+ close: { type: Boolean, required: false },
28
+ color: { type: null, required: false },
29
+ variant: { type: null, required: false },
21
30
  onConfirm: { type: Function, required: false },
22
31
  onDismiss: { type: Function, required: false },
23
- onClose: { type: Function, required: false }
32
+ ui: { type: Object, required: false }
24
33
  });
25
- defineEmits(["update:open", "close", "after:leave"]);
26
- const sizeClass = computed(() => {
27
- switch (size) {
28
- case "sm":
29
- return "sm:max-w-md";
30
- case "md":
31
- return "sm:max-w-3xl";
32
- default:
33
- return "max-w-3xl";
34
- }
35
- });
36
- const isConfirming = ref(false);
34
+ const emits = defineEmits(["update:open", "close", "after:leave"]);
35
+ const isLoading = ref(false);
37
36
  const isComplete = ref(false);
38
- const error = ref(null);
39
- const confirm = async () => {
40
- if (props.onConfirm) {
41
- if (async) {
42
- isConfirming.value = true;
43
- error.value = null;
44
- try {
45
- await Promise.resolve(props.onConfirm());
46
- isConfirming.value = false;
47
- isComplete.value = true;
48
- await new Promise((resolve) => setTimeout(resolve, successDelay));
49
- props.onClose?.();
50
- } catch (e) {
51
- isConfirming.value = false;
52
- error.value = e instanceof Error ? e.message : "An error occurred";
53
- return;
54
- }
37
+ const isError = ref(false);
38
+ const ui = computed(
39
+ () => tv({
40
+ extend: tv(theme)
41
+ })({
42
+ size: "sm",
43
+ color,
44
+ variant
45
+ })
46
+ );
47
+ const colorIconMap = {
48
+ primary: "i-lucide-info",
49
+ secondary: "i-lucide-info",
50
+ success: "i-lucide-circle-check",
51
+ info: "i-lucide-info",
52
+ warning: "i-lucide-triangle-alert",
53
+ error: "i-lucide-circle-x",
54
+ neutral: "i-lucide-info"
55
+ };
56
+ const dialogIcon = computed(() => {
57
+ if (icon === false) return void 0;
58
+ return colorIconMap[color];
59
+ });
60
+ const closeButtonProps = computed(() => ({
61
+ icon: "i-lucide-x",
62
+ size: "md",
63
+ color: "neutral",
64
+ variant: "ghost"
65
+ }));
66
+ const confirmHandler = async () => {
67
+ if (!onConfirm) {
68
+ emits("close");
69
+ return;
70
+ }
71
+ try {
72
+ const result = onConfirm();
73
+ const isAsync = result instanceof Promise;
74
+ if (isAsync) {
75
+ isLoading.value = true;
76
+ await result;
77
+ isLoading.value = false;
78
+ isComplete.value = true;
79
+ setTimeout(() => {
80
+ emits("close");
81
+ }, 800);
55
82
  } else {
56
- props.onConfirm();
57
- props.onClose?.();
83
+ emits("close");
58
84
  }
85
+ } catch (error) {
86
+ isLoading.value = false;
87
+ isError.value = true;
88
+ console.error("Dialog confirm error:", error);
89
+ setTimeout(() => {
90
+ isError.value = false;
91
+ }, 2e3);
59
92
  }
60
93
  };
61
94
  const dismiss = () => {
62
- if (props.onDismiss) {
63
- props.onDismiss();
95
+ if (onDismiss) {
96
+ onDismiss();
64
97
  }
65
- props.onClose?.();
98
+ emits("close");
99
+ };
100
+ const handleClose = () => {
101
+ dismiss();
66
102
  };
67
103
  </script>
68
104
 
69
105
  <template>
70
106
  <UModal
71
- :open="open"
72
- :dismissible="clickToClose"
107
+ :dismissible="false"
73
108
  :close="false"
74
109
  :title="title"
110
+ :description="description"
75
111
  :ui="{
76
- content: `${sizeClass} divide-y-0`,
77
- header: 'px-2 sm:px-5 py-3',
78
- body: 'p-2 sm:p-5 sm:pt-0',
79
- footer: 'justify-end gap-3'
112
+ content: ui.content({ class: uiProps?.content }),
113
+ header: ui.header({ class: uiProps?.header }),
114
+ body: ui.body({ class: uiProps?.body }),
115
+ footer: ui.footer({ class: uiProps?.footer })
80
116
  }"
81
- @update:open="$emit('update:open', $event)"
82
- @close="$emit('close', $event)"
83
- @after:leave="$emit('after:leave')"
84
- >
85
- <template #body>
86
- <div class="space-y-3">
87
- <!-- eslint-disable-next-line vue/no-v-html -->
88
- <div class="text-sm text-gray-500" v-html="description" />
117
+ @close="emits('close', $event)"
118
+ @after:leave="emits('after:leave')">
119
+ <template #header>
120
+ <div class="relative w-full flex items-start gap-3">
121
+ <UIcon v-if="dialogIcon" :name="dialogIcon" data-slot="icon" :class="ui.icon({ class: uiProps?.icon })" />
122
+ <div class="flex-1 min-w-0">
123
+ <DialogTitle v-if="title" data-slot="title" :class="ui.title({ class: uiProps?.title })">
124
+ <slot name="title">
125
+ {{ title }}
126
+ </slot>
127
+ </DialogTitle>
89
128
 
90
- <!-- Error message -->
91
- <div
92
- v-if="error"
93
- class="text-sm text-red-500 dark:text-red-400 bg-red-50 dark:bg-red-950 p-3 rounded"
94
- >
95
- {{ error }}
129
+ <DialogDescription v-if="description" :class="ui.description({ class: uiProps?.description })">
130
+ <slot name="description">
131
+ {{ description }}
132
+ </slot>
133
+ </DialogDescription>
96
134
  </div>
135
+
136
+ <UButton
137
+ v-if="close"
138
+ v-bind="closeButtonProps"
139
+ data-slot="close"
140
+ :class="ui.close({ class: uiProps?.close })"
141
+ @click="handleClose" />
97
142
  </div>
98
143
  </template>
144
+
99
145
  <template #footer>
100
- <UButton
101
- v-if="dismissLabel"
102
- :ui="{ base: 'justify-around' }"
103
- color="neutral"
104
- variant="outline"
105
- :label="dismissLabel"
106
- @click="dismiss"
107
- />
108
- <UButton
109
- color="neutral"
110
- :loading="isConfirming"
111
- :disabled="isComplete"
112
- :label="isComplete ? void 0 : confirmLabel"
113
- :icon="isComplete ? 'i-heroicons-check' : void 0"
114
- :ui="{ base: 'justify-around' }"
115
- @click="confirm"
116
- />
146
+ <slot name="actions">
147
+ <!-- Dismiss button (No) - hidden when loading, complete, or error -->
148
+ <UButton
149
+ v-if="dismissLabel && !isLoading && !isComplete && !isError"
150
+ :label="dismissLabel"
151
+ size="lg"
152
+ :color="color"
153
+ variant="solid"
154
+ @click="dismiss" />
155
+
156
+ <!-- Confirm button (Yes) - shows loading/complete/error states -->
157
+ <UButton
158
+ :label="isError ? 'Error' : isComplete ? 'Complete' : confirmLabel"
159
+ size="lg"
160
+ color="neutral"
161
+ variant="outline"
162
+ :loading="isLoading"
163
+ :disabled="isComplete"
164
+ :leading-icon="isError ? 'i-lucide-circle-x' : isComplete ? 'i-lucide-check' : void 0"
165
+ :class="{ 'animate-shake': isError }"
166
+ @click="confirmHandler" />
167
+ </slot>
117
168
  </template>
118
169
  </UModal>
119
170
  </template>
@@ -1,25 +1,49 @@
1
- interface Props {
2
- open?: boolean;
3
- size?: "sm" | "md";
1
+ import type { AppConfig } from "@nuxt/schema";
2
+ import theme from "#build/ui-elements/dialog-confirm";
3
+ import type { ComponentConfig } from "../types/tv.js";
4
+ type DialogConfirm = ComponentConfig<typeof theme, AppConfig, "dialogConfirm">;
5
+ export interface DialogConfirmEmits {
6
+ "update:open": [value: boolean];
7
+ close: [value?: any];
8
+ "after:leave": [];
9
+ }
10
+ export interface DialogConfirmSlots {
11
+ title(props?: {}): any;
12
+ description(props?: {}): any;
13
+ actions(props?: {}): any;
14
+ }
15
+ export interface DialogConfirmProps {
4
16
  title: string;
5
17
  description?: string;
18
+ icon?: boolean;
6
19
  confirmLabel?: string;
7
20
  dismissLabel?: string;
8
- clickToClose?: boolean;
9
- async?: boolean;
10
- successDelay?: number;
21
+ close?: boolean;
22
+ color?: DialogConfirm["variants"]["color"];
23
+ variant?: DialogConfirm["variants"]["variant"];
11
24
  onConfirm?: (() => void) | (() => Promise<void>);
12
25
  onDismiss?: () => void;
13
- onClose?: () => void;
26
+ ui?: Partial<DialogConfirm["slots"]>;
14
27
  }
15
- declare const __VLS_export: import("@vue/runtime-core").DefineComponent<Props, {}, {}, {}, {}, import("@vue/runtime-core").ComponentOptionsMixin, import("@vue/runtime-core").ComponentOptionsMixin, {
16
- "update:open": (value: boolean) => any;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<DialogConfirmProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
31
  close: (value?: any) => any;
32
+ "update:open": (value: boolean) => any;
18
33
  "after:leave": () => any;
19
- }, string, import("@vue/runtime-core").PublicProps, Readonly<Props> & Readonly<{
20
- "onUpdate:open"?: ((value: boolean) => any) | undefined;
34
+ }, string, import("vue").PublicProps, Readonly<DialogConfirmProps> & Readonly<{
21
35
  onClose?: ((value?: any) => any) | undefined;
36
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
22
37
  "onAfter:leave"?: (() => any) | undefined;
23
- }>, {}, {}, {}, {}, string, import("@vue/runtime-core").ComponentProvideOptions, false, {}, any>;
24
- declare const _default: typeof __VLS_export;
25
- export default _default;
38
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
39
+ title?: (props: {}) => any;
40
+ } & {
41
+ description?: (props: {}) => any;
42
+ } & {
43
+ actions?: (props: {}) => any;
44
+ }>;
45
+ type __VLS_WithSlots<T, S> = T & {
46
+ new (): {
47
+ $slots: S;
48
+ };
49
+ };
@@ -1,12 +1,21 @@
1
- import DialogConfirm from "../components/DialogConfirm.vue.js";
2
1
  type CallbackFn = () => void | Promise<void>;
3
- interface DialogInstance {
4
- onConfirm: (fn: CallbackFn) => DialogInstance;
5
- onDismiss: (fn: CallbackFn) => DialogInstance;
6
- open: () => DialogInstance;
2
+ type Color = "primary" | "secondary" | "success" | "info" | "warning" | "error" | (string & {});
3
+ type Variant = "solid" | "outline";
4
+ export interface DialogConfirmOptions {
5
+ title: string;
6
+ description?: string;
7
+ icon?: boolean;
8
+ confirmLabel?: string;
9
+ dismissLabel?: string;
10
+ color?: Color;
11
+ variant?: Variant;
12
+ close?: boolean;
13
+ onConfirm?: CallbackFn;
14
+ onDismiss?: CallbackFn;
15
+ ui?: any;
7
16
  }
8
17
  export declare const useDialog: () => {
9
- confirm: (options: Omit<InstanceType<typeof DialogConfirm>["$props"], "onDismiss" | "onConfirm" | "class">) => DialogInstance;
10
- confirmNavigate: (path: string) => void;
18
+ confirm: (options: DialogConfirmOptions) => void;
19
+ confirmNavigate: (path: string, options?: Omit<DialogConfirmOptions, "title" | "description" | "onConfirm">) => void;
11
20
  };
12
21
  export {};
@@ -4,48 +4,21 @@ import { navigateTo } from "#app";
4
4
  export const useDialog = () => {
5
5
  const overlay = useOverlay();
6
6
  const confirm = (options) => {
7
- let confirmCallback = () => {
8
- };
9
- let dismissCallback = () => {
10
- };
11
7
  const modal = overlay.create(DialogConfirm, {
12
8
  destroyOnClose: true,
13
- props: {
14
- ...options,
15
- onConfirm: async () => {
16
- await confirmCallback();
17
- },
18
- onDismiss: async () => {
19
- await dismissCallback();
20
- }
21
- // onClose: () => {
22
- // modal.close();
23
- // },
24
- }
9
+ props: options
25
10
  });
26
- const dialogInstance = {
27
- onConfirm: (fn) => {
28
- confirmCallback = fn;
29
- return dialogInstance;
30
- },
31
- onDismiss: (fn) => {
32
- dismissCallback = fn;
33
- return dialogInstance;
34
- },
35
- open: () => {
36
- modal.open();
37
- return dialogInstance;
38
- }
39
- };
40
- return dialogInstance;
11
+ modal.open();
41
12
  };
42
- const confirmNavigate = (path) => {
13
+ const confirmNavigate = (path, options) => {
43
14
  confirm({
44
15
  title: "Leave this page?",
45
- description: "Are you sure you want to navigate away? Unsaved changes will be lost."
46
- }).onConfirm(() => {
47
- navigateTo(path);
48
- }).open();
16
+ description: "Are you sure you want to navigate away? Unsaved changes will be lost.",
17
+ ...options,
18
+ onConfirm: () => {
19
+ navigateTo(path);
20
+ }
21
+ });
49
22
  };
50
23
  return { confirm, confirmNavigate };
51
24
  };
@@ -0,0 +1 @@
1
+ @source "../theme";@source "./components";@keyframes shake{0%,to{transform:translateX(0)}10%,30%,50%,70%,90%{transform:translateX(-4px)}20%,40%,60%,80%{transform:translateX(4px)}}.animate-shake{animation:shake .5s ease-in-out}
@@ -0,0 +1,64 @@
1
+ import type { ClassValue, TVVariants, TVCompoundVariants, TVDefaultVariants } from "tailwind-variants";
2
+ /**
3
+ * Defines the AppConfig object based on the tailwind-variants configuration.
4
+ */
5
+ export type TVConfig<T extends Record<string, any>> = {
6
+ [P in keyof T]?: {
7
+ [K in keyof T[P] as K extends "base" | "slots" | "variants" | "defaultVariants" ? K : never]?: K extends "base" ? ClassValue : K extends "slots" ? {
8
+ [S in keyof T[P]["slots"]]?: ClassValue;
9
+ } : K extends "variants" ? TVVariants<T[P]["slots"], ClassValue, WidenVariantsValues<T[P]["variants"]>> : K extends "defaultVariants" ? TVDefaultVariants<WidenVariantsValues<T[P]["variants"]>, T[P]["slots"], object, undefined> : never;
10
+ };
11
+ } & {
12
+ [P in keyof T]?: {
13
+ compoundVariants?: TVCompoundVariants<WidenVariantsValues<T[P]["variants"]>, T[P]["slots"], ClassValue, object, undefined>;
14
+ };
15
+ };
16
+ type WidenVariantsValues<V extends Record<string, any> | undefined> = V extends Record<string, any> ? V & {
17
+ [K in keyof V]: V[K] extends Record<string, any> ? V[K] & Record<string & {}, any> : V[K];
18
+ } : V;
19
+ /**
20
+ * Utility type to flatten intersection types for better IDE hover information.
21
+ * @template T The type to flatten.
22
+ */
23
+ type Id<T> = {} & {
24
+ [P in keyof T]: T[P];
25
+ };
26
+ type ComponentVariants<T extends {
27
+ variants?: Record<string, Record<string, any>>;
28
+ }> = {
29
+ [K in keyof T["variants"]]: keyof T["variants"][K];
30
+ };
31
+ type ComponentSlots<T extends {
32
+ slots?: Record<string, any>;
33
+ }> = Id<{
34
+ [K in keyof T["slots"]]?: ClassValue;
35
+ }>;
36
+ type ComponentUI<T extends {
37
+ slots?: Record<string, any>;
38
+ }> = Id<{
39
+ [K in keyof Required<T["slots"]>]: (props?: Record<string, any>) => string;
40
+ }>;
41
+ type GetComponentAppConfig<A, U extends string, K extends string> = A extends Record<U, Record<K, any>> ? A[U][K] : {};
42
+ type ComponentAppConfig<T, A extends Record<string, any>, K extends string, U extends string = "ui" | "uiElements"> = A & (U extends "uiElements" ? {
43
+ uiElements?: {
44
+ [k in K]?: Partial<T>;
45
+ };
46
+ } : {
47
+ [key in Exclude<U, "uiElements">]?: {
48
+ [k in K]?: Partial<T>;
49
+ };
50
+ });
51
+ /**
52
+ * Defines the configuration shape expected for a component.
53
+ * @template T The component's theme imported from `#build/ui-elements/*`.
54
+ * @template A The base AppConfig type from `@nuxt/schema`.
55
+ * @template K The key identifying the component (e.g., 'dialogConfirm').
56
+ * @template U The top-level key in AppConfig ('ui' or 'uiElements').
57
+ */
58
+ export type ComponentConfig<T extends Record<string, any>, A extends Record<string, any>, K extends string, U extends "ui" | "uiElements" = "uiElements"> = {
59
+ AppConfig: ComponentAppConfig<T, A, K, U>;
60
+ variants: ComponentVariants<T & GetComponentAppConfig<A, U, K>>;
61
+ slots: ComponentSlots<T>;
62
+ ui: ComponentUI<T>;
63
+ };
64
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ export declare const tv: import("tailwind-variants").TV;
@@ -0,0 +1,2 @@
1
+ import { createTV } from "tailwind-variants";
2
+ export const tv = /* @__PURE__ */ createTV({});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-ui-elements",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A collection of beautiful, animated UI components for Nuxt applications",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/genu/nuxt-ui-elements.git",
@@ -9,6 +9,7 @@
9
9
  ],
10
10
  "type": "module",
11
11
  "main": "./dist/module.mjs",
12
+ "style": "./dist/runtime/index.css",
12
13
  "typesVersions": {
13
14
  "*": {
14
15
  ".": [
@@ -19,27 +20,29 @@
19
20
  "exports": {
20
21
  ".": {
21
22
  "types": "./dist/types.d.mts",
23
+ "style": "./dist/runtime/index.css",
22
24
  "import": "./dist/module.mjs"
23
25
  }
24
26
  },
25
27
  "dependencies": {
26
28
  "@nuxt/kit": "^4.2.2",
27
- "culori": "^4.0.2",
28
- "motion-v": "^1.7.4",
29
+ "scule": "^1.3.0",
29
30
  "tailwind-variants": "^3.2.2"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@nuxt/devtools": "^3.1.1",
34
+ "@nuxt/eslint-config": "^1.12.1",
33
35
  "@nuxt/module-builder": "^1.0.2",
34
36
  "@nuxt/schema": "^4.2.2",
35
- "@nuxt/test-utils": "^3.21.0",
37
+ "@nuxt/test-utils": "^3.22.0",
36
38
  "@types/culori": "^4.0.1",
37
39
  "@types/node": "latest",
38
40
  "changelogen": "^0.6.2",
41
+ "eslint": "^9.39.2",
42
+ "eslint-config-prettier": "10.1.8",
43
+ "eslint-plugin-prettier": "5.5.4",
39
44
  "nuxt": "^4.2.2",
40
- "oxfmt": "^0.20.0",
41
- "oxlint": "^1.35.0",
42
- "oxlint-tsgolint": "^0.10.0",
45
+ "prettier": "^3.7.4",
43
46
  "typescript": "~5.9.3",
44
47
  "vitest": "^4.0.16",
45
48
  "vue-tsc": "^3.2.1"
@@ -51,10 +54,10 @@
51
54
  "dev": "pnpm dev:prepare && nuxi dev playground",
52
55
  "dev:build": "nuxi build playground",
53
56
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
54
- "lint": "oxlint --type-aware",
55
- "lint:fix": "oxlint --fix --type-aware",
56
- "format": "oxfmt --write",
57
- "format:check": "oxfmt --check",
57
+ "lint": "eslint .",
58
+ "lint:fix": "eslint . --fix",
59
+ "format": "prettier --write .",
60
+ "format:check": "prettier --check .",
58
61
  "release": "pnpm lint && pnpm test && pnpm prepack && changelogen --release && pnpm publish && git push --follow-tags",
59
62
  "test": "vitest run",
60
63
  "test:watch": "vitest watch",