React-select not following flexbox alignment - css

I am trying to make my navbar and I want to display both react-selects in one single row so I did this
Everything was working with vanilla select but adding the react-select breaks my styling
Navbar.js
import React, { useState } from "react";
import classes from "./Navbar.module.css";
import { useDispatch } from "react-redux";
import { selectAction } from "../../../store/actions/actions";
import Logo from "../../../images/BB_S5_excl_namelab_c.jpg";
import Select from 'react-select';
const CharacterOptions = [
{ value: '-----------', label: '-----------' },
{ value: 'Get character by id', label: 'Get character by id' },
{ value: 'Get character by category', label: 'Get character by category' },
{ value: 'Get random character', label: 'Get random character' },
{ value: 'Search character by name', label: 'Search character by name' },
];
const QuoteOptions = [
{ value: '-----------', label: '-----------' },
{ value: 'Get quote by id', label: 'Get quote by id' },
{ value: 'Get quotes by series', label: 'Get quotes by series' },
{ value: 'Get a random quote', label: 'Get a random quote' },
{ value: 'Get quote by author', label: 'Get quote by author' },
{ value: 'Get a random quote by author', label: 'Get a random quote by author' },
];
const Navbar = (props) => {
const dispatch = useDispatch();
const [selectedCharacter, setSelectedCharacter] = useState(false);
const [selectedQuote, setSelectedQuote] = useState(false);
const dropDownCharacterSelected = (value) => {
if (value !== "-----------") {
setSelectedCharacter(true);
dispatch(selectAction(value.value));
} else {
dropDownCharacterDeSelected(value.value);
dispatch(selectAction(null));
}
};
const dropDownCharacterDeSelected = () => {
setSelectedCharacter(false);
};
const dropDownQuoteSelected = (value) => {
if (value !== "-----------") {
setSelectedQuote(true);
dispatch(selectAction(value.value));
} else {
dropDownQuoteDeSelected(value.value);
dispatch(selectAction(null));
}
};
const dropDownQuoteDeSelected = () => {
setSelectedQuote(false);
};
return (
<nav className={classes.Navbar}>
<img src={Logo} alt="Logo" width="100" height="60" />
<div>
<ul>
<li>
<label>Characters</label>
<Select
disabled={selectedQuote}
onChange={dropDownCharacterSelected}
options={CharacterOptions}
/>
</li>
<li>
<label>Quotes</label>
<Select
disabled={selectedCharacter}
onChange={dropDownQuoteSelected}
options={QuoteOptions}
/>
</li>
</ul>
</div>
</nav>
);
};
export default Navbar;
Navbar.module.css
.Navbar{
display: flex;
height: 52px;
box-sizing: content-box;
background-image: linear-gradient(60deg, #29323c 0%, #485563 100%);
padding: 10px;
justify-content: center;
align-items: center;
}
.Navbar div ul {
display: flex;
}
.Navbar ul li {
display: flex;
list-style: none;
}
.Navbar label {
width: 100px;
border-radius:8px;
margin:10px;
background-color: lightgrey;
color:darkgreen;
font-weight: bold;
font-size: 20px;
padding: 10px;
}
Edit
Cant increase width of react-select
Here is my Codesandbox URL for your reference
https://codesandbox.io/s/youthful-gauss-r1d8o

You can achieve that by using flexbox to align your items in a row:
.Navbar div ul {
width: 100%;
display: flex; /* use flexbox to align the inputs on same row */
}
.Navbar ul li {
display: flex; /* use flexbox to align the labels on same row than the inputs */
align-items: center; /* align the items vertically */
list-style: none;
}
.Navbar ul li input {
min-width: 100px; /* specify react-select component's width */
}

Adding display: flex, will show both component in the same row:
.Navbar div ul {
...
display: flex;
...
}

Related

CSS hover applies to child elements (absolute positioned) also however I want it to affect only the parent container

I have a header with lots of navItems, for the last navItem, I have another expander div attached which also captures the hover effect of the parent div. How can I disable the pointer-events to none for this child absolute expander?
If I set pointer event of the absolute expander to none then the elements inside of expander also lose hover effect. (Note that they're also of same hover class).
Here's my JSX (HTML like template):
import React from 'react';
import classes from './NavItems.module.css';
import navItems from '#/constants/navItems.json';
import { BiChevronDown } from 'react-icons/bi';
import { useAppDispatch, useAppSelector } from '#/redux/hooks';
import { updateActiveSlice } from '#/slices/navigation.slice';
import { motion } from 'framer-motion';
const getActiveItem = (activeItem: string) => {
const result = navItems.find((element) => element.value === activeItem);
return result;
};
const moreItems = {
value: 'more',
label: 'More',
index: -1
};
const getItems = (activeItem: string) => {
const activeElement = getActiveItem(activeItem);
if (navItems.length > 5 && activeElement) {
if (activeElement.index > 3) return [...navItems.slice(0, 4), activeElement];
return [...navItems.slice(0, 4), moreItems];
}
return navItems;
};
export default function NavItems() {
const { activeSection } = useAppSelector((state) => state.navigation);
const dispatch = useAppDispatch();
const items = getItems(activeSection);
const activeItem = getActiveItem(activeSection);
return (
<div className={classes.NavItemContainer}>
{items.map((item, index) => {
const isLastItem = index === items.length - 1;
const isActive =
(activeItem ? activeItem.value : activeSection) === item.value;
return (
<h4
key={item.value}
className={[
classes.NavItem,
isLastItem ? classes.LastItem : null,
isActive ? classes.ActiveItem : null
].join(' ')}
onClick={() => {
// TODO: verify more click
dispatch(updateActiveSlice(item.value));
}}
>
{item.label}
{isLastItem && (
<React.Fragment>
<BiChevronDown className={classes.ShowMoreIcon} />
<motion.div
initial={{ y: 50 }}
animate={{ y: [-50, 20, 0] }}
className={classes.Expander}
>
<h4 className={classes.NavItem}>Rest</h4>
<h4 className={classes.NavItem}>Next Next Next</h4>
<h4 className={classes.NavItem}>Trust</h4>
<h4 className={classes.NavItem}>Rust</h4>
</motion.div>
</React.Fragment>
)}
</h4>
);
})}
</div>
);
}
and here's my CSS:
.NavItemContainer {
display: flex;
align-items: center;
color: var(--inactive-silver);
gap: 2rem;
}
.NavItem {
letter-spacing: 0.05em;
font-family: var(--raleway);
font-style: normal;
font-weight: 600;
font-size: 1.2rem;
line-height: var(--lineheight);
cursor: pointer;
transition: all 0.1s ease-in;
}
.NavItem:hover {
scale: 0.96;
}
.LastItem {
display: flex;
align-items: center;
position: relative;
}
.ActiveItem {
color: var(--active-blue);
}
.ShowMoreIcon {
font-size: 2rem;
color: var(--inactive-silver);
}
.Expander {
position: absolute;
top: 130%;
right: 5%;
width: max-content;
padding: 0 1rem;
background-color: var(--light-background-color);
box-shadow: var(--box-shadow-thick);
text-align: end;
border-radius: 0.4rem;
}
.Expander:hover {
scale: 1;
}

How can i make slider animation?

I need to make the vertical slider animation ( dots and line ) as in this pic
i managed to do the Accordion and the dots but i don't know how i will going to implement it ( i'm using pseudo )
**my accordion component Where i define the logic of my nested accordions as in images based on array of data **
function MultiLevelAccordion({
data,
bodyClass,
headerClass,
wrapperClass,
renderHeader,
renderContent,
}) {
const RootAccordionId = 'parent-0';
const [accordionsStates, setActiveCardsIndex] = useMergeState({});
const onAccordionToggled = (id, activeEventKey) => {
console.log(activeEventKey);
setActiveCardsIndex({
[id]: activeEventKey ? Number(activeEventKey) : activeEventKey
});
};
console.log('data', data);
const accordionGenerator = (data, parentId) => {
return map(data, (item, index) => {
const active = accordionsStates[parentId] === index;
const hasChildren = item.hasOwnProperty('children') && isArray(item.children) && !isEmpty(item.children);
const isRootAccordion = RootAccordionId === parentId;
const isLastNestedAccordion = !isRootAccordion && !hasChildren;
const accordion = (
<Card className={classNames(wrapperClass, {
'nested-root-accordion': !isRootAccordion,
'last-nested-root-accordion': isLastNestedAccordion,
'multi-level-accordion': !isLastNestedAccordion
})}
>
<Accordion.Toggle
{...{ ...item.id && { id: item.id } }}
onClick={() => this}
as={Card.Header}
eventKey={`${index}`}
className={'cursor-pointer d-flex flex-column justify-content-center'}
>
<div className="d-flex justify-content-between align-items-center">
{renderHeader(item, hasChildren)}
<img
style={{
transition: 'all .5s ease-in-out',
transform: `rotate(${active ? 180 : 0}deg)`
}}
src={setIcon('arrow-down')}
className="ml-2"
alt="collapse"
/>
</div>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body
className={`accordion-content-wrapper ${!hasChildren ? 'accordion-children-body' : ''} ${bodyClass}`}
>
{!hasChildren ? renderContent(item, hasChildren) : (
<Accordion onSelect={activeEventKey => onAccordionToggled(`${parentId}-${index}`, activeEventKey)}>
<Fade cascade top when={active}>
{accordionGenerator(item.children, `${parentId}-${index}`)}
</Fade>
</Accordion>
)}
</Card.Body>
</Accordion.Collapse>
</Card>
);
return isRootAccordion ? accordion : (
<div className={'d-flex align-items-center'}>
{accordion}
<div className="accordion-indicator-wrapper">
<div className="accordion-indicator" id={`indicator-${parentId}-${index}`} />
</div>
</div>
);
});
};
if (!isArray(data)) {
return;
}
return (
<Accordion onSelect={activeEventKey => onAccordionToggled(RootAccordionId, activeEventKey)}>
{accordionGenerator(data, RootAccordionId)}
</Accordion>
);
}
export default MultiLevelAccordion;
the styles used in scss
.faqs-questions-wrapper {
padding: 20px 10px
}
.faqs-q-count {
color: $black-color;
font-size: calc(1rem - 1rem/8)
}
.faqs-q-a-wrapper {
flex-basis: 95%;
}
.faq-child-title {
color: $black-color
}
.nested-root-accordion {
flex-basis: 90%;
}
.accordion-indicator-wrapper {
flex-basis: 10%;
width: 100%;
display: flex;
justify-content: center;
.accordion-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: $theme-color;
position: relative;
}
}
Any clue?
Thanks in Advance.
React JS is gonna make this easy
The lines expansion will need to be coded based on the height of the box window
For the dropdown container keep the vertical button lines in a separate div than the Accordian
Check out this pen for creating lines between buttons
https://codepen.io/cataldie/pen/ExVGjya
css part:
.status-container{
padding:10px;
margin:10px;
position:relative;
display: inline-block;
}
.bullet{
padding:0px;
margin:0px;
display: inline-block;
z-index: 10;
}
.bullet:before {
content: ' \25CF';
font-size: 5em;
}
.bullet-before{
/*position:relative;
right:-12px;*/
}
.bullet-after{
/*position:relative;
left:-30px;*/
}
.line{
stroke:blue;
stroke-width:0.3em;
padding:0px;
margin:0px;
display: inline-block;
}
.line-on{
stroke:red;
}
.line-off{
stroke:gray;
}
.color-on{
color: red;
}
.color-off{
color: gray;
}
https://codepen.io/emwing/pen/LgzJOx
I think you can use some inspiration here

How to remove right&left borders from table while keeping just the inside ones?

I have a custom table made in React with PostCSS which I not made (therefore is kind of confusing. I need to remove the left and right borders.
I want to remove both borders from the left and the right (in the image I managed to remove one (red <- ->) and just keep the inside ones.
Here is my postCSS code: (it's kind of a custom table)
.datatable-wrapper {
background : #ffffff;
min-height: 41rem;
.filter-column{
border-right: 1px solid var(--clear-grey);
}
.btn-filter_view{
cursor: pointer;
display: flex;
align-items: center;
}
>.container {
padding: .3rem .7rem .3rem .8rem;
}
.badge {
border: 1px solid;
border-radius: 4px;
display: inline-flex;
font-size: 0.75rem;
line-height: 1.125rem;
padding: 0.1875rem 0.6875rem 0.1875rem 0.6875rem;
width: -moz-fit-content;
width: fit-content;
}
.selected-items {
padding-left: 1.0625rem;
padding-right: 2.125rem;
height : 3.5rem;
border-radius: .25rem;
background-color: #0f4aa1;
font-size: 0.875rem;
font-weight: 500;
color: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
.actions{
>span:nth-child(2){
margin-left: 3.6875rem;
}
}
}
.hidden-selected-items {
display: none;
overflow: hidden;
transition: max-height 0.6s ease;
}
.search {
min-height : 2.75rem;
color : #9ea0a5;
font-size: .75rem;
vertical-align: middle;
/* border: 1px solid var(--clear-grey); */
margin-right: 5px;
.form-group {
min-width : 100%;
}
.form-control {
color : #9ea0a5;
font-size: .75rem;
min-width : 100%;
height : 100%;
}
.group-search{
width:100%;
padding-left: 1.6875rem;
>.form-group {
background:transparent;
>.input-group{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: center;
padding: .2rem;
width: 10.875rem;
}
}
}
>.search-info {
padding: .5rem;
.dropdown-toggle {
min-width : 3.5rem;
min-height : 1.8rem;
font-size: .75rem;
padding-right: 1.5rem;
}
.dropdown-item {
font-size: .75rem;
height : 1.8rem;
}
}
> [class^="col-xs-"] {
/* padding: 0.5625rem 1.6875rem 0.5625rem 1.6875rem; */
font-size: 0.875rem;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
align-content: stretch;
.input-group-append {
display: initial;
margin-left: -1rem;
}
>.uikon {
padding : .250rem;
}
}
}
.hidden-search {
display: none;
overflow: hidden;
transition: max-height 0.6s ease;
}
.filter {
color : #9ea0a5;
font-size: .75rem;
border-top: 1px solid var(--clear-grey);
border-radius: 4px;
padding: .5rem;
> [class^="col-xs-12"] {
>.filter-elements {
display: flex;
align-items: center;
>.form-group {
width: inherit;
margin: 0 .5rem 0 0;
.form-control {
color : #9ea0a5;
font-size: .75rem;
/* min-width : 100%; */
height : 100%;
width: 9.375rem;
}
>.btn {
position: relative;
float: right;
color : #9ea0a5;
font-size: .75rem;
}
}
.dropdown-toggle {
min-height : 1.8rem;
width: 9.375rem;
/* width: 100%; */
font-size: .75rem;
padding-right: 1.5rem;
}
.dropdown-item {
font-size: 0.875rem;
height: auto;
padding-bottom: 0.625rem;
padding-top: 0.625rem;
}
}
}
}
.hidden-filter {
display: none;
overflow: hidden;
transition: max-height 0.6s ease;
}
.head {
cursor: pointer;
border-top: 1px solid var(--clear-grey);
border-bottom: 1px solid var(--clear-grey);
min-height : 2.75rem;
color : #9ea0a5;
font-size: 0.75rem;
vertical-align: middle;
width: 100%;
> [class^="col-xs-"] {
font-size: 0.75rem;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
align-content: stretch;
/* border-left: 1px solid var(--clear-grey); */
border-right: 1px solid var(--clear-grey);
padding-left: 0.93125rem;
}
}
.body {
/* min-height : 4.375rem; */
color : #3e3f42;
font-size: .75rem;
vertical-align: middle;
width: 100%;
> [class^="col-xs-"] {
border-right: 1px solid var(--clear-grey);
border-bottom: 1px solid var(--clear-grey);
padding-top: 1.5rem;
font-size: 0.875rem;
padding-bottom: 1.5rem;
/* width: 7.99375rem; */
display: flex;
flex-direction: row;
flex-wrap: nowrap;
/* justify-content: flex-start; */
padding-left: 0.93125rem;
align-items: center;
align-content: flex-start;
}
.check {
min-height : 2.75rem;
/* border: 1px solid var(--clear-grey); */
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
align-content: flex-start;
}
}
.datatable {
min-width: 100%;
padding-right: 2.25rem;
}
}
Here is my .jsx code (only where the table is implemented):
// #flow
import * as React from 'react';
import classnames from 'classnames';
import { t as typy } from 'typy';
import { Button, Checkbox, TextField, Select } from '#duik/it'
import Icon from '#duik/icon'
import arrowdown from '__/images/arrowdown.svg';
import { Grid, Row, Col, } from 'react-flexbox-grid';
import { Formik, Field } from 'formik';
import { DUIKSelectDate, DUIKSelect, DUIKTextField } from 'produceUI';
import { Pagination } from 'produceUI/atoms';
type Props = {
/**
* A simple definition of each column, so we know what will be displayed and how.
* This requires an 'elementKey', which is they key to be used on each element of
* the 'data' list to get and display a value. In case an 'elementKey' is not given,
* or the 'elementKey' points to something different than a number or a string
* 'renderCell' should be present. */
columns: {
className?: string,
elementKey?: string,
title?: string,
invisible?: boolean,
filterable?: boolean,
sortable?: boolean,
width?: number,
selectable?: boolean,
cols?: number,
capitalLetters: Boolean,
renderCell?: (element: Object, rowIndex: number, key: string) => React.Node,
showDetail?: (element: Object, rowIndex: number, key: string) => React.Node,
}[],
/** Filter the data elements with this field */
filter?: string | number,
/** An array of JSON Objects. Nesting objects is allowed. */
placeholderSearch?: string,
filteredLabelForMany?: string,
filteredLabelForOne?: string,
data: { [string]: any },
/** Every row needs an unique identifier. What is the key for it? */
dataID?: string,
/** Should it have checkboxes? */
selectable?: boolean,
/** Functional component to be rendered on the bottom of the table when
* "selectable" is true AND when there is at least one selected row. The selected
* elements are passed as a parameter so this logic is managed from the parent component.
*/
actionsForSelected?: (elements: { [string]: any }) => React.Node,
/** A React element to be rendered in case the data is empty */
renderEmpty?: React.Node,
totalPages?: number,
currentPage?: number,
pagesLimit?: number,
onChangePage?: (page: number) => void,
idToShow?: number,
onClickDelete?: (ids: []) => void,
handleSort?: (elementKey: string) => void,
debounceOnSearch?: (textToFilter: string) => void,
onFilter?: (values: object) => void,
onClickFilter?: () => void,
shouldFilter?: boolean,
shouldSearch?: boolean,
};
function getID(element, key): string | number {
const value = typy(element, key);
return value.isString
? value.safeString
: value.isNumber
? value.safeNumber
: 0;
}
const Table = (props: Props) => {
const {
columns = [], data = [],
placeholderSearch, filter, selectable,
filteredLabelForMany, filteredLabelForOne,
renderEmpty, actionsForSelected,
dataID, idToShow,
totalPages, currentPage, pagesLimit,
onChangePage, onClickDelete, debounceOnSearch,
handleSort, onFilter, onClickFilter,
shouldFilter, shouldSearch
} = props;
const [selected, setSelected] = React.useState([]);
const [dataToShow, setDataToShow] = React.useState(data);
const [selectedId, setSelectedId] = React.useState(0);
const [selectAll, setSelectAll] = React.useState(false);
const [activeOption, setActiveOption] = React.useState()
const convertPagesToArrayPages = () => {
let idx = 1;
let tmpArray = [];
while (idx <= totalPages) {
tmpArray.push({ label: `${idx}`, value: idx++ });
}
return tmpArray;
};
const arrayPages = convertPagesToArrayPages();
React.useEffect(() => {
const ids = data.map(d => getID(d, dataID));
const newSelected = selected.filter(s => ids.indexOf(getID(s, dataID)));
setDataToShow(data);
setSelected(newSelected);
}, [JSON.stringify(data), dataID]); // This is because the data variable is always a new one.
React.useEffect(() => {
setSelectedId(idToShow);
}, [idToShow]); // This is because the data variable is always a new one.
const filterKeys = columns.filter(col => (col.filterable)).map(col => (col.elementKey));
const initialFilterFields = () => {
let values = {};
columns.map((item, index) => {
if (item.filterable) {
if (item.type === 'select') {
values[item.elementKey] = item.activeElinSelect;
} else
values[item.elementKey] = '';
}
});
return values;
};
const renderHeaders = () => {
return (
<React.Fragment>
<Row className={selected.length > 0 ? "selected-items" : "hidden-selected-items"}>
<Col xs={10}>
{`${selected.length} ${selected.length > 1 ? filteredLabelForOne : filteredLabelForMany} Seleccionada(s) `}
</Col>
<Col xs={1}>
{`Descargar `}
</Col>
<Col xs={1}>
<a onClick={(ev) => onClickDelete(selected)} href="#"><span>Rechazar</span></a>
</Col>
</Row>
<Row className={shouldFilter ? "filter" : "hidden-filter"}>
<Col xs={12} >
<Formik
initialValues={initialFilterFields()}
render={({ values }) => {
return (
<div className='filter-elements'>
{columns.map((item, index) => {
if (item.filterable) {
return (
item.type === 'select' ?
<div className="form-group" key={`selecteFilterField_${item.elementKey}`}>
<Field
activeOption={item.activeElinSelect}
options={item.data}
id={item.elementKey}
name={item.elementKey}
placeholder={item.title}
component={DUIKSelect}
/>
</div>
: item.type === 'date'
?
<div className="form-group" key={`datedFilterField_${item.elementKey}`}>
<Field
placeholder={item.title}
format="dd/mm/yyyy"
id={item.elementKey}
name={item.elementKey}
type='text'
component={DUIKSelectDate}
/>
</div>
:
<Field
key={`textFilterField_${index}`}
placeholder={item.title}
id={item.elementKey}
name={item.elementKey}
type='text'
component={DUIKTextField}
/>
);
}
})
}
<div className="form-group">
<Button onClick={() => {
/* onFilter(values); */
/*
if(status !== null || PF !== null){}
*/
}}>
Filtrar
</Button>
</div>
</div>
)
}}
>
</Formik>
</Col>
</Row>
{/*Fila de filtro, búsqueda, etc.*/}
<Row className={shouldSearch ? "search" : "hidden-search"}>
{/* Filtro */}
<Col className='filter-column' xs={1} onClick={() => { onClickFilter() }}>
<span className='btn-filter_view'><Icon mr>view_list</Icon>Filtrar</span>
</Col>
{/* Búsqueda */}
<Col xs={3}>
<div className='group-search'>
<TextField
placeholder={placeholderSearch}
rightEl={<Icon mr>search_left</Icon>}
onChange={e => {
debounceOnSearch(e.target.value);
}}
/>
</div>
</Col>
<Col xs={5} xsOffset={3} className='search-info'>
<span>
{`${dataToShow.length <= 0 ? 'No existen datos ' : dataToShow.length} ${dataToShow.length === 1 ? filteredLabelForOne : filteredLabelForMany}`}
</span>
<Pagination
pages={totalPages}
currentPage={currentPage}
limit={pagesLimit}
onChange={onChangePage}
/>
<b>Ir a página</b>
<Select
activeOption={arrayPages.length > 0 ? arrayPages[0] : { label: 'Seleccione opcion', value: 0 }}
defaultOption={arrayPages.length > 0 ? arrayPages[0] : null}
options={arrayPages}
/>
</Col>
</Row>
<Row className='head'>
{/* Checkbox maestro */}
<Col xs={1}>
{selectable ?
<Checkbox
value={selectAll}
onChange={(ev) => {
const checked = typy(ev, 'target.checked').safeBoolean;
if (!checked) setSelected([]);
else {
let tmp = [];
dataToShow.map((item, index) => {
tmp.push(item.id);
})
setSelected(tmp);
}
setSelectAll(checked);
}}
/> :
null
}
</Col>
{columns.map((item, index) => {
return (
<Col xs={item.cols} key={`col_${index}`}
onClick={
data.length > 2 ? () => handleSort(item.elementKey) : null
}>
{item.title}
</Col>
)
})
}
</Row>
</React.Fragment>
);
};
const shouldShowRow = (element) => {
if (filter) {
return filterKeys.some((key) => {
const field = typy(element, key);
if (field.isNumber) return `${field.safeNumber}`.includes(`${filter}`);
if (field.isString) return field.safeString.includes(filter);
return false;
});
}
return true;
};
const renderBody = () => dataToShow.reduce((accum: any[] = [], rowData, rindex) => {
if (!shouldShowRow(rowData)) return accum;
const cols = columns.map((col, cindex) => {
const renderer = col.renderCell
? col.renderCell
: (element, rowIndex, key) => {
const field = typy(element, key);
if (field.isNumber) return field.safeNumber;
if (field.isString) return col.capitalLetters ? field.safeString.toUpperCase() : field.safeString;
return undefined;
};
const content = (
<Col xs={col.cols}
style={{ justifyContent: col.position }}
key={cindex}
>
{renderer(rowData, rindex, typy(col, 'elementKey').safeString)
}
</Col>
);
return content;
});
const eid = getID(rowData, dataID);
const checkboxCell = selectable
? (
<Col xs={1} className='check'>
<div className="icon-arrow">
<img
onClick={() => {
const checked = true;
setSelectedId(eid === selectedId ? 0 : eid);
}}
src={arrowdown}
className={eid === selectedId ? 'arrow_up' : 'arrow_down'}
/>
</div>
<Checkbox
id={`check-${rindex}`}
name={`check-${rindex}`}
value={selected.indexOf(eid) > -1}
checked={selected.indexOf(eid) > -1}
onChange={(ev) => {
const checked = typy(ev, 'target.checked').safeBoolean;
if (!checked) setSelected(selected.filter(s => s !== eid));
else if (selected.indexOf(eid) === -1) setSelected([...selected, eid]);
}}
/>
</Col>
)
: undefined;
return [
...accum,
(<Row className='body' key={rindex}>{checkboxCell}{cols}
<div className='panel'>
<div className={eid === selectedId ? 'showed-panel' : 'hidden-panel'}>
{
rowData.detailComponent
? (<rowData.detailComponent />)
: ''
}
</div>
</div>
</Row>),
];
}, []);
let content = null;
let pagination = null;
if (data && data.length > 0) {
content = (
<Grid fluid>
{renderHeaders()}
{renderBody()}
</Grid>
);
} else {
content = (
<Grid fluid>
{renderHeaders()}
{renderEmpty}
</Grid>
);
}
return (
<div className='datatable-wrapper'>
{content}
{pagination}
<div className={classnames('datatable-actions')}>
{actionsForSelected && selected.some(t => t)
? actionsForSelected(data.filter(d => selected.indexOf(getID(d, dataID)) > -1))
: null
}
</div>
</div>
);
};
Table.defaultProps = {
filter: undefined,
selectable: false,
renderEmpty: undefined,
actionsForSelected: undefined,
dataID: 'id',
placeholderSearch: 'Buscar',
filteredLabelForMany: 'Elementos',
filteredLabelForOne: 'Elemento',
idToShow: 0,
};
export default Table;

Styling ReactSelect with CSS-in-JS

Here is a link to another good stackoverflow post that I am basing my initial answer on. I am attempting to style my ReactSelect component so that it looks something like this:
It's not obvious from the screenshot above, but the select box has a much smaller height (29 pixels total) than the default ReactSelect, and that's my first objective with styling (reduce the height). Here is my code right now attempting to reduce the height, derived mainly from the stackoverflow post i've linked to:
const customStyles = {
control: base => ({
...base,
height: 22,
minHeight: 22
})
};
const customControlStyles = base => ({
...base,
height: 22,
minHeight: 22
});
const selectOptions = [
{ value: 'pct', label: 'FG Percentage' },
{ value: 'attFreq', label: 'FG Frequency' },
{ value: 'made', label: 'Total Makes' },
{ value: 'att', label: 'Total Attempts' }];
const shotdistSelect =
(<Select
// arrowRenderer={null}
maxMenuHeight={30}
placeholder={'Knickerbockers'}
isClearable={false}
isDisabled={this.props.loading}
backspaceRemovesValue={false}
isSearchable={true}
value={this.state.shotdistType}
onChange={this.handleShotdistChange}
options={selectOptions}
styles={{ control: customControlStyles }}
// styles={{ customStyles }}
/>);
And here's the result of the example above:
... not exactly what I was going for. Further, when I use customStyles rather than customControlStyles in the example above, the styling no longer works, and I am not sure what I've done wrong in creating customStyles that is causing it to not work. I figure I will need to do something similar to customStyles, as it seems I'll need to style more than just the control part of ReactSelect.
And 2nd, I'd like to remove both the vertical bar and down caret in the ReactSelect, similar to the initial screenshot.
Any help with this styling would be greatly appreciated!! I've been working on this for quite some time with no success yet. Thanks!
Option 1
Make sure you're using the most up-to-date version of react-select (v2.3.0). I was able to accomplish what you want by using a bit of CSS with the style keys offered by react-select.
Working example: https://codesandbox.io/s/7y6901y950
containers/Form/Form.js
import React, { Component } from "react";
import CustomSelect from "../../components/CustomSelect/CustomSelect";
const fgOptions = [
{ value: "pct", label: "FG Percentage" },
{ value: "attFreq", label: "FG Frequency" },
{ value: "made", label: "Total Makes" },
{ value: "att", label: "Total Attempts" }
];
const saveOptions = [
{ value: "pct", label: "Save Percentage" },
{ value: "sFreq", label: "Save Frequency" },
{ value: "tSaves", label: "Total Saves" }
];
const assistOptions = [
{ value: "pct", label: "Assist Percentage" },
{ value: "aFreq", label: "Assist Frequency" },
{ value: "tAssist", label: "Total Assists" }
];
export default class Form extends Component {
handleChange = (name, value) => {
this.setState({ [name]: value });
};
handleSubmit = e => {
e.preventDefault();
alert(JSON.stringify(this.state, null, 4));
};
render = () => (
<form onSubmit={this.handleSubmit} className="app-container">
<h1>React Select Styling</h1>
<CustomSelect
name="fg"
label="FG:"
placeholder="Field Goals"
handleChange={this.handleChange}
selectOptions={fgOptions}
/>
<CustomSelect
name="assists"
label="AS:"
placeholder="Assists"
handleChange={this.handleChange}
selectOptions={assistOptions}
/>
<CustomSelect
name="saves"
label="SV:"
placeholder="Saves"
handleChange={this.handleChange}
selectOptions={saveOptions}
/>
<button type="submit" className="submit">
Submit
</button>
</form>
);
}
components/CustomSelect/CustomSelect.js
import React from "react";
import PropTypes from "prop-types";
import Select from "react-select";
import { labelStyles, selectStyles } from "./styles/styles";
const CustomSelect = ({
handleChange,
label,
name,
placeholder,
selectOptions,
value
}) => (
<div className="select-container">
<label htmlFor={name} style={labelStyles}>
{label}
</label>
<Select
name={name}
placeholder={placeholder}
isClearable={false}
backspaceRemovesValue={false}
isSearchable={true}
value={value}
onChange={value => handleChange(name, value)}
options={selectOptions}
styles={selectStyles}
/>
</div>
);
CustomSelect.propTypes = {
handleChange: PropTypes.func.isRequired,
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
placeholder: PropTypes.string.isRequired,
selectOptions: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
})
),
value: PropTypes.objectOf({
value: PropTypes.string,
label: PropTypes.string
})
};
export default CustomSelect;
components/CustomSelect/styles/styles.js (see documentation for style keys -- if you add longer labels, then you must adjust labelStyles width property; otherwise, the label to select ratio will vary)
export const selectStyles = {
option: (provided, state) => ({
...provided,
borderBottom: "1px dotted pink",
color: state.isSelected ? "blue" : "",
fontSize: 16,
backgroundColor: state.isSelected ? "#eee" : "",
textAlign: "left",
cursor: "pointer"
}),
container: base => ({
...base,
width: "100%"
}),
control: base => ({
...base,
height: 32,
minHeight: 32,
fontSize: 16,
borderRadius: 0,
width: "100%",
textAlign: "left",
cursor: "pointer"
}),
dropdownIndicator: base => ({
...base,
display: "none"
}),
indicatorSeparator: base => ({
...base,
display: "none"
}),
valueContainer: base => ({
...base,
padding: 0,
paddingLeft: 2
})
};
export const labelStyles = {
fontSize: 16,
paddingTop: 8,
marginRight: 5,
width: 50,
textAlign: "right"
};
styles.css
.app-container {
padding: 0px 20px;
text-align: center;
font-family: sans-serif;
}
.select-container {
display: flex;
margin: 0 auto;
width: 100%;
max-width: 500px;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-flex: 1;
margin-bottom: 10px;
}
.submit {
cursor: pointer;
background-color: #1e87f0;
color: #fff;
border: 1px solid transparent;
box-sizing: border-box;
padding: 0 30px;
vertical-align: middle;
font-size: 14px;
line-height: 38px;
text-transform: uppercase;
transition: 0.1s ease-in-out;
transition-property: color, background-color, border-color;
}
.submit:hover {
background-color: #0f7ae5;
color: #fff;
}
.submit:focus {
background-color: #1e87f0;
color: #fff;
outline: none;
}
Option 2
Or, you can do everything without using react-select, which I'd highly recommend as it excludes yet another dependency! As such, you have the option to style it as you wish (entirely through css, entirely through css-in-js or a combination).
Working example: https://codesandbox.io/s/w72k49nn27 (this example only uses css)

Having trouble getting rid of the blue highlight

I've been working on a section with expandable/collapsible sections. When I click on a section to expand or collapse it, a blue focus area shows up but it is placed on a weird angle. I don't know what is causing it and would like a solution to either get rid of it or place it back at the normal horizontal angle. Does anybody have any suggestions as to how to fix this?
I am using a Macbook and Chrome browser.
The entire grey block that this component appears in is placed at an angle as you can see from the top of the image attached below but in the reverse direction from the highlighted focus area.
My css:
#import '../../theme/variables.css';
.rotatedSection {
padding-bottom: 2rem;
}
.container {
max-width: 64rem;
margin: 0 auto;
display: flex;
padding: 2rem 0;
#media screen and (max-width: 68rem) {
margin: 0 3rem;
}
}
.accordianContainer {
flex: 1;
margin-right: 2rem;
min-width: 500px;
#media screen and (max-width: $tablet-lg-max-width) {
margin-right: 0;
}
#media screen and (max-width: 900px) {
min-width: 0;
}
}
.imageContainer {
flex: 1;
margin-left: 2rem;
max-height: 300px;
display: flex;
justify-content: center;
img {
flex: 1;
}
#media screen and (max-width: $tablet-lg-max-width) {
margin-left: 0;
}
}
.heading {
composes: h2 from 'theme/text';
margin-left: auto;
margin-right: auto;
}
My react code:
import React, {Component, PropTypes} from 'react';
import RotatedSection from 'components/RotatedSection';
import AccordionItem from './AccordionItem';
import css from './styles.css';
class AccordionSectionWithImage extends Component {
constructor (props) {
super(props);
this.state = {
activeIndex: null,
};
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.setActive = this.setActive.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
onOpen = (index) => {
this.setActive(index);
};
onClose = (callback = () => null) => {
this.setActive(null);
callback();
};
setActive = (activeIndex) => this.setState({activeIndex});
handleClickOutside = () => this.props.collapseOnBlur && this.onClose();
render () {
const {
entry: {
items,
heading,
image,
},
showIndex,
classNames,
meta = {},
} = this.props;
const {routeParams, toggleHamburger} = meta;
const {activeIndex} = this.state;
return (
<RotatedSection color='whisper' className={css.rotatedSection}>
<div className={css.container}>
<div className={css.accordianContainer}>
<h2 className={css.heading}>{heading}</h2>
{items && items.map((item, index) => (
<AccordionItem
key={index}
showIndex={showIndex}
entry={item}
meta={{
position: index,
isOpen: (index === activeIndex),
onOpen: () => this.onOpen(index),
onClose: () => this.onClose(),
onChildClick: () => this.onClose(toggleHamburger),
routeParams,
}}
classNames={classNames}
/>
))}
</div>
<div className={css.imageContainer}>
<img src={image && image.fields && image.fields.file.url} alt='Educational assessment' />
</div>
</div>
</RotatedSection>
);
}
}
AccordionSectionWithImage.propTypes = {
meta: PropTypes.object,
entry: PropTypes.object,
collapseOnBlur: PropTypes.bool,
showIndex: PropTypes.bool,
classNames: PropTypes.object,
};
export default AccordionSectionWithImage;
React component for individual section:
function AccordionItem (props) {
const {
meta: {
isOpen,
onOpen,
onClose,
},
entry: {
heading,
text,
},
} = props;
const handleClick = () => (isOpen ? onClose() : onOpen());
return (
<div className={css.itemContainer}>
<div className={css.innerContainer}>
<h3 className={css.heading} onClick={handleClick}>
<span className={css.titleText}>{heading}</span>
<i className={`zmdi zmdi-plus ${css.titleToggle}`} />
</h3>
{isOpen && (
<div className={css.contents}>
{text}
</div>
)}
</div>
</div>
);
}
For anybody else experiencing a similar problem:
Problem only appeared on mobile phones and the device mode of chrome inspector. It was due to the tap-highlight property.
Setting -webkit-tap-highlight-color to rgba(0,0,0,0) hid the problem but it's a non standard css property so the solution may not work for all devices/browsers/users.

Resources