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
59467d3c
Unverified
Commit
59467d3c
authored
Apr 01, 2020
by
Dominik Prokop
Committed by
GitHub
Apr 01, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
NewPanelEdit: add search, scroll and some layout to vis tab (#23253)
parent
540d1d9b
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
157 additions
and
112 deletions
+157
-112
public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx
+41
-4
public/app/features/dashboard/panel_editor/VizTypePicker.tsx
+42
-35
public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx
+74
-17
public/sass/components/_panel_editor.scss
+0
-56
No files found.
public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx
View file @
59467d3c
import
React
,
{
FC
}
from
'react'
;
import
React
,
{
FC
,
useState
}
from
'react'
;
import
{
css
}
from
'emotion'
;
import
{
css
}
from
'emotion'
;
import
{
GrafanaTheme
,
PanelPlugin
,
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
GrafanaTheme
,
PanelPlugin
,
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
useTheme
,
stylesFactory
}
from
'@grafana/ui'
;
import
{
CustomScrollbar
,
useTheme
,
stylesFactory
,
Forms
,
Icon
}
from
'@grafana/ui'
;
import
{
changePanelPlugin
}
from
'../../state/actions'
;
import
{
changePanelPlugin
}
from
'../../state/actions'
;
import
{
StoreState
}
from
'app/types'
;
import
{
StoreState
}
from
'app/types'
;
import
{
PanelModel
}
from
'../../state/PanelModel'
;
import
{
PanelModel
}
from
'../../state/PanelModel'
;
...
@@ -23,6 +23,7 @@ interface DispatchProps {
...
@@ -23,6 +23,7 @@ interface DispatchProps {
type
Props
=
OwnProps
&
ConnectedProps
&
DispatchProps
;
type
Props
=
OwnProps
&
ConnectedProps
&
DispatchProps
;
export
const
VisualizationTabUnconnected
:
FC
<
Props
>
=
({
panel
,
plugin
,
changePanelPlugin
})
=>
{
export
const
VisualizationTabUnconnected
:
FC
<
Props
>
=
({
panel
,
plugin
,
changePanelPlugin
})
=>
{
const
[
searchQuery
,
setSearchQuery
]
=
useState
(
''
);
const
theme
=
useTheme
();
const
theme
=
useTheme
();
const
styles
=
getStyles
(
theme
);
const
styles
=
getStyles
(
theme
);
...
@@ -36,15 +37,51 @@ export const VisualizationTabUnconnected: FC<Props> = ({ panel, plugin, changePa
...
@@ -36,15 +37,51 @@ export const VisualizationTabUnconnected: FC<Props> = ({ panel, plugin, changePa
return
(
return
(
<
div
className=
{
styles
.
wrapper
}
>
<
div
className=
{
styles
.
wrapper
}
>
<
VizTypePicker
current=
{
plugin
.
meta
}
onTypeChange=
{
onPluginTypeChange
}
searchQuery=
{
''
}
onClose=
{
()
=>
{}
}
/>
<
div
className=
{
styles
.
search
}
>
<
Forms
.
Input
value=
{
searchQuery
}
onChange=
{
e
=>
setSearchQuery
(
e
.
currentTarget
.
value
)
}
prefix=
{
<
Icon
name=
"filter"
className=
{
styles
.
icon
}
/>
}
placeholder=
"Filter visualisations"
autoFocus
/>
</
div
>
<
div
className=
{
styles
.
visList
}
>
<
CustomScrollbar
>
<
VizTypePicker
current=
{
plugin
.
meta
}
onTypeChange=
{
onPluginTypeChange
}
searchQuery=
{
searchQuery
}
onClose=
{
()
=>
{}
}
/>
</
CustomScrollbar
>
</
div
>
</
div
>
</
div
>
);
);
};
};
const
getStyles
=
stylesFactory
((
theme
:
GrafanaTheme
)
=>
{
const
getStyles
=
stylesFactory
((
theme
:
GrafanaTheme
)
=>
{
return
{
return
{
icon
:
css
`
color:
${
theme
.
colors
.
gray33
}
;
`
,
wrapper
:
css
`
wrapper
:
css
`
padding:
${
theme
.
spacing
.
md
}
;
display: flex;
flex-direction: column;
flex-grow: 1;
max-height: 100%;
`
,
search
:
css
`
padding:
${
theme
.
spacing
.
sm
}
${
theme
.
spacing
.
md
}
;
flex-grow: 0;
flex-shrink: 1;
margin-bottom:
${
theme
.
spacing
.
sm
}
;
`
,
visList
:
css
`
flex-grow: 1;
height: 100%;
overflow: hidden;
padding-left:
${
theme
.
spacing
.
md
}
;
`
,
`
,
};
};
});
});
...
...
public/app/features/dashboard/panel_editor/VizTypePicker.tsx
View file @
59467d3c
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
useMemo
}
from
'react'
;
import
config
from
'app/core/config'
;
import
config
from
'app/core/config'
;
import
VizTypePickerPlugin
from
'./VizTypePickerPlugin'
;
import
VizTypePickerPlugin
from
'./VizTypePickerPlugin'
;
import
{
EmptySearchResult
}
from
'@grafana/ui'
;
import
{
EmptySearchResult
,
stylesFactory
,
useTheme
}
from
'@grafana/ui'
;
import
{
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
GrafanaTheme
,
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
css
}
from
'emotion'
;
export
interface
Props
{
export
interface
Props
{
current
:
PanelPluginMeta
;
current
:
PanelPluginMeta
;
...
@@ -12,62 +13,68 @@ export interface Props {
...
@@ -12,62 +13,68 @@ export interface Props {
onClose
:
()
=>
void
;
onClose
:
()
=>
void
;
}
}
export
class
VizTypePicker
extends
PureComponent
<
Props
>
{
export
const
VizTypePicker
:
React
.
FC
<
Props
>
=
({
searchQuery
,
onTypeChange
,
current
})
=>
{
searchInput
:
HTMLElement
;
const
theme
=
useTheme
();
pluginList
=
this
.
getPanelPlugins
;
const
styles
=
getStyles
(
theme
);
const
pluginsList
:
PanelPluginMeta
[]
=
useMemo
(()
=>
{
constructor
(
props
:
Props
)
{
super
(
props
);
}
get
maxSelectedIndex
()
{
const
filteredPluginList
=
this
.
getFilteredPluginList
();
return
filteredPluginList
.
length
-
1
;
}
get
getPanelPlugins
():
PanelPluginMeta
[]
{
const
allPanels
=
config
.
panels
;
const
allPanels
=
config
.
panels
;
return
Object
.
keys
(
allPanels
)
return
Object
.
keys
(
allPanels
)
.
filter
(
key
=>
allPanels
[
key
][
'hideFromList'
]
===
false
)
.
filter
(
key
=>
allPanels
[
key
][
'hideFromList'
]
===
false
)
.
map
(
key
=>
allPanels
[
key
])
.
map
(
key
=>
allPanels
[
key
])
.
sort
((
a
:
PanelPluginMeta
,
b
:
PanelPluginMeta
)
=>
a
.
sort
-
b
.
sort
);
.
sort
((
a
:
PanelPluginMeta
,
b
:
PanelPluginMeta
)
=>
a
.
sort
-
b
.
sort
);
}
}
,
[]);
renderVizPlugin
=
(
plugin
:
PanelPluginMeta
,
index
:
number
)
=>
{
const
renderVizPlugin
=
(
plugin
:
PanelPluginMeta
,
index
:
number
)
=>
{
const
{
onTypeChange
}
=
this
.
props
;
const
isCurrent
=
plugin
.
id
===
current
.
id
;
const
isCurrent
=
plugin
.
id
===
this
.
props
.
current
.
id
;
const
filteredPluginList
=
getFilteredPluginList
()
;
const
matchesQuery
=
filteredPluginList
.
indexOf
(
plugin
)
>
-
1
;
return
(
return
(
<
VizTypePickerPlugin
key=
{
plugin
.
id
}
isCurrent=
{
isCurrent
}
plugin=
{
plugin
}
onClick=
{
()
=>
onTypeChange
(
plugin
)
}
/>
<
VizTypePickerPlugin
disabled=
{
!
matchesQuery
}
key=
{
plugin
.
id
}
isCurrent=
{
isCurrent
}
plugin=
{
plugin
}
onClick=
{
()
=>
onTypeChange
(
plugin
)
}
/>
);
);
};
};
getFilteredPluginList
=
():
PanelPluginMeta
[]
=>
{
const
getFilteredPluginList
=
():
PanelPluginMeta
[]
=>
{
const
{
searchQuery
}
=
this
.
props
;
const
regex
=
new
RegExp
(
searchQuery
,
'i'
);
const
regex
=
new
RegExp
(
searchQuery
,
'i'
);
const
pluginList
=
this
.
pluginList
;
const
filtered
=
plugin
List
.
filter
(
item
=>
{
return
plugins
List
.
filter
(
item
=>
{
return
regex
.
test
(
item
.
name
);
return
regex
.
test
(
item
.
name
);
});
});
return
filtered
;
};
};
render
()
{
const
filteredPluginList
=
getFilteredPluginList
();
const
filteredPluginList
=
this
.
getFilteredPluginList
();
const
hasResults
=
filteredPluginList
.
length
>
0
;
const
hasResults
=
filteredPluginList
.
length
>
0
;
return
(
return
(
<
div
className=
"viz-picker"
>
<
div
className=
{
styles
.
wrapper
}
>
<
div
className=
"viz-picker-list"
>
<
div
className=
{
styles
.
grid
}
>
{
hasResults
?
(
{
hasResults
?
(
filteredPluginList
.
map
((
plugin
,
index
)
=>
this
.
renderVizPlugin
(
plugin
,
index
))
pluginsList
.
map
((
plugin
,
index
)
=>
renderVizPlugin
(
plugin
,
index
))
)
:
(
)
:
(
<
EmptySearchResult
>
Could not find anything matching your query
</
EmptySearchResult
>
<
EmptySearchResult
>
Could not find anything matching your query
</
EmptySearchResult
>
)
}
)
}
</
div
>
</
div
>
</
div
>
</
div
>
);
);
}
};
}
const
getStyles
=
stylesFactory
((
theme
:
GrafanaTheme
)
=>
{
return
{
wrapper
:
css
`
padding-right:
${
theme
.
spacing
.
md
}
;
`
,
grid
:
css
`
max-width: 100%;
display: grid;
grid-gap:
${
theme
.
spacing
.
md
}
;
grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
`
,
};
});
public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx
View file @
59467d3c
import
React
from
'react'
;
import
React
from
'react'
;
import
classNames
from
'classnames'
;
import
{
GrafanaTheme
,
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
PanelPluginMeta
}
from
'@grafana/data'
;
import
{
stylesFactory
,
useTheme
}
from
'@grafana/ui'
;
import
{
css
,
cx
}
from
'emotion'
;
import
tinycolor
from
'tinycolor2'
;
interface
Props
{
interface
Props
{
isCurrent
:
boolean
;
isCurrent
:
boolean
;
plugin
:
PanelPluginMeta
;
plugin
:
PanelPluginMeta
;
onClick
:
()
=>
void
;
onClick
:
()
=>
void
;
disabled
:
boolean
;
}
}
const
VizTypePickerPlugin
=
React
.
memo
(
const
VizTypePickerPlugin
:
React
.
FC
<
Props
>
=
({
isCurrent
,
plugin
,
onClick
,
disabled
})
=>
{
({
isCurrent
,
plugin
,
onClick
}:
Props
)
=>
{
const
theme
=
useTheme
();
const
cssClass
=
classNames
({
const
styles
=
getStyles
(
theme
);
'viz-picker__item'
:
true
,
const
cssClass
=
cx
({
'viz-picker__item--current'
:
isCurrent
,
[
styles
.
item
]:
true
,
[
styles
.
current
]:
isCurrent
,
[
styles
.
disabled
]:
disabled
,
});
});
return
(
return
(
<
div
className=
{
cssClass
}
onClick=
{
onClick
}
title=
{
plugin
.
name
}
>
<
div
className=
{
cssClass
}
onClick=
{
disabled
?
()
=>
{}
:
onClick
}
title=
{
plugin
.
name
}
>
<
div
className=
"viz-picker__item-name"
>
{
plugin
.
name
}
</
div
>
<
div
className=
{
styles
.
name
}
>
{
plugin
.
name
}
</
div
>
<
img
className=
"viz-picker__item-img"
src=
{
plugin
.
info
.
logos
.
small
}
/>
<
img
className=
{
styles
.
img
}
src=
{
plugin
.
info
.
logos
.
small
}
/>
</
div
>
</
div
>
);
);
},
};
(
prevProps
,
nextProps
)
=>
{
if
(
prevProps
.
isCurrent
===
nextProps
.
isCurrent
)
{
const
getStyles
=
stylesFactory
((
theme
:
GrafanaTheme
)
=>
{
return
true
;
return
{
}
item
:
css
`
return
false
;
background:
${
theme
.
isLight
?
theme
.
colors
.
white
:
theme
.
colors
.
gray05
}
;
border: 1px solid
${
theme
.
isLight
?
theme
.
colors
.
gray3
:
theme
.
colors
.
dark10
}
;
border-radius: 3px;
height: 100px;
width: 100%;
max-width: 200px;
flex-shrink: 0;
flex-direction: column;
text-align: center;
cursor: pointer;
display: flex;
margin-right: 10px;
margin-bottom: 10px;
align-items: center;
justify-content: center;
padding-bottom: 6px;
transition: transform 1 ease;
&:hover {
box-shadow: 0 0 4px
${
theme
.
colors
.
blueLight
}
;
background:
${
theme
.
isLight
?
tinycolor
(
theme
.
colors
.
blueBase
)
.
lighten
(
45
)
.
toHexString
()
:
tinycolor
(
theme
.
colors
.
blueBase
)
.
darken
(
46
)
.
toHexString
()}
;
border: 1px solid
${
theme
.
colors
.
blueLight
}
;
}
}
);
`
,
current
:
css
`
box-shadow: 0 0 6px
${
theme
.
colors
.
orange
}
!important;
border: 1px solid
${
theme
.
colors
.
orange
}
!important;
background:
${
theme
.
isLight
?
theme
.
colors
.
white
:
theme
.
colors
.
gray05
}
;
`
,
disabled
:
css
`
opacity: 0.2;
filter: grayscale(1);
cursor: pointer;
`
,
name
:
css
`
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size:
${
theme
.
typography
.
size
.
sm
}
;
display: flex;
flex-direction: column;
align-self: center;
height: 23px;
font-weight:
${
theme
.
typography
.
weight
.
semibold
}
;
`
,
img
:
css
`
height: 55px;
`
,
};
});
export
default
VizTypePickerPlugin
;
export
default
VizTypePickerPlugin
;
public/sass/components/_panel_editor.scss
View file @
59467d3c
...
@@ -121,62 +121,6 @@
...
@@ -121,62 +121,6 @@
}
}
}
}
.viz-picker
{
position
:
relative
;
}
.viz-picker-list
{
display
:
flex
;
flex-wrap
:
wrap
;
}
.viz-picker__item
{
background
:
$panel-editor-viz-item-bg
;
border
:
$panel-editor-viz-item-border
;
border-radius
:
3px
;
height
:
100px
;
width
:
145px
;
flex-shrink
:
0
;
flex-direction
:
column
;
text-align
:
center
;
cursor
:
pointer
;
display
:
flex
;
margin-right
:
10px
;
margin-bottom
:
10px
;
align-items
:
center
;
justify-content
:
center
;
padding-bottom
:
6px
;
transition
:
transform
1
ease
;
&
:hover
{
box-shadow
:
$panel-editor-viz-item-shadow-hover
;
background
:
$panel-editor-viz-item-bg-hover
;
border
:
$panel-editor-viz-item-border-hover
;
}
&
--current
{
box-shadow
:
0
0
6px
$orange
!
important
;
border
:
1px
solid
$orange
!
important
;
background
:
$panel-editor-viz-item-bg
!
important
;
}
}
.viz-picker__item-name
{
text-overflow
:
ellipsis
;
overflow
:
hidden
;
white-space
:
nowrap
;
font-size
:
$font-size-sm
;
display
:
flex
;
flex-direction
:
column
;
align-self
:
center
;
height
:
23px
;
font-weight
:
$font-weight-semi-bold
;
}
.viz-picker__item-img
{
height
:
55px
;
}
.panel-editor-tabs
{
.panel-editor-tabs
{
z-index
:
2
;
z-index
:
2
;
display
:
flex
;
display
:
flex
;
...
...
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