@bagelink/vue 1.15.202 → 1.15.207
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/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +2 -2
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/charts/BarChart.vue.d.ts.map +1 -1
- package/dist/components/charts/ChartTooltip.vue.d.ts.map +1 -1
- package/dist/components/charts/Donut.vue.d.ts.map +1 -1
- package/dist/components/charts/Funnel.vue.d.ts.map +1 -1
- package/dist/components/charts/Gauge.vue.d.ts.map +1 -1
- package/dist/components/charts/LineChart.vue.d.ts.map +1 -1
- package/dist/components/charts/MapChart.vue.d.ts +25 -0
- package/dist/components/charts/MapChart.vue.d.ts.map +1 -0
- package/dist/components/charts/RadialBars.vue.d.ts.map +1 -1
- package/dist/components/charts/RankBars.vue.d.ts.map +1 -1
- package/dist/components/charts/StatCard.vue.d.ts.map +1 -1
- package/dist/components/charts/core/arc.d.ts +6 -0
- package/dist/components/charts/core/arc.d.ts.map +1 -0
- package/dist/components/charts/core/useChartTip.d.ts +13 -0
- package/dist/components/charts/core/useChartTip.d.ts.map +1 -0
- package/dist/components/charts/core/world-paths.d.ts +13 -0
- package/dist/components/charts/core/world-paths.d.ts.map +1 -0
- package/dist/components/charts/index.d.ts +2 -0
- package/dist/components/charts/index.d.ts.map +1 -1
- package/dist/components/charts/mapTypes.d.ts +7 -0
- package/dist/components/charts/mapTypes.d.ts.map +1 -0
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +3 -0
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +0 -1
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/form-flow/FormFlowPlayer.vue.d.ts +18 -0
- package/dist/form-flow/FormFlowPlayer.vue.d.ts.map +1 -1
- package/dist/form-flow/structure.d.ts +12 -0
- package/dist/form-flow/structure.d.ts.map +1 -1
- package/dist/index.cjs +177 -154
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +37519 -42750
- package/dist/style.css +1 -2
- package/dist/utils/BagelFormUtils.d.ts +111 -0
- package/dist/utils/BagelFormUtils.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/charts/BarChart.vue +15 -15
- package/src/components/charts/ChartTooltip.vue +7 -6
- package/src/components/charts/Donut.vue +25 -40
- package/src/components/charts/Funnel.vue +10 -12
- package/src/components/charts/Gauge.vue +4 -2
- package/src/components/charts/LineChart.vue +15 -17
- package/src/components/charts/MapChart.vue +198 -0
- package/src/components/charts/RadialBars.vue +3 -1
- package/src/components/charts/RankBars.vue +2 -0
- package/src/components/charts/Sparkline.vue +1 -1
- package/src/components/charts/StatCard.vue +2 -0
- package/src/components/charts/core/arc.ts +53 -0
- package/src/components/charts/core/useChartTip.ts +35 -0
- package/src/components/charts/core/world-paths.ts +351 -0
- package/src/components/charts/index.ts +3 -0
- package/src/components/charts/mapTypes.ts +6 -0
- package/src/components/dataTable/useTableVirtualization.ts +5 -5
- package/src/components/form/inputs/DateInput.vue +9 -1
- package/src/components/form/inputs/NumberInput.vue +17 -6
- package/src/components/form/inputs/index.ts +0 -1
- package/src/components/index.ts +2 -2
- package/src/form-flow/FormFlowPlayer.vue +49 -0
- package/src/form-flow/structure.ts +12 -0
- package/src/index.ts +1 -0
- package/src/utils/BagelFormUtils.ts +341 -0
- package/dist/components/dataTable/useTableVirtualization.d.ts +0 -25
- package/dist/components/dataTable/useTableVirtualization.d.ts.map +0 -1
- package/dist/components/form/inputs/JSONInput.vue.d.ts +0 -21
- package/dist/components/form/inputs/JSONInput.vue.d.ts.map +0 -1
- package/src/components/form/inputs/JSONInput.vue +0 -60
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* MapChart — SVG choropleth of countries (ISO-3166-1 alpha-2). Pure SVG, no
|
|
4
|
+
* tiles. Same “plot only / wrap in Card” contract as BarChart / Donut.
|
|
5
|
+
*
|
|
6
|
+
* <MapChart :data="[{ id: 'US', value: 4200 }, { id: 'IL', label: 'Israel', value: 900 }]" />
|
|
7
|
+
* <MapChart :data="rows" color="green" :height="280" />
|
|
8
|
+
*/
|
|
9
|
+
import { computed, ref } from 'vue'
|
|
10
|
+
import type { ThemeType } from '../../types'
|
|
11
|
+
import ChartTooltip from './ChartTooltip.vue'
|
|
12
|
+
import { formatValue, type ValueFormat } from './core/format'
|
|
13
|
+
import { resolveColor } from './core/palette'
|
|
14
|
+
import { useChartAnim } from './core/useChartAnim'
|
|
15
|
+
import { tipFollowStyle, trackLocalPointer } from './core/useChartTip'
|
|
16
|
+
import { WORLD_NAMES, WORLD_PATHS, WORLD_VIEWBOX } from './core/world-paths'
|
|
17
|
+
import type { MapDatum } from './mapTypes'
|
|
18
|
+
|
|
19
|
+
defineOptions({ name: 'BglMapChart' })
|
|
20
|
+
|
|
21
|
+
export type { MapDatum }
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<{
|
|
24
|
+
data: MapDatum[]
|
|
25
|
+
/** Base tone for the value ramp (low → high intensity). */
|
|
26
|
+
color?: ThemeType | string
|
|
27
|
+
height?: number
|
|
28
|
+
/** Show min→max legend strip under the map. */
|
|
29
|
+
legend?: boolean
|
|
30
|
+
currency?: string
|
|
31
|
+
prefix?: string
|
|
32
|
+
suffix?: string
|
|
33
|
+
animated?: boolean
|
|
34
|
+
}>(), {
|
|
35
|
+
color: 'primary',
|
|
36
|
+
height: 280,
|
|
37
|
+
legend: true,
|
|
38
|
+
animated: true,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const el = ref<HTMLElement>()
|
|
42
|
+
const { progress } = useChartAnim({ el, enabled: props.animated, duration: 700 })
|
|
43
|
+
|
|
44
|
+
const fmt = computed<ValueFormat>(() => ({
|
|
45
|
+
currency: props.currency,
|
|
46
|
+
prefix: props.prefix,
|
|
47
|
+
suffix: props.suffix,
|
|
48
|
+
compact: true,
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
const byId = computed(() => {
|
|
52
|
+
const map = new Map<string, MapDatum>()
|
|
53
|
+
for (const d of props.data) {
|
|
54
|
+
const id = d.id?.toUpperCase?.() ?? ''
|
|
55
|
+
if (!id) continue
|
|
56
|
+
const prev = map.get(id)
|
|
57
|
+
map.set(id, prev
|
|
58
|
+
? { ...d, id, value: prev.value + d.value, label: d.label ?? prev.label }
|
|
59
|
+
: { ...d, id })
|
|
60
|
+
}
|
|
61
|
+
return map
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const maxValue = computed(() => Math.max(0, ...[...byId.value.values()].map(d => d.value)))
|
|
65
|
+
|
|
66
|
+
const baseColor = computed(() => resolveColor(props.color))
|
|
67
|
+
const emptyFill = 'color-mix(in srgb, var(--bgl-gray) 12%, var(--bgl-box-bg))'
|
|
68
|
+
const strokeColor = 'color-mix(in srgb, var(--bgl-gray) 28%, var(--bgl-box-bg))'
|
|
69
|
+
|
|
70
|
+
/** Continuous ramp: empty → light tint → full base color by value share. */
|
|
71
|
+
function fillFor(value: number | undefined): string {
|
|
72
|
+
if (value == null || value <= 0 || maxValue.value <= 0) return emptyFill
|
|
73
|
+
const t = Math.sqrt(value / maxValue.value) // sqrt so mid values stay visible
|
|
74
|
+
const pct = 28 + t * 72 * progress.value
|
|
75
|
+
return `color-mix(in srgb, ${baseColor.value} ${pct}%, var(--bgl-box-bg))`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface CountryPath {
|
|
79
|
+
id: string
|
|
80
|
+
d: string
|
|
81
|
+
label: string
|
|
82
|
+
value: number | undefined
|
|
83
|
+
fill: string
|
|
84
|
+
hasData: boolean
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const countries = computed<CountryPath[]>(() =>
|
|
88
|
+
Object.entries(WORLD_PATHS).map(([id, d]) => {
|
|
89
|
+
const row = byId.value.get(id)
|
|
90
|
+
const value = row?.value
|
|
91
|
+
return {
|
|
92
|
+
id,
|
|
93
|
+
d,
|
|
94
|
+
label: row?.label || WORLD_NAMES[id] || id,
|
|
95
|
+
value,
|
|
96
|
+
fill: fillFor(value),
|
|
97
|
+
hasData: value != null && value > 0,
|
|
98
|
+
}
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const hoverId = ref<string | null>(null)
|
|
103
|
+
const tipX = ref(0)
|
|
104
|
+
const tipY = ref(0)
|
|
105
|
+
|
|
106
|
+
function onEnter(id: string) { hoverId.value = id }
|
|
107
|
+
function onLeave() { hoverId.value = null }
|
|
108
|
+
function onMove(e: MouseEvent) { trackLocalPointer(e, tipX, tipY) }
|
|
109
|
+
|
|
110
|
+
const hover = computed(() => {
|
|
111
|
+
if (!hoverId.value) return null
|
|
112
|
+
const c = countries.value.find(x => x.id === hoverId.value)
|
|
113
|
+
return c?.hasData ? c : null
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const hoverRows = computed(() => hover.value
|
|
117
|
+
? [{ name: hover.value.label, color: hover.value.fill, value: formatValue(hover.value.value!, fmt.value) }]
|
|
118
|
+
: [])
|
|
119
|
+
|
|
120
|
+
const tooltipStyle = tipFollowStyle(tipX, tipY)
|
|
121
|
+
|
|
122
|
+
const legendMin = computed(() => formatValue(0, fmt.value))
|
|
123
|
+
const legendMax = computed(() => formatValue(maxValue.value, fmt.value))
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<div ref="el" class="bgl-map w-100p flex column gap-075">
|
|
128
|
+
<div
|
|
129
|
+
class="bgl-map__plot relative w-100p"
|
|
130
|
+
:style="{ height: `${height}px` }"
|
|
131
|
+
@mousemove="onMove"
|
|
132
|
+
@mouseleave="onLeave"
|
|
133
|
+
>
|
|
134
|
+
<svg
|
|
135
|
+
class="bgl-map__svg"
|
|
136
|
+
dir="ltr"
|
|
137
|
+
:viewBox="`0 0 ${WORLD_VIEWBOX.width} ${WORLD_VIEWBOX.height}`"
|
|
138
|
+
preserveAspectRatio="xMidYMid meet"
|
|
139
|
+
width="100%"
|
|
140
|
+
height="100%"
|
|
141
|
+
>
|
|
142
|
+
<path
|
|
143
|
+
v-for="c in countries"
|
|
144
|
+
:key="c.id"
|
|
145
|
+
:d="c.d"
|
|
146
|
+
:fill="c.fill"
|
|
147
|
+
:stroke="strokeColor"
|
|
148
|
+
stroke-width="0.6"
|
|
149
|
+
vector-effect="non-scaling-stroke"
|
|
150
|
+
class="bgl-map__country"
|
|
151
|
+
:class="{
|
|
152
|
+
'opacity-5': hoverId != null && hoverId !== c.id,
|
|
153
|
+
'bgl-map__country--hot': hoverId === c.id && c.hasData,
|
|
154
|
+
}"
|
|
155
|
+
@mouseenter="onEnter(c.id)"
|
|
156
|
+
/>
|
|
157
|
+
</svg>
|
|
158
|
+
|
|
159
|
+
<ChartTooltip
|
|
160
|
+
v-if="hover"
|
|
161
|
+
prefer="above"
|
|
162
|
+
:style="tooltipStyle"
|
|
163
|
+
:label="hover.label"
|
|
164
|
+
:rows="hoverRows"
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div
|
|
169
|
+
v-if="legend && maxValue > 0"
|
|
170
|
+
class="bgl-map__legend flex align-items-center gap-05 txt12 color-gray"
|
|
171
|
+
>
|
|
172
|
+
<span class="tabular-nums">{{ legendMin }}</span>
|
|
173
|
+
<div
|
|
174
|
+
class="bgl-map__ramp flex-1"
|
|
175
|
+
:style="{
|
|
176
|
+
background: `linear-gradient(to right, color-mix(in srgb, ${baseColor} 28%, var(--bgl-box-bg)), ${baseColor})`,
|
|
177
|
+
}"
|
|
178
|
+
/>
|
|
179
|
+
<span class="tabular-nums">{{ legendMax }}</span>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</template>
|
|
183
|
+
|
|
184
|
+
<style scoped>
|
|
185
|
+
.bgl-map__plot { min-height: 0; }
|
|
186
|
+
.bgl-map__svg { display: block; overflow: visible; }
|
|
187
|
+
.bgl-map__country {
|
|
188
|
+
cursor: default;
|
|
189
|
+
transition: opacity 0.15s ease, fill 0.15s ease;
|
|
190
|
+
outline: none;
|
|
191
|
+
}
|
|
192
|
+
.bgl-map__country--hot { stroke: var(--bgl-gray); stroke-width: 1.2; }
|
|
193
|
+
.bgl-map__ramp {
|
|
194
|
+
height: 0.5rem;
|
|
195
|
+
border-radius: 999px;
|
|
196
|
+
min-width: 4rem;
|
|
197
|
+
}
|
|
198
|
+
</style>
|
|
@@ -11,6 +11,8 @@ import { alpha, resolveColor } from './core/palette'
|
|
|
11
11
|
import { useChartAnim } from './core/useChartAnim'
|
|
12
12
|
import type { ThemeType } from '../../types'
|
|
13
13
|
|
|
14
|
+
defineOptions({ name: 'BglRadialBars' })
|
|
15
|
+
|
|
14
16
|
interface Ring { label: string; value: number; color?: ThemeType | string; max?: number }
|
|
15
17
|
|
|
16
18
|
const props = withDefaults(defineProps<{
|
|
@@ -37,7 +39,7 @@ const props = withDefaults(defineProps<{
|
|
|
37
39
|
})
|
|
38
40
|
|
|
39
41
|
const el = ref<HTMLElement>()
|
|
40
|
-
const { progress } = useChartAnim({ el, enabled: props.animated, duration:
|
|
42
|
+
const { progress } = useChartAnim({ el, enabled: props.animated, duration: 700 })
|
|
41
43
|
|
|
42
44
|
const cx = computed(() => props.size / 2)
|
|
43
45
|
|
|
@@ -11,6 +11,8 @@ import { ramp, resolveColor } from './core/palette'
|
|
|
11
11
|
import { formatValue, type ValueFormat } from './core/format'
|
|
12
12
|
import type { ThemeType } from '../../types'
|
|
13
13
|
|
|
14
|
+
defineOptions({ name: 'BglRankBars' })
|
|
15
|
+
|
|
14
16
|
interface Row { label: string; value: number; color?: ThemeType | string }
|
|
15
17
|
|
|
16
18
|
const props = withDefaults(defineProps<{
|
|
@@ -37,7 +37,7 @@ const props = withDefaults(defineProps<{
|
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const el = ref<HTMLElement>()
|
|
40
|
-
const { progress } = useChartAnim({ el, enabled: props.animated, duration:
|
|
40
|
+
const { progress } = useChartAnim({ el, enabled: props.animated, duration: 700 })
|
|
41
41
|
|
|
42
42
|
const stroke = computed(() => resolveColor(props.color))
|
|
43
43
|
const points = computed(() => normalizeSeries(props.data, undefined)[0]?.points ?? [])
|
|
@@ -15,6 +15,8 @@ import { formatValue } from './core/format'
|
|
|
15
15
|
import { resolveColor } from './core/palette'
|
|
16
16
|
import type { ThemeType } from '../../types'
|
|
17
17
|
|
|
18
|
+
defineOptions({ name: 'BglStatCard' })
|
|
19
|
+
|
|
18
20
|
const props = withDefaults(defineProps<{
|
|
19
21
|
label: string
|
|
20
22
|
value: number | string
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG arc helpers for pie / donut slices.
|
|
3
|
+
* Full 360° arcs must be split — a single SVG arc with coincident endpoints collapses.
|
|
4
|
+
*/
|
|
5
|
+
const TWO_PI = Math.PI * 2
|
|
6
|
+
const EPS = 1e-6
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build an SVG path for a pie wedge (`ri === 0`) or donut slice (`ri > 0`)
|
|
10
|
+
* spanning `sweep` radians clockwise from `a0` (0 = east, −π/2 = north).
|
|
11
|
+
*/
|
|
12
|
+
export function slicePath(
|
|
13
|
+
cx: number,
|
|
14
|
+
cy: number,
|
|
15
|
+
r: number,
|
|
16
|
+
ri: number,
|
|
17
|
+
a0: number,
|
|
18
|
+
sweep: number,
|
|
19
|
+
): string {
|
|
20
|
+
if (sweep <= EPS || r <= 0) return ''
|
|
21
|
+
|
|
22
|
+
// Full circle / ring — two semicircle arcs (SVG can't draw 360° in one A).
|
|
23
|
+
if (sweep >= TWO_PI - EPS) {
|
|
24
|
+
if (ri <= 0) {
|
|
25
|
+
return `M ${cx + r} ${cy} A ${r} ${r} 0 1 1 ${cx - r} ${cy} A ${r} ${r} 0 1 1 ${cx + r} ${cy} Z`
|
|
26
|
+
}
|
|
27
|
+
return [
|
|
28
|
+
`M ${cx + r} ${cy}`,
|
|
29
|
+
`A ${r} ${r} 0 1 1 ${cx - r} ${cy}`,
|
|
30
|
+
`A ${r} ${r} 0 1 1 ${cx + r} ${cy}`,
|
|
31
|
+
`L ${cx + ri} ${cy}`,
|
|
32
|
+
`A ${ri} ${ri} 0 1 0 ${cx - ri} ${cy}`,
|
|
33
|
+
`A ${ri} ${ri} 0 1 0 ${cx + ri} ${cy}`,
|
|
34
|
+
'Z',
|
|
35
|
+
].join(' ')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const a1 = a0 + sweep
|
|
39
|
+
const large = sweep > Math.PI ? 1 : 0
|
|
40
|
+
const x0 = cx + r * Math.cos(a0)
|
|
41
|
+
const y0 = cy + r * Math.sin(a0)
|
|
42
|
+
const x1 = cx + r * Math.cos(a1)
|
|
43
|
+
const y1 = cy + r * Math.sin(a1)
|
|
44
|
+
|
|
45
|
+
if (ri > 0) {
|
|
46
|
+
const xi1 = cx + ri * Math.cos(a1)
|
|
47
|
+
const yi1 = cy + ri * Math.sin(a1)
|
|
48
|
+
const xi0 = cx + ri * Math.cos(a0)
|
|
49
|
+
const yi0 = cy + ri * Math.sin(a0)
|
|
50
|
+
return `M ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1} L ${xi1} ${yi1} A ${ri} ${ri} 0 ${large} 0 ${xi0} ${yi0} Z`
|
|
51
|
+
}
|
|
52
|
+
return `M ${cx} ${cy} L ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1} Z`
|
|
53
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tooltip anchoring helpers for charts.
|
|
3
|
+
*
|
|
4
|
+
* Two placement modes (match ChartTooltip `prefer`):
|
|
5
|
+
* - **follow** (`prefer="above"`): cursor-anchored — Donut, Map, Funnel
|
|
6
|
+
* - **pinned** (`prefer="top"`): top of chart, x follows mouse — Line, Bar
|
|
7
|
+
*/
|
|
8
|
+
import { computed, type Ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
/** Update tip coords from a mouse event relative to `currentTarget`. */
|
|
11
|
+
export function trackLocalPointer(
|
|
12
|
+
e: MouseEvent,
|
|
13
|
+
tipX: Ref<number>,
|
|
14
|
+
tipY?: Ref<number>,
|
|
15
|
+
) {
|
|
16
|
+
const el = e.currentTarget as HTMLElement | null
|
|
17
|
+
if (!el) return
|
|
18
|
+
const rect = el.getBoundingClientRect()
|
|
19
|
+
tipX.value = e.clientX - rect.left
|
|
20
|
+
if (tipY) tipY.value = e.clientY - rect.top
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Cursor-follow style: `{ left, top }` for `prefer="above"|"below"`. */
|
|
24
|
+
export function tipFollowStyle(tipX: Ref<number>, tipY: Ref<number>) {
|
|
25
|
+
return computed(() => ({ left: `${tipX.value}px`, top: `${tipY.value}px` }))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Pinned-to-top style: `{ left }` clamped inside the chart for `prefer="top"`. */
|
|
29
|
+
export function tipPinnedStyle(tipX: Ref<number>, width: Ref<number>, half = 70) {
|
|
30
|
+
return computed(() => {
|
|
31
|
+
const w = width.value
|
|
32
|
+
const x = Math.min(Math.max(tipX.value, half), Math.max(half, w - half))
|
|
33
|
+
return { left: `${x}px` }
|
|
34
|
+
})
|
|
35
|
+
}
|