/*
* This file is part of Cockpit.
*
* Copyright (C) 2016 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see .
*/
(function() {
"use strict";
var cockpit = require("cockpit");
var React = require("react");
require("page.css");
var _ = cockpit.gettext;
var textForUndefined = _("undefined");
/* React pattern component for a dropdown/select control
* Entries should be child components of type SelectEntry (html )
* Expected properties:
* - initial (optional) initial value to display, default: first entry
* - onChange (optional) callback (parameter data) when the selection has changed
* - id (optional) html id of the top level node
*/
var Select = React.createClass({
propTypes: {
initial: React.PropTypes.string,
onChange: React.PropTypes.func,
id: React.PropTypes.string,
},
handleDocumentClick: function(node, ev) {
// clicking outside the select control should ensure it's closed
if (!node.contains(ev.target))
this.setState({ open: false });
},
componentDidMount: function() {
var handler = this.handleDocumentClick.bind(this, React.findDOMNode(this));
this.setState({ documentClickHandler: handler });
document.addEventListener('click', handler, false);
},
componentWillUnmount: function() {
document.removeEventListener('click', this.state.documentClickHandler, false);
},
getInitialState: function() {
return {
open: false,
currentData: this.props.initial,
documentClickHandler: undefined,
};
},
clickHandler: function(ev) {
// only consider clicks with the primary button
if (ev && ev.button !== 0)
return;
if (ev.target.tagName == 'A') {
var liElement = ev.target.offsetParent;
var elementData;
if ('data-data' in liElement.attributes)
elementData = liElement.attributes['data-data'].value;
this.setState({ open: false });
// if the item didn't change, don't do anything
if (elementData === this.state.currentData)
return;
this.setState({ currentData: elementData });
if (this.props.onChange)
this.props.onChange(elementData);
} else {
this.setState({ open: !this.state.open });
}
},
render: function() {
var self = this;
var currentValue;
var listItems = React.Children.map(this.props.children, function(itm) {
var data = ('data' in itm.props) ? itm.props.data : undefined;
// we need to have some kind of value
var value = (itm.props.children !== undefined) ? itm.props.children : textForUndefined;
if (data === self.state.currentData)
currentValue = value;
// if there's no initial value, use the first one
else if (!self.props.initial && currentValue === undefined)
currentValue = value;
return {itm};
});
var classes = "btn-group bootstrap-select dropdown";
if (this.state.open)
classes += " open";
return (
);
}
});
/* Entry class for the select component
* Dynamic lists should make sure to also provide 'key' props for react to use
* Expected properties:
* - data optional, will be passed to the select's onChange callback
* Example: Some entry
*/
var SelectEntry = React.createClass({
propTypes: {
data: React.PropTypes.string.isRequired,
},
render: function() {
var value = (this.props.children !== undefined) ? this.props.children : textForUndefined;
return {value};
}
});
module.exports = {
Select: Select,
SelectEntry: SelectEntry,
};
}());