@mui/material#Autocomplete TypeScript Examples
The following examples show how to use
@mui/material#Autocomplete.
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: ServerSwitch.tsx From NekoMaid with MIT License | 5 votes |
ServerSwitch: React.FC<DialogProps> = props => {
const [value, setValue] = useState<string>('')
let error = false
// eslint-disable-next-line no-new
try { if (value) new URL(value.startsWith('http://') ? value : 'http://' + value) } catch { error = true }
return <Dialog fullWidth maxWidth='xs' {...props}>
<DialogTitle>{lang.serverSwitch.title}</DialogTitle>
<DialogContent sx={{ overflow: 'hidden' }}>
<Autocomplete
freeSolo
inputValue={value}
clearOnBlur={false}
onInputChange={(_: any, v: string) => setValue(v)}
noOptionsText={lang.serverSwitch.noServer}
style={{ width: '100%', maxWidth: 500, marginTop: 10 }}
options={JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')}
getOptionLabel={(option: any) => option.address}
renderInput={(props: any) => <TextField
{...props}
error={error}
label={minecraft['addServer.enterIp']}
helperText={error ? lang.serverSwitch.wrongHostname : undefined}
/>}
/>
<DialogContentText>{lang.serverSwitch.content}</DialogContentText>
</DialogContent>
<DialogActions>
<Button
color='primary'
disabled={error}
onClick={() => (location.search = '?' + encodeURIComponent(value))}
>{lang.serverSwitch.connect}</Button>
</DialogActions>
</Dialog>
}
Example #2
Source File: PluginBlock.tsx From Cromwell with MIT License | 5 votes |
export function PluginBlockSidebar(props: TBlockMenuProps) {
const data = props.block?.getData();
// const pluginInfo = props.plugins?.find(p => p.name === data?.plugin?.pluginName);
const forceUpdate = useForceUpdate();
const pluginBlocks = getPluginBlocks();
const pluginProps: any = props.block.getContentInstance().props;
const currentId = getPluginBlockId(
data?.plugin?.pluginName ?? pluginProps.pluginName ?? '',
data?.plugin?.blockName ?? pluginProps.blockName ?? '',
);
const currentBlock = pluginBlocks.find(b => getPluginBlockId(b.pluginName, b.blockName) === currentId);
useEffect(() => {
onPluginBlockRegister(() => {
forceUpdate();
});
}, []);
const handleChange = (event: any, newValue: TPluginBlockOptions | null) => {
if (newValue?.pluginName && newValue?.blockName) {
const data = props.block?.getData();
if (!data.plugin) data.plugin = {};
data.plugin.pluginName = newValue.pluginName;
data.plugin.blockName = newValue.blockName;
props.modifyData?.(data);
forceUpdate();
}
}
const handleChangeInstanceSettings = (settings: any) => {
if (!data.plugin) data.plugin = {};
data.plugin.instanceSettings = settings;
props?.modifyData?.(data);
forceUpdate?.();
}
return (
<div>
<div className={styles.settingsHeader}>
<PluginIcon className={styles.customIcon} />
{props.isGlobalElem(props.getBlockElementById(data?.id)) && (
<div className={styles.headerIcon}>
<Tooltip title="Global block">
<PublicIcon />
</Tooltip>
</div>
)}
<h3 className={styles.settingsTitle}>Plugin settings</h3>
</div>
<Autocomplete
onChange={handleChange}
options={pluginBlocks ?? []}
value={(pluginBlocks ?? []).find(p => getPluginBlockId(p.pluginName, p.blockName) === currentId)}
getOptionLabel={(option) => option.blockName}
renderInput={(params) => <TextField {...params}
placeholder="Plugin"
/>}
/>
{currentBlock?.component && (
<currentBlock.component
{...{
...props,
forceUpdate,
instanceSettings: data?.plugin?.instanceSettings ?? {},
changeInstanceSettings: handleChangeInstanceSettings,
}}
/>
)}
<StylesEditor
forceUpdate={forceUpdate}
blockProps={props}
/>
</div>
);
}
Example #3
Source File: NativeSelect.tsx From GTAV-NativeDB with MIT License | 5 votes |
function NativeSelect({ value, onChange, sx }: NativeSelectProps) {
const natives = useNatives()
const nativesArray = useMemo(() => Object.values(natives), [natives])
return (
<Autocomplete
options={nativesArray}
value={natives[value]}
onChange={(e, native) => native && onChange(native.hash)}
renderInput={(params) => (
<TextField
{...params}
label="Preview Native"
/>
)}
renderOption={(props, option) => (
<li {...props}>
<Typography noWrap>{option.name}</Typography>
</li>
)}
ListboxComponent={
ListboxComponent as React.ComponentType<React.HTMLAttributes<HTMLElement>>
}
getOptionLabel={(native) => native.name}
sx={sx}
disableClearable
disableListWrap
fullWidth
/>
)
}
Example #4
Source File: ArtifactAutocomplete.tsx From genshin-optimizer with MIT License | 5 votes |
function ArtifactSingleAutocomplete<T extends ArtifactSingleAutocompleteKey>({ allArtifactKeys, selectedArtifactKey, setArtifactKey, getName, getImage, label, disable= () => false, showDefault = false, defaultText = "", defaultIcon = "", flattenCorners = false, ...props }:
ArtifactSingleAutocompleteProps<T>) {
const theme = useTheme();
const options = useMemo(() =>
(showDefault
? [{ key: "" as T, label: defaultText }]
: []
).concat(allArtifactKeys.map(key => (
{ key: key, label: getName(key) }
))), [allArtifactKeys, getName, defaultText, showDefault])
return <Autocomplete
autoHighlight
options={options}
value={{ key: selectedArtifactKey, label: getName(selectedArtifactKey) }}
onChange={(_, newValue) => setArtifactKey(newValue ? newValue.key : "")}
getOptionLabel={(option) => option.label ? option.label : defaultText}
isOptionEqualToValue={(option, value) => option.key === value.key}
getOptionDisabled={option => option.key ? disable(option.key) : false}
renderInput={(props) => <SolidColoredTextField
{...props}
label={label}
startAdornment={getImage(selectedArtifactKey)}
hasValue={selectedArtifactKey ? true : false}
flattenCorners={flattenCorners}
/>}
renderOption={(props, option) => (
<MenuItemWithImage
key={option.key}
value={option.key}
image={getImage(option.key)}
text={option.label}
theme={theme}
isSelected={selectedArtifactKey === option.key}
props={props}
/>
)}
{...props}
/>
}
Example #5
Source File: GroupMemberSelect.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
export default function GroupMemberSelect({
group,
onChange,
value = null,
disabled = false,
noDisabledStyling = false,
className = null,
...props
}) {
const members = useRecoilValue(groupMembers(group.id));
const memberIDToUsername = useRecoilValue(groupMemberIDsToUsername(group.id));
return (
<Autocomplete
options={members.map((m) => m.user_id)}
getOptionLabel={(option) => memberIDToUsername[option]}
value={value}
disabled={disabled}
openOnFocus
fullWidth
PopperComponent={StyledAutocompletePopper}
className={className}
onChange={(event, newValue) => onChange(newValue)}
renderOption={(props, user_id) => (
<Box component="li" {...props}>
<Typography variant="body2" component="span" sx={{ ml: 1 }}>
{memberIDToUsername[user_id]}
</Typography>
</Box>
)}
renderInput={
noDisabledStyling
? (params) => <DisabledTextField variant="standard" {...params} {...props} />
: (params) => <TextField variant="standard" {...params} {...props} />
}
/>
);
}
Example #6
Source File: AccountSelect.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
export default function AccountSelect({
group,
onChange,
value = null,
exclude = null,
disabled = false,
noDisabledStyling = false,
className = null,
...props
}) {
const accounts = useRecoilValue(accountsSeenByUser(group.id));
const filteredAccounts =
exclude !== null ? accounts.filter((account) => exclude.indexOf(account.id) < 0) : accounts;
return (
<Autocomplete
options={filteredAccounts}
getOptionLabel={(option) => option.name}
value={value}
disabled={disabled}
openOnFocus
fullWidth
PopperComponent={StyledAutocompletePopper}
disableClearable
className={className}
onChange={(event, newValue) => onChange(newValue)}
renderOption={(props, option) => (
<Box component="li" {...props}>
{option.type === "personal" ? <Person /> : <CompareArrows />}
<Typography variant="body2" component="span" sx={{ ml: 1 }}>
{option.name}
</Typography>
</Box>
)}
renderInput={
noDisabledStyling
? (params) => <DisabledTextField variant="standard" {...params} {...props} />
: (params) => <TextField variant="standard" {...params} {...props} />
}
/>
);
}
Example #7
Source File: Order.tsx From Cromwell with MIT License | 4 votes |
OrderPage = () => {
const { id: orderId } = useParams<{ id: string }>();
const client = getGraphQLClient();
const [data, setData] = useState<TOrder | null>(null);
const [notFound, setNotFound] = useState(false);
const [isCartUpdated, setIsCartUpdated] = useState(false);
const [orderLoading, setOrderLoading] = useState<boolean>(false);
const forceUpdate = useForceUpdate();
const cstoreRef = useRef(getCStore({ local: true }));
const cstore = cstoreRef.current;
const cart = cstore.getCart();
const cartInfo = cstore.getCartTotal();
const updatedCartTotal = cartInfo.total ?? 0;
const updatedOrderTotalPrice = parseFloat((updatedCartTotal +
(data?.shippingPrice ?? 0)).toFixed(2));
// Support old and new address format
const { addressString, addressJson } = parseAddress(data?.customerAddress);
useEffect(() => {
getOrderData();
}, []);
const getOrderData = async () => {
let orderData: TOrder;
setOrderLoading(true);
try {
orderData = await client.getOrderById(parseInt(orderId), gql`
fragment AdminOrderFragment on Order {
...OrderFragment
coupons {
...CouponFragment
}
}
${client.CouponFragment}
${client.OrderFragment}
`, 'AdminOrderFragment');
if (orderData) {
setData(orderData);
updateCart(orderData);
}
} catch (e) {
console.error(e)
}
setOrderLoading(false);
if (!orderData) {
setNotFound(true);
}
}
const updateCart = async (order: TOrder) => {
let oldCart = order.cart;
if (typeof oldCart === 'string') {
try {
oldCart = JSON.parse(oldCart);
} catch (e) { console.error(e); }
}
if (!Array.isArray(oldCart) || oldCart.length === 0) {
oldCart = [];
}
oldCart.forEach(product => cstore.addToCart(product));
if (order?.coupons?.length) {
cstore.setCoupons(order.coupons);
}
const cart = cstore.getCart();
return cart;
}
const handleDeleteFromCart = (item: TStoreListItem) => {
cstore.removeFromCart(item);
setIsCartUpdated(true);
forceUpdate();
}
const handleSave = async () => {
if (data) {
const inputData: TOrderInput = {
status: data.status,
cart: JSON.stringify(cart),
orderTotalPrice: isCartUpdated ? updatedOrderTotalPrice : data.orderTotalPrice,
cartTotalPrice: isCartUpdated ? updatedCartTotal : data.cartTotalPrice,
cartOldTotalPrice: data.cartOldTotalPrice,
shippingPrice: data.shippingPrice,
totalQnt: data.totalQnt,
userId: data.userId,
customerName: data.customerName,
customerPhone: data.customerPhone,
customerEmail: data.customerEmail,
customerAddress: data.customerAddress,
customerComment: data.customerComment,
shippingMethod: data.shippingMethod,
paymentMethod: data.paymentMethod,
currency: data.currency,
couponCodes: cstore.getCoupons()?.map(c => c.code) ?? [],
}
try {
await client?.updateOrder(data.id, inputData);
await getOrderData();
toast.success('Saved!');
} catch (e) {
toast.error('Failed to save');
console.error(e)
}
}
}
const handleInputChange = (prop: keyof TOrder, val: any) => {
if (data) {
setData((prevData) => {
const newData = Object.assign({}, prevData);
(newData[prop] as any) = val;
return newData;
});
}
}
if (notFound) {
return (
<div className={styles.OrderPage}>
<div className={styles.notFoundPage}>
<p className={styles.notFoundText}>Order not found</p>
</div>
</div>
)
}
return (
<div className={styles.OrderPage}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<IconButton
onClick={() => window.history.back()}
>
<ArrowBackIcon style={{ fontSize: '18px' }} />
</IconButton>
<p className={commonStyles.pageTitle}>order</p>
</div>
<div className={styles.headerActions}>
<Button variant="contained" color="primary"
className={styles.saveBtn}
size="small"
onClick={handleSave}>Update</Button>
</div>
</div>
<div className={styles.content}>
<div className={styles.fields}>
{orderLoading && (
Array(8).fill(1).map((it, index) => (
<Skeleton style={{ marginBottom: '10px' }} key={index} height={"50px"} />
))
)}
{!orderLoading && (
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<Autocomplete
value={data?.status ?? orderStatuses[0]}
onChange={(event: any, newValue: string | null) => {
handleInputChange('status', newValue);
}}
classes={{ paper: styles.popper }}
options={orderStatuses}
getOptionLabel={(option) => option}
className={styles.textField}
fullWidth
renderInput={(params) => <TextField {...params}
label="Status"
variant="standard"
fullWidth
/>}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Name"
value={data?.customerName || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => { handleInputChange('customerName', e.target.value) }}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Phone"
value={data?.customerPhone || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => { handleInputChange('customerPhone', e.target.value) }}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Email"
value={data?.customerEmail || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => { handleInputChange('customerEmail', e.target.value) }}
/>
</Grid>
{!addressJson && (
<Grid item xs={12} sm={12}>
<TextField label="Address"
value={addressString || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => { handleInputChange('customerAddress', e.target.value) }}
/>
</Grid>
)}
{addressJson && (
Object.entries<any>(addressJson).map(([fieldKey, value]) => {
return (
<Grid item xs={12} sm={6} key={fieldKey}>
<TextField label={fieldKey}
value={value || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => {
const newVal = e.target.value;
handleInputChange('customerAddress', JSON.stringify({
...addressJson,
[fieldKey]: newVal,
}))
}}
/>
</Grid>
)
}))}
<Grid item xs={12} sm={12}>
<TextField label="Comment"
value={data?.customerComment || ''}
fullWidth
variant="standard"
className={styles.textField}
onChange={(e) => { handleInputChange('customerComment', e.target.value) }}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Payment method"
disabled
value={data?.paymentMethod}
fullWidth
variant="standard"
className={styles.textField}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Shipping method"
disabled
value={data?.shippingMethod}
fullWidth
variant="standard"
className={styles.textField}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Shipping price"
value={data?.shippingPrice ?? 0}
className={styles.textField}
variant="standard"
onChange={(e) => {
let newPrice = Number(e.target.value);
if (!e.target.value) newPrice = 0;
if (!isNaN(newPrice)) handleInputChange('shippingPrice',
newPrice);
setIsCartUpdated(true);
}}
fullWidth
InputProps={{
inputComponent: NumberFormatCustom as any,
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Created"
value={toLocaleDateTimeString(data?.createDate)}
fullWidth
variant="standard"
className={styles.textField}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Last updated"
value={toLocaleDateTimeString(data?.updateDate)}
fullWidth
variant="standard"
className={styles.textField}
/>
</Grid>
</Grid>
)}
</div>
{!!cstore.getCoupons()?.length && (
<div className={styles.fields}>
<p>Applied coupons</p>
{cstore.getCoupons().map(coupon => (
<Box key={coupon.id}
sx={{ display: 'flex', alignItems: 'center', mt: 2, }}
>
<Link to={`${couponPageInfo.baseRoute}/${coupon.id}`} >
<Box sx={{ mr: 2, border: '1px dashed #222', py: '5px', px: '10px', borderRadius: '6px', cursor: 'pointer' }}
>{coupon.code}</Box>
</Link>
{coupon.discountType === 'fixed' && (
<Box>{cstore.getPriceWithCurrency(coupon.value)}</Box>
)}
{coupon.discountType === 'percentage' && (
<Box>{coupon.value}%</Box>
)}
<IconButton onClick={() => {
cstore.setCoupons(cstore.getCoupons()
.filter(c => c.id !== coupon.id));
setIsCartUpdated(true);
forceUpdate();
}}
sx={{ ml: 2 }}
>
<CloseIcon />
</IconButton>
</Box>
))}
</div>
)}
<div className={styles.sums}>
{!orderLoading && data && (
<>
<p>Cart total: <b>{cstore.getPriceWithCurrency(isCartUpdated ?
updatedCartTotal : data.cartTotalPrice)}</b></p>
<p>Shipping: <b>{cstore.getPriceWithCurrency(data.shippingPrice ?? 0)}</b></p>
<p>Order total: <b>{cstore.getPriceWithCurrency(isCartUpdated ?
updatedOrderTotalPrice : data.orderTotalPrice)}</b></p>
</>
)}
{orderLoading && (
Array(4).fill(1).map((it, index) => (
<Skeleton style={{ marginBottom: '3px' }} key={index} height={"20px"} />
))
)}
</div>
<div className={styles.cart}>
{!orderLoading && cart.map((it, i) => {
const product = it.product;
const checkedAttrKeys = Object.keys(it.pickedAttributes || {});
if (product) {
const productLink = `${productPageInfo.baseRoute}/${product.id}`;
return (
<Grid key={i} className={styles.cartItem} container>
<Grid item xs={2} className={styles.itemBlock}>
<Link to={productLink}>
<img src={product.mainImage} className={styles.mainImage} />
</Link>
</Grid>
<Grid item xs={4} className={styles.itemBlock}>
<Link to={productLink} className={styles.productName}>{product.name}</Link>
<div className={styles.priceBlock}>
{(product?.oldPrice !== undefined && product?.oldPrice !== null) && (
<p className={styles.oldPrice}>{cstore.getPriceWithCurrency(product.oldPrice)}</p>
)}
<p className={styles.price}>{cstore.getPriceWithCurrency(product?.price)}</p>
</div>
</Grid>
<Grid item xs={3} className={styles.itemBlock}>
{checkedAttrKeys.map(key => {
const vals = it.pickedAttributes ? it.pickedAttributes[key] : [];
const valsStr = vals.join(', ');
return <p key={key}>{key}: {valsStr}</p>
})}
</Grid>
<Grid item xs={2} className={styles.itemBlock}>
<p>Qty: {it.amount}</p>
</Grid>
<Grid item xs={1} className={styles.itemBlock} style={{ marginLeft: 'auto', paddingRight: '0px' }}>
<div>
<IconButton
aria-label="Delete"
onClick={() => { handleDeleteFromCart(it); }}
>
<DeleteForeverIcon />
</IconButton>
</div>
</Grid>
</Grid>
)
}
})}
{orderLoading && (
Array(2).fill(1).map((it, index) => (
<Skeleton style={{ margin: '0 20px 5px 20px' }} key={index} height={"60px"} />
))
)}
</div>
</div>
</div >
)
}
Example #8
Source File: Terminal.tsx From NekoMaid with MIT License | 4 votes |
Terminal: React.FC = () => {
const logs = useMemo<JSX.Element[]>(() => [], [])
const ref = useRef<HTMLDivElement | null>(null)
const plugin = usePlugin()
const [, update] = useState(0)
const [open, setOpen] = useState(false)
const [command, setCommand] = useState('')
const [suggestions, setSuggestions] = useState<Array<[string, boolean] | [string]>>([])
const getSuggestions = useMemo(() => throttle(
(it: string) => {
let cmd = it.substr(0, it.lastIndexOf(' '))
if (cmd) cmd += ' '
return plugin.emit('console:complete', (data: string[] = []) => {
setSuggestions(JSON.parse(localStorage.getItem(`NekoMaid:${address}:commandHistory`) || '[]').concat(data.map(c => [cmd + c] as [string])))
}, it)
},
500
), [])
const scrollToEnd = useMemo(() => throttle(
(elm: HTMLDivElement) => {
const select = (window.getSelection || document.getSelection)()
if (select && !select.isCollapsed) {
let node = select?.anchorNode
while (node && node !== document) {
if (node === elm) return
node = node.parentNode
}
}
if (elm) elm.lastElementChild?.scrollIntoView()
},
300
), [])
const execCommand = () => {
if (!command) return
plugin.emit('console:run', action, command)
const arr = JSON.parse(localStorage.getItem(`NekoMaid:${address}:commandHistory`) || '[]').filter((it: [string]) => it[0] !== command)
if (arr.length === 5) arr.pop()
arr.unshift([command, true])
localStorage.setItem(`NekoMaid:${address}:commandHistory`, JSON.stringify(arr))
setCommand('')
}
useEffect(() => {
const runCommand = (it: string) => plugin.emit('console:run', action, it)
let lastLog: Log = {} as any
const onLog = (data: Log) => {
if (lastLog.logger === data.logger && (lastLog.time / 100 | 0) === (data.time / 100 | 0) &&
lastLog.level === data.level && (!lastLog.components === !data.components)) {
logs.pop()
if (data.components) {
lastLog.components!.push(null as any)
lastLog.components = lastLog.components!.concat(data.components)
} else lastLog.msg += '\n' + data.msg
data = lastLog
} else lastLog = data
logs.push(parseLog(data, runCommand, setCommand))
update(++i)
}
const offLogs = plugin.on('console:logs', (it: Log[]) => {
logs.length = 0
it.forEach(onLog)
})
const offLog = plugin.on('console:log', onLog)
plugin.switchPage('console')
return () => {
offLogs()
offLog()
}
}, [])
useEffect(() => { ref.current && scrollToEnd(ref.current) }, [logs[logs.length - 1]])
return <Box sx={{
width: '100%',
height: '100vh',
overflow: 'hidden',
display: 'flex',
fontSize: '0.91rem',
flexDirection: 'column',
fontFamily: '"Roboto Mono","Helvetica","Arial",sans-serif',
'& p': {
margin: 0,
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
display: 'flex',
'& .msg': {
flex: '1'
},
'& .logger': {
color: theme => theme.palette.secondary.main,
fontStyle: 'italic'
},
'& .level': {
userSelect: 'none',
height: 'fit-content',
fontWeight: 'bolder',
cursor: 'pointer',
color: theme => theme.palette.primary.main
},
'& .white': {
textShadow: theme => theme.palette.mode === 'light' ? '#000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0' : undefined
},
'& .black': {
textShadow: theme => theme.palette.mode === 'dark' ? '#fff 1px 0 0, #fff 0 1px 0, #fff -1px 0 0, #fff 0 -1px 0' : undefined
},
'& .more': {
color: theme => theme.palette.secondary.main,
marginRight: '4px',
cursor: 'pointer',
textDecoration: 'underline'
}
},
'& .warn, & .warn .level': {
color: theme => theme.palette.warning.main
},
'& .error, & .error .level': {
color: theme => theme.palette.error.main
}
}}>
<Toolbar />
<Box
ref={ref}
sx={{
height: '100%',
overflow: 'hidden scroll',
backgroundColor: theme => theme.palette.background.default,
padding: theme => theme.spacing(1)
}}>
{logs}
</Box>
<Paper sx={{
display: 'flex',
borderRadius: '4px 4px 0 0',
padding: theme => theme.spacing(1),
zIndex: 2
}}>
<Autocomplete
freeSolo
open={open}
inputValue={command}
options={suggestions}
onOpen={() => setOpen(true)}
onClose={() => setOpen(false)}
onFocus={() => getSuggestions(command)}
onKeyUp={(e: any) => e.key === 'Enter' && (!open || !suggestions.length) && execCommand()}
sx={{ flex: '1' }}
classes={{ popper: 'command-popper' }}
renderInput={params => <TextField {...params as any} label={lang.terminal.command} />}
getOptionLabel={it => typeof it === 'string' ? it : it[0]}
groupBy={it => it[1] ? lang.history : lang.terminal.command}
onInputChange={(_, it) => {
getSuggestions(it)
setCommand(it)
}}
/>
<IconButton
color='primary'
disabled={!command}
onClick={execCommand}
sx={{ margin: theme => theme.spacing('auto', 0, 'auto', 1) }}
><Send /></IconButton>
</Paper>
</Box>
}
Example #9
Source File: BlockEditor.tsx From NekoMaid with MIT License | 4 votes |
BlockEditor: React.FC = () => {
const theme = useTheme()
const plugin = usePlugin()
const his = useHistory()
const loc = useLocation()
const globalData = useGlobalData()
const drawerWidth = useDrawerWidth()
const [block, setBlock] = useState<Block>()
const [types, setTypes] = useState<string[]>([])
const [worlds, setWorlds] = useState<string[]>([])
const params = { world: '', x: 0, y: 0, z: 0 }
if (loc.pathname.startsWith('/NekoMaid/block/')) {
const arr = loc.pathname.split('/')
if (arr.length > 6) {
params.world = arr[3]
params.x = +arr[4]
params.y = +arr[5]
params.z = +arr[6]
} else his.push('/NekoMaid/block')
}
useEffect(() => {
const off = plugin.emit('item:blocks', (types: string[], worlds: string[]) => {
setTypes(types)
setWorlds(worlds)
})
.on('block:select', (world, x, y, z) => his.push(`/NekoMaid/block/${world}/${x}/${y}/${z}`))
return () => void off()
}, [])
const update = () => {
if (params.world) {
plugin.emit('block:fetch', (block: Block) => {
if (!block) {
failed()
his.push('/NekoMaid/block')
return
}
if (globalData.hasNBTAPI && block.nbt) block.nbt = stringify(parse(block.nbt), { pretty: true })
setBlock(block)
}, params.world, params.x, params.y, params.z)
}
}
const updateWithAction = (res: boolean) => {
action(res)
update()
}
useEffect(update, [params.world, params.x, params.y, params.z])
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
<Grid item lg={6} md={12} xl={6} xs={12}>
<Card sx={{ '& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' } }}>
<CardHeader
title={lang.blockEditor.title}
sx={{ position: 'relative' }}
action={<Box sx={cardActionStyles}>
<IconButton
size='small'
disabled={!block || (!block.data && !block.nbt)}
onClick={() => block && plugin.emit('block:save', (res: boolean) => {
action(res)
update()
}, params.world, params.x, params.y, params.z, block.nbt || null, block.data || null)}
><Save /></IconButton>
<IconButton
size='small'
disabled={!block}
onClick={() => {
update()
success()
}}
><Refresh /></IconButton>
</Box>}
/>
<Divider />
{block
? <>
<CardContent sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
<ItemViewer item={{ type: block.type }} />
<Autocomplete
options={types}
sx={{ maxWidth: 300, marginLeft: 1, flexGrow: 1 }}
value={block.type}
onChange={(_, it) => it && plugin.emit('block:type', (res: boolean) => {
action(res)
update()
}, params.world, params.x, params.y, params.z, (block.type = it))}
getOptionLabel={it => {
const locatedName = getName(it.toLowerCase())
return (locatedName ? locatedName + ' ' : '') + it
}}
renderInput={(params) => <TextField {...params} label={lang.itemEditor.itemType} size='small' variant='standard' />}
/>
</CardContent>
{block.data != null && <Accordion sx={{ '&::before': { opacity: '1!important' } }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}><Typography>{lang.data}</Typography></AccordionSummary>
<AccordionDetails sx={{ padding: 0, '& .CodeMirror': { width: '100%', height: 350 } }}>
<UnControlled
value={block.data}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, data: string) => (block.data = data)}
/>
</AccordionDetails>
</Accordion>}
{block.nbt != null && <Accordion sx={{ '&::before': { opacity: '1!important', display: '!important' } }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}><Typography>NBT</Typography></AccordionSummary>
<AccordionDetails sx={{ padding: 0, '& .CodeMirror': { width: '100%', height: 350 } }}>
<UnControlled
value={block.nbt}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, data: string) => (block.nbt = data)}
/>
</AccordionDetails>
</Accordion>}
</>
: <CardContent>{worlds.length ? <BlockSelector worlds={worlds} /> : <Empty />}</CardContent>}
</Card>
</Grid>
{block?.inventory?.length
? <Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={minecraft[('container.' + block.inventoryType || '').toLowerCase()] || lang.blockEditor.container}
sx={{ position: 'relative' }}
/>
<Divider />
<CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto', textAlign: 'center' }}>
{block.inventory.map((it, i) => <React.Fragment key={i}><ItemViewer
item={it}
data={{ type: InvType.BLOCK, solt: i, ...params }}
onDrag={() => plugin.emit('block:setItem', update, params.world, params.x, params.y, params.z, i, null, -1)}
onDrop={(item, obj) => plugin.emit('block:setItem', update, params.world, params.x, params.y, params.z, i,
JSON.stringify(item), compare(obj, params) ? obj.solt : -1)}
onEdit={item => item !== false && plugin.emit('block:setItem', updateWithAction, params.world, params.x, params.y,
params.z, i, item && JSON.stringify(item), -1)}
/>{!((i + 1) % 9) && <br />}</React.Fragment>)}
</CardContent>
</Card>
</Grid>
: undefined}
</Grid>
</Container>
</Box>
}
Example #10
Source File: ItemViewer.tsx From NekoMaid with MIT License | 4 votes |
ItemEditor: React.FC = () => {
const plugin = usePlugin()
const theme = useTheme()
const [item, setItem] = useState<Item | undefined>()
const [types, setTypes] = useState<string[]>([])
const [tab, setTab] = useState(0)
const [level, setLevel] = useState(1)
const [enchantment, setEnchantment] = useState<string | undefined>()
const [nbtText, setNBTText] = useState('')
const nbt: NBT = item?.nbt ? parse(item.nbt) : { id: 'minecraft:' + (item?.type || 'air').toLowerCase(), Count: new Byte(1) } as any
useEffect(() => {
if (!item || types.length) return
plugin.emit('item:fetch', (a: string[], b: string[]) => {
setTypes(a)
enchantments = b
})
}, [item])
useEffect(() => {
_setItem = (it: any) => {
setItem(it)
setNBTText(it.nbt ? stringify(parse(it.nbt), { pretty: true }) : '')
}
return () => { _setItem = null }
}, [])
const cancel = () => {
setItem(undefined)
if (_resolve) {
_resolve(false)
_resolve = null
}
}
const update = () => {
const newItem: any = { ...item }
if (nbt) {
newItem.nbt = stringify(nbt as any)
setNBTText(stringify(nbt, { pretty: true }))
}
setItem(newItem)
}
const isAir = item?.type === 'AIR'
const name = nbt?.tag?.display?.Name
const enchantmentMap: Record<string, true> = { }
return <Dialog open={!!item} onClose={cancel}>
<DialogTitle>{lang.itemEditor.title}</DialogTitle>
<DialogContent sx={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
{item && <Box sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
<ItemViewer item={item} />
<Autocomplete
options={types}
sx={{ maxWidth: 300, marginLeft: 1, flexGrow: 1 }}
value={item?.type}
onChange={(_, it) => {
item.type = it || 'AIR'
if (nbt) nbt.id = 'minecraft:' + (it ? it.toLowerCase() : 'air')
update()
}}
getOptionLabel={it => {
const locatedName = getName(it.toLowerCase())
return (locatedName ? locatedName + ' ' : '') + it
}}
renderInput={(params) => <TextField {...params} label={lang.itemEditor.itemType} size='small' variant='standard' />}
/>
</Box>}
<Tabs centered value={tab} onChange={(_, it) => setTab(it)} sx={{ marginBottom: 2 }}>
<Tab label={lang.itemEditor.baseAttribute} disabled={isAir} />
<Tab label={minecraft['container.enchant']} disabled={isAir} />
<Tab label='NBT' disabled={isAir} />
</Tabs>
{nbt && tab === 0 && <Grid container spacing={1} rowSpacing={1}>
<Grid item xs={12} md={6}><TextField
fullWidth
label={lang.itemEditor.count}
type='number'
variant='standard'
value={nbt.Count}
disabled={isAir}
onChange={e => {
nbt.Count = new Byte(item!.amount = parseInt(e.target.value))
update()
}}
/></Grid>
<Grid item xs={12} md={6}><TextField
fullWidth
label={lang.itemEditor.damage}
type='number'
variant='standard'
value={nbt.tag?.Damage}
disabled={isAir}
onChange={e => {
set(nbt, 'tag.Damage', parseInt(e.target.value))
update()
}}
/></Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label={lang.itemEditor.displayName}
variant='standard'
disabled={isAir}
value={name ? stringifyTextComponent(JSON.parse(name)) : ''}
onChange={e => {
set(nbt, 'tag.display.Name', JSON.stringify(item!.name = e.target.value))
update()
}}
/>
<FormControlLabel
label={minecraft['item.unbreakable']}
disabled={isAir}
checked={nbt.tag?.Unbreakable?.value === 1}
control={<Checkbox
checked={nbt.tag?.Unbreakable?.value === 1}
onChange={e => {
set(nbt, 'tag.Unbreakable', new Byte(+e.target.checked))
update()
}} />
}
/>
</Grid>
<Grid item xs={12} md={6}><TextField
fullWidth
multiline
label={lang.itemEditor.lore}
variant='standard'
maxRows={5}
disabled={isAir}
value={nbt.tag?.display?.Lore?.map(l => stringifyTextComponent(JSON.parse(l)))?.join('\n') || ''}
onChange={e => {
set(nbt, 'tag.display.Lore', e.target.value.split('\n').map(text => JSON.stringify(text)))
update()
}}
/></Grid>
</Grid>}
{nbt && tab === 1 && <Grid container spacing={1} sx={{ width: '100%' }}>
{nbt.tag?.Enchantments?.map((it, i) => {
enchantmentMap[it.id] = true
return <Grid item key={i}><Chip label={getEnchantmentName(it)} onDelete={() => {
nbt?.tag?.Enchantments?.splice(i, 1)
update()
}} /></Grid>
})}
<Grid item><Chip label={lang.itemEditor.newEnchantment} color='primary' onClick={() => {
setEnchantment('')
setLevel(1)
}} /></Grid>
<Dialog onClose={() => setEnchantment(undefined)} open={enchantment != null}>
<DialogTitle>{lang.itemEditor.newEnchantmentTitle}</DialogTitle>
<DialogContent>
<Box component='form' sx={{ display: 'flex', flexWrap: 'wrap' }}>
<FormControl variant='standard' sx={{ m: 1, minWidth: 120 }}>
<InputLabel htmlFor='item-editor-enchantment-selector'>{minecraft['container.enchant']}</InputLabel>
<Select
id='item-editor-enchantment-selector'
label={minecraft['container.enchant']}
value={enchantment || ''}
onChange={e => setEnchantment(e.target.value)}
>{enchantments
.filter(e => !(e in enchantmentMap))
.map(it => <MenuItem key={it} value={it}>{getEnchantmentName(it)}</MenuItem>)}
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<TextField
label={lang.itemEditor.level}
type='number'
variant='standard'
value={level}
onChange={e => setLevel(parseInt(e.target.value))}
/>
</FormControl>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={() => setEnchantment(undefined)}>{minecraft['gui.cancel']}</Button>
<Button disabled={!enchantment || isNaN(level)} onClick={() => {
if (nbt) {
if (!nbt.tag) nbt.tag = { Damage: new Int(0) }
;(nbt.tag.Enchantments || (nbt.tag.Enchantments = [])).push({ id: enchantment!, lvl: new Short(level) })
}
setEnchantment(undefined)
update()
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
</Grid>}
</DialogContent>
{nbt && tab === 2 && <Box sx={{
'& .CodeMirror': { width: '100%' },
'& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
}}>
<UnControlled
value={nbtText}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, nbt: string) => {
const n = parse(nbt) as any as NBT
const newItem: any = { ...item, nbt }
if (n.Count?.value != null) newItem.amount = n.Count.value
setItem(newItem)
}}
/>
</Box>}
<DialogActions>
<Button onClick={cancel}>{minecraft['gui.cancel']}</Button>
<Button onClick={() => {
setItem(undefined)
if (_resolve) {
_resolve(!item || item.type === 'AIR' ? null : item)
_resolve = null
}
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
}
Example #11
Source File: client.tsx From mui-toolpad with MIT License | 4 votes |
function QueryEditor({
appId,
connectionId,
value,
onChange,
}: QueryEditorProps<GoogleSheetsApiQuery>) {
const [spreadsheetQuery, setSpreadsheetQuery] = React.useState<string | null>(null);
const debouncedSpreadsheetQuery = useDebounced(spreadsheetQuery, 300);
const fetchedFiles: UseQueryResult<GoogleDriveFiles> = client.useQuery('dataSourceFetchPrivate', [
appId,
connectionId,
{
type: GoogleSheetsPrivateQueryType.FILES_LIST,
spreadsheetQuery: debouncedSpreadsheetQuery,
},
]);
const fetchedFile: UseQueryResult<GoogleDriveFile> = client.useQuery(
'dataSourceFetchPrivate',
value.spreadsheetId
? [
appId,
connectionId,
{
type: GoogleSheetsPrivateQueryType.FILE_GET,
spreadsheetId: value.spreadsheetId,
},
]
: null,
);
const fetchedSpreadsheet: UseQueryResult<GoogleSpreadsheet> = client.useQuery(
'dataSourceFetchPrivate',
value.spreadsheetId
? [
appId,
connectionId,
{
type: GoogleSheetsPrivateQueryType.FETCH_SPREADSHEET,
spreadsheetId: value.spreadsheetId,
},
]
: null,
);
const selectedSheet = React.useMemo(
() =>
fetchedSpreadsheet.data?.sheets?.find(
(sheet) => sheet.properties?.title === value.sheetName,
) ?? null,
[fetchedSpreadsheet, value],
);
const handleSpreadsheetChange = React.useCallback(
(event, newValue: GoogleDriveFile | null) => {
onChange({
...value,
sheetName: null,
spreadsheetId: newValue?.id ?? null,
});
},
[onChange, value],
);
const handleSheetChange = React.useCallback(
(event, newValue: GoogleSheet | null) => {
onChange({
...value,
sheetName: newValue?.properties?.title ?? null,
});
},
[onChange, value],
);
const handleRangeChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
onChange({
...value,
ranges: event.target.value,
});
},
[onChange, value],
);
const handleTransformChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
onChange({
...value,
headerRow: event.target.checked,
});
},
[onChange, value],
);
const handleSpreadsheetInput = React.useCallback(
(event: React.SyntheticEvent, input: string, reason: string) => {
if (reason === 'input') {
setSpreadsheetQuery(input);
}
},
[],
);
return (
<Stack direction="column" gap={2}>
<Autocomplete
fullWidth
value={fetchedFile.data ?? null}
loading={fetchedFiles.isLoading}
loadingText={'Loading...'}
options={fetchedFiles.data?.files ?? []}
getOptionLabel={(option: GoogleDriveFile) => option.name ?? ''}
onInputChange={handleSpreadsheetInput}
onChange={handleSpreadsheetChange}
isOptionEqualToValue={(option: GoogleDriveFile, val: GoogleDriveFile) =>
option.id === val.id
}
renderInput={(params) => <TextField {...params} label="Select spreadsheet" />}
renderOption={(props, option) => {
return (
<li {...props} key={option.id}>
{option.name}
</li>
);
}}
/>
<Autocomplete
fullWidth
loading={fetchedSpreadsheet.isLoading}
value={selectedSheet}
loadingText={'Loading...'}
options={fetchedSpreadsheet.data?.sheets ?? []}
getOptionLabel={(option: GoogleSheet) => option.properties?.title ?? ''}
onChange={handleSheetChange}
renderInput={(params) => <TextField {...params} label="Select sheet" />}
renderOption={(props, option) => {
return (
<li {...props} key={option?.properties?.sheetId}>
{option?.properties?.title}
</li>
);
}}
/>
<TextField
label="Range"
helperText={`In the form of A1:Z999`}
value={value.ranges}
disabled={!value.sheetName}
onChange={handleRangeChange}
/>
<FormControlLabel
label="First row contains column headers"
control={
<Checkbox
checked={value.headerRow}
onChange={handleTransformChange}
inputProps={{ 'aria-label': 'controlled' }}
/>
}
/>
</Stack>
);
}
Example #12
Source File: RegionSelect.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
RegionSelect = ({
type,
onChange,
inputProps,
}: {
type: "minio" | "s3" | "gcs" | "azure" | "unsupported";
onChange: (obj: any) => void;
inputProps?: any;
}) => {
const regionList = getRegions(type);
const [value, setValue] = React.useState("");
return (
<Autocomplete
sx={{
"& .MuiOutlinedInput-root": {
padding: 0,
paddingLeft: "10px",
fontSize: 13,
fontWeight: 600,
},
"& .MuiAutocomplete-inputRoot": {
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "#e5e5e5",
borderWidth: 1,
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: "#07193E",
borderWidth: 1,
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "#07193E",
borderWidth: 1,
},
},
}}
freeSolo
selectOnFocus
handleHomeEndKeys
onChange={(event, newValue) => {
let newVal: any = newValue;
if (typeof newValue === "string") {
newVal = {
label: newValue,
};
} else if (newValue && newValue.inputValue) {
// Create a new value from the user input
newVal = {
label: newValue.inputValue,
};
} else {
newVal = newValue;
}
setValue(newVal);
onChange(newVal?.value);
}}
value={value}
onInputChange={(e: any) => {
const { target: { value = "" } = {} } = e || {};
onChange(value);
}}
getOptionLabel={(option) => {
// Value selected with enter, right from the input
if (typeof option === "string") {
return option;
}
// Add "xxx" option created dynamically
if (option.inputValue) {
return option.inputValue;
}
// Regular option
return option.value;
}}
options={regionList}
filterOptions={(opts: any[], state: any) => {
const filterText = state.inputValue.toLowerCase();
return opts.filter((opt) =>
`${opt.label.toLowerCase()}${opt.value.toLowerCase()}`.includes(
filterText
)
);
}}
renderOption={(props: any, opt: any) => {
return (
<li {...props}>
<Box
sx={{
display: "flex",
flexFlow: "column",
alignItems: "baseline",
padding: "4px",
borderBottom: "1px solid #eaeaea",
cursor: "pointer",
width: "100%",
"& .label": {
fontSize: "13px",
fontWeight: 500,
},
"& .value": {
fontSize: "11px",
fontWeight: 400,
},
}}
>
<span className="label">{opt.value}</span>
<span className="value">{opt.label}</span>
</Box>
</li>
);
}}
renderInput={(params) => (
<TextField {...params} {...inputProps} fullWidth />
)}
/>
);
}
Example #13
Source File: CharacterAutocomplete.tsx From genshin-optimizer with MIT License | 4 votes |
export default function CharacterAutocomplete({ value, onChange, defaultText = "", defaultIcon = "", placeholderText = "", labelText = "", showDefault = false, showInventory = false, showEquipped = false, filter = () => true, disable = () => false, ...props }: CharacterAutocompleteProps) {
// TODO: #412 We shouldn't be loading all the character translation files. Should have a separate lookup file for character name.
const { t } = useTranslation(["ui", "artifact", ...allCharacterKeys.map(k => `char_${k}_gen`)])
const theme = useTheme()
const { database } = useContext(DatabaseContext)
const characterSheets = usePromise(CharacterSheet.getAll, [])
const filterConfigs = useMemo(() => characterSheets && characterFilterConfigs(database, characterSheets), [database, characterSheets])
const characterKeys = database._getCharKeys().filter(ck => characterSheets?.[ck] && filter(characterSheets[ck], ck)).sort()
const textForValue = useCallback((value: CharacterAutocompleteValue): string => {
switch (value) {
case "Equipped":
return t("artifact:filterLocation.currentlyEquipped")
case "Inventory":
return t("artifact:filterLocation.inventory")
case "":
return defaultText
default:
return t(`char_${value}_gen:name`)
}
}, [defaultText, t])
const imageForValue = useCallback((value: CharacterAutocompleteValue): Displayable => {
switch (value) {
case "Equipped":
return <FontAwesomeIcon icon={faUserShield} />
case "Inventory":
return <BusinessCenter />
case "":
return defaultIcon
default:
return <ThumbSide src={characterSheets![value]?.thumbImgSide} sx={{ pr: 1 }} />
}
}, [defaultIcon, characterSheets])
const characterOptions = useMemo(() => filterConfigs && charOptions(characterKeys, filterConfigs, textForValue, showDefault, showInventory, showEquipped),
[filterConfigs, characterKeys, showDefault, showInventory, showEquipped, textForValue])
if (!characterSheets || !characterOptions) return null
return <Autocomplete
autoHighlight
options={characterOptions}
getOptionLabel={(option) => option.label}
onChange={(_, newValue) => onChange(newValue ? newValue.value : "")}
isOptionEqualToValue={(option, value) => option.value === value.value}
getOptionDisabled={option => option.value ? disable(option.value) : false}
value={{ value, label: textForValue(value) }}
renderInput={(props) => <SolidColoredTextField
{...props}
label={labelText}
placeholder={placeholderText}
startAdornment={imageForValue(value)}
hasValue={value ? true : false}
/>}
renderOption={(props, option) => {
const favorite = option.value !== "Equipped" && option.value !== "Inventory"
&& option.value !== "" && database._getChar(option.value)?.favorite
return <MenuItemWithImage
key={option.value ? option.value : "default"}
value={option.value ? option.value : "default"}
image={imageForValue(option.value)}
text={
<Suspense fallback={<Skeleton variant="text" width={100} />}>
<Typography variant="inherit" noWrap>
{textForValue(option.value)}
</Typography>
</Suspense>
}
theme={theme}
isSelected={value === option.value}
addlElement={<>
{favorite && <Box display="flex" flexGrow={1} />}
{favorite && <Favorite sx={{ ml: 1, mr: -0.5 }} />}
</>}
props={props}
/>
}}
{...props}
/>
}
Example #14
Source File: SettingsModal.tsx From rewind with MIT License | 4 votes |
function SkinsSettings() {
// TODO: Button for synchronizing skin list again
const theater = useCommonManagers();
const { preferredSkinId } = useObservable(() => theater.skinSettingsStore.settings$, DEFAULT_SKIN_SETTINGS);
const chosenSkinId = stringToSkinId(preferredSkinId);
const skins = useObservable(() => theater.skinManager.skinList$, []);
const skinOptions: SkinId[] = useMemo(
() => [DEFAULT_OSU_SKIN_ID, DEFAULT_REWIND_SKIN_ID].concat(skins.map((name) => ({ source: "osu", name }))),
[skins],
);
useEffect(() => {
theater.skinManager.loadSkinList();
}, [theater]);
// TODO:
const handleSkinChange = useCallback(
(skinId: SkinId) => {
(async function () {
try {
await theater.skinManager.loadSkin(skinId);
} catch (e) {
// Show some error dialog
console.error(`Could not load skin ${skinId}`);
}
})();
// TODO: Error handling
},
[theater],
);
return (
<Box sx={{ p: 2 }}>
<Autocomplete
id="skin-selection-demo"
// TODO: Make sure skinIds are sorted
options={skinOptions}
groupBy={(option: SkinId) => sourceName[option.source]}
value={chosenSkinId}
onChange={(event, newValue) => {
if (newValue) {
handleSkinChange(newValue as SkinId);
}
}}
getOptionLabel={(option: SkinId) => option.name}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Skin" />}
isOptionEqualToValue={(option, value) => option.name === value.name && option.source === value.source}
/>
</Box>
);
}
Example #15
Source File: index.tsx From ExpressLRS-Configurator with GNU General Public License v3.0 | 4 votes |
Omnibox: FunctionComponent<OmniboxProps> = ({
options,
currentValue,
onChange,
title,
disabled = false,
loading = false,
groupBy,
}) => {
const onInputChange = (_event: any, opt: Option | null) => {
if (opt && opt.value) {
onChange(opt.value);
} else {
onChange(null);
}
};
const filterOptions = (
values: Option[],
{ inputValue }: FilterOptionsState<Option>
): OptionWithMatches[] => {
if (inputValue) {
const searchResults = new QuickScore(values, ['label']).search(
inputValue
);
return searchResults.map(
(result: { item: Option; matches: { label: number[][] } }) => {
return {
...result.item,
matches: result.matches.label,
};
}
);
}
// if no inputValue, then maintain original item order
return values.map((item) => {
return {
...item,
matches: [[0, 0]],
};
});
};
return (
<Autocomplete
id={`omnibox-${title}`}
options={options}
disablePortal
fullWidth
loading={loading}
disabled={disabled}
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, otherOption) =>
option.value === otherOption.value
}
openOnFocus
clearIcon={false}
renderInput={(params) => (
<TextField {...params} label={title} margin="none" />
)}
filterOptions={filterOptions}
groupBy={groupBy}
value={currentValue}
onChange={onInputChange}
renderOption={(props, option) => {
const opt: OptionWithMatches = option as OptionWithMatches;
const parts = parse(option.label, opt.matches);
return (
<li {...props}>
<div>
{parts.map(
(
part: { highlight: any; text: React.ReactNode },
index: string | number | null | undefined
) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 800 : 400,
}}
>
{part.text}
</span>
)
)}
</div>
</li>
);
}}
/>
);
}
Example #16
Source File: latest-frontend-dependencies.tsx From Cromwell with MIT License | 4 votes |
export default function FrontendDependencies() {
const [expanded, setExpanded] = React.useState(false);
const [versionsLoading, setVersionsLoading] = React.useState(false);
const [cmsVersions, setCmsVersions] = React.useState<string[]>([]);
const [pickedVersion, setPickedVersion] = React.useState<string | undefined>();
const [dependencies, setDependencies] = useState<{
name: string;
version: string;
}[]>([]);
const handleExpandClick = () => {
setExpanded(!expanded);
};
useEffect(() => {
(async () => {
setVersionsLoading(true);
const versions = (await apiClient.getFrontendDependenciesBindings())
.filter(compareVersions.validate)
.sort(compareVersions).reverse();
setCmsVersions(versions);
const latest = versions[0];
setPickedVersion(latest);
await getDependencies(latest);
})();
}, []);
const getDependencies = async (version: string) => {
setVersionsLoading(true);
const deps = await apiClient.getFrontendDependenciesList(version);
setDependencies(Object.keys(deps?.latestVersions ?? {}).map(pckg => ({
name: pckg,
version: (deps?.latestVersions ?? {})[pckg],
})));
setVersionsLoading(false);
}
const getDepName = (option) => `${option.name}: "${option.version}"`;
const changeCmsVersion = async (version: string) => {
setPickedVersion(version);
setDependencies([]);
await getDependencies(version);
}
return (
<Layout
title='Hello from'
description=""
>
<div className={styles.content}>
<h1 className={styles.title}>Latest Frontend dependencies</h1>
<Link
style={{ marginBottom: '25px' }}
href="/docs/development/frontend-dependencies">Documentation</Link>
<div className={styles.searchBox} >
<Autocomplete
id="cms-versions"
options={cmsVersions ?? ['']}
value={pickedVersion ?? ''}
onChange={(event, value) => changeCmsVersion(value)}
getOptionLabel={ver => ver}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params}
label="Pick CMS version"
variant="outlined"
/>}
/>
</div>
<div className={styles.searchBox}>
{versionsLoading ? (
<CircularProgress />
) : (
<Autocomplete
id="dependencies-versions"
options={dependencies}
getOptionLabel={getDepName}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params}
label="Search dependencies..."
variant="outlined"
/>}
/>
)}
</div>
<div className={styles.listHeader} onClick={handleExpandClick}>
<h3 className={styles.expandTitle}>Expand all</h3>
<IconButton >
<ExpandMoreIcon
style={{ transform: expanded ? 'rotate(180deg)' : '' }}
/>
</IconButton>
</div>
<Collapse in={expanded} timeout="auto" unmountOnExit>
{dependencies.map(dep => {
return (
<div key={dep.name}>{getDepName(dep)}</div>
)
})}
</Collapse>
</div>
</Layout>
)
}
Example #17
Source File: blog.tsx From Cromwell with MIT License | 4 votes |
BlogPage: TPageWithLayout<BlogProps> = (props) => {
const filterInput = useRef<TPostFilter>({});
const listId = 'Blog_list_01';
const publishSort = useRef<"ASC" | "DESC">('DESC');
const forceUpdate = useForceUpdate();
const resetList = () => {
const list = getBlockInstance<TCList>(listId)?.getContentInstance();
list?.updateData();
}
useEffect(() => {
// updateList();
}, []);
const handleChangeTags = (event: any, newValue?: (TTag | undefined | string)[]) => {
filterInput.current.tagIds = newValue?.map(tag => (tag as TTag)?.id);
forceUpdate();
resetList();
}
const handleGetPosts = (params: TPagedParams<TPost>) => {
params.orderBy = 'publishDate';
params.order = publishSort.current;
return handleGetFilteredPosts(params, filterInput.current);
}
const handleChangeSort = (event: SelectChangeEvent<unknown>) => {
if (event.target.value === 'Newest') publishSort.current = 'DESC';
if (event.target.value === 'Oldest') publishSort.current = 'ASC';
resetList();
}
const handleTagClick = (tag?: TTag) => {
if (!tag) return;
if (filterInput.current.tagIds?.length === 1 &&
filterInput.current.tagIds[0] === tag.id) return;
handleChangeTags(null, [tag]);
forceUpdate();
}
return (
<CContainer className={commonStyles.content} id="blog-1">
<CContainer className={styles.filter} id="blog-2">
<Autocomplete
multiple
freeSolo
value={filterInput.current.tagIds?.map(id => props.tags?.find(tag => tag.id === id)) ?? []}
className={styles.filterItem}
options={props.tags ?? []}
getOptionLabel={(option: any) => option?.name ?? ''}
style={{ width: 300 }}
onChange={handleChangeTags}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Tags"
/>
)}
/>
<FormControl className={styles.filterItem}>
<InputLabel className={styles.sortLabel}>Sort</InputLabel>
<Select
onChange={handleChangeSort}
variant="standard"
defaultValue='Newest'
>
{['Newest', 'Oldest'].map(sort => (
<MenuItem value={sort} key={sort}>{sort}</MenuItem>
))}
</Select>
</FormControl>
</CContainer>
<CContainer style={{ marginBottom: '20px' }} id="blog-3">
<CList<TPost>
id={listId}
editorHidden
ListItem={(props) => (
<div className={styles.postWrapper}>
<PostCard onTagClick={handleTagClick} post={props.data} key={props.data?.id} />
</div>
)}
usePagination
useShowMoreButton
useQueryPagination
disableCaching
pageSize={20}
scrollContainerSelector={`.${layoutStyles.Layout}`}
firstBatch={props.posts}
loader={handleGetPosts}
cssClasses={{
page: styles.postList
}}
elements={{
pagination: MuiPagination
}}
/>
</CContainer>
</CContainer>
);
}
Example #18
Source File: search.tsx From Cromwell with MIT License | 4 votes |
SearchPage: TPageWithLayout<SearchPageProps> = (props) => {
const filterInput = useRef<TPostFilter>({});
const listId = 'Blog_list_01';
const publishSort = useRef<"ASC" | "DESC">('DESC');
const forceUpdate = useForceUpdate();
const titleSearchId = "post-filter-search";
const updateList = () => {
const list = getBlockInstance<TCList>(listId)?.getContentInstance();
list?.clearState();
list?.init();
list?.updateData();
}
const handleChangeTags = (event: any, newValue?: (TTag | undefined | string)[]) => {
filterInput.current.tagIds = newValue?.map(tag => (tag as TTag)?.id);
forceUpdate();
updateList();
}
const handleGetPosts = (params: TPagedParams<TPost>) => {
params.orderBy = 'publishDate';
params.order = publishSort.current;
return handleGetFilteredPosts(params, filterInput.current);
}
const handleChangeSort = (event: SelectChangeEvent<unknown>) => {
if (event.target.value === 'Newest') publishSort.current = 'DESC';
if (event.target.value === 'Oldest') publishSort.current = 'ASC';
updateList();
}
const handleTagClick = (tag?: TTag) => {
if (!tag) return;
if (filterInput.current.tagIds?.length === 1 &&
filterInput.current.tagIds[0] === tag.id) return;
handleChangeTags(null, [tag]);
forceUpdate();
}
const handleTitleInput = debounce(400, () => {
filterInput.current.titleSearch = (document.getElementById(titleSearchId) as HTMLInputElement)?.value ?? undefined;
updateList();
});
return (
<CContainer className={commonStyles.content} id="search_01">
<CContainer className={styles.filter} id="search_02">
<div className={styles.filterLeft}>
<TextField
className={styles.filterItem}
placeholder="Search by title"
id={titleSearchId}
variant="standard"
onChange={handleTitleInput}
/>
<Autocomplete
multiple
freeSolo
value={filterInput.current.tagIds?.map(id => props.tags?.find(tag => tag.id === id)) ?? []}
className={styles.filterItem}
options={props.tags ?? []}
getOptionLabel={(option: any) => option?.name ?? ''}
style={{ width: 300 }}
onChange={handleChangeTags}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Tags"
/>
)}
/>
</div>
<FormControl className={styles.filterItem}>
<InputLabel className={styles.sortLabel}>Sort</InputLabel>
<Select
style={{ width: '100px' }}
onChange={handleChangeSort}
variant="standard"
defaultValue='Newest'
>
{['Newest', 'Oldest'].map(sort => (
<MenuItem value={sort} key={sort}>{sort}</MenuItem>
))}
</Select>
</FormControl>
</CContainer>
<CContainer style={{ marginBottom: '20px' }} id="search_03">
<CList<TPost>
id={listId}
ListItem={(props) => (
<div className={styles.postWrapper}>
<PostCard onTagClick={handleTagClick} data={props.data} key={props.data?.id} />
</div>
)}
usePagination
useShowMoreButton
useQueryPagination
disableCaching
pageSize={20}
scrollContainerSelector={`.${layoutStyles.Layout}`}
firstBatch={props.posts}
loader={handleGetPosts}
cssClasses={{
page: styles.postList
}}
elements={{
pagination: Pagination
}}
/>
</CContainer>
</CContainer>
);
}
Example #19
Source File: PageSettings.tsx From Cromwell with MIT License | 4 votes |
PageSettings = (props: {
pageConfig: TPageConfig;
handlePageInfoChange: (page: TPageConfig) => void;
themeConfig?: TThemeConfig;
}) => {
const { pageConfig, themeConfig } = props;
const genericPages = themeConfig?.genericPages ?? [{ route: "pages/[slug]", name: "default" }];
const pageLayout = useRef(pageConfig?.layoutRoute ?? genericPages[0].route);
const handlePageSettingsChange = (prop: keyof TPageConfig, val: any) => {
if (pageConfig?.isVirtual && prop === 'route') {
if (!val) val = '';
const prefix = pageLayout.current.replace('[slug]', '');
val = val.replace(prefix, '');
val = val.replace(/\W/g, '-');
val = prefix + val;
}
const next = Object.assign({}, pageConfig, { [prop]: val, layoutRoute: pageLayout.current });
props.handlePageInfoChange(next);
}
const changeLayout = (route: string) => {
pageLayout.current = route;
handlePageSettingsChange('route', pageConfig.route);
}
return (
<div className={styles.pageSettings}>
{pageConfig?.isVirtual && !!genericPages?.length && genericPages.length > 1 && (
<Select
label="Layout name"
value={pageLayout.current}
options={genericPages.map(p => ({ label: p.name, value: p.route }))}
onChange={(event) => changeLayout(event.target.value as string)}
className={styles.textField}
/>
)}
<TextField label="Route" variant="outlined"
disabled={!pageConfig?.isVirtual}
value={pageConfig.route ?? ''}
className={styles.textField}
onChange={(e) => { handlePageSettingsChange('route', e.target.value) }}
/>
<TextField label="Name" variant="outlined"
value={pageConfig.name ?? ''}
className={styles.textField}
onChange={(e) => { handlePageSettingsChange('name', e.target.value) }}
/>
<TextField label="Meta title" variant="outlined"
value={pageConfig.title ?? ''}
className={styles.textField}
onChange={(e) => { handlePageSettingsChange('title', e.target.value) }}
/>
<TextField label="Meta description" variant="outlined"
value={pageConfig.description ?? ''}
className={styles.textField}
onChange={(e) => { handlePageSettingsChange('description', e.target.value) }}
/>
<Autocomplete
multiple
freeSolo
options={[]}
className={styles.textField}
value={pageConfig?.keywords ?? []}
getOptionLabel={(option) => option as any}
onChange={(e, newVal) => {
handlePageSettingsChange('keywords', newVal);
}}
renderInput={(params) => (
<Tooltip title="Press ENTER to add">
<TextField
{...params}
variant="outlined"
label="Meta keywords"
/>
</Tooltip>
)}
/>
<TextField label="Head HTML" variant="outlined"
value={pageConfig.headHtml ?? ''}
className={styles.textField}
multiline
onChange={(e) => { handlePageSettingsChange('headHtml', e.target.value) }}
/>
<TextField label="Footer HTML" variant="outlined"
value={pageConfig.footerHtml ?? ''}
className={styles.textField}
multiline
onChange={(e) => { handlePageSettingsChange('footerHtml', e.target.value) }}
/>
</div>
)
}
Example #20
Source File: MainInfoCard.tsx From Cromwell with MIT License | 4 votes |
MainInfoCard = (props: {
product: TProduct | TProductVariant,
setProdData: (data: TProduct | TProductVariant) => void;
isProductVariant?: boolean;
canValidate?: boolean;
}) => {
const productPrevRef = React.useRef<TProductVariant | TProduct | null>(props.product);
const cardIdRef = React.useRef<string>(getRandStr(10));
const productRef = React.useRef<TProductVariant | TProduct | null>(props.product);
if (props.product !== productPrevRef.current) {
productPrevRef.current = props.product;
productRef.current = props.product;
}
const forceUpdate = useForceUpdate();
const editorId = "prod-text-editor_" + cardIdRef.current;
const product = productRef.current;
const setProdData = (data: TProduct) => {
Object.keys(data).forEach(key => { productRef.current[key] = data[key] })
props.setProdData(data);
forceUpdate();
}
const fullSave = async () => {
setProdData({
...(product as TProduct),
description: await getEditorHtml(editorId),
descriptionDelta: JSON.stringify(await getEditorData(editorId)),
});
}
useEffect(() => {
init();
}, [])
const init = async () => {
let descriptionDelta;
if (product?.descriptionDelta) {
try {
descriptionDelta = JSON.parse(product.descriptionDelta);
} catch (e) { console.error(e) }
}
const updateText = debounce(600, () => {
fullSave();
})
await initTextEditor({
htmlId: editorId,
data: descriptionDelta,
placeholder: 'Product description...',
onChange: updateText,
});
}
const handleChange = (prop: keyof TProduct, val: any) => {
if (product) {
const prod = Object.assign({}, product);
(prod[prop] as any) = val;
if (prop === 'images') {
prod.mainImage = val?.[0];
}
setProdData(prod as TProduct);
}
}
if (!product) return null;
let pageFullUrl;
if ((product as TProduct)?.slug) {
pageFullUrl = serviceLocator.getFrontendUrl() + resolvePageRoute('product', {
slug: (product as TProduct).slug ?? (product as TProduct).id + '',
});
}
return (
<Grid container spacing={3}>
<Grid item xs={12} sm={12}>
<TextField label="Name" variant="standard"
value={product.name ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('name', e.target.value) }}
error={props.canValidate && !product?.name}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="SKU" variant="standard"
value={product.sku ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('sku', e.target.value) }}
/>
</Grid>
<Grid item xs={12} sm={6}></Grid>
<Grid item xs={12} sm={6}>
<Select
fullWidth
label="Stock status"
variant="standard"
value={product.stockStatus}
onChange={(e) => { handleChange('stockStatus', e.target.value) }}
options={['In stock', 'Out of stock', 'On backorder'] as TStockStatus[]}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Stock amount" variant="standard"
value={product.stockAmount ?? ''}
className={styles.textField}
type="number"
onChange={(e) => {
let val = parseInt(e.target.value);
if (isNaN(val)) val = null;
if (val && val < 0) val = 0;
handleChange('stockAmount', val);
}}
/>
</Grid>
<Grid item xs={12} sm={12} style={{ display: 'flex', alignItems: 'center' }}>
<FormGroup>
<FormControlLabel control={<Checkbox defaultChecked />}
label="Manage stock"
checked={!!product?.manageStock}
onChange={() => handleChange('manageStock', !product?.manageStock)}
/>
</FormGroup>
<Tooltip title="Automatically manage stock amount when new orders placed">
<InfoOutlinedIcon />
</Tooltip>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Price" variant="standard"
value={product.price ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('price', e.target.value) }}
InputProps={{
inputComponent: NumberFormatCustom as any,
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField label="Old price" variant="standard"
value={product.oldPrice ?? ''}
className={styles.textField}
onChange={(e) => {
const val = e.target.value;
handleChange('oldPrice', (val && val !== '') ? val : null);
}}
InputProps={{
inputComponent: NumberFormatCustom as any,
}}
/>
</Grid>
<Grid item xs={12} sm={12}>
<div className={styles.imageBlock}>
<GalleryPicker
classes={{
imagePicker: {
root: styles.imageItem
}
}}
label="Gallery"
images={((product as TProduct)?.images ?? []).map(src => ({ src }))}
onChange={(val) => handleChange('images', val.map(s => s.src))}
/>
</div>
</Grid>
<Grid item xs={12} sm={12}>
<div className={styles.descriptionEditor}>
<div style={{ minHeight: '300px' }} id={editorId}></div>
</div>
</Grid>
<Grid item xs={12} sm={12}>
{props.isProductVariant !== true && (
<TextField label="Page URL" variant="standard"
value={(product as TProduct).slug ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('slug', e.target.value) }}
helperText={pageFullUrl}
/>
)}
</Grid>
<Grid item xs={12} sm={12}>
{props.isProductVariant !== true && (
<TextField label="Meta title" variant="standard"
value={(product as TProduct).pageTitle ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('pageTitle', e.target.value) }}
/>
)}
</Grid>
<Grid item xs={12} sm={12}>
{props.isProductVariant !== true && (
<TextField label="Meta description" variant="standard"
multiline
value={(product as TProduct).pageDescription ?? ''}
className={styles.textField}
onChange={(e) => { handleChange('pageDescription', e.target.value) }}
/>
)}
</Grid>
<Grid item xs={12} sm={12}>
{props.isProductVariant !== true && (
<Autocomplete
multiple
freeSolo
options={[]}
className={styles.textField}
value={((product as TProduct).meta?.keywords ?? []) as any}
getOptionLabel={(option) => option}
onChange={(e, newVal) => {
handleChange('meta', {
...((product as TProduct).meta ?? {}),
keywords: newVal
})
}}
renderInput={(params) => (
<Tooltip title="Press ENTER to add">
<TextField
{...params}
variant="standard"
label="Meta keywords"
/>
</Tooltip>
)}
/>
)}
</Grid>
</Grid>
)
}
Example #21
Source File: PostSettings.tsx From Cromwell with MIT License | 4 votes |
PostSettings = (props: {
postData?: TPost;
isSettingsOpen: boolean;
anchorEl: Element;
allTags?: TTag[] | null;
onClose: (newData: Partial<TPost>) => void;
isSaving?: boolean;
handleUnpublish: () => void;
refetchMeta: () => Promise<Record<string, string> | undefined>;
}) => {
const { postData, refetchMeta } = props;
const [title, setTitle] = useState<string | undefined>(postData?.title ?? null);
const [mainImage, setMainImage] = useState<string | undefined>(postData?.mainImage ?? null);
const [pageDescription, setPageDescription] = useState<string | undefined>(postData?.pageDescription ?? null);
const [pageKeywords, setPageKeywords] = useState<string[] | undefined>(postData?.meta?.keywords ?? null);
const [pageTitle, setPageTitle] = useState<string | undefined>(postData?.pageTitle ?? null);
const [slug, setSlug] = useState<string | undefined>(postData?.slug ?? null);
const [tags, setTags] = useState<TTag[] | undefined>(postData?.tags ?? []);
const [publishDate, setPublishDate] = useState<Date | undefined | null>(postData?.publishDate ?? null);
const [featured, setFeatured] = useState<boolean | undefined | null>(postData?.featured ?? null);
const handleChangeTags = (event: any, newValue: TTag[]) => {
setTags(newValue);
}
const handleChangeKeywords = (event: any, newValue: string[]) => {
setPageKeywords(newValue);
}
const handleClose = async () => {
const newData = Object.assign({}, postData);
newData.title = title;
newData.mainImage = mainImage;
newData.pageDescription = pageDescription;
newData.pageTitle = pageTitle;
newData.slug = slug;
newData.tags = tags;
newData.publishDate = publishDate;
newData.featured = featured;
if (pageKeywords) {
if (!newData.meta) newData.meta = {};
newData.meta.keywords = pageKeywords;
}
newData.customMeta = Object.assign({}, postData.customMeta, await getCustomMetaFor(EDBEntity.Post));
props.onClose(newData);
}
let pageFullUrl;
if (slug) {
pageFullUrl = serviceLocator.getFrontendUrl() + resolvePageRoute('post', { slug: slug ?? postData.id + '' });
}
return (
<Popover
disableEnforceFocus
open={props.isSettingsOpen}
elevation={0}
anchorEl={props.anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
classes={{ paper: styles.popover }}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
<div className={styles.PostSettings}>
<p className={styles.headerText}>Post meta</p>
<IconButton className={styles.closeBtn}
id="post-settings-close-btn"
onClick={handleClose}>
<CloseIcon />
</IconButton>
<TextField
label="Title"
value={title ?? ''}
fullWidth
className={styles.settingItem}
variant="standard"
onChange={e => setTitle(e.target.value)}
/>
<TextField
label="Page URL"
className={styles.settingItem}
fullWidth
value={slug ?? ''}
onChange={e => setSlug(e.target.value)}
variant="standard"
helperText={pageFullUrl}
/>
<ImagePicker
label="Main image"
onChange={(val) => setMainImage(val)}
value={mainImage}
className={styles.imageBox}
backgroundSize='cover'
showRemove
/>
<Autocomplete
multiple
options={props.allTags ?? []}
defaultValue={tags?.map(tag => (props.allTags ?? []).find(allTag => allTag.name === tag.name)) ?? []}
getOptionLabel={(option) => option.name}
onChange={handleChangeTags}
renderInput={(params) => (
<TextField
{...params}
className={styles.settingItem}
variant="standard"
label="Tags"
/>
)}
/>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Publish date"
value={publishDate}
onChange={(newValue) => {
if (!newValue) {
setPublishDate(null);
return;
}
const date = new Date(newValue);
if (isNaN(date.getTime())) {
setPublishDate(null);
return;
}
setPublishDate(date);
}}
renderInput={(params) => <TextField
variant="standard"
fullWidth
{...params} />}
/>
</LocalizationProvider>
<FormControlLabel
control={
<Checkbox
checked={featured}
onChange={() => setFeatured(!featured)}
color="primary"
/>
}
style={{ margin: '10px 0' }}
className={styles.settingItem}
label="Featured post"
/>
<TextField
label="Meta title"
className={styles.settingItem}
fullWidth
variant="standard"
value={pageTitle ?? ''}
onChange={e => setPageTitle(e.target.value)}
/>
<TextField
label="Meta description"
className={styles.settingItem}
fullWidth
variant="standard"
value={pageDescription ?? ''}
onChange={e => setPageDescription(e.target.value)}
/>
<Autocomplete
multiple
freeSolo
options={[]}
value={(pageKeywords ?? []) as any}
getOptionLabel={(option) => option}
onChange={handleChangeKeywords}
renderInput={(params) => (
<Tooltip title="Press ENTER to add">
<TextField
{...params}
className={styles.settingItem}
variant="standard"
label="Meta keywords"
/>
</Tooltip>
)}
/>
{postData?.published && (
<Tooltip title="Remove post from publication">
<Button variant="contained" color="primary"
className={styles.publishBtn}
size="small"
disabled={props.isSaving}
onClick={props.handleUnpublish}
>Unpublish</Button>
</Tooltip>
)}
<div style={{ marginBottom: '15px' }}></div>
{postData && (
<RenderCustomFields
entityType={EDBEntity.Post}
entityData={postData}
refetchMeta={refetchMeta}
/>
)}
</div>
</Popover>
)
}
Example #22
Source File: EntityTable.tsx From Cromwell with MIT License | 4 votes |
render() {
const tableColumns = this.getColumns();
return (
<div className={styles.EntityTable}>
<div className={styles.header}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<h1 className={styles.pageTitle}>{this.props.listLabel}</h1>
{this.props.customElements?.listLeftActions}
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
{this.props.customElements?.listRightActions}
{!!(this.filters?.length || this.sortBy?.column
|| this.props.isFilterActive?.()) && (
<Tooltip title="Clear filters">
<IconButton className={styles.iconButton}
onClick={this.clearAllFilters}>
<ClearAllIcon />
</IconButton>
</Tooltip>
)}
<DeleteSelectedButton
style={{ marginRight: '10px' }}
onClick={this.handleDeleteSelected}
totalElements={this.totalElements}
/>
{this.props.entityBaseRoute && !this.props.hideAddNew && (
<Button variant="contained"
size="small"
onClick={this.handleCreate}
>Add new</Button>
)}
</div>
</div>
<div className={styles.tableHeader}>
<div className={commonStyles.center}>
<Tooltip title="Select all">
<Checkbox
checked={this.props.allSelected ?? false}
onChange={this.handleToggleSelectAll}
/>
</Tooltip>
</div>
<div className={styles.tableColumnNames}>
{tableColumns.map(col => {
if (!col.visible) return null;
const columnFilter = this.filters?.find(filter => filter.key === col.name);
let searchQuery = columnFilter?.value !== null && columnFilter?.value !== undefined &&
columnFilter.value !== '' ? columnFilter.value : null;
if (col.searchOptions) {
const autocompleteVal = this.getAutocompleteValueFromSearch(searchQuery, col);
if (Array.isArray(autocompleteVal)) {
searchQuery = autocompleteVal.map(val => val.label).join(', ');
} else if (autocompleteVal) {
searchQuery = autocompleteVal.label;
}
}
return (
<div key={col.name}
id={`column_${col.name}`}
className={clsx(styles.columnName)}
style={this.getColumnStyles(col, tableColumns)}
>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Tooltip title="Open search" placement="top" enterDelay={500}>
<p onClick={() => this.openColumnSearch(col)}
className={clsx(styles.ellipsis, styles.columnNameText)}>{col.label}</p>
</Tooltip>
{!col.disableSort && (
<Tooltip title="Toggle sort" placement="top" enterDelay={500}>
<div onClick={() => this.toggleOrderBy(col)}
className={styles.orderButton}>
<ArrowDropDownIcon
style={{
transform: this.sortBy?.column?.name === col.name
&& this.sortBy?.sort === 'ASC' ? 'rotate(180deg)' : undefined,
transition: '0.3s',
color: this.sortBy?.column?.name !== col.name ? '#aaa' : '#9747d3',
fill: this.sortBy?.column?.name !== col.name ? '#aaa' : '#9747d3',
fontSize: this.sortBy?.column?.name === col.name ? '26px' : undefined,
}}
/>
</div>
</Tooltip>
)}
</div>
{searchQuery && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<p className={clsx(styles.searchQuery, styles.ellipsis)}>{searchQuery}</p>
<Tooltip title="Clear search" enterDelay={500}>
<div onClick={() => this.clearColumnSearch(col)}
style={{ cursor: 'pointer' }}>
<CloseIcon style={{
fontSize: '14px',
color: '#555'
}} />
</div>
</Tooltip>
</div>
)}
</div>
)
})}
</div>
<div className={clsx(commonStyles.center, styles.headerSettings)}>
<Tooltip title="Configure columns">
<IconButton
onClick={this.toggleConfigureColumns}
ref={this.configureColumnsButtonRef}
disabled={!tableColumns?.length}
>
<SettingsIcon />
</IconButton>
</Tooltip>
<Popover
open={!!this.state?.configureColumnsOpen}
anchorEl={this.configureColumnsButtonRef.current}
onClose={this.toggleConfigureColumns}
classes={{ paper: styles.popoverPaper }}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
elevation={0}
>
<div className={styles.columnsConfigure}>
<Button size="small" variant="outlined"
onClick={this.resetConfiguredColumns}
style={{ margin: '10px 0 0 10px' }}
>Reset</Button>
<DraggableList<TColumnConfigureItemData>
data={tableColumns.map(col => ({
id: col.name,
column: col,
sortedColumns: this.sortedColumns,
}))}
onChange={this.changeColumnsOrder}
component={ColumnConfigureItem}
/>
</div>
</Popover>
<Popover
open={!!this.state?.columnSearch}
anchorEl={() => document.getElementById(`column_${this.state?.columnSearch?.name}`)}
onClose={this.closeColumnSearch}
classes={{ paper: styles.popoverPaper }}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
elevation={0}
>
<div className={styles.columnsConfigure}
style={{ padding: '10px 15px' }}>
{this.state?.columnSearch?.searchOptions ? (
<Autocomplete
multiple={this.state.columnSearch.multipleOptions}
options={this.state.columnSearch.searchOptions}
getOptionLabel={(option: any) => option?.label ?? ''}
defaultValue={this.getAutocompleteValueFromSearch(this.currentSearch,
this.state.columnSearch)}
className={styles.filterItem}
onChange={(event, newVal) => {
if (Array.isArray(newVal)) newVal = JSON.stringify(newVal.map(val => typeof val === 'object' ? val?.value : val));
this.currentSearch = typeof newVal === 'object' ? newVal?.value : newVal
}}
classes={{ popper: styles.autocompletePopper }}
renderInput={(params) => <TextField
{...params}
variant="standard"
fullWidth
label={`Search ${this.state?.columnSearch?.label ?? ''}`}
/>}
/>
) : (
<TextField
fullWidth
onChange={(event) => this.currentSearch = event.target.value}
variant="standard"
label={`Search ${this.state?.columnSearch?.label ?? ''}`}
defaultValue={this.currentSearch}
/>
)}
</div>
</Popover>
</div>
</div>
<CList<TEntityType, TListItemProps<TEntityType, TFilterType>>
className={styles.listWrapper}
id={this.listId}
ListItem={EntityTableItem}
useAutoLoading
usePagination
listItemProps={{
handleDeleteBtnClick: this.handleDeleteItem,
toggleSelection: this.handleToggleItemSelection,
tableProps: this.props,
getColumns: this.getColumns,
getColumnStyles: this.getColumnStyles,
}}
useQueryPagination
loader={this.getManyFilteredItems}
cssClasses={{
scrollBox: styles.list,
contentWrapper: styles.listContent,
}}
elements={{
pagination: Pagination,
preloader: listPreloader
}}
/>
</div >
)
}
Example #23
Source File: ArtifactAutocomplete.tsx From genshin-optimizer with MIT License | 3 votes |
function ArtifactMultiAutocomplete<T extends ArtifactMultiAutocompleteKey>({ allArtifactKeys, selectedArtifactKeys, setArtifactKeys, getName, getImage, label, ...props }:
ArtifactMultiAutocompleteProps<T>) {
const theme = useTheme();
const handleChange = (_, value: ArtifactMultiAutocompleteOption<T>[]) => {
setArtifactKeys(value.map(v => v.key))
};
const options = useMemo(() => allArtifactKeys.map(key => ({ key: key, label: getName(key) })), [allArtifactKeys, getName])
return <Autocomplete
autoHighlight
multiple
options={options}
value={selectedArtifactKeys.map(key => ({ key: key, label: getName(key) }))}
onChange={handleChange}
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) => option.key === value.key}
renderInput={(params) => <TextField
{...params}
label={label}
variant="filled"
InputLabelProps={{ style: { color: theme.palette.text.primary } }}
color={selectedArtifactKeys.length ? "success" : "primary"}
type="search"
/>}
renderOption={(props, option) => (
<MenuItemWithImage
key={option.key}
value={option.key}
image={getImage(option.key)}
text={option.label}
theme={theme}
isSelected={selectedArtifactKeys.includes(option.key)}
props={props}
/>
)}
renderTags={(selected, getTagProps) => selected.map((value, index) => {
const element = allElementsWithPhy.find(ele => value.key === `${ele}_dmg_`)
const color = element ? element : undefined
return <Chip {...getTagProps({ index })} key={value.key} icon={getImage(value.key)} label={value.label} color={color} />
})}
{...props}
/>
}
Example #24
Source File: PersonAutocomplete.tsx From frontend with MIT License | 3 votes |
export default function PersonAutocomplete({
onSelect,
showId,
autocompleteProps,
}: PersonAutocompleteProps) {
const { t } = useTranslation('person')
const {
data: personList,
isLoading,
refetch,
} = usePersonList({
enabled: false,
refetchOnWindowFocus: false,
})
return (
<Autocomplete
isOptionEqualToValue={(option, value) => option.firstName === value.firstName}
options={personList || []}
getOptionLabel={(person) =>
showId
? `${person.firstName} ${person.lastName} (${person.id})`
: person.firstName + ' ' + person.lastName
}
onChange={(e, person) => {
onSelect(person)
}}
onOpen={() => {
refetch()
}}
loading={isLoading}
renderInput={(params) => (
<TextField
{...params}
type="text"
fullWidth
defaultValue=""
label={t('person:autocomplete.personSearch')}
/>
)}
{...autocompleteProps}
/>
)
}
Example #25
Source File: Vault.tsx From NekoMaid with MIT License | 1 votes |
PermissionDialog: React.FC<{ plugin: Plugin, id: string | undefined, isGroup: boolean, onClose: () => void }> = ({ plugin, id, onClose, isGroup }) => {
const [value, setValue] = useState('')
const [status, setStatus] = useState<boolean | undefined>(false)
const [options, setOptions] = useState<string[]>([])
useEffect(() => {
if (!id) return
setValue('')
setStatus(false)
plugin.emit('vault:getAllPermissions', (it: any) => setOptions(it.sort()))
}, [id])
const queryStatus = useMemo(() => throttle((value: string) => plugin.emit('vault:permission', setStatus, id, value, 0, isGroup), 500), [id, isGroup])
return <Dialog open={!!id} onClose={onClose}>
<DialogTitle>{lang.vault.editorTitle}</DialogTitle>
<DialogContent sx={{ overflow: 'hidden' }}>
<DialogContentText>{lang.vault.permissionInput}: <span className='bold' style={{ }}>
({isGroup ? lang.vault.permissionGroup : minecraft['entity.minecraft.player']}: {id})</span></DialogContentText>
<Autocomplete
freeSolo
options={options}
sx={{ marginTop: 1 }}
inputValue={value}
renderInput={params => <TextField {...params as any} label={lang.vault.permission} />}
onInputChange={(_, it) => {
setValue(it)
setStatus(undefined)
queryStatus(it)
}}
/>
<Box sx={{ display: 'flex', alignItems: 'center', marginTop: 1 }}>
{lang.status}:{status == null
? <CircularProgress size={20} sx={{ margin: '5px' }}/>
: status ? <Check color='success' /> : <Close color='error' />}
{status != null && <Button
disabled={!value}
variant='outlined'
size='small'
onClick={() => plugin.emit('vault:permission', (res: boolean) => {
action(res)
setStatus(undefined)
queryStatus(value)
}, id, value, status ? 2 : 1, isGroup)}
>{lang.vault[status ? 'removePermission' : 'addPermission']}</Button>}
</Box>
</DialogContent>
<DialogActions><Button onClick={onClose}>{minecraft['gui.back']}</Button></DialogActions>
</Dialog>
}