@frigear-nu/ui 0.0.8 → 0.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/README.md +46 -2
- package/dist/module.mjs +12 -9
- package/dist/runtime/components/ConfirmDialog.d.vue.ts +1 -0
- package/dist/runtime/components/ConfirmDialog.vue +4 -2
- package/dist/runtime/components/ConfirmDialog.vue.d.ts +1 -0
- package/dist/runtime/components/global/DescriptionList.d.vue.ts +28 -0
- package/dist/runtime/components/global/DescriptionList.vue +51 -0
- package/dist/runtime/components/global/DescriptionList.vue.d.ts +28 -0
- package/dist/runtime/components/global/FormSlideover.d.vue.ts +46 -0
- package/dist/runtime/components/global/FormSlideover.vue +53 -0
- package/dist/runtime/components/global/FormSlideover.vue.d.ts +46 -0
- package/dist/runtime/components/global/ListCard.d.vue.ts +29 -0
- package/dist/runtime/components/global/ListCard.vue +74 -0
- package/dist/runtime/components/global/ListCard.vue.d.ts +29 -0
- package/dist/runtime/components/global/PageHeading.d.vue.ts +28 -0
- package/dist/runtime/components/global/PageHeading.vue +53 -0
- package/dist/runtime/components/global/PageHeading.vue.d.ts +28 -0
- package/dist/runtime/components/global/PageTabs.d.vue.ts +19 -0
- package/dist/runtime/components/global/PageTabs.vue +28 -0
- package/dist/runtime/components/global/PageTabs.vue.d.ts +19 -0
- package/dist/runtime/components/global/PageToolbar.d.vue.ts +15 -0
- package/dist/runtime/components/global/PageToolbar.vue +15 -0
- package/dist/runtime/components/global/PageToolbar.vue.d.ts +15 -0
- package/dist/runtime/components/global/RowMenu.d.vue.ts +19 -0
- package/dist/runtime/components/global/RowMenu.vue +31 -0
- package/dist/runtime/components/global/RowMenu.vue.d.ts +19 -0
- package/dist/runtime/components/global/SectionCard.d.vue.ts +31 -0
- package/dist/runtime/components/global/SectionCard.vue +68 -0
- package/dist/runtime/components/global/SectionCard.vue.d.ts +31 -0
- package/dist/runtime/components/global/StatCard.d.vue.ts +26 -0
- package/dist/runtime/components/global/StatCard.vue +62 -0
- package/dist/runtime/components/global/StatCard.vue.d.ts +26 -0
- package/dist/runtime/components/global/TableCard.d.vue.ts +31 -0
- package/dist/runtime/components/global/TableCard.vue +46 -0
- package/dist/runtime/components/global/TableCard.vue.d.ts +31 -0
- package/dist/runtime/components/global/TableIdentity.d.vue.ts +23 -0
- package/dist/runtime/components/global/TableIdentity.vue +28 -0
- package/dist/runtime/components/global/TableIdentity.vue.d.ts +23 -0
- package/dist/runtime/components/global/TablePagination.d.vue.ts +36 -0
- package/dist/runtime/components/global/TablePagination.vue +51 -0
- package/dist/runtime/components/global/TablePagination.vue.d.ts +36 -0
- package/dist/runtime/composables/index.d.ts +8 -0
- package/dist/runtime/composables/index.js +8 -0
- package/dist/runtime/composables/useAction.d.ts +14 -0
- package/dist/runtime/composables/useAction.js +20 -0
- package/dist/runtime/composables/useCopyToClipboard.d.ts +4 -0
- package/dist/runtime/composables/useCopyToClipboard.js +20 -0
- package/dist/runtime/composables/useErrorToast.d.ts +9 -0
- package/dist/runtime/composables/useErrorToast.js +9 -0
- package/dist/runtime/composables/useFormat.d.ts +23 -0
- package/dist/runtime/composables/useFormat.js +19 -0
- package/dist/runtime/composables/useLocaleLink.d.ts +13 -0
- package/dist/runtime/composables/useLocaleLink.js +19 -0
- package/dist/runtime/composables/useNavLinks.d.ts +13 -0
- package/dist/runtime/composables/useNavLinks.js +15 -0
- package/dist/runtime/composables/useTableQuery.d.ts +34 -0
- package/dist/runtime/composables/useTableQuery.js +70 -0
- package/dist/runtime/composables/useTableSort.d.ts +21 -0
- package/dist/runtime/composables/useTableSort.js +40 -0
- package/dist/runtime/i18n/lang/da.json +19 -0
- package/dist/runtime/i18n/lang/en.json +19 -0
- package/dist/runtime/types.d.ts +34 -0
- package/dist/runtime/types.js +0 -0
- package/dist/runtime/utils/errors.d.ts +9 -0
- package/dist/runtime/utils/errors.js +18 -0
- package/dist/runtime/utils/format.d.ts +12 -0
- package/dist/runtime/utils/format.js +26 -0
- package/dist/runtime/utils/index.d.ts +6 -0
- package/dist/runtime/utils/index.js +6 -0
- package/dist/runtime/utils/locale.d.ts +8 -0
- package/dist/runtime/utils/locale.js +10 -0
- package/dist/runtime/utils/status.d.ts +7 -0
- package/dist/runtime/utils/status.js +17 -0
- package/dist/runtime/utils/surface.d.ts +22 -0
- package/dist/runtime/utils/surface.js +10 -0
- package/dist/runtime/utils/table.d.ts +28 -0
- package/dist/runtime/utils/table.js +17 -0
- package/package.json +28 -14
- package/dist/runtime/components/App/AppPageHeader.d.vue.ts +0 -23
- package/dist/runtime/components/App/AppPageHeader.vue +0 -58
- package/dist/runtime/components/App/AppPageHeader.vue.d.ts +0 -23
package/README.md
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
# @frigear-nu/ui
|
|
2
2
|
|
|
3
|
-
Shared UI components for Frigear Nuxt applications.
|
|
3
|
+
Shared UI components, composables and helpers for Frigear Nuxt applications, built on Nuxt UI. Registers its
|
|
4
|
+
components globally (no prefix) and auto-imports its composables and helpers. It brings patterns, not a theme: every
|
|
5
|
+
card uses the same `bg-elevated/50 ring ring-default rounded-lg` surface and the host's Nuxt UI colours.
|
|
4
6
|
|
|
7
|
+
Built-in strings come from `@nuxtjs/i18n` when installed (`patterns.*`, `actions.*`; `en` and `da` included), with
|
|
8
|
+
English fallbacks; every one of them can be overridden by a prop.
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
## Page patterns
|
|
11
|
+
|
|
12
|
+
| Component | Use |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `PageHeading` | Title block of an app page: breadcrumb (array) or eyebrow line (string), `#badge`, description, meta row, `#actions` |
|
|
15
|
+
| `PageTabs` | Tabs under a heading: links (`to`, route decides) or state (`v-model` + `value`) |
|
|
16
|
+
| `SectionCard` | A titled card for one section (form, key facts); `flush` for tables and lists |
|
|
17
|
+
| `TableCard` | The surface a `UTable` sits in (`:ui="tableUi()"`), with `#header` and `#footer` (total, pagination) |
|
|
18
|
+
| `RowMenu` | The `⋯` menu of a table row or heading; order Open → edits → destructive group |
|
|
19
|
+
| `StatCard` | A number with a label, tinted icon and hint; links to its list with `to` |
|
|
20
|
+
| `ListCard` | A short divided list with a "View all" link |
|
|
21
|
+
| `DescriptionList` | Label/value rows; empty values read "Not set"; slot per `key` |
|
|
22
|
+
| `PageToolbar` | Search and filters above a table, `#right` for counts |
|
|
23
|
+
| `FormSlideover` | Create/edit form in a slideover with Cancel + submit (`form="<formId>"`); works with `useOverlay` |
|
|
24
|
+
| `ConfirmDialog` / `useConfirmDialog()` | Confirmation before destructive actions |
|
|
25
|
+
|
|
26
|
+
| Composable | Use |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `useAction()` | `{ busy, run(fn, { success?, error? }) }`: toasts success, toasts and swallows errors |
|
|
29
|
+
| `useErrorToast()` | `(e, title?) => void`: an error as a red toast |
|
|
30
|
+
| `useCopyToClipboard()` | `{ copy(text, message?) }` with a "Copied" toast |
|
|
31
|
+
| `useFormat()` | `{ date, dateTime, time, dateRange, money }` in the active locale and the app time zone (`appConfig.frigearUi.timeZone`, e.g. `Europe/Copenhagen`), so server and client render the same text |
|
|
32
|
+
| `useLocaleLink()` | `{ localize, strip, isActive }`: locale-prefix aware paths (identity without i18n) |
|
|
33
|
+
| `useNavLinks(items, { exact })` | Menu items to `UNavigationMenu` links with localised `to` and `active` |
|
|
34
|
+
|
|
35
|
+
| Helper (`@frigear-nu/ui/utils`) | Use |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `errorMessage(e, fallback)`, `errorToToast(e, overrides?)` | The useful message of a `$fetch`/`createError` error |
|
|
38
|
+
| `formatDate`, `formatDateTime`, `formatTime`, `formatDateRange` | `Intl` date formatting (`value, locale?, options?`) |
|
|
39
|
+
| `statusColor(status, overrides?)` | Badge colour of a status (published/active/paid → success, pending → warning, failed → error) |
|
|
40
|
+
| `stripLocale(path, codes)` | A path without its `/<locale>` prefix |
|
|
41
|
+
| `surfaceClass`, `tableUi(overrides?)` | The card surface and a `UTable` `:ui` for `TableCard` |
|
|
42
|
+
|
|
43
|
+
Types (`DescriptionItem`, `PageTabItem`, `StatusColor`, …) are exported from `@frigear-nu/ui/types`.
|
|
44
|
+
|
|
45
|
+
## Inputs
|
|
46
|
+
|
|
47
|
+
`CurrencyInput`, `MoneyInput`, `DateInput`, `IntervalInput`, `TagInput`, `UrlInput`, `IconInput`, `SwitchToggle`,
|
|
48
|
+
`MarkdownTextarea`, `TranslatedInput`; `MonacoCodeEditor` only when the host installs `nuxt-monaco-editor`.
|
|
49
|
+
|
|
50
|
+
See the [documentation](../../apps/docs/content/4.modules/17.ui.md) and the [playground](./playground).
|
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createResolver, defineNuxtModule, addComponentsDir, addImportsDir } from '@nuxt/kit';
|
|
1
|
+
import { createResolver, defineNuxtModule, addComponentsDir, hasNuxtModule, addImportsDir } from '@nuxt/kit';
|
|
2
|
+
import { registerTailwindSource } from '@frigear-nu/module-kit/utils';
|
|
2
3
|
|
|
3
4
|
const defaultI18nConfig = (resolver) => {
|
|
4
5
|
const langDir = resolver.resolve("./runtime/i18n/lang");
|
|
@@ -23,23 +24,25 @@ const module$1 = defineNuxtModule({
|
|
|
23
24
|
},
|
|
24
25
|
moduleDependencies: {
|
|
25
26
|
"@nuxt/ui": {},
|
|
26
|
-
"
|
|
27
|
+
"@frigear-nu/module-kit": {},
|
|
28
|
+
// Only needed for `MonacoCodeEditor`, which is registered when the host installs `nuxt-monaco-editor` itself.
|
|
29
|
+
"nuxt-monaco-editor": {
|
|
30
|
+
optional: true
|
|
31
|
+
},
|
|
27
32
|
"@nuxtjs/i18n": {
|
|
28
33
|
optional: true
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
|
-
setup(_options,
|
|
32
|
-
|
|
33
|
-
path: resolver.resolve("./runtime/components/global"),
|
|
34
|
-
global: true,
|
|
35
|
-
prefix: ""
|
|
36
|
-
});
|
|
36
|
+
setup(_options, nuxt) {
|
|
37
|
+
registerTailwindSource(nuxt, resolver.resolve("./runtime"));
|
|
37
38
|
addComponentsDir({
|
|
38
39
|
path: resolver.resolve("./runtime/components"),
|
|
39
40
|
prefix: "",
|
|
40
|
-
|
|
41
|
+
pathPrefix: false,
|
|
42
|
+
ignore: hasNuxtModule("nuxt-monaco-editor", nuxt) ? [] : ["**/MonacoCodeEditor.vue"]
|
|
41
43
|
});
|
|
42
44
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
45
|
+
addImportsDir(resolver.resolve("./runtime/utils"));
|
|
43
46
|
},
|
|
44
47
|
hooks: {
|
|
45
48
|
"i18n:registerModule": (register) => {
|
|
@@ -4,6 +4,7 @@ interface ConfirmDialogProps {
|
|
|
4
4
|
dismissible?: boolean;
|
|
5
5
|
confirmText?: string;
|
|
6
6
|
cancelText?: string;
|
|
7
|
+
/** Styles the confirm button as destructive (red). */
|
|
7
8
|
destructive?: boolean;
|
|
8
9
|
}
|
|
9
10
|
declare const __VLS_export: import("vue").DefineComponent<ConfirmDialogProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
@@ -8,6 +8,8 @@ defineProps({
|
|
|
8
8
|
destructive: { type: Boolean, required: false }
|
|
9
9
|
});
|
|
10
10
|
const emits = defineEmits(["close"]);
|
|
11
|
+
const t = useUiT("patterns");
|
|
12
|
+
const actions = useUiT("actions");
|
|
11
13
|
</script>
|
|
12
14
|
|
|
13
15
|
<template>
|
|
@@ -19,13 +21,13 @@ const emits = defineEmits(["close"]);
|
|
|
19
21
|
>
|
|
20
22
|
<template #footer>
|
|
21
23
|
<UButton
|
|
22
|
-
:label="cancelText || 'Cancel'"
|
|
24
|
+
:label="cancelText || actions('cancel', 'Cancel')"
|
|
23
25
|
color="neutral"
|
|
24
26
|
variant="outline"
|
|
25
27
|
@click="emits('close', false)"
|
|
26
28
|
/>
|
|
27
29
|
<UButton
|
|
28
|
-
:label="confirmText || 'Confirm'"
|
|
30
|
+
:label="confirmText || t('confirm', 'Confirm')"
|
|
29
31
|
:color="destructive ? 'error' : 'neutral'"
|
|
30
32
|
@click="emits('close', true)"
|
|
31
33
|
/>
|
|
@@ -4,6 +4,7 @@ interface ConfirmDialogProps {
|
|
|
4
4
|
dismissible?: boolean;
|
|
5
5
|
confirmText?: string;
|
|
6
6
|
cancelText?: string;
|
|
7
|
+
/** Styles the confirm button as destructive (red). */
|
|
7
8
|
destructive?: boolean;
|
|
8
9
|
}
|
|
9
10
|
declare const __VLS_export: import("vue").DefineComponent<ConfirmDialogProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DescriptionItem } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Label/value rows for a detail card. Each row renders the slot named after its `key` (or `label`) when given,
|
|
4
|
+
* otherwise `value`; empty values read `empty` ("Not set") in muted text. `align="end"` right-aligns the values.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
items: DescriptionItem[];
|
|
8
|
+
empty?: string;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
align?: 'start' | 'end';
|
|
11
|
+
};
|
|
12
|
+
declare var __VLS_7: string, __VLS_8: {
|
|
13
|
+
item: DescriptionItem;
|
|
14
|
+
};
|
|
15
|
+
type __VLS_Slots = {} & {
|
|
16
|
+
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
19
|
+
align: "start" | "end";
|
|
20
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
items: { type: Array, required: true },
|
|
4
|
+
empty: { type: String, required: false },
|
|
5
|
+
loading: { type: Boolean, required: false },
|
|
6
|
+
align: { type: String, required: false, default: "start" }
|
|
7
|
+
});
|
|
8
|
+
const t = useUiT("patterns");
|
|
9
|
+
const emptyLabel = computed(() => props.empty ?? t("notSet", "Not set"));
|
|
10
|
+
const isEmpty = (value) => value === void 0 || value === null || value === "";
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<dl
|
|
15
|
+
class="grid gap-x-6 gap-y-3 text-sm"
|
|
16
|
+
:class="align === 'end' ? 'grid-cols-[auto_1fr] items-center' : 'sm:grid-cols-[10rem_1fr]'"
|
|
17
|
+
>
|
|
18
|
+
<template
|
|
19
|
+
v-for="item in items"
|
|
20
|
+
:key="item.key ?? item.label"
|
|
21
|
+
>
|
|
22
|
+
<dt class="text-muted">
|
|
23
|
+
{{ item.label }}
|
|
24
|
+
</dt>
|
|
25
|
+
<dd
|
|
26
|
+
class="text-highlighted min-w-0 wrap-break-word"
|
|
27
|
+
:class="align === 'end' && 'text-right'"
|
|
28
|
+
>
|
|
29
|
+
<USkeleton
|
|
30
|
+
v-if="loading"
|
|
31
|
+
class="h-4 w-24"
|
|
32
|
+
:class="align === 'end' && 'ml-auto'"
|
|
33
|
+
/>
|
|
34
|
+
<slot
|
|
35
|
+
v-else
|
|
36
|
+
:name="item.key ?? item.label"
|
|
37
|
+
:item="item"
|
|
38
|
+
>
|
|
39
|
+
<span
|
|
40
|
+
v-if="isEmpty(item.value)"
|
|
41
|
+
class="text-dimmed"
|
|
42
|
+
>{{ emptyLabel }}</span>
|
|
43
|
+
<span
|
|
44
|
+
v-else
|
|
45
|
+
class="whitespace-pre-line"
|
|
46
|
+
>{{ item.value }}</span>
|
|
47
|
+
</slot>
|
|
48
|
+
</dd>
|
|
49
|
+
</template>
|
|
50
|
+
</dl>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DescriptionItem } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Label/value rows for a detail card. Each row renders the slot named after its `key` (or `label`) when given,
|
|
4
|
+
* otherwise `value`; empty values read `empty` ("Not set") in muted text. `align="end"` right-aligns the values.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
items: DescriptionItem[];
|
|
8
|
+
empty?: string;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
align?: 'start' | 'end';
|
|
11
|
+
};
|
|
12
|
+
declare var __VLS_7: string, __VLS_8: {
|
|
13
|
+
item: DescriptionItem;
|
|
14
|
+
};
|
|
15
|
+
type __VLS_Slots = {} & {
|
|
16
|
+
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
17
|
+
};
|
|
18
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
19
|
+
align: "start" | "end";
|
|
20
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A create/edit form in a slideover. The caller renders the form in the default slot as
|
|
3
|
+
* `<UForm :id="formId" …>` (or `<form :id="formId" @submit.prevent>`); the footer holds Cancel and the submit
|
|
4
|
+
* button, which submits that form through its `form` attribute.
|
|
5
|
+
*
|
|
6
|
+
* Open it with `useOverlay()`: wrap it in a component of your own whose root is `FormSlideover` and which emits
|
|
7
|
+
* `close` with the saved record (Cancel emits `close(false)`), then
|
|
8
|
+
* `const record = await overlay.create(MyFormSlideover).open(props).result`.
|
|
9
|
+
*/
|
|
10
|
+
type __VLS_Props = {
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
formId: string;
|
|
14
|
+
submitLabel: string;
|
|
15
|
+
cancelLabel?: string;
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
declare function cancel(): void;
|
|
20
|
+
type __VLS_ModelProps = {
|
|
21
|
+
'open'?: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
24
|
+
declare var __VLS_9: {}, __VLS_12: {
|
|
25
|
+
cancel: typeof cancel;
|
|
26
|
+
};
|
|
27
|
+
type __VLS_Slots = {} & {
|
|
28
|
+
default?: (props: typeof __VLS_9) => any;
|
|
29
|
+
} & {
|
|
30
|
+
footer?: (props: typeof __VLS_12) => any;
|
|
31
|
+
};
|
|
32
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
|
+
close: (value: false) => any;
|
|
34
|
+
"update:open": (value: boolean | undefined) => any;
|
|
35
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
36
|
+
onClose?: ((value: false) => any) | undefined;
|
|
37
|
+
"onUpdate:open"?: ((value: boolean | undefined) => any) | undefined;
|
|
38
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
39
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
40
|
+
declare const _default: typeof __VLS_export;
|
|
41
|
+
export default _default;
|
|
42
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
43
|
+
new (): {
|
|
44
|
+
$slots: S;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
title: { type: String, required: true },
|
|
4
|
+
description: { type: String, required: false },
|
|
5
|
+
formId: { type: String, required: true },
|
|
6
|
+
submitLabel: { type: String, required: true },
|
|
7
|
+
cancelLabel: { type: String, required: false },
|
|
8
|
+
loading: { type: Boolean, required: false },
|
|
9
|
+
disabled: { type: Boolean, required: false }
|
|
10
|
+
});
|
|
11
|
+
const emit = defineEmits(["close"]);
|
|
12
|
+
const open = defineModel("open", { type: Boolean });
|
|
13
|
+
const actions = useUiT("actions");
|
|
14
|
+
const cancelText = computed(() => props.cancelLabel ?? actions("cancel", "Cancel"));
|
|
15
|
+
function cancel() {
|
|
16
|
+
open.value = false;
|
|
17
|
+
emit("close", false);
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<USlideover
|
|
23
|
+
v-model:open="open"
|
|
24
|
+
:title="title"
|
|
25
|
+
:description="description"
|
|
26
|
+
:ui="{ footer: 'justify-end gap-2' }"
|
|
27
|
+
>
|
|
28
|
+
<template #body>
|
|
29
|
+
<slot />
|
|
30
|
+
</template>
|
|
31
|
+
<template #footer>
|
|
32
|
+
<slot
|
|
33
|
+
name="footer"
|
|
34
|
+
:cancel="cancel"
|
|
35
|
+
>
|
|
36
|
+
<UButton
|
|
37
|
+
:label="cancelText"
|
|
38
|
+
color="neutral"
|
|
39
|
+
variant="outline"
|
|
40
|
+
:disabled="loading"
|
|
41
|
+
@click="cancel"
|
|
42
|
+
/>
|
|
43
|
+
<UButton
|
|
44
|
+
type="submit"
|
|
45
|
+
:form="formId"
|
|
46
|
+
:label="submitLabel"
|
|
47
|
+
:loading="loading"
|
|
48
|
+
:disabled="disabled"
|
|
49
|
+
/>
|
|
50
|
+
</slot>
|
|
51
|
+
</template>
|
|
52
|
+
</USlideover>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A create/edit form in a slideover. The caller renders the form in the default slot as
|
|
3
|
+
* `<UForm :id="formId" …>` (or `<form :id="formId" @submit.prevent>`); the footer holds Cancel and the submit
|
|
4
|
+
* button, which submits that form through its `form` attribute.
|
|
5
|
+
*
|
|
6
|
+
* Open it with `useOverlay()`: wrap it in a component of your own whose root is `FormSlideover` and which emits
|
|
7
|
+
* `close` with the saved record (Cancel emits `close(false)`), then
|
|
8
|
+
* `const record = await overlay.create(MyFormSlideover).open(props).result`.
|
|
9
|
+
*/
|
|
10
|
+
type __VLS_Props = {
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
formId: string;
|
|
14
|
+
submitLabel: string;
|
|
15
|
+
cancelLabel?: string;
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
declare function cancel(): void;
|
|
20
|
+
type __VLS_ModelProps = {
|
|
21
|
+
'open'?: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
24
|
+
declare var __VLS_9: {}, __VLS_12: {
|
|
25
|
+
cancel: typeof cancel;
|
|
26
|
+
};
|
|
27
|
+
type __VLS_Slots = {} & {
|
|
28
|
+
default?: (props: typeof __VLS_9) => any;
|
|
29
|
+
} & {
|
|
30
|
+
footer?: (props: typeof __VLS_12) => any;
|
|
31
|
+
};
|
|
32
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
|
+
close: (value: false) => any;
|
|
34
|
+
"update:open": (value: boolean | undefined) => any;
|
|
35
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
36
|
+
onClose?: ((value: false) => any) | undefined;
|
|
37
|
+
"onUpdate:open"?: ((value: boolean | undefined) => any) | undefined;
|
|
38
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
39
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
40
|
+
declare const _default: typeof __VLS_export;
|
|
41
|
+
export default _default;
|
|
42
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
43
|
+
new (): {
|
|
44
|
+
$slots: S;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short list in a card (dashboard and overview widgets): title, optional description and `#actions`, rows in the
|
|
3
|
+
* default slot (put `<li>`s inside, they get dividers) and a "View all" link to `to`. `empty` replaces the rows
|
|
4
|
+
* when `isEmpty` is set.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
isEmpty?: boolean;
|
|
11
|
+
empty?: string;
|
|
12
|
+
to?: string;
|
|
13
|
+
toLabel?: string;
|
|
14
|
+
};
|
|
15
|
+
declare var __VLS_1: {}, __VLS_18: {};
|
|
16
|
+
type __VLS_Slots = {} & {
|
|
17
|
+
actions?: (props: typeof __VLS_1) => any;
|
|
18
|
+
} & {
|
|
19
|
+
default?: (props: typeof __VLS_18) => any;
|
|
20
|
+
};
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|
|
25
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
26
|
+
new (): {
|
|
27
|
+
$slots: S;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { surfaceClass } from "../../utils/surface";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
title: { type: String, required: true },
|
|
5
|
+
description: { type: String, required: false },
|
|
6
|
+
loading: { type: Boolean, required: false },
|
|
7
|
+
isEmpty: { type: Boolean, required: false },
|
|
8
|
+
empty: { type: String, required: false },
|
|
9
|
+
to: { type: String, required: false },
|
|
10
|
+
toLabel: { type: String, required: false }
|
|
11
|
+
});
|
|
12
|
+
const t = useUiT("patterns");
|
|
13
|
+
const linkLabel = computed(() => props.toLabel ?? t("viewAll", "View all"));
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<section :class="[surfaceClass, 'flex h-full flex-col']">
|
|
18
|
+
<div class="border-default flex items-center justify-between gap-3 border-b px-4 py-3">
|
|
19
|
+
<div class="min-w-0">
|
|
20
|
+
<h2 class="text-highlighted text-sm font-semibold">
|
|
21
|
+
{{ title }}
|
|
22
|
+
</h2>
|
|
23
|
+
<p
|
|
24
|
+
v-if="description"
|
|
25
|
+
class="text-muted text-xs"
|
|
26
|
+
>
|
|
27
|
+
{{ description }}
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
<div
|
|
31
|
+
v-if="$slots.actions"
|
|
32
|
+
class="flex items-center gap-2"
|
|
33
|
+
>
|
|
34
|
+
<slot name="actions" />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="flex-1 px-4">
|
|
38
|
+
<div
|
|
39
|
+
v-if="loading"
|
|
40
|
+
class="space-y-3 py-4"
|
|
41
|
+
>
|
|
42
|
+
<USkeleton class="h-4 w-2/3" />
|
|
43
|
+
<USkeleton class="h-4 w-1/2" />
|
|
44
|
+
<USkeleton class="h-4 w-3/5" />
|
|
45
|
+
</div>
|
|
46
|
+
<p
|
|
47
|
+
v-else-if="isEmpty"
|
|
48
|
+
class="text-muted py-6 text-center text-sm"
|
|
49
|
+
>
|
|
50
|
+
{{ empty }}
|
|
51
|
+
</p>
|
|
52
|
+
<ul
|
|
53
|
+
v-else
|
|
54
|
+
class="divide-default divide-y text-sm [&>li]:py-2.5"
|
|
55
|
+
>
|
|
56
|
+
<slot />
|
|
57
|
+
</ul>
|
|
58
|
+
</div>
|
|
59
|
+
<div
|
|
60
|
+
v-if="to"
|
|
61
|
+
class="border-default border-t px-4 py-2.5"
|
|
62
|
+
>
|
|
63
|
+
<UButton
|
|
64
|
+
:to="to"
|
|
65
|
+
variant="link"
|
|
66
|
+
color="neutral"
|
|
67
|
+
size="xs"
|
|
68
|
+
class="p-0"
|
|
69
|
+
trailing-icon="i-lucide-arrow-right"
|
|
70
|
+
:label="linkLabel"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</section>
|
|
74
|
+
</template>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short list in a card (dashboard and overview widgets): title, optional description and `#actions`, rows in the
|
|
3
|
+
* default slot (put `<li>`s inside, they get dividers) and a "View all" link to `to`. `empty` replaces the rows
|
|
4
|
+
* when `isEmpty` is set.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
isEmpty?: boolean;
|
|
11
|
+
empty?: string;
|
|
12
|
+
to?: string;
|
|
13
|
+
toLabel?: string;
|
|
14
|
+
};
|
|
15
|
+
declare var __VLS_1: {}, __VLS_18: {};
|
|
16
|
+
type __VLS_Slots = {} & {
|
|
17
|
+
actions?: (props: typeof __VLS_1) => any;
|
|
18
|
+
} & {
|
|
19
|
+
default?: (props: typeof __VLS_18) => any;
|
|
20
|
+
};
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|
|
25
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
26
|
+
new (): {
|
|
27
|
+
$slots: S;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BreadcrumbItem } from '@nuxt/ui';
|
|
2
|
+
/**
|
|
3
|
+
* The title block of an app page: breadcrumb (or a muted eyebrow line) above, the title with an optional `#badge`,
|
|
4
|
+
* the description, a meta row (default slot) and right-aligned `#actions`. Detail pages pass a breadcrumb array
|
|
5
|
+
* so the parent list is one click away.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** A muted line above the title, or links back up the hierarchy. */
|
|
11
|
+
breadcrumb?: string | BreadcrumbItem[];
|
|
12
|
+
};
|
|
13
|
+
type __VLS_Slots = {
|
|
14
|
+
title?: () => unknown;
|
|
15
|
+
badge?: () => unknown;
|
|
16
|
+
description?: () => unknown;
|
|
17
|
+
default?: () => unknown;
|
|
18
|
+
actions?: () => unknown;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
title: { type: String, required: false },
|
|
4
|
+
description: { type: String, required: false },
|
|
5
|
+
breadcrumb: { type: [String, Array], required: false }
|
|
6
|
+
});
|
|
7
|
+
defineSlots();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<header class="flex flex-wrap items-end justify-between gap-x-4 gap-y-3">
|
|
12
|
+
<div class="min-w-0 flex-1 basis-72 space-y-1">
|
|
13
|
+
<UBreadcrumb
|
|
14
|
+
v-if="Array.isArray(breadcrumb) && breadcrumb.length"
|
|
15
|
+
:items="breadcrumb"
|
|
16
|
+
/>
|
|
17
|
+
<p
|
|
18
|
+
v-else-if="typeof breadcrumb === 'string' && breadcrumb"
|
|
19
|
+
class="text-muted truncate text-sm"
|
|
20
|
+
>
|
|
21
|
+
{{ breadcrumb }}
|
|
22
|
+
</p>
|
|
23
|
+
<div class="flex min-w-0 flex-wrap items-center gap-2">
|
|
24
|
+
<h1 class="text-highlighted text-xl font-semibold tracking-tight text-pretty sm:text-2xl">
|
|
25
|
+
<slot name="title">
|
|
26
|
+
{{ title }}
|
|
27
|
+
</slot>
|
|
28
|
+
</h1>
|
|
29
|
+
<slot name="badge" />
|
|
30
|
+
</div>
|
|
31
|
+
<p
|
|
32
|
+
v-if="description || $slots.description"
|
|
33
|
+
class="text-muted text-sm text-pretty"
|
|
34
|
+
>
|
|
35
|
+
<slot name="description">
|
|
36
|
+
{{ description }}
|
|
37
|
+
</slot>
|
|
38
|
+
</p>
|
|
39
|
+
<div
|
|
40
|
+
v-if="$slots.default"
|
|
41
|
+
class="text-muted flex flex-wrap items-center gap-x-4 gap-y-1 pt-1 text-sm"
|
|
42
|
+
>
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div
|
|
47
|
+
v-if="$slots.actions"
|
|
48
|
+
class="flex flex-wrap items-center gap-2"
|
|
49
|
+
>
|
|
50
|
+
<slot name="actions" />
|
|
51
|
+
</div>
|
|
52
|
+
</header>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BreadcrumbItem } from '@nuxt/ui';
|
|
2
|
+
/**
|
|
3
|
+
* The title block of an app page: breadcrumb (or a muted eyebrow line) above, the title with an optional `#badge`,
|
|
4
|
+
* the description, a meta row (default slot) and right-aligned `#actions`. Detail pages pass a breadcrumb array
|
|
5
|
+
* so the parent list is one click away.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** A muted line above the title, or links back up the hierarchy. */
|
|
11
|
+
breadcrumb?: string | BreadcrumbItem[];
|
|
12
|
+
};
|
|
13
|
+
type __VLS_Slots = {
|
|
14
|
+
title?: () => unknown;
|
|
15
|
+
badge?: () => unknown;
|
|
16
|
+
description?: () => unknown;
|
|
17
|
+
default?: () => unknown;
|
|
18
|
+
actions?: () => unknown;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PageTabItem } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tabs under a page heading. Link mode: items carry `to` and the route decides which one is active. State mode:
|
|
4
|
+
* bind `v-model` and give items a `value`; clicking one selects it. Icons are hidden on small screens.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
items: PageTabItem[];
|
|
8
|
+
};
|
|
9
|
+
type __VLS_ModelProps = {
|
|
10
|
+
modelValue?: string;
|
|
11
|
+
};
|
|
12
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
16
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|