@globalbrain/sefirot 4.15.0 → 4.17.0

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.
@@ -3,6 +3,7 @@ import { useElementSize } from '@vueuse/core'
3
3
  import * as d3 from 'd3'
4
4
  import { useTemplateRef, watch } from 'vue'
5
5
  import { type ChartColor, type KV, type Margins, c, scheme } from '../support/Chart'
6
+ import { getTextSize } from '../support/Text'
6
7
 
7
8
  const props = withDefaults(defineProps<{
8
9
  // State
@@ -19,6 +20,8 @@ const props = withDefaults(defineProps<{
19
20
  yLabel?: string
20
21
  xLabelOffset?: number
21
22
  yLabelOffset?: number
23
+ xLabelTickGap?: number
24
+ yLabelTickGap?: number
22
25
  xLabelFontSize?: string
23
26
  yLabelFontSize?: string
24
27
  ticks?: number
@@ -38,11 +41,13 @@ const props = withDefaults(defineProps<{
38
41
 
39
42
  xLabelFontSize: '14px',
40
43
  yLabelFontSize: '14px',
44
+ xLabelTickGap: 20,
45
+ yLabelTickGap: 20,
41
46
  ticks: 5,
42
47
  tickFontSize: '14px',
43
48
 
44
49
  tooltip: true,
45
- tooltipFormat: (d: KV) => `${d.key} ${d.value}`,
50
+ tooltipFormat: (d: KV) => `${d.key}&nbsp;&ndash; ${d.value}`,
46
51
 
47
52
  animate: true,
48
53
  debug: false
@@ -67,6 +72,7 @@ function renderChart({
67
72
 
68
73
  // Create color scale
69
74
  const color = scheme(props.data, props.colors)
75
+ const font = getComputedStyle(chartRef.value).fontFamily
70
76
 
71
77
  // Clear any existing SVG
72
78
  d3
@@ -77,25 +83,37 @@ function renderChart({
77
83
  // Set dimensions and margins
78
84
  const vertical = props.type === 'vertical'
79
85
 
86
+ const maxKeyLength = props.data.reduce((a, b) => Math.max(a, b.key.length), Number.NEGATIVE_INFINITY)
87
+ const maxValueLength = props.data.reduce((a, b) => Math.max(a, b.value.toLocaleString().length), Number.NEGATIVE_INFINITY)
88
+
89
+ const maxVerticalTickWidthInCh = vertical ? maxValueLength : maxKeyLength
90
+ const maxVerticalTickWidthInPx = getTextSize('0'.repeat(maxVerticalTickWidthInCh), `400 ${props.tickFontSize} ${font}`).width
91
+ const verticalLabelWidthInPx = props.yLabel ? getTextSize(props.yLabel, `400 ${props.yLabelFontSize} ${font}`).height : 0
92
+ const gapBetweenVerticalLabelAndTicks = props.yLabel ? props.yLabelTickGap : 0
93
+
94
+ const maxHorizontalTickHeightInPx = getTextSize('0', `400 ${props.tickFontSize} ${font}`).height // wrapping isn't supported
95
+ const horizontalLabelHeightInPx = props.xLabel ? getTextSize(props.xLabel, `400 ${props.xLabelFontSize} ${font}`).height : 0
96
+ const gapBetweenHorizontalLabelAndTicks = props.xLabel ? props.xLabelTickGap : 0
97
+
98
+ const xLabelOffset = props.xLabelOffset ?? horizontalLabelHeightInPx + 9 + maxHorizontalTickHeightInPx + gapBetweenHorizontalLabelAndTicks
99
+ const yLabelOffset = props.yLabelOffset ?? 9 + maxVerticalTickWidthInPx + gapBetweenVerticalLabelAndTicks
100
+
80
101
  const margin = {
81
102
  top: props.margins?.top ?? 30,
82
- right: props.margins?.right ?? 40,
83
- bottom: props.margins?.bottom ?? (props.xLabel ? 80 : 60),
84
- left: props.margins?.left ?? (props.yLabel ? (vertical ? 80 : 100) : (vertical ? 60 : 80))
103
+ right: props.margins?.right ?? 30,
104
+ bottom: (props.margins?.bottom ?? 30) + horizontalLabelHeightInPx + xLabelOffset - (props.xLabel ? 9 : 0),
105
+ left: (props.margins?.left ?? 30) + verticalLabelWidthInPx + yLabelOffset
85
106
  }
86
107
 
87
108
  const width = clientWidth - margin.left - margin.right
88
109
  const height = clientHeight - margin.top - margin.bottom
89
110
 
90
- const xLabelOffset = props.xLabelOffset ?? 46
91
- const yLabelOffset = props.yLabelOffset ?? (vertical ? 40 : 56)
92
-
93
111
  // Create SVG
94
112
  const svg = d3
95
113
  .select(chartRef.value)
96
114
  .append('svg')
97
115
  .attr('width', '100%')
98
- .attr('height', height + margin.top + margin.bottom)
116
+ .attr('height', clientHeight)
99
117
  .append('g')
100
118
  .attr('transform', `translate(${margin.left},${margin.top})`)
101
119
 
@@ -50,7 +50,7 @@ const props = withDefaults(defineProps<{
50
50
  legendFontSize: '14px',
51
51
 
52
52
  tooltip: true,
53
- tooltipFormat: (d: KV) => `${d.key} ${d.value}`,
53
+ tooltipFormat: (d: KV) => `${d.key}&nbsp;&ndash; ${d.value}`,
54
54
 
55
55
  animate: true,
56
56
  debug: false
@@ -5,7 +5,7 @@ import xor from 'lodash-es/xor'
5
5
  import { computed, nextTick, reactive, ref, shallowRef, toValue, unref, watch } from 'vue'
6
6
  import { type Table } from '../composables/Table'
7
7
  import { smartComputed } from '../support/Reactivity'
8
- import { getTextWidth } from '../support/Text'
8
+ import { getTextSize } from '../support/Text'
9
9
  import SInputCheckbox from './SInputCheckbox.vue'
10
10
  import SInputRadio from './SInputRadio.vue'
11
11
  import SSpinner from './SSpinner.vue'
@@ -231,12 +231,12 @@ const actionsColumnWidth = computed(() => {
231
231
 
232
232
  // has only label
233
233
  if (label && !icon) {
234
- return 1 /* border */ + 12 /* padding */ + getTextWidth(label, font) + 12 /* padding */ + 1 /* border */
234
+ return 1 /* border */ + 12 /* padding */ + getTextSize(label, font).width + 12 /* padding */ + 1 /* border */
235
235
  }
236
236
 
237
237
  // has both icon and label
238
238
  if (icon && label) {
239
- return 1 /* border */ + 8 /* padding */ + 16 /* icon */ + 4 /* padding */ + getTextWidth(label, font) + 10 /* padding */ + 1 /* border */
239
+ return 1 /* border */ + 8 /* padding */ + 16 /* icon */ + 4 /* padding */ + getTextSize(label, font).width + 10 /* padding */ + 1 /* border */
240
240
  }
241
241
 
242
242
  return 0
@@ -2,16 +2,16 @@
2
2
 
3
3
  let _canvas: HTMLCanvasElement
4
4
 
5
- export function getTextWidth(text: string, font: string): number
6
- export function getTextWidth(text: string, el: HTMLElement): number
7
-
8
- export function getTextWidth(text: string, fontOrEl: string | HTMLElement): number {
5
+ export function getTextSize(text: string, fontOrEl: string | HTMLElement) {
9
6
  const canvas = _canvas || (_canvas = document.createElement('canvas'))
10
7
  const context = canvas.getContext('2d')!
11
8
  context.font = typeof fontOrEl === 'string' ? fontOrEl : getCanvasFont(fontOrEl)
12
9
  const metrics = context.measureText(text)
13
10
 
14
- return metrics.width
11
+ return {
12
+ width: metrics.width,
13
+ height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
14
+ }
15
15
  }
16
16
 
17
17
  function getCanvasFont(el: HTMLElement) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
3
  "type": "module",
4
- "version": "4.15.0",
4
+ "version": "4.17.0",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "description": "Vue Components for Global Brain Design System.",
7
7
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",