@octabits-io/nuxt-ui-kit 0.11.0 → 0.12.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/dist/i18n/index.d.ts
CHANGED
package/dist/i18n/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -129,6 +129,14 @@ interface PageActionsItem {
|
|
|
129
129
|
key: string;
|
|
130
130
|
icon: string;
|
|
131
131
|
label: string;
|
|
132
|
+
/**
|
|
133
|
+
* 'ai' renders the item in the AI cluster: sparkles + primary-soft (AiButton
|
|
134
|
+
* styling). One inline AI item → verb-labeled button; several → a labeled
|
|
135
|
+
* "AI ∨" dropdown. Collapsed AI items form their own menu group.
|
|
136
|
+
*/
|
|
137
|
+
kind?: 'action' | 'ai';
|
|
138
|
+
/** Menu-only helper text (shown in the AI dropdown / overflow rows). */
|
|
139
|
+
description?: string;
|
|
132
140
|
/** Inline button tone. At most ONE 'primary' item should be visible per state. */
|
|
133
141
|
tone?: 'primary' | 'neutral';
|
|
134
142
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octabits-io/nuxt-ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Frontend kit for Nuxt/Vue admin SPAs: OIDC session harness (oidc-client-ts), Eden Treaty client factory, auth/org store cores, and a route-guard builder — factory-style seams the app wires into its own plugins, stores, and middleware",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Shipped as source: the consumer's Vite compiles this SFC. All imports are
|
|
3
|
+
// explicit — no reliance on the consumer's auto-import configuration.
|
|
4
|
+
import UButton from '@nuxt/ui/components/Button.vue';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* THE visual token for AI triggers: sparkles + primary-soft + verb label.
|
|
8
|
+
* Every button that launches AI work (workflow, generation, suggestion) renders
|
|
9
|
+
* through this primitive so "AI will do something" always looks the same.
|
|
10
|
+
* Convention: `i-lucide-sparkles` is reserved exclusively for AI triggers;
|
|
11
|
+
* soft/solid primary = AI acts on data, ghost = AI conversation.
|
|
12
|
+
*/
|
|
13
|
+
withDefaults(defineProps<{
|
|
14
|
+
label: string
|
|
15
|
+
/** Leading icon. Keep the sparkles default unless a workflow icon is clearer. */
|
|
16
|
+
icon?: string
|
|
17
|
+
/** e.g. 'i-lucide-chevron-down' for dropdown triggers. */
|
|
18
|
+
trailingIcon?: string
|
|
19
|
+
size?: 'xs' | 'sm' | 'md'
|
|
20
|
+
variant?: 'soft' | 'solid' | 'subtle' | 'outline' | 'ghost'
|
|
21
|
+
loading?: boolean
|
|
22
|
+
disabled?: boolean
|
|
23
|
+
}>(), {
|
|
24
|
+
icon: 'i-lucide-sparkles',
|
|
25
|
+
trailingIcon: undefined,
|
|
26
|
+
size: 'sm',
|
|
27
|
+
variant: 'soft',
|
|
28
|
+
loading: false,
|
|
29
|
+
disabled: false,
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<UButton
|
|
35
|
+
:label="label"
|
|
36
|
+
:icon="icon"
|
|
37
|
+
:trailing-icon="trailingIcon"
|
|
38
|
+
color="primary"
|
|
39
|
+
:variant="variant"
|
|
40
|
+
:size="size"
|
|
41
|
+
:loading="loading"
|
|
42
|
+
:disabled="disabled"
|
|
43
|
+
/>
|
|
44
|
+
</template>
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// Shipped as source: the consumer's Vite compiles this SFC. All imports are
|
|
3
3
|
// explicit — no reliance on the consumer's auto-import configuration.
|
|
4
|
-
// i18n key contract: pageChrome.help (+ PageActionMenu's pageChrome.moreActions).
|
|
4
|
+
// i18n key contract: pageChrome.help, pageChrome.ai (+ PageActionMenu's pageChrome.moreActions).
|
|
5
5
|
import { computed, inject } from 'vue';
|
|
6
6
|
import { useI18n } from 'vue-i18n';
|
|
7
7
|
import USeparator from '@nuxt/ui/components/Separator.vue';
|
|
8
|
+
import UDropdownMenu from '@nuxt/ui/components/DropdownMenu.vue';
|
|
9
|
+
import UIcon from '@nuxt/ui/components/Icon.vue';
|
|
8
10
|
import type { DropdownMenuItem } from '@nuxt/ui';
|
|
11
|
+
import AiButton from './AiButton.vue';
|
|
9
12
|
import PageAction from './PageAction.vue';
|
|
10
13
|
import PageActionMenu from './PageActionMenu.vue';
|
|
11
14
|
// Package-name import (not ../composables): only src/components is packed, and
|
|
@@ -55,13 +58,33 @@ const collapsed = computed(() => {
|
|
|
55
58
|
|
|
56
59
|
const showHelp = computed(() => props.help && Boolean(helpPanel?.hasActions.value));
|
|
57
60
|
|
|
58
|
-
const
|
|
61
|
+
const actionItems = computed(() => props.items.filter(item => (item.kind ?? 'action') !== 'ai'));
|
|
62
|
+
const aiItems = computed(() => props.items.filter(item => item.kind === 'ai'));
|
|
63
|
+
|
|
64
|
+
const isInlineBound = (item: PageActionsItem) =>
|
|
59
65
|
(item.visibility ?? 'auto') === 'always'
|
|
60
|
-
|| ((item.visibility ?? 'auto') === 'auto' && !collapsed.value)
|
|
61
|
-
|
|
66
|
+
|| ((item.visibility ?? 'auto') === 'auto' && !collapsed.value);
|
|
67
|
+
|
|
68
|
+
const inlineItems = computed(() => actionItems.value.filter(isInlineBound));
|
|
69
|
+
|
|
70
|
+
// AI cluster: one inline item renders as its own verb-labeled AiButton; several
|
|
71
|
+
// share a labeled "AI ∨" dropdown (icons + descriptions per row).
|
|
72
|
+
const inlineAiItems = computed(() => aiItems.value.filter(isInlineBound));
|
|
73
|
+
const aiDropdownItems = computed<DropdownMenuItem[]>(() =>
|
|
74
|
+
inlineAiItems.value.map(item => ({
|
|
75
|
+
label: item.label,
|
|
76
|
+
description: item.description,
|
|
77
|
+
icon: item.icon,
|
|
78
|
+
disabled: item.disabled || Boolean(item.disabledReason),
|
|
79
|
+
loading: item.loading,
|
|
80
|
+
onSelect: item.onSelect,
|
|
81
|
+
})),
|
|
82
|
+
);
|
|
62
83
|
|
|
63
84
|
const inlineUtilityItems = computed(() => collapsed.value ? [] : props.utilityItems);
|
|
64
85
|
|
|
86
|
+
// Descriptions render only in the dedicated AI dropdown (which has wrap/width
|
|
87
|
+
// styling) — the compact ⋯ overflow stays label-only.
|
|
65
88
|
function toMenuItem(item: PageActionsItem): DropdownMenuItem {
|
|
66
89
|
return {
|
|
67
90
|
label: item.label,
|
|
@@ -77,18 +100,25 @@ function toMenuItem(item: PageActionsItem): DropdownMenuItem {
|
|
|
77
100
|
|
|
78
101
|
const menuGroups = computed<DropdownMenuItem[][]>(() => {
|
|
79
102
|
const collapsedAutos = collapsed.value
|
|
80
|
-
?
|
|
103
|
+
? actionItems.value.filter(item => (item.visibility ?? 'auto') === 'auto')
|
|
81
104
|
: [];
|
|
82
105
|
|
|
83
106
|
// Menu-only items grouped by section, in first-appearance order.
|
|
84
107
|
const sections = new Map<string, PageActionsItem[]>();
|
|
85
|
-
for (const item of
|
|
108
|
+
for (const item of actionItems.value) {
|
|
86
109
|
if ((item.visibility ?? 'auto') !== 'menu') continue;
|
|
87
110
|
const section = item.section ?? 'default';
|
|
88
111
|
if (!sections.has(section)) sections.set(section, []);
|
|
89
112
|
sections.get(section)!.push(item);
|
|
90
113
|
}
|
|
91
114
|
|
|
115
|
+
// AI items bound to the menu (explicit 'menu', or 'auto' while collapsed)
|
|
116
|
+
// form their own group between the action sections and the utilities.
|
|
117
|
+
const aiGroup = aiItems.value.filter(item =>
|
|
118
|
+
(item.visibility ?? 'auto') === 'menu'
|
|
119
|
+
|| ((item.visibility ?? 'auto') === 'auto' && collapsed.value),
|
|
120
|
+
);
|
|
121
|
+
|
|
92
122
|
const utilityGroup: DropdownMenuItem[] = collapsed.value
|
|
93
123
|
? [
|
|
94
124
|
...props.utilityItems.map(toMenuItem),
|
|
@@ -101,6 +131,7 @@ const menuGroups = computed<DropdownMenuItem[][]>(() => {
|
|
|
101
131
|
return [
|
|
102
132
|
collapsedAutos.map(toMenuItem),
|
|
103
133
|
...[...sections.values()].map(group => group.map(toMenuItem)),
|
|
134
|
+
aiGroup.map(toMenuItem),
|
|
104
135
|
utilityGroup,
|
|
105
136
|
].filter(group => group.length > 0);
|
|
106
137
|
});
|
|
@@ -125,6 +156,35 @@ const hasUtilityRegion = computed(() =>
|
|
|
125
156
|
:target="item.target"
|
|
126
157
|
@click="item.onSelect?.()"
|
|
127
158
|
/>
|
|
159
|
+
<!-- AI cluster: soft-primary sparkles = "AI acts on data". One item → its
|
|
160
|
+
verb label; several → the shared labeled dropdown. -->
|
|
161
|
+
<AiButton
|
|
162
|
+
v-if="inlineAiItems.length === 1"
|
|
163
|
+
:label="inlineAiItems[0]!.label"
|
|
164
|
+
:loading="inlineAiItems[0]!.loading"
|
|
165
|
+
:disabled="inlineAiItems[0]!.disabled || Boolean(inlineAiItems[0]!.disabledReason)"
|
|
166
|
+
@click="inlineAiItems[0]!.onSelect?.()"
|
|
167
|
+
/>
|
|
168
|
+
<UDropdownMenu
|
|
169
|
+
v-else-if="inlineAiItems.length > 1"
|
|
170
|
+
:items="aiDropdownItems"
|
|
171
|
+
:content="{ align: 'end' }"
|
|
172
|
+
:ui="{
|
|
173
|
+
content: 'w-72',
|
|
174
|
+
item: 'gap-2.5 p-2',
|
|
175
|
+
itemLabel: 'font-medium text-highlighted',
|
|
176
|
+
itemDescription: 'mt-0.5 whitespace-normal text-xs/4',
|
|
177
|
+
}"
|
|
178
|
+
>
|
|
179
|
+
<AiButton :label="t('pageChrome.ai')" trailing-icon="i-lucide-chevron-down" />
|
|
180
|
+
<template #item-leading="{ item }">
|
|
181
|
+
<span
|
|
182
|
+
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-primary/20 to-primary/5 text-primary ring-1 ring-primary/20 ring-inset"
|
|
183
|
+
>
|
|
184
|
+
<UIcon :name="(item.icon as string) ?? 'i-lucide-sparkles'" class="size-4" />
|
|
185
|
+
</span>
|
|
186
|
+
</template>
|
|
187
|
+
</UDropdownMenu>
|
|
128
188
|
<PageActionMenu :items="menuGroups" />
|
|
129
189
|
<template v-if="hasUtilityRegion">
|
|
130
190
|
<USeparator orientation="vertical" class="h-5 mx-1" />
|
|
@@ -11,6 +11,14 @@ export interface PageActionsItem {
|
|
|
11
11
|
key: string;
|
|
12
12
|
icon: string;
|
|
13
13
|
label: string;
|
|
14
|
+
/**
|
|
15
|
+
* 'ai' renders the item in the AI cluster: sparkles + primary-soft (AiButton
|
|
16
|
+
* styling). One inline AI item → verb-labeled button; several → a labeled
|
|
17
|
+
* "AI ∨" dropdown. Collapsed AI items form their own menu group.
|
|
18
|
+
*/
|
|
19
|
+
kind?: 'action' | 'ai';
|
|
20
|
+
/** Menu-only helper text (shown in the AI dropdown / overflow rows). */
|
|
21
|
+
description?: string;
|
|
14
22
|
/** Inline button tone. At most ONE 'primary' item should be visible per state. */
|
|
15
23
|
tone?: 'primary' | 'neutral';
|
|
16
24
|
/**
|