mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-03 12:05:52 +01:00
scm-ui: new repository layout
This commit is contained in:
86
scm-ui/ui-components/src/forms/AddEntryToTableField.js
Normal file
86
scm-ui/ui-components/src/forms/AddEntryToTableField.js
Normal file
@@ -0,0 +1,86 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import { AddButton } from "../buttons";
|
||||
import InputField from "./InputField";
|
||||
|
||||
type Props = {
|
||||
addEntry: string => void,
|
||||
disabled: boolean,
|
||||
buttonLabel: string,
|
||||
fieldLabel: string,
|
||||
errorMessage: string,
|
||||
helpText?: string,
|
||||
validateEntry?: string => boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
entryToAdd: string
|
||||
};
|
||||
|
||||
class AddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
entryToAdd: ""
|
||||
};
|
||||
}
|
||||
|
||||
isValid = () => {
|
||||
const {validateEntry} = this.props;
|
||||
if (!this.state.entryToAdd || this.state.entryToAdd === "" || !validateEntry) {
|
||||
return true;
|
||||
} else {
|
||||
return validateEntry(this.state.entryToAdd);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
disabled,
|
||||
buttonLabel,
|
||||
fieldLabel,
|
||||
errorMessage,
|
||||
helpText
|
||||
} = this.props;
|
||||
return (
|
||||
<div className="field">
|
||||
<InputField
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={this.handleAddEntryChange}
|
||||
validationError={!this.isValid()}
|
||||
value={this.state.entryToAdd}
|
||||
onReturnPressed={this.appendEntry}
|
||||
disabled={disabled}
|
||||
helpText={helpText}
|
||||
/>
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled || this.state.entryToAdd ==="" || !this.isValid()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
addButtonClicked = (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.appendEntry();
|
||||
};
|
||||
|
||||
appendEntry = () => {
|
||||
const { entryToAdd } = this.state;
|
||||
this.props.addEntry(entryToAdd);
|
||||
this.setState({ ...this.state, entryToAdd: "" });
|
||||
};
|
||||
|
||||
handleAddEntryChange = (entryname: string) => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
entryToAdd: entryname
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default AddEntryToTableField;
|
||||
@@ -0,0 +1,89 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import type { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
|
||||
import Autocomplete from "../Autocomplete";
|
||||
import AddButton from "../buttons/AddButton";
|
||||
|
||||
type Props = {
|
||||
addEntry: SelectValue => void,
|
||||
disabled: boolean,
|
||||
buttonLabel: string,
|
||||
fieldLabel: string,
|
||||
helpText?: string,
|
||||
loadSuggestions: string => Promise<AutocompleteObject>,
|
||||
placeholder?: string,
|
||||
loadingMessage?: string,
|
||||
noOptionsMessage?: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
selectedValue?: SelectValue
|
||||
};
|
||||
|
||||
class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { selectedValue: undefined };
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
disabled,
|
||||
buttonLabel,
|
||||
fieldLabel,
|
||||
helpText,
|
||||
loadSuggestions,
|
||||
placeholder,
|
||||
loadingMessage,
|
||||
noOptionsMessage
|
||||
} = this.props;
|
||||
|
||||
const { selectedValue } = this.state;
|
||||
return (
|
||||
<div className="field">
|
||||
<Autocomplete
|
||||
label={fieldLabel}
|
||||
loadSuggestions={loadSuggestions}
|
||||
valueSelected={this.handleAddEntryChange}
|
||||
helpText={helpText}
|
||||
value={selectedValue}
|
||||
placeholder={placeholder}
|
||||
loadingMessage={loadingMessage}
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
creatable={true}
|
||||
/>
|
||||
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
addButtonClicked = (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.appendEntry();
|
||||
};
|
||||
|
||||
appendEntry = () => {
|
||||
const { selectedValue } = this.state;
|
||||
if (!selectedValue) {
|
||||
return;
|
||||
}
|
||||
// $FlowFixMe null is needed to clear the selection; undefined does not work
|
||||
this.setState({ ...this.state, selectedValue: null }, () =>
|
||||
this.props.addEntry(selectedValue)
|
||||
);
|
||||
};
|
||||
|
||||
handleAddEntryChange = (selection: SelectValue) => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
selectedValue: selection
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default AutocompleteAddEntryToTableField;
|
||||
50
scm-ui/ui-components/src/forms/Checkbox.js
Normal file
50
scm-ui/ui-components/src/forms/Checkbox.js
Normal file
@@ -0,0 +1,50 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Help } from "../index";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
name?: string,
|
||||
checked: boolean,
|
||||
onChange?: (value: boolean, name?: string) => void,
|
||||
disabled?: boolean,
|
||||
helpText?: string
|
||||
};
|
||||
|
||||
class Checkbox extends React.Component<Props> {
|
||||
|
||||
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(event.target.checked, this.props.name);
|
||||
}
|
||||
};
|
||||
|
||||
renderHelp = () => {
|
||||
const helpText = this.props.helpText;
|
||||
if (helpText) {
|
||||
return <Help message={helpText} />;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="field is-grouped">
|
||||
<div className="control">
|
||||
<label className="checkbox" disabled={this.props.disabled}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.props.checked}
|
||||
onChange={this.onCheckboxChange}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
{" "}
|
||||
{this.props.label}
|
||||
{this.renderHelp()}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
43
scm-ui/ui-components/src/forms/DropDown.js
Normal file
43
scm-ui/ui-components/src/forms/DropDown.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
options: string[],
|
||||
optionValues?: string[],
|
||||
optionSelected: string => void,
|
||||
preselectedOption?: string,
|
||||
className: any,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class DropDown extends React.Component<Props> {
|
||||
render() {
|
||||
const { options, optionValues, preselectedOption, className, disabled } = this.props;
|
||||
return (
|
||||
<div className={classNames(className, "select", disabled ? "disabled": "")}>
|
||||
<select
|
||||
value={preselectedOption ? preselectedOption : ""}
|
||||
onChange={this.change}
|
||||
disabled={disabled}
|
||||
>
|
||||
<option key="" />
|
||||
{options.map((option, index) => {
|
||||
return (
|
||||
<option key={option} value={optionValues && optionValues[index] ? optionValues[index] : option}>
|
||||
{option}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
change = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||
this.props.optionSelected(event.target.value);
|
||||
};
|
||||
}
|
||||
|
||||
export default DropDown;
|
||||
69
scm-ui/ui-components/src/forms/FilterInput.js
Normal file
69
scm-ui/ui-components/src/forms/FilterInput.js
Normal file
@@ -0,0 +1,69 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
filter: string => void,
|
||||
value?: string,
|
||||
|
||||
// context props
|
||||
classes: Object,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
type State = {
|
||||
value: string
|
||||
};
|
||||
|
||||
const styles = {
|
||||
inputField: {
|
||||
float: "right",
|
||||
marginTop: "1.25rem"
|
||||
},
|
||||
inputHeight: {
|
||||
height: "2.5rem"
|
||||
}
|
||||
};
|
||||
|
||||
class FilterInput extends React.Component<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { value: this.props.value ? this.props.value : "" };
|
||||
}
|
||||
|
||||
handleChange = event => {
|
||||
this.setState({ value: event.target.value });
|
||||
};
|
||||
|
||||
handleSubmit = event => {
|
||||
this.props.filter(this.state.value);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { classes, t } = this.props;
|
||||
return (
|
||||
<form
|
||||
className={classNames(classes.inputField, "input-field")}
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
<div className="control has-icons-left">
|
||||
<input
|
||||
className={classNames(classes.inputHeight, "input")}
|
||||
type="search"
|
||||
placeholder={t("filterEntries")}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<span className="icon is-small is-left">
|
||||
<i className="fas fa-search" />
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(translate("commons")(FilterInput));
|
||||
90
scm-ui/ui-components/src/forms/InputField.js
Normal file
90
scm-ui/ui-components/src/forms/InputField.js
Normal file
@@ -0,0 +1,90 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
name?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
type?: string,
|
||||
autofocus?: boolean,
|
||||
onChange: (value: string, name?: string) => void,
|
||||
onReturnPressed?: () => void,
|
||||
validationError: boolean,
|
||||
errorMessage: string,
|
||||
disabled?: boolean,
|
||||
helpText?: string
|
||||
};
|
||||
|
||||
class InputField extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "text",
|
||||
placeholder: ""
|
||||
};
|
||||
|
||||
field: ?HTMLInputElement;
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autofocus && this.field) {
|
||||
this.field.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
this.props.onChange(event.target.value, this.props.name);
|
||||
};
|
||||
|
||||
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
||||
const onReturnPressed = this.props.onReturnPressed;
|
||||
if (!onReturnPressed) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
onReturnPressed();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
type,
|
||||
placeholder,
|
||||
value,
|
||||
validationError,
|
||||
errorMessage,
|
||||
disabled,
|
||||
label,
|
||||
helpText
|
||||
} = this.props;
|
||||
const errorView = validationError ? "is-danger" : "";
|
||||
const helper = validationError ? (
|
||||
<p className="help is-danger">{errorMessage}</p>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
return (
|
||||
<div className="field">
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className="control">
|
||||
<input
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
className={classNames("input", errorView)}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
{helper}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default InputField;
|
||||
37
scm-ui/ui-components/src/forms/LabelWithHelpIcon.js
Normal file
37
scm-ui/ui-components/src/forms/LabelWithHelpIcon.js
Normal file
@@ -0,0 +1,37 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Help from "../Help.js";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
helpText?: string
|
||||
};
|
||||
|
||||
class LabelWithHelpIcon extends React.Component<Props> {
|
||||
|
||||
renderHelp() {
|
||||
const { helpText } = this.props;
|
||||
if (helpText) {
|
||||
return (
|
||||
<Help message={helpText} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {label } = this.props;
|
||||
|
||||
if (label) {
|
||||
const help = this.renderHelp();
|
||||
return (
|
||||
<label className="label">
|
||||
{label} { help }
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export default LabelWithHelpIcon;
|
||||
37
scm-ui/ui-components/src/forms/MemberNameTagGroup.js
Normal file
37
scm-ui/ui-components/src/forms/MemberNameTagGroup.js
Normal file
@@ -0,0 +1,37 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { DisplayedUser } from "@scm-manager/ui-types";
|
||||
import TagGroup from "./TagGroup";
|
||||
|
||||
type Props = {
|
||||
members: string[],
|
||||
memberListChanged: (string[]) => void,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class MemberNameTagGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { members, t } = this.props;
|
||||
const membersExtended = members.map(id => {
|
||||
return { id, displayName: id, mail: "" };
|
||||
});
|
||||
return (
|
||||
<TagGroup
|
||||
items={membersExtended}
|
||||
label={t("group.members")}
|
||||
helpText={t("groupForm.help.memberHelpText")}
|
||||
onRemove={this.removeEntry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
removeEntry = (membersExtended: DisplayedUser[]) => {
|
||||
const members = membersExtended.map(function(item) {
|
||||
return item["id"];
|
||||
});
|
||||
this.props.memberListChanged(members);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("groups")(MemberNameTagGroup);
|
||||
112
scm-ui/ui-components/src/forms/PasswordConfirmation.js
Normal file
112
scm-ui/ui-components/src/forms/PasswordConfirmation.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import InputField from "./InputField";
|
||||
|
||||
type State = {
|
||||
password: string,
|
||||
confirmedPassword: string,
|
||||
passwordValid: boolean,
|
||||
passwordConfirmationFailed: boolean
|
||||
};
|
||||
type Props = {
|
||||
passwordChanged: (string, boolean) => void,
|
||||
passwordValidator?: string => boolean,
|
||||
// Context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class PasswordConfirmation extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
password: "",
|
||||
confirmedPassword: "",
|
||||
passwordValid: true,
|
||||
passwordConfirmationFailed: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
password: "",
|
||||
confirmedPassword: "",
|
||||
passwordValid: true,
|
||||
passwordConfirmationFailed: false
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.newPassword")}
|
||||
type="password"
|
||||
onChange={this.handlePasswordChange}
|
||||
value={this.state.password ? this.state.password : ""}
|
||||
validationError={!this.state.passwordValid}
|
||||
errorMessage={t("password.passwordInvalid")}
|
||||
/>
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.confirmPassword")}
|
||||
type="password"
|
||||
onChange={this.handlePasswordValidationChange}
|
||||
value={this.state ? this.state.confirmedPassword : ""}
|
||||
validationError={this.state.passwordConfirmationFailed}
|
||||
errorMessage={t("password.passwordConfirmFailed")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
validatePassword = password => {
|
||||
const { passwordValidator } = this.props;
|
||||
if (passwordValidator) {
|
||||
return passwordValidator(password);
|
||||
}
|
||||
|
||||
return password.length >= 6 && password.length < 32;
|
||||
};
|
||||
|
||||
handlePasswordValidationChange = (confirmedPassword: string) => {
|
||||
const passwordConfirmed = this.state.password === confirmedPassword;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
confirmedPassword,
|
||||
passwordConfirmationFailed: !passwordConfirmed
|
||||
},
|
||||
this.propagateChange
|
||||
);
|
||||
};
|
||||
|
||||
handlePasswordChange = (password: string) => {
|
||||
const passwordConfirmationFailed =
|
||||
password !== this.state.confirmedPassword;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
passwordValid: this.validatePassword(password),
|
||||
passwordConfirmationFailed,
|
||||
password: password
|
||||
},
|
||||
this.propagateChange
|
||||
);
|
||||
};
|
||||
|
||||
isValid = () => {
|
||||
return this.state.passwordValid && !this.state.passwordConfirmationFailed;
|
||||
};
|
||||
|
||||
propagateChange = () => {
|
||||
this.props.passwordChanged(this.state.password, this.isValid());
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("commons")(PasswordConfirmation);
|
||||
41
scm-ui/ui-components/src/forms/Radio.js
Normal file
41
scm-ui/ui-components/src/forms/Radio.js
Normal file
@@ -0,0 +1,41 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Help } from "../index";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
name?: string,
|
||||
value?: string,
|
||||
checked: boolean,
|
||||
onChange?: (value: boolean, name?: string) => void,
|
||||
disabled?: boolean,
|
||||
helpText?: string
|
||||
};
|
||||
|
||||
class Radio extends React.Component<Props> {
|
||||
renderHelp = () => {
|
||||
const helpText = this.props.helpText;
|
||||
if (helpText) {
|
||||
return <Help message={helpText} />;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<label className="radio" disabled={this.props.disabled}>
|
||||
<input
|
||||
type="radio"
|
||||
name={this.props.name}
|
||||
value={this.props.value}
|
||||
checked={this.props.checked}
|
||||
onChange={this.props.onChange}
|
||||
disabled={this.props.disabled}
|
||||
/>{" "}
|
||||
{this.props.label}
|
||||
{this.renderHelp()}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Radio;
|
||||
71
scm-ui/ui-components/src/forms/Select.js
Normal file
71
scm-ui/ui-components/src/forms/Select.js
Normal file
@@ -0,0 +1,71 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
name?: string,
|
||||
label?: string,
|
||||
options: SelectItem[],
|
||||
value?: string,
|
||||
onChange: (value: string, name?: string) => void,
|
||||
loading?: boolean,
|
||||
helpText?: string,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class Select extends React.Component<Props> {
|
||||
field: ?HTMLSelectElement;
|
||||
|
||||
componentDidMount() {
|
||||
// trigger change after render, if value is null to set it to the first value
|
||||
// of the given options.
|
||||
if (!this.props.value && this.field && this.field.value) {
|
||||
this.props.onChange(this.field.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||
this.props.onChange(event.target.value, this.props.name);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, value, label, helpText, loading, disabled } = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className={classNames(
|
||||
"control select",
|
||||
loadingClass
|
||||
)}>
|
||||
<select
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
disabled={disabled}
|
||||
>
|
||||
{options.map(opt => {
|
||||
return (
|
||||
<option value={opt.value} key={"KEY_" + opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Select;
|
||||
66
scm-ui/ui-components/src/forms/TagGroup.js
Normal file
66
scm-ui/ui-components/src/forms/TagGroup.js
Normal file
@@ -0,0 +1,66 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import type { DisplayedUser } from "@scm-manager/ui-types";
|
||||
import { Help, Tag } from "../index";
|
||||
|
||||
type Props = {
|
||||
items: DisplayedUser[],
|
||||
label: string,
|
||||
helpText?: string,
|
||||
onRemove: (DisplayedUser[]) => void,
|
||||
|
||||
// context props
|
||||
classes: Object
|
||||
};
|
||||
|
||||
const styles = {
|
||||
help: {
|
||||
position: "relative"
|
||||
}
|
||||
};
|
||||
|
||||
class TagGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { items, label, helpText, classes } = this.props;
|
||||
let help = null;
|
||||
if (helpText) {
|
||||
help = <Help className={classes.help} message={helpText} />;
|
||||
}
|
||||
return (
|
||||
<div className="field is-grouped is-grouped-multiline">
|
||||
{label && items ? (
|
||||
<div className="control">
|
||||
<strong>
|
||||
{label}
|
||||
{help}
|
||||
{items.length > 0 ? ":" : ""}
|
||||
</strong>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{items.map((item, key) => {
|
||||
return (
|
||||
<div className="control" key={key}>
|
||||
<div className="tags has-addons">
|
||||
<Tag
|
||||
color="info is-outlined"
|
||||
label={item.displayName}
|
||||
onRemove={() => this.removeEntry(item)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
removeEntry = item => {
|
||||
const newItems = this.props.items.filter(name => name !== item);
|
||||
this.props.onRemove(newItems);
|
||||
};
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(TagGroup);
|
||||
57
scm-ui/ui-components/src/forms/Textarea.js
Normal file
57
scm-ui/ui-components/src/forms/Textarea.js
Normal file
@@ -0,0 +1,57 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
name?: string,
|
||||
label?: string,
|
||||
placeholder?: SelectItem[],
|
||||
value?: string,
|
||||
autofocus?: boolean,
|
||||
onChange: (value: string, name?: string) => void,
|
||||
helpText?: string,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class Textarea extends React.Component<Props> {
|
||||
field: ?HTMLTextAreaElement;
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autofocus && this.field) {
|
||||
this.field.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
|
||||
this.props.onChange(event.target.value, this.props.name);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { placeholder, value, label, helpText, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className="control">
|
||||
<textarea
|
||||
className="textarea"
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
onChange={this.handleInput}
|
||||
value={value}
|
||||
disabled={!!disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Textarea;
|
||||
16
scm-ui/ui-components/src/forms/index.js
Normal file
16
scm-ui/ui-components/src/forms/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// @create-index
|
||||
|
||||
export { default as AddEntryToTableField } from "./AddEntryToTableField.js";
|
||||
export { default as AutocompleteAddEntryToTableField } from "./AutocompleteAddEntryToTableField.js";
|
||||
export { default as TagGroup } from "./TagGroup.js";
|
||||
export { default as MemberNameTagGroup } from "./MemberNameTagGroup.js";
|
||||
export { default as Checkbox } from "./Checkbox.js";
|
||||
export { default as Radio } from "./Radio.js";
|
||||
export { default as FilterInput } from "./FilterInput.js";
|
||||
export { default as InputField } from "./InputField.js";
|
||||
export { default as Select } from "./Select.js";
|
||||
export { default as Textarea } from "./Textarea.js";
|
||||
export { default as PasswordConfirmation } from "./PasswordConfirmation.js";
|
||||
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon.js";
|
||||
export { default as DropDown } from "./DropDown.js";
|
||||
|
||||
Reference in New Issue
Block a user