Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
26b168f7
Unverified
Commit
26b168f7
authored
Jan 29, 2021
by
Ryan McKinley
Committed by
GitHub
Jan 29, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BarChart: add alpha bar chart panel (#30323)
parent
c4381905
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
1495 additions
and
120 deletions
+1495
-120
packages/grafana-data/src/types/options.ts
+3
-1
packages/grafana-ui/src/components/BarChart/BarChart.story.tsx
+65
-0
packages/grafana-ui/src/components/BarChart/BarChart.tsx
+322
-0
packages/grafana-ui/src/components/BarChart/bars.ts
+287
-0
packages/grafana-ui/src/components/BarChart/distribute.ts
+49
-0
packages/grafana-ui/src/components/BarChart/quadtree.ts
+113
-0
packages/grafana-ui/src/components/BarChart/types.ts
+52
-0
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
+25
-12
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
+17
-2
packages/grafana-ui/src/components/VizLegend/VizLegend.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendSeriesIcon.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendStatsList.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx
+3
-0
packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx
+3
-0
packages/grafana-ui/src/components/index.ts
+2
-0
packages/grafana-ui/src/components/uPlot/config.ts
+28
-2
packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts
+9
-1
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts
+49
-1
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts
+27
-3
packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts
+24
-5
packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts
+24
-12
packages/grafana-ui/src/components/uPlot/types.ts
+2
-1
public/app/features/dashboard/components/PanelEditor/DefaultFieldConfigEditor.tsx
+1
-1
public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx
+1
-1
public/app/features/plugins/built_in_plugins.ts
+2
-0
public/app/plugins/panel/barchart/BarChartPanel.tsx
+103
-0
public/app/plugins/panel/barchart/img/barchart.svg
+2
-0
public/app/plugins/panel/barchart/module.tsx
+150
-0
public/app/plugins/panel/barchart/plugin.json
+18
-0
public/app/plugins/panel/stat/types.ts
+5
-4
public/app/plugins/panel/timeseries/config.ts
+94
-74
No files found.
packages/grafana-data/src/types/options.ts
View file @
26b168f7
import
{
DataFrame
}
from
'./dataFrame'
;
/**
* Base class for editor builders
*
...
...
@@ -49,5 +51,5 @@ export interface OptionEditorConfig<TOptions, TSettings = any, TValue = any> {
/**
* Function that enables configuration of when option editor should be shown based on current panel option properties.
*/
showIf
?:
(
currentOptions
:
TOptions
)
=>
boolean
|
undefined
;
showIf
?:
(
currentOptions
:
TOptions
,
data
?:
DataFrame
[]
)
=>
boolean
|
undefined
;
}
packages/grafana-ui/src/components/BarChart/BarChart.story.tsx
0 → 100644
View file @
26b168f7
import
{
toDataFrame
,
FieldType
,
VizOrientation
}
from
'@grafana/data'
;
import
React
from
'react'
;
import
{
withCenteredStory
}
from
'../../utils/storybook/withCenteredStory'
;
import
{
BarChart
}
from
'./BarChart'
;
import
{
LegendDisplayMode
}
from
'../VizLegend/types'
;
import
{
prepDataForStorybook
}
from
'../../utils/storybook/data'
;
import
{
useTheme
}
from
'../../themes'
;
import
{
select
}
from
'@storybook/addon-knobs'
;
import
{
BarChartOptions
,
BarStackingMode
,
BarValueVisibility
}
from
'./types'
;
export
default
{
title
:
'Visualizations/BarChart'
,
component
:
BarChart
,
decorators
:
[
withCenteredStory
],
parameters
:
{
docs
:
{},
},
};
const
getKnobs
=
()
=>
{
return
{
legendPlacement
:
select
(
'Legend placement'
,
{
bottom
:
'bottom'
,
right
:
'right'
,
},
'bottom'
),
orientation
:
select
(
'Bar orientation'
,
{
vertical
:
VizOrientation
.
Vertical
,
horizontal
:
VizOrientation
.
Horizontal
,
},
VizOrientation
.
Vertical
),
};
};
export
const
Basic
:
React
.
FC
=
()
=>
{
const
{
legendPlacement
,
orientation
}
=
getKnobs
();
const
theme
=
useTheme
();
const
frame
=
toDataFrame
({
fields
:
[
{
name
:
'x'
,
type
:
FieldType
.
string
,
values
:
[
'group 1'
,
'group 2'
]
},
{
name
:
'a'
,
type
:
FieldType
.
number
,
values
:
[
10
,
20
]
},
{
name
:
'b'
,
type
:
FieldType
.
number
,
values
:
[
30
,
10
]
},
],
});
const
data
=
prepDataForStorybook
([
frame
],
theme
);
const
options
:
BarChartOptions
=
{
orientation
:
orientation
,
legend
:
{
displayMode
:
LegendDisplayMode
.
List
,
placement
:
legendPlacement
,
calcs
:
[]
},
stacking
:
BarStackingMode
.
None
,
showValue
:
BarValueVisibility
.
Always
,
barWidth
:
0.97
,
groupWidth
:
0.7
,
};
return
<
BarChart
data=
{
data
[
0
]
}
width=
{
600
}
height=
{
400
}
theme=
{
theme
}
{
...
options
}
/>;
};
packages/grafana-ui/src/components/BarChart/BarChart.tsx
0 → 100644
View file @
26b168f7
import
React
,
{
useCallback
,
useMemo
,
useRef
}
from
'react'
;
import
{
compareDataFrameStructures
,
DataFrame
,
DefaultTimeZone
,
formattedValueToString
,
getFieldDisplayName
,
getFieldSeriesColor
,
getFieldColorModeForField
,
TimeRange
,
VizOrientation
,
fieldReducers
,
reduceField
,
DisplayValue
,
}
from
'@grafana/data'
;
import
{
VizLayout
}
from
'../VizLayout/VizLayout'
;
import
{
Themeable
}
from
'../../types'
;
import
{
useRevision
}
from
'../uPlot/hooks'
;
import
{
UPlotChart
}
from
'../uPlot/Plot'
;
import
{
UPlotConfigBuilder
}
from
'../uPlot/config/UPlotConfigBuilder'
;
import
{
AxisPlacement
,
ScaleDirection
,
ScaleDistribution
,
ScaleOrientation
}
from
'../uPlot/config'
;
import
{
useTheme
}
from
'../../themes'
;
import
{
GraphNGLegendEvent
,
GraphNGLegendEventMode
}
from
'../GraphNG/types'
;
import
{
FIXED_UNIT
}
from
'../GraphNG/GraphNG'
;
import
{
LegendDisplayMode
,
VizLegendItem
}
from
'../VizLegend/types'
;
import
{
VizLegend
}
from
'../VizLegend/VizLegend'
;
import
{
BarChartFieldConfig
,
BarChartOptions
,
BarValueVisibility
,
defaultBarChartFieldConfig
}
from
'./types'
;
import
{
BarsOptions
,
getConfig
}
from
'./bars'
;
/**
* @alpha
*/
export
interface
Props
extends
Themeable
,
BarChartOptions
{
height
:
number
;
width
:
number
;
data
:
DataFrame
;
onLegendClick
?:
(
event
:
GraphNGLegendEvent
)
=>
void
;
onSeriesColorChange
?:
(
label
:
string
,
color
:
string
)
=>
void
;
}
/**
* @alpha
*/
export
const
BarChart
:
React
.
FunctionComponent
<
Props
>
=
({
width
,
height
,
data
,
orientation
,
groupWidth
,
barWidth
,
showValue
,
legend
,
onLegendClick
,
onSeriesColorChange
,
...
plotProps
})
=>
{
if
(
!
data
||
data
.
fields
.
length
<
2
)
{
return
<
div
>
Missing data
</
div
>;
}
// dominik? TODO? can this all be moved into `useRevision`
const
compareFrames
=
useCallback
((
a
?:
DataFrame
|
null
,
b
?:
DataFrame
|
null
)
=>
{
if
(
a
&&
b
)
{
return
compareDataFrameStructures
(
a
,
b
);
}
return
false
;
},
[]);
const
configRev
=
useRevision
(
data
,
compareFrames
);
const
theme
=
useTheme
();
// Updates only when the structure changes
const
configBuilder
=
useMemo
(()
=>
{
if
(
!
orientation
||
orientation
===
VizOrientation
.
Auto
)
{
orientation
=
width
<
height
?
VizOrientation
.
Horizontal
:
VizOrientation
.
Vertical
;
}
// bar orientation -> x scale orientation & direction
let
xOri
:
ScaleOrientation
,
xDir
:
ScaleDirection
,
yOri
:
ScaleOrientation
,
yDir
:
ScaleDirection
;
if
(
orientation
===
VizOrientation
.
Vertical
)
{
xOri
=
ScaleOrientation
.
Horizontal
;
xDir
=
ScaleDirection
.
Right
;
yOri
=
ScaleOrientation
.
Vertical
;
yDir
=
ScaleDirection
.
Up
;
}
else
{
xOri
=
ScaleOrientation
.
Vertical
;
xDir
=
ScaleDirection
.
Down
;
yOri
=
ScaleOrientation
.
Horizontal
;
yDir
=
ScaleDirection
.
Right
;
}
const
formatValue
=
showValue
!==
BarValueVisibility
.
Never
?
(
seriesIdx
:
number
,
value
:
any
)
=>
formattedValueToString
(
data
.
fields
[
seriesIdx
].
display
!
(
value
))
:
undefined
;
// Use bar width when only one field
if
(
data
.
fields
.
length
===
2
)
{
groupWidth
=
barWidth
;
barWidth
=
1
;
}
const
opts
:
BarsOptions
=
{
xOri
,
xDir
,
groupWidth
,
barWidth
,
formatValue
,
onHover
:
(
seriesIdx
:
number
,
valueIdx
:
number
)
=>
{
console
.
log
(
'hover'
,
{
seriesIdx
,
valueIdx
});
},
onLeave
:
(
seriesIdx
:
number
,
valueIdx
:
number
)
=>
{
console
.
log
(
'leave'
,
{
seriesIdx
,
valueIdx
});
},
};
const
config
=
getConfig
(
opts
);
const
builder
=
new
UPlotConfigBuilder
();
builder
.
addHook
(
'init'
,
config
.
init
);
builder
.
addHook
(
'drawClear'
,
config
.
drawClear
);
builder
.
addHook
(
'setCursor'
,
config
.
setCursor
);
builder
.
setCursor
(
config
.
cursor
);
builder
.
setSelect
(
config
.
select
);
builder
.
addScale
({
scaleKey
:
'x'
,
isTime
:
false
,
distribution
:
ScaleDistribution
.
Ordinal
,
orientation
:
xOri
,
direction
:
xDir
,
});
builder
.
addAxis
({
scaleKey
:
'x'
,
isTime
:
false
,
placement
:
xOri
===
0
?
AxisPlacement
.
Bottom
:
AxisPlacement
.
Left
,
splits
:
config
.
xSplits
,
values
:
config
.
xValues
,
grid
:
false
,
ticks
:
false
,
gap
:
15
,
theme
,
});
let
seriesIndex
=
0
;
// iterate the y values
for
(
let
i
=
1
;
i
<
data
.
fields
.
length
;
i
++
)
{
const
field
=
data
.
fields
[
i
];
field
.
state
!
.
seriesIndex
=
seriesIndex
++
;
const
customConfig
:
BarChartFieldConfig
=
{
...
defaultBarChartFieldConfig
,
...
field
.
config
.
custom
};
const
scaleKey
=
field
.
config
.
unit
||
FIXED_UNIT
;
const
colorMode
=
getFieldColorModeForField
(
field
);
const
scaleColor
=
getFieldSeriesColor
(
field
,
theme
);
const
seriesColor
=
scaleColor
.
color
;
builder
.
addSeries
({
scaleKey
,
lineWidth
:
customConfig
.
lineWidth
,
lineColor
:
seriesColor
,
fillOpacity
:
customConfig
.
fillOpacity
,
theme
,
colorMode
,
pathBuilder
:
config
.
drawBars
,
pointsBuilder
:
config
.
drawPoints
,
show
:
!
customConfig
.
hideFrom
?.
graph
,
gradientMode
:
customConfig
.
gradientMode
,
thresholds
:
field
.
config
.
thresholds
,
/*
lineColor: customConfig.lineColor ?? seriesColor,
lineWidth: customConfig.lineWidth,
lineStyle: customConfig.lineStyle,
*/
// The following properties are not used in the uPlot config, but are utilized as transport for legend config
dataFrameFieldIndex
:
{
fieldIndex
:
i
,
frameIndex
:
0
,
},
fieldName
:
getFieldDisplayName
(
field
,
data
),
hideInLegend
:
customConfig
.
hideFrom
?.
legend
,
});
// The builder will manage unique scaleKeys and combine where appropriate
builder
.
addScale
({
scaleKey
,
min
:
field
.
config
.
min
,
max
:
field
.
config
.
max
,
softMin
:
customConfig
.
axisSoftMin
,
softMax
:
customConfig
.
axisSoftMax
,
orientation
:
yOri
,
direction
:
yDir
,
});
if
(
customConfig
.
axisPlacement
!==
AxisPlacement
.
Hidden
)
{
let
placement
=
customConfig
.
axisPlacement
;
if
(
!
placement
||
placement
===
AxisPlacement
.
Auto
)
{
placement
=
AxisPlacement
.
Left
;
}
if
(
xOri
===
1
)
{
if
(
placement
===
AxisPlacement
.
Left
)
{
placement
=
AxisPlacement
.
Bottom
;
}
if
(
placement
===
AxisPlacement
.
Right
)
{
placement
=
AxisPlacement
.
Top
;
}
}
builder
.
addAxis
({
scaleKey
,
label
:
customConfig
.
axisLabel
,
size
:
customConfig
.
axisWidth
,
placement
,
formatValue
:
(
v
)
=>
formattedValueToString
(
field
.
display
!
(
v
)),
theme
,
});
}
}
return
builder
;
},
[
data
,
configRev
,
orientation
,
width
,
height
]);
const
onLabelClick
=
useCallback
(
(
legend
:
VizLegendItem
,
event
:
React
.
MouseEvent
)
=>
{
const
{
fieldIndex
}
=
legend
;
if
(
!
onLegendClick
||
!
fieldIndex
)
{
return
;
}
onLegendClick
({
fieldIndex
,
mode
:
GraphNGLegendEventMode
.
AppendToSelection
,
});
},
[
onLegendClick
,
data
]
);
const
hasLegend
=
useRef
(
legend
&&
legend
.
displayMode
!==
LegendDisplayMode
.
Hidden
);
const
legendItems
=
configBuilder
.
getSeries
()
.
map
<
VizLegendItem
|
undefined
>
((
s
)
=>
{
const
seriesConfig
=
s
.
props
;
const
fieldIndex
=
seriesConfig
.
dataFrameFieldIndex
;
if
(
seriesConfig
.
hideInLegend
||
!
fieldIndex
)
{
return
undefined
;
}
const
field
=
data
.
fields
[
fieldIndex
.
fieldIndex
];
if
(
!
field
)
{
return
undefined
;
}
return
{
disabled
:
!
seriesConfig
.
show
??
false
,
fieldIndex
,
color
:
seriesConfig
.
lineColor
!
,
label
:
seriesConfig
.
fieldName
,
yAxis
:
1
,
getDisplayValues
:
()
=>
{
if
(
!
legend
.
calcs
?.
length
)
{
return
[];
}
const
fieldCalcs
=
reduceField
({
field
,
reducers
:
legend
.
calcs
,
});
return
legend
.
calcs
.
map
<
DisplayValue
>
((
reducer
)
=>
{
return
{
...
field
.
display
!
(
fieldCalcs
[
reducer
]),
title
:
fieldReducers
.
get
(
reducer
).
name
,
};
});
},
};
})
.
filter
((
i
)
=>
i
!==
undefined
)
as
VizLegendItem
[];
let
legendElement
:
React
.
ReactElement
|
undefined
;
if
(
hasLegend
&&
legendItems
.
length
>
0
)
{
legendElement
=
(
<
VizLayout
.
Legend
position=
{
legend
.
placement
}
maxHeight=
"35%"
maxWidth=
"60%"
>
<
VizLegend
onLabelClick=
{
onLabelClick
}
placement=
{
legend
.
placement
}
items=
{
legendItems
}
displayMode=
{
legend
.
displayMode
}
onSeriesColorChange=
{
onSeriesColorChange
}
/>
</
VizLayout
.
Legend
>
);
}
return
(
<
VizLayout
width=
{
width
}
height=
{
height
}
legend=
{
legendElement
}
>
{
(
vizWidth
:
number
,
vizHeight
:
number
)
=>
(
<
UPlotChart
data=
{
data
}
config=
{
configBuilder
}
width=
{
vizWidth
}
height=
{
vizHeight
}
timeRange=
{
({
from
:
1
,
to
:
1
}
as
unknown
)
as
TimeRange
}
// HACK
timeZone=
{
DefaultTimeZone
}
/>
)
}
</
VizLayout
>
);
};
packages/grafana-ui/src/components/BarChart/bars.ts
0 → 100644
View file @
26b168f7
import
uPlot
,
{
Axis
,
Series
,
Cursor
,
BBox
}
from
'uplot'
;
import
{
Quadtree
,
Rect
,
pointWithin
}
from
'./quadtree'
;
import
{
distribute
,
SPACE_BETWEEN
}
from
'./distribute'
;
const
pxRatio
=
devicePixelRatio
;
const
groupDistr
=
SPACE_BETWEEN
;
const
barDistr
=
SPACE_BETWEEN
;
const
font
=
Math
.
round
(
10
*
pxRatio
)
+
'px Arial'
;
type
WalkTwoCb
=
null
|
((
idx
:
number
,
offPx
:
number
,
dimPx
:
number
)
=>
void
);
function
walkTwo
(
groupWidth
:
number
,
barWidth
:
number
,
yIdx
:
number
,
xCount
:
number
,
yCount
:
number
,
xDim
:
number
,
xDraw
?:
WalkTwoCb
,
yDraw
?:
WalkTwoCb
)
{
distribute
(
xCount
,
groupWidth
,
groupDistr
,
null
,
(
ix
,
offPct
,
dimPct
)
=>
{
let
groupOffPx
=
xDim
*
offPct
;
let
groupWidPx
=
xDim
*
dimPct
;
xDraw
&&
xDraw
(
ix
,
groupOffPx
,
groupWidPx
);
yDraw
&&
distribute
(
yCount
,
barWidth
,
barDistr
,
yIdx
,
(
iy
,
offPct
,
dimPct
)
=>
{
let
barOffPx
=
groupWidPx
*
offPct
;
let
barWidPx
=
groupWidPx
*
dimPct
;
yDraw
(
ix
,
groupOffPx
+
barOffPx
,
barWidPx
);
});
});
}
/**
* @internal
*/
export
interface
BarsOptions
{
xOri
:
1
|
0
;
xDir
:
1
|
-
1
;
groupWidth
:
number
;
barWidth
:
number
;
formatValue
?:
(
seriesIdx
:
number
,
value
:
any
)
=>
string
;
onHover
?:
(
seriesIdx
:
number
,
valueIdx
:
any
)
=>
void
;
onLeave
?:
(
seriesIdx
:
number
,
valueIdx
:
any
)
=>
void
;
}
/**
* @internal
*/
export
function
getConfig
(
opts
:
BarsOptions
)
{
const
{
xOri
:
ori
,
xDir
:
dir
,
groupWidth
,
barWidth
,
formatValue
,
onHover
,
onLeave
}
=
opts
;
let
qt
:
Quadtree
;
const
drawBars
:
Series
.
PathBuilder
=
(
u
,
sidx
,
i0
,
i1
)
=>
{
return
uPlot
.
orient
(
u
,
sidx
,
(
series
,
dataX
,
dataY
,
scaleX
,
scaleY
,
valToPosX
,
valToPosY
,
xOff
,
yOff
,
xDim
,
yDim
,
moveTo
,
lineTo
,
rect
)
=>
{
const
fill
=
new
Path2D
();
let
numGroups
=
dataX
.
length
;
let
barsPerGroup
=
u
.
series
.
length
-
1
;
let
y0Pos
=
valToPosY
(
0
,
scaleY
,
yDim
,
yOff
);
const
_dir
=
dir
*
(
ori
===
0
?
1
:
-
1
);
walkTwo
(
groupWidth
,
barWidth
,
sidx
-
1
,
numGroups
,
barsPerGroup
,
xDim
,
null
,
(
ix
,
x0
,
wid
)
=>
{
let
lft
=
Math
.
round
(
xOff
+
(
_dir
===
1
?
x0
:
xDim
-
x0
-
wid
));
let
barWid
=
Math
.
round
(
wid
);
if
(
dataY
[
ix
]
!=
null
)
{
let
yPos
=
valToPosY
(
dataY
[
ix
]
!
,
scaleY
,
yDim
,
yOff
);
let
btm
=
Math
.
round
(
Math
.
max
(
yPos
,
y0Pos
));
let
top
=
Math
.
round
(
Math
.
min
(
yPos
,
y0Pos
));
let
barHgt
=
btm
-
top
;
rect
(
fill
,
lft
,
top
,
barWid
,
barHgt
);
let
x
=
ori
===
0
?
Math
.
round
(
lft
-
xOff
)
:
Math
.
round
(
top
-
yOff
);
let
y
=
ori
===
0
?
Math
.
round
(
top
-
yOff
)
:
Math
.
round
(
lft
-
xOff
);
let
w
=
ori
===
0
?
barWid
:
barHgt
;
let
h
=
ori
===
0
?
barHgt
:
barWid
;
qt
.
add
({
x
,
y
,
w
,
h
,
sidx
:
sidx
,
didx
:
ix
});
}
});
return
{
stroke
:
fill
,
fill
,
};
}
);
};
const
drawPoints
:
Series
.
Points
.
Show
=
formatValue
==
null
?
false
:
(
u
,
sidx
,
i0
,
i1
)
=>
{
u
.
ctx
.
font
=
font
;
u
.
ctx
.
fillStyle
=
'white'
;
uPlot
.
orient
(
u
,
sidx
,
(
series
,
dataX
,
dataY
,
scaleX
,
scaleY
,
valToPosX
,
valToPosY
,
xOff
,
yOff
,
xDim
,
yDim
,
moveTo
,
lineTo
,
rect
)
=>
{
let
numGroups
=
dataX
.
length
;
let
barsPerGroup
=
u
.
series
.
length
-
1
;
const
_dir
=
dir
*
(
ori
===
0
?
1
:
-
1
);
walkTwo
(
groupWidth
,
barWidth
,
sidx
-
1
,
numGroups
,
barsPerGroup
,
xDim
,
null
,
(
ix
,
x0
,
wid
)
=>
{
let
lft
=
Math
.
round
(
xOff
+
(
_dir
===
1
?
x0
:
xDim
-
x0
-
wid
));
let
barWid
=
Math
.
round
(
wid
);
// prettier-ignore
if
(
dataY
[
ix
]
!=
null
)
{
let
yPos
=
valToPosY
(
dataY
[
ix
]
!
,
scaleY
,
yDim
,
yOff
);
/* eslint-disable no-multi-spaces */
let
x
=
ori
===
0
?
Math
.
round
(
lft
+
barWid
/
2
)
:
Math
.
round
(
yPos
);
let
y
=
ori
===
0
?
Math
.
round
(
yPos
)
:
Math
.
round
(
lft
+
barWid
/
2
);
u
.
ctx
.
textAlign
=
ori
===
0
?
'center'
:
dataY
[
ix
]
!
>=
0
?
'left'
:
'right'
;
u
.
ctx
.
textBaseline
=
ori
===
1
?
'middle'
:
dataY
[
ix
]
!
>=
0
?
'bottom'
:
'top'
;
/* eslint-enable */
u
.
ctx
.
fillText
(
formatValue
(
sidx
,
dataY
[
ix
]),
x
,
y
);
}
});
}
);
return
false
;
};
/*
const yRange: Scale.Range = (u, dataMin, dataMax) => {
// @ts-ignore
let [min, max] = uPlot.rangeNum(0, dataMax, 0.05, true);
return [0, max];
};
*/
const
xSplits
:
Axis
.
Splits
=
(
u
:
uPlot
,
axisIdx
:
number
)
=>
{
const
dim
=
ori
===
0
?
u
.
bbox
.
width
:
u
.
bbox
.
height
;
const
_dir
=
dir
*
(
ori
===
0
?
1
:
-
1
);
let
splits
:
number
[]
=
[];
distribute
(
u
.
data
[
0
].
length
,
groupWidth
,
groupDistr
,
null
,
(
di
,
lftPct
,
widPct
)
=>
{
let
groupLftPx
=
(
dim
*
lftPct
)
/
pxRatio
;
let
groupWidPx
=
(
dim
*
widPct
)
/
pxRatio
;
let
groupCenterPx
=
groupLftPx
+
groupWidPx
/
2
;
splits
.
push
(
u
.
posToVal
(
groupCenterPx
,
'x'
));
});
return
_dir
===
1
?
splits
:
splits
.
reverse
();
};
// @ts-ignore
const
xValues
:
Axis
.
Values
=
(
u
)
=>
u
.
data
[
0
];
let
hovered
:
Rect
|
null
=
null
;
let
barMark
=
document
.
createElement
(
'div'
);
barMark
.
classList
.
add
(
'bar-mark'
);
barMark
.
style
.
position
=
'absolute'
;
barMark
.
style
.
background
=
'rgba(255,255,255,0.4)'
;
// hide crosshair cursor & hover points
const
cursor
:
Cursor
=
{
x
:
false
,
y
:
false
,
points
:
{
show
:
false
,
},
};
// disable selection
// uPlot types do not export the Select interface prior to 1.6.4
const
select
:
Partial
<
BBox
>
=
{
show
:
false
,
};
const
init
=
(
u
:
uPlot
)
=>
{
let
over
=
u
.
root
.
querySelector
(
'.u-over'
)
!
as
HTMLElement
;
over
.
style
.
overflow
=
'hidden'
;
over
.
appendChild
(
barMark
);
};
const
drawClear
=
(
u
:
uPlot
)
=>
{
qt
=
qt
||
new
Quadtree
(
0
,
0
,
u
.
bbox
.
width
,
u
.
bbox
.
height
);
qt
.
clear
();
// clear the path cache to force drawBars() to rebuild new quadtree
u
.
series
.
forEach
((
s
)
=>
{
// @ts-ignore
s
.
_paths
=
null
;
});
};
// handle hover interaction with quadtree probing
const
setCursor
=
(
u
:
uPlot
)
=>
{
let
found
:
Rect
|
null
=
null
;
let
cx
=
u
.
cursor
.
left
!
*
pxRatio
;
let
cy
=
u
.
cursor
.
top
!
*
pxRatio
;
qt
.
get
(
cx
,
cy
,
1
,
1
,
(
o
)
=>
{
if
(
pointWithin
(
cx
,
cy
,
o
.
x
,
o
.
y
,
o
.
x
+
o
.
w
,
o
.
y
+
o
.
h
))
{
found
=
o
;
}
});
if
(
found
)
{
// prettier-ignore
if
(
found
!==
hovered
)
{
/* eslint-disable no-multi-spaces */
barMark
.
style
.
display
=
''
;
barMark
.
style
.
left
=
found
!
.
x
/
pxRatio
+
'px'
;
barMark
.
style
.
top
=
found
!
.
y
/
pxRatio
+
'px'
;
barMark
.
style
.
width
=
found
!
.
w
/
pxRatio
+
'px'
;
barMark
.
style
.
height
=
found
!
.
h
/
pxRatio
+
'px'
;
hovered
=
found
;
/* eslint-enable */
if
(
onHover
!=
null
)
{
onHover
(
hovered
!
.
sidx
,
hovered
!
.
didx
);
}
}
}
else
if
(
hovered
!=
null
)
{
if
(
onLeave
!=
null
)
{
onLeave
(
hovered
!
.
sidx
,
hovered
!
.
didx
);
}
hovered
=
null
;
barMark
.
style
.
display
=
'none'
;
}
};
return
{
// cursor & select opts
cursor
,
select
,
// scale & axis opts
// yRange,
xValues
,
xSplits
,
// pathbuilders
drawBars
,
drawPoints
,
// hooks
init
,
drawClear
,
setCursor
,
};
}
packages/grafana-ui/src/components/BarChart/distribute.ts
0 → 100644
View file @
26b168f7
function
roundDec
(
val
:
number
,
dec
:
number
)
{
return
Math
.
round
(
val
*
(
dec
=
10
**
dec
))
/
dec
;
}
export
const
SPACE_BETWEEN
=
1
;
export
const
SPACE_AROUND
=
2
;
export
const
SPACE_EVENLY
=
3
;
const
coord
=
(
i
:
number
,
offs
:
number
,
iwid
:
number
,
gap
:
number
)
=>
roundDec
(
offs
+
i
*
(
iwid
+
gap
),
6
);
export
type
Each
=
(
idx
:
number
,
offPct
:
number
,
dimPct
:
number
)
=>
void
;
/**
* @internal
*/
export
function
distribute
(
numItems
:
number
,
sizeFactor
:
number
,
justify
:
number
,
onlyIdx
:
number
|
null
,
each
:
Each
)
{
let
space
=
1
-
sizeFactor
;
/* eslint-disable no-multi-spaces */
// prettier-ignore
let
gap
=
(
justify
===
SPACE_BETWEEN
?
space
/
(
numItems
-
1
)
:
justify
===
SPACE_AROUND
?
space
/
(
numItems
)
:
justify
===
SPACE_EVENLY
?
space
/
(
numItems
+
1
)
:
0
);
if
(
isNaN
(
gap
)
||
gap
===
Infinity
)
{
gap
=
0
;
}
// prettier-ignore
let
offs
=
(
justify
===
SPACE_BETWEEN
?
0
:
justify
===
SPACE_AROUND
?
gap
/
2
:
justify
===
SPACE_EVENLY
?
gap
:
0
);
/* eslint-enable */
let
iwid
=
sizeFactor
/
numItems
;
let
_iwid
=
roundDec
(
iwid
,
6
);
if
(
onlyIdx
==
null
)
{
for
(
let
i
=
0
;
i
<
numItems
;
i
++
)
{
each
(
i
,
coord
(
i
,
offs
,
iwid
,
gap
),
_iwid
);
}
}
else
{
each
(
onlyIdx
,
coord
(
onlyIdx
,
offs
,
iwid
,
gap
),
_iwid
);
}
}
packages/grafana-ui/src/components/BarChart/quadtree.ts
0 → 100644
View file @
26b168f7
const
MAX_OBJECTS
=
10
;
const
MAX_LEVELS
=
4
;
export
type
Quads
=
[
Quadtree
,
Quadtree
,
Quadtree
,
Quadtree
];
export
type
Rect
=
{
x
:
number
;
y
:
number
;
w
:
number
;
h
:
number
;
[
_
:
string
]:
any
};
/**
* @internal
*/
export
function
pointWithin
(
px
:
number
,
py
:
number
,
rlft
:
number
,
rtop
:
number
,
rrgt
:
number
,
rbtm
:
number
)
{
return
px
>=
rlft
&&
px
<=
rrgt
&&
py
>=
rtop
&&
py
<=
rbtm
;
}
/**
* @internal
*/
export
class
Quadtree
{
o
:
Rect
[];
q
:
Quads
|
null
;
constructor
(
public
x
:
number
,
public
y
:
number
,
public
w
:
number
,
public
h
:
number
,
public
l
:
number
=
0
)
{
this
.
o
=
[];
this
.
q
=
null
;
}
split
()
{
let
t
=
this
,
x
=
t
.
x
,
y
=
t
.
y
,
w
=
t
.
w
/
2
,
h
=
t
.
h
/
2
,
l
=
t
.
l
+
1
;
t
.
q
=
[
// top right
new
Quadtree
(
x
+
w
,
y
,
w
,
h
,
l
),
// top left
new
Quadtree
(
x
,
y
,
w
,
h
,
l
),
// bottom left
new
Quadtree
(
x
,
y
+
h
,
w
,
h
,
l
),
// bottom right
new
Quadtree
(
x
+
w
,
y
+
h
,
w
,
h
,
l
),
];
}
// invokes callback with index of each overlapping quad
quads
(
x
:
number
,
y
:
number
,
w
:
number
,
h
:
number
,
cb
:
(
q
:
Quadtree
)
=>
void
)
{
let
t
=
this
,
q
=
t
.
q
!
,
hzMid
=
t
.
x
+
t
.
w
/
2
,
vtMid
=
t
.
y
+
t
.
h
/
2
,
startIsNorth
=
y
<
vtMid
,
startIsWest
=
x
<
hzMid
,
endIsEast
=
x
+
w
>
hzMid
,
endIsSouth
=
y
+
h
>
vtMid
;
// top-right quad
startIsNorth
&&
endIsEast
&&
cb
(
q
[
0
]);
// top-left quad
startIsWest
&&
startIsNorth
&&
cb
(
q
[
1
]);
// bottom-left quad
startIsWest
&&
endIsSouth
&&
cb
(
q
[
2
]);
// bottom-right quad
endIsEast
&&
endIsSouth
&&
cb
(
q
[
3
]);
}
add
(
o
:
Rect
)
{
let
t
=
this
;
if
(
t
.
q
!=
null
)
{
t
.
quads
(
o
.
x
,
o
.
y
,
o
.
w
,
o
.
h
,
(
q
)
=>
{
q
.
add
(
o
);
});
}
else
{
let
os
=
t
.
o
;
os
.
push
(
o
);
if
(
os
.
length
>
MAX_OBJECTS
&&
t
.
l
<
MAX_LEVELS
)
{
t
.
split
();
for
(
let
i
=
0
;
i
<
os
.
length
;
i
++
)
{
let
oi
=
os
[
i
];
t
.
quads
(
oi
.
x
,
oi
.
y
,
oi
.
w
,
oi
.
h
,
(
q
)
=>
{
q
.
add
(
oi
);
});
}
t
.
o
.
length
=
0
;
}
}
}
get
(
x
:
number
,
y
:
number
,
w
:
number
,
h
:
number
,
cb
:
(
o
:
Rect
)
=>
void
)
{
let
t
=
this
;
let
os
=
t
.
o
;
for
(
let
i
=
0
;
i
<
os
.
length
;
i
++
)
{
cb
(
os
[
i
]);
}
if
(
t
.
q
!=
null
)
{
t
.
quads
(
x
,
y
,
w
,
h
,
(
q
)
=>
{
q
.
get
(
x
,
y
,
w
,
h
,
cb
);
});
}
}
clear
()
{
this
.
o
.
length
=
0
;
this
.
q
=
null
;
}
}
packages/grafana-ui/src/components/BarChart/types.ts
0 → 100644
View file @
26b168f7
import
{
VizOrientation
}
from
'@grafana/data'
;
import
{
AxisConfig
,
GraphGradientMode
,
HideableFieldConfig
}
from
'../uPlot/config'
;
import
{
VizLegendOptions
}
from
'../VizLegend/types'
;
/**
* @alpha
*/
export
enum
BarStackingMode
{
None
=
'none'
,
Standard
=
'standard'
,
Percent
=
'percent'
,
}
/**
* @alpha
*/
export
enum
BarValueVisibility
{
Auto
=
'auto'
,
Never
=
'never'
,
Always
=
'always'
,
}
/**
* @alpha
*/
export
interface
BarChartOptions
{
orientation
:
VizOrientation
;
legend
:
VizLegendOptions
;
stacking
:
BarStackingMode
;
showValue
:
BarValueVisibility
;
barWidth
:
number
;
groupWidth
:
number
;
}
/**
* @alpha
*/
export
interface
BarChartFieldConfig
extends
AxisConfig
,
HideableFieldConfig
{
lineWidth
?:
number
;
// 0
fillOpacity
?:
number
;
// 100
gradientMode
?:
GraphGradientMode
;
}
/**
* @alpha
*/
export
const
defaultBarChartFieldConfig
:
BarChartFieldConfig
=
{
lineWidth
:
1
,
fillOpacity
:
80
,
gradientMode
:
GraphGradientMode
.
None
,
axisSoftMin
:
0
,
};
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
View file @
26b168f7
...
...
@@ -18,7 +18,14 @@ import {
import
{
useTheme
}
from
'../../themes'
;
import
{
UPlotChart
}
from
'../uPlot/Plot'
;
import
{
PlotProps
}
from
'../uPlot/types'
;
import
{
AxisPlacement
,
DrawStyle
,
GraphFieldConfig
,
PointVisibility
}
from
'../uPlot/config'
;
import
{
AxisPlacement
,
DrawStyle
,
GraphFieldConfig
,
PointVisibility
,
ScaleDirection
,
ScaleOrientation
,
}
from
'../uPlot/config'
;
import
{
VizLayout
}
from
'../VizLayout/VizLayout'
;
import
{
LegendDisplayMode
,
VizLegendItem
,
VizLegendOptions
}
from
'../VizLegend/types'
;
import
{
VizLegend
}
from
'../VizLegend/VizLegend'
;
...
...
@@ -123,6 +130,8 @@ export const GraphNG: React.FC<GraphNGProps> = ({
if
(
xField
.
type
===
FieldType
.
time
)
{
builder
.
addScale
({
scaleKey
:
'x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
isTime
:
true
,
range
:
()
=>
{
const
r
=
currentTimeRange
.
current
!
;
...
...
@@ -141,6 +150,8 @@ export const GraphNG: React.FC<GraphNGProps> = ({
// Not time!
builder
.
addScale
({
scaleKey
:
'x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
});
builder
.
addAxis
({
...
...
@@ -170,18 +181,20 @@ export const GraphNG: React.FC<GraphNGProps> = ({
const
scaleColor
=
getFieldSeriesColor
(
field
,
theme
);
const
seriesColor
=
scaleColor
.
color
;
if
(
customConfig
.
axisPlacement
!==
AxisPlacement
.
Hidden
)
{
// The builder will manage unique scaleKeys and combine where appropriate
builder
.
addScale
({
scaleKey
,
distribution
:
customConfig
.
scaleDistribution
?.
type
,
log
:
customConfig
.
scaleDistribution
?.
log
,
min
:
field
.
config
.
min
,
max
:
field
.
config
.
max
,
softMin
:
customConfig
.
axisSoftMin
,
softMax
:
customConfig
.
axisSoftMax
,
});
// The builder will manage unique scaleKeys and combine where appropriate
builder
.
addScale
({
scaleKey
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
distribution
:
customConfig
.
scaleDistribution
?.
type
,
log
:
customConfig
.
scaleDistribution
?.
log
,
min
:
field
.
config
.
min
,
max
:
field
.
config
.
max
,
softMin
:
customConfig
.
axisSoftMin
,
softMax
:
customConfig
.
axisSoftMax
,
});
if
(
customConfig
.
axisPlacement
!==
AxisPlacement
.
Hidden
)
{
builder
.
addAxis
({
scaleKey
,
label
:
customConfig
.
axisLabel
,
...
...
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
View file @
26b168f7
...
...
@@ -10,7 +10,14 @@ import {
FieldConfig
,
getFieldDisplayName
,
}
from
'@grafana/data'
;
import
{
AxisPlacement
,
DrawStyle
,
GraphFieldConfig
,
PointVisibility
}
from
'../uPlot/config'
;
import
{
AxisPlacement
,
DrawStyle
,
GraphFieldConfig
,
PointVisibility
,
ScaleDirection
,
ScaleOrientation
,
}
from
'../uPlot/config'
;
import
{
UPlotConfigBuilder
}
from
'../uPlot/config/UPlotConfigBuilder'
;
import
{
UPlotChart
}
from
'../uPlot/Plot'
;
import
{
Themeable
}
from
'../../types'
;
...
...
@@ -91,6 +98,8 @@ export class Sparkline extends PureComponent<Props, State> {
const
xField
=
data
.
fields
[
0
];
builder
.
addScale
({
scaleKey
:
'x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
isTime
:
false
,
//xField.type === FieldType.time,
range
:
()
=>
{
const
{
sparkline
}
=
this
.
props
;
...
...
@@ -124,7 +133,13 @@ export class Sparkline extends PureComponent<Props, State> {
}
const
scaleKey
=
config
.
unit
||
'__fixed'
;
builder
.
addScale
({
scaleKey
,
min
:
field
.
config
.
min
,
max
:
field
.
config
.
max
});
builder
.
addScale
({
scaleKey
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
min
:
field
.
config
.
min
,
max
:
field
.
config
.
max
,
});
builder
.
addAxis
({
scaleKey
,
theme
,
...
...
packages/grafana-ui/src/components/VizLegend/VizLegend.tsx
View file @
26b168f7
...
...
@@ -3,6 +3,9 @@ import { LegendProps, LegendDisplayMode } from './types';
import
{
VizLegendTable
}
from
'./VizLegendTable'
;
import
{
VizLegendList
}
from
'./VizLegendList'
;
/**
* @public
*/
export
const
VizLegend
:
React
.
FunctionComponent
<
LegendProps
>
=
({
items
,
displayMode
,
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx
View file @
26b168f7
...
...
@@ -9,6 +9,9 @@ import { VizLegendListItem } from './VizLegendListItem';
export
interface
Props
extends
VizLegendBaseProps
{}
/**
* @internal
*/
export
const
VizLegendList
:
React
.
FunctionComponent
<
Props
>
=
({
items
,
itemRenderer
,
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx
View file @
26b168f7
...
...
@@ -14,6 +14,9 @@ export interface Props {
onSeriesColorChange
?:
SeriesColorChangeHandler
;
}
/**
* @internal
*/
export
const
VizLegendListItem
:
React
.
FunctionComponent
<
Props
>
=
({
item
,
onSeriesColorChange
,
onLabelClick
})
=>
{
const
styles
=
useStyles
(
getStyles
);
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendSeriesIcon.tsx
View file @
26b168f7
...
...
@@ -8,6 +8,9 @@ interface Props {
onColorChange
:
(
color
:
string
)
=>
void
;
}
/**
* @internal
*/
export
const
VizLegendSeriesIcon
:
React
.
FunctionComponent
<
Props
>
=
({
disabled
,
color
,
onColorChange
})
=>
{
return
disabled
?
(
<
SeriesIcon
color=
{
color
}
/>
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendStatsList.tsx
View file @
26b168f7
...
...
@@ -18,6 +18,9 @@ const VizLegendItemStat: React.FunctionComponent<{ stat: DisplayValue }> = ({ st
VizLegendItemStat
.
displayName
=
'VizLegendItemStat'
;
/**
* @internal
*/
export
const
VizLegendStatsList
:
React
.
FunctionComponent
<
{
stats
:
DisplayValue
[]
}
>
=
({
stats
})
=>
{
if
(
stats
.
length
===
0
)
{
return
null
;
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx
View file @
26b168f7
...
...
@@ -8,6 +8,9 @@ import sortBy from 'lodash/sortBy';
import
{
LegendTableItem
}
from
'./VizLegendTableItem'
;
import
{
GrafanaTheme
}
from
'@grafana/data'
;
/**
* @internal
*/
export
const
VizLegendTable
:
FC
<
VizLegendTableProps
>
=
({
items
,
sortBy
:
sortKey
,
...
...
packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx
View file @
26b168f7
...
...
@@ -15,6 +15,9 @@ export interface Props {
onSeriesColorChange
?:
SeriesColorChangeHandler
;
}
/**
* @internal
*/
export
const
LegendTableItem
:
React
.
FunctionComponent
<
Props
>
=
({
item
,
onSeriesColorChange
,
...
...
packages/grafana-ui/src/components/index.ts
View file @
26b168f7
...
...
@@ -207,5 +207,7 @@ export * from './uPlot/plugins';
export
{
useRefreshAfterGraphRendered
}
from
'./uPlot/hooks'
;
export
{
usePlotContext
,
usePlotData
,
usePlotPluginContext
}
from
'./uPlot/context'
;
export
{
GraphNG
,
FIXED_UNIT
}
from
'./GraphNG/GraphNG'
;
export
{
BarChart
}
from
'./BarChart/BarChart'
;
export
{
BarChartOptions
,
BarStackingMode
,
BarValueVisibility
,
BarChartFieldConfig
}
from
'./BarChart/types'
;
export
{
GraphNGLegendEvent
,
GraphNGLegendEventMode
}
from
'./GraphNG/types'
;
export
*
from
'./NodeGraph'
;
packages/grafana-ui/src/components/uPlot/config.ts
View file @
26b168f7
...
...
@@ -46,6 +46,26 @@ export enum LineInterpolation {
export
enum
ScaleDistribution
{
Linear
=
'linear'
,
Logarithmic
=
'log'
,
Ordinal
=
'ordinal'
,
}
/**
* @alpha
*/
export
enum
ScaleOrientation
{
Horizontal
=
0
,
Vertical
=
1
,
}
/**
* @alpha
*/
export
enum
ScaleDirection
{
Up
=
1
,
Right
=
1
,
Down
=
-
1
,
Left
=
-
1
,
}
/**
...
...
@@ -129,10 +149,16 @@ export interface HideSeriesConfig {
/**
* @alpha
*/
export
interface
GraphFieldConfig
extends
LineConfig
,
FillConfig
,
PointsConfig
,
AxisConfig
{
export
interface
HideableFieldConfig
{
hideFrom
?:
HideSeriesConfig
;
}
/**
* @alpha
*/
export
interface
GraphFieldConfig
extends
LineConfig
,
FillConfig
,
PointsConfig
,
AxisConfig
,
HideableFieldConfig
{
drawStyle
?:
DrawStyle
;
gradientMode
?:
GraphGradientMode
;
hideFrom
?:
HideSeriesConfig
;
}
/**
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts
View file @
26b168f7
...
...
@@ -11,9 +11,12 @@ export interface AxisProps {
label
?:
string
;
show
?:
boolean
;
size
?:
number
|
null
;
gap
?:
number
;
placement
?:
AxisPlacement
;
grid
?:
boolean
;
ticks
?:
boolean
;
formatValue
?:
(
v
:
any
)
=>
string
;
splits
?:
Axis
.
Splits
;
values
?:
any
;
isTime
?:
boolean
;
timeZone
?:
TimeZone
;
...
...
@@ -37,7 +40,10 @@ export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
show
=
true
,
placement
=
AxisPlacement
.
Auto
,
grid
=
true
,
ticks
=
true
,
gap
=
5
,
formatValue
,
splits
,
values
,
isTime
,
timeZone
,
...
...
@@ -54,16 +60,18 @@ export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
font
:
`12px 'Roboto'`
,
labelFont
:
`12px 'Roboto'`
,
size
:
this
.
props
.
size
??
calculateAxisSize
,
gap
,
grid
:
{
show
:
grid
,
stroke
:
gridColor
,
width
:
1
/
devicePixelRatio
,
},
ticks
:
{
show
:
t
rue
,
show
:
t
icks
,
stroke
:
gridColor
,
width
:
1
/
devicePixelRatio
,
},
splits
,
values
:
values
,
space
:
calculateSpace
,
};
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts
View file @
26b168f7
...
...
@@ -2,7 +2,15 @@
import
{
UPlotConfigBuilder
}
from
'./UPlotConfigBuilder'
;
import
{
GrafanaTheme
}
from
'@grafana/data'
;
import
{
GraphGradientMode
,
AxisPlacement
,
DrawStyle
,
PointVisibility
,
ScaleDistribution
}
from
'../config'
;
import
{
GraphGradientMode
,
AxisPlacement
,
DrawStyle
,
PointVisibility
,
ScaleDistribution
,
ScaleOrientation
,
ScaleDirection
,
}
from
'../config'
;
import
darkTheme
from
'../../../themes/dark'
;
describe
(
'UPlotConfigBuilder'
,
()
=>
{
...
...
@@ -26,7 +34,9 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -41,11 +51,15 @@ describe('UPlotConfigBuilder', () => {
builder
.
addScale
({
scaleKey
:
'scale-x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
isTime
:
true
,
});
builder
.
addScale
({
scaleKey
:
'scale-y'
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
isTime
:
false
,
});
...
...
@@ -66,20 +80,26 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {
"scale-x": Object {
"auto": false,
"dir": 1,
"ori": 0,
"range": [Function],
"time": true,
},
"scale-y": Object {
"auto": true,
"dir": 1,
"distr": 1,
"log": undefined,
"ori": 1,
"range": [Function],
"time": false,
},
},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -92,11 +112,15 @@ describe('UPlotConfigBuilder', () => {
builder
.
addScale
({
scaleKey
:
'scale-x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
isTime
:
true
,
});
builder
.
addScale
({
scaleKey
:
'scale-x'
,
orientation
:
ScaleOrientation
.
Horizontal
,
direction
:
ScaleDirection
.
Right
,
isTime
:
false
,
});
...
...
@@ -109,6 +133,8 @@ describe('UPlotConfigBuilder', () => {
builder
.
addScale
({
scaleKey
:
'scale-y'
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
isTime
:
false
,
distribution
:
ScaleDistribution
.
Linear
,
});
...
...
@@ -129,15 +155,19 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {
"scale-y": Object {
"auto": true,
"dir": 1,
"distr": 1,
"log": undefined,
"ori": 1,
"range": [Function],
"time": false,
},
},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -150,6 +180,8 @@ describe('UPlotConfigBuilder', () => {
builder
.
addScale
({
scaleKey
:
'scale-y'
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
isTime
:
false
,
distribution
:
ScaleDistribution
.
Linear
,
});
...
...
@@ -171,15 +203,19 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {
"scale-y": Object {
"auto": true,
"dir": 1,
"distr": 1,
"log": undefined,
"ori": 1,
"range": [Function],
"time": false,
},
},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -192,6 +228,8 @@ describe('UPlotConfigBuilder', () => {
builder
.
addScale
({
scaleKey
:
'scale-y'
,
orientation
:
ScaleOrientation
.
Vertical
,
direction
:
ScaleDirection
.
Up
,
isTime
:
false
,
distribution
:
ScaleDistribution
.
Linear
,
log
:
10
,
...
...
@@ -214,15 +252,19 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {
"scale-y": Object {
"auto": true,
"dir": 1,
"distr": 1,
"log": undefined,
"ori": 1,
"range": [Function],
"time": false,
},
},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -254,6 +296,7 @@ describe('UPlotConfigBuilder', () => {
"axes": Array [
Object {
"font": "12px 'Roboto'",
"gap": 5,
"grid": Object {
"show": false,
"stroke": "#ffffff",
...
...
@@ -267,6 +310,7 @@ describe('UPlotConfigBuilder', () => {
"side": 2,
"size": [Function],
"space": [Function],
"splits": undefined,
"stroke": "gray",
"ticks": Object {
"show": true,
...
...
@@ -291,7 +335,9 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {},
"select": undefined,
"series": Array [
Object {},
],
...
...
@@ -410,7 +456,9 @@ describe('UPlotConfigBuilder', () => {
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {},
"select": undefined,
"series": Array [
Object {},
Object {
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts
View file @
26b168f7
import
{
Plot
Series
Config
}
from
'../types'
;
import
{
PlotConfig
}
from
'../types'
;
import
{
ScaleProps
,
UPlotScaleBuilder
}
from
'./UPlotScaleBuilder'
;
import
{
SeriesProps
,
UPlotSeriesBuilder
}
from
'./UPlotSeriesBuilder'
;
import
{
AxisProps
,
UPlotAxisBuilder
}
from
'./UPlotAxisBuilder'
;
import
{
AxisPlacement
}
from
'../config'
;
import
{
Cursor
,
Band
}
from
'uplot'
;
import
{
Cursor
,
Band
,
Hooks
,
BBox
}
from
'uplot'
;
import
{
defaultsDeep
}
from
'lodash'
;
type
valueof
<
T
>
=
T
[
keyof
T
];
export
class
UPlotConfigBuilder
{
private
series
:
UPlotSeriesBuilder
[]
=
[];
private
axes
:
Record
<
string
,
UPlotAxisBuilder
>
=
{};
private
scales
:
UPlotScaleBuilder
[]
=
[];
private
bands
:
Band
[]
=
[];
private
cursor
:
Cursor
|
undefined
;
// uPlot types don't export the Select interface prior to 1.6.4
private
select
:
Partial
<
BBox
>
|
undefined
;
private
hasLeftAxis
=
false
;
private
hasBottomAxis
=
false
;
private
hooks
:
Hooks
.
Arrays
=
{};
addHook
(
type
:
keyof
Hooks
.
Defs
,
hook
:
valueof
<
Hooks
.
Defs
>
)
{
if
(
!
this
.
hooks
[
type
])
{
this
.
hooks
[
type
]
=
[];
}
this
.
hooks
[
type
]
!
.
push
(
hook
as
any
);
}
addAxis
(
props
:
AxisProps
)
{
props
.
placement
=
props
.
placement
??
AxisPlacement
.
Auto
;
...
...
@@ -54,6 +67,11 @@ export class UPlotConfigBuilder {
this
.
cursor
=
cursor
;
}
// uPlot types don't export the Select interface prior to 1.6.4
setSelect
(
select
:
Partial
<
BBox
>
)
{
this
.
select
=
select
;
}
addSeries
(
props
:
SeriesProps
)
{
this
.
series
.
push
(
new
UPlotSeriesBuilder
(
props
));
}
...
...
@@ -77,13 +95,19 @@ export class UPlotConfigBuilder {
}
getConfig
()
{
const
config
:
Plot
Series
Config
=
{
series
:
[{}]
};
const
config
:
PlotConfig
=
{
series
:
[{}]
};
config
.
axes
=
this
.
ensureNonOverlappingAxes
(
Object
.
values
(
this
.
axes
)).
map
((
a
)
=>
a
.
getConfig
());
config
.
series
=
[...
config
.
series
,
...
this
.
series
.
map
((
s
)
=>
s
.
getConfig
())];
config
.
scales
=
this
.
scales
.
reduce
((
acc
,
s
)
=>
{
return
{
...
acc
,
...
s
.
getConfig
()
};
},
{});
config
.
hooks
=
this
.
hooks
;
/* @ts-ignore */
// uPlot types don't export the Select interface prior to 1.6.4
config
.
select
=
this
.
select
;
config
.
cursor
=
this
.
cursor
||
{};
// When bands exist, only keep fill when defined
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts
View file @
26b168f7
import
uPlot
,
{
Scale
,
Range
}
from
'uplot'
;
import
{
PlotConfigBuilder
}
from
'../types'
;
import
{
ScaleDistribution
}
from
'../config'
;
import
{
ScaleDistribution
,
ScaleOrientation
,
ScaleDirection
}
from
'../config'
;
export
interface
ScaleProps
{
scaleKey
:
string
;
...
...
@@ -9,8 +9,10 @@ export interface ScaleProps {
max
?:
number
|
null
;
softMin
?:
number
|
null
;
softMax
?:
number
|
null
;
range
?:
()
=>
number
[];
// min/max
range
?:
Scale
.
Range
;
distribution
?:
ScaleDistribution
;
orientation
:
ScaleOrientation
;
direction
:
ScaleDirection
;
log
?:
number
;
}
...
...
@@ -21,10 +23,25 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
}
getConfig
()
{
const
{
isTime
,
scaleKey
,
min
:
hardMin
,
max
:
hardMax
,
softMin
,
softMax
,
range
}
=
this
.
props
;
const
{
isTime
,
scaleKey
,
min
:
hardMin
,
max
:
hardMax
,
softMin
,
softMax
,
range
,
direction
,
orientation
,
}
=
this
.
props
;
const
distribution
=
!
isTime
?
{
distr
:
this
.
props
.
distribution
===
ScaleDistribution
.
Logarithmic
?
3
:
1
,
distr
:
this
.
props
.
distribution
===
ScaleDistribution
.
Logarithmic
?
3
:
this
.
props
.
distribution
===
ScaleDistribution
.
Ordinal
?
2
:
1
,
log
:
this
.
props
.
distribution
===
ScaleDistribution
.
Logarithmic
?
this
.
props
.
log
||
2
:
undefined
,
}
:
{};
...
...
@@ -57,7 +74,7 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
let
hardMinOnly
=
softMin
==
null
&&
hardMin
!=
null
;
let
hardMaxOnly
=
softMax
==
null
&&
hardMax
!=
null
;
if
(
scale
.
distr
===
1
)
{
if
(
scale
.
distr
===
1
||
scale
.
distr
===
2
)
{
// @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta
minMax
=
uPlot
.
rangeNum
(
hardMinOnly
?
hardMin
:
dataMin
,
hardMaxOnly
?
hardMax
:
dataMax
,
rangeConfig
);
}
else
if
(
scale
.
distr
===
3
)
{
...
...
@@ -81,6 +98,8 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
time
:
isTime
,
auto
:
!
isTime
,
range
:
range
??
rangeFn
,
dir
:
direction
,
ori
:
orientation
,
...
distribution
,
},
};
...
...
packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts
View file @
26b168f7
...
...
@@ -22,7 +22,9 @@ export interface SeriesProps extends LineConfig, FillConfig, PointsConfig {
/** Used when gradientMode is set to Scheme */
colorMode
?:
FieldColorMode
;
fieldName
:
string
;
drawStyle
:
DrawStyle
;
drawStyle
?:
DrawStyle
;
pathBuilder
?:
Series
.
PathBuilder
;
pointsBuilder
?:
Series
.
Points
.
Show
;
show
?:
boolean
;
dataFrameFieldIndex
?:
DataFrameFieldIndex
;
hideInLegend
?:
boolean
;
...
...
@@ -33,6 +35,8 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
getConfig
()
{
const
{
drawStyle
,
pathBuilder
,
pointsBuilder
,
lineInterpolation
,
lineWidth
,
lineStyle
,
...
...
@@ -46,9 +50,13 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
let
lineConfig
:
Partial
<
Series
>
=
{};
if
(
drawStyle
===
DrawStyle
.
Points
)
{
if
(
pathBuilder
!=
null
)
{
lineConfig
.
paths
=
pathBuilder
;
lineConfig
.
stroke
=
this
.
getLineColor
();
lineConfig
.
width
=
lineWidth
;
}
else
if
(
drawStyle
===
DrawStyle
.
Points
)
{
lineConfig
.
paths
=
()
=>
null
;
}
else
{
}
else
if
(
drawStyle
!=
null
)
{
lineConfig
.
stroke
=
this
.
getLineColor
();
lineConfig
.
width
=
lineWidth
;
if
(
lineStyle
&&
lineStyle
.
fill
!==
'solid'
)
{
...
...
@@ -71,18 +79,22 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
},
};
// we cannot set points.show property above (even to undefined) as that will clear uPlot's default auto behavior
if
(
drawStyle
===
DrawStyle
.
Points
)
{
pointsConfig
.
points
!
.
show
=
true
;
if
(
pointsBuilder
!=
null
)
{
pointsConfig
.
points
!
.
show
=
pointsBuilder
;
}
else
{
if
(
showPoints
===
PointVisibility
.
Auto
)
{
if
(
drawStyle
===
DrawStyle
.
Bars
)
{
// we cannot set points.show property above (even to undefined) as that will clear uPlot's default auto behavior
if
(
drawStyle
===
DrawStyle
.
Points
)
{
pointsConfig
.
points
!
.
show
=
true
;
}
else
{
if
(
showPoints
===
PointVisibility
.
Auto
)
{
if
(
drawStyle
===
DrawStyle
.
Bars
)
{
pointsConfig
.
points
!
.
show
=
false
;
}
}
else
if
(
showPoints
===
PointVisibility
.
Never
)
{
pointsConfig
.
points
!
.
show
=
false
;
}
else
if
(
showPoints
===
PointVisibility
.
Always
)
{
pointsConfig
.
points
!
.
show
=
true
;
}
}
else
if
(
showPoints
===
PointVisibility
.
Never
)
{
pointsConfig
.
points
!
.
show
=
false
;
}
else
if
(
showPoints
===
PointVisibility
.
Always
)
{
pointsConfig
.
points
!
.
show
=
true
;
}
}
...
...
packages/grafana-ui/src/components/uPlot/types.ts
View file @
26b168f7
...
...
@@ -3,7 +3,8 @@ import uPlot, { Options, Hooks } from 'uplot';
import
{
DataFrame
,
TimeRange
,
TimeZone
}
from
'@grafana/data'
;
import
{
UPlotConfigBuilder
}
from
'./config/UPlotConfigBuilder'
;
export
type
PlotSeriesConfig
=
Pick
<
Options
,
'series'
|
'scales'
|
'axes'
|
'cursor'
|
'bands'
>
;
export
type
PlotConfig
=
Pick
<
Options
,
'series'
|
'scales'
|
'axes'
|
'cursor'
|
'bands'
|
'hooks'
|
'select'
>
;
export
type
PlotPlugin
=
{
id
:
string
;
/** can mutate provided opts as necessary */
...
...
public/app/features/dashboard/components/PanelEditor/DefaultFieldConfigEditor.tsx
View file @
26b168f7
...
...
@@ -18,7 +18,7 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
const
renderEditor
=
useCallback
(
(
item
:
FieldConfigPropertyItem
,
categoryItemCount
:
number
)
=>
{
if
(
item
.
isCustom
&&
item
.
showIf
&&
!
item
.
showIf
(
config
.
defaults
.
custom
))
{
if
(
item
.
isCustom
&&
item
.
showIf
&&
!
item
.
showIf
(
config
.
defaults
.
custom
,
data
))
{
return
null
;
}
...
...
public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx
View file @
26b168f7
...
...
@@ -60,7 +60,7 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({
{
Object
.
keys
(
optionEditors
).
map
((
c
,
i
)
=>
{
const
optionsToShow
=
optionEditors
[
c
]
.
map
((
e
,
j
)
=>
{
if
(
e
.
showIf
&&
!
e
.
showIf
(
options
))
{
if
(
e
.
showIf
&&
!
e
.
showIf
(
options
,
data
))
{
return
null
;
}
...
...
public/app/features/plugins/built_in_plugins.ts
View file @
26b168f7
...
...
@@ -55,6 +55,7 @@ import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
import
*
as
gaugePanel
from
'app/plugins/panel/gauge/module'
;
import
*
as
pieChartPanel
from
'app/plugins/panel/piechart/module'
;
import
*
as
barGaugePanel
from
'app/plugins/panel/bargauge/module'
;
import
*
as
barChartPanel
from
'app/plugins/panel/barchart/module'
;
import
*
as
logsPanel
from
'app/plugins/panel/logs/module'
;
import
*
as
newsPanel
from
'app/plugins/panel/news/module'
;
import
*
as
livePanel
from
'app/plugins/panel/live/module'
;
...
...
@@ -101,6 +102,7 @@ const builtInPlugins: any = {
'app/plugins/panel/gauge/module'
:
gaugePanel
,
'app/plugins/panel/piechart/module'
:
pieChartPanel
,
'app/plugins/panel/bargauge/module'
:
barGaugePanel
,
'app/plugins/panel/barchart/module'
:
barChartPanel
,
'app/plugins/panel/logs/module'
:
logsPanel
,
'app/plugins/panel/welcome/module'
:
welcomeBanner
,
'app/plugins/panel/nodeGraph/module'
:
nodeGraph
,
...
...
public/app/plugins/panel/barchart/BarChartPanel.tsx
0 → 100755
View file @
26b168f7
import
React
,
{
useCallback
,
useMemo
}
from
'react'
;
import
{
DataFrame
,
Field
,
FieldType
,
PanelProps
}
from
'@grafana/data'
;
import
{
BarChart
,
BarChartOptions
,
GraphNGLegendEvent
}
from
'@grafana/ui'
;
import
{
changeSeriesColorConfigFactory
}
from
'../timeseries/overrides/colorSeriesConfigFactory'
;
import
{
hideSeriesConfigFactory
}
from
'../timeseries/overrides/hideSeriesConfigFactory'
;
import
{
config
}
from
'app/core/config'
;
interface
Props
extends
PanelProps
<
BarChartOptions
>
{}
interface
BarData
{
error
?:
string
;
frame
?:
DataFrame
;
// first string vs all numbers
}
/**
* @alpha
*/
export
const
BarChartPanel
:
React
.
FunctionComponent
<
Props
>
=
({
data
,
options
,
width
,
height
,
fieldConfig
,
onFieldConfigChange
,
})
=>
{
if
(
!
data
||
!
data
.
series
?.
length
)
{
return
(
<
div
className=
"panel-empty"
>
<
p
>
No data found in response
</
p
>
</
div
>
);
}
const
onLegendClick
=
useCallback
(
(
event
:
GraphNGLegendEvent
)
=>
{
onFieldConfigChange
(
hideSeriesConfigFactory
(
event
,
fieldConfig
,
data
.
series
));
},
[
fieldConfig
,
onFieldConfigChange
,
data
.
series
]
);
const
onSeriesColorChange
=
useCallback
(
(
label
:
string
,
color
:
string
)
=>
{
onFieldConfigChange
(
changeSeriesColorConfigFactory
(
label
,
color
,
fieldConfig
));
},
[
fieldConfig
,
onFieldConfigChange
]
);
const
barData
=
useMemo
<
BarData
>
(()
=>
{
const
firstFrame
=
data
.
series
[
0
];
const
firstString
=
firstFrame
.
fields
.
find
((
f
)
=>
f
.
type
===
FieldType
.
string
);
if
(
!
firstString
)
{
return
{
error
:
'Bar charts requires a string field'
,
};
}
const
fields
:
Field
[]
=
[
firstString
];
for
(
const
f
of
firstFrame
.
fields
)
{
if
(
f
.
type
===
FieldType
.
number
)
{
fields
.
push
(
f
);
}
}
if
(
fields
.
length
<
2
)
{
return
{
error
:
'No numeric fields found'
,
};
}
return
{
frame
:
{
...
firstFrame
,
fields
,
// filtered to to the values we have
},
};
},
[
width
,
height
,
options
,
data
]);
if
(
barData
.
error
)
{
return
(
<
div
className=
"panel-empty"
>
<
p
>
{
barData
.
error
}
</
p
>
</
div
>
);
}
if
(
!
barData
.
frame
)
{
return
(
<
div
className=
"panel-empty"
>
<
p
>
No data found in response
</
p
>
</
div
>
);
}
return
(
<
BarChart
data=
{
barData
.
frame
}
width=
{
width
}
height=
{
height
}
theme=
{
config
.
theme
}
onLegendClick=
{
onLegendClick
}
onSeriesColorChange=
{
onSeriesColorChange
}
{
...
options
}
/>
);
};
public/app/plugins/panel/barchart/img/barchart.svg
0 → 100644
View file @
26b168f7
<svg
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 78.59 80.42"
><defs><style>
.cls-1{fill:#3865ab;}.cls-2{fill:url(#linear-gradient);}.cls-3{fill:url(#linear-gradient-2);}.cls-4{fill:url(#linear-gradient-3);}
</style><linearGradient
id=
"linear-gradient"
x1=
"15.72"
y1=
"68.76"
x2=
"15.43"
y2=
"49.22"
gradientUnits=
"userSpaceOnUse"
><stop
offset=
"0"
stop-color=
"#f2cc0c"
/><stop
offset=
"1"
stop-color=
"#ff9830"
/></linearGradient><linearGradient
id=
"linear-gradient-2"
x1=
"45.26"
y1=
"68.7"
x2=
"44.55"
y2=
"24.23"
xlink:href=
"#linear-gradient"
/><linearGradient
id=
"linear-gradient-3"
x1=
"73.92"
y1=
"67.1"
x2=
"74.48"
y2=
"8.07"
xlink:href=
"#linear-gradient"
/></defs><g
id=
"Layer_2"
data-name=
"Layer 2"
><g
id=
"Icons"
><rect
class=
"cls-1"
y=
"33.91"
width=
"8.74"
height=
"38.3"
rx=
"1"
/><rect
class=
"cls-1"
x=
"29.31"
y=
"39.5"
width=
"8.74"
height=
"32.72"
rx=
"1"
/><rect
class=
"cls-1"
y=
"76.42"
width=
"78.59"
height=
"4"
rx=
"1"
/><rect
class=
"cls-1"
x=
"58.62"
y=
"9.13"
width=
"8.74"
height=
"63.08"
rx=
"1"
/><rect
class=
"cls-2"
x=
"11.22"
y=
"48"
width=
"8.74"
height=
"24.21"
rx=
"1"
/><rect
class=
"cls-3"
x=
"40.53"
y=
"20.1"
width=
"8.74"
height=
"52.12"
rx=
"1"
/><rect
class=
"cls-4"
x=
"69.84"
width=
"8.74"
height=
"72.21"
rx=
"1"
/></g></g></svg>
\ No newline at end of file
public/app/plugins/panel/barchart/module.tsx
0 → 100755
View file @
26b168f7
import
{
DataFrame
,
FieldColorModeId
,
FieldConfigProperty
,
FieldType
,
PanelPlugin
,
VizOrientation
,
}
from
'@grafana/data'
;
import
{
BarChartPanel
}
from
'./BarChartPanel'
;
import
{
BarChartFieldConfig
,
BarChartOptions
,
BarStackingMode
,
BarValueVisibility
,
graphFieldOptions
,
}
from
'@grafana/ui'
;
import
{
addAxisConfig
,
addHideFrom
,
addLegendOptions
}
from
'../timeseries/config'
;
import
{
defaultBarChartFieldConfig
}
from
'@grafana/ui/src/components/BarChart/types'
;
export
const
plugin
=
new
PanelPlugin
<
BarChartOptions
,
BarChartFieldConfig
>
(
BarChartPanel
)
.
useFieldConfig
({
standardOptions
:
{
[
FieldConfigProperty
.
Color
]:
{
settings
:
{
byValueSupport
:
false
,
},
defaultValue
:
{
mode
:
FieldColorModeId
.
PaletteClassic
,
},
},
},
useCustomConfig
:
(
builder
)
=>
{
const
cfg
=
defaultBarChartFieldConfig
;
builder
.
addSliderInput
({
path
:
'lineWidth'
,
name
:
'Line width'
,
defaultValue
:
cfg
.
lineWidth
,
settings
:
{
min
:
0
,
max
:
10
,
step
:
1
,
},
})
.
addSliderInput
({
path
:
'fillOpacity'
,
name
:
'Fill opacity'
,
defaultValue
:
cfg
.
fillOpacity
,
settings
:
{
min
:
0
,
max
:
100
,
step
:
1
,
},
})
.
addRadio
({
path
:
'gradientMode'
,
name
:
'Gradient mode'
,
defaultValue
:
graphFieldOptions
.
fillGradient
[
0
].
value
,
settings
:
{
options
:
graphFieldOptions
.
fillGradient
,
},
});
addAxisConfig
(
builder
,
cfg
,
true
);
addHideFrom
(
builder
);
},
})
.
setPanelOptions
((
builder
)
=>
{
builder
.
addRadio
({
path
:
'orientation'
,
name
:
'Orientation'
,
settings
:
{
options
:
[
{
value
:
VizOrientation
.
Auto
,
label
:
'Auto'
},
{
value
:
VizOrientation
.
Horizontal
,
label
:
'Horizontal'
},
{
value
:
VizOrientation
.
Vertical
,
label
:
'Vertical'
},
],
},
defaultValue
:
VizOrientation
.
Auto
,
})
.
addRadio
({
path
:
'stacking'
,
name
:
'Stacking'
,
settings
:
{
options
:
[
{
value
:
BarStackingMode
.
None
,
label
:
'None'
},
{
value
:
BarStackingMode
.
Standard
,
label
:
'Standard'
},
{
value
:
BarStackingMode
.
Percent
,
label
:
'Percent'
},
],
},
defaultValue
:
BarStackingMode
.
None
,
showIf
:
()
=>
false
,
// <<< Hide from the UI for now
})
.
addRadio
({
path
:
'showValue'
,
name
:
'Show values'
,
settings
:
{
options
:
[
{
value
:
BarValueVisibility
.
Auto
,
label
:
'Auto'
},
{
value
:
BarValueVisibility
.
Always
,
label
:
'Always'
},
{
value
:
BarValueVisibility
.
Never
,
label
:
'Never'
},
],
},
defaultValue
:
BarValueVisibility
.
Auto
,
})
.
addSliderInput
({
path
:
'groupWidth'
,
name
:
'Group width'
,
defaultValue
:
0.7
,
settings
:
{
min
:
0
,
max
:
1
,
step
:
0.01
,
},
showIf
:
(
c
,
data
)
=>
{
if
(
c
.
stacking
&&
c
.
stacking
!==
BarStackingMode
.
None
)
{
return
false
;
}
return
countNumberFields
(
data
)
!==
1
;
},
})
.
addSliderInput
({
path
:
'barWidth'
,
name
:
'Bar width'
,
defaultValue
:
0.97
,
settings
:
{
min
:
0
,
max
:
1
,
step
:
0.01
,
},
});
addLegendOptions
(
builder
);
});
function
countNumberFields
(
data
?:
DataFrame
[]):
number
{
let
count
=
0
;
if
(
data
)
{
for
(
const
frame
of
data
)
{
for
(
const
field
of
frame
.
fields
)
{
if
(
field
.
type
===
FieldType
.
number
)
{
count
++
;
}
}
}
}
return
count
;
}
public/app/plugins/panel/barchart/plugin.json
0 → 100755
View file @
26b168f7
{
"type"
:
"panel"
,
"name"
:
"Bar chart"
,
"id"
:
"barchart"
,
"state"
:
"alpha"
,
"info"
:
{
"author"
:
{
"name"
:
"Grafana Labs"
,
"url"
:
"https://grafana.com"
},
"logos"
:
{
"small"
:
"img/barchart.svg"
,
"large"
:
"img/barchart.svg"
}
}
}
public/app/plugins/panel/stat/types.ts
View file @
26b168f7
...
...
@@ -11,6 +11,7 @@ import {
FieldOverrideContext
,
getFieldDisplayName
,
escapeStringForRegex
,
VizOrientation
,
}
from
'@grafana/data'
;
import
{
PanelOptionsEditorBuilder
}
from
'@grafana/data'
;
...
...
@@ -101,12 +102,12 @@ export function addStandardDataReduceOptions(
description
:
'Stacking direction in case of multiple series or fields'
,
settings
:
{
options
:
[
{
value
:
'auto'
,
label
:
'Auto'
},
{
value
:
'horizontal'
,
label
:
'Horizontal'
},
{
value
:
'vertical'
,
label
:
'Vertical'
},
{
value
:
VizOrientation
.
Auto
,
label
:
'Auto'
},
{
value
:
VizOrientation
.
Horizontal
,
label
:
'Horizontal'
},
{
value
:
VizOrientation
.
Vertical
,
label
:
'Vertical'
},
],
},
defaultValue
:
'auto'
,
defaultValue
:
VizOrientation
.
Auto
,
});
}
...
...
public/app/plugins/panel/timeseries/config.ts
View file @
26b168f7
import
{
FieldColorModeId
,
FieldConfigEditorBuilder
,
FieldConfigProperty
,
FieldType
,
identityOverrideProcessor
,
...
...
@@ -21,6 +22,7 @@ import {
ScaleDistributionConfig
,
GraphGradientMode
,
LegendDisplayMode
,
AxisConfig
,
}
from
'@grafana/ui'
;
import
{
SeriesConfigEditor
}
from
'./HideSeriesConfigEditor'
;
import
{
ScaleDistributionEditor
}
from
'./ScaleDistributionEditor'
;
...
...
@@ -151,85 +153,103 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
step
:
1
,
},
showIf
:
(
c
)
=>
c
.
showPoints
!==
PointVisibility
.
Never
||
c
.
drawStyle
===
DrawStyle
.
Points
,
})
.
addRadio
({
path
:
'axisPlacement'
,
name
:
'Placement'
,
category
:
[
'Axis'
],
defaultValue
:
graphFieldOptions
.
axisPlacement
[
0
].
value
,
settings
:
{
options
:
graphFieldOptions
.
axisPlacement
,
},
})
.
addTextInput
({
path
:
'axisLabel'
,
name
:
'Label'
,
category
:
[
'Axis'
],
defaultValue
:
''
,
settings
:
{
placeholder
:
'Optional text'
,
},
showIf
:
(
c
)
=>
c
.
axisPlacement
!==
AxisPlacement
.
Hidden
,
// no matter what the field type is
shouldApply
:
()
=>
true
,
})
.
addNumberInput
({
path
:
'axisWidth'
,
name
:
'Width'
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'Auto'
,
},
showIf
:
(
c
)
=>
c
.
axisPlacement
!==
AxisPlacement
.
Hidden
,
})
.
addNumberInput
({
path
:
'axisSoftMin'
,
name
:
'Soft min'
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'See: Standard options > Min'
,
},
})
.
addNumberInput
({
path
:
'axisSoftMax'
,
name
:
'Soft max'
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'See: Standard options > Max'
,
},
})
.
addCustomEditor
<
void
,
ScaleDistributionConfig
>
({
id
:
'scaleDistribution'
,
path
:
'scaleDistribution'
,
name
:
'Scale'
,
category
:
[
'Axis'
],
editor
:
ScaleDistributionEditor
,
override
:
ScaleDistributionEditor
,
defaultValue
:
{
type
:
ScaleDistribution
.
Linear
},
shouldApply
:
(
f
)
=>
f
.
type
===
FieldType
.
number
,
process
:
identityOverrideProcessor
,
})
.
addCustomEditor
({
id
:
'hideFrom'
,
name
:
'Hide in area'
,
category
:
[
'Series'
],
path
:
'hideFrom'
,
defaultValue
:
{
tooltip
:
false
,
graph
:
false
,
legend
:
false
,
},
editor
:
SeriesConfigEditor
,
override
:
SeriesConfigEditor
,
shouldApply
:
()
=>
true
,
hideFromDefaults
:
true
,
hideFromOverrides
:
true
,
process
:
(
value
)
=>
value
,
});
addAxisConfig
(
builder
,
cfg
);
addHideFrom
(
builder
);
},
};
}
export
function
addHideFrom
(
builder
:
FieldConfigEditorBuilder
<
AxisConfig
>
)
{
builder
.
addCustomEditor
({
id
:
'hideFrom'
,
name
:
'Hide in area'
,
category
:
[
'Series'
],
path
:
'hideFrom'
,
defaultValue
:
{
tooltip
:
false
,
graph
:
false
,
legend
:
false
,
},
editor
:
SeriesConfigEditor
,
override
:
SeriesConfigEditor
,
shouldApply
:
()
=>
true
,
hideFromDefaults
:
true
,
hideFromOverrides
:
true
,
process
:
(
value
)
=>
value
,
});
}
export
function
addAxisConfig
(
builder
:
FieldConfigEditorBuilder
<
AxisConfig
>
,
defaultConfig
:
AxisConfig
,
hideScale
?:
boolean
)
{
builder
.
addRadio
({
path
:
'axisPlacement'
,
name
:
'Placement'
,
category
:
[
'Axis'
],
defaultValue
:
graphFieldOptions
.
axisPlacement
[
0
].
value
,
settings
:
{
options
:
graphFieldOptions
.
axisPlacement
,
},
})
.
addTextInput
({
path
:
'axisLabel'
,
name
:
'Label'
,
category
:
[
'Axis'
],
defaultValue
:
''
,
settings
:
{
placeholder
:
'Optional text'
,
},
showIf
:
(
c
)
=>
c
.
axisPlacement
!==
AxisPlacement
.
Hidden
,
// no matter what the field type is
shouldApply
:
()
=>
true
,
})
.
addNumberInput
({
path
:
'axisWidth'
,
name
:
'Width'
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'Auto'
,
},
showIf
:
(
c
)
=>
c
.
axisPlacement
!==
AxisPlacement
.
Hidden
,
})
.
addNumberInput
({
path
:
'axisSoftMin'
,
name
:
'Soft min'
,
defaultValue
:
defaultConfig
.
axisSoftMin
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'See: Standard options > Min'
,
},
})
.
addNumberInput
({
path
:
'axisSoftMax'
,
name
:
'Soft max'
,
defaultValue
:
defaultConfig
.
axisSoftMax
,
category
:
[
'Axis'
],
settings
:
{
placeholder
:
'See: Standard options > Max'
,
},
});
if
(
!
hideScale
)
{
builder
.
addCustomEditor
<
void
,
ScaleDistributionConfig
>
({
id
:
'scaleDistribution'
,
path
:
'scaleDistribution'
,
name
:
'Scale'
,
category
:
[
'Axis'
],
editor
:
ScaleDistributionEditor
,
override
:
ScaleDistributionEditor
,
defaultValue
:
{
type
:
ScaleDistribution
.
Linear
},
shouldApply
:
(
f
)
=>
f
.
type
===
FieldType
.
number
,
process
:
identityOverrideProcessor
,
});
}
}
export
function
addLegendOptions
(
builder
:
PanelOptionsEditorBuilder
<
OptionsWithLegend
>
)
{
builder
.
addRadio
({
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment