@octabits-io/nuxt-ui-kit 0.8.0 → 0.9.1
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octabits-io/nuxt-ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
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",
|
|
@@ -30,25 +30,38 @@ const props = withDefaults(defineProps<{
|
|
|
30
30
|
showLabel?: boolean
|
|
31
31
|
/** Whether the tooltip should respect global disabled state. */
|
|
32
32
|
tooltipDisabled?: boolean
|
|
33
|
+
/**
|
|
34
|
+
* Why the action is currently unavailable. When set, the button renders
|
|
35
|
+
* disabled and the tooltip shows "label — reason", keeping the action's
|
|
36
|
+
* purpose visible alongside the blocker. Hover on the disabled button works
|
|
37
|
+
* via an internal span wrapper (disabled buttons don't dispatch pointer
|
|
38
|
+
* events).
|
|
39
|
+
*/
|
|
40
|
+
disabledReason?: string | null
|
|
33
41
|
}>(), {
|
|
34
42
|
tone: 'neutral',
|
|
35
43
|
loading: false,
|
|
36
44
|
disabled: false,
|
|
37
45
|
showLabel: false,
|
|
38
46
|
tooltipDisabled: false,
|
|
47
|
+
disabledReason: null,
|
|
39
48
|
})
|
|
40
49
|
|
|
41
50
|
const slots = useSlots()
|
|
42
51
|
|
|
43
52
|
const hasLabelSlot = computed(() => Boolean(slots.default))
|
|
44
53
|
const isIconOnly = computed(() => !hasLabelSlot.value && !props.showLabel)
|
|
54
|
+
const isBlocked = computed(() => Boolean(props.disabledReason))
|
|
55
|
+
const tooltipText = computed(() =>
|
|
56
|
+
props.disabledReason ? `${props.label} — ${props.disabledReason}` : props.label,
|
|
57
|
+
)
|
|
45
58
|
|
|
46
59
|
const buttonProps = computed<Partial<ButtonProps>>(() => {
|
|
47
60
|
const base: Partial<ButtonProps> = {
|
|
48
61
|
icon: props.icon,
|
|
49
62
|
size: 'sm',
|
|
50
63
|
loading: props.loading,
|
|
51
|
-
disabled: props.disabled || props.loading,
|
|
64
|
+
disabled: props.disabled || props.loading || isBlocked.value,
|
|
52
65
|
to: props.to,
|
|
53
66
|
target: props.target,
|
|
54
67
|
}
|
|
@@ -72,8 +85,25 @@ if (import.meta.dev && !props.label) {
|
|
|
72
85
|
</script>
|
|
73
86
|
|
|
74
87
|
<template>
|
|
75
|
-
<UTooltip
|
|
88
|
+
<UTooltip
|
|
89
|
+
v-if="isIconOnly || isBlocked"
|
|
90
|
+
:text="tooltipText"
|
|
91
|
+
:disabled="!isBlocked && tooltipDisabled"
|
|
92
|
+
>
|
|
93
|
+
<!-- Disabled buttons don't dispatch pointer events: when blocked, the span
|
|
94
|
+
is the hover target and the button opts out of pointer events. -->
|
|
95
|
+
<span v-if="isBlocked" class="inline-flex">
|
|
96
|
+
<UButton
|
|
97
|
+
v-bind="buttonProps"
|
|
98
|
+
class="pointer-events-none"
|
|
99
|
+
:aria-label="tooltipText"
|
|
100
|
+
:label="!isIconOnly && showLabel ? label : undefined"
|
|
101
|
+
>
|
|
102
|
+
<slot v-if="!isIconOnly" />
|
|
103
|
+
</UButton>
|
|
104
|
+
</span>
|
|
76
105
|
<UButton
|
|
106
|
+
v-else
|
|
77
107
|
v-bind="buttonProps"
|
|
78
108
|
:aria-label="label"
|
|
79
109
|
/>
|
|
@@ -15,7 +15,14 @@ import PageUtilityActions from './PageUtilityActions.vue'
|
|
|
15
15
|
* Standard page header.
|
|
16
16
|
*
|
|
17
17
|
* Conventions enforced by this component and its siblings:
|
|
18
|
-
* -
|
|
18
|
+
* - EVERY inline `#actions` button is labeled (`show-label`) — no icon-only
|
|
19
|
+
* buttons in the header. Hierarchy comes from tone alone: at most ONE
|
|
20
|
+
* `tone="primary"` (solid) per state — the state's main next step — ghost
|
|
21
|
+
* neutral for the rest.
|
|
22
|
+
* - Actions that are destructive only in *some* states (e.g. cancel) render
|
|
23
|
+
* inline while they are the state's decision counterpart, and move to the
|
|
24
|
+
* overflow menu (red) once they become destructive maintenance.
|
|
25
|
+
* - Max 3 inline actions visible per state in `#actions`; more go in `overflowItems`.
|
|
19
26
|
* - Destructive actions are ALWAYS placed inside the overflow menu, never inline.
|
|
20
27
|
* - Header height, spacing, and tooltip behavior are normalized via `PageAction`.
|
|
21
28
|
* - Utility triggers (Help, AI history, …) live in `#utility` (default =
|
|
@@ -96,7 +103,9 @@ const titleClass = computed(() => props.density === 'compact' ? 'font-display te
|
|
|
96
103
|
<slot name="badges" />
|
|
97
104
|
</div>
|
|
98
105
|
|
|
99
|
-
|
|
106
|
+
<!-- flex-wrap: labeled actions overflow narrow (mobile) viewports otherwise —
|
|
107
|
+
the outer header wraps rows, but this cluster must wrap internally too. -->
|
|
108
|
+
<div class="ml-auto flex flex-wrap items-center gap-1">
|
|
100
109
|
<slot name="actions" />
|
|
101
110
|
<PageActionMenu
|
|
102
111
|
v-if="overflowItems.length"
|