@bagelink/vue 1.15.94 → 1.15.96
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/components/Card.vue.d.ts +13 -14
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPopover.vue.d.ts +2 -10
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts +10 -0
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/index.cjs +25 -25
- package/dist/index.mjs +2630 -2591
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Card.vue +76 -12
- package/src/components/ListItem.vue +24 -17
- package/src/components/layout/AppLayout.vue +19 -3
package/package.json
CHANGED
package/src/components/Card.vue
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
defineOptions({ name: 'BglCard' })
|
|
3
3
|
import type { GradientDirProp, GradientProp } from '@bagelink/vue'
|
|
4
|
-
import { computed } from 'vue'
|
|
4
|
+
import { computed, useSlots } from 'vue'
|
|
5
5
|
import { useGradientVariant } from '@bagelink/vue'
|
|
6
6
|
|
|
7
|
+
const slots = useSlots()
|
|
8
|
+
|
|
7
9
|
const props = defineProps<{
|
|
8
10
|
label?: string
|
|
9
|
-
/** Card header title.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/** Card header title. Sits in the center of the header row. Pairs with the
|
|
12
|
+
surrounding slots to build a 6-region card:
|
|
13
|
+
|
|
14
|
+
┌ #top-start ──── #header / title ──── #top-end (or #header-action) ┐
|
|
15
|
+
│ (default slot) │
|
|
16
|
+
└ #bottom-start ─────── #footer ──────────────── #bottom-end ──────┘
|
|
17
|
+
|
|
18
|
+
Start/end are logical (RTL-aware). The header row renders if any of
|
|
19
|
+
`title` / `#header` / `#top-start` / `#top-end` / `#header-action` is
|
|
20
|
+
present; the footer row renders if any of `#footer` / `#bottom-start` /
|
|
21
|
+
`#bottom-end` is present. For full control of the header center, use the
|
|
22
|
+
`#header` slot instead of `title`. */
|
|
13
23
|
title?: string
|
|
14
24
|
thin?: boolean
|
|
15
25
|
outline?: boolean
|
|
@@ -62,6 +72,15 @@ const is = computed(() => {
|
|
|
62
72
|
if (props.to !== undefined && props.to !== '') { return 'router-link' }
|
|
63
73
|
return 'div'
|
|
64
74
|
})
|
|
75
|
+
|
|
76
|
+
// Header row shows if any of the header pieces are present (or a title is set).
|
|
77
|
+
const hasHeader = computed(() =>
|
|
78
|
+
!!(props.title || slots.header || slots['header-action'] || slots['top-start'] || slots['top-end']),
|
|
79
|
+
)
|
|
80
|
+
// Footer row shows if any footer piece is present.
|
|
81
|
+
const hasFooter = computed(() =>
|
|
82
|
+
!!(slots.footer || slots['bottom-start'] || slots['bottom-end']),
|
|
83
|
+
)
|
|
65
84
|
</script>
|
|
66
85
|
|
|
67
86
|
<template>
|
|
@@ -80,14 +99,47 @@ const is = computed(() => {
|
|
|
80
99
|
<span v-if="label" class="card_label block label">
|
|
81
100
|
{{ label }}
|
|
82
101
|
</span>
|
|
83
|
-
<!-- Header row:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
<!-- Header row: [top-start] [header / title] [top-end / header-action].
|
|
103
|
+
Logical (RTL-aware) start/end. A full `#header` override still replaces
|
|
104
|
+
the title slot only, so `#top-start` / `#top-end` (and the legacy
|
|
105
|
+
`#header-action`) keep working alongside it. -->
|
|
106
|
+
<div
|
|
107
|
+
v-if="hasHeader"
|
|
108
|
+
class="card_header flex space-between align-items-center w-100p"
|
|
109
|
+
>
|
|
110
|
+
<span v-if="$slots['top-start']" class="card_header_start flex align-items-center gap-05">
|
|
111
|
+
<slot name="top-start" />
|
|
112
|
+
</span>
|
|
113
|
+
<span class="card_header_main flex-grow w-100p">
|
|
114
|
+
<slot name="header">
|
|
115
|
+
<span class="card_header_title">{{ title }}</span>
|
|
116
|
+
</slot>
|
|
117
|
+
</span>
|
|
118
|
+
<span
|
|
119
|
+
v-if="$slots['top-end'] || $slots['header-action']"
|
|
120
|
+
class="card_header_action flex align-items-center gap-05"
|
|
121
|
+
>
|
|
122
|
+
<slot name="top-end"><slot name="header-action" /></slot>
|
|
123
|
+
</span>
|
|
89
124
|
</div>
|
|
125
|
+
|
|
90
126
|
<slot />
|
|
127
|
+
|
|
128
|
+
<!-- Footer row: [bottom-start] [footer] [bottom-end]. -->
|
|
129
|
+
<div
|
|
130
|
+
v-if="hasFooter"
|
|
131
|
+
class="card_footer flex space-between align-items-center"
|
|
132
|
+
>
|
|
133
|
+
<span v-if="$slots['bottom-start']" class="card_footer_start flex align-items-center gap-05">
|
|
134
|
+
<slot name="bottom-start" />
|
|
135
|
+
</span>
|
|
136
|
+
<span class="card_footer_main flex-grow">
|
|
137
|
+
<slot name="footer" />
|
|
138
|
+
</span>
|
|
139
|
+
<span v-if="$slots['bottom-end']" class="card_footer_end flex align-items-center gap-05">
|
|
140
|
+
<slot name="bottom-end" />
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
91
143
|
</component>
|
|
92
144
|
</template>
|
|
93
145
|
|
|
@@ -106,10 +158,23 @@ padding: 0.85rem var(--bgl-card-pad);
|
|
|
106
158
|
border-bottom: 1px solid var(--bgl-border-color);
|
|
107
159
|
min-height: 0;
|
|
108
160
|
}
|
|
161
|
+
.card_header_main {
|
|
162
|
+
text-align: start;
|
|
163
|
+
min-width: 0;
|
|
164
|
+
}
|
|
109
165
|
.card_header_title {
|
|
110
166
|
font-weight: 600;
|
|
111
167
|
}
|
|
112
168
|
|
|
169
|
+
/* Footer row: mirrors the header — full-bleed divider via negative margins,
|
|
170
|
+
border on top instead of bottom. */
|
|
171
|
+
.card_footer {
|
|
172
|
+
margin: var(--bgl-card-pad) calc(var(--bgl-card-pad) * -1) calc(var(--bgl-card-pad) * -1);
|
|
173
|
+
padding: 0.85rem var(--bgl-card-pad);
|
|
174
|
+
border-top: 1px solid var(--bgl-border-color);
|
|
175
|
+
min-height: 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
113
178
|
.bgl_card {
|
|
114
179
|
--bgl-card-pad: 2rem;
|
|
115
180
|
border-radius: var(--bgl-card-border-radius);
|
|
@@ -129,7 +194,6 @@ padding: var(--bgl-card-pad);
|
|
|
129
194
|
/* When padding is stripped via the p-0 utility, the header supplies its own. */
|
|
130
195
|
.bgl_card.p-0 { --bgl-card-pad: 0rem; }
|
|
131
196
|
.bgl_card.p-0 .card_header { padding-inline: 1rem; padding-block: 0.75rem; margin-bottom: 0; }
|
|
132
|
-
/* Likewise the label hangs off the card edge on a p-0 card, so give it its own. */
|
|
133
197
|
.bgl_card.p-0 .card_label { padding-inline: 1rem; padding-top: 0.75rem; }
|
|
134
198
|
|
|
135
199
|
.bgl_card.BagelTable {
|
|
@@ -229,40 +229,47 @@ opacity: 0.5;
|
|
|
229
229
|
pointer-events: none;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
/* Non-clickable rows render as a plain <div> — keep the default arrow cursor
|
|
233
|
+
(the base .list-item rule sets `cursor: pointer` for the interactive case).
|
|
234
|
+
The hover tint is already scoped to .list-item-fullrow, so nothing to undo. */
|
|
235
|
+
.notClickable {
|
|
236
|
+
cursor: default;
|
|
237
|
+
}
|
|
238
|
+
|
|
232
239
|
/* Accent used by the icon-chip and (for rounded/active) the tint. Defaults to
|
|
233
240
|
primary; set per-item via `color` prop → --bgl-list-item-accent. */
|
|
234
241
|
.list-item-row {
|
|
235
242
|
--bgl-list-item-accent: var(--bgl-primary);
|
|
236
243
|
}
|
|
237
244
|
|
|
238
|
-
/* Hover/active
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.
|
|
242
|
-
.list-item.
|
|
245
|
+
/* Hover/active paint on the ROW (not the inner clickable element) so the tint
|
|
246
|
+
spans the whole item — including the #start/#end slots that live outside the
|
|
247
|
+
clickable area. Bagelink's translucent gray tint already flips from
|
|
248
|
+
translucent-dark (light mode) to translucent-light (.bgl-dark-mode), so it
|
|
249
|
+
reads correctly on any background. Override per-list with --bgl-list-item-hover.
|
|
250
|
+
Only interactive rows (those carrying .list-item-fullrow) get the tint — a
|
|
251
|
+
plain non-clickable row must stay flat. */
|
|
252
|
+
.list-item-fullrow:hover,
|
|
253
|
+
.list-item-row:has(.list-item.router-link-exact-active) {
|
|
243
254
|
background-color: var(--bgl-list-item-hover, var(--bgl-gray-tint));
|
|
244
255
|
}
|
|
245
|
-
.list-item.router-link-exact-active,
|
|
246
|
-
.list-item-active
|
|
256
|
+
.list-item-row:has(.list-item.router-link-exact-active),
|
|
257
|
+
.list-item-active {
|
|
247
258
|
background-color: var(--bgl-list-item-active, color-mix(in srgb, var(--bgl-list-item-accent) 12%, transparent));
|
|
248
259
|
color: var(--bgl-list-item-accent);
|
|
249
260
|
}
|
|
250
261
|
|
|
251
|
-
/*
|
|
252
|
-
|
|
262
|
+
/* Match the painted background to the row's corner style: flush rows are square,
|
|
263
|
+
while rounded/standalone rows pick up the card radius so the tint follows the
|
|
264
|
+
pill shape rather than bleeding past it. */
|
|
265
|
+
.list-item-rounded {
|
|
253
266
|
border-radius: var(--bgl-card-border-radius);
|
|
254
267
|
}
|
|
255
|
-
.list-item-rounded
|
|
268
|
+
.list-item-rounded:hover {
|
|
256
269
|
background-color: var(--bgl-list-item-hover, color-mix(in srgb, var(--bgl-list-item-accent) 8%, transparent));
|
|
257
270
|
}
|
|
258
271
|
|
|
259
|
-
.
|
|
260
|
-
.notClickable:hover {
|
|
261
|
-
background-color: unset;
|
|
262
|
-
cursor: default;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.list-item:active {
|
|
272
|
+
.list-item-fullrow:active {
|
|
266
273
|
filter: var(--bgl-hover-filter);
|
|
267
274
|
}
|
|
268
275
|
|
|
@@ -13,6 +13,11 @@ interface Props {
|
|
|
13
13
|
/** Persist sidebar state to localStorage (desktop only). Default true. */
|
|
14
14
|
persist?: boolean
|
|
15
15
|
storageKey?: string
|
|
16
|
+
/** Width of the end-docked aside panel (e.g. an AI chat). Reserved as content
|
|
17
|
+
margin while `asideOpen` is true. */
|
|
18
|
+
asideWidth?: string
|
|
19
|
+
/** v-model:asideOpen — controls whether content reserves room for the aside. */
|
|
20
|
+
asideOpen?: boolean
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -22,6 +27,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
22
27
|
open: undefined,
|
|
23
28
|
persist: true,
|
|
24
29
|
storageKey: 'bgl-sidebar-open',
|
|
30
|
+
asideWidth: '400px',
|
|
31
|
+
asideOpen: false,
|
|
25
32
|
})
|
|
26
33
|
|
|
27
34
|
const emit = defineEmits<{ 'update:open': [value: boolean] }>()
|
|
@@ -73,11 +80,14 @@ function closeOnMobile() {
|
|
|
73
80
|
|
|
74
81
|
// Computed styles for main content margin
|
|
75
82
|
const mainContentStyles = computed(() => {
|
|
83
|
+
// The end-docked aside reserves room on desktop only (it overlays on mobile).
|
|
84
|
+
const marginInlineEnd = !isMobile.value && props.asideOpen ? props.asideWidth : '0'
|
|
76
85
|
if (isMobile.value) {
|
|
77
|
-
return { marginInlineStart: '0' }
|
|
86
|
+
return { marginInlineStart: '0', marginInlineEnd }
|
|
78
87
|
}
|
|
79
88
|
return {
|
|
80
|
-
marginInlineStart: isOpen.value ? props.sidebarWidth : sidebarCollapsedWidth
|
|
89
|
+
marginInlineStart: isOpen.value ? props.sidebarWidth : sidebarCollapsedWidth,
|
|
90
|
+
marginInlineEnd,
|
|
81
91
|
}
|
|
82
92
|
})
|
|
83
93
|
|
|
@@ -113,6 +123,10 @@ onUnmounted(() => {
|
|
|
113
123
|
<!-- Sidebar Slot -->
|
|
114
124
|
<slot name="sidebar" v-bind="{ isOpen, isMobile, toggleMenu, closeOnMobile }" />
|
|
115
125
|
|
|
126
|
+
<!-- End-docked aside slot (e.g. AI chat). Owns its own open state +
|
|
127
|
+
launcher; sync it to `asideOpen` so content reserves room for it. -->
|
|
128
|
+
<slot name="aside" v-bind="{ isMobile }" />
|
|
129
|
+
|
|
116
130
|
<!-- Main Content Area -->
|
|
117
131
|
<main
|
|
118
132
|
class="main-content flex column ease-300"
|
|
@@ -157,7 +171,9 @@ z-index: 40;
|
|
|
157
171
|
.main-content {
|
|
158
172
|
flex: 1;
|
|
159
173
|
overflow: hidden;
|
|
160
|
-
|
|
174
|
+
/* Pushes when the sidebar or end aside opens/closes. Keep in sync with the
|
|
175
|
+
aside panel's own slide transition (see ChatPanel) so they move together. */
|
|
176
|
+
transition: margin 300ms ease;
|
|
161
177
|
min-height: 100vh
|
|
162
178
|
}
|
|
163
179
|
|