@orbcharts/plugins-basic 3.0.0-alpha.24
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +201 -0
- package/package.json +41 -0
- package/src/grid/defaults.ts +95 -0
- package/src/grid/gridObservables.ts +114 -0
- package/src/grid/index.ts +12 -0
- package/src/grid/plugins/BarStack.ts +661 -0
- package/src/grid/plugins/Bars.ts +604 -0
- package/src/grid/plugins/BarsTriangle.ts +594 -0
- package/src/grid/plugins/Dots.ts +427 -0
- package/src/grid/plugins/GroupArea.ts +636 -0
- package/src/grid/plugins/GroupAxis.ts +363 -0
- package/src/grid/plugins/Lines.ts +528 -0
- package/src/grid/plugins/Ranking.ts +0 -0
- package/src/grid/plugins/RankingAxis.ts +0 -0
- package/src/grid/plugins/ScalingArea.ts +168 -0
- package/src/grid/plugins/ValueAxis.ts +356 -0
- package/src/grid/plugins/ValueStackAxis.ts +372 -0
- package/src/grid/types.ts +102 -0
- package/src/index.ts +7 -0
- package/src/multiGrid/index.ts +0 -0
- package/src/multiGrid/plugins/Diverging.ts +0 -0
- package/src/multiGrid/plugins/DivergingAxes.ts +0 -0
- package/src/multiGrid/plugins/TwoScaleAxes.ts +0 -0
- package/src/multiGrid/plugins/TwoScales.ts +0 -0
- package/src/multiValue/index.ts +0 -0
- package/src/multiValue/plugins/Scatter.ts +0 -0
- package/src/multiValue/plugins/ScatterAxes.ts +0 -0
- package/src/noneData/defaults.ts +47 -0
- package/src/noneData/index.ts +4 -0
- package/src/noneData/plugins/Container.ts +11 -0
- package/src/noneData/plugins/Tooltip.ts +305 -0
- package/src/noneData/types.ts +26 -0
- package/src/relationship/index.ts +0 -0
- package/src/relationship/plugins/Relationship.ts +0 -0
- package/src/series/defaults.ts +82 -0
- package/src/series/index.ts +6 -0
- package/src/series/plugins/Bubbles.ts +553 -0
- package/src/series/plugins/Pie.ts +603 -0
- package/src/series/plugins/PieEventTexts.ts +194 -0
- package/src/series/plugins/PieLabels.ts +289 -0
- package/src/series/plugins/Waffle.ts +0 -0
- package/src/series/seriesUtils.ts +51 -0
- package/src/series/types.ts +53 -0
- package/src/tree/index.ts +0 -0
- package/src/tree/plugins/TreeMap.ts +0 -0
- package/src/utils/commonUtils.ts +22 -0
- package/src/utils/d3Graphics.ts +125 -0
- package/src/utils/d3Utils.ts +73 -0
- package/src/utils/observables.ts +14 -0
- package/src/utils/orbchartsUtils.ts +70 -0
- package/tsconfig.json +14 -0
- package/vite.config.js +45 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
type RenderCircleTextParams = {
|
3
|
+
text: string,
|
4
|
+
radius: number,
|
5
|
+
lineHeight: number,
|
6
|
+
isBreakAll: boolean,
|
7
|
+
limit?:number
|
8
|
+
}
|
9
|
+
|
10
|
+
type Line = { width: number; text: string }
|
11
|
+
|
12
|
+
export function renderCircleText (selection: d3.Selection<any, any, any, any>, {
|
13
|
+
text,
|
14
|
+
radius,
|
15
|
+
lineHeight,
|
16
|
+
isBreakAll = false,
|
17
|
+
limit = 0
|
18
|
+
}: RenderCircleTextParams): d3.Selection<SVGTSpanElement, Line, SVGTextElement, any> | undefined {
|
19
|
+
if (selection == null || text == null) {
|
20
|
+
console.error("selection or text is not defined")
|
21
|
+
return
|
22
|
+
}
|
23
|
+
if (radius == null) {
|
24
|
+
const getBox = selection.node().getBBox()
|
25
|
+
radius = getBox.width / 2
|
26
|
+
}
|
27
|
+
|
28
|
+
function getWords (text: string) {
|
29
|
+
let words
|
30
|
+
if (isBreakAll) {
|
31
|
+
words = text.split('')
|
32
|
+
} else {
|
33
|
+
words = text.split(/\s+/g) // To hyphenate: /\s+|(?<=-)/
|
34
|
+
}
|
35
|
+
if (!words[words.length - 1]) words.pop()
|
36
|
+
if (!words[0]) words.shift()
|
37
|
+
return words
|
38
|
+
}
|
39
|
+
|
40
|
+
// 省略文章字數
|
41
|
+
function ellipisText (text:string, limit:number) {
|
42
|
+
if (text && limit) {
|
43
|
+
if (text.length > limit) {
|
44
|
+
text = text.substring(0, limit) + "..."; // 超過字數以"..."取代
|
45
|
+
}
|
46
|
+
}
|
47
|
+
return text;
|
48
|
+
}
|
49
|
+
|
50
|
+
function measureWidth (text: string) {
|
51
|
+
const context = document.createElement("canvas").getContext("2d")
|
52
|
+
// return text => context.measureText(text).width
|
53
|
+
return context?.measureText(text)?.width ?? 0
|
54
|
+
}
|
55
|
+
|
56
|
+
function getTargetWidth (text: string) {
|
57
|
+
const m = measureWidth(text.trim())
|
58
|
+
const result = Math.sqrt(m * lineHeight)
|
59
|
+
return result
|
60
|
+
// return(
|
61
|
+
// Math.sqrt(measureWidth(text.trim()) * lineHeight)
|
62
|
+
// )
|
63
|
+
}
|
64
|
+
|
65
|
+
function getLines (words: string[], targetWidth: number) {
|
66
|
+
let line: Line = { width: 0, text: '' }
|
67
|
+
let lineWidth0 = Infinity
|
68
|
+
const lines: Array<Line> = []
|
69
|
+
let space = " "
|
70
|
+
if (isBreakAll) {
|
71
|
+
space = ""
|
72
|
+
}
|
73
|
+
for (let i = 0, n = words.length; i < n; ++i) {
|
74
|
+
const lineText1 = (line.text ? line.text + space : '') + words[i]
|
75
|
+
const lineWidth1 = measureWidth(lineText1)
|
76
|
+
if ((lineWidth0 + lineWidth1) / 2 < targetWidth) {
|
77
|
+
line.width = lineWidth0 = lineWidth1
|
78
|
+
line.text = lineText1
|
79
|
+
} else {
|
80
|
+
lineWidth0 = measureWidth(words[i])
|
81
|
+
line = {width: lineWidth0, text: words[i]}
|
82
|
+
lines.push(line)
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return lines
|
86
|
+
}
|
87
|
+
|
88
|
+
function getTextRadius (lines: Array<Line>) {
|
89
|
+
let radius = 0
|
90
|
+
for (let i = 0, n = lines.length; i < n; ++i) {
|
91
|
+
const dy: number = (Math.abs(i - n / 2 + 0.5) + 0.5) * lineHeight
|
92
|
+
const dx: number = lines[i].width / 2
|
93
|
+
radius = Math.max(radius, Math.sqrt(dx ** 2 + dy ** 2))
|
94
|
+
}
|
95
|
+
return radius
|
96
|
+
}
|
97
|
+
|
98
|
+
function draw (selection: d3.Selection<any, any, any, any>, text: string) {
|
99
|
+
if(limit > 0) text = ellipisText(text,limit)
|
100
|
+
const words = getWords(text)
|
101
|
+
const targetWidth = getTargetWidth(text)
|
102
|
+
const lines = getLines(words, targetWidth)
|
103
|
+
const textRadius = getTextRadius(lines)
|
104
|
+
|
105
|
+
let t = selection.select<SVGTextElement>("text")
|
106
|
+
if (!t.size()) {
|
107
|
+
t = selection.append("text")
|
108
|
+
}
|
109
|
+
t.attr("transform", `translate(${0},${0}) scale(${radius / textRadius})`)
|
110
|
+
const tspanUpdate = t.selectAll<SVGTSpanElement, Line>("tspan")
|
111
|
+
.data(lines)
|
112
|
+
const tspanEnter = tspanUpdate.enter()
|
113
|
+
.append<SVGTSpanElement>("tspan")
|
114
|
+
.attr("x", 0)
|
115
|
+
.merge(tspanUpdate as d3.Selection<SVGTSpanElement, Line, SVGTextElement, undefined>)
|
116
|
+
.attr("y", (d: Line, i: number) => (i - lines.length / 2 + 0.8) * lineHeight)
|
117
|
+
.text((d: Line) => d.text)
|
118
|
+
tspanUpdate.exit().remove()
|
119
|
+
|
120
|
+
// return selection.node()
|
121
|
+
return tspanUpdate.merge(tspanEnter)
|
122
|
+
}
|
123
|
+
|
124
|
+
return draw(selection, text)
|
125
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import * as d3 from 'd3'
|
2
|
+
|
3
|
+
export function getSvgGElementSize (selection: d3.Selection<SVGGElement, any, any, any>): DOMRect {
|
4
|
+
try {
|
5
|
+
return selection.node()!.getBBox()
|
6
|
+
} catch (e: any) {
|
7
|
+
throw new Error(e)
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
// 使用字串加入svg
|
12
|
+
export function appendSvg (selection: d3.Selection<any, any, any, any>, svgString: string): void {
|
13
|
+
function parseSvg (svgString: string) {
|
14
|
+
const div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
15
|
+
div.innerHTML= '<svg xmlns="http://www.w3.org/2000/svg">'+ svgString +'</svg>';
|
16
|
+
const frag = document.createDocumentFragment()
|
17
|
+
while (frag && div?.firstChild?.firstChild)
|
18
|
+
frag.appendChild(div.firstChild.firstChild);
|
19
|
+
return frag;
|
20
|
+
}
|
21
|
+
// 刪除現有子節點
|
22
|
+
const node = selection.node()
|
23
|
+
while(node.hasChildNodes())
|
24
|
+
{
|
25
|
+
node.removeChild(node.firstChild);
|
26
|
+
}
|
27
|
+
// 加入dom
|
28
|
+
selection.node().appendChild(parseSvg(svgString))
|
29
|
+
}
|
30
|
+
|
31
|
+
export function getD3TransitionEase (easeName: string) {
|
32
|
+
if (easeName.substring(0, 4) !== 'ease') {
|
33
|
+
return d3.easeCubic
|
34
|
+
}
|
35
|
+
return (d3 as any)[easeName] ?? d3.easeCubic
|
36
|
+
}
|
37
|
+
|
38
|
+
export function makeD3Arc ({ axisWidth, innerRadius, outerRadius, padAngle, cornerRadius }: {
|
39
|
+
axisWidth: number
|
40
|
+
innerRadius: number
|
41
|
+
outerRadius: number
|
42
|
+
padAngle: number
|
43
|
+
cornerRadius: number
|
44
|
+
}): d3.Arc<any, d3.DefaultArcObject> {
|
45
|
+
const arcScale = d3.scaleLinear()
|
46
|
+
.domain([0, 1])
|
47
|
+
.range([0, axisWidth / 2])
|
48
|
+
|
49
|
+
const _outerRadius = arcScale(outerRadius)!
|
50
|
+
|
51
|
+
return d3.arc()
|
52
|
+
.innerRadius(arcScale(innerRadius)!)
|
53
|
+
.outerRadius(_outerRadius)
|
54
|
+
.padAngle(padAngle)
|
55
|
+
.padRadius(_outerRadius)
|
56
|
+
.cornerRadius(cornerRadius)
|
57
|
+
}
|
58
|
+
|
59
|
+
export const parseTickFormatValue = (value: any, tickFormat: string | ((text: d3.NumberValue) => string)) => {
|
60
|
+
if (tickFormat! instanceof Function == true) {
|
61
|
+
return (tickFormat as ((text: d3.NumberValue) => string))(value)
|
62
|
+
}
|
63
|
+
return d3.format(tickFormat as string)!(value)
|
64
|
+
}
|
65
|
+
|
66
|
+
export const parseDateTickFormatValue = (value: any, tickFormat: string | ((text: d3.NumberValue) => string)) => {
|
67
|
+
if (tickFormat! instanceof Function == true) {
|
68
|
+
return (tickFormat as ((text: d3.NumberValue) => string))(value)
|
69
|
+
}
|
70
|
+
return d3.timeFormat(tickFormat as string)!(value)
|
71
|
+
}
|
72
|
+
|
73
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import * as d3 from 'd3'
|
2
|
+
import { Observable, merge, distinctUntilChanged, fromEvent } from 'rxjs'
|
3
|
+
|
4
|
+
export function d3EventObservable(selection: d3.Selection<any, any, any, any>, event: any) {
|
5
|
+
// Start with an observable that will never emit
|
6
|
+
let obs = new Observable(() => {});
|
7
|
+
selection.each(function () {
|
8
|
+
// Create observables from each of the elements
|
9
|
+
const events = fromEvent(this as any, event);
|
10
|
+
// Merge the observables into one
|
11
|
+
obs = merge(obs, events);
|
12
|
+
});
|
13
|
+
return obs;
|
14
|
+
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import type { AxisPosition, ColorType, ChartParams, ComputedDatumBase, ComputedDatumSeriesValue } from '@orbcharts/core'
|
2
|
+
import { getMinAndMax } from './commonUtils'
|
3
|
+
|
4
|
+
// 取得最小及最大值 - datum格式陣列資料
|
5
|
+
export function getMinAndMaxValue (data: ComputedDatumBase[]): [number, number] {
|
6
|
+
const arr = data
|
7
|
+
.filter(d => d.value != null && d.visible != false)
|
8
|
+
.map(d => d.value as number)
|
9
|
+
return getMinAndMax(arr)
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
export function getColor (colorType: ColorType, fullChartParams: ChartParams) {
|
14
|
+
const colors = fullChartParams.colors[fullChartParams.colorScheme]
|
15
|
+
// 對應series資料中第1個顏色
|
16
|
+
if (colorType === 'series') {
|
17
|
+
return colors.series[0]
|
18
|
+
}
|
19
|
+
// 對應colorType設定的顏色
|
20
|
+
return colors[colorType] != null
|
21
|
+
? colors[colorType]
|
22
|
+
: colors.primary
|
23
|
+
}
|
24
|
+
|
25
|
+
export function getDatumColor ({ datum, colorType, fullChartParams }: { datum: ComputedDatumBase, colorType: ColorType, fullChartParams: ChartParams }) {
|
26
|
+
// 對應series資料中的顏色
|
27
|
+
if (colorType === 'series') {
|
28
|
+
if ((datum as unknown as ComputedDatumSeriesValue).color) {
|
29
|
+
return (datum as unknown as ComputedDatumSeriesValue).color
|
30
|
+
} else {
|
31
|
+
// 非series類型的資料則回傳陣列中第1個顏色
|
32
|
+
return fullChartParams.colors[fullChartParams.colorScheme].series[0]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
// 對應colorType設定的顏色
|
36
|
+
return fullChartParams.colors[fullChartParams.colorScheme][colorType] != null
|
37
|
+
? fullChartParams.colors[fullChartParams.colorScheme][colorType]
|
38
|
+
: fullChartParams.colors[fullChartParams.colorScheme].primary
|
39
|
+
}
|
40
|
+
|
41
|
+
export function getClassName (pluginName: string, elementName: string, modifier?: string) {
|
42
|
+
const modifierText = modifier ? `--${modifier}` : ''
|
43
|
+
return `orbcharts-${pluginName}__${elementName}${modifierText}`
|
44
|
+
}
|
45
|
+
|
46
|
+
export function getUniID (pluginName: string, elementName: string) {
|
47
|
+
const textLength = 5
|
48
|
+
// 英文+數字
|
49
|
+
const randomText: string = Math.random().toString(36).substr(2, textLength)
|
50
|
+
|
51
|
+
return getClassName(pluginName, elementName, randomText)
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
export function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
|
56
|
+
xAxisPosition: AxisPosition
|
57
|
+
yAxisPosition: AxisPosition
|
58
|
+
width: number
|
59
|
+
height: number
|
60
|
+
}) {
|
61
|
+
if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
|
62
|
+
return { width, height }
|
63
|
+
} else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
|
64
|
+
return {
|
65
|
+
width: height,
|
66
|
+
height: width
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
package/tsconfig.json
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"outDir": "./dist/",
|
4
|
+
"sourceMap": true,
|
5
|
+
"noImplicitAny": true,
|
6
|
+
"module": "es6",
|
7
|
+
"target": "es5",
|
8
|
+
"jsx": "react",
|
9
|
+
"allowJs": true,
|
10
|
+
"moduleResolution": "node",
|
11
|
+
"allowSyntheticDefaultImports" : true,
|
12
|
+
"esModuleInterop" : true
|
13
|
+
}
|
14
|
+
}
|
package/vite.config.js
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
// import { fileURLToPath, URL } from 'node:url'
|
2
|
+
|
3
|
+
// import { resolve } from 'path'
|
4
|
+
import { defineConfig } from 'vite'
|
5
|
+
import dts from 'vite-plugin-dts'
|
6
|
+
|
7
|
+
const releaseConfig = {
|
8
|
+
plugins: [
|
9
|
+
dts({
|
10
|
+
insertTypesEntry: true
|
11
|
+
})
|
12
|
+
],
|
13
|
+
compilerOptions: {
|
14
|
+
composite: true
|
15
|
+
},
|
16
|
+
build: {
|
17
|
+
lib: {
|
18
|
+
entry: "src/index.ts",
|
19
|
+
name: 'orbcharts-plugins-basic',
|
20
|
+
formats: ["es", "umd"],
|
21
|
+
fileName: format => `orbcharts-plugins-basic.${format}.js`
|
22
|
+
},
|
23
|
+
// rollupOptions: {
|
24
|
+
// input: {
|
25
|
+
// main: resolve(__dirname, "src/index.ts")
|
26
|
+
// },
|
27
|
+
// external: ['vue'],
|
28
|
+
// output: {
|
29
|
+
// assetFileNames: 'my-library.css',
|
30
|
+
// exports: "named",
|
31
|
+
// globals: {
|
32
|
+
// vue: 'Vue',
|
33
|
+
// },
|
34
|
+
// },
|
35
|
+
// },
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
export default defineConfig(({ command, mode }) => {
|
40
|
+
if (mode === 'release') {
|
41
|
+
return releaseConfig
|
42
|
+
} else {
|
43
|
+
return releaseConfig
|
44
|
+
}
|
45
|
+
})
|