@bagelink/vue 1.15.154 → 1.15.156

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.15.154",
4
+ "version": "1.15.156",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -177,7 +177,7 @@ const tooltipStyle = computed(() => {
177
177
  </svg>
178
178
 
179
179
  <ChartTooltip
180
- v-if="hoverIdx != null" :style="tooltipStyle"
180
+ v-if="hoverIdx != null" prefer="top" :style="tooltipStyle"
181
181
  :label="hoverLabel" :rows="hoverRows" :total="hoverTotal"
182
182
  />
183
183
  </div>
@@ -6,7 +6,13 @@
6
6
  *
7
7
  * Positioned by the host via `style` (absolute left/top); the host owns
8
8
  * mouse tracking. Non-interactive (pointer-events: none).
9
+ *
10
+ * Placement is dynamic by default: the tooltip measures itself and flips to
11
+ * the opposite side of its anchor whenever the preferred side would clip past
12
+ * the top/bottom of the viewport. Hosts only express a *preferred* side via
13
+ * `prefer` (or the legacy `above`/`below` flags).
9
14
  */
15
+ import { nextTick, ref, watch } from 'vue'
10
16
  import { useI18n } from '@bagelink/vue'
11
17
 
12
18
  export interface TipRow { name: string; color?: string; value: string }
@@ -16,41 +22,95 @@ defineOptions({ name: 'BglChartTooltip' })
16
22
 
17
23
  const { $t } = useI18n()
18
24
 
19
- withDefaults(defineProps<{
25
+ const props = withDefaults(defineProps<{
20
26
  label?: string
21
27
  /** Big focal number (used by single-value charts like the funnel). */
22
28
  hero?: string
23
29
  rows?: TipRow[]
24
30
  total?: string | null
25
31
  stats?: TipStat[]
26
- /** Float above the anchor point instead of below it. */
32
+ /** Preferred side relative to the anchor point. The tooltip still flips to
33
+ the opposite side automatically when the preferred side would clip.
34
+ - `above`/`below`: cursor-anchored charts (a small gap is kept).
35
+ - `top`: pinned to the top of the chart (no vertical flip). */
36
+ prefer?: 'above' | 'below' | 'top'
37
+ /** @deprecated use `prefer="above"`. Float above the anchor point. */
27
38
  above?: boolean
39
+ /** @deprecated use `prefer="below"`. Float below the anchor point. */
40
+ below?: boolean
28
41
  }>(), {
29
42
  rows: () => [],
30
43
  stats: () => [],
44
+ prefer: 'below',
31
45
  })
46
+
47
+ // Resolve the caller's preferred side (new `prefer` prop wins; fall back to the
48
+ // legacy boolean flags for compatibility).
49
+ function preferredSide(): 'above' | 'below' | 'top' {
50
+ if (props.above) return 'above'
51
+ if (props.below) return 'below'
52
+ return props.prefer
53
+ }
54
+
55
+ const el = ref<HTMLElement>()
56
+ // The side actually used after measuring (may differ from the preferred one).
57
+ const side = ref<'above' | 'below' | 'top'>(preferredSide())
58
+
59
+ /**
60
+ * Place the tooltip by the anchor's position in the viewport:
61
+ * anchors in the top half get the tooltip *below* them, anchors in the
62
+ * bottom half get it *above*. `top` placements are pinned to the chart and
63
+ * never flip.
64
+ */
65
+ function place() {
66
+ const node = el.value
67
+ const want = preferredSide()
68
+ if (!node || want === 'top') { side.value = want; return }
69
+
70
+ // Normalize to the `below` transform first so the measured `top` always
71
+ // reflects the stable anchor point (not a post-flip offset), avoiding
72
+ // flip-flop right around the midpoint.
73
+ side.value = 'below'
74
+ nextTick(() => {
75
+ const n = el.value
76
+ if (!n) return
77
+ const r = n.getBoundingClientRect()
78
+ side.value = r.top < window.innerHeight / 2 ? 'below' : 'above'
79
+ })
80
+ }
81
+
82
+ // Re-place whenever the anchor moves or the content changes.
83
+ watch(
84
+ () => [props.label, props.hero, props.total, props.rows, props.stats, props.prefer, props.above, props.below],
85
+ place,
86
+ { immediate: true, deep: true },
87
+ )
32
88
  </script>
33
89
 
34
90
  <template>
35
- <div class="bgl-tip absolute pointer-events-none nowrap z-5 color-white line-height-13" :class="{ 'bgl-tip--above': above }">
36
- <p v-if="label" class="bgl-tip__label m-0">{{ label }}</p>
37
- <p v-if="hero" class="bgl-tip__hero m-0 tabular-nums">{{ hero }}</p>
91
+ <div
92
+ ref="el"
93
+ class="bgl-tip absolute top-0 pointer-events-none nowrap z-5 color-white line-height-16"
94
+ :class="{ 'bgl-tip--above': side === 'above', 'bgl-tip--below': side === 'below' }"
95
+ >
96
+ <p v-if="label" class="opacity-7 txt-075rem m-0 pb-025 ">{{ label }}</p>
97
+ <p v-if="hero" class="m-0 tabular-nums bold pb-025 txt-1-25rem">{{ hero }}</p>
38
98
 
39
- <p v-for="(r, i) in rows" :key="i" class="bgl-tip__row flex align-items-center m-0">
99
+ <p v-for="(r, i) in rows" :key="i" class="gap-05 flex align-items-center m-0">
40
100
  <span v-if="r.color" class="bgl-tip__dot flex-shrink-0" :style="{ background: r.color }" />
41
101
  <span v-if="rows.length > 1" class="bgl-tip__name">{{ r.name }}</span>
42
- <span class="bgl-tip__val tabular-nums">{{ r.value }}</span>
102
+ <span class="ms-auto tabular-nums semibold">{{ r.value }}</span>
43
103
  </p>
44
104
 
45
- <p v-if="total" class="bgl-tip__row bgl-tip__total flex align-items-center m-0">
105
+ <p v-if="total" class="gap-05 bgl-tip__total flex align-items-center m-0">
46
106
  <span class="bgl-tip__name">{{ $t('charts.total') }}</span>
47
- <span class="bgl-tip__val tabular-nums">{{ total }}</span>
107
+ <span class="ms-auto tabular-nums semibold">{{ total }}</span>
48
108
  </p>
49
109
 
50
110
  <div v-if="stats.length" class="bgl-tip__stats flex column">
51
- <p v-for="(s, i) in stats" :key="i" class="bgl-tip__row flex align-items-center m-0">
111
+ <p v-for="(s, i) in stats" :key="i" class="gap-05 flex align-items-center m-0 space-between">
52
112
  <span class="bgl-tip__name">{{ s.label }}</span>
53
- <span class="bgl-tip__val tabular-nums">{{ s.value }}</span>
113
+ <span class="ms-auto tabular-nums semibold">{{ s.value }}</span>
54
114
  </p>
55
115
  </div>
56
116
  </div>
@@ -58,17 +118,14 @@ withDefaults(defineProps<{
58
118
 
59
119
  <style scoped>
60
120
  .bgl-tip {
61
- top: 0; transform: translateX(-50%); transform-origin: center;
121
+ transform: translateX(-50%); transform-origin: center;
62
122
  background: var(--bgl-black); border-radius: 0.5rem; padding: 0.4rem 0.6rem;
63
123
  font-size: 0.75rem; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); min-width: 7rem;
64
124
  }
65
125
  .bgl-tip--above { transform: translate(-50%, calc(-100% - 0.6rem)); }
66
- .bgl-tip__label { margin-bottom: 0.2rem; opacity: 0.7; font-size: 0.7rem; }
67
- .bgl-tip__hero { margin-bottom: 0.2rem; font-size: 1.05rem; font-weight: 700; }
68
- .bgl-tip__row { gap: 0.4rem; }
126
+ .bgl-tip--below { transform: translate(-50%, 0.6rem); }
69
127
  .bgl-tip__dot { width: 8px; height: 8px; border-radius: 2px; }
70
128
  .bgl-tip__name { opacity: 0.7; }
71
- .bgl-tip__val { margin-inline-start: auto; font-weight: 600; }
72
129
  .bgl-tip__total,
73
130
  .bgl-tip__stats {
74
131
  margin-top: 0.28rem; padding-top: 0.28rem;
@@ -190,7 +190,7 @@ const tooltipStyle = computed(() => ({ left: `${tipX.value}px`, top: `${tipY.val
190
190
  <span v-if="centerLabel" class="color-gray txt12">{{ centerLabel }}</span>
191
191
  </div>
192
192
  <ChartTooltip
193
- v-if="hoverArc" above :style="tooltipStyle"
193
+ v-if="hoverArc" prefer="above" :style="tooltipStyle"
194
194
  :label="hoverArc.label" :hero="hoverValue" :stats="hoverStats"
195
195
  />
196
196
  </div>
@@ -292,7 +292,7 @@ const tooltipStyle = computed(() => ({ left: `${tipX.value}px`, top: `${tipY.val
292
292
 
293
293
  <!-- Shared tooltip -->
294
294
  <ChartTooltip
295
- v-if="hoverStage" above :style="tooltipStyle"
295
+ v-if="hoverStage" prefer="above" :style="tooltipStyle"
296
296
  :label="hoverStage.label"
297
297
  :hero="isSegmented ? undefined : hoverStage.display"
298
298
  :rows="isSegmented ? hoverStage.rows : []"
@@ -231,7 +231,7 @@ const tooltipStyle = computed(() => {
231
231
 
232
232
  <!-- shared tooltip following the mouse -->
233
233
  <ChartTooltip
234
- v-if="hoverIdx != null" :style="tooltipStyle"
234
+ v-if="hoverIdx != null" prefer="top" :style="tooltipStyle"
235
235
  :label="hoverLabel" :rows="hoverRows" :total="hoverTotal"
236
236
  />
237
237
  </div>