@bagelink/vue 1.15.61 → 1.15.65
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/AccordionItem.vue.d.ts.map +1 -1
- package/dist/components/Avatar.vue.d.ts +6 -1
- package/dist/components/Avatar.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts +7 -0
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/EmptyState.vue.d.ts +43 -0
- package/dist/components/EmptyState.vue.d.ts.map +1 -0
- package/dist/components/Icon/Icon.vue.d.ts +13 -0
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/components/Image.vue.d.ts +26 -1
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts +9 -9
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/Menu.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +3 -3
- package/dist/components/calendar/CalendarPopover.vue.d.ts +10 -0
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/charts/BarChart.vue.d.ts +34 -0
- package/dist/components/charts/BarChart.vue.d.ts.map +1 -0
- package/dist/components/charts/ChartTooltip.vue.d.ts +33 -0
- package/dist/components/charts/ChartTooltip.vue.d.ts.map +1 -0
- package/dist/components/charts/Donut.vue.d.ts +53 -0
- package/dist/components/charts/Donut.vue.d.ts.map +1 -0
- package/dist/components/charts/Funnel.vue.d.ts +53 -0
- package/dist/components/charts/Funnel.vue.d.ts.map +1 -0
- package/dist/components/charts/Gauge.vue.d.ts +28 -0
- package/dist/components/charts/Gauge.vue.d.ts.map +1 -0
- package/dist/components/charts/LineChart.vue.d.ts +37 -0
- package/dist/components/charts/LineChart.vue.d.ts.map +1 -0
- package/dist/components/charts/RadialBars.vue.d.ts +34 -0
- package/dist/components/charts/RadialBars.vue.d.ts.map +1 -0
- package/dist/components/charts/RankBars.vue.d.ts +27 -0
- package/dist/components/charts/RankBars.vue.d.ts.map +1 -0
- package/dist/components/charts/Sparkline.vue.d.ts +25 -0
- package/dist/components/charts/Sparkline.vue.d.ts.map +1 -0
- package/dist/components/charts/StatCard.vue.d.ts +28 -0
- package/dist/components/charts/StatCard.vue.d.ts.map +1 -0
- package/dist/components/charts/core/data.d.ts +46 -0
- package/dist/components/charts/core/data.d.ts.map +1 -0
- package/dist/components/charts/core/format.d.ts +13 -0
- package/dist/components/charts/core/format.d.ts.map +1 -0
- package/dist/components/charts/core/palette.d.ts +19 -0
- package/dist/components/charts/core/palette.d.ts.map +1 -0
- package/dist/components/charts/core/uid.d.ts +2 -0
- package/dist/components/charts/core/uid.d.ts.map +1 -0
- package/dist/components/charts/core/useChartAnim.d.ts +11 -0
- package/dist/components/charts/core/useChartAnim.d.ts.map +1 -0
- package/dist/components/charts/core/useChartFrame.d.ts +21 -0
- package/dist/components/charts/core/useChartFrame.d.ts.map +1 -0
- package/dist/components/charts/core/useScale.d.ts +16 -0
- package/dist/components/charts/core/useScale.d.ts.map +1 -0
- package/dist/components/charts/index.d.ts +12 -0
- package/dist/components/charts/index.d.ts.map +1 -0
- package/dist/components/form/inputs/RadioGroup.vue.d.ts +1 -0
- package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RangeInput.vue.d.ts +13 -4
- package/dist/components/form/inputs/RangeInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Divider.vue.d.ts.map +1 -1
- package/dist/components/layout/Layout.vue.d.ts +1 -1
- package/dist/components/layout/Layout.vue.d.ts.map +1 -1
- package/dist/components/layout/Panel.vue.d.ts +1 -1
- package/dist/components/layout/Panel.vue.d.ts.map +1 -1
- package/dist/components/layout/SidebarNavItem.vue.d.ts +3 -1
- package/dist/components/layout/SidebarNavItem.vue.d.ts.map +1 -1
- package/dist/components/layout/Timeline.types.d.ts +9 -0
- package/dist/components/layout/Timeline.types.d.ts.map +1 -0
- package/dist/components/layout/Timeline.vue.d.ts +42 -0
- package/dist/components/layout/Timeline.vue.d.ts.map +1 -0
- package/dist/components/layout/TimelineItem.vue.d.ts +37 -0
- package/dist/components/layout/TimelineItem.vue.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/dialog/Dialog.vue.d.ts +4 -0
- package/dist/dialog/Dialog.vue.d.ts.map +1 -1
- package/dist/index.cjs +110 -116
- package/dist/index.mjs +38104 -37045
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/components/AccordionItem.vue +24 -22
- package/src/components/Avatar.vue +49 -11
- package/src/components/Badge.vue +4 -7
- package/src/components/Card.vue +32 -2
- package/src/components/Dropdown.vue +14 -3
- package/src/components/EmptyState.vue +91 -0
- package/src/components/Icon/Icon.vue +118 -25
- package/src/components/Image.vue +70 -3
- package/src/components/ListItem.vue +43 -22
- package/src/components/Menu.vue +10 -2
- package/src/components/charts/BarChart.vue +197 -0
- package/src/components/charts/ChartTooltip.vue +74 -0
- package/src/components/charts/Donut.vue +219 -0
- package/src/components/charts/Funnel.vue +377 -0
- package/src/components/charts/Gauge.vue +90 -0
- package/src/components/charts/LineChart.vue +255 -0
- package/src/components/charts/RadialBars.vue +99 -0
- package/src/components/charts/RankBars.vue +72 -0
- package/src/components/charts/Sparkline.vue +90 -0
- package/src/components/charts/StatCard.vue +84 -0
- package/src/components/charts/core/data.ts +95 -0
- package/src/components/charts/core/format.ts +64 -0
- package/src/components/charts/core/palette.ts +52 -0
- package/src/components/charts/core/uid.ts +6 -0
- package/src/components/charts/core/useChartAnim.ts +60 -0
- package/src/components/charts/core/useChartFrame.ts +49 -0
- package/src/components/charts/core/useScale.ts +39 -0
- package/src/components/charts/index.ts +12 -0
- package/src/components/form/inputs/RadioGroup.vue +2 -1
- package/src/components/form/inputs/RangeInput.vue +43 -15
- package/src/components/form/inputs/SelectInput.vue +1 -19
- package/src/components/index.ts +3 -1
- package/src/components/layout/AppSidebar.vue +1 -0
- package/src/components/layout/Divider.vue +2 -9
- package/src/components/layout/SidebarNavItem.vue +82 -41
- package/src/components/layout/Timeline.types.ts +9 -0
- package/src/components/layout/Timeline.vue +54 -0
- package/src/components/layout/TimelineItem.vue +93 -0
- package/src/components/layout/index.ts +3 -0
- package/src/dialog/Dialog.vue +29 -1
- package/src/styles/bagel.css +1 -0
- package/src/styles/dark.css +13 -0
- package/src/styles/gradients.css +181 -0
- package/src/styles/layout.css +9 -0
- package/src/styles/text.css +38 -6
- package/src/styles/theme.css +1 -1
- package/dist/components/analytics/BarChart.vue.d.ts +0 -47
- package/dist/components/analytics/BarChart.vue.d.ts.map +0 -1
- package/dist/components/analytics/KpiCard.vue.d.ts +0 -24
- package/dist/components/analytics/KpiCard.vue.d.ts.map +0 -1
- package/dist/components/analytics/LineChart.vue.d.ts +0 -35
- package/dist/components/analytics/LineChart.vue.d.ts.map +0 -1
- package/dist/components/analytics/PieChart.vue.d.ts +0 -53
- package/dist/components/analytics/PieChart.vue.d.ts.map +0 -1
- package/dist/components/analytics/index.d.ts +0 -5
- package/dist/components/analytics/index.d.ts.map +0 -1
- package/src/components/analytics/BarChart.vue +0 -262
- package/src/components/analytics/KpiCard.vue +0 -84
- package/src/components/analytics/LineChart.vue +0 -357
- package/src/components/analytics/PieChart.vue +0 -544
- package/src/components/analytics/index.ts +0 -4
|
@@ -13,8 +13,10 @@ interface LinkWithAction extends NavLink {
|
|
|
13
13
|
|
|
14
14
|
const props = withDefaults(defineProps<{
|
|
15
15
|
link: LinkWithAction
|
|
16
|
-
/** sidebar is expanded (true
|
|
16
|
+
/** sidebar is visually expanded (stays true during the collapse transition) */
|
|
17
17
|
open: boolean
|
|
18
|
+
/** real open state — flips immediately when the user toggles the sidebar */
|
|
19
|
+
realOpen?: boolean
|
|
18
20
|
isMobile: boolean
|
|
19
21
|
bgColor: string
|
|
20
22
|
textColor: string
|
|
@@ -45,6 +47,16 @@ const isActive = computed(() => selfActive.value || (hasChildren.value && childA
|
|
|
45
47
|
const expanded = ref(childActive.value)
|
|
46
48
|
watch(childActive, v => { if (v) expanded.value = true })
|
|
47
49
|
|
|
50
|
+
// Collapse the open child group smoothly *before* the layout switches to the
|
|
51
|
+
// icon-only flyout, so parents don't jump up when the sidebar narrows. We watch
|
|
52
|
+
// `realOpen` (which flips immediately on toggle) rather than `open` (which stays
|
|
53
|
+
// true throughout the width transition), so the grid-rows collapse animation runs
|
|
54
|
+
// in sync with the sidebar narrowing instead of snapping at the end.
|
|
55
|
+
watch(() => props.realOpen, (isOpenNow) => {
|
|
56
|
+
if (isOpenNow) expanded.value = childActive.value
|
|
57
|
+
else expanded.value = false
|
|
58
|
+
})
|
|
59
|
+
|
|
48
60
|
function onParentClick() {
|
|
49
61
|
if (hasChildren.value) expanded.value = !expanded.value
|
|
50
62
|
else props.link.action?.()
|
|
@@ -71,11 +83,10 @@ function onParentClick() {
|
|
|
71
83
|
<!-- PARENT with children, EXPANDED sidebar -->
|
|
72
84
|
<div v-else-if="open || isMobile" class="sidebar-group w100p">
|
|
73
85
|
<Btn
|
|
74
|
-
fullWidth alignTxt="start" class="flex-shrink-0 px-075 sidebar-group-toggle"
|
|
75
|
-
:class="{ '
|
|
86
|
+
fullWidth alignTxt="start" flat class="flex-shrink-0 px-075 sidebar-group-toggle txt-start sidebar-parent"
|
|
87
|
+
:class="{ 'sidebar-parent-active bold': isActive }"
|
|
76
88
|
:style="{
|
|
77
|
-
|
|
78
|
-
color: isActive ? 'white' : textColor,
|
|
89
|
+
color: isActive ? activeColor : textColor,
|
|
79
90
|
}"
|
|
80
91
|
@click="onParentClick"
|
|
81
92
|
>
|
|
@@ -84,19 +95,22 @@ function onParentClick() {
|
|
|
84
95
|
<Icon name="expand_more" size="1.1" class="nav-text sidebar-chevron" :class="{ 'sidebar-chevron-open': expanded }" />
|
|
85
96
|
</Btn>
|
|
86
97
|
<div class="sidebar-children" :class="{ 'sidebar-children-open': expanded }">
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
<div class="sidebar-children-inner ps-1">
|
|
99
|
+
<Btn
|
|
100
|
+
v-for="child in link.children" :key="child.to || child.label"
|
|
101
|
+
fullWidth alignTxt="start" class="flex-shrink-0 ps-075 pe-075 sidebar-child"
|
|
102
|
+
:class="{ 'bold sidebar-child-active': linkActive(child) }"
|
|
103
|
+
:style="{
|
|
104
|
+
color: linkActive(child) ? activeColor : textColor,
|
|
105
|
+
'--sidebar-active-color': activeColor,
|
|
106
|
+
}"
|
|
107
|
+
flat
|
|
108
|
+
:to="child.to || '/'" @click="child.action"
|
|
109
|
+
>
|
|
110
|
+
<Icon v-if="child.icon" :name="child.icon" size="1.05" class="opacity-7" />
|
|
111
|
+
<span class="nav-text">{{ resolveI18n(child.label) }}</span>
|
|
112
|
+
</Btn>
|
|
113
|
+
</div>
|
|
100
114
|
</div>
|
|
101
115
|
</div>
|
|
102
116
|
|
|
@@ -105,6 +119,7 @@ function onParentClick() {
|
|
|
105
119
|
v-else
|
|
106
120
|
placement="right-start"
|
|
107
121
|
:triggers="['hover']"
|
|
122
|
+
:delay="{ show: 0, hide: 200 }"
|
|
108
123
|
card
|
|
109
124
|
class="w100p sidebar-flyout-trigger"
|
|
110
125
|
>
|
|
@@ -122,15 +137,15 @@ function onParentClick() {
|
|
|
122
137
|
</Btn>
|
|
123
138
|
</template>
|
|
124
139
|
<!-- flyout content -->
|
|
125
|
-
<div class="sidebar-flyout p-05">
|
|
126
|
-
<p class="sidebar-flyout-label">{{ resolveI18n(link.label) }}</p>
|
|
140
|
+
<div class="sidebar-flyout p-05 min-w-180px display-flex gap-025 column">
|
|
141
|
+
<p class="sidebar-flyout-label m-0 semi opacity-5 uppercase">{{ resolveI18n(link.label) }}</p>
|
|
127
142
|
<Btn
|
|
128
143
|
v-for="child in link.children" :key="child.to || child.label"
|
|
129
|
-
fullWidth alignTxt="start" thin class="flex-shrink-0 px-075 sidebar-
|
|
130
|
-
:class="{ '
|
|
144
|
+
fullWidth alignTxt="start" thin flat class="flex-shrink-0 px-075 radius-1 sidebar-child"
|
|
145
|
+
:class="{ 'bold sidebar-child-active': linkActive(child) }"
|
|
131
146
|
:style="{
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
color: linkActive(child) ? activeColor : 'var(--bgl-text-color)',
|
|
148
|
+
'--sidebar-active-color': activeColor,
|
|
134
149
|
}"
|
|
135
150
|
:to="child.to || '/'" @click="child.action"
|
|
136
151
|
>
|
|
@@ -148,39 +163,65 @@ function onParentClick() {
|
|
|
148
163
|
opacity: 0.6;
|
|
149
164
|
}
|
|
150
165
|
.sidebar-chevron-open {
|
|
151
|
-
transform: rotate(180deg);
|
|
166
|
+
transform: rotate(180deg)!important;
|
|
152
167
|
}
|
|
153
168
|
.sidebar-children {
|
|
154
169
|
display: grid;
|
|
155
170
|
grid-template-rows: 0fr;
|
|
156
|
-
transition: grid-template-rows 0.
|
|
171
|
+
transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
157
172
|
overflow: hidden;
|
|
158
173
|
}
|
|
159
174
|
.sidebar-children-open {
|
|
160
175
|
grid-template-rows: 1fr;
|
|
161
176
|
}
|
|
162
|
-
.sidebar-children
|
|
177
|
+
.sidebar-children-inner {
|
|
163
178
|
min-height: 0;
|
|
164
|
-
}
|
|
165
|
-
.sidebar-child {
|
|
166
|
-
font-size: 0.85rem;
|
|
167
|
-
}
|
|
168
|
-
.sidebar-flyout {
|
|
169
|
-
min-width: 180px;
|
|
170
179
|
display: flex;
|
|
171
180
|
flex-direction: column;
|
|
172
|
-
gap:
|
|
181
|
+
gap: 0.125rem;
|
|
173
182
|
}
|
|
183
|
+
.sidebar-children-open .sidebar-children-inner {
|
|
184
|
+
padding-block: 0.25rem;
|
|
185
|
+
}
|
|
186
|
+
/* Parent toggle — flat <Btn> with no color prop picks up a default `pair-black`
|
|
187
|
+
background; neutralize it so the active parent shows only colored text/icon. */
|
|
188
|
+
.sidebar-parent.bgl_btn {
|
|
189
|
+
background-color: transparent !important;
|
|
190
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
191
|
+
}
|
|
192
|
+
.sidebar-parent.bgl_btn:hover {
|
|
193
|
+
background-color: color-mix(in srgb, currentColor 8%, transparent) !important;
|
|
194
|
+
filter: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
174
197
|
.sidebar-flyout-label {
|
|
175
|
-
margin: 0;
|
|
176
198
|
padding: 0.25rem 0.5rem 0.375rem;
|
|
177
199
|
font-size: 0.6875rem;
|
|
178
|
-
font-weight: 600;
|
|
179
|
-
text-transform: uppercase;
|
|
180
|
-
letter-spacing: 0.05em;
|
|
181
|
-
opacity: 0.45;
|
|
182
200
|
}
|
|
183
|
-
|
|
184
|
-
|
|
201
|
+
</style>
|
|
202
|
+
|
|
203
|
+
<!-- Global (un-scoped) so the same child styling also applies inside the flyout,
|
|
204
|
+
which is teleported to <body> and therefore outside this component's scope. -->
|
|
205
|
+
<style>
|
|
206
|
+
.sidebar-child {
|
|
207
|
+
font-size: 0.85rem;
|
|
208
|
+
position: relative;
|
|
209
|
+
transition: background-color 0.15s ease, color 0.15s ease, font-weight 0.15s ease;
|
|
210
|
+
}
|
|
211
|
+
/* Neutralize the default `pair-black` background a flat <Btn> picks up when no
|
|
212
|
+
color prop is set, so we can control the child background ourselves. */
|
|
213
|
+
.sidebar-child.bgl_btn {
|
|
214
|
+
background-color: transparent !important;
|
|
215
|
+
}
|
|
216
|
+
/* Active child — filled with a subtle tint of the active color. */
|
|
217
|
+
.sidebar-child.bgl_btn.sidebar-child-active,
|
|
218
|
+
.sidebar-child.bgl_btn.sidebar-child-active:hover {
|
|
219
|
+
background-color: color-mix(in srgb, var(--sidebar-active-color) 14%, transparent) !important;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Hover indication for children (mirrors the parent feel) */
|
|
223
|
+
.sidebar-child.bgl_btn:hover {
|
|
224
|
+
background-color: color-mix(in srgb, currentColor 12%, transparent) !important;
|
|
225
|
+
filter: none;
|
|
185
226
|
}
|
|
186
227
|
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
defineOptions({ name: 'BglTimeline' })
|
|
3
|
+
import { useSlots } from 'vue'
|
|
4
|
+
import type { TimelineEntry } from './Timeline.types'
|
|
5
|
+
import TimelineItem from './TimelineItem.vue'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Vertical activity / history feed. Two ways to use it:
|
|
9
|
+
*
|
|
10
|
+
* 1. Data-driven — pass `items`; each maps to a <TimelineItem>. Use the default
|
|
11
|
+
* scoped slot to render custom body content per entry:
|
|
12
|
+
* <Timeline :items="activity">
|
|
13
|
+
* <template #default="{ item }">{{ item.title }}</template>
|
|
14
|
+
* </Timeline>
|
|
15
|
+
*
|
|
16
|
+
* 2. Composition — drop <TimelineItem> children directly (omit `items`). Mark the
|
|
17
|
+
* last one with `last` to drop its connector, or let data-driven mode do it.
|
|
18
|
+
*/
|
|
19
|
+
const { items = [] } = defineProps<{
|
|
20
|
+
items?: TimelineEntry[]
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const slots = useSlots()
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div class="bgl-timeline grid">
|
|
28
|
+
<template v-if="items.length">
|
|
29
|
+
<TimelineItem
|
|
30
|
+
v-for="(item, i) in items"
|
|
31
|
+
:key="i"
|
|
32
|
+
:title="item.title"
|
|
33
|
+
:meta="item.meta"
|
|
34
|
+
:icon="item.icon"
|
|
35
|
+
:color="item.color"
|
|
36
|
+
:last="i === items.length - 1"
|
|
37
|
+
>
|
|
38
|
+
<template v-if="slots.default" #default>
|
|
39
|
+
<slot :item="item" :index="i" />
|
|
40
|
+
</template>
|
|
41
|
+
<template v-if="slots.meta" #meta>
|
|
42
|
+
<slot name="meta" :item="item" :index="i" />
|
|
43
|
+
</template>
|
|
44
|
+
</TimelineItem>
|
|
45
|
+
</template>
|
|
46
|
+
<slot v-else name="items" />
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<style scoped>
|
|
51
|
+
.bgl-timeline {
|
|
52
|
+
align-content: start;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
defineOptions({ name: 'BglTimelineItem' })
|
|
3
|
+
import type { IconType, ThemeType } from '@bagelink/vue'
|
|
4
|
+
import { Icon } from '@bagelink/vue'
|
|
5
|
+
import { useSlots } from 'vue'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One node in a <Timeline>: a marker (icon dot or plain dot) with a connecting
|
|
9
|
+
* line, a title, optional meta line, and free content via the default slot.
|
|
10
|
+
* - `icon` renders inside the dot; otherwise a small solid dot is shown.
|
|
11
|
+
* - `color` themes the dot (and icon).
|
|
12
|
+
* - `last` removes the connector line (auto-handled by <Timeline>).
|
|
13
|
+
* - Slots: `#marker` (replace the dot), `#meta`, default (body).
|
|
14
|
+
*/
|
|
15
|
+
const {
|
|
16
|
+
title = '',
|
|
17
|
+
meta = '',
|
|
18
|
+
icon,
|
|
19
|
+
color = 'primary',
|
|
20
|
+
last = false,
|
|
21
|
+
} = defineProps<{
|
|
22
|
+
title?: string
|
|
23
|
+
meta?: string
|
|
24
|
+
icon?: IconType
|
|
25
|
+
color?: ThemeType
|
|
26
|
+
last?: boolean
|
|
27
|
+
}>()
|
|
28
|
+
|
|
29
|
+
const slots = useSlots()
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="bgl-tl-item flex gap-075" :class="{ 'bgl-tl-item--last': last }">
|
|
34
|
+
<div class="bgl-tl-rail flex-column align-items-center flex-shrink-0">
|
|
35
|
+
<span class="bgl-tl-marker flex-center" :style="{ '--tl-color': `var(--bgl-${color})` }">
|
|
36
|
+
<slot name="marker">
|
|
37
|
+
<Icon v-if="icon" :icon="icon" :size="0.85" />
|
|
38
|
+
<span v-else class="bgl-tl-dot" />
|
|
39
|
+
</slot>
|
|
40
|
+
</span>
|
|
41
|
+
<span v-if="!last" class="bgl-tl-line" />
|
|
42
|
+
</div>
|
|
43
|
+
<div class="bgl-tl-body min-w-0">
|
|
44
|
+
<p v-if="title || slots.title" class="bgl-tl-title m-0"><slot name="title">{{ title }}</slot></p>
|
|
45
|
+
<div v-if="slots.default" class="bgl-tl-content"><slot /></div>
|
|
46
|
+
<p v-if="meta || slots.meta" class="bgl-tl-meta m-0"><slot name="meta">{{ meta }}</slot></p>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<style scoped>
|
|
52
|
+
.bgl-tl-item {
|
|
53
|
+
min-height: 1.75rem;
|
|
54
|
+
}
|
|
55
|
+
.bgl-tl-marker {
|
|
56
|
+
width: 1.625rem;
|
|
57
|
+
height: 1.625rem;
|
|
58
|
+
border-radius: 50%;
|
|
59
|
+
background: color-mix(in srgb, var(--tl-color) 15%, transparent);
|
|
60
|
+
color: var(--tl-color);
|
|
61
|
+
}
|
|
62
|
+
.bgl-tl-dot {
|
|
63
|
+
width: 0.5rem;
|
|
64
|
+
height: 0.5rem;
|
|
65
|
+
border-radius: 50%;
|
|
66
|
+
background: var(--tl-color);
|
|
67
|
+
}
|
|
68
|
+
.bgl-tl-line {
|
|
69
|
+
width: 2px;
|
|
70
|
+
flex: 1;
|
|
71
|
+
background: var(--bgl-border-color);
|
|
72
|
+
margin: 2px 0;
|
|
73
|
+
}
|
|
74
|
+
.bgl-tl-body {
|
|
75
|
+
padding-bottom: 1rem;
|
|
76
|
+
}
|
|
77
|
+
.bgl-tl-item--last .bgl-tl-body {
|
|
78
|
+
padding-bottom: 0;
|
|
79
|
+
}
|
|
80
|
+
.bgl-tl-title {
|
|
81
|
+
font-size: 0.875rem;
|
|
82
|
+
line-height: 1.3;
|
|
83
|
+
}
|
|
84
|
+
.bgl-tl-meta {
|
|
85
|
+
font-size: 0.6875rem;
|
|
86
|
+
color: var(--bgl-text-soft, var(--bgl-gray));
|
|
87
|
+
margin-top: 0.125rem;
|
|
88
|
+
}
|
|
89
|
+
.bgl-tl-content {
|
|
90
|
+
font-size: 0.8125rem;
|
|
91
|
+
color: var(--bgl-text-soft, var(--bgl-gray));
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
@@ -12,5 +12,8 @@ export { default as SidebarMenu } from './SidebarMenu.vue'
|
|
|
12
12
|
export { default as Skeleton } from './Skeleton.vue'
|
|
13
13
|
export { default as TabbedLayout } from './TabbedLayout.vue'
|
|
14
14
|
export { default as Tabs } from './Tabs.vue'
|
|
15
|
+
export { default as Timeline } from './Timeline.vue'
|
|
16
|
+
export type { TimelineEntry } from './Timeline.types'
|
|
17
|
+
export { default as TimelineItem } from './TimelineItem.vue'
|
|
15
18
|
export { default as TabsBody } from './TabsBody.vue'
|
|
16
19
|
export { default as TabsNav } from './TabsNav.vue'
|
package/src/dialog/Dialog.vue
CHANGED
|
@@ -19,6 +19,9 @@ const props = withDefaults(defineProps<{
|
|
|
19
19
|
centerTitle?: boolean
|
|
20
20
|
ellipsisTitle?: boolean
|
|
21
21
|
closePlacement?: 'header-end' | 'header-start' | 'overlay-start' | 'overlay-end' | 'none'
|
|
22
|
+
/** Show a drag-handle grip at the top — the mobile bottom-sheet affordance.
|
|
23
|
+
* Defaults to true when position="bottom". Set false to opt out. */
|
|
24
|
+
grip?: boolean
|
|
22
25
|
}>(), {
|
|
23
26
|
width: 'm',
|
|
24
27
|
position: 'center',
|
|
@@ -27,9 +30,13 @@ const props = withDefaults(defineProps<{
|
|
|
27
30
|
thin: false,
|
|
28
31
|
centerTitle: false,
|
|
29
32
|
ellipsisTitle: false,
|
|
30
|
-
closePlacement: 'header-end'
|
|
33
|
+
closePlacement: 'header-end',
|
|
34
|
+
grip: undefined,
|
|
31
35
|
})
|
|
32
36
|
|
|
37
|
+
// Grip is the native bottom-sheet handle; default it on for bottom-positioned dialogs.
|
|
38
|
+
const showGrip = computed(() => props.grip ?? props.position === 'bottom')
|
|
39
|
+
|
|
33
40
|
const emit = defineEmits<{
|
|
34
41
|
'update:open': [value: boolean]
|
|
35
42
|
'close': []
|
|
@@ -114,6 +121,8 @@ defineExpose({ close })
|
|
|
114
121
|
@cancel="onCancel" @animationend="onAnimationEnd"
|
|
115
122
|
>
|
|
116
123
|
<div class="grid grid-dialog max-height-100-2" @click.stop>
|
|
124
|
+
<!-- Bottom-sheet grip handle -->
|
|
125
|
+
<div v-if="showGrip" class="dialog-grip" aria-hidden="true" />
|
|
117
126
|
<!-- Header -->
|
|
118
127
|
<header
|
|
119
128
|
v-if="title || dismissable" class="flex space-between flex-shrink-0 align-items-start gap-05"
|
|
@@ -261,6 +270,25 @@ dialog.dialog-bottom.is-closing {
|
|
|
261
270
|
animation: slide-out-bottom 0.2s ease-in forwards;
|
|
262
271
|
}
|
|
263
272
|
|
|
273
|
+
/* Bottom sheet: round only the top corners + full-width on mobile. */
|
|
274
|
+
dialog.dialog-bottom {
|
|
275
|
+
border-end-end-radius: 0;
|
|
276
|
+
border-end-start-radius: 0;
|
|
277
|
+
}
|
|
278
|
+
@media (max-width: 910px) {
|
|
279
|
+
dialog.dialog-bottom { width: 100%; max-width: 100%; margin-inline: 0; }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Drag-handle grip for bottom sheets. */
|
|
283
|
+
.dialog-grip {
|
|
284
|
+
width: 40px;
|
|
285
|
+
height: 4px;
|
|
286
|
+
border-radius: 100px;
|
|
287
|
+
background: var(--bgl-border-color);
|
|
288
|
+
margin: 8px auto 4px;
|
|
289
|
+
flex-shrink: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
264
292
|
/* Keyframes - shared and reusable */
|
|
265
293
|
@keyframes fade-in {
|
|
266
294
|
from {
|
package/src/styles/bagel.css
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
@import "colors.css";
|
|
34
34
|
@import "mobileColors.css";
|
|
35
35
|
@import "appearance.css";
|
|
36
|
+
@import "gradients.css";
|
|
36
37
|
|
|
37
38
|
/* Icon font-family bindings — mirrored from Icon.vue so icons work even when
|
|
38
39
|
that component's injected <style> is not yet present (e.g. Vite dev mode). */
|
package/src/styles/dark.css
CHANGED
|
@@ -80,6 +80,10 @@
|
|
|
80
80
|
--bgl-skeleton-bg: var(--bgl-dm-surface-2);
|
|
81
81
|
--bgl-skeleton-pulse: var(--bgl-dm-surface);
|
|
82
82
|
--bgl-gray-light: var(--bgl-dm-surface-2);
|
|
83
|
+
/* --bgl-gray (#b7b7b7 in light) is used directly by color-gray / bg-gray and
|
|
84
|
+
as a neutral mid tone. On dark, keep it a readable mid-gray so color-gray
|
|
85
|
+
text stays legible; bg-gray fills get a dedicated darker treatment below. */
|
|
86
|
+
--bgl-gray: #8b8f96;
|
|
83
87
|
|
|
84
88
|
/* ---- Text ------------------------------------------------------------- */
|
|
85
89
|
--bgl-text-color: var(--bgl-dm-text);
|
|
@@ -269,6 +273,15 @@
|
|
|
269
273
|
color: var(--bgl-dm-text);
|
|
270
274
|
}
|
|
271
275
|
|
|
276
|
+
/* bg-gray is a solid light-gray (#b7b7b7) fill in light mode — used as a
|
|
277
|
+
neutral surface. On dark it becomes a bright patch, so remap it to a raised
|
|
278
|
+
dark surface (one step up from the card) like the other neutral fills. */
|
|
279
|
+
.bgl-dark-mode .bg-gray:not(.keep-white) {
|
|
280
|
+
background: var(--bgl-dm-surface-2) !important;
|
|
281
|
+
--alpha-color: var(--bgl-dm-surface-2);
|
|
282
|
+
color: var(--bgl-dm-text);
|
|
283
|
+
}
|
|
284
|
+
|
|
272
285
|
.bgl-dark-mode .bg-input-white:not(.keep-white) input,
|
|
273
286
|
.bgl-dark-mode .bg-input-white:not(.keep-white) textarea,
|
|
274
287
|
.bgl-dark-mode .bg-input-white:not(.keep-white) .selectinput-btn,
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
* GRADIENTS — a small, memorable system that mirrors the color utilities.
|
|
3
|
+
*
|
|
4
|
+
* <div class="bg-gradient from-purple to-blue">
|
|
5
|
+
* <div class="bg-gradient to-tr from-pink to-orange">
|
|
6
|
+
* <div class="bg-gradient from-primary via-purple to-turquoise">
|
|
7
|
+
* <div class="text-gradient from-blue to-purple">Gorgeous</div>
|
|
8
|
+
* <div class="bg-gradient-brand"> (named presets)
|
|
9
|
+
*
|
|
10
|
+
* How it works:
|
|
11
|
+
* - `from-<tone>` / `via-<tone>` / `to-<tone>` set --bgl-grad-from/via/to.
|
|
12
|
+
* - `bg-gradient` paints them as a linear gradient (default 135°).
|
|
13
|
+
* - direction modifiers (to-t/-b/-l/-r/-tl/-tr/-bl/-br) override the angle.
|
|
14
|
+
* - `gradient-radial` / `gradient-conic` swap the gradient type.
|
|
15
|
+
* - `text-gradient` clips the gradient to text.
|
|
16
|
+
* Tokens resolve through var(--bgl-<tone>) so everything is theme + dark aware.
|
|
17
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
18
|
+
|
|
19
|
+
:root {
|
|
20
|
+
--bgl-grad-angle: 135deg;
|
|
21
|
+
--bgl-grad-from: transparent;
|
|
22
|
+
--bgl-grad-via: ;
|
|
23
|
+
--bgl-grad-to: transparent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ── Paint ─────────────────────────────────────────────────────────────────── */
|
|
27
|
+
/* The stop list collapses to 2 stops when --bgl-grad-via is empty, 3 when set. */
|
|
28
|
+
.bg-gradient {
|
|
29
|
+
background-image: linear-gradient(
|
|
30
|
+
var(--bgl-grad-angle),
|
|
31
|
+
var(--bgl-grad-from),
|
|
32
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to)
|
|
33
|
+
) !important;
|
|
34
|
+
}
|
|
35
|
+
.gradient-radial.bg-gradient,
|
|
36
|
+
.bg-gradient.gradient-radial {
|
|
37
|
+
background-image: radial-gradient(
|
|
38
|
+
circle at var(--bgl-grad-pos, center),
|
|
39
|
+
var(--bgl-grad-from),
|
|
40
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to)
|
|
41
|
+
) !important;
|
|
42
|
+
}
|
|
43
|
+
.gradient-conic.bg-gradient,
|
|
44
|
+
.bg-gradient.gradient-conic {
|
|
45
|
+
background-image: conic-gradient(
|
|
46
|
+
from var(--bgl-grad-angle),
|
|
47
|
+
var(--bgl-grad-from),
|
|
48
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to)
|
|
49
|
+
) !important;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ── Direction modifiers ───────────────────────────────────────────────────── */
|
|
53
|
+
.to-t { --bgl-grad-angle: to top; }
|
|
54
|
+
.to-b { --bgl-grad-angle: to bottom; }
|
|
55
|
+
.to-l { --bgl-grad-angle: to left; }
|
|
56
|
+
.to-r { --bgl-grad-angle: to right; }
|
|
57
|
+
.to-tl { --bgl-grad-angle: to top left; }
|
|
58
|
+
.to-tr { --bgl-grad-angle: to top right; }
|
|
59
|
+
.to-bl { --bgl-grad-angle: to bottom left; }
|
|
60
|
+
.to-br { --bgl-grad-angle: to bottom right; }
|
|
61
|
+
|
|
62
|
+
/* ── Text gradient ─────────────────────────────────────────────────────────── */
|
|
63
|
+
.text-gradient {
|
|
64
|
+
background-image: linear-gradient(
|
|
65
|
+
var(--bgl-grad-angle),
|
|
66
|
+
var(--bgl-grad-from),
|
|
67
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to)
|
|
68
|
+
);
|
|
69
|
+
background-clip: text;
|
|
70
|
+
-webkit-background-clip: text;
|
|
71
|
+
color: transparent;
|
|
72
|
+
-webkit-text-fill-color: transparent;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ── Stop utilities (generated per tone) ───────────────────────────────────── */
|
|
76
|
+
/* Base tones */
|
|
77
|
+
.from-primary { --bgl-grad-from: var(--bgl-primary); }
|
|
78
|
+
.from-blue { --bgl-grad-from: var(--bgl-blue); }
|
|
79
|
+
.from-green { --bgl-grad-from: var(--bgl-green); }
|
|
80
|
+
.from-red { --bgl-grad-from: var(--bgl-red); }
|
|
81
|
+
.from-yellow { --bgl-grad-from: var(--bgl-yellow); }
|
|
82
|
+
.from-purple { --bgl-grad-from: var(--bgl-purple); }
|
|
83
|
+
.from-brown { --bgl-grad-from: var(--bgl-brown); }
|
|
84
|
+
.from-orange { --bgl-grad-from: var(--bgl-orange); }
|
|
85
|
+
.from-turquoise { --bgl-grad-from: var(--bgl-turquoise); }
|
|
86
|
+
.from-pink { --bgl-grad-from: var(--bgl-pink); }
|
|
87
|
+
.from-gray { --bgl-grad-from: var(--bgl-gray); }
|
|
88
|
+
.from-black { --bgl-grad-from: var(--bgl-black, #000); }
|
|
89
|
+
.from-white { --bgl-grad-from: var(--bgl-white, #fff); }
|
|
90
|
+
.from-bg { --bgl-grad-from: var(--bgl-bg); }
|
|
91
|
+
.from-box { --bgl-grad-from: var(--bgl-box-bg); }
|
|
92
|
+
.from-transparent { --bgl-grad-from: transparent; }
|
|
93
|
+
|
|
94
|
+
.via-primary { --bgl-grad-via: var(--bgl-primary), ; }
|
|
95
|
+
.via-blue { --bgl-grad-via: var(--bgl-blue), ; }
|
|
96
|
+
.via-green { --bgl-grad-via: var(--bgl-green), ; }
|
|
97
|
+
.via-red { --bgl-grad-via: var(--bgl-red), ; }
|
|
98
|
+
.via-yellow { --bgl-grad-via: var(--bgl-yellow), ; }
|
|
99
|
+
.via-purple { --bgl-grad-via: var(--bgl-purple), ; }
|
|
100
|
+
.via-brown { --bgl-grad-via: var(--bgl-brown), ; }
|
|
101
|
+
.via-orange { --bgl-grad-via: var(--bgl-orange), ; }
|
|
102
|
+
.via-turquoise { --bgl-grad-via: var(--bgl-turquoise), ; }
|
|
103
|
+
.via-pink { --bgl-grad-via: var(--bgl-pink), ; }
|
|
104
|
+
.via-gray { --bgl-grad-via: var(--bgl-gray), ; }
|
|
105
|
+
.via-transparent { --bgl-grad-via: transparent, ; }
|
|
106
|
+
|
|
107
|
+
.to-primary { --bgl-grad-to: var(--bgl-primary); }
|
|
108
|
+
.to-blue { --bgl-grad-to: var(--bgl-blue); }
|
|
109
|
+
.to-green { --bgl-grad-to: var(--bgl-green); }
|
|
110
|
+
.to-red { --bgl-grad-to: var(--bgl-red); }
|
|
111
|
+
.to-yellow { --bgl-grad-to: var(--bgl-yellow); }
|
|
112
|
+
.to-purple { --bgl-grad-to: var(--bgl-purple); }
|
|
113
|
+
.to-brown { --bgl-grad-to: var(--bgl-brown); }
|
|
114
|
+
.to-orange { --bgl-grad-to: var(--bgl-orange); }
|
|
115
|
+
.to-turquoise { --bgl-grad-to: var(--bgl-turquoise); }
|
|
116
|
+
.to-pink { --bgl-grad-to: var(--bgl-pink); }
|
|
117
|
+
.to-gray { --bgl-grad-to: var(--bgl-gray); }
|
|
118
|
+
.to-black { --bgl-grad-to: var(--bgl-black, #000); }
|
|
119
|
+
.to-white { --bgl-grad-to: var(--bgl-white, #fff); }
|
|
120
|
+
.to-bg { --bgl-grad-to: var(--bgl-bg); }
|
|
121
|
+
.to-box { --bgl-grad-to: var(--bgl-box-bg); }
|
|
122
|
+
.to-transparent { --bgl-grad-to: transparent; }
|
|
123
|
+
|
|
124
|
+
/* Common tint/light/dark stops (compose with from-/to-) */
|
|
125
|
+
.from-primary-tint { --bgl-grad-from: var(--bgl-primary-tint); }
|
|
126
|
+
.to-primary-tint { --bgl-grad-to: var(--bgl-primary-tint); }
|
|
127
|
+
.from-blue-tint { --bgl-grad-from: var(--bgl-blue-tint); }
|
|
128
|
+
.to-blue-tint { --bgl-grad-to: var(--bgl-blue-tint); }
|
|
129
|
+
|
|
130
|
+
/* ── Named presets — the gorgeous, copy-pasted brand sweeps, tokenized ──────── */
|
|
131
|
+
.bg-gradient-brand {
|
|
132
|
+
background-image: linear-gradient(135deg, var(--bgl-primary), var(--bgl-purple)) !important;
|
|
133
|
+
}
|
|
134
|
+
.bg-gradient-sunset {
|
|
135
|
+
background-image: linear-gradient(135deg, var(--bgl-orange), var(--bgl-pink)) !important;
|
|
136
|
+
}
|
|
137
|
+
.bg-gradient-ocean {
|
|
138
|
+
background-image: linear-gradient(135deg, var(--bgl-blue), var(--bgl-turquoise)) !important;
|
|
139
|
+
}
|
|
140
|
+
.bg-gradient-dusk {
|
|
141
|
+
background-image: linear-gradient(135deg, var(--bgl-purple), var(--bgl-blue)) !important;
|
|
142
|
+
}
|
|
143
|
+
.bg-gradient-citrus {
|
|
144
|
+
background-image: linear-gradient(135deg, var(--bgl-yellow), var(--bgl-orange)) !important;
|
|
145
|
+
}
|
|
146
|
+
/* Subtle surface sweep for cards/panels — dark-mode aware via tokens. */
|
|
147
|
+
.bg-gradient-surface {
|
|
148
|
+
background-image: linear-gradient(160deg,
|
|
149
|
+
color-mix(in srgb, var(--bgl-box-bg) 100%, transparent),
|
|
150
|
+
color-mix(in srgb, var(--bgl-primary) 6%, var(--bgl-box-bg))) !important;
|
|
151
|
+
}
|
|
152
|
+
/* Multi-radial "mesh" hero background built from the brand palette. */
|
|
153
|
+
.bg-gradient-mesh {
|
|
154
|
+
background-color: var(--bgl-box-bg) !important;
|
|
155
|
+
background-image:
|
|
156
|
+
radial-gradient(at 0% 0%, color-mix(in srgb, var(--bgl-primary) 35%, transparent) 0px, transparent 50%),
|
|
157
|
+
radial-gradient(at 100% 0%, color-mix(in srgb, var(--bgl-purple) 30%, transparent) 0px, transparent 50%),
|
|
158
|
+
radial-gradient(at 100% 100%, color-mix(in srgb, var(--bgl-turquoise) 25%, transparent) 0px, transparent 50%),
|
|
159
|
+
radial-gradient(at 0% 100%, color-mix(in srgb, var(--bgl-pink) 25%, transparent) 0px, transparent 50%) !important;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Soft fade for chart/sparkline fills: tone at top → transparent. Pair with a
|
|
163
|
+
from-<tone> and a direction, e.g. `bg-gradient-fade to-b from-blue`. */
|
|
164
|
+
.bg-gradient-fade {
|
|
165
|
+
--bgl-grad-to: transparent;
|
|
166
|
+
background-image: linear-gradient(var(--bgl-grad-angle),
|
|
167
|
+
color-mix(in srgb, var(--bgl-grad-from) 22%, transparent),
|
|
168
|
+
transparent) !important;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
172
|
+
.bg-gradient-animate {
|
|
173
|
+
background-size: 200% 200% !important;
|
|
174
|
+
animation: bgl-grad-pan 8s ease infinite;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
@keyframes bgl-grad-pan {
|
|
178
|
+
0% { background-position: 0% 50%; }
|
|
179
|
+
50% { background-position: 100% 50%; }
|
|
180
|
+
100% { background-position: 0% 50%; }
|
|
181
|
+
}
|
package/src/styles/layout.css
CHANGED
|
@@ -3949,6 +3949,15 @@
|
|
|
3949
3949
|
flex-shrink: 0;
|
|
3950
3950
|
}
|
|
3951
3951
|
|
|
3952
|
+
.min-w-0 {
|
|
3953
|
+
/* shrink below content size for ellipsis truncation in flex rows */
|
|
3954
|
+
min-width: 0;
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
.min-h-0 {
|
|
3958
|
+
min-height: 0;
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3952
3961
|
.flex-grow-2 {
|
|
3953
3962
|
flex-grow: 2;
|
|
3954
3963
|
}
|