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
4633da47
Commit
4633da47
authored
Oct 26, 2016
by
Matt
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dash-edit-mode' of
https://github.com/grafana/grafana
into dash-edit-mode
parents
e7f5b765
c609586f
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
261 additions
and
163 deletions
+261
-163
pkg/api/frontendsettings.go
+20
-0
public/app/core/routes/dashboard_loaders.js
+8
-1
public/app/features/dashboard/dashboard_ctrl.ts
+1
-0
public/app/features/dashboard/row/options.html
+65
-30
public/app/features/dashboard/row/options.ts
+113
-3
public/app/features/dashboard/row/row.html
+12
-57
public/app/features/dashboard/row/row.ts
+7
-60
public/sass/components/edit_sidemenu.scss
+1
-1
public/sass/pages/_dashboard.scss
+34
-11
No files found.
pkg/api/frontendsettings.go
View file @
4633da47
...
...
@@ -127,6 +127,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
"name"
:
panel
.
Name
,
"id"
:
panel
.
Id
,
"info"
:
panel
.
Info
,
"sort"
:
getPanelSort
(
panel
.
Id
),
}
}
...
...
@@ -150,6 +151,25 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
return
jsonObj
,
nil
}
func
getPanelSort
(
id
string
)
int
{
sort
:=
100
switch
id
{
case
"graph"
:
sort
=
1
case
"singlestat"
:
sort
=
2
case
"table"
:
sort
=
3
case
"text"
:
sort
=
4
case
"alertlist"
:
sort
=
5
case
"dashlist"
:
sort
=
6
}
return
sort
}
func
GetFrontendSettings
(
c
*
middleware
.
Context
)
{
settings
,
err
:=
getFrontendSettingsMap
(
c
)
if
err
!=
nil
{
...
...
public/app/core/routes/dashboard_loaders.js
View file @
4633da47
...
...
@@ -31,7 +31,14 @@ function (coreModule) {
meta
:
{
canStar
:
false
,
canShare
:
false
},
dashboard
:
{
title
:
"New dashboard"
,
rows
:
[{
title
:
'Dashboard Row'
,
height
:
'250px'
,
panels
:[]
}]
rows
:
[
{
title
:
'Dashboard Row'
,
height
:
'250px'
,
panels
:[],
isNew
:
true
,
}
]
},
},
$scope
);
});
...
...
public/app/features/dashboard/dashboard_ctrl.ts
View file @
4633da47
...
...
@@ -107,6 +107,7 @@ export class DashboardCtrl {
title
:
'New row'
,
panels
:
[],
height
:
'250px'
,
isNew
:
true
,
});
};
...
...
public/app/features/dashboard/row/options.html
View file @
4633da47
<div
class=
"dash-row-options"
>
<div
class=
"
tabbed-view-header
"
>
<
h2
class=
"tabbed-view-titl
e"
>
Row Options
</h2
>
<button
class=
"tabbed-view-close-btn"
ng-click=
"ctrl.onClose()"
>
<i
class=
"fa fa-remove"
></i
>
</button
>
</div
>
<br
>
<div
class=
"gf-form-group"
>
<
div
class=
"gf-form-inline"
>
<div
class=
"
edit-tab-with-sidemenu
"
>
<
aside
class=
"edit-sidemenu-asid
e"
>
<ul
class=
"edit-sidemenu"
>
<li
ng-class=
"{active: ctrl.subTabIndex === 0}"
>
<a
ng-click=
"ctrl.subTabIndex = 0"
>
Add Panel
</a>
</li
>
<li
ng-class=
"{active: ctrl.subTabIndex === 1}"
>
<a
ng-click=
"ctrl.subTabIndex = 1"
>
Row Options
</a
>
</li
>
<li
ng-class=
"{active: ctrl.subTabIndex === 2}"
>
<a
ng-click=
"ctrl.deleteRow()"
>
Delete
</a
>
</li>
</ul
>
<
/aside
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-6"
>
Title
</span>
<input
type=
"text"
class=
"gf-form-input max-width-14"
ng-model=
'ctrl.row.title'
></input>
<div
class=
"edit-tab-content"
ng-if=
"ctrl.subTabIndex === 0"
>
<h5
class=
"section-heading"
>
Add Panel
</h5>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-6"
>
Search
</span>
<input
type=
"text"
class=
"gf-form-input max-width-14"
ng-model=
'ctrl.panelSearch'
give-focus=
'true'
ng-keydown=
"ctrl.keyDown($event)"
ng-change=
"ctrl.panelSearchChanged()"
></input>
</div>
</div>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-6"
>
Height
</span>
<input
type=
"text"
class=
"gf-form-input max-width-8"
ng-model=
'ctrl.row.height'
></input>
<div
class=
"add-panel-panels-wrapper"
>
<div
class=
"add-panel-panels"
>
<div
class=
"add-panel-item"
ng-repeat=
"panel in ctrl.panelHits"
ng-class=
"{active: $index === ctrl.activeIndex}"
ng-click=
"ctrl.addPanel(panel)"
>
<img
class=
"add-panel-item-img"
ng-src=
"{{panel.info.logos.small}}"
></img>
<div
class=
"add-panel-item-name"
>
{{panel.name}}
</div>
</div>
</div>
</div>
<gf-form-switch
class=
"gf-form"
label=
"Show Title"
tooltip=
"Check to always show row title"
checked=
"ctrl.row.showTitle"
>
</gf-form-switch>
</div>
</div>
<h5
class=
"section-heading"
>
Row Templating
</h5>
<div
class=
"edit-tab-content"
ng-if=
"ctrl.subTabIndex === 1"
>
<div
class=
"gf-form-group"
>
<h5
class=
"section-heading"
>
Options
</h5>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-6"
>
Title
</span>
<input
type=
"text"
class=
"gf-form-input max-width-14"
ng-model=
'ctrl.row.title'
></input>
</div>
<div
class=
"gf-form"
>
<label
class=
"gf-form-label width-6"
>
Size
</label>
<div
class=
"gf-form-select-wrapper"
>
<select
class=
"input-small gf-form-input"
ng-model=
"ctrl.row.titleSize"
ng-options=
"f for f in ctrl.fontSizes"
></select>
</div>
</div>
<gf-form-switch
class=
"gf-form"
label=
"Show"
checked=
"ctrl.row.showTitle"
>
</gf-form-switch>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-6"
>
Height
</span>
<input
type=
"text"
class=
"gf-form-input max-width-14"
ng-model=
'ctrl.row.height'
></input>
</div>
</div>
</div>
<h5
class=
"section-heading"
>
Row Templating
</h5>
<div
class=
"gf-form-group"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label"
>
Repeat Row
</span>
<div
class=
"gf-form-select-wrapper max-width-10"
>
<select
class=
"gf-form-input"
ng-model=
"row.repeat"
ng-options=
"f.name as f.name for f in dashboard.templating.list"
>
<option
value=
""
></option>
<div
class=
"gf-form-group"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label"
>
Repeat Row
</span>
<div
class=
"gf-form-select-wrapper max-width-10"
>
<select
class=
"gf-form-input"
ng-model=
"row.repeat"
ng-options=
"f.name as f.name for f in dashboard.templating.list"
>
<option
value=
""
></option>
</div>
</div>
</div>
</div>
</div>
</div>
public/app/features/dashboard/row/options.ts
View file @
4633da47
...
...
@@ -2,9 +2,120 @@
import
_
from
'lodash'
;
import
{
coreModule
}
from
'app/core/core'
;
import
config
from
'app/core/config'
;
import
{
coreModule
,
appEvents
}
from
'app/core/core'
;
export
class
RowOptionsCtrl
{
row
:
any
;
dashboard
:
any
;
rowCtrl
:
any
;
subTabIndex
:
number
;
allPanels
:
any
;
panelHits
:
any
;
activeIndex
:
any
;
panelSearch
:
any
;
fontSizes
=
[
'h1'
,
'h2'
,
'h3'
,
'h4'
,
'h5'
,
'h6'
];
/** @ngInject */
constructor
(
private
$scope
,
private
$timeout
,
private
$rootScope
)
{
this
.
row
=
this
.
rowCtrl
.
row
;
this
.
dashboard
=
this
.
rowCtrl
.
dashboard
;
this
.
subTabIndex
=
0
;
this
.
row
.
titleSize
=
this
.
row
.
titleSize
||
'h6'
;
this
.
allPanels
=
_
.
orderBy
(
_
.
map
(
config
.
panels
,
item
=>
item
),
'sort'
);
this
.
panelHits
=
this
.
allPanels
;
this
.
activeIndex
=
0
;
}
keyDown
(
evt
)
{
if
(
evt
.
keyCode
===
27
)
{
this
.
rowCtrl
.
showOptions
=
false
;
return
;
}
if
(
evt
.
keyCode
===
40
||
evt
.
keyCode
===
39
)
{
this
.
moveSelection
(
1
);
}
if
(
evt
.
keyCode
===
38
||
evt
.
keyCode
===
37
)
{
this
.
moveSelection
(
-
1
);
}
if
(
evt
.
keyCode
===
13
)
{
var
selectedPanel
=
this
.
panelHits
[
this
.
activeIndex
];
if
(
selectedPanel
)
{
this
.
addPanel
(
selectedPanel
);
}
}
}
moveSelection
(
direction
)
{
var
max
=
this
.
panelHits
.
length
;
var
newIndex
=
this
.
activeIndex
+
direction
;
this
.
activeIndex
=
((
newIndex
%=
max
)
<
0
)
?
newIndex
+
max
:
newIndex
;
}
panelSearchChanged
()
{
var
items
=
this
.
allPanels
.
slice
();
var
startsWith
=
[];
var
contains
=
[];
var
searchLower
=
this
.
panelSearch
.
toLowerCase
();
var
item
;
while
(
item
=
items
.
shift
())
{
var
nameLower
=
item
.
name
.
toLowerCase
();
if
(
nameLower
.
indexOf
(
searchLower
)
===
0
)
{
startsWith
.
push
(
item
);
}
else
if
(
nameLower
.
indexOf
(
searchLower
)
!==
-
1
)
{
contains
.
push
(
item
);
}
}
this
.
panelHits
=
startsWith
.
concat
(
contains
);
this
.
activeIndex
=
0
;
}
addPanel
(
panelPluginInfo
)
{
var
defaultSpan
=
12
;
var
_as
=
12
-
this
.
dashboard
.
rowSpan
(
this
.
row
);
var
panel
=
{
id
:
null
,
title
:
config
.
new_panel_title
,
error
:
false
,
span
:
_as
<
defaultSpan
&&
_as
>
0
?
_as
:
defaultSpan
,
editable
:
true
,
type
:
panelPluginInfo
.
id
,
isNew
:
true
,
};
this
.
rowCtrl
.
showOptions
=
false
;
this
.
dashboard
.
addPanel
(
panel
,
this
.
row
);
this
.
$timeout
(()
=>
{
this
.
$rootScope
.
appEvent
(
'panel-change-view'
,
{
fullscreen
:
true
,
edit
:
true
,
panelId
:
panel
.
id
});
});
}
deleteRow
()
{
if
(
!
this
.
row
.
panels
.
length
)
{
this
.
dashboard
.
rows
=
_
.
without
(
this
.
dashboard
.
rows
,
this
.
row
);
return
;
}
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete'
,
text
:
'Are you sure you want to delete this row?'
,
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
this
.
dashboard
.
rows
=
_
.
without
(
this
.
dashboard
.
rows
,
this
.
row
);
}
});
}
}
export
function
rowOptionsDirective
()
{
...
...
@@ -15,8 +126,7 @@ export function rowOptionsDirective() {
bindToController
:
true
,
controllerAs
:
'ctrl'
,
scope
:
{
row
:
"="
,
onClose
:
"&"
rowCtrl
:
"="
,
},
};
}
...
...
public/app/features/dashboard/row/row.html
View file @
4633da47
<div
class=
"dash-row-header gf-form-inline"
ng-if=
"ctrl.dashboard.editMode"
>
<div
class=
"gf-form gf-form--grow dropdown"
>
<a
class=
"btn gf-form-btn btn-inverse dropdown-toggle"
data-toggle=
"dropdown"
>
<span>
{{ctrl.row.title}}
</span>
<i
class=
"fa fa-caret-down"
></i>
</a>
<div
class=
"dash-row-header"
>
<a
class=
"dash-row-header-title"
ng-click=
"ctrl.showOptions = !ctrl.showOptions"
>
<span
ng-class=
"ctrl.row.titleSize"
>
{{ctrl.row.title}}
</span>
<i
class=
"fa fa-caret-down"
></i>
</a>
<ul
class=
"dropdown-menu"
role=
"menu"
aria-labelledby=
"drop1"
>
<li>
<a
ng-click=
"ctrl.showOptions = !ctrl.showOptions"
>
Options
</a>
</li>
<li
class=
"dropdown-submenu"
>
<a
href=
"javascript:void(0);"
>
Height
</a>
<ul
class=
"dropdown-menu"
>
<li><a
ng-click=
"ctrl.setHeight('25px')"
>
25 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('100px')"
>
100 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('150px')"
>
150 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('200px')"
>
200 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('250px')"
>
250 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('300px')"
>
300 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('350px')"
>
350 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('450px')"
>
450 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('500px')"
>
500 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('600px')"
>
600 px
</a></li>
<li><a
ng-click=
"ctrl.setHeight('700px')"
>
700 px
</a></li>
</ul>
</li>
<li
class=
"dropdown-submenu"
>
<a
href=
"javascript:void(0);"
>
Move
</a>
<ul
class=
"dropdown-menu"
>
<li><a
ng-click=
"ctrl.Move('up')"
>
Up
</a></li>
<li><a
ng-click=
"ctrl.Move('down')"
>
Down
</a></li>
<li><a
ng-click=
"ctrl.Move('top')"
>
To Top
</a></li>
<li><a
ng-click=
"ctrl.Move('bottom')"
>
To Bottom
</a></li>
</ul>
</li>
<li>
<a
ng-click=
"ctrl.deleteRow()"
>
Delete
</a>
</li>
</ul>
<div
class=
"dash-row-header-spacer"
>
</div>
<a
class=
"btn gf-form-btn btn-inverse"
ng-click=
"ctrl.addPanelMode=!ctrl.addPanelMode"
>
Add Panel
<i
class=
"fa fa-plus"
></i>
<div
class=
"dash-row-collapse-toggle"
ng-click=
"ctrl.row.collapse = !ctrl.row.collapse"
>
<a
class=
"pointer"
>
<i
class=
"fa fa-chevron-down"
ng-show=
"!ctrl.row.collapse"
></i>
<i
class=
"fa fa-chevron-right"
ng-show=
"ctrl.row.collapse"
></i>
</a>
<div
class=
"gf-form-label gf-form-label--grow text-right"
ng-click=
"ctrl.row.collapse = !ctrl.row.collapse"
>
<a
class=
"pointer"
>
<i
class=
"fa fa-chevron-down"
ng-show=
"!ctrl.row.collapse"
></i>
<i
class=
"fa fa-chevron-right"
ng-show=
"ctrl.row.collapse"
></i>
</a>
</div>
</div>
</div>
<div
ng-if=
"ctrl.showOptions"
>
<dash-row-options
row
=
"ctrl.row"
on-close=
"ctrl.showOptions = false;
"
></dash-row-options>
<dash-row-options
row
-ctrl=
"ctrl
"
></dash-row-options>
</div>
<div
class=
"panels-wrapper"
ng-if=
"!ctrl.row.collapse"
>
<div
class=
"add-panel-container"
ng-if=
"ctrl.addPanelMode"
>
<div
class=
"add-panel-item"
ng-repeat=
"panel in ctrl.panelPlugins"
>
<img
class=
"add-panel-item-img"
ng-src=
"{{panel.info.logos.small}}"
></img>
<div
class=
"add-panel-item-name"
>
{{panel.name}}
</div>
</div>
</div>
<div
ng-repeat=
"panel in ctrl.row.panels track by panel.id"
class=
"panel"
ui-draggable=
"!ctrl.dashboard.meta.fullscreen"
drag=
"panel.id"
ui-on-drop=
"ctrl.onDrop($data, panel)"
drag-handle-class=
"drag-handle"
panel-width
>
<plugin-component
type=
"panel"
class=
"panel-margin"
>
</plugin-component>
...
...
public/app/features/dashboard/row/row.ts
View file @
4633da47
...
...
@@ -5,36 +5,23 @@ import $ from 'jquery';
import
angular
from
'angular'
;
import
config
from
'app/core/config'
;
import
{
coreModule
,
appEvents
}
from
'app/core/core'
;
import
{
coreModule
}
from
'app/core/core'
;
import
'./options'
;
export
class
DashRowCtrl
{
dashboard
:
any
;
row
:
any
;
panelPlugins
;
addPanelSegment
;
showOptions
:
boolean
;
/** @ngInject */
/** @ngInject */
constructor
(
private
$scope
,
private
$rootScope
,
private
$timeout
,
private
uiSegmentSrv
,
private
$q
)
{
this
.
panelPlugins
=
config
.
panels
;
console
.
log
(
this
.
panelPlugins
);
this
.
row
.
title
=
this
.
row
.
title
||
'Row title'
;
this
.
addPanelSegment
=
uiSegmentSrv
.
newSegment
({
value
:
'add'
,
custom
:
'false'
,
html
:
'Add Panel <i class="fa fa-plus"></i>'
,
renderer
:
(
item
,
defaultHighlighter
)
=>
{
return
'<img src="'
+
item
.
img
+
'">'
+
defaultHighlighter
(
item
.
text
);
}
});
}
getPanels
(
)
{
return
this
.
$q
.
when
(
_
.
map
(
config
.
panels
,
panel
=>
{
return
this
.
uiSegmentSrv
.
newSegment
({
value
:
panel
.
name
})
;
}
));
if
(
this
.
row
.
isNew
)
{
this
.
showOptions
=
true
;
delete
this
.
row
.
isNew
;
}
}
onDrop
(
panelId
,
dropTarget
)
{
...
...
@@ -55,51 +42,11 @@ export class DashRowCtrl {
this
.
$rootScope
.
$broadcast
(
'render'
);
}
addPanel
(
panel
)
{
this
.
dashboard
.
addPanel
(
panel
,
this
.
row
);
this
.
$timeout
(()
=>
{
this
.
$scope
.
$broadcast
(
'render'
);
});
}
setHeight
(
height
)
{
this
.
row
.
height
=
height
;
this
.
$scope
.
$broadcast
(
'render'
);
}
addPanelDefault
(
type
)
{
var
defaultSpan
=
12
;
var
_as
=
12
-
this
.
dashboard
.
rowSpan
(
this
.
row
);
var
panel
=
{
title
:
config
.
new_panel_title
,
error
:
false
,
span
:
_as
<
defaultSpan
&&
_as
>
0
?
_as
:
defaultSpan
,
editable
:
true
,
type
:
type
,
isNew
:
true
,
};
this
.
addPanel
(
panel
);
}
deleteRow
()
{
if
(
!
this
.
row
.
panels
.
length
)
{
this
.
dashboard
.
rows
=
_
.
without
(
this
.
dashboard
.
rows
,
this
.
row
);
return
;
}
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete'
,
text
:
'Are you sure you want to delete this row?'
,
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
this
.
dashboard
.
rows
=
_
.
without
(
this
.
dashboard
.
rows
,
this
.
row
);
}
});
}
moveRow
(
direction
)
{
var
rowsList
=
this
.
dashboard
.
rows
;
var
rowIndex
=
_
.
indexOf
(
rowsList
,
this
.
row
);
...
...
public/sass/components/edit_sidemenu.scss
View file @
4633da47
...
...
@@ -9,7 +9,7 @@
}
.edit-sidemenu-aside
{
width
:
16rem
;
min-
width
:
16rem
;
}
.edit-sidemenu
{
...
...
public/sass/pages/_dashboard.scss
View file @
4633da47
...
...
@@ -211,19 +211,40 @@ div.flot-text {
.dash-row-header
{
display
:
flex
;
flex-direction
:
row
;
text-align
:
left
;
align-items
:
center
;
margin-right
:
$panel-margin
;
margin-left
:
$gf-form-margin
;
border-bottom
:
$panel-border
;
}
.dash-row-header-title
{
font-family
:
$headings-font-family
;
padding
:
0
.7rem
;
i
{
font-size
:
0
.9rem
;
position
:
relative
;
top
:
2px
;
left
:
1px
;
color
:
$text-muted
;
}
}
.dash-row-header-chevron
{
flex-grow
:
100
;
.dash-row-header-add-panel
{
padding
:
0
.7rem
;
i
{
font-size
:
0
.9rem
;
position
:
relative
;
top
:
2px
;
left
:
1px
;
color
:
$text-muted
;
}
}
.dash-row-header-spacer
{
flex
:
50
;
}
.dash-row-collapse-toggle
{
flex-grow
:
30
;
cursor
:
pointer
;
text-align
:
right
;
margin-right
:
0
.6rem
;
font-size
:
$font-size-sm
;
...
...
@@ -247,7 +268,7 @@ div.flot-text {
.dash-row-options
{
background
:
$panel-bg
;
margin
:
0
$panel-margin
*
2
0
$panel-margin
;
padding
:
$spacer
*
2
;
padding
:
$spacer
*
1
.5
;
}
.dash-row-options-close-btn
{
...
...
@@ -266,18 +287,20 @@ div.flot-text {
}
}
.add-panel-container
{
display
:
flex
;
flex-direction
:
row
;
.add-panel-panels
{
}
.add-panel-item
{
background
:
$
pan
el-bg
;
background
:
$
input-lab
el-bg
;
padding
:
$spacer
;
min-width
:
10rem
;
max-width
:
10rem
;
text-align
:
center
;
margin
:
$panel-margin
;
margin
:
$gf-form-margin
;
float
:
left
;
cursor
:
pointer
;
&
.active
,
&
:hover
{
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,.
075
)
,
0
0
5px
rgba
(
82
,
168
,
236
,
10
.8
)
}
...
...
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