nuxt-unified-ui 0.5.14 → 0.5.16
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/package.json +4 -4
- package/skills/nuxt-unified-ui/SKILL.md +11 -5
- package/skills/nuxt-unified-ui/references/code-style.md +113 -40
- package/skills/nuxt-unified-ui/references/dialogs.md +2 -2
- package/skills/nuxt-unified-ui/references/tables.md +3 -9
- package/skills/nuxt-unified-ui/references/toast-and-ui.md +4 -1
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.16",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@formkit/tempo": "1.1.0",
|
|
23
|
-
"@iconify-json/lucide": "1.2.
|
|
23
|
+
"@iconify-json/lucide": "1.2.136",
|
|
24
24
|
"@nuxt/kit": "4.5.2",
|
|
25
|
-
"@nuxt/ui": "4.11.
|
|
25
|
+
"@nuxt/ui": "4.11.2",
|
|
26
26
|
"@nuxtjs/i18n": "10.6.0",
|
|
27
27
|
"@types/canvas-confetti": "1.9.0",
|
|
28
28
|
"@vueuse/core": "15.0.0",
|
|
29
29
|
"@vueuse/nuxt": "15.0.0",
|
|
30
30
|
"canvas-confetti": "1.9.4",
|
|
31
|
-
"radashi": "12.9.
|
|
31
|
+
"radashi": "12.9.6",
|
|
32
32
|
"unified-mongo-filter": "0.4.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
@@ -8,10 +8,11 @@ description: >-
|
|
|
8
8
|
(attribute order, row actions, pagination), pages /
|
|
9
9
|
definePageMeta, page /* params */ and /* seo */ blocks, ufetch / useUFetch wrapping, unified resources
|
|
10
10
|
(server plugins, REST handleResource*, resource-manager dashboard /
|
|
11
|
-
customization),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
customization), layer-private app/atoms and app/libs vs public
|
|
12
|
+
components/utils (relative imports), attribute order/defaults, and
|
|
13
|
+
whitespace/formatting conventions for all Nuxt-generated code. Use when
|
|
14
|
+
working in or consuming nuxt-unified-ui, declaring or customizing resources,
|
|
15
|
+
or whenever generating Vue/Nuxt code that must match unified code style.
|
|
15
16
|
---
|
|
16
17
|
|
|
17
18
|
# nuxt-unified-ui
|
|
@@ -69,12 +70,13 @@ Absolute highlights:
|
|
|
69
70
|
- Kebab-case tags (`u-button`, `un-card`)
|
|
70
71
|
- `v-if` / `v-for` on `<template>` wrappers — not on rendered nodes
|
|
71
72
|
- If a condition changes several component attributes, prefer explicit `<template v-if>` / `v-else` component variants over nested ternaries and overly dynamic bindings
|
|
72
|
-
-
|
|
73
|
+
- Tags with children keep all attributes on one line with the opening tag unless a multiline attribute (multi-line array/object/function binding) forces a split — then the opening tag goes on its own line, one attribute per line, value styled like JS; childless tags are self-closing — one single-line attribute stays inline, otherwise tag and attributes each go on their own line with `/>` on its own line; attribute order + default omissions (`variant="subtle"`, **Cancel only → `ghost`**, omit neutral `color`, `loading-auto`); split non-self-closing `>` on same line as last attr; split self-closing `/>` on its own line
|
|
73
74
|
- `{{ ... }}` on its own line (static + dynamic text may mix)
|
|
74
75
|
- `/* section */` comments; imports co-located under the section that uses them
|
|
75
76
|
- Light naming: `handleXxx` handlers, `it` in short callbacks, descriptive `for...of`, computeds use block + `return`
|
|
76
77
|
- Pages: explicit `definePageMeta.name`, `/* params */` for `route.params` / `route.query`, required `/* seo */` (`useHead` + `useSeoMeta`, `useJsonld` when installed) → [pages.md](references/pages.md)
|
|
77
78
|
- Fetching: `ufetch(url, {` one line; `useUFetch` with URL on next line + `data*Data` / `is*Loading` / `refresh*` → [data-fetching.md](references/data-fetching.md)
|
|
79
|
+
- Layer modules: generate private first (`app/atoms/` components, `app/libs/` functions and similar); promote to `app/components/` / `app/utils/` only when another layer needs them; import `atoms`/`libs` with relative paths → [code-style.md](references/code-style.md)
|
|
78
80
|
|
|
79
81
|
---
|
|
80
82
|
|
|
@@ -152,6 +154,7 @@ From `nuxt.config.ts`: `@vueuse/nuxt`, `@nuxt/ui`, `@nuxtjs/i18n`; `ui.colorMode
|
|
|
152
154
|
| New page / route | [pages.md](references/pages.md) |
|
|
153
155
|
| List/detail fetch or mutation | [data-fetching.md](references/data-fetching.md) |
|
|
154
156
|
| New / custom resource | [resources.md](references/resources.md) |
|
|
157
|
+
| New component / util in a layer | Start in `app/atoms/` or `app/libs/`; relative imports; promote to `components/` / `utils/` only if another layer needs it → [code-style.md](references/code-style.md) |
|
|
155
158
|
| Formatting any of the above | [code-style.md](references/code-style.md) |
|
|
156
159
|
|
|
157
160
|
## Do / don’t
|
|
@@ -164,6 +167,7 @@ From `nuxt.config.ts`: `@vueuse/nuxt`, `@nuxt/ui`, `@nuxtjs/i18n`; `ui.colorMode
|
|
|
164
167
|
- Handle dialog actions in `onClick`
|
|
165
168
|
- Follow code style for every generated file
|
|
166
169
|
- Resources: plugin → full REST set → dashboard nav / custom `<resource-manager>` page → [resources.md](references/resources.md)
|
|
170
|
+
- Start generated layer components in `app/atoms/` and functions in `app/libs/`; import them with relative paths; move to `app/components/` / `app/utils/` only when another layer needs them
|
|
167
171
|
|
|
168
172
|
**Don’t**
|
|
169
173
|
|
|
@@ -173,3 +177,5 @@ From `nuxt.config.ts`: `@vueuse/nuxt`, `@nuxt/ui`, `@nuxtjs/i18n`; `ui.colorMode
|
|
|
173
177
|
- Set choice-button `value` unless the await result must distinguish buttons
|
|
174
178
|
- Assume color mode is enabled (layer disables it)
|
|
175
179
|
- Reimplement resource CRUD in route files (use `handleResource*`; customize via dedicated pages + domain APIs)
|
|
180
|
+
- Put a layer-private component or util in `components/` or `utils/` (becomes app-global)
|
|
181
|
+
- Import `atoms`/`libs` via `~/`, `#layers`, or from another layer — use relative paths; promote first if another layer needs them
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Nuxt unified code style
|
|
2
2
|
|
|
3
|
-
**Mandatory** whenever generating or editing code in a Nuxt project that uses this stack. Applies to **all** Nuxt project files: Vue SFCs and `.ts`/`.js` under `app
|
|
3
|
+
**Mandatory** whenever generating or editing code in a Nuxt project that uses this stack. Applies to **all** Nuxt project files: Vue SFCs and `.ts`/`.js` under `app/` (including `atoms/` and `libs/`), `server/`, composables, utils, plugins, middleware, etc.
|
|
4
4
|
|
|
5
|
-
This document is about the **look and shape** of code — whitespace, wrapping, braces, template structure, sectioning, and light naming that affects scanning — not business logic
|
|
5
|
+
This document is about the **look and shape** of code — whitespace, wrapping, braces, template structure, sectioning, file placement for layer-private vs public modules, and light naming that affects scanning — not business logic.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -19,8 +19,8 @@ Write code so a reader can **scan vertically** and see structure before details.
|
|
|
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.
|
|
21
21
|
|
|
22
|
-
4. **Templates
|
|
23
|
-
|
|
22
|
+
4. **Templates stay single-line unless a multiline attribute — or a childless multi-attribute tag — forces a split.**
|
|
23
|
+
A tag with children keeps all attributes on the same single line as the opening tag — no wrapping for length or attribute count. Only a multiline attribute (an array, object, or function literal bound to an attribute that spans multiple lines) forces such a tag to split: then the opening tag goes on its own line, every other attribute gets its own line, and the multiline value is formatted like a JS literal. Childless tags follow their own exception (self-closing; split when they carry more than one attribute or a multiline attribute). Closing `>` / `/>` placement is consistent; interpolations sit on their own line (static + dynamic text may share one `{{ ... }}` or surrounding text).
|
|
24
24
|
|
|
25
25
|
5. **Section comments are the map.**
|
|
26
26
|
`/* section */` labels define logical domains, while declaration-kind spacing exposes the structure inside each domain. Imports sit in the section that needs them, not in a hoisted pile at the top.
|
|
@@ -49,6 +49,7 @@ When editing an existing file, **absolute rules below always win**. For choices
|
|
|
49
49
|
| Braces | always for `if` / `else` / `for` / `while` — no brace-less single-liners |
|
|
50
50
|
| `else` / `catch` | on their **own line** after `}` |
|
|
51
51
|
| `.js` / `.ts` file start | **two** blank lines at the top, **unless** the file starts with imports — then **no** blank lines before the first `import` |
|
|
52
|
+
| **Single-line principle (JS/TS only)** | **Do not wrap JS/TS code solely because it has grown long**; keep every statement on a single line unless the explicit JS/TS wrapping rules below dictate otherwise. The default is single-statement-per-line; only break across lines when a specific rule permits or requires it. Template sections have their own rule (single-line attributes unless a multiline attribute forces a split) — see Template rules. |
|
|
52
53
|
|
|
53
54
|
---
|
|
54
55
|
|
|
@@ -296,9 +297,9 @@ const response = await ufetch(
|
|
|
296
297
|
|
|
297
298
|
### Template bindings — compactness
|
|
298
299
|
|
|
299
|
-
-
|
|
300
|
-
- Multi-key template object/array bindings are
|
|
301
|
-
- Simple scalars and simple ternaries stay inline; break only when
|
|
300
|
+
- A bound array, object, or function literal that spans multiple lines is a **multiline attribute** and forces the tag to split (see Attribute wrapping). A single-pair object with scalar values stays inline and does **not** trigger a split: `:ui="{ content: 'max-w-7xl' }"`
|
|
301
|
+
- Multi-key or nested template object/array bindings are multiline attributes: split the tag and format the value like a JS literal
|
|
302
|
+
- Simple scalars and simple ternaries stay inline; break only when a branch becomes a multiline object/array or nested structure
|
|
302
303
|
- When one condition changes several attributes, labels/icons, or an object-shaped binding such as `to`, use adjacent `<template v-if>` / `v-else-if` / `v-else` branches with explicit component variants. Prefer small markup duplication over nested ternaries and overly dynamic attributes.
|
|
303
304
|
|
|
304
305
|
```vue
|
|
@@ -372,7 +373,7 @@ The comment names a logical domain, not a declaration kind. Follow it with a bla
|
|
|
372
373
|
```ts
|
|
373
374
|
/* resource */
|
|
374
375
|
|
|
375
|
-
import ResourceExplorerCell from '
|
|
376
|
+
import ResourceExplorerCell from '../atoms/resource-explorer-cell.vue';
|
|
376
377
|
|
|
377
378
|
|
|
378
379
|
const itemsPerPage = ref(20);
|
|
@@ -431,6 +432,39 @@ Place non-auto-imported imports **inside the section that uses them**, not hoist
|
|
|
431
432
|
import { VisXYContainer, VisLine } from '@unovis/vue';
|
|
432
433
|
```
|
|
433
434
|
|
|
435
|
+
### Layer-private `atoms` / `libs` vs public `components` / `utils`
|
|
436
|
+
|
|
437
|
+
Nuxt auto-imports `app/components/` and `app/utils/` across the **entire** app (every layer). Files that must stay inside one layer do not belong there.
|
|
438
|
+
|
|
439
|
+
Any component or util in a layer which is not supposed to be used from another layer should be put in (`atoms` for components) and (`libs` for functions and ...), and if we notice that one of these is needed in another layer, we move it to `components` or `utils` folder. A generated component or util starts in these private folders, then moves into public ones if needed. These should be imported with relative path in their callsites.
|
|
440
|
+
|
|
441
|
+
| Role | Directory | Visibility |
|
|
442
|
+
|------|-----------|------------|
|
|
443
|
+
| Private Vue component | `app/atoms/` | This layer only |
|
|
444
|
+
| Private function / helper / similar | `app/libs/` | This layer only |
|
|
445
|
+
| Public Vue component | `app/components/` | Whole app (auto-imported) |
|
|
446
|
+
| Public util | `app/utils/` | Whole app (auto-imported) |
|
|
447
|
+
|
|
448
|
+
`app/composables/` is public like `utils/`. Private composables and other non-component helpers go in `app/libs/`.
|
|
449
|
+
|
|
450
|
+
**Generate private first.** New components go in `app/atoms/`. New functions and similar go in `app/libs/`. Promote (move the file, update call sites) to `app/components/` or `app/utils/` only when another layer needs them. After a promote, drop the relative import — public modules are auto-imported.
|
|
451
|
+
|
|
452
|
+
**Import `atoms` / `libs` with relative paths only** (`../atoms/foo.vue`, `../libs/bar`). Never `~/`, `@/`, `#layers/`, or other aliases. Never import another layer's `atoms` or `libs`; promote first, then use the public auto-import.
|
|
453
|
+
|
|
454
|
+
Do not register `atoms` or `libs` with Nuxt `components` / `imports` config.
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
// ✅ same layer
|
|
458
|
+
import ResourceExplorerCell from '../atoms/resource-explorer-cell.vue';
|
|
459
|
+
import { formatColumn } from '../libs/format-column';
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
// ❌ aliases / other layers
|
|
464
|
+
import ResourceExplorerCell from '~/atoms/resource-explorer-cell.vue';
|
|
465
|
+
import { formatColumn } from '#layers/other-layer/libs/format-column';
|
|
466
|
+
```
|
|
467
|
+
|
|
434
468
|
### Watcher formatting
|
|
435
469
|
|
|
436
470
|
- Prefer `watchImmediate` over `watch(..., { immediate: true })`
|
|
@@ -475,40 +509,69 @@ Keep tight `v-if` / `v-else` chains adjacent (no blank line between matching bra
|
|
|
475
509
|
|
|
476
510
|
### Attribute wrapping (hard rule)
|
|
477
511
|
|
|
478
|
-
- **
|
|
479
|
-
- **
|
|
480
|
-
- **
|
|
481
|
-
-
|
|
512
|
+
- **Childless tags must be self-closing:** a tag with no children is written `<tag ... />` — never an empty open/close pair (`<tag ...></tag>`).
|
|
513
|
+
- **Tags with children — single line unless multiline:** all attributes stay on the **same single line** as the opening tag, regardless of attribute count or line length. Never wrap only because the line got long or because there are 2+ attributes. This applies equally to rendered elements, components, `u-modal`, and structural `<template>` wrappers.
|
|
514
|
+
- **Only trigger for tags with children — multiline attribute:** such a tag splits only when one of its attributes is a **multiline attribute**: an array, object, or function literal bound to the attribute that spans multiple lines (e.g. `:class="{\n … \n}"`, `:items="[\n … \n]"`). References, calls, and scalar expressions (`:field="field"`, `v-bind="radOmit(action, [...])"`, `:is="elementsMap[x]"`, `@click="handleSave"`) are **not** multiline attributes, even when their runtime value is an object. A single-pair object with scalar values (`:ui="{ content: 'max-w-5xl' }"`) stays inline and does **not** trigger a split.
|
|
515
|
+
- **Childless exception:** a childless (self-closing) tag with a single single-line attribute stays on one line with the tag (`<u-icon name="lucide:check" />`). When it has more than one attribute **or** at least one multiline attribute, the tag and its attributes each go on their own line: the opening tag on its own line (`<tag`), one attribute per line in attribute order, and `/>` on its own line.
|
|
516
|
+
- **Split shape (tags with children):** when a multiline attribute triggers the split, the opening tag goes on its **own line** (`<tag`), every other attribute gets **its own line** in attribute order, and the multiline attribute's value is formatted **like a JS value** (one entry per line, trailing commas). Closing `>` / `/>` placement follows [`>` and `/>` placement](#-and--placement).
|
|
482
517
|
|
|
483
518
|
```vue
|
|
484
|
-
<!-- ✅
|
|
485
|
-
<
|
|
486
|
-
<u-form-field label="Captcha">
|
|
519
|
+
<!-- ✅ tag with children, no multiline attribute — one line, however many attributes -->
|
|
520
|
+
<u-modal :ui="{ content: 'max-w-5xl' }" scrollable @update:open="!$event && emit('close')">
|
|
487
521
|
...
|
|
488
|
-
</u-
|
|
489
|
-
<u-icon name="lucide:check" />
|
|
522
|
+
</u-modal>
|
|
490
523
|
|
|
491
|
-
<!-- ✅ structural template wrappers
|
|
524
|
+
<!-- ✅ structural template wrappers follow the same rule -->
|
|
492
525
|
<template v-for="column in columns" :key="column.accessorKey" #[column.accessorKey+'-cell']="{ row }">
|
|
493
526
|
...
|
|
494
527
|
</template>
|
|
495
528
|
|
|
496
|
-
<!-- ✅
|
|
497
|
-
<
|
|
529
|
+
<!-- ✅ tag with children + multiline attribute — splits, value styled like JS -->
|
|
530
|
+
<div
|
|
531
|
+
v-if="show"
|
|
532
|
+
:class="{
|
|
533
|
+
'p-3': !fluidBody,
|
|
534
|
+
}">
|
|
498
535
|
...
|
|
499
|
-
</
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<!-- ✅ childless, single single-line attribute — one line -->
|
|
539
|
+
<u-icon name="lucide:check" />
|
|
500
540
|
|
|
501
|
-
<!-- ✅
|
|
541
|
+
<!-- ✅ childless, more than one attribute — tag and attributes each on their own line -->
|
|
502
542
|
<u-button
|
|
503
543
|
variant="subtle"
|
|
504
544
|
icon="lucide:refresh-ccw"
|
|
505
545
|
@click="refresh"
|
|
506
546
|
/>
|
|
547
|
+
|
|
548
|
+
<!-- ✅ childless with a multiline attribute — same split shape -->
|
|
549
|
+
<component
|
|
550
|
+
:is="elementsMap[field.identifier]"
|
|
551
|
+
:field="field"
|
|
552
|
+
:class="{
|
|
553
|
+
'p-2': true,
|
|
554
|
+
}"
|
|
555
|
+
/>
|
|
556
|
+
|
|
557
|
+
<!-- ❌ childless multi-attribute tag on one line -->
|
|
558
|
+
<u-button variant="subtle" icon="lucide:refresh-ccw" @click="refresh" />
|
|
559
|
+
|
|
560
|
+
<!-- ❌ empty open/close pair — self-close instead -->
|
|
561
|
+
<div class="grow"></div>
|
|
562
|
+
|
|
563
|
+
<!-- ❌ splitting a tag with children that has no multiline attribute -->
|
|
564
|
+
<un-card
|
|
565
|
+
icon="lucide:key"
|
|
566
|
+
:title="title"
|
|
567
|
+
fluid-body>
|
|
568
|
+
...
|
|
569
|
+
</un-card>
|
|
507
570
|
```
|
|
508
571
|
|
|
509
572
|
### Attribute order
|
|
510
573
|
|
|
511
|
-
When
|
|
574
|
+
When a multiline attribute forces a split, order attributes as:
|
|
512
575
|
|
|
513
576
|
1. Refs / identity: `ref`, `id`, `name`
|
|
514
577
|
2. Component visual props: `variant`, `color`, `size`, `icon`, static `label`
|
|
@@ -573,27 +636,30 @@ Prefer defaults by **omitting** props rather than restating them:
|
|
|
573
636
|
```
|
|
574
637
|
### `>` and `/>` placement
|
|
575
638
|
|
|
576
|
-
|
|
639
|
+
Single-line tags keep their closer on the same line (`<u-icon name="lucide:check" />`, `<div class="space-y-3">`).
|
|
640
|
+
|
|
641
|
+
**Split (multiline-attribute) non-self-closing:** `>` on the **same line** as the last attribute:
|
|
577
642
|
|
|
578
643
|
```vue
|
|
579
|
-
<
|
|
580
|
-
|
|
581
|
-
:
|
|
582
|
-
|
|
644
|
+
<div
|
|
645
|
+
v-if="show"
|
|
646
|
+
:class="{
|
|
647
|
+
'p-3': !fluidBody,
|
|
648
|
+
}">
|
|
583
649
|
...
|
|
584
|
-
</
|
|
650
|
+
</div>
|
|
585
651
|
```
|
|
586
652
|
|
|
587
|
-
**
|
|
653
|
+
**Split self-closing (childless multi-attribute or multiline attribute):** `/>` on its **own line**; always a space before `/>`:
|
|
588
654
|
|
|
589
655
|
```vue
|
|
590
|
-
<
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
656
|
+
<component
|
|
657
|
+
:is="elementsMap[field.identifier]"
|
|
658
|
+
:field="field"
|
|
659
|
+
:class="{
|
|
660
|
+
'p-2': true,
|
|
661
|
+
}"
|
|
594
662
|
/>
|
|
595
|
-
|
|
596
|
-
<u-icon name="lucide:check" />
|
|
597
663
|
```
|
|
598
664
|
|
|
599
665
|
Closing tags for block components (`</un-card>`, `</u-modal>`, …) always on their own line.
|
|
@@ -766,8 +832,10 @@ export default defineEventHandler(async event => {
|
|
|
766
832
|
| `if (!x) return;` | braced block |
|
|
767
833
|
| `} else {` | `}\nelse {` |
|
|
768
834
|
| `toastSuccess({ title: 'x' })` one-liner object | multi-line object + trailing comma |
|
|
769
|
-
|
|
|
770
|
-
|
|
|
835
|
+
| Splitting a tag with children that has no multiline attribute (wrapping for count/length) | keep all attributes on one line with the opening tag |
|
|
836
|
+
| Childless multi-attribute tag on one line | split: opening tag on its own line, one attribute per line, `/>` on its own line |
|
|
837
|
+
| Empty open/close pair (`<div ...></div>`) | self-close (`<div ... />`) |
|
|
838
|
+
| Keeping a tag single-line when it has a multiline attribute | split: opening tag on its own line, one attribute per line, value like JS |
|
|
771
839
|
| `>` on its own line after attrs | `>` after last attr |
|
|
772
840
|
| `{{ x }}` glued to tags | interpolation on its own line (static + dynamic mix OK) |
|
|
773
841
|
| `ghost` on non-Cancel buttons | `ghost` only for Cancel |
|
|
@@ -781,6 +849,10 @@ export default defineEventHandler(async event => {
|
|
|
781
849
|
| `.ts`/`.js` with no leading blanks (and no imports) | two blank lines at file start |
|
|
782
850
|
| Blank lines before first `import` | `import` on line 1 |
|
|
783
851
|
| Blank lines around a function that is only one `for` / `if` / `try` chain | Function `{` / `}` flush against that block |
|
|
852
|
+
| Layer-private component in `components/` | `app/atoms/` |
|
|
853
|
+
| Layer-private util in `utils/` or `composables/` | `app/libs/` |
|
|
854
|
+
| `~/atoms/...` / `#layers/.../atoms` / `#layers/.../libs` | Relative `../atoms/...` / `../libs/...` |
|
|
855
|
+
| Import another layer's `atoms` or `libs` | Promote to `components/` / `utils/`, then auto-import |
|
|
784
856
|
|
|
785
857
|
---
|
|
786
858
|
|
|
@@ -796,11 +868,11 @@ export default defineEventHandler(async event => {
|
|
|
796
868
|
- [ ] Tiny helpers stay tight
|
|
797
869
|
- [ ] Return-only multi-criteria functions use a compact exhaustive `if` / `else if` / `else`; broader functions may use early returns
|
|
798
870
|
- [ ] `else` / `catch` on new line
|
|
799
|
-
- [ ] Script objects multi-line; template single-
|
|
871
|
+
- [ ] Script objects multi-line (the JS/TS single-line principle does not cover templates); template tags stay single-line unless a multiline attribute (multi-line array/object/function binding) forces a split; single-pair scalar objects (e.g. `:ui="{ content: '...' }"`) stay inline
|
|
800
872
|
- [ ] Kebab-case component tags
|
|
801
873
|
- [ ] `v-if` / `v-for` on `<template>` wrappers
|
|
802
874
|
- [ ] Conditional states that change several attributes use explicit `<template v-if>` component variants, not nested ternaries
|
|
803
|
-
- [ ]
|
|
875
|
+
- [ ] Tags with children keep all attributes on one line with the opening tag unless a multiline attribute forces a split (opening tag on its own line, one attribute per line, value like JS); childless tags are self-closing — single single-line attribute stays inline, otherwise tag and attributes each go on their own line with `/>` on its own line; closing `>` / `/>` placement is correct
|
|
804
876
|
- [ ] Attribute order + default-value omissions respected (`subtle`, **Cancel → `ghost` only**, no neutral color noise, `loading-auto`)
|
|
805
877
|
- [ ] `{{ }}` on own line (static + dynamic mix OK)
|
|
806
878
|
- [ ] Every section is named; imports are co-located with the section that uses them
|
|
@@ -808,3 +880,4 @@ export default defineEventHandler(async event => {
|
|
|
808
880
|
- [ ] Computeds that return structures use block + `return`
|
|
809
881
|
- [ ] Pages: explicit `definePageMeta.name`, `/* params */` + `/* seo */` placement, named navigation ([pages.md](pages.md))
|
|
810
882
|
- [ ] Fetching: `ufetch` / `useUFetch` wrap styles and destructure names ([data-fetching.md](data-fetching.md))
|
|
883
|
+
- [ ] New layer components/utils start in `app/atoms/` or `app/libs/`; `atoms`/`libs` imported with relative paths only; promote to `components/` / `utils/` only when another layer needs them
|
|
@@ -156,7 +156,7 @@ Action entries support `actionType: 'spacer'` (flex grow) and optional `tooltip`
|
|
|
156
156
|
|
|
157
157
|
Cancel / dismiss actions use `variant: 'ghost'` (or `variant="ghost"`). Do **not** use `ghost` on primary/submit/other actions — prefer omit / `subtle`.
|
|
158
158
|
|
|
159
|
-
Hand-rolled dialog shells
|
|
159
|
+
Hand-rolled dialog shells follow the template single-line rule — this shell has no multiline attribute, so it stays on one line (see [code-style.md](code-style.md)):
|
|
160
160
|
|
|
161
161
|
```vue
|
|
162
162
|
<u-modal :ui="{ content: 'max-w-5xl' }" scrollable @update:open="!$event && emit('close')">
|
|
@@ -169,7 +169,7 @@ Hand-rolled dialog shells keep **`u-modal` attributes on one line** (see [code-s
|
|
|
169
169
|
- Prefer `launchFormPickerDialog` / `launchChoicePickerDialog` over hand-rolled `u-modal` for these flows
|
|
170
170
|
- Handle actions in button / submit `onClick`
|
|
171
171
|
- Keep field lists consistent with `un-form` (`identifier`, not `type`, for element kind)
|
|
172
|
-
- Mark Cancel with `variant: 'ghost'`; keep
|
|
172
|
+
- Mark Cancel with `variant: 'ghost'`; keep dialog shells single-line unless a multiline attribute forces a split
|
|
173
173
|
|
|
174
174
|
**Don’t**
|
|
175
175
|
|
|
@@ -57,7 +57,7 @@ Omit unused props. When present, write them in this order:
|
|
|
57
57
|
13. `:extra-actions`
|
|
58
58
|
14. `:meta`
|
|
59
59
|
|
|
60
|
-
Models are always **page size, then page**.
|
|
60
|
+
Models are always **page size, then page**. Attributes stay on one line with the opening tag (a multiline attribute is the only split trigger — see [code-style.md](code-style.md)).
|
|
61
61
|
|
|
62
62
|
### Paged table
|
|
63
63
|
|
|
@@ -84,10 +84,7 @@ Models are always **page size, then page**. Closing `>` sits on the same line as
|
|
|
84
84
|
No footer, no page models, no `total-items`:
|
|
85
85
|
|
|
86
86
|
```vue
|
|
87
|
-
<un-card
|
|
88
|
-
:icon="icon"
|
|
89
|
-
:title="title"
|
|
90
|
-
fluid-body>
|
|
87
|
+
<un-card :icon="icon" :title="title" fluid-body>
|
|
91
88
|
<un-table
|
|
92
89
|
:columns="columns"
|
|
93
90
|
:data="rows"
|
|
@@ -298,10 +295,7 @@ const currentPage = ref(1);
|
|
|
298
295
|
Prefer an `un-card` with `fluid-body` so the table and footer are edge-to-edge:
|
|
299
296
|
|
|
300
297
|
```vue
|
|
301
|
-
<un-card
|
|
302
|
-
:title="`Manage ${titlePlural}`"
|
|
303
|
-
fluid-body
|
|
304
|
-
:append-actions="toolbarActions">
|
|
298
|
+
<un-card :title="`Manage ${titlePlural}`" fluid-body :append-actions="toolbarActions">
|
|
305
299
|
<un-table
|
|
306
300
|
:columns="columns"
|
|
307
301
|
:loading="isItemsLoading"
|
|
@@ -60,7 +60,10 @@ Action buttons: `loading-auto`, props via `radOmit` excluding `actionType` / `to
|
|
|
60
60
|
## `un-spinner` — `app/components/un-spinner.vue`
|
|
61
61
|
|
|
62
62
|
```vue
|
|
63
|
-
<u-icon
|
|
63
|
+
<u-icon
|
|
64
|
+
name="lucide:loader-circle"
|
|
65
|
+
class="animate-spin"
|
|
66
|
+
/>
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
No props.
|