Component which i have built
// libs
import React from 'react';
// material components
// styles
import {
StyledSelect,
StyledFormControl,
StyledMenuList,
ISelectProps,
} from './styles';
function Select(props: ISelectProps) {
const {
displayEmpty,
iconBordered,
options,
value,
children,
onChange,
className,
} = props;
return (
<StyledFormControl className={className}>
<StyledSelect
MenuProps={{
classes: { paper: 'Menupaper' },
anchorOrigin: {
vertical: 'bottom',
horizontal: 'left',
},
getContentAnchorEl: null,
}}
IconComponent={(props) => {
let iconClass = 'icon-container';
if (props.className.split(' ').indexOf('MuiSelect-iconOpen') > -1) {
iconClass += ' icon-rotate';
}
iconClass += ` ${props.className}`;
return (
<>
<span className="icon-border" />
<span className={iconClass}>
<i className="icon-arrow-down icon-rotate" />
</span>
</>
);
}}
autoWidth
iconBordered={iconBordered}
displayEmpty={displayEmpty}
onChange={onChange}
variant="outlined"
value={value}
>
{!!options
? options.map(({ value, label }) => (
<StyledMenuList disableRipple value={value}>
{label}
</StyledMenuList>
))
: children}
</StyledSelect>
</StyledFormControl>
);
}
export default Select;
Accessing the component:
<FormField>
<Left>
<Label>{t('search_pane.status')}</Label>
<Select
displayEmpty
menuClass="menu"
className="searchSelect"
value={chatState}
onChange={handleChatStatusChange}
options={Object.keys(CHAT_STATE_OPTIONS).map((el) => {
return { value: el, label: CHAT_STATE_OPTIONS[el] };
})}
/>
</Left>
<Right>
<Label>{t('search_pane.date')}</Label>
<Select
className="searchSelect"
menuClass="menu"
value={date}
onChange={handleDateChange}
options={Object.keys(DATE_OPTIONS).map((el) => {
return { value: el, label: DATE_OPTIONS[el] };
})}
/>
</Right>
</FormField>
Style of the MenuList:
export const StyledMenuList = styled(MenuItem)`
min-width: 125px;
margin-left: 6px;
margin-right: 6px;
padding-left: 8px;
font-size: ${({ theme }) => theme.font.sizes[500]};
color: ${({ theme }) => theme.colors.SECONDARY[600]};
transition: all 300ms ease;
transition-property: background-color, color;
&:focus {
background-color: ${({ theme }) => theme.colors.WHITE};
}
&:hover {
background-color: ${({ theme }) => theme.colors.PRIMARY[100]} !important;
color: ${({ theme }) => theme.colors.PRIMARY[600]};
border-radius: 4px;
}
`;
I want my min-width to be changeable from where I am calling the component
const FormField = styled.div`
display: flex;
margin-top: 18px;
margin-bottom: ${({ theme }) => theme.spacing[400]};
width: 100%;
.searchSelect {
width: 100%;
margin-top: 7px;
height: ${({ theme }) => theme.spacing[700]};
.MuiOutlinedInput-root {
height: ${({ theme }) => theme.spacing[700]};
}
.icon-container {
top: 22px;
}
li {
min-width: 192px;
}
}
`;
All the css changes takes effect except the li 192px can someone help?
I have tried passing a menuClass css etc but nothing has worked
EDIT:
I have also tried:
<StyledMenuList
classes={{ root: menuClass }}
disableRipple
value={value}
>
{label}
</StyledMenuList>
passing menuClass via props
Without a codesandbox it is a little harder, providing one would be of great help.
My suggest based on how material-ui Select works is that your li-MenuItems are not in the same DOM level as the .searchSelect element as your styling expects and that's why your styling is not getting applied.
Try to put it outside - something like this:
const FormField = styled.div`
...
.searchSelect {
width: 100%;
margin-top: 7px;
height: ${({ theme }) => theme.spacing[700]};
.MuiOutlinedInput-root {
height: ${({ theme }) => theme.spacing[700]};
}
.icon-container {
top: 22px;
}
}
li {
min-width: 192px;
}
`;
Related
I have use React-redux and styled components for my app. I store my initial state theme as a string which is light and dark. then I connect my styled components intial light theme and dark theme in my root app. My dark mood works fine when i used select options but when i used input checkbox it does not work. I never used input checkbox, after reading couple example I used checked and put my initial theme(which is coming from my redux store), then in my handleChange I did, if the event target has dark then dispatch the dark theme. But nothing happening in that handle change. don't know what i am doing wrong.
Here is my toggle component
import React, { useState } from 'react';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { appSettings } from '../../state/appSettings';
import { TRootState } from '../../state/index';
export default function Toggle({ }: IProp) {
const dispatch = useDispatch();
const { "appSettings": appSettingState } = useSelector((state: TRootState) => state);
const { theme } = appSettingState || {};
console.log(theme); // inital state which is "light".
return (
<>
{/* This input checkbox does not work */}
<CheckBoxWrapper>
<CheckBox
onChange={(e) => { // This function does not work
e.target.value === `dark` ?
dispatch(appSettings?.actions?.enableDarkTheme()) :
dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>
<CheckBoxLabel htmlFor="toggleSwitch" />
</CheckBoxWrapper>
<br></br>
{/* THIS SELECT OPTIONS WORK FINE. AND I CAN GET DARK AND LIGHT THEME */}
<h2>Theme</h2>
<select
name="theme"
id="theme-select"
value={theme}
onChange={(e) => {
if (e.target.value === `dark`) {
dispatch(appSettings?.actions?.enableDarkTheme());
} else {
dispatch(appSettings?.actions?.enableLightTheme());
}
}}
>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</>
);
}
// This toogle input styled
const CheckBoxWrapper = styled.div`
position: fixed;
top:10px;
right:10px;
`;
const CheckBoxLabel = styled.label`
position: absolute;
top: 0;
left: 0;
width: 42px;
height: 26px;
border-radius: 15px;
background: #bebebe;
cursor: pointer;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin: 3px;
background: #ffffff;
box-shadow: 1px 3px 3px 1px rgba(0, 0, 0, 0.2);
transition: 0.2s;
}
`;
const CheckBox = styled.input`
opacity: 0;
z-index: 1;
border-radius: 15px;
width: 42px;
height: 26px;
&:checked + ${CheckBoxLabel} {
background: #4fbe79;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin-left: 21px;
transition: 0.2s;
}
}
`;
Check the change event's checked property. e.target.checked
<CheckBox
onChange={(e: any) => {
e.target.checked
? dispatch(appSettings?.actions?.enableDarkTheme())
: dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>
So I have a simple navbar that when hits a certain media query it turns into a hamburger, and when I click on hamburger-icon, I want the new navbar element slide over. Here is how I have it setup.
Layout Component
const Layout = ({ children }) => {
const [isOpen, setIsOpen] = React.useState(false);
const toggleSidebar = () => {
setIsOpen(!isOpen)
}
return (
<>
<Navbar toggleSidebar={toggleSidebar} />
<Sidebar isOpen={isOpen} toggleSidebar={toggleSidebar}/>
{children}
<Footer />
</>
)
}
Sidebar Component
const SidebarAside = styled.aside`
background: ${props => props.theme.grey10};
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
display: grid;
place-items: center;
opacity: 0;
transition: ${props => props.theme.transition};
transform: translateX(-100%);
${props =>
props.showsidebar && css`
opacity: 1;
tranform: translateX(0);
`
}
`
const CloseButton = styled.button`
position: absolute;
right: 4.75%;
top: 2.75%;
font-size: 2.5rem;
background: transparent;
border-color: transparent;
color: ${props => props.theme.redDark};
cursor: pointer;
`
const Sidebar = ({isOpen, toggleSidebar,}) => {
return (
<SidebarAside showsidebar={isOpen}>
<CloseButton onClick={toggleSidebar}>
<FaTimes />
</CloseButton>
</SidebarAside>
)
}
First time using styled-components, and not totally sure what my angle for this should be.
Ultimately I figured it out, with just changing the syntax in the styled component to...
Sidebar Styled Component
opacity: ${props => props.showsidebar ? '1' : '0'};
transform: ${props => props.showsidebar ? 'translateX(0)' : 'translateX(-100%)'};
`
const Sidebar = ({isOpen, toggleSidebar,}) => {
return (
<SidebarAside showsidebar={isOpen}>
<CloseButton onClick={toggleSidebar}>
<FaTimes />
</CloseButton>
</SideBarAside>
I'm new to styled-components; I have a hamburger component and I'm trying to make a transition when the burger menu turns into an X using a styled-component.
I added a transition: all 0.3s linear to the div but I just can't figure out what is going wrong with this transition.
export default (props) => {
const StyledBurger = styled.div`
width: 2rem;
height: 2rem;
position: fixed;
top: 15px;
left: 20px;
z-index: 10;
display: none;
div {
transition: all 0.3s linear;
width: 2rem;
height: 0.25rem;
background-color: ${({ open }) => (open ? "#ccc" : "#333")};
border-radius: 10px;
transform-origin: 1px;
&:nth-child(1) {
transform: ${({ open }) => (open ? "rotate(45deg)" : "rotate(0)")};
}
&:nth-child(2) {
transform: ${({ open }) =>
open ? "translateX(100%)" : "translateX(0)"};
opacity: ${({ open }) => (open ? 0 : 1)};
}
&:nth-child(3) {
transform: ${({ open }) => (open ? "rotate(-45deg)" : "rotate(0)")};
}
}
`;
const [open, setOpen] = useState(false);
return (
<>
<StyledBurger open={open} onClick={() => setOpen(!open)}>
<div />
<div />
<div />
</StyledBurger>
</>
);
};
You have "display: none" in your StyledBurger. That's probably why you don't see children.
I don't know much about styled-components, I am using a toggle switch to change themes and my theme does switch from dark to light but the icons I'm using don't switch icons. The icons are valid, when I switch the order of the component the Moon icon shows only instead, I'm guessing this is something with syntax?
import React from 'react'
import { func, string } from 'prop-types';
import styled from 'styled-components';
import { ReactComponent as MoonIcon } from '../components/icons/moon.svg';
import { ReactComponent as SunIcon } from '../components/icons/sun.svg';
const ToggleContainer = styled.button`
background: ${({ theme }) => theme.gradient};
border: 2px solid ${({ theme }) => theme.toggleBorder};
border-radius: 30px;
cursor: pointer;
display: flex;
font-size: 0.5rem;
justify-content: space-between;
margin: 0 auto;
overflow: hidden;
padding: 0.5rem;
position: relative;
width: 8rem;
height: 4rem;
svg {
height: auto;
width: 2.5rem;
transition: all 0.3s linear;
// sun icon
&:first-child {
transform: ${({ lightTheme }) => lightTheme ? 'translateY(0)' : 'translateY(100px)'};
}
// moon icon
&:nth-child(2) {
transform: ${({ lightTheme }) => lightTheme ? 'translateY(-100px)' : 'translateY(0)'};
}
}
`;
const Toggle = ({ theme, toggleTheme }) => {
const isLight = theme === 'light';
return (
<ToggleContainer onClick={toggleTheme} >
<MoonIcon />
<SunIcon />
</ToggleContainer>
);
};
Toggle.propTypes = {
theme: string.isRequired,
toggleTheme: func.isRequired,
}
export default Toggle;
lightmode
darkmode
Add lightTheme={isLight} to this code
At: <ToggleContainer onClick={toggleTheme} >
Final: <ToggleContainer onClick={toggleTheme} lightTheme={isLight}>
Also, you can play with transform like below for switching between,
`&:first-child {
transform: ${({ lightTheme }) => lightTheme ? 'translateX(0px)' : 'translateX(-150px)'};
}
&:nth-child(2) {
transform: ${({ lightTheme }) => lightTheme ? 'translateX(100px)' : 'translateX(0px)'};
}`
I'm trying to customize a MUI TextField Select component with styled components.
The ideia is styled-compoents provide diferent classes to Select field and Menu, so i can have their styled separated.
const StyledSelect = styled(({ className, ...props }) => {
return (
<TextField {...props}
classes={{ root: className }}
SelectProps={{
MenuProps: {
classes: { paper: className, list: className },
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
transformOrigin: {
vertical: "top",
horizontal: "left"
},
getContentAnchorEl: null
},
}}
/>
)
})`
& {
background-color: #454D5D;
border-radius: 10px;
margin-top: 5px;
}
& li {
color: #FFF;
}
&.MuiFormControl-root {
background-color: transparent;
}
& .MuiListItem-root {
font-size: 18px;
}
& .MuiListItem-root.Mui-selected {
background-color: #1A2233;
}
& .MuiFormLabel-root {
font-family: 'Roboto';
font-weight: 300;
}
& .MuiInputLabel-shrink {
color: ${props => props.color};
font-weight: normal;
}
& .MuiInput-underline:after {
border-bottom: 2px solid ${props => props.errors[props.field.name] && props.touched[props.field.name]
? CASABLANCA : props.color};
transition: none;
transform: none;
}
& .MuiInput-underline:before {
border-bottom: 1px solid ${props => props.color}
}
& .MuiSelect-roo {
color: black;
font-family: 'Roboto';
font-weight: 300;
}
& .MuiSelect-select:focus {
background: transparent;
}
`;
I wish my TextField class would be different from MenuProps class
One way to solve this is to have one wrapper component per class name you need to generate. In my example below, StyledTextField takes care of the root class name for TextField (the className property is equivalent to classes.root) and then MenuPaperClass provides an additional class name.
import React from "react";
import ReactDOM from "react-dom";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import styled from "styled-components";
const StyledTextField = styled(TextField)`
/* && to add specificity */
&& {
border: 1px solid green;
}
`;
const MenuPaperClass = styled(({ className, ...props }) => {
return (
<StyledTextField
SelectProps={{ MenuProps: { classes: { paper: className } } }}
value="1"
select
{...props}
>
<MenuItem value="1">One</MenuItem>
<MenuItem value="2">Two</MenuItem>
<MenuItem value="3">Three</MenuItem>
</StyledTextField>
);
})`
&& {
background-color: lightblue;
}
`;
function App() {
return (
<div className="App">
<MenuPaperClass />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This isn't a particularly elegant solution and gets pretty tedious if you have 3 or more separate classes you want to use, so I may come back to this later to consider alternative approaches/syntax, but this does work.