Option box width size change in react-select - css

I would like to change react-select option box width.
example here=>
If my content is larger than the option, its show horizontal scroll but I don't want horizontal scroll. How can I change the size of option box width?
Another thing is How can I show the selected value as CscID one even option box is showing CscID + CscDesc? Now when I select the option, its CscID + CscDesc is showing in the selected box.
Here is my Select =>
const formatOptionLabel = ({ CscID, CscDesc }) => (
<div style={{ display: "flex"}}>
<div>{CscID}</div>
<div>{CscDesc}</div>
</div>
);
const customStyles = {
control: styles => ({ ...styles, }),
option: (styles) => {
return {
...styles,
width: '10000px', //For testing
};
},
};
<Select
styles={customStyles}
formatOptionLabel={formatOptionLabel}
getOptionValue={option =>
`${option.CscID}`
}
options={datasource}
/>

Yes, I can edit the width of option box.
const customStyles = {
control: styles => ({ ...styles,
}),
option: styles => ({ ...styles,
}),
menu: styles => ({ ...styles,
width: '500px'
})
};
According to the documentation, Its called menu. If you want to update the other style, check here=> Style Keys
And this option is super helpful for inspecting the menu box => menuIsOpen={true}

You normally don't change width of the wrapping element in your use case. As the text limit can be any so you wont set the width of select according to the text but the text according to your select's width. Meaning the select will have a fixed width and the text exceeding the the fixed width must be converted to ellipsis through css like
.options-select {
overflow: ellipsis
}
Ellipsis on hover must show the fullname in a tooltip for better UX.

For re-size problem, you may refer to this existing answer: React Select auto size width
For second problem, I don't think it is possible by using react-Select.
If you go to github and look further into the source code of react-Select, you may find that the ValueType of value property is expected to be same as OptionsType of options.
To be precise, the code is "export type ValueType = OptionType | OptionsType | null | void;", which could be found in react-select/packages/react-select/src/types.js
For your convenience, this is the link towards its source code, which can help you figure out more hidden behaviors: https://github.com/JedWatson/react-select/tree/master/packages/react-select/src

Related

MUI How to make a child dropdown stretch to fill a table-cell parent element

I have a parent element with a display of table-cell, and a child dropdown element that I want to stretch to fill the available space within the parent element. I've tried setting the child element's width to 100%, or playing with the display/positioning but it's not filling the entire space.
Here's a sandbox of what I'm trying to do:
https://codesandbox.io/s/serene-hermann-b61vwr?file=/src/App.js
as you can see the dropdown size is not the same as the table cell and it looks weird, how can I fix it?
The issue is the table header cells have padding which doesn't work well with your dropdowns. You essentially need to apply a different style to the table cell headers that contain dropdowns.
You may find a more elegant solution with your code but this should help you get started. Now the dropdowns will take the full-width of their cells. Here's an example:
Cloned from StyledTableCell:
const StyledTableDropdownCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.primary.main,
color: theme.palette.common.white,
borderBottom: 0,
border: "1px solid black",
align: "center",
padding: 0 // <--- No more table header cell padding
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14
}
}));
table.js
<StyledTableRow>
{tableHeadData.map(({ label, align, rowSpan, colSpan }, index) => {
console.log({ label, index });
if ([0,1].includes(index)) { // <--- this could be done better
return (
<StyledTableDropdownCell
key={index}
align={align}
rowSpan={rowSpan}
colSpan={colSpan}
>
{label}
</StyledTableDropdownCell>
);
}
return (
<StyledTableCell
key={index}
align={align}
rowSpan={rowSpan}
colSpan={colSpan}
>
{label}
</StyledTableCell>
);
})}
</StyledTableRow>

Change the default position of options in MUI

I am using MUI for UI.
I have some options, given in image below:
It is perfect till now. But I want to move options container to more bottom. (Currently it is set as top: 64px which is default).
I want to set it as top : 100px. After setting, it will look like :
I have tried to achieve this, but css didn't apply.
const useStyles = makeStyles((theme) => ({
userAge: {
"& .Mui-focused": {
"body .MuiPaper-root": {
top: "100px !important" // 64px is default
}
}
}
}));
//jsx return
<FormControl fullWidth className={classes.userAge}>
// more code here
</FormControl>
You can also try here : https://codesandbox.io/s/funny-shannon-h6flch?file=/demo.tsx
Note :
I don't want to set top : 100px globally.
Made some changes to your codesandbox. Hope it helps ;)
Styles need to be applied to menu via MenuProps and then applying class based styles to paper
https://codesandbox.io/s/change-the-default-position-of-options-in-mui-stackoverflow-lm9mxn?file=/demo.tsx

filter icon in material-ui's v5 DataGrid

The default behavior for the new DataGrid is to hide a filter icon unless you hover over the column header (and have a filter applied). In the previous version the icon remained visible.
Codesandbox https://codesandbox.io/s/mui-datagrid-filter-icon-7rbrk
When a filter is applied it adds a new iconButtonContainer div. The classes are: MuiDataGrid-iconButtonContainer css-ltf0zy-MuiDataGrid-iconButtonContainer
Is there a way to override this behavior? All I'd like to do is set visibility to always be visible when that div is generated by the library.
The answer here was to create a separate styled component of the data grid and use the global classnames imported from mui to reference the correct one for the style you wish to override. In my case it was something like:
const MyStyledGrid = styled(DataGrid, () => ({
[`& .${gridClasses.iconButtonContainer}`] : {
visibility: "visible",
width: "auto"
}
}))
function MyComponent() {
return (
<MyStyledDataGrid {...props} />
)
}

css transform: translate transition behaving strangely

On this sandbox, I've recreated the classic sliding-puzzle game.
On my GameBlock component, I'm using a combination of css transform: translate(x,y) and transition: transform in order to animate the sliding game-pieces:
const StyledGameBlock = styled.div<{
index: number;
isNextToSpace: boolean;
backgroundColor: string;
}>`
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: ${BLOCK_SIZE}px;
height: ${BLOCK_SIZE}px;
background-color: ${({ backgroundColor }) => backgroundColor};
${({ isNextToSpace }) => isNextToSpace && "cursor: pointer"};
${({ index }) => css`
transform: translate(
${getX(index) * BLOCK_SIZE}px,
${getY(index) * BLOCK_SIZE}px
);
`}
transition: transform 400ms;
`;
Basically, I'm using the block's current index on the board in order to calculate it's x and y values which change the transform: translate value of the block when it's being moved.
While this does manage to trigger a smooth transition when sliding the block to the top, to the right and to the left - for some reason, sliding the block from top to bottom doesn't transition smoothly.
Any ideas what's causing this exception?
React, lists and keys
What you're seeing is the result of a mount/unmount of the <GameBlock /> components.
Although you're passing a key prop to the component, React is unsure that you're still rendering the same element.
If I have to guess why react is uncertain, I would put the culprit at:
Changing the array sort with:
const previousSpace = gameBlocks[spaceIndex];
gameBlocks[spaceIndex] = gameBlocks[index];
gameBlocks[index] = previousSpace;
having different virtual DOM results using the conditional on isSpace:
({ correctIndex, currentIndex, isSpace, isNextToSpace }) => isSpace ? null : ( <GameBlock ....
Usually in applications, we don't mind a re-mount since it's pretty fast. When we attach an animation, we don't want any re-mounts since they mess with the css-transitions.
in order for react to be certain it's the same node and no re-mount is needed. we should take care that; between renders; the virtual dom stays mostly the same.
we can achieve that not doing anything fancy in the render of the list, and passing down the same keys between renders.
Pass isSpace down
Instead of changing the the rendered DOM nodes, we want the list render to always return an equal amount of nodes, with the exact same keys for each Node, in the same order.
simply passing 'isSpace' down and styling as display:none; should do the trick.
<GameBlock
...
isSpace={isSpace}
...
>
const StyledGameBlock = styled.div<{ ....}>`
...
display: ${({isSpace})=> isSpace? 'none':'flex'};
...
`;
Making sure to not change the arraysort
React considers the gameBlocks array to be modified, the keys are in a different order. Thus triggering unmount/mount of the rendered <GameBlock/> components.
We can make sure that react considers this array to be unmodified, by only changing the properties of the items in the list and not the sort itself.
in your case, we can leave all properties as is, only changing the currentIndex for the blocks that are moved/swapped with each other.
const onMove = useCallback(
(index) => {
const newSpaceIndex = gameBlocks[index].currentIndex; // the space will get the current index of the clicked block.
const movedBlockNewIndex = gameBlocks[spaceIndex].currentIndex; // the clicked block will get the index of the space.
setState({
spaceIndex: spaceIndex, // the space will always have the same index in the array.
gameBlocks: gameBlocks.map((block) => {
const isMovingBlock = index === block.correctIndex; // check if this block is the one that was clicked
const isSpaceBlock =
gameBlocks[spaceIndex].currentIndex === block.currentIndex; // check if this block is the space block.
let newCurrentIndex = block.currentIndex; // most blocks will stay in their spot.
if (isMovingBlock) {
newCurrentIndex = movedBlockNewIndex; // the moving block will swap with the space.
}
if (isSpaceBlock) {
newCurrentIndex = newSpaceIndex; // the space will swap with the moving block
}
return {
...block,
currentIndex: newCurrentIndex,
isNextToSpace: getIsNextToSpace(newCurrentIndex, newSpaceIndex)
};
})
});
},
[gameBlocks, spaceIndex]
);
...
// we have to be sure to call onMove the with the index of the clicked block.
() => onMove(correctIndex)
The only things we've changed are is the currentIndex of the clicked block and the space.
sandbox:
sandbox example based on your provided sandbox.
closing thoughts: I think your code was easy to read and understand, good job on that!
Additionally to the excellent answer and explanations #Lars provided, I wanted to share visual proof that certain <GameBlock /> components are indeed unmounted or changed in order, causing the hiccup in the CSS animation.
As you can see, when focussing one of the blocks and sliding down, the element changes its position in the DOM.

position absolute - float within screen limits (React)

I'm trying to create an App with a global dictionary; so that when a word that appears in the dictionary is hovered than a small box appears next to it with a definition.
The problem is that the text in the dictionary can appear any where on the screen, and I need to align the floating box so that it will not be displayed out side of the screen
Similar to this
only that I need to be able to style the floating box, like this
Note that the box display outside of the screen:
I tired to use ui material ToolTip
but it throws
TypeError
Cannot read property 'className' of undefined
I solved a similar problem before with jQuery, where I dynamically calculated the position of the box, relative to the screen and the current element.
but I don't know how to do it in react, mainly since I don't know how to get the position of the current element dynamical.
Please help
To give an idea where to start, have a look at useCallback and refs for React. With the given information from node.getBoundingClientRect(), you could calculate if your tooltip is outside the visible area of the browser.
// edit: useCallback won't work in this case, because the visibility is triggered by a css hover and the dimensions are not yet available for the hidden tooltip. Here is a possible solution with useRef and use useEffect though:
function ToolTip({ word, description }) {
const [left, setLeft] = useState(0);
const [hover, setHover] = useState(false);
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
const { right } = ref.current.getBoundingClientRect();
if (window.innerWidth < right) {
setLeft(window.innerWidth - right);
}
}
}, [hover]);
return (
<span
desc={description}
className="dashed"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => {
setHover(false);
setLeft(0);
}}
>
{word}
<div ref={ref} className="explain" style={{ left }}>
<h2>{word}</h2>
{description}
</div>
</span>
);
}
Codepen example: https://codesandbox.io/s/lk60yj307
I was able to do it with
https://www.npmjs.com/package/#syncfusion/ej2-react-popups
But I still wonder what is the correct way to do it in code.

Resources