nuxt-unified-ui 0.5.4 → 0.5.6
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/app/components/un-card.vue +2 -0
- package/app/components/un-typography.vue +31 -6
- package/app/utils/launch-toast.ts +16 -0
- package/app/utils/make-confetti.ts +182 -0
- package/modules/radashi.ts +7 -7
- package/package.json +3 -1
- package/skills/nuxt-unified-ui/SKILL.md +2 -1
- package/skills/nuxt-unified-ui/references/code-style.md +55 -2
- package/skills/nuxt-unified-ui/references/dialogs.md +1 -1
- package/skills/nuxt-unified-ui/references/layer-setup.md +2 -2
- package/skills/nuxt-unified-ui/references/public-surface.md +4 -1
- package/skills/nuxt-unified-ui/references/radashi.md +1 -1
- package/skills/nuxt-unified-ui/references/toast-and-ui.md +7 -2
|
@@ -15,6 +15,7 @@ const props = defineProps<{
|
|
|
15
15
|
titleClasses?: string;
|
|
16
16
|
subtitleClasses?: string;
|
|
17
17
|
textClasses?: string;
|
|
18
|
+
headerLevel?: number;
|
|
18
19
|
|
|
19
20
|
fluidBody?: boolean;
|
|
20
21
|
|
|
@@ -42,6 +43,7 @@ const slots = useSlots();
|
|
|
42
43
|
:icon-classes="props.iconClasses"
|
|
43
44
|
:title-classes="props.titleClasses"
|
|
44
45
|
:subtitle-classes="props.subtitleClasses"
|
|
46
|
+
:header-level="props.headerLevel"
|
|
45
47
|
class="p-3">
|
|
46
48
|
|
|
47
49
|
<template v-if="isSlotFilled(slots.title)" #title>
|
|
@@ -27,11 +27,36 @@ const props = defineProps({
|
|
|
27
27
|
textClasses: {
|
|
28
28
|
type: String,
|
|
29
29
|
},
|
|
30
|
+
headerLevel: {
|
|
31
|
+
type: Number,
|
|
32
|
+
default: 2,
|
|
33
|
+
},
|
|
30
34
|
});
|
|
31
35
|
|
|
32
36
|
const slots = useSlots();
|
|
33
37
|
|
|
34
38
|
|
|
39
|
+
/* header */
|
|
40
|
+
|
|
41
|
+
const titleTag = computed(() => {
|
|
42
|
+
return `h${resolveHeaderLevel(props.headerLevel)}`;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const subtitleTag = computed(() => {
|
|
46
|
+
return `h${Math.min(resolveHeaderLevel(props.headerLevel) + 1, 6)}`;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function resolveHeaderLevel(level) {
|
|
51
|
+
if (level >= 1 && level <= 6) {
|
|
52
|
+
return level;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return 2;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
35
60
|
/* flags */
|
|
36
61
|
|
|
37
62
|
const shouldShow = computed(() => {
|
|
@@ -59,12 +84,12 @@ const shouldShow = computed(() => {
|
|
|
59
84
|
<template v-if="props.title || isSlotFilled(slots.title)">
|
|
60
85
|
<div style="font-size: 1.3em;">
|
|
61
86
|
<slot name="title">
|
|
62
|
-
<
|
|
63
|
-
|
|
87
|
+
<component
|
|
88
|
+
:is="titleTag"
|
|
64
89
|
class="font-medium"
|
|
65
90
|
:class="props.titleClasses">
|
|
66
91
|
{{ props.title }}
|
|
67
|
-
</
|
|
92
|
+
</component>
|
|
68
93
|
</slot>
|
|
69
94
|
</div>
|
|
70
95
|
</template>
|
|
@@ -74,12 +99,12 @@ const shouldShow = computed(() => {
|
|
|
74
99
|
<template v-if="props.subtitle || isSlotFilled(slots.subtitle)">
|
|
75
100
|
<div style="font-size: 0.9em;" :style="{ marginInlineStart: props.icon ? '2em' : '0' }">
|
|
76
101
|
<slot name="subtitle">
|
|
77
|
-
<
|
|
78
|
-
|
|
102
|
+
<component
|
|
103
|
+
:is="subtitleTag"
|
|
79
104
|
class="font-light"
|
|
80
105
|
:class="props.subtitleClasses">
|
|
81
106
|
{{ props.subtitle }}
|
|
82
|
-
</
|
|
107
|
+
</component>
|
|
83
108
|
</slot>
|
|
84
109
|
</div>
|
|
85
110
|
</template>
|
|
@@ -24,3 +24,19 @@ export function toastError(options: ITypedToast) {
|
|
|
24
24
|
...options,
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
export function toastWarning(options: ITypedToast) {
|
|
29
|
+
toast({
|
|
30
|
+
icon: 'lucide:triangle-alert',
|
|
31
|
+
color: 'warning',
|
|
32
|
+
...options,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function toastInfo(options: ITypedToast) {
|
|
37
|
+
toast({
|
|
38
|
+
icon: 'lucide:info',
|
|
39
|
+
color: 'info',
|
|
40
|
+
...options,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import confetti from 'canvas-confetti';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function makeEdgeBumps() {
|
|
5
|
+
|
|
6
|
+
const bumpsPerFullEdge = 24;
|
|
7
|
+
const bumpsPerHalfEdge = bumpsPerFullEdge / 2;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
left: [
|
|
12
|
+
...makeSectionBumps({
|
|
13
|
+
from: {
|
|
14
|
+
x: 0.5,
|
|
15
|
+
y: 0,
|
|
16
|
+
},
|
|
17
|
+
to: {
|
|
18
|
+
x: 0,
|
|
19
|
+
y: 0,
|
|
20
|
+
},
|
|
21
|
+
angle: 270,
|
|
22
|
+
count: bumpsPerHalfEdge,
|
|
23
|
+
}),
|
|
24
|
+
...makeSectionBumps({
|
|
25
|
+
from: {
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0,
|
|
28
|
+
},
|
|
29
|
+
to: {
|
|
30
|
+
x: 0,
|
|
31
|
+
y: 1,
|
|
32
|
+
},
|
|
33
|
+
angle: 0,
|
|
34
|
+
count: bumpsPerFullEdge,
|
|
35
|
+
}),
|
|
36
|
+
...makeSectionBumps({
|
|
37
|
+
from: {
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 1,
|
|
40
|
+
},
|
|
41
|
+
to: {
|
|
42
|
+
x: 0.5,
|
|
43
|
+
y: 1,
|
|
44
|
+
},
|
|
45
|
+
angle: 90,
|
|
46
|
+
count: bumpsPerHalfEdge,
|
|
47
|
+
includeEnd: true,
|
|
48
|
+
}),
|
|
49
|
+
],
|
|
50
|
+
right: [
|
|
51
|
+
...makeSectionBumps({
|
|
52
|
+
from: {
|
|
53
|
+
x: 0.5,
|
|
54
|
+
y: 0,
|
|
55
|
+
},
|
|
56
|
+
to: {
|
|
57
|
+
x: 1,
|
|
58
|
+
y: 0,
|
|
59
|
+
},
|
|
60
|
+
angle: 270,
|
|
61
|
+
count: bumpsPerHalfEdge,
|
|
62
|
+
}),
|
|
63
|
+
...makeSectionBumps({
|
|
64
|
+
from: {
|
|
65
|
+
x: 1,
|
|
66
|
+
y: 0,
|
|
67
|
+
},
|
|
68
|
+
to: {
|
|
69
|
+
x: 1,
|
|
70
|
+
y: 1,
|
|
71
|
+
},
|
|
72
|
+
angle: 180,
|
|
73
|
+
count: bumpsPerFullEdge,
|
|
74
|
+
}),
|
|
75
|
+
...makeSectionBumps({
|
|
76
|
+
from: {
|
|
77
|
+
x: 1,
|
|
78
|
+
y: 1,
|
|
79
|
+
},
|
|
80
|
+
to: {
|
|
81
|
+
x: 0.5,
|
|
82
|
+
y: 1,
|
|
83
|
+
},
|
|
84
|
+
angle: 90,
|
|
85
|
+
count: bumpsPerHalfEdge,
|
|
86
|
+
includeEnd: true,
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function makeSectionBumps(options: { from: { x: number, y: number; }; to: { x: number; y: number; }; angle: number; count: number; includeEnd?: boolean; }) {
|
|
94
|
+
return Array.from({ length: options.count }, (_, index) => {
|
|
95
|
+
|
|
96
|
+
const progress = options.includeEnd
|
|
97
|
+
? index / Math.max(options.count - 1, 1)
|
|
98
|
+
: index / options.count;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
x: options.from.x + ((options.to.x - options.from.x) * progress),
|
|
102
|
+
y: options.from.y + ((options.to.y - options.from.y) * progress),
|
|
103
|
+
angle: options.angle,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
export function makeConfettiParade(duration = 1000, config: confetti.Options) {
|
|
111
|
+
|
|
112
|
+
if (import.meta.server) {
|
|
113
|
+
return Promise.resolve();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
const end = Date.now() + duration;
|
|
118
|
+
|
|
119
|
+
(function frame() {
|
|
120
|
+
|
|
121
|
+
confetti(config);
|
|
122
|
+
|
|
123
|
+
if (Date.now() < end) {
|
|
124
|
+
requestAnimationFrame(frame);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}());
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
return new Promise(resolve => setTimeout(resolve, duration));
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function makeConfettiOnTop(duration = 1000) {
|
|
135
|
+
return makeConfettiParade(duration, {
|
|
136
|
+
particleCount: 7,
|
|
137
|
+
angle: 270,
|
|
138
|
+
origin: {
|
|
139
|
+
x: 0.5,
|
|
140
|
+
y: 0,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function makeConfettiOnEdges(duration = 2000) {
|
|
146
|
+
|
|
147
|
+
if (import.meta.server) {
|
|
148
|
+
return Promise.resolve();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
const { left, right } = makeEdgeBumps();
|
|
153
|
+
const delay = duration / left.length;
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
for (const [index, leftBump] of left.entries()) {
|
|
157
|
+
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
|
|
160
|
+
for (const bump of [leftBump, right[index]!]) {
|
|
161
|
+
confetti({
|
|
162
|
+
particleCount: 24,
|
|
163
|
+
angle: bump.angle,
|
|
164
|
+
spread: 50,
|
|
165
|
+
startVelocity: 22,
|
|
166
|
+
scalar: 0.7,
|
|
167
|
+
ticks: 160,
|
|
168
|
+
origin: {
|
|
169
|
+
x: bump.x,
|
|
170
|
+
y: bump.y,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
}, index * delay);
|
|
176
|
+
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
return new Promise(resolve => setTimeout(resolve, duration));
|
|
181
|
+
|
|
182
|
+
}
|
package/modules/radashi.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { addImports, addServerImports, defineNuxtModule } from '@nuxt/kit';
|
|
2
2
|
import * as radashi from 'radashi';
|
|
3
3
|
|
|
4
4
|
|
|
@@ -9,14 +9,14 @@ export default defineNuxtModule({
|
|
|
9
9
|
setup() {
|
|
10
10
|
for (const name of Object.keys(radashi)) {
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const as = `${prefix}${radashi.pascal(name)}`;
|
|
14
|
-
|
|
15
|
-
addImports({
|
|
12
|
+
const spec = {
|
|
16
13
|
name,
|
|
17
|
-
as
|
|
14
|
+
as: `rad${radashi.pascal(name)}`,
|
|
18
15
|
from: 'radashi',
|
|
19
|
-
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
addImports(spec);
|
|
19
|
+
addServerImports(spec);
|
|
20
20
|
|
|
21
21
|
}
|
|
22
22
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-unified-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.6",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
"@nuxt/kit": "4.5.2",
|
|
25
25
|
"@nuxt/ui": "4.11.0",
|
|
26
26
|
"@nuxtjs/i18n": "10.6.0",
|
|
27
|
+
"@types/canvas-confetti": "1.9.0",
|
|
27
28
|
"@vueuse/core": "14.4.0",
|
|
28
29
|
"@vueuse/nuxt": "14.4.0",
|
|
30
|
+
"canvas-confetti": "1.9.4",
|
|
29
31
|
"radashi": "12.9.1",
|
|
30
32
|
"unified-mongo-filter": "0.4.0"
|
|
31
33
|
},
|
|
@@ -60,6 +60,7 @@ Absolute highlights:
|
|
|
60
60
|
- Every `<script setup>` section starts with `/* section name */`, then a blank line
|
|
61
61
|
- Within a section, group declarations by kind (imports, refs, computeds, watchers, functions, etc.): two blank lines between groups; no blanks between consecutive refs; one blank between consecutive members of other groups
|
|
62
62
|
- Non-trivial async/functions: blank line after `{`, double blank between major steps, blank before `}`
|
|
63
|
+
- A function whose body is one `for` / `while` / `if` or one connected `if` / `else` / `else if` or `try` / `catch` / `finally` chain stays flush: no blank lines between the function `{` / `}` and that block
|
|
63
64
|
- A function dedicated to choosing a return value from multiple criteria uses one exhaustive `if` / `else if` / `else` chain; broader functions may use guard clauses and early returns
|
|
64
65
|
- `else` / `catch` on their own line after `}`
|
|
65
66
|
- Script object literals always multi-line (even one property)
|
|
@@ -141,7 +142,7 @@ From `nuxt.config.ts`: `@vueuse/nuxt`, `@nuxt/ui`, `@nuxtjs/i18n`; `ui.colorMode
|
|
|
141
142
|
| Schema form | `useForm` + `<form-tag />` / `<un-form>` → [forms.md](references/forms.md) |
|
|
142
143
|
| Modal form | `launchFormPickerDialog` + `submitButton.onClick` → [dialogs.md](references/dialogs.md) |
|
|
143
144
|
| Confirm / choice | `launchChoicePickerDialog` + button `onClick` (avoid `value`) |
|
|
144
|
-
| Feedback | `toastSuccess` / `toastError` / `toast` |
|
|
145
|
+
| Feedback | `toastSuccess` / `toastError` / `toastWarning` / `toastInfo` / `toast` |
|
|
145
146
|
| Page chrome | `un-typography` + `un-card` |
|
|
146
147
|
| Custom field | `registerFormExtraElement` in a plugin |
|
|
147
148
|
| Utilities | `radXxx` → [radashi.md](references/radashi.md) |
|
|
@@ -14,7 +14,7 @@ Write code so a reader can **scan vertically** and see structure before details.
|
|
|
14
14
|
Start each logical `<script setup>` domain with a `/* section name */` comment and a blank line. Within that section, group imports, refs, computeds, watchers, functions, and other declarations by kind. Double blank lines separate groups; refs stay tightly stacked; consecutive members of other groups have one blank line between them.
|
|
15
15
|
|
|
16
16
|
2. **Function body spacing follows the work.**
|
|
17
|
-
A non-trivial workflow function is a mini-document: blank line after `{`, full-block guards, double blanks between major steps, and a blank before `}`. Small helpers and functions whose whole job is choosing a return value stay compact.
|
|
17
|
+
A non-trivial workflow function is a mini-document: blank line after `{`, full-block guards, double blanks between major steps, and a blank before `}`. A function whose body is one control block — or one connected `if` / `else` / `else if` or `try` / `catch` / `finally` chain — stays flush: no blanks between the function braces and that block. Small helpers and functions whose whole job is choosing a return value stay compact.
|
|
18
18
|
|
|
19
19
|
3. **One idea per line in structured data.**
|
|
20
20
|
Object/array literals in script are multi-line with trailing commas — even single-property objects passed to helpers (`toastSuccess`, `ufetch` options, etc.). Compact one-liners hide diffs and force horizontal reading.
|
|
@@ -138,6 +138,7 @@ async function handleLogin() {
|
|
|
138
138
|
- Guards as full blocks
|
|
139
139
|
- Double blank between guard/setup and main work / between major steps
|
|
140
140
|
- Blank line before closing `}`
|
|
141
|
+
- **Not** when the body is a single (connected) control block — that stays flush; see below
|
|
141
142
|
|
|
142
143
|
### Tiny blocks stay tight
|
|
143
144
|
|
|
@@ -156,6 +157,56 @@ async function handleSubmitSelection(items) {
|
|
|
156
157
|
|
|
157
158
|
Same for a `finally` that only flips one flag.
|
|
158
159
|
|
|
160
|
+
### Single-block functions stay flush
|
|
161
|
+
|
|
162
|
+
When a function body is **only** one control block (`for`, `while`, `if`, or similar) or **only** one connected chain (`if` / `else if` / `else`, or `try` / `catch` / `finally`), do **not** put blank lines before or after that block. The declaration line connects to the block (next line after `{` is the block), and the function’s closing `}` connects to the end of the block (no blank above it).
|
|
163
|
+
|
|
164
|
+
`if` / `else` / `else if` and `try` / `catch` / `finally` already stay adjacent — no blank lines between their parts — so the whole chain counts as one block.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
function fireBumps(bumps) {
|
|
168
|
+
for (const bump of bumps) {
|
|
169
|
+
confetti({
|
|
170
|
+
particleCount: 20,
|
|
171
|
+
origin: {
|
|
172
|
+
x: bump.x,
|
|
173
|
+
y: bump.y,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function abortIfServer() {
|
|
180
|
+
if (import.meta.server) {
|
|
181
|
+
return Promise.resolve();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function saveOrToast() {
|
|
186
|
+
try {
|
|
187
|
+
await save();
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
toastError({
|
|
191
|
+
title: 'Failed',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
// ❌ breathing-room blanks around a lone block
|
|
199
|
+
function fireBumps(bumps) {
|
|
200
|
+
|
|
201
|
+
for (const bump of bumps) {
|
|
202
|
+
...
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
If the function has **any other statement** besides that one block (a guard plus a loop, setup then a `for`, two separate `if`s, …), use the non-trivial workflow spacing instead. Interior spacing *inside* the block still follows the other rules.
|
|
209
|
+
|
|
159
210
|
### Return-only decision functions
|
|
160
211
|
|
|
161
212
|
When a function's whole job is to choose and return a value from multiple criteria, express the complete decision as one compact `if` / `else if` / `else` chain:
|
|
@@ -174,7 +225,7 @@ function getSortLabel(column) {
|
|
|
174
225
|
}
|
|
175
226
|
```
|
|
176
227
|
|
|
177
|
-
Use this pattern only when value selection is essentially the function's entire body. Functions that perform broader work may use guard clauses and early returns when those make the workflow clearer; do not force their logic into an exhaustive chain.
|
|
228
|
+
Use this pattern only when value selection is essentially the function's entire body. Keep the chain flush with the function braces (single-block rule). Functions that perform broader work may use guard clauses and early returns when those make the workflow clearer; do not force their logic into an exhaustive chain.
|
|
178
229
|
|
|
179
230
|
### `else` / `catch`
|
|
180
231
|
|
|
@@ -724,6 +775,7 @@ export default defineEventHandler(async event => {
|
|
|
724
775
|
| One-line `useUFetch(...)` | URL on next line; options multi-line |
|
|
725
776
|
| `.ts`/`.js` with no leading blanks (and no imports) | two blank lines at file start |
|
|
726
777
|
| Blank lines before first `import` | `import` on line 1 |
|
|
778
|
+
| Blank lines around a function that is only one `for` / `if` / `try` chain | Function `{` / `}` flush against that block |
|
|
727
779
|
|
|
728
780
|
---
|
|
729
781
|
|
|
@@ -735,6 +787,7 @@ export default defineEventHandler(async event => {
|
|
|
735
787
|
- [ ] Every `<script setup>` section starts with `/* section name */`, followed by a blank line
|
|
736
788
|
- [ ] Declarations are grouped by kind inside each section: two blanks between groups; no blanks between refs; one blank between other same-kind declarations
|
|
737
789
|
- [ ] Non-trivial workflow functions: blank after `{`, double blanks between major steps, blank before `}`
|
|
790
|
+
- [ ] Single-block functions (one `for` / `while` / `if`, or one connected `if` / `else` / `try` / `catch` chain) stay flush with the function braces
|
|
738
791
|
- [ ] Tiny helpers stay tight
|
|
739
792
|
- [ ] Return-only multi-criteria functions use a compact exhaustive `if` / `else if` / `else`; broader functions may use early returns
|
|
740
793
|
- [ ] `else` / `catch` on new line
|
|
@@ -110,7 +110,7 @@ Requires app tree under `u-app` (toaster from Nuxt UI + layer plugin `plugins/us
|
|
|
110
110
|
| `launchDialog({ component, props })` | `modal.open(props).result` | `app/utils/launch-dialog.ts` |
|
|
111
111
|
| `launchFormPickerDialog(options)` | submitted form object (or dismiss) | `app/utils/launch-form-picker-dialog.ts` |
|
|
112
112
|
| `launchChoicePickerDialog(options)` | button `value` if set (or dismiss) | `app/utils/launch-choice-picker-dialog.ts` |
|
|
113
|
-
| `toast` / `toastSuccess` / `toastError` | void | `app/utils/launch-toast.ts` |
|
|
113
|
+
| `toast` / `toastSuccess` / `toastError` / `toastWarning` / `toastInfo` | void | `app/utils/launch-toast.ts` |
|
|
114
114
|
|
|
115
115
|
```ts
|
|
116
116
|
await launchDialog({
|
|
@@ -37,7 +37,7 @@ Both are required:
|
|
|
37
37
|
## Peer / stack
|
|
38
38
|
|
|
39
39
|
- Peer dependency: `nuxt >= 4.5.2` (required)
|
|
40
|
-
- Bundled deps (layer): `@nuxt/ui`, `@nuxtjs/i18n`, `@vueuse/nuxt`, `@vueuse/core`, `radashi`, `unified-mongo-filter`, `@formkit/tempo`, Lucide iconify JSON, `@nuxt/kit`
|
|
40
|
+
- Bundled deps (layer): `@nuxt/ui`, `@nuxtjs/i18n`, `@vueuse/nuxt`, `@vueuse/core`, `radashi`, `unified-mongo-filter`, `@formkit/tempo`, `canvas-confetti`, `@types/canvas-confetti`, Lucide iconify JSON, `@nuxt/kit`
|
|
41
41
|
|
|
42
42
|
## Modules
|
|
43
43
|
|
|
@@ -51,7 +51,7 @@ modules: [
|
|
|
51
51
|
]
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Additionally, Nuxt auto-loads `modules/radashi.ts`, which registers every radashi export as `rad` + PascalCase name (`get` → `radGet`, `pick` → `radPick`, `camel` → `radCamel`, …). Full catalog: [radashi.md](radashi.md).
|
|
54
|
+
Additionally, Nuxt auto-loads `modules/radashi.ts`, which registers every radashi export as `rad` + PascalCase name (`get` → `radGet`, `pick` → `radPick`, `camel` → `radCamel`, …) in both the Vue app (`addImports`) and Nitro server (`addServerImports`). Full catalog: [radashi.md](radashi.md).
|
|
55
55
|
|
|
56
56
|
## UI defaults
|
|
57
57
|
|
|
@@ -35,6 +35,8 @@ Reference components in **kebab-case** in templates.
|
|
|
35
35
|
| `toast` | `utils/launch-toast.ts` | Uses `$toaster` from plugin |
|
|
36
36
|
| `toastSuccess` | same | `icon: lucide:check`, `color: success` |
|
|
37
37
|
| `toastError` | same | `icon: lucide:circle-alert`, `color: error` |
|
|
38
|
+
| `toastWarning` | same | `icon: lucide:triangle-alert`, `color: warning` |
|
|
39
|
+
| `toastInfo` | same | `icon: lucide:info`, `color: info` |
|
|
38
40
|
|
|
39
41
|
Plugin: `plugins/use-toaster.ts` → `provide.toaster = useToast()`.
|
|
40
42
|
|
|
@@ -53,6 +55,7 @@ Plugin: `plugins/use-toaster.ts` → `provide.toaster = useToast()`.
|
|
|
53
55
|
| `formatDate` / `parseDate` | `utils/format-date.ts` | `@formkit/tempo` helpers |
|
|
54
56
|
| `isSlotFilled` | `utils/is-slot-filled.ts` | Slot emptiness check |
|
|
55
57
|
| `pathRelativeToBase` | `utils/path-relative.ts` | Also exported from package entry |
|
|
58
|
+
| `makeConfettiParade` / `makeConfettiOnTop` / `makeConfettiOnEdges` | `utils/make-confetti.ts` | `canvas-confetti` helpers |
|
|
56
59
|
|
|
57
60
|
## Built-in form element identifiers
|
|
58
61
|
|
|
@@ -60,7 +63,7 @@ Mapped in `un-form`: `input`, `textarea`, `select`, `series`, `date`, `checkbox`
|
|
|
60
63
|
|
|
61
64
|
## Radashi
|
|
62
65
|
|
|
63
|
-
All radashi runtime exports are auto-imported as `radXxx
|
|
66
|
+
All radashi runtime exports are auto-imported as `radXxx` in both `app/` and `server/`. See [radashi.md](radashi.md).
|
|
64
67
|
|
|
65
68
|
## Package entry types (`index.d.ts`)
|
|
66
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Radashi auto-imports (`radXxx`)
|
|
2
2
|
|
|
3
|
-
Layer module `modules/radashi.ts` registers every runtime export from `radashi` as a Nuxt auto-import:
|
|
3
|
+
Layer module `modules/radashi.ts` registers every runtime export from `radashi` as a Nuxt auto-import in both the Vue app (`addImports`) and Nitro server (`addServerImports`):
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
rad + PascalCase(originalName)
|
|
@@ -9,6 +9,8 @@ type ITypedToast = Omit<IToast, 'icon' | 'color'>
|
|
|
9
9
|
toast(options: IToast)
|
|
10
10
|
toastSuccess(options: ITypedToast) // icon lucide:check, color success
|
|
11
11
|
toastError(options: ITypedToast) // icon lucide:circle-alert, color error
|
|
12
|
+
toastWarning(options: ITypedToast) // icon lucide:triangle-alert, color warning
|
|
13
|
+
toastInfo(options: ITypedToast) // icon lucide:info, color info
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
Implementation uses `useNuxtApp().$toaster` from `app/plugins/use-toaster.ts`:
|
|
@@ -23,11 +25,13 @@ export default defineNuxtPlugin(() => {
|
|
|
23
25
|
})
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
Pass any Nuxt UI toast fields (`title`, `description`, `color`, …).
|
|
28
|
+
Pass any Nuxt UI toast fields (`title`, `description`, `color`, …). Typed helpers omit `icon`/`color` from the options type; call `toast` directly for full control.
|
|
27
29
|
|
|
28
30
|
## `un-typography` — `app/components/un-typography.vue`
|
|
29
31
|
|
|
30
|
-
Props: `icon`, `title`, `subtitle`, `text`, plus `*Classes` variants.
|
|
32
|
+
Props: `icon`, `title`, `subtitle`, `text`, plus `*Classes` variants, and `headerLevel` (1–6, default `2`).
|
|
33
|
+
|
|
34
|
+
Title renders as `h{headerLevel}`; subtitle renders as the next heading level (capped at `h6`).
|
|
31
35
|
|
|
32
36
|
Slots: `title`, `subtitle`, `append`.
|
|
33
37
|
|
|
@@ -42,6 +46,7 @@ Notable props:
|
|
|
42
46
|
| Prop | Role |
|
|
43
47
|
|------|------|
|
|
44
48
|
| `icon`, `title`, `subtitle`, `text` | Header / body copy |
|
|
49
|
+
| `headerLevel` | Passed to `un-typography` (1–6, default `2`) |
|
|
45
50
|
| `fluidBody` | Drop body padding when true |
|
|
46
51
|
| `actions` | Footer buttons; items may set `actionType: 'spacer' \| 'button'`, `tooltip` |
|
|
47
52
|
| `verticalActions` | Stack actions; buttons `block` |
|