How to add a label to a border in mui? - reactjs

I would like to have a list wrapped in a border which looks and behaves the same as a textfield border:
Example textfield and list which should have both same border.
In the image, the border around the list looks similar than the one around the textfield but most notably, the label is missing. How can I add the label and how would I set up the focus listeners to get the same hover and selection behaviour?
The typescript code for the list:
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600'}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>
I am also open for alternative approaches. Thanks for the help.

Here is my answer using React and Mui (only for icon).
It relies on flex.
We have a main container that only draws its left, bottom and right borders.
Then we have a header container in charge of drawing the top border in two parts (before and after) and a section with an icon and title.
You can either pass an icon and a title, just a title, just an icon, or nothing at all.
borderedSection.js:
import React from "react";
import SvgIcon from "#mui/material/SvgIcon";
import styles from "./borderedSection.module.scss";
function BorderedSection({ icon, title, children }) {
return (
<div className={styles.mainContainer}>
<div className={styles.header}>
<div className={styles.headerBorderBefore}></div>
{(icon || title) && (
<div className={styles.headerTitle}>
{icon && <SvgIcon component={icon} />}
{title && <span className={styles.title}>{title}</span>}
</div>
)}
<div className={styles.headerBorderAfter}></div>
</div>
<div className={styles.childrenContainer}>{children}</div>
</div>
);
}
export default BorderedSection;
borderedSection.module.scss:
$border-color: #b2b2b2;
.mainContainer {
display: flex;
flex-direction: column;
max-width: 100%;
border-left: 1px solid $border-color;
border-bottom: 1px solid $border-color;
border-right: 1px solid $border-color;
border-radius: 5px;
margin: 1em;
.childrenContainer {
padding: 1em;
}
.header {
display: flex;
flex-direction: row;
width: 100% !important;
.headerBorderBefore {
border-top: 1px solid $border-color;
width: 1em;
border-top-left-radius: 5px;
}
.headerTitle {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
gap: 0.25em;
width: fit-content;
height: 2em;
margin: -1em 0.5em 0em 0.5em;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1em;
font-weight: 600;
}
.headerBorderAfter {
border-top: 1px solid $border-color;
width: 1em;
flex-grow: 2;
border-top-right-radius: 5px;
}
}
}
usage:
import React from "react";
import BorderedSection from "./borderedSection";
import InfoIcon from "#mui/icons-material/Info";
function Example() {
return (
<div style={{ padding: "2em" }}>
<BorderedSection icon={InfoIcon} title="Icon and title">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection title="Title only">
<div>a first child with quite a long text</div>
<div>a second child</div>
</BorderedSection>
<BorderedSection icon={InfoIcon} >
<div>Icon only</div>
<div>a second child with quite a long text</div>
</BorderedSection>
<BorderedSection >
<div>No icon and no title</div>
<div>a second child with quite a long text</div>
</BorderedSection>
</div>
);
}
Here is how it looks:
I hope it helps

I now managed to hack a solution which looks the same. I do still hope though that there is a clean way to do this: result.
<FormLabel style={{marginLeft: "0.71em", marginTop: "-0.71em", paddingLeft: "0.44em", zIndex: 2, width: "4.2em", backgroundColor: "#383838", position: "absolute", fontSize: "0.75em"}}>Damage</FormLabel>
<List dense sx={{ borderRadius: 1, border: 1, borderColor: 'grey.600', "&:hover": { borderColor: 'grey.200' }}}>
<ListItem secondaryAction={<IconButton edge="end" aria-label="delete"><DeleteIcon /></IconButton>}>
<ListItemText primary="primary" secondary="group id"/>
</ListItem>
</List>

I needed the same thing. As I was poking around I noticed that MUI accomplished this by using the fieldset tag. I created a quick and dirty component (OutlinedBox) to get this effect:
import React from "react";
import {Box, FormLabel} from "#mui/material";
const OutlinedBox = (props) => {
const {
label,
children
} = props;
return (
<Box>
<FormLabel
sx={{
marginLeft: "0.71em",
marginTop: "-0.71em",
paddingLeft: "0.44em",
paddingRight: '0.44em',
zIndex: 2,
backgroundColor: (theme) => theme.palette.background.default,
position: "absolute",
fontSize: "0.75em",
width: 'auto',
}}>{label}</FormLabel>
<Box
sx={{
position: 'relative',
borderRadius: theme => theme.shape.borderRadius + 'px',
fontSize: '0.875rem',
}}
>
<Box
sx={{
padding: (theme) => theme.spacing(1),
display: 'flex',
gap: (theme) => theme.spacing(1),
flexWrap: 'wrap',
overflow: 'auto'
}}
>
{children}
</Box>
<fieldset aria-hidden={"true"} style={{
textAlign: 'left',
position: 'absolute',
bottom: 0,
right: 0,
top: '-5px',
left: 0,
margin: 0,
padding: '0 8px',
pointerEvents: 'none',
borderRadius: 'inherit',
borderStyle: 'solid',
borderWidth: '1px',
overflow: 'hidden',
minWidth: '0%',
borderColor: 'rgba(255, 255, 255, 0.23)',
}}
>
<legend style={{
float: 'unset',
overflow: 'hidden',
display: 'block',
width: 'auto',
padding: 0,
height: '11px',
fontSize: '0.75em',
visibility: 'hidden',
maxWidth: '100%',
'-webkit-transition': 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
transition: 'max-width 100ms cubic-bezier(0.0, 0, 0.2, 1) 50ms',
whiteSpace: 'nowrap',
}}><span>{label}</span></legend>
</fieldset>
</Box>
</Box>
);
}
export { OutlinedBox };
// Example usage: <OutlinedBox label="Test">Some content here</OutlinedBox>
I figured I'd post it here in case anyone needs the same thing and comes across this question. All the styling stuff was copied from the styles MUI was using. There may be a better way to read some of this off of the theme, so if anyone decides to use this you may want to tweak it some.

Related

How can I use media query with emotion/styled/macro?

I cannot find any code that using styled macro with media query and don't understand why It's so rare to find code using emotion/styled/macro.
I know it allows css style in object literal
const StyledUl = styled("ul")(
{
backgroundColor: "#fff",
height: "200px",
width: "100%",
overflowY: "scroll",
position: "absolute",
margin: 0,
padding: "5px",
boxShadow: "-1px 15px 34px -21px rgba(0,32,86,0.21)",
boxSizing: "border-box",
borderRadius: "8px",
zIndex: 9999,
}
)
But how can I use media query?
And where can I find documentation about emotion/styled/macro?
Nothing special is required. Just add a #media ... query as a property:
import styled from "#emotion/styled/macro";
const StyledUl = styled("ul")({
"#media (max-width: 600px)": {
backgroundColor: "#000",
color: "#fff"
},
backgroundColor: "#fff",
height: "200px",
width: "calc(100% - 40px)",
overflowY: "scroll",
position: "absolute",
margin: 0,
padding: "5px",
boxShadow: "-1px 15px 34px -21px rgba(0,32,86,0.21)",
boxSizing: "border-box",
borderRadius: "8px",
zIndex: 9999
});
export default StyledUl;
Optionally, you can use a template literal to style a styled.HTMLElement:
import styled from "#emotion/styled/macro";
const StyledUlTemplate = styled.ul`
#media (max-width: 600px) {
background-color: #000;
color: #fff;
}
background-color: #fff;
height: 200px;
width: calc(100% - 40px);
overflow-y: scroll;
position: absolute;
top: 60%;
margin: 0;
padding: 5px;
box-shadow: -1px 15px 34px -21px rgba(0, 32, 86, 0.21);
box-sizing: border-box;
border-radius: 8px;
z-index: 9999;
`;
export default StyledUlTemplate;
Demo (drag the middle bar left/right to resize the Browser tab to trigger the style changes):

position absolute top:0, bottom: 0, right:0, left:0 and parent paddings

position absolute with all its properties in 0 makes the child element expand in the parent element, and if I put a padding in the parent element without a height the child with the position absolute cover until the of the parent padding, my theory is right or I am forgotting something here?there could be another different case?
import React from 'react';
import {View, StyleSheet} from 'react-native';
import Video from 'react-native-video';
export default function Reproductor(props) {
return (
<View style={styles.container}>
<View style={styles.videoContainer}>
<Video
source={{
uri:
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
}}
resizeMode="cover"
style={styles.video}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
height: 250,
justifyContent: 'center',
alignItems: 'center',
},
videoContainer: {
width: '90%',
paddingTop: '56.25%',
borderRadius: 20,
overflow: 'hidden',
borderWidth: 3,
backgroundColor: 'black',
},
video: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
the child (video) covers the padding
An absolutely positioned element is an element whose computed
position value is absolute or fixed. The top, right,
bottom, and left properties specify offsets from the edges of the element's containing block.
https://developer.mozilla.org/en-US/docs/Web/CSS/position
position absolute related to the edges of the parent element (= ignores parent padding/border).
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white; height: 50px;"></div>
<div style="position: absolute; background: blue; top:0; left:0; color: white;">child</div>
</div>
top: 0; right: 0; bottom: 0; left: 0; is the way to set overlay:
Example: https://www.w3schools.com/howto/howto_css_overlay.asp
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">div</div>
<div style="position: absolute; background: rgba(0, 0, 0, 0.5); top:0px; left:0px; right: 0; bottom: 0; color: white;">overlay</div>
</div>
Add space by top: 10px; for example (or em % and so on)
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:10px; left:10px; color: white;">child</div>
</div>
Or add a margin for the absolute element:
<div style="padding: 50px; background: red; color: white; position:relative;">
<div style="border: 1px solid white;">Parent</div>
<div style="position: absolute; background: blue; top:0px; left:0px; color: white;margin: 10px;">margin: 10px around</div>
</div>
You find more tricks/ideas but these are the basics ideas.
Thank you I understand, but you said that it cover padding and border (position absolute related to the edges of the parent element (= ignores parent padding/border).), and I try to cover the border too but it just ignore the padding not the border, what is my mistake here?
export default function MainScreen(props) {
return (
<View style={style.container}>
<View style={style.element} />
</View>
);
}
const style = StyleSheet.create({
container: {
borderWidth: 3,
padding: 40,
alignSelf: 'center',
width: 200,
height: 200,
backgroundColor: 'grey',
},
element: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: 100,
height: 100,
backgroundColor: 'purple',
},
});

Adjusting toolbar height for material-table

The blue marked area takes up a lot of space. How can I adjust the height of the Toolbar?
Can you show by example?
Or how can I create a css file and import it into the toolbar. I need to change the height, I couldn't change whatever I did. please can you help with this?
There is almost no method I have not tried.
In short, I want to adjust the height of the Mtabletoolbar field marked in blue.
<MaterialTable
Toolbar: props => (
<div style={{ backgroundColor: 'blue', }}>
<MTableToolbar {...props} classes={{ customizeToolbar: "15px" }} />
</div>
),
/>
`
const styles = {
customizeToolbar: {
minHeight: "100px"
}
}
`
I have been trying for 2 days, please can you help with the subject?
I need to change the style structure below. especially I have to change the min-height
.MuiToolbar-regular {
min-height: 64px;
}
`
.MuiToolbar-root {
display: flex;
position: relative;
align-items: center;
}
.MuiToolbar-regular {
min-height: 56px;
}
#media (min-width:0px) and (orientation: landscape) {
.MuiToolbar-regular {
min-height: 48px;
}
}
#media (min-width:600px) {
.MuiToolbar-regular {
min-height: 64px;
}
}
.MuiToolbar-dense {
min-height: 48px;
}
`
Simply adjust the style of the element where blue appears,
and make the child vertical center as below:
<div
style={{
backgroundColor: "lightblue",
height: "200px",
display: "flex",
alignItems: "center"
}}
>
<MTableToolbar {...props} />
</div>

React Stripe Elements - set width to auto for iFrame? Elements invisible inside grid and modal

I am trying to build a Stripe form using React Elements, inside of a React Modal, with layout managed by React Grid
<StripeProvider apiKey="stripeApiKey">
<Grid
container
direction="column"
alignItems="center"
justify="center"
style={{ minWidth: '30rem', maxWidth: '100rem' }}
>
<Grid item>
<Grid
container
direction="column"
justify="center"
style={{
backgroundColor: theme.palette.primary.dark,
padding: '73px',
width: '100%'
}}
>
<Elements>
<CardElement />
</Elements>
</Grid>
</Grid>
</Grid>
</StripeProvider>
The above renders the CardElement, but it's set to 1px width. If I manually set the IFrame's width property to auto it completely resolves my problem. How can I set the IFrame's width property for Stripe? (or is this even possible with cross-origin IFrame)
Or if not, how else can I resolve this issue to get the fields to display?
The style for individual elements for React-Stripe has to be passed manually as props. Below is an example from their official react-stripe-elements sample:
Elements
<CardNumberElement
onBlur={handleBlur}
onChange={this.handleChange}
onFocus={handleFocus}
onReady={handleReady}
{...createOptions(this.props.fontSize)}
/>
Function createOptions
const createOptions = (fontSize, padding) => {
return {
style: {
base: {
fontSize,
color: '#424770',
letterSpacing: '0.025em',
fontFamily: 'Source Code Pro, monospace',
'::placeholder': {
color: '#aab7c4',
},
...(padding ? { padding } : {}),
},
invalid: {
color: '#9e2146',
},
},
};
};
css
input,
.StripeElement {
display: block;
margin: 10px 0 20px 0;
max-width: 500px;
padding: 10px 14px;
font-size: 1em;
font-family: 'Source Code Pro', monospace;
box-shadow: rgba(50, 50, 93, 0.14902) 0px 1px 3px, rgba(0, 0, 0, 0.0196078) 0px 1px 0px;
border: 0;
outline: 0;
border-radius: 4px;
background: white;
}

ReactJS + MaterialUI: SelectField height change

I'm using React material-ui SelectField on my page. I'm not able to adjust the height of the select field. Here is my code snippet:
<SelectField
underlineStyle={{
display: 'none'
}}
style={{
width: '49%',
padding: '0 0 5em 5em',
border: '1px solid #ccc',
borderRadius: '5px',
backgroundColor: '#fff',
margin: '16px 0px 0px 28px',
}}
floatingLabelText="Select Stack*"
value={this.state.stack}
onChange={this.handleStackChange.bind(this)}
>
{this.state.dropDownStack}
</SelectField>
My UI is looking as below
I need to reduce the height of dropdown box, how can I do it?
You have to set it using following:
<SelectField
className="custom-selectfield"
underlineStyle={{
visibility: 'hidden',
}}
floatingLabelStyle={{
top: '14px',
}}
style={{
border: '1px solid #ccc',
height: '45px',
}}
floatingLabelText="Select Stack*"
value={this.state.stack}
onChange={this.handleStackChange.bind(this)}>
{this.state.dropDownStack}
</SelectField>
There is some internal CSS applied in material UI so in order to overwrite it added a custom class to SelectPicker component named custom-selectfield
Add following lines in your CSS file
.custom-selectfield > div:nth-child(2) {
margin-top: -6px !important;
}

Resources