@material-ui/core#InputAdornment TypeScript Examples
The following examples show how to use
@material-ui/core#InputAdornment.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: TokenSymbol.tsx From parity-bridges-ui with GNU General Public License v3.0 | 6 votes |
TokenSymbol = ({ position = 'start' }: Props): React.ReactElement => {
const { targetChainDetails, sourceChainDetails } = useSourceTarget();
const { isBridged } = useGUIContext();
let chainTokens = targetChainDetails.apiConnection.api.registry.chainTokens;
if (!isBridged) {
chainTokens = sourceChainDetails.apiConnection.api.registry.chainTokens;
}
return <InputAdornment position={position}>{chainTokens}</InputAdornment>;
}
Example #2
Source File: index.tsx From frontegg-react with MIT License | 6 votes |
appendAdornment = (
props: Partial<MaterialTextFieldProps> | undefined,
at: keyof MaterialInputProps,
iconAction: InputProps['iconAction'],
icon: InputProps['prefixIcon'] | InputProps['suffixIcon']
) => {
const position: InputAdornmentProps['position'] = at === 'startAdornment' ? 'start' : 'end';
return {
...props,
InputProps: {
[at]: (
<InputAdornment onClick={iconAction} position={position}>
{iconAction ? <IconButton>{icon}</IconButton> : icon}
</InputAdornment>
),
},
};
}
Example #3
Source File: Searchbar.tsx From homebase-app with MIT License | 6 votes |
SearchInput: React.FC<{ search: any }> = ({ search }) => {
return (
<StyledInput
id="standard-search"
label="Search field"
type="search"
placeholder="Search"
onChange={(e) => search(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon color="secondary" />
</InputAdornment>
),
}}
/>
);
}
Example #4
Source File: Autocomplete.test.tsx From abacus with GNU General Public License v2.0 | 6 votes |
test('it shows as loading data', () => {
const isLoading = true
const { container } = render(
<MockFormik initialValues={{ name: 'no_name' }}>
<Field
component={Autocomplete}
name='name'
id='name'
fullWidth
options={[]}
loading={isLoading}
renderInput={(params: AutocompleteRenderInputParams) => {
return (
<MuiTextField
{...params}
placeholder='wp_username'
helperText='Use WordPress.com username.'
variant='outlined'
required
label='Owner'
InputProps={{
...autocompleteInputProps(params, isLoading),
startAdornment: <InputAdornment position='start'>@</InputAdornment>,
}}
InputLabelProps={{
shrink: true,
}}
/>
)
}}
/>
</MockFormik>,
)
expect(container).toMatchSnapshot('loading')
})
Example #5
Source File: Registration.tsx From glific-frontend with GNU Affero General Public License v3.0 | 6 votes |
InfoAdornment = ( <InputAdornment position="end" className={styles.InputAdornment}> <Tooltip title="You can customize your Glific account URL as shown in preview" placement="right" tooltipClass={styles.Tooltip} > <InfoIcon /> </Tooltip> </InputAdornment> )
Example #6
Source File: FilterInput.tsx From ow-mod-manager with MIT License | 6 votes |
FilterInput: React.FunctionComponent<Props> = ({
value,
onChange,
label,
}) => {
const [filterText, setFilterText] = useState(value);
const debouncedFilterText = useDebounce(filterText, 200);
useEffect(() => {
onChange(debouncedFilterText);
}, [debouncedFilterText, onChange]);
return (
<OutlinedInput
margin="dense"
onChange={({ currentTarget }) => {
setFilterText(currentTarget.value);
}}
value={filterText}
placeholder={label}
color="secondary"
startAdornment={
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
}
endAdornment={
filterText !== '' && (
<InputAdornment position="end">
<IconButton onClick={() => setFilterText('')} size="small">
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}
/>
);
}
Example #7
Source File: SearchBar.tsx From log4brains with Apache License 2.0 | 6 votes |
export function SearchBar(props: SearchBarProps) {
const { InputProps, InputLabelProps, open, onClear, ...params } = props;
const classes = useStyles(props);
return (
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
startAdornment={
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
}
endAdornment={
// eslint-disable-next-line react/destructuring-assignment
<Fade in={open && props.inputProps.value !== ""}>
<InputAdornment position="end">
<IconButton
onClick={(event) => onClear(event)}
size="small"
title="Clear"
className={classes.clearIcon}
>
<ClearIcon />
</IconButton>
</InputAdornment>
</Fade>
}
ref={InputProps.ref}
{...params}
/>
);
}
Example #8
Source File: Index.stories.tsx From anchor-web-app with Apache License 2.0 | 6 votes |
textFieldInputProps = { endAdornment: ( <InputAdornment position="end"> <Tooltip color="error" title="Error Tooltip Content" placement="top"> <Warning /> </Tooltip> </InputAdornment> ), }
Example #9
Source File: AdvancedSettings.tsx From lobis-frontend with MIT License | 5 votes |
function AdvancedSettings({ open, handleClose, slippage, recipientAddress, onRecipientAddressChange, onSlippageChange }: IAdvancedSettingsProps) {
return (
<Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
<Paper className="ohm-card ohm-popover">
<div className="cross-wrap">
<IconButton onClick={handleClose}>
<SvgIcon color="primary" component={XIcon} />
</IconButton>
</div>
<Box className="card-content">
<InputLabel htmlFor="slippage">
<p className="input-lable">Slippage</p>
</InputLabel>
<FormControl variant="outlined" color="primary" fullWidth>
<OutlinedInput
id="slippage"
value={slippage}
onChange={onSlippageChange}
fullWidth
type="number"
//@ts-ignore
max="100"
min="100"
className="bond-input"
endAdornment={
<InputAdornment position="end">
<p className="percent">%</p>
</InputAdornment>
}
/>
<div className="help-text">
<p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
</div>
</FormControl>
</Box>
</Paper>
</Modal>
);
}
Example #10
Source File: index.tsx From wonderland-frontend with MIT License | 5 votes |
function ChooseToken({ open, handleClose, handleSelect, bond }: IChooseTokenProps) {
const { tokens, loading } = useTokens();
const [quantity, setQuantity] = useState("");
const filtredTokens = tokens.filter(({ name, address, isAvax }) => {
let addressTest = true;
if (quantity && quantity.length === 42) {
addressTest = address.toLocaleLowerCase() === quantity.toLowerCase();
}
let nameTest = true;
if (quantity && quantity.length < 10) {
nameTest = name.toLowerCase().includes(quantity.toLowerCase());
}
let lpFilter = true;
if (bond.name === mim.name) {
lpFilter = mimToken.address !== address;
}
if (bond.name === wavax.name) {
lpFilter = isAvax ? false : wavaxToken.address !== address;
}
return nameTest && addressTest && lpFilter;
});
return (
<Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
<Paper className="ohm-card ohm-popover choose-token-poper">
<div className="cross-wrap">
<IconButton onClick={handleClose}>
<SvgIcon color="primary" component={XIcon} />
</IconButton>
</div>
<Box>
<div className="choose-token-poper-header">
<p className="choose-token-poper-header-title">Choose token</p>
<OutlinedInput
placeholder="Search name or paste address"
className="choose-token-poper-header-input"
value={quantity}
onChange={e => setQuantity(e.target.value)}
labelWidth={0}
startAdornment={
<InputAdornment position="start">
<Box display="flex" alignItems="center" justifyContent="center" width={"24px"}>
<img src={IconsSearch} style={{ height: "24px", width: "24px" }} />
</Box>
</InputAdornment>
}
/>
</div>
<div className="choose-token-poper-body">
{filtredTokens.map(token => (
<div onClick={() => handleSelect(token)} key={token.address} className="choose-token-poper-body-item">
<img className="choose-token-poper-body-item-img" src={token.img} alt="" />
<div className="choose-token-poper-body-item-desc">
<p className="choose-token-poper-body-item-name">{token.name}</p>
<div className="choose-token-poper-body-item-balance">{loading ? <Skeleton width="50px" /> : <p>{trim(token.balance, 6)}</p>}</div>
</div>
</div>
))}
</div>
</Box>
</Paper>
</Modal>
);
}
Example #11
Source File: Autocomplete.test.tsx From abacus with GNU General Public License v2.0 | 5 votes |
test('null is a valid value', async () => {
const isLoading = false
const options: AutocompleteItem[] = [
{
name: 'Hello World',
value: 'hello_world',
},
]
const { container } = render(
<MockFormik initialValues={{ name: null }}>
<Field
component={Autocomplete}
name='name'
id='name'
fullWidth
options={options}
loading={isLoading}
renderInput={(params: AutocompleteRenderInputParams) => {
return (
<MuiTextField
{...params}
placeholder='wp_username'
helperText='Use WordPress.com username.'
variant='outlined'
required
label='Owner'
InputProps={{
...autocompleteInputProps(params, isLoading),
startAdornment: <InputAdornment position='start'>@</InputAdornment>,
}}
InputLabelProps={{
shrink: true,
}}
/>
)
}}
/>
</MockFormik>,
)
await act(async () => {
fireEvent.click(screen.getByRole('textbox'))
})
expect(container).toMatchSnapshot('selected no value')
})
Example #12
Source File: settings_button.tsx From jupyter-extensions with Apache License 2.0 | 5 votes |
render() {
return (
<React.Fragment>
<IconButton
title="Settings"
color="inherit"
onClick={() => this._onClick()}
>
<SettingsIcon fontSize="small" />
</IconButton>
<Dialog
open={this.state.open}
onClose={() => this._onClose()}
fullWidth
>
<DialogTitle>Settings</DialogTitle>
<DialogContent>
<Grid container alignItems="center" spacing={2}>
<Grid item xs={11}>
<Typography id="input-slider">Sync Frequency</Typography>
</Grid>
<Grid item xs={1}>
<Tooltip title="Time between each sync with remote repository. After each sync, the extension will wait before syncing again.">
<HelpOutlineIcon color="disabled" />
</Tooltip>
</Grid>
<Grid item xs={1}>
<TimerIcon />
</Grid>
<Grid item xs={7}>
<Slider
value={
typeof this.state.value === 'number'
? this.state.value / (this.maxInterval / 100)
: 0
}
onChange={(event, value) =>
this._onSliderChange(event, value)
}
aria-labelledby="sync-interval-slider"
/>
</Grid>
<Grid item xs={4}>
<Input
value={this.state.value}
onChange={event => this._onInputChange(event)}
onBlur={() => this._onBlur()}
margin="dense"
inputProps={{
step: 1,
min: 0,
max: this.maxInterval,
type: 'number',
'aria-labelledby': 'sync-interval-slider',
}}
endAdornment={
<InputAdornment position="end">seconds</InputAdornment>
}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => this._onClose()} color="primary">
Cancel
</Button>
<Button
onClick={() => this._onSave()}
color="primary"
variant="contained"
>
Save
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
Example #13
Source File: OnCallList.tsx From backstage-plugin-opsgenie with MIT License | 5 votes |
SchedulesGrid = ({ schedules, cardsPerPage }: { schedules: Schedule[], cardsPerPage: number }) => {
const classes = useStyles();
const [results, setResults] = React.useState(schedules);
const [search, setSearch] = React.useState("");
const [page, setPage] = React.useState(1);
const [offset, setOffset] = React.useState(0);
const handleChange = (_: React.ChangeEvent<unknown>, value: number) => {
setOffset((value - 1) * cardsPerPage);
setPage(value);
};
const debouncedSearch = useDebounce(search, 300);
// debounced search
useEffect(
() => {
if (!debouncedSearch) {
setResults(schedules);
return;
}
const filtered = schedules.filter(schedule => {
return schedule.name.toLowerCase().includes(debouncedSearch.toLowerCase());
});
setResults(filtered);
},
[debouncedSearch, schedules]
);
return (
<div>
<TextField
fullWidth
variant="outlined"
className={classes.search}
placeholder="Team…"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
)
}}
onChange={e => setSearch(e.target.value)}
/>
<ItemCardGrid classes={{ root: classes.onCallItemGrid }}>
{results.filter((_, i) => i >= offset && i < offset + cardsPerPage).map(schedule => <OnCallForScheduleCard key={schedule.id} schedule={schedule} />)}
</ItemCardGrid>
<Pagination
className={classes.pagination}
count={Math.ceil(results.length / cardsPerPage)}
page={page}
onChange={handleChange}
showFirstButton
showLastButton
/>
</div>
);
}
Example #14
Source File: BoundaryDropdown.tsx From prism-frontend with MIT License | 5 votes |
SearchField = forwardRef(
(
{
// important this isn't called `value` since this would confuse <Select/>
// the main purpose of wrapping this text-field is for this very purpose.
search,
setSearch,
}: {
search: string;
setSearch: (val: string) => void;
},
ref: TextFieldProps['ref'],
) => {
const styles = useStyles();
return (
<TextField
ref={ref}
onKeyDown={e => e.stopPropagation()}
className={styles.searchField}
value={search}
onChange={e => {
setSearch(e.target.value);
// when something is selected, and the user tries to search, this field deselects for some reason,
// thus reselect on change. Important to capture target as it's null inside timeout.
const { target } = e;
setTimeout(() => {
target.focus();
}, TIMEOUT_ANIMATION_DELAY);
}}
InputProps={{
startAdornment: (
<InputAdornment position="end">
<Search />
</InputAdornment>
),
}}
/>
);
},
)
Example #15
Source File: UpdatableField.tsx From clearflask with Apache License 2.0 | 5 votes |
render() {
return (
<div className={this.props.classes.wrapper}>
<TextField
value={(this.state.value === undefined ? this.props.value : this.state.value) || ''}
onChange={e => this.setState({ value: e.target.value })}
type={!this.props.isPassword || this.state.revealPassword ? 'text' : 'password'}
disabled={this.state.isSubmitting}
helperText={this.props.helperText}
InputProps={{
readOnly: this.props.isToken ? true : undefined,
endAdornment: this.props.isPassword ? (
<InputAdornment position='end'>
<IconButton
aria-label='Toggle password visibility'
onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
disabled={this.state.isSubmitting}
>
{this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
</IconButton>
</InputAdornment>
) : (this.props.isToken ? (
<InputAdornment position='end'>
<IconButton
aria-label='Reset'
onClick={() => this.setState({ value: randomUuid().replace(/[^a-zA-Z0-9]+/g, '') })}
disabled={this.state.isSubmitting}
>
<KeyRefreshIcon fontSize='small' />
</IconButton>
</InputAdornment>
) : undefined),
}}
/>
<SubmitButton
aria-label="Save"
color='primary'
style={{
visibility: !this.state.value || this.state.value === this.props.value
? 'hidden' : undefined
}}
isSubmitting={this.state.isSubmitting}
onClick={() => {
this.setState({ isSubmitting: true });
this.props.onSave(this.state.value || '')
.then(() => this.setState({
isSubmitting: false,
value: this.props.value,
}))
.catch(e => this.setState({
isSubmitting: false,
}));
}}
>Save</SubmitButton>
</div>
);
}
Example #16
Source File: AdvancedSettings.tsx From wonderland-frontend with MIT License | 5 votes |
function AdvancedSettings({ open, handleClose, slippage, onSlippageChange }: IAdvancedSettingsProps) {
const [value, setValue] = useState(slippage);
useEffect(() => {
let timeount: any = null;
clearTimeout(timeount);
timeount = setTimeout(() => onSlippageChange(value), 1000);
return () => clearTimeout(timeount);
}, [value]);
return (
<Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
<Paper className="ohm-card ohm-popover">
<div className="cross-wrap">
<IconButton onClick={handleClose}>
<SvgIcon color="primary" component={XIcon} />
</IconButton>
</div>
<p className="hades-title">Settings</p>
<Box className="card-content">
<InputLabel htmlFor="slippage">
<p className="input-lable">Slippage</p>
</InputLabel>
<FormControl variant="outlined" color="primary" fullWidth>
<OutlinedInput
id="slippage"
value={value}
onChange={(e: any) => setValue(e.target.value)}
fullWidth
type="number"
className="bond-input"
endAdornment={
<InputAdornment position="end">
<p className="percent">%</p>
</InputAdornment>
}
/>
<div className="help-text">
<p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
</div>
</FormControl>
</Box>
</Paper>
</Modal>
);
}
Example #17
Source File: MapSearch.tsx From covid19testing-map with GNU General Public License v3.0 | 5 votes |
MapSearch = ({ map }: SearchProps) => {
const classes = useStyles();
const [searchBox, setSearchBox] = useState<any>();
const onLoad = (ref: any) => {
setSearchBox(ref);
};
const onPlacesChanged = () => {
if (searchBox.getPlaces()[0] !== undefined) {
map.fitBounds(searchBox.getPlaces()[0].geometry.viewport);
}
};
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: REACT_APP_GCP_MAPS_API_KEY,
libraries,
});
const handleSearchClick = React.useCallback(() => {
trackUiClick('Search');
}, []);
const renderSearchBox = () => {
return (
<div id="search-input">
<StandaloneSearchBox onLoad={onLoad} onPlacesChanged={onPlacesChanged}>
<Input
placeholder='Search places like "Albany", "NYU", or "94025"'
className={classes.input}
inputProps={{ 'aria-label': 'search google maps' }}
onClick={handleSearchClick}
startAdornment={
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
}
/>
</StandaloneSearchBox>
</div>
);
};
if (loadError) {
return <div>Search bar cannot be loaded right now, sorry.</div>;
}
return isLoaded ? renderSearchBox() : <div />;
}
Example #18
Source File: AdvancedSettings.tsx From rugenerous-frontend with MIT License | 5 votes |
function AdvancedSettings({
open,
handleClose,
slippage,
recipientAddress,
onRecipientAddressChange,
onSlippageChange,
}: IAdvancedSettingsProps) {
const [value, setValue] = useState(slippage);
useEffect(() => {
let timeount: any = null;
clearTimeout(timeount);
timeount = setTimeout(() => onSlippageChange(value), 1000);
return () => clearTimeout(timeount);
}, [value]);
return (
<Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
<Paper className="ohm-card ohm-popover">
<div className="cross-wrap">
<IconButton onClick={handleClose}>
<SvgIcon color="primary" component={XIcon} />
</IconButton>
</div>
<p className="hades-title">Settings</p>
<Box className="card-content">
<InputLabel htmlFor="slippage">
<p className="input-lable">Slippage</p>
</InputLabel>
<FormControl variant="outlined" color="primary" fullWidth>
<OutlinedInput
id="slippage"
value={value}
onChange={(e: any) => setValue(e.target.value)}
fullWidth
type="number"
className="bond-input"
endAdornment={
<InputAdornment position="end">
<p className="percent">%</p>
</InputAdornment>
}
/>
<div className="help-text">
<p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
</div>
</FormControl>
<InputLabel htmlFor="recipient">
<p className="input-lable">Recipient Address</p>
</InputLabel>
<FormControl variant="outlined" color="primary" fullWidth>
<OutlinedInput
className="bond-input"
id="recipient"
value={recipientAddress}
onChange={onRecipientAddressChange}
type="text"
/>
<div className="help-text">
<p className="text-bond-desc">
Choose recipient address. By default, this is your currently connected address
</p>
</div>
</FormControl>
</Box>
</Paper>
</Modal>
);
}
Example #19
Source File: EntitySearchBar.tsx From backstage with Apache License 2.0 | 5 votes |
EntitySearchBar = () => {
const classes = useStyles();
const { filters, updateFilters } = useEntityList();
const [search, setSearch] = useState(filters.text?.value ?? '');
useDebounce(
() => {
updateFilters({
text: search.length ? new EntityTextFilter(search) : undefined,
});
},
250,
[search, updateFilters],
);
return (
<Toolbar className={classes.searchToolbar}>
<FormControl>
<Input
aria-label="search"
id="input-with-icon-adornment"
className={classes.input}
placeholder="Search"
autoComplete="off"
onChange={event => setSearch(event.target.value)}
value={search}
startAdornment={
<InputAdornment position="start">
<Search />
</InputAdornment>
}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="clear search"
onClick={() => setSearch('')}
edge="end"
disabled={search.length === 0}
>
<Clear />
</IconButton>
</InputAdornment>
}
/>
</FormControl>
</Toolbar>
);
}
Example #20
Source File: TextInput.stories.tsx From anchor-web-app with Apache License 2.0 | 5 votes |
textAdornmentInputProps = { endAdornment: <InputAdornment position="end">UST</InputAdornment>, }
Example #21
Source File: ProductCreate.tsx From ra-enterprise-demo with MIT License | 5 votes |
ProductCreate: FC<CreateProps> = props => {
useDefineAppLocation('catalog.products.create');
const classes = useStyles();
return (
<Create actions={<CreateActions />} {...props}>
<TabbedForm>
<FormTab label="resources.products.tabs.image">
<TextInput source="image" fullWidth validate={required()} />
<TextInput
source="thumbnail"
fullWidth
validate={required()}
/>
</FormTab>
<FormTab label="resources.products.tabs.details" path="details">
<TextInput source="reference" validate={required()} />
<NumberInput
source="price"
validate={required()}
className={classes.price}
InputProps={{
startAdornment: (
<InputAdornment position="start">
€
</InputAdornment>
),
}}
/>
<NumberInput
source="width"
validate={required()}
className={classes.width}
formClassName={classes.widthFormGroup}
InputProps={{
endAdornment: (
<InputAdornment position="start">
cm
</InputAdornment>
),
}}
/>
<NumberInput
source="height"
validate={required()}
className={classes.height}
formClassName={classes.heightFormGroup}
InputProps={{
endAdornment: (
<InputAdornment position="start">
cm
</InputAdornment>
),
}}
/>
<ReferenceInput
source="category_id"
reference="categories"
allowEmpty
>
<SelectInput source="name" />
</ReferenceInput>
<NumberInput
source="stock"
validate={required()}
className={classes.stock}
/>
</FormTab>
<FormTab
label="resources.products.tabs.description"
path="description"
>
<MarkdownInput source="description" label="" />
</FormTab>
</TabbedForm>
</Create>
);
}
Example #22
Source File: Input.tsx From prompts-ai with MIT License | 5 votes |
export default function Input(props: Props) {
const dispatch = useDispatch();
const inputValue = useSelector(
(state: RootState) => {
const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!;
return workspace.conversations.find(c => c.id === props.conversationId)!.inputValue;
}
);
const hasStarted = useSelector(
(state: RootState) => {
const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!;
return workspace.conversations.find(c => c.id === props.conversationId)!.parts.some(c => c.submitted);
}
);
const onSend = () => {
dispatch(sendMessageInConversationAsync(props.conversationId));
props.afterSend();
dispatch(normalizeConversations());
}
const onInputChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
dispatch(updateConversationInputValue({conversationId: props.conversationId, inputValue: event.currentTarget.value}));
}
return <TextField multiline
label={'Message (Cmd+Enter to send)'}
InputLabelProps={{
shrink: true,
}}
placeholder={hasStarted ? 'Your response' : 'Start a conversation'}
value={inputValue}
onChange={onInputChange}
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.metaKey && event.key === 'Enter') {
onSend();
}
}}
onKeyUp={(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.ctrlKey && event.key === 'Enter') {
onSend();
}
}}
variant={'outlined'}
fullWidth={true}
InputProps={{
endAdornment: (<InputAdornment position="end">
<IconButton edge="end" onClick={onSend}>
<SendIcon />
</IconButton>
</InputAdornment>)
}}
/>;
}
Example #23
Source File: LabelDialog.tsx From knboard with MIT License | 5 votes |
LabelDialog = () => {
const theme = useTheme();
const dispatch = useDispatch();
const open = useSelector((state: RootState) => state.label.dialogOpen);
const labels = useSelector(selectAllLabels);
const [searchValue, setSearchValue] = useState("");
const [creating, setCreating] = useState(false);
const xsDown = useMediaQuery(theme.breakpoints.down("xs"));
const filteredLabels = labels.filter((label) =>
label.name.toLowerCase().match(searchValue.trim().toLowerCase())
);
const handleClose = () => {
dispatch(setDialogOpen(false));
};
return (
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
fullScreen={xsDown}
>
<Close onClose={handleClose} />
<DialogTitle id="edit-labels">Edit labels</DialogTitle>
<Container>
<Flex
css={css`
align-items: flex-end;
${creating && "margin-bottom: 1rem;"}
`}
>
<LabelCount>
{filteredLabels.length} label{filteredLabels.length !== 1 && "s"}
</LabelCount>
<div>
<Hidden xsDown implementation="css">
<TextField
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search labels"
InputProps={{
startAdornment: (
<InputAdornment
position="start"
css={css`
color: #ccc;
`}
>
<FontAwesomeIcon icon={faSearch} />
</InputAdornment>
),
}}
/>
</Hidden>
</div>
<Button
size="small"
color="primary"
variant="contained"
onClick={() => setCreating(true)}
css={css`
margin-right: 1rem;
`}
>
New label
</Button>
</Flex>
{creating && <LabelCreate setCreating={setCreating} />}
<Table>
{filteredLabels.map((label) => (
<LabelRow key={label.id + label.color + label.name} label={label} />
))}
</Table>
</Container>
</Dialog>
);
}
Example #24
Source File: App.tsx From isitworththecost with MIT License | 4 votes |
function App() {
const classes = useStyles()
const [values, setValues] = React.useState({
timeCost: { value: 25, unit: 'dollars', period: 'hour' },
serviceCost: { value: 125, unit: 'dollars', period: 'month' },
trainingTime: { value: 2, unit: 'hour', period: null },
timeSavings: { value: 60, unit: 'min', period: 'day' },
peopleCount: { value: 1, unit: null, period: null },
savingPeriodCost: 'year',
savingPeriodPeople: 'day',
paybackPeriod: 'day',
})
const [costs, setCosts] = React.useState({
employeePerYear: 0,
servicePerYear: 0,
trainingPerYear: 0,
savingsPerYear: 0,
freeTimePerYear: 0,
paybackTimePerYear: 0,
})
const handleChange = (prop: string, key: string | null = null) => (
event: ChangeEvent<HTMLInputElement | { value: unknown }>,
): void => {
let val: any = event.target.value
if (key === null) {
setValues({
...values,
[prop]: val,
})
} else {
if (key === 'value' && (val < 0 || isNaN(val))) {
val = 0
}
setValues({
...values,
[prop]: {
//@ts-ignore
value: values[prop].value,
//@ts-ignore
unit: values[prop].unit,
//@ts-ignore
period: values[prop].period,
//@ts-ignore
[key]: val,
},
})
}
}
useEffect(() => {
// save this to state for now for ease of visibility
const employeePerYear =
values.timeCost.value * periodToYear(values.timeCost.period, 1)
const servicePerYear =
values.serviceCost.value *
periodToYear(values.serviceCost.period, 1)
// assumes amortisation period of 1 year
const trainingPerYear =
unitToYear(values.trainingTime.unit, values.trainingTime.value) *
employeePerYear *
values.peopleCount.value
const freeTimePerYear =
periodToYear(
values.timeSavings.period,
unitToYear(values.timeSavings.unit, values.timeSavings.value),
) * values.peopleCount.value
const savingsPerYear =
employeePerYear * freeTimePerYear - servicePerYear - trainingPerYear
const paybackTimePerYear =
(trainingPerYear + servicePerYear) / employeePerYear
setCosts({
employeePerYear,
servicePerYear,
trainingPerYear,
savingsPerYear,
freeTimePerYear,
paybackTimePerYear,
})
}, [values])
return (
<Container maxWidth={'md'}>
<Paper className={classes.root} variant={'outlined'}>
<div className={classes.heading}>
<TopControls />
<Typography variant="h2" component="h1">
Is it worth the cost?
</Typography>
<Typography variant="h5" component="p" gutterBottom>
A simple check on whether purchasing a service is worth
the cost.
</Typography>
</div>
<Grid container>
<Grid item xs={12} md={6}>
<h2>Basics</h2>
<p>1. Cost of your time or an employees time.</p>
<FormControl
className={clsx(classes.margin, classes.textField)}
variant="outlined"
>
<InputLabel htmlFor="time-cost">
Time Cost
</InputLabel>
<OutlinedInput
id="time-cost"
value={values.timeCost.value}
type="number"
onChange={handleChange('timeCost', 'value')}
startAdornment={
<InputAdornment position="start">
<AttachMoneyIcon />
</InputAdornment>
}
labelWidth={80}
/>
</FormControl>
<FormControl
variant="outlined"
className={classes.margin}
>
<InputLabel htmlFor="time-cost-unit">
per
</InputLabel>
<Select
native
value={values.timeCost.period}
onChange={handleChange('timeCost', 'period')}
labelWidth={40}
inputProps={{
name: 'per',
id: 'time-cost-unit',
}}
>
<option value={'hour'}>hour</option>
<option value={'day'}>day</option>
<option value={'week'}>week</option>
<option value={'month'}>month</option>
<option value={'year'}>year</option>
</Select>
</FormControl>
<p>2. Cost of the service under consideration.</p>
<FormControl
className={clsx(classes.margin, classes.textField)}
variant="outlined"
>
<InputLabel htmlFor="service-cost">
Service Cost
</InputLabel>
<OutlinedInput
id="service-cost"
value={values.serviceCost.value}
type="number"
onChange={handleChange('serviceCost', 'value')}
startAdornment={
<InputAdornment position="start">
<AttachMoneyIcon />
</InputAdornment>
}
labelWidth={95}
/>
</FormControl>
<FormControl
variant="outlined"
className={classes.margin}
>
<InputLabel htmlFor="service-cost-period">
per
</InputLabel>
<Select
native
value={values.serviceCost.period}
onChange={handleChange('serviceCost', 'period')}
labelWidth={40}
inputProps={{
name: 'per',
id: 'service-cost-period',
}}
>
{/*<option value={'hour'}>hour</option>*/}
<option value={'day'}>day</option>
<option value={'week'}>week</option>
<option value={'month'}>month</option>
<option value={'year'}>year</option>
</Select>
</FormControl>
<p>
3. Estimate the training time required (one person).
</p>
<FormControl
fullWidth
className={clsx(classes.margin, classes.textField)}
variant="outlined"
>
<InputLabel htmlFor="training-time">
Training Time
</InputLabel>
<OutlinedInput
id="training-time"
value={values.trainingTime.value}
type="number"
onChange={handleChange('trainingTime', 'value')}
startAdornment={
<InputAdornment position="start">
<AccessTimeIcon />
</InputAdornment>
}
labelWidth={105}
/>
</FormControl>
<FormControl
variant="outlined"
className={classes.margin}
>
<Select
native
value={values.trainingTime.unit}
onChange={handleChange('trainingTime', 'unit')}
inputProps={{
name: 'per',
id: 'training-time-unit',
}}
>
<option value={'hour'}>hours</option>
<option value={'day'}>days</option>
<option value={'week'}>weeks</option>
<option value={'month'}>months</option>
{/*<option value={'year'}>years</option>*/}
</Select>
</FormControl>
<p>
4. Estimate the time this service will save (one
person).
</p>
<FormControl
className={clsx(classes.margin, classes.textField)}
variant="outlined"
>
<InputLabel htmlFor="time-savings">
Time Saved
</InputLabel>
<OutlinedInput
id="time-savings"
type="number"
value={values.timeSavings.value}
onChange={handleChange('timeSavings', 'value')}
startAdornment={
<InputAdornment position="start">
<AccessTimeIcon />
</InputAdornment>
}
labelWidth={80}
/>
</FormControl>
<FormControl
variant="outlined"
className={classes.margin}
>
<Select
native
value={values.timeSavings.unit}
onChange={handleChange('timeSavings', 'unit')}
>
<option value={'minute'}>minutes</option>
<option value={'hour'}>hours</option>
<option value={'day'}>days</option>
<option value={'week'}>weeks</option>
<option value={'month'}>months</option>
</Select>
</FormControl>
<FormControl
variant="outlined"
className={classes.margin}
>
<InputLabel htmlFor="time-savings-period">
per
</InputLabel>
<Select
id={'time-savings-period'}
native
value={values.timeSavings.period}
onChange={handleChange('timeSavings', 'period')}
labelWidth={40}
>
<option value={'hour'}>hour</option>
<option value={'day'}>day</option>
<option value={'week'}>week</option>
<option value={'month'}>month</option>
<option value={'year'}>year</option>
</Select>
</FormControl>
<p>5. Number of people using the service.</p>
<FormControl
className={clsx(classes.margin, classes.textField)}
variant="outlined"
>
<InputLabel htmlFor="people-count">
People
</InputLabel>
<OutlinedInput
id="people-count"
type="number"
value={values.peopleCount.value}
onChange={handleChange('peopleCount', 'value')}
startAdornment={
<InputAdornment position="start">
<EmojiPeopleIcon />
</InputAdornment>
}
labelWidth={50}
/>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<Breakdown
values={values}
costs={costs}
handleChange={handleChange}
/>
</Grid>
</Grid>
</Paper>
</Container>
)
}
Example #25
Source File: Settings.tsx From swap-ui with Apache License 2.0 | 4 votes |
function SettingsDetails() {
const styles = useStyles();
const { slippage, setSlippage, fairOverride, setFairOverride } =
useSwapContext();
const [showSettingsDialog, setShowSettingsDialog] = useState(false);
const fair = useSwapFair();
const { swapClient } = useDexContext();
const setSlippageHandler = (value?: number) => {
setSlippage(!value || value < 0 ? 0 : value);
};
return (
<div style={{ padding: "15px", width: "305px" }}>
<Typography style={{ fontWeight: "bold" }}>Settings</Typography>
<div>
<div style={{ marginTop: "10px" }}>
<Typography color="textSecondary" style={{ fontSize: "12px" }}>
Slippage tolerance
</Typography>
<TextField
type="number"
placeholder="Error tolerance percentage"
value={slippage}
onChange={(e) => setSlippageHandler(parseFloat(e.target.value))}
style={{
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
/>
</div>
<div style={{ marginTop: "10px" }}>
<Typography color="textSecondary" style={{ fontSize: "12px" }}>
Fair price
</Typography>
<div style={{ display: "flex" }}>
<TextField
type="number"
placeholder="Fair price override"
value={fair}
onChange={(e) => setFairOverride(parseFloat(e.target.value))}
style={{
marginRight: "10px",
flex: 1,
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
disabled={fairOverride === null}
/>
<Button
component="div"
variant="contained"
onClick={() => {
if (fair === undefined) {
console.error("Fair is undefined");
return;
}
if (fairOverride === null) {
setFairOverride(fair);
} else {
setFairOverride(null);
}
}}
className={
fairOverride === null
? styles.fairAutoSelected
: styles.fairAuto
}
>
Auto
</Button>
</div>
</div>
<div style={{ margin: "10px 0px" }}>
<CloseNewAccountsSwitch />
</div>
<Button
variant="contained"
fullWidth
disabled={swapClient.program.provider.wallet.publicKey === null}
onClick={() => setShowSettingsDialog(true)}
>
Manage Dex Accounts
</Button>
</div>
<OpenOrdersDialog
open={showSettingsDialog}
onClose={() => setShowSettingsDialog(false)}
/>
</div>
);
}
Example #26
Source File: UserEdit.tsx From clearflask with Apache License 2.0 | 4 votes |
render() {
const userId = this.props.userId || this.state.createdUserId;
const isMe = !!this.props.loggedInUser && this.props.loggedInUser.userId === userId;
const isModOrAdminLoggedIn = this.props.server.isModOrAdminLoggedIn();
var content;
if (!userId) {
// Create form
if (!isModOrAdminLoggedIn) return null;
content = (
<div key='create-form' className={this.props.classes.section}>
<PanelTitle text={this.props.t('create-user')} />
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<AvatarDisplay user={{
name: this.state.displayName || '',
}} size={40} />
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<TextField
value={this.state.displayName || ''}
onChange={e => this.setState({ displayName: e.target.value })}
/>
<Button aria-label="Create" color='primary' style={{
visibility:
!this.state.displayName ? 'hidden' : undefined
}} onClick={async () => {
if (!this.state.displayName || !isModOrAdminLoggedIn) {
return;
}
const newUserAdmin = await (await this.props.server.dispatchAdmin()).userCreateAdmin({
projectId: this.props.server.getProjectId(),
userCreateAdmin: { name: this.state.displayName },
});
this.setState({
createdUserId: newUserAdmin.userId,
userAdmin: newUserAdmin,
displayName: undefined,
});
}}>{this.props.t('save')}</Button>
</Grid>
</Grid>
</div>
);
} else if (!isModOrAdminLoggedIn && !isMe) {
// View only
content = (
<div key='view-only' className={this.props.classes.section}>
<PanelTitle text={this.props.t('info')} />
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<AvatarDisplay user={this.props.user} size={40} />
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
{DisplayUserName(this.props.user)}
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('registered')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<TimeAgo date={this.props.user?.created || 0} />
</Grid>
</Grid>
</div>
);
} else {
// Edit form (for both self and by admin/mod)
var user: Client.UserMe | Admin.UserAdmin | undefined;
var balance: number | undefined;
if (this.props.loggedInUser?.userId === userId) {
user = this.props.loggedInUser;
balance = this.props.loggedInUserBalance;
} else {
user = this.state.userAdmin;
balance = this.state.userAdmin?.balance;
if (this.userAdminFetchedForUserId !== userId) {
this.userAdminFetchedForUserId = userId;
this.props.server.dispatchAdmin().then(d => d.userGetAdmin({
projectId: this.props.server.getProjectId(),
userId,
}))
.then(userAdmin => this.setState({
userAdmin,
userAdminStatus: Status.FULFILLED,
}))
.catch(e => this.setState({
userAdminStatus: Status.REJECTED,
}));
}
}
if (!user) {
return (<LoadingPage />);
}
const balanceAdjustmentHasError = !!this.state.balanceAdjustment && (!parseInt(this.state.balanceAdjustment) || !+this.state.balanceAdjustment || parseInt(this.state.balanceAdjustment) !== parseFloat(this.state.balanceAdjustment));
const browserPushControl = this.renderBrowserPushControl(isMe, user);
// const androidPushControl = this.renderMobilePushControl(MobileNotificationDevice.Android);
// const iosPushControl = this.renderMobilePushControl(MobileNotificationDevice.Ios);
const emailControl = this.renderEmailControl(isMe, user);
const isPushOrAnon = !user.email && !user.isExternal;
const categoriesWithSubscribe = (this.props.categories || []).filter(c => !!c.subscription);
content = (
<React.Fragment key='edit-user'>
<div className={this.props.classes.section}>
<PanelTitle text={this.props.t('account')} />
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<AvatarDisplay user={{
...user,
...(this.state.displayName !== undefined ? {
name: this.state.displayName,
} : {}),
...(this.state.email !== undefined ? {
email: this.state.email,
} : {}),
}} size={40} />
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
{!!user.isExternal ? (
<Tooltip title={this.props.t('cannot-be-changed')} placement='top-start'>
<Typography>{user.name || 'None'}</Typography>
</Tooltip>
) : (
<>
<TextField
id='displayName'
error={!user.name}
value={(this.state.displayName === undefined ? user.name : this.state.displayName) || ''}
onChange={e => this.setState({ displayName: e.target.value })}
/>
<Button aria-label={this.props.t('save')} color='primary' style={{
visibility:
!this.state.displayName
|| this.state.displayName === user.name
? 'hidden' : undefined
}} onClick={async () => {
if (!this.state.displayName
|| !user
|| this.state.displayName === user.name) {
return;
}
if (isModOrAdminLoggedIn) {
const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdateAdmin: { name: this.state.displayName },
});
this.setState({ displayName: undefined, userAdmin: newUserAdmin });
} else {
await (await this.props.server.dispatch()).userUpdate({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdate: { name: this.state.displayName },
});
this.setState({ displayName: undefined });
}
}}>{this.props.t('save')}</Button>
</>
)}
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('email')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
{!!user.isExternal ? (
<Tooltip title={this.props.t('cannot-be-changed')} placement='top-start'>
<Typography>{user.email || this.props.t('none')}</Typography>
</Tooltip>
) : (
<>
<TextField
id='email'
value={(this.state.email === undefined ? user.email : this.state.email) || ''}
onChange={e => this.setState({ email: e.target.value })}
autoFocus={!!this.state.createdUserId}
/>
<Button aria-label={this.props.t('save')} color='primary' style={{
visibility:
!this.state.email
|| this.state.email === user.email
? 'hidden' : undefined
}} onClick={async () => {
if (!this.state.email
|| !user
|| this.state.email === user.email) {
return;
}
if (isModOrAdminLoggedIn) {
const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdateAdmin: { email: this.state.email },
});
this.setState({ email: undefined, userAdmin: newUserAdmin });
} else {
await (await this.props.server.dispatch()).userUpdate({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdate: { email: this.state.email },
});
this.setState({ email: undefined });
}
}}>{this.props.t('save')}</Button>
</>
)}
</Grid>
</Grid>
{!user.isExternal && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('password-0')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<TextField
id='password'
value={this.state.password || ''}
onChange={e => this.setState({ password: e.target.value })}
type={this.state.revealPassword ? 'text' : 'password'}
disabled={!this.state.email && !user.email}
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<IconButton
aria-label='Toggle password visibility'
onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
disabled={!this.state.email && !user.email}
>
{this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
</IconButton>
</InputAdornment>
),
}}
/>
<Button aria-label={this.props.t('save')} color='primary' style={{
visibility:
!this.state.password
|| this.state.password === user.name
? 'hidden' : undefined
}} onClick={async () => {
if (!this.state.password
|| !user) {
return;
}
if (isModOrAdminLoggedIn) {
const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdateAdmin: { password: this.state.password },
});
this.setState({ password: undefined, userAdmin: newUserAdmin });
} else {
await (await this.props.server.dispatch()).userUpdate({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdate: { password: this.state.password },
});
this.setState({ password: undefined });
}
}}>{this.props.t('save')}</Button>
</Grid>
</Grid>
)}
{this.props.credits && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('balance')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<CreditView val={balance || 0} credits={this.props.credits} />
{isMe && !!this.props.credits?.creditPurchase?.redirectUrl && (
<Button
component={'a' as any}
className={this.props.classes.linkGetMore}
color='primary'
href={this.props.credits.creditPurchase.redirectUrl}
target='_blank'
underline='none'
rel='noopener nofollow'
>
{this.props.credits.creditPurchase.buttonTitle || 'Get more'}
</Button>
)}
{isModOrAdminLoggedIn && (
<>
<IconButton onClick={() => this.setState({ transactionCreateOpen: !this.state.transactionCreateOpen })}>
<EditIcon />
</IconButton>
<Collapse in={this.state.transactionCreateOpen}>
<div>
<TextField
label='Adjustment amount'
value={this.state.balanceAdjustment || ''}
error={balanceAdjustmentHasError}
helperText={balanceAdjustmentHasError ? 'Invalid number' : (
!this.state.balanceAdjustment ? undefined : (
<CreditView
val={+this.state.balanceAdjustment}
credits={this.props.credits}
/>
))}
onChange={e => this.setState({ balanceAdjustment: e.target.value })}
/>
<TextField
label='Transaction note'
value={this.state.balanceDescription || ''}
onChange={e => this.setState({ balanceDescription: e.target.value })}
/>
<Button aria-label="Save" color='primary' style={{
visibility:
(this.state.balanceAdjustment || 0) === 0
? 'hidden' : undefined
}} onClick={async () => {
if (this.state.balanceAdjustment === undefined
|| +this.state.balanceAdjustment === 0
|| !user) {
return;
}
const dispatcher = await this.props.server.dispatchAdmin();
const newUserAdmin = await dispatcher.userUpdateAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdateAdmin: {
transactionCreate: {
amount: +this.state.balanceAdjustment,
summary: this.state.balanceDescription,
}
},
});
this.setState({
userAdmin: newUserAdmin,
transactionCreateOpen: false,
balanceAdjustment: undefined,
balanceDescription: undefined,
});
}}>Save</Button>
</div>
</Collapse>
</>
)}
</Grid>
</Grid>
)}
{isModOrAdminLoggedIn && (
<>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('is-moderator')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<FormControlLabel
control={(
<Switch
color='default'
checked={!!user.isMod}
onChange={async (e, checked) => {
const dispatcher = await this.props.server.dispatchAdmin();
const newUserAdmin = await dispatcher.userUpdateAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
userUpdateAdmin: { isMod: !user?.isMod },
});
this.setState({ password: undefined, userAdmin: newUserAdmin });
}}
/>
)}
label={(
<FormHelperText component='span'>
{user.isMod ? this.props.t('yes') : this.props.t('no')}
</FormHelperText>
)}
/>
</Grid>
</Grid>
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('user-id')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<Typography>{userId}</Typography>
</Grid>
</Grid>
</>
)}
{!!isMe && !this.props.suppressSignOut && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>
{this.props.t('sign-out-of-your-account')}
{!!isPushOrAnon && (
<Collapse in={!!this.state.signoutWarnNoEmail}>
<Alert
variant='outlined'
severity='warning'
>
{this.props.t('please-add-an-email-before')}
</Alert>
</Collapse>
)}
</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<Button
disabled={!!isPushOrAnon && !!this.state.signoutWarnNoEmail}
onClick={() => {
if (isPushOrAnon) {
this.setState({ signoutWarnNoEmail: true });
} else {
this.props.server.dispatch().then(d => d.userLogout({
projectId: this.props.server.getProjectId(),
}));
}
}}
>Sign out</Button>
</Grid>
</Grid>
)}
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('delete-account')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
<Button
onClick={() => this.setState({ deleteDialogOpen: true })}
>Delete</Button>
<Dialog
open={!!this.state.deleteDialogOpen}
onClose={() => this.setState({ deleteDialogOpen: false })}
>
<DialogTitle>Delete account?</DialogTitle>
<DialogContent>
<DialogContentText>{isMe
? 'By deleting your account, you will be signed out of your account and your account will be permanently deleted including all of your data.'
: 'Are you sure you want to permanently delete this user?'}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({ deleteDialogOpen: false })}>Cancel</Button>
<Button style={{ color: this.props.theme.palette.error.main }} onClick={async () => {
if (isModOrAdminLoggedIn) {
await (await this.props.server.dispatchAdmin()).userDeleteAdmin({
projectId: this.props.server.getProjectId(),
userId: userId!,
});
} else {
await (await this.props.server.dispatch()).userDelete({
projectId: this.props.server.getProjectId(),
userId: userId!,
});
}
this.props.onDeleted?.();
this.setState({ deleteDialogOpen: false });
}}>{this.props.t('delete')}</Button>
</DialogActions>
</Dialog>
</Grid>
</Grid>
</div>
<div className={this.props.classes.section}>
<PanelTitle text={this.props.t('notifications')} />
{browserPushControl && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>{this.props.t('browser-desktop-messages')}</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{browserPushControl}</Grid>
</Grid>
)}
{/* {androidPushControl && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>Android Push messages</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{androidPushControl}</Grid>
</Grid>
)}
{iosPushControl && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}><Typography>Apple iOS Push messages</Typography></Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{iosPushControl}</Grid>
</Grid>
)} */}
{emailControl && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}>
<Typography>
{this.props.t('email')}
{user.email !== undefined && (<Typography variant='caption'> ({truncateWithElipsis(20, user.email)})</Typography>)}
</Typography>
</Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{emailControl}</Grid>
</Grid>
)}
{categoriesWithSubscribe.map(category => !!user && (
<Grid container alignItems='center' className={this.props.classes.item}>
<Grid item xs={12} sm={6}>
<Typography>{this.props.t('new-category', { category: category.name })}</Typography>
</Grid>
<Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
{this.renderCategorySubscribeControl(category, isMe, user)}
</Grid>
</Grid>
))}
</div>
</React.Fragment>
);
}
return (
<div className={classNames(this.props.className, this.props.classes.settings)}>
{content}
</div>
);
}
Example #27
Source File: TechDocsSearch.tsx From backstage with Apache License 2.0 | 4 votes |
TechDocsSearchBar = (props: TechDocsSearchProps) => {
const { entityId, debounceTime = 150 } = props;
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const {
term,
setTerm,
setFilters,
result: { loading, value: searchVal },
} = useSearch();
const classes = useStyles();
const [options, setOptions] = useState<any[]>([]);
useEffect(() => {
let mounted = true;
if (mounted && searchVal) {
// TODO: Change this into getting only subset of search results from the BE in the first place
// once pagination is implemented for search engines
// See: https://github.com/backstage/backstage/issues/6062
const searchResults = searchVal.results.slice(0, 10);
setOptions(searchResults);
}
return () => {
mounted = false;
};
}, [loading, searchVal]);
const [value, setValue] = useState<string>(term);
useDebounce(() => setTerm(value), debounceTime, [value]);
// Update the filter context when the entityId changes, e.g. when the search
// bar continues to be rendered, navigating between different TechDocs sites.
const { kind, name, namespace } = entityId;
useEffect(() => {
setFilters(prevFilters => {
return {
...prevFilters,
kind,
namespace,
name,
};
});
}, [kind, namespace, name, setFilters]);
const handleQuery = (e: ChangeEvent<HTMLInputElement>) => {
if (!open) {
setOpen(true);
}
setValue(e.target.value);
};
const handleSelection = (_: any, selection: TechDocsSearchResult | null) => {
if (selection?.document) {
const { location } = selection.document;
navigate(location);
}
};
return (
<Autocomplete
classes={{ root: classes.root }}
data-testid="techdocs-search-bar"
size="small"
open={open}
getOptionLabel={() => ''}
filterOptions={x => {
return x; // This is needed to get renderOption to be called after options change. Bug in material-ui?
}}
onClose={() => {
setOpen(false);
}}
onFocus={() => {
setOpen(true);
}}
onChange={handleSelection}
blurOnSelect
noOptionsText="No results found"
value={null}
options={options}
renderOption={({ document, highlight }) => (
<TechDocsSearchResultListItem
result={document}
lineClamp={3}
asListItem={false}
asLink={false}
title={document.title}
highlight={highlight}
/>
)}
loading={loading}
renderInput={params => (
<TextField
{...params}
data-testid="techdocs-search-bar-input"
variant="outlined"
fullWidth
placeholder={`Search ${entityId.name} docs`}
value={value}
onChange={handleQuery}
InputProps={{
...params.InputProps,
startAdornment: (
<InputAdornment position="start">
<IconButton aria-label="Query" disabled>
<SearchIcon />
</IconButton>
</InputAdornment>
),
endAdornment: (
<React.Fragment>
{loading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
);
}
Example #28
Source File: ProductEdit.tsx From ra-enterprise-demo with MIT License | 4 votes |
ProductEdit: FC<EditProps> = props => {
const { resource, id } = props;
const classes = useStyles();
const notify = useNotify();
const { loading } = useLock(
resource as string,
id as string,
'todousername',
{
onFailure: () => {
notify('ra-realtime.notification.lock.lockedBySomeoneElse');
},
}
);
if (loading) {
return <CircularProgress />;
}
return (
<Edit
{...props}
className={classes.root}
title={<ProductTitle />}
actions={<EditActions />}
component="div"
>
<ProductEditFormWithPreview toolbar={<CustomToolbar />}>
<Poster />
<TextInput source="image" fullWidth />
<TextInput source="thumbnail" fullWidth />
<AccordionSection
label="resources.products.tabs.description"
data-tour-id="description-tab"
fullWidth
>
<MarkdownInput source="description" label="" />
</AccordionSection>
<AccordionSection
label="resources.products.tabs.details"
fullWidth
>
<TextInput source="reference" />
<NumberInput
source="price"
className={classes.price}
InputProps={{
startAdornment: (
<InputAdornment position="start">
€
</InputAdornment>
),
}}
/>
<NumberInput
source="width"
className={classes.width}
formClassName={classes.widthFormGroup}
InputProps={{
endAdornment: (
<InputAdornment position="start">
cm
</InputAdornment>
),
}}
/>
<NumberInput
source="height"
className={classes.height}
formClassName={classes.heightFormGroup}
InputProps={{
endAdornment: (
<InputAdornment position="start">
cm
</InputAdornment>
),
}}
/>
<ReferenceInput source="category_id" reference="categories">
<SelectInput source="name" />
</ReferenceInput>
<NumberInput source="stock" className={classes.stock} />
</AccordionSection>
<AccordionSection
label="resources.products.tabs.reviews"
fullWidth
>
<ReferenceManyField
label=""
reference="reviews"
target="product_id"
pagination={<ReferenceManyFieldPagination />}
fullWidth
>
<Datagrid>
<DateField source="date" />
<CustomerReferenceField />
<StarRatingField />
<TextField
source="comment"
cellClassName={classes.comment}
/>
<TextField source="status" />
<EditButton />
</Datagrid>
</ReferenceManyField>
</AccordionSection>
</ProductEditFormWithPreview>
</Edit>
);
}
Example #29
Source File: Detail.tsx From Demae with MIT License | 4 votes |
Edit = ({ sku, onClose }: { sku: SKU, onClose: () => void }) => {
const theme = useTheme();
const [setProcessing] = useProcessing()
const [provider] = useProviderBlank()
const [product] = useAdminProviderProductDraft()
const [images, setImages] = useState<File[]>([])
const [name] = useTextField(sku?.name)
const [caption] = useTextField(sku?.caption)
const [price, setPrice] = useTextField(String(sku?.price), { inputProps: { pattern: "^[0-9]*$" } })
const [taxRate, setTaxRate] = useTextField(String(sku?.taxRate), { inputProps: { pattern: "^([1-9]|[1-9][0-9])$" } })
const [currency, setCurrency] = useSelect(sku.currency)
const [inventory, setInventory] = useSelect(sku.inventory.type)
const [stockValue, setStockValue] = useSelect(sku.inventory.value || "in_stock")
const [quantity] = useTextField(String(sku.inventory.quantity || 0))
const [description, setDescription] = useState(sku?.description || "");
const [selectedTab, setSelectedTab] = useState<"write" | "preview">("write");
const [showDrawer, closeDrawer] = useDrawer()
const [showSnackbar] = useSnackbar()
const currencyMenu = useMenu(SupportedCurrencies.map(c => {
return {
label: `${c.code} ${c.symbol}`,
value: c.code,
}
}))
const inventoryMenu = useMenu([
{
label: "Bucket",
value: "bucket"
},
{
label: "Finite",
value: "finite"
},
{
label: "Infinite",
value: "infinite"
}
])
const stockValueMenu = useMenu([
{
label: "In Stock",
value: "in_stock"
},
{
label: "Limited",
value: "limited"
},
{
label: "Out Of Stock",
value: "out_of_stock"
}
])
useEdit(async (event) => {
event.preventDefault()
if (!provider) return
if (!product) return
if (!sku) return
setProcessing(true)
const batch = firebase.firestore().batch()
const uploadedImages = await Promise.all(uploadImages(images))
const priceValue = price.value as string
const taxRateValue = taxRate.value as string
sku.name = name.value as string
sku.caption = caption.value as string
sku.description = description
sku.price = Number(priceValue.replace(",", ""))
sku.taxRate = Number(taxRateValue.replace(",", ""))
sku.currency = currency.value as CurrencyCode
sku.inventory = {
type: inventory.value as StockType,
value: stockValue.value as StockValue,
quantity: Number(quantity.value)
}
sku.productReference = product.documentReference
const nowPrice = product.price || {}
var productPrice = nowPrice[sku.currency] || Infinity
productPrice = Math.min(productPrice, sku.price)
productPrice = Math.max(productPrice, 0)
if (uploadedImages.length) {
const fileterd = uploadedImages.filter(image => !!image) as StorageFile[]
const images = fileterd.map(value => value.data())
batch.set(sku.documentReference, {
...sku.data(),
images: firebase.firestore.FieldValue.arrayUnion(...images)
}, { merge: true })
} else {
batch.set(sku.documentReference, {
...sku.data(),
}, { merge: true })
}
batch.update(product.documentReference, {
price: {
...nowPrice,
[sku.currency]: productPrice
}
})
await batch.commit()
setProcessing(false)
onClose()
if (!sku.isAvailable) {
if (sku.inventory.type === "finite") {
const snapshot = await provider.products.collectionReference
.doc(product.id)
.collection("skus")
.doc(sku.id)
.collection("stocks")
.get()
const count = snapshot.docs.reduce((prev, current) => {
return prev + current.data()!["count"]
}, 0)
if (count > 0) {
showDrawer(
<ActionSheet title="Do you want to make the SKU available?" actions={
[
{
title: "YES",
handler: async () => {
sku.isAvailable = true
await sku.update()
showSnackbar("success", `You must publish your product for the changes to take effect.`)
closeDrawer()
}
}
]
} />
)
}
} else {
showDrawer(
<ActionSheet title="Do you want to make the SKU available?" actions={
[
{
title: "YES",
handler: async () => {
sku.isAvailable = true
await sku.update()
showSnackbar("success", `You must publish your product for the changes to take effect.`)
closeDrawer()
}
}
]
} />
)
}
}
})
const uploadImages = (files: File[]) => {
return files.map(file => {
return uploadImage(file)
})
}
const extension = (type: string) => {
if (type === "image/jpeg") return "jpg"
if (type === "image/png") return "png"
}
const uploadImage = (file: File): Promise<StorageFile | undefined> => {
const id = firebase.firestore().collection("/dummy").doc().id
const ref = firebase.storage().ref(product!.documentReference.path + `/images/${id}.${extension(file.type)}`)
return new Promise((resolve, reject) => {
ref.put(file).then(async (snapshot) => {
if (snapshot.state === "success") {
const storageFile = new StorageFile()
if (snapshot.metadata.contentType) {
storageFile.mimeType = snapshot.metadata.contentType
}
storageFile.path = ref.fullPath
resolve(storageFile)
} else {
reject(undefined)
}
})
})
}
const updatedAt = Dayjs(sku.updatedAt.toDate())
return (
<Paper elevation={0} style={{
height: "100%",
width: "100%",
background: "inherit"
}}>
<Box width="100%" padding={2}>
<Paper elevation={0} square={false} style={{
height: "100%",
width: "100%",
marginBottom: theme.spacing(2)
}}>
<Box padding={2} height="100%">
<article>
<Box paddingBottom={1} display="flex">
<Box flexGrow={1}>
<Typography variant="h2">{sku.name}</Typography>
<Box color="text.secondary">
<Typography variant="caption">
{`ID: ${sku.id}`} - {updatedAt.format("YYYY-MM-DD HH:mm:ss")}
</Typography>
</Box>
<Box display="flex" alignItems="center" paddingY={1}>
<Typography variant="subtitle1" style={{
marginRight: theme.spacing(0.5),
}}>Inventory Type</Typography>
<Chip label={sku.inventory.type} variant="outlined" />
</Box>
</Box>
</Box>
<Divider />
<Box paddingY={2}>
<MediaController URLs={sku.imageURLs()} isEditing maxCount={MAXIMUM_NUMBER_OF_IMAGES}
onDrop={(files) => {
setImages(files)
}}
onDeleteImage={async (props) => {
const { index } = props
showDrawer(
<ActionSheet title="Do you want to delete the image?" actions={
[
{
title: "Delete",
handler: async () => {
const image = sku.images[index]
const imageData = image.data()
await sku.documentReference.update({
images: firebase.firestore.FieldValue.arrayRemove(imageData)
})
showSnackbar("success", "The image has been removed.")
closeDrawer()
}
}
]
} />
)
}}
onDeleteUploadImage={(props) => {
const { index } = props
const _images = images.filter((value, idx) => index !== idx)
setImages(_images)
}}
onError={() => {
showSnackbar("error", `The maximum number of images is ${MAXIMUM_NUMBER_OF_IMAGES}.`)
}} />
<Box paddingTop={2}>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>Name</Typography>
<TextField variant="outlined" margin="dense" required {...name} fullWidth />
</Box>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>Caption</Typography>
<TextField variant="outlined" margin="dense" required {...caption} fullWidth />
</Box>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>Description</Typography>
<ReactMde
value={description}
onChange={setDescription}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={markdown =>
Promise.resolve(converter.makeHtml(markdown))
}
/>
</Box>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>Price</Typography>
<Box display="flex">
<FormControl variant="outlined" size="small" margin="dense">
<Select variant="outlined" {...currency} >
{currencyMenu}
</Select>
</FormControl>
<TextField variant="outlined" margin="dense" required {...price} style={{
marginLeft: "8px"
}} />
</Box>
</Box>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>TaxRate</Typography>
<Box display="flex">
<TextField variant="outlined" margin="dense" required {...taxRate} InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>
}} />
</Box>
</Box>
<Box paddingBottom={2}>
<Typography variant="subtitle1" gutterBottom>Inventory</Typography>
<FormControl variant="outlined" margin="dense" size="small">
<Select variant="outlined" {...inventory} >
{inventoryMenu}
</Select>
</FormControl>
{inventory.value === "bucket" &&
<FormControl variant="outlined" margin="dense" size="small" style={{ marginLeft: "8px" }}>
<Select variant="outlined" {...stockValue}>
{stockValueMenu}
</Select>
</FormControl>
}
</Box>
</Box>
</Box>
</article>
</Box>
</Paper >
</Box>
{inventory.value === "finite" &&
<Box padding={2} width="100%">
<Typography variant="h2" gutterBottom>Inventory</Typography>
<Paper elevation={0} square={false} style={{
height: "100%",
marginBottom: theme.spacing(2)
}}>
<Box>
<InventoryTableRow sku={sku} />
</Box>
</Paper>
<Typography variant="body2" gutterBottom>Manage inventory, including different sizes and colors.</Typography>
</Box>
}
</Paper>
)
}