How to add a Select addon to input field in Semantic UI React? - forms

I am using Semantic UI React for my React JS project. I need to add an addon to the input field, but cant do it.
(ideally the addon would be on the right side)
Anyone can help me?
Thanks

The Select component is a wrapper around Dropdown. I think that you're looking for this example.
I also made a working example for you:
import React from 'react'
import { Dropdown, Input } from 'semantic-ui-react'
const options = [
{ key: '86', text: '+86', value: '86' },
{ key: '89', text: '+89', value: '89' },
]
const Example = () => (
<Input
label={{
basic: true,
content: <Dropdown compact options={options} defaultValue='86' />
}}
placeholder='1234'
/>
)

Related

How to pass DOM elements for libraries (eg. ChartJS, Hightcharts) in Virtual DOMs (such as Qwik)?

Background
I have personally used React, Vue and Angular extensively in the past. And a lot of times I need to create applications with charts generated within them from selective data. I'm recently trying out Qwik due to its promise of speed and attempted to create charts within it using ChartJs. But while ChartJs has separate libraries available for React, Vue, Angular, Svelte, etc. it does not have one for Qwik understandably.
Issue
Many plugins such as Highcharts and ChartJs often require a DOM element to be sent to its functions to identify where to render their output. But when we are dealing with virtual DOMs, I can't run JS selector scripts to fetch DOM elements and pass them into a function within a component. Therefore, as of now, I have not been able to use ChartJs in my Qwik project.
Attempts
I have only looked for solutions for this issue and not found any workable approaches. From ChartJs docs the following code is their raw JS way of implementing charts:
new Chart(
document.getElementById('acquisitions'),
{
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Acquisitions by year',
data: data.map(row => row.count)
}
]
}
}
);
As expected document.getElementById does not work inside a component and that is where I'm stuck. I've only created the useMount$() function where I expect to place the logic for generating my chart and also looked around for React solutions by perhaps using references and what not. But, other than that, I have been unable to find anything more.
I understand that looking at the source code of the React library for ChartJs would provide me clues but while I investigate a library (which I find difficult at my current level) I was hoping for a pointer to the solution from the Stack Overflow community.
Searching "ref" on the Qwik docs does not return any search results but I had found the git project from another developer online and tried to replicate the use of references from his approach:
Child component code:
import { component$, useMount$, Ref, useStylesScoped$ } from "#builder.io/qwik";
import { Chart } from 'chart.js/auto';
interface GraphProps {
data: object[];
reference: Ref<Element>;
}
export default component$((props: GraphProps) => {
useStylesScoped$(styles);
useMount$(() => {
new Chart(
props.reference.value,
{
<... options here ...>
}
);
});
return (
<div id="chartContent">
</div>
);
});
Parent component code:
import { component$, useRef } from "#builder.io/qwik";
import ContentCard from "../components/contentCard/contentCard";
import ChartJSGraph from "../components/chartJSGraph/chartJSGraph";
...
export default component$(() => {
const leftChartContainer = useRef();
return (
<div>
<div className="row">
<ContentCard>
<div className="graph-container">
<ChartJSGraph
data={[
{ year: 2010, count: 10 },
...
]}
reference={leftChartContainer}
/>
</div>
</ContentCard>
</div>
</div>
)
});
As these are just findings from a YouTuber's code it could be outdated so is certainly not necessarily a reliable source. But so far searching the official docs have not led me to any official approach for references.
The DOM element that is passed to the charting library can only be accessed once it has been mounted to the page. Qwik/Vue/React all provide component mounted hooks.
https://qwik.builder.io/docs/components/lifecycle/#usemount
https://vuejs.org/api/composition-api-lifecycle.html#onmounted
https://reactjs.org/docs/react-component.html#componentdidmount
Inside these mounted hooks you can reference your DOM element via id or querySelector or using the internal DOM reference feature of Qwuik/Vue/React and then use that when initialising the chart. The latter is the cleaner approach.
For example, in Vue:
<template>
<div id="acquisitions" ref="chartEl"></div>
</template>
<script setup>
import Chart from 'chart.js/auto';
import { ref, onMounted } from 'vue';
const chartEl = ref(null)
onMounted(() => {
const chartOpts = {
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Acquisitions by year',
data: data.map(row => row.count)
}
]
}
}
new Chart(
chartEl.value,
chartOpts
);
})
</script>
Solution
Sadly this was a silly issue of perhaps on my network side or god knows what why the search engine on the Qwik doc never suggested anything for me when I looked up "Ref" in their docs. But my problem has been solved after finding the following link:
https://qwik.builder.io/tutorial/hooks/use-signal/#example
For future reference for myself or any beginners facing the similar issue, I'm writing down my implementation below:
// Root component
import { component$, useSignal } from "#builder.io/qwik";
...
import ChartJSGraph from "../components/chartJSGraph/chartJSGraph";
export default component$(() => {
const chartData1 = useSignal({
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: 'Inventory Value per Outlet',
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
});
return (
<div class="w-100 h-100">
...
<ChartJSGraph
width={'100%'}
height={'25px'}
chartData={chartData1.value}
/>
</div>
);
});
And here's the code for my ChartJSGraph component that uses the data supplied to generate the chart while using the reference of the canvas element to point to ChartJS where to create the chart.
// ChartJSGraph component
import { component$, useClientEffect$, useSignal } from "#builder.io/qwik";
import { Chart } from 'chart.js/auto';
...
interface GraphProps {
height: string;
width: string;
chartData: object;
}
export default component$((props: GraphProps) => {
const outputRef = useSignal<Element>();
useClientEffect$(() => {
new Chart(
outputRef.value,
{
type: 'line',
data: props.chartData
}
);
});
return (
<>
<canvas ref={outputRef} width={props.width} height={props.height}>
</canvas>
</>
);
});

material text field label not copyable?

I am using MUI's Text Field component and found there's literally no way to copy the label contents. Is there a way to copy the label somehow?
See the demo here: https://codesandbox.io/s/4ou0l7?file=/demo.tsx
Thanks
It is because material UI is disabling the label selection using CSS.
You can enable it back in a few ways. You can enable it for a certain field or across all of them using the material UI theme override ability.
In order to enable label selection only to one field, you have pass an additional prop to your TextField: InputLabelProps={{ sx: { userSelect: "text" } }}
And here I have provided you with the second way to do that for all the text fields:
import * as React from "react";
import Box from "#mui/material/Box";
import TextField from "#mui/material/TextField";
import { createTheme, ThemeProvider } from "#mui/material/styles";
const theme = createTheme({
components: {
MuiInputLabel: {
styleOverrides: {
root: {
userSelect: "text"
}
}
}
}
});
const StateTextFields = () => {
const [name, setName] = React.useState("Cat in the Hat");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
return (
<Box
component="form"
sx={{
"& > :not(style)": { m: 1, width: "25ch" }
}}
noValidate
autoComplete="off"
>
<TextField
id="outlined-name"
label="Name"
value={name}
onChange={handleChange}
/>
<TextField
id="outlined-uncontrolled"
label="Uncontrolled"
defaultValue="foo"
/>
</Box>
);
};
export default () => (
<ThemeProvider theme={theme}>
<StateTextFields />
</ThemeProvider>
);
Of course, you can extract this ThemeProvider into a separate file and wrap with it the whole project, not only this file. It is combined just for the example.
I found a way that this can be done using the helperText and my solution is more of a hack. I am using the helperText and positioning it where the label was used to be, giving it a background and bringing it to front using z-index.
Also you can either choose to use the label or replace it with the placeholder depending if you are happy with the animation.
Here is a codesandbox based on your code in case you need it.
<TextField
id="outlined-name"
label="Name"
// placeholder="Name"
value={name}
onChange={handleChange}
helperText="Name"
sx={{
"& .MuiFormHelperText-root": {
top: "-11px",
position: "absolute",
zIndex: "1",
background: "white",
padding: "0 4px",
}
}}
/>

#mui/lab/DateRangePicker - How to add custom toolbar with action buttons above and under calendar

I'm using DateRangePicker from #mui/lab and now, there is need to add custom toolbar with action buttons above or under calendar (for example, buttons apply or cancel). I struggled to find solution but couldn't find something useful. It should be on both desktop and mobile versions of datepicker.
For all those who suffer from the lack of simple and necessary solutions.
Hope this will help 🫡
Here the docs, but there is huge lack of information and examples
<LocalizationProvider dateAdapter={AdapterDateFns} localeText={{ start: 'from', end: 'to' }}>
<DateRangePicker
components={{ //edit component
PaperContent: data => { //exactly PaperContent
const childCopy = [...data.children]; //duplicate to avoid errors
const Component = () => <Box>BUILD YOUR COMPONENT</Box>;
childCopy.push(<Component />);
return childCopy; //return array of childrens
}
}}
value={value}
onChange={newValue => {
setValue(newValue);
}}
renderInput={(startProps, endProps) => (
<>
<MNVTTextField {...startProps} />
<Box>-</Box>
<MNVTTextField {...endProps} />
</>
)}
/>
</LocalizationProvider>;

MaterialUI (MUI). How to pass handlers via props to DataGrid component (for using them in column type=actions)

The docs say: https://mui.com/components/data-grid/columns/
If the column type is 'actions', you need to provide a getActions
function that returns an array of actions available for each row
(React elements). You can add the showInMenu prop on the returned
React elements to signal the data grid to group these actions inside a
row menu.
{
field: 'actions',
type: 'actions',
getActions: (params: GridRowParams) => [
<GridActionsCellItem icon={...} onClick={...} label="Delete" />,
<GridActionsCellItem icon={...} onClick={...} label="Print" showInMenu />,
]
}
How to pass via props onClick handlers?
<DataGrid deleteHandler={...} printHandler={...} />
I'm sorry for stupid question. Of course we can pass handlers in columns prop :))) But I have found even better solution. I don't use calback handlers in action column, but I use component.
getActions: (params: any) => [
<GridActionsCellItem
icon={<OpenIcon />}
label='Open'
component={Link}
to={`/counterparties/${params.id}`}
/>
The docs actually explain how to add handler:
Visit Special properties then scroll down a bit then click Show the full source icon
Basically we need to declare columns inside our functional component where the handler resides, but wrap the columns array using useMemo() to prevent unnecessary rerender.
const columns = React.useMemo(
() => [
{
field: 'actions',
type: 'actions',
width: 80,
getActions: (params) => [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={deleteUser(params.id)}
/>,
<GridActionsCellItem
icon={<SecurityIcon />}
label="Toggle Admin"
onClick={toggleAdmin(params.id)}
showInMenu
/>,
<GridActionsCellItem
icon={<FileCopyIcon />}
label="Duplicate User"
onClick={duplicateUser(params.id)}
showInMenu
/>,
],
},
], [deleteUser, toggleAdmin, duplicateUser]);

MaterialUI together with styled-components, SSR

I'm building a new project with SSR using Next.js, MaterialUI and styled-components. From what I know, MaterialUI uses JSS as a tool for SSR (according to the example in its repository). I wonder if anyone knows how I can make it work with styled-components. I opened issues in MaterialUI and styled-components repositories, both authors answered me that they don't know how to make it work together. But probably anyone did it already? Or at least can tell me where to dig to solve this problem. Thanks in advance!
You can use styled-components with material ui, but you'll end up needing to use !important a lot. Like this:
import Button from "material-ui/Button"
const MyButton = styled(Button)`
background: red !important;
`
In the project I'm working on with the same combo, I've just resorted to using the JSS style material-ui wants you to use with the whole withStyles HOC..
You may check their docs here https://material-ui.com/guides/interoperability/#styled-components, you may check the deeper elements section if you want to override specific classes https://material-ui.com/guides/interoperability/#deeper-elements
below is my example where for the switch component
const StyledSwitch = styled(({ ...other }) => (
<div>
<Switch
{...other}
classes={{ colorSecondary: 'colorSecondary', checked: 'checked', bar: 'bar' }}
/>
</div>
))`
& .colorSecondary.checked + .bar {
background-color: ${props => props.theme.lighter.toString()};
}
& .colorSecondary.checked {
color: ${props => props.theme.default.toString()};
}
`;
export default StyledSwitch;
usage
<StyledSwitch theme={lightTheme.secondary} />
this is using a theme but you can specify any color you want
Looks like we have 3 ways (could be easier, but not everything is flowers) to override Material UI styles with Styled Components. Here is my Gist.
I do it like this:
In head component of app:
const styleNode = document.createComment('insertion-point-jss')
document.head.insertBefore(styleNode, document.head.firstChild)
const generateClassName = createGenerateClassName()
const jss = create({
...jssPreset(),
insertionPoint: 'insertion-point-jss'
})
<JssProvider jss={jss} generateClassName={generateClassName}>
<Main />
</JssProvider>
and then just style:
import styled from 'styled-components'
import Select from '#material-ui/core/Select'
import Input from '#material-ui/core/Input'
import React from 'react'
export const InputM = styled(({ ...other }) => (
<Input {...other} classes={{ input: 'input' }} />
))`
color: ${p => p.theme.textColor};
& .icon {
font-family: ${p => p.theme.fontFamily};
font-size: ${p => p.theme.fontSize}px;
color: ${p => p.theme.textColor};
}
`