Suppose that we have 2 different functions (or more) which accept an one argument from some executor and return the result object. Let me show in an example:
const style_1 = theme => ({
header : {
color : theme.primary
}
})
const style_2 = theme => ({
header : {
backgroundColor : theme.secondary,
color : "red"
}
})
I want to execute them and merge the resulted object into an one! In case of objects it's a trivial task eg.
const style_1 = {
header : {
color : theme.primary
}
}
const style_2 = {
header : {
backgroundColor : theme.secondary,
color : "red"
}
}
const res = {...style_1, ...style_2}
expected result is
{
header :
{
backgroundColor : "black",
color : "red"
}
}
But in case of functions it's becomes a lil bit annoying.
So question is. Is it possible to implement what I want? (via Lodash or by using something else)
I've decided that I might create something like that
const style_1 = theme => ({
header : {
color : theme.secondary
backgroundColor : "black"
},
footer : {
color : "purple"
}
})
const style_2 = (theme, extendStyles) => {
const extendStyles = extendStyles(theme);
return {
header : {
color : theme.primary,
...extendsStyles.header
},
...extendStyles
}
}
but this solutions is a lil bit ugly cause I should take care about this thing in an every style which might be override. Maybe there are exist some utilities/helpers which can help to handle it in a more nice way?
P.S. Don't ask me about this interesting requirements, it's just a withStyle feature of the MaterilUI and I wondered how to handle it right.
Thanks for any help.
You can use _.invokeMap() and _.merge() to generate a combine styled object from an array of style functions and a theme:
const applyTheme = (styles, theme) =>
_.merge(..._.invokeMap(styles, _.call, null, theme));
const style_1 = theme => ({
header : {
color : theme.primary,
border: `1px solid ${theme.primary}`
}
})
const style_2 = theme => ({
header : {
backgroundColor : theme.secondary,
color : "red"
}
})
const theme = { primary: 'red', secondary: 'blue' }
const result = applyTheme([style_1, style_2], theme)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Related
I am using MUI's Autocomplete to group my suggestions inside of search Bar.
I think it seems to be simple but not .
Firstly please look at the shape of my data
[{
"id" : 1,
"bookName" : "it starts with us",
"author" : "Colin Hoover"
},
{ "id" : 2,
"bookName" : "it ends with us",
"author" : "Colin Hoover"
},
{ "id" : 3,
"bookName" : "The great Gatsby",
"author" : "Scott Fitzgerald"
},
{ "id" : 4,
"bookName" : "To Kill a mocking bird",
"author" : "Harper Lee"
},
{ "id" : 5,
"bookName" : "Remainders of Him",
"author" : "Colin Hoover"
}, ... ]
now you can see, there are 3 books written by 'Colin Hoover', placed at different Ids. What I want to achieve is, my MUI's autocomplete should render
Colin Hoover //author name as heading and books underneath it
Remainders of Him
It Ends with us
It starts with us
... other books from this specific author
into my groupBy attribute in MUI's Autocomplete.
Issue I am having now - When I arrange the books which are meant for a certain author in consecutive Id's (say, id: 1, 2, 3, 4 ..) , the group-by of autocomplete works fine (i.e, it groups the different books under the same author if the author occurs at consecutive ids). But, when I arrange them in random fashion ( ie, id 1,3, 5, 8, 9 ..occupies books under same name "Colin Hoover"), then it doesn't group any of it rather it creates duplicate headers as "Colin Hoover" into autosuggestion list, which I don't want.
ALl I want is same header and all of his/her written books under his name into MUI's autocomplete.
Here's my Code
imports ...
export default function JobSearch () {
const [ books, setBooks ] = useState<Book[]>([]);
const [ searchTerm, setSearchTerm ] = useState<string>('');
// using redux
const dispatch = useAppDispatch();
const { status } = useAppSelector ( state => state.menu );
useEffect(() => {
agent.Books.getBooks() // my endpoint for getting the books
.then((books) => { setBooks(books);
console.log(books)})
.catch((error) => console.log(error))
}, []);
const suggestions = books.map((book) => {
const bookDetail = `${book.name} by ${book.author}`
return {
bookDetail,
...book
}});
const handleChangeInput = ( event : any ) => {
setSearchTerm( event.target.value);
console.log(event.target.value);
}
return (
<Fragment>
<Autocomplete
open = { status.name === "autoCompleteMenu" && status.open }
autoHighlight = {true}
autoComplete = {true}
noOptionsText = {`Search for ${searchTerm}`}
onOpen = { () => {
dispatch(setOpenMenu("autoCompleteMenu"));
}}
onClose = { () => dispatch(setCloseMenu("autoCompleteMenu"))}
options = {suggestions.sort((a, b) => -b.bookDetail.localeCompare(a.bookDetail))}
isOptionEqualToValue = {(option, value) => option.bookDetail === value.bookDetail}
groupBy = {(option) => option.author} // I want all books sorted under same author
getOptionLabel={(option) => option.bookDetail}
renderInput = {(params) => (
<TextField { ...params}
label = {'search ...'}
onChange = {handleChangeInput}
variant = {'outlined'} /> )} />
</Fragment>
)
}
I know I have to implement 'sort' into groupBy, but how to do the same, so as to sort all the books according to the author name and pass it as a parameter of the groupBy attribute.
All Suggestions are welcome and highly appreciated.
I am using Mantine for a search bar and I need to get the wordcount of the text area. This is using Nodejs and React. I need to be able to export this value to use in a different file.
import React, { useState } from 'react';
import { TextInput, createStyles } from '#mantine/core';
var count = document.getElementById('count');
const useStyles = createStyles((theme, { floating }: { floating: boolean }) => ({
root: {
position: 'relative',
},
label: {
position: 'absolute',
zIndex: 2,
top: 7,
left: theme.spacing.sm,
pointerEvents: 'none',
color: floating
? theme.colorScheme === 'dark'
? theme.white
: theme.black
: theme.colorScheme === 'dark'
? theme.colors.dark[3]
: theme.colors.gray[5],
transition: 'transform 150ms ease, color 150ms ease, font-size 150ms ease',
transform: floating ? `translate(-${theme.spacing.sm}px, -28px)` : 'none',
fontSize: floating ? theme.fontSizes.xs : theme.fontSizes.sm,
fontWeight: floating ? 500 : 400,
},
required: {
transition: 'opacity 150ms ease',
opacity: floating ? 1 : 0,
},
input: {
'&::placeholder': {
transition: 'color 150ms ease',
color: !floating ? 'transparent' : undefined,
},
},
}
)
);
export function FloatingLabelInput() {
const [focused, setFocused] = useState(false);
const [value, setValue] = useState('');
const { classes } = useStyles({ floating: value.trim().length !== 0 || focused });
const uniqueid = "input";
return(
<TextInput
id={ uniqueid }
placeholder="Add anything you want to the book of the internet."
required
classNames={classes}
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
mt="md"
onKeyUp={(e) => {
var text = value.split(' ');
var wordcount = 0;
for (var i = 0; i < text.length; i++) {
if (text[i] !== ' ') {
wordcount++;
}
}
count.innerText = wordcount;
}
}
autoComplete="nope"
/>
);
}
As you can see, it correctly outputs it into html, but returning inside the function doesnt work at all.
I tried exporting it, I tried returning it to the function but it doesn't see it. I tried exporting and using modules exports but that doesnt work either. Any help would be appreciated.
In the following code snippet, my root component (called App) is responsible for keeping the app state, but it can give any piece of state to any of its children. It can also give state modifiers (setX functions) to its children, which is what I am demonstrating here:
function Input ({ setWordCount }) {
function updateWordCount (event) {
setWordCount(event.target.value.split(' ').length)
}
return <input type="text" onKeyUp={updateWordCount} />
}
function SomeOtherComponent ({ count }) {
return (
<span>: {count} words</span>
)
}
function App () {
const [wordCount, setWordCount] = React.useState(0)
return (
<p>
<Input setWordCount={setWordCount} />
<SomeOtherComponent count={wordCount} />
</p>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.5/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.5/umd/react-dom.production.min.js"></script>
<div id="app" />
As you can see, the Input component can call the setWordCount function provided by its parent to change a piece of state. Then, the parent (App) can give that piece of state to any number of its children. Each component can live in a separate file too, this would still work…
I'm not sure if I understood your question correctly, but hopefully, this can give you ideas you can reuse in your own code?
I am using a LightningChart JS by Arction to plot a bar graph and it keeps crashing after adding the rectangle figures with an error message: t.toFixed is not a function
The series being used is a rectangle series and I'd like to use only one rectangle series because I need them all under one group.
Below is my code
// Core react imports
import React, { useEffect, useCallback } from 'react'
// React bootstrap imports
import { Col } from "react-bootstrap"
// Chart imports
import {
lightningChart,
SolidFill,
SolidLine,
emptyTick,
emptyLine,
FontSettings,
ColorHEX,
} from "#arction/lcjs"
import axios from "axios"
export default function Histogram() {
const createChart = useCallback(
() => {
const chart = lightningChart().ChartXY({ containerId: "myplot" });
chart
.setTitle("RR Histogram")
.setTitleFillStyle((solidFill) => solidFill.setColor(ColorHEX("#000")))
.setTitleMarginTop(0)
.setTitleMarginBottom(0)
.setChartBackgroundFillStyle((solidFill) =>
solidFill.setColor(ColorHEX("#FFF"))
)
.setBackgroundFillStyle((solidFill) =>
solidFill.setColor(ColorHEX("#FFF"))
)
.setZoomingRectangleStrokeStyle(
new SolidLine({
fillStyle: new SolidFill({ color: ColorHEX("#000") }),
})
)
.setTitleFont(new FontSettings({ size: 20 }));
// Configure X-axis of chart to be progressive and have nice interval.
chart
.getDefaultAxisX();
// .setTickStyle(emptyTick)
// .setNibStyle(emptyLine)
// .setTitleFont(new FontSettings({ size: 12 }))
// .setStrokeStyle(emptyLine);
chart
.getDefaultAxisY();
// .setTickStyle(emptyTick)
// .setNibStyle(emptyLine)
// .setStrokeStyle(emptyLine);
let rectSeries = chart
.addRectangleSeries()
.setDefaultStyle(figure => figure.setFillStyle(new SolidFill({
color: ColorHEX("#000")
})));
let rr_hist = {};
axios
.get("Api url here")
.then((res) => {
console.log(res)
rr_hist = res.data;
})
.catch((err) => console.log(err));
setTimeout(() => {
for (let point in rr_hist) {
let insert_Point = {
height: rr_hist[point],
y: 0,
x: point,
width: 1
}
let bar = rectSeries.add(insert_Point);
bar.setDimensions(insert_Point);
bar.setFillStyle(new SolidFill({ color: ColorHEX("#000") }));
bar.setStrokeStyle(new SolidLine({
fillStyle: new SolidFill({ color: ColorHEX("#000") }),
}))
}
console.log(rr_hist)
}, 2000)
},
[],
)
useEffect(() => {
createChart()
}, [createChart])
return (
<Col xs={12} style={{ height: "100%", width: "100%" }}>
<div id="myplot" style={{ height: "100%", width: "100%" }}></div>
</Col>
)
}
Also could you please let me know how to improve the styling?
Most likely reason for the crash is that your height or x field for the new rectangle figure is not a number. LightningChart JS doesn't do type conversions for input values.
So when adding new rectangles to rectangle series make sure to do the type conversion from string to number yourself.
let insert_Point = {
height: Number(rr_hist[point]),
y: 0,
x: Number(point),
width: 1
}
let bar = rectSeries.add(insert_Point);
Instead of using Number for the conversion you could use parseFloat or parseInt depending on the type of data you use. See https://stackoverflow.com/a/13676265/6198227 that answer for more detailed differences between Number and parseFloat.
For styling, it looks like you would benefit from using a light colored theme. When creating the chart with ChartXY you can specify theme option.
const chart = lightningChart().ChartXY({
theme: Themes.light
})
You can see the available themes in our docs Themes
I've cloned a repository which focuses on creating a To-Do application using ES6 and Polymer 3. I'm trying to implement a button which turns the background color containing a string green upon click. I've tried doing this, but I keep failing to get the desired result.
Example code:
static get properties() {
return {
list: {type: Array},
todo: {type: String},
};
}
constructor() {
super();
this.list = [
this.todoItem('buy cereal'),
this.todoItem('buy milk')
];
this.todo = '';
this.createNewToDoItem = this.createNewToDoItem.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleInput = this.handleInput.bind(this);
}
todoItem(todo) {
return {todo}
}
createNewToDoItem() {
this.list = [
...this.list,
this.todoItem(this.todo)
];
this.todo = '';
}
//Right here is where I tried to implement the background color change.
checkItem() {
checkItem = document.getElementById('checkItem'),
checkItem.addEventListener('click', () => {
this.list = this.list.filter(this.todo)
document.body.style.backgroundColor = 'green';
});
}
deleteItem(indexToDelete) {
this.list = this.list.filter((toDo, index) => index !== indexToDelete);
}
render() {
return html`
${style}
<div class="ToDo">
<h1>Grocery List</h1>
<h1 class="ToDo-Header">What do I need to buy today?</h1>
<div class="ToDo-Container">
<div class="ToDo-Content">
${repeat(
this.list,
(item, key) => {
return html`
<to-do-item
item=${item.todo}
.deleteItem=${this.deleteItem.bind(this, key)}
></to-do-item>
`;
}
)}
</div>
I'd be eternally thankful if someone helped me out. I've created two JSFiddle links which show the code I've worked on thus far:
Link 1: https://jsfiddle.net/r2mxzp1c/ (Check line 42-49)
Link 2: https://jsfiddle.net/zt0x5u94/ (Check line 13 & 22-24)
I'm not sure about the approach. But this link might help you
https://stackblitz.com/edit/web-components-zero-to-hero-part-one?file=to-do-app.js
from this guy: https://stackblitz.com/#thepassle
You should try to make the reactive templating work for you by defining presentation details in terms of your element's properties.
For example, this is a stripped-down approach to the same problem:
class TestElement extends LitElement{
static get properties() {
return {
'items': { 'type': Array }
};
}
constructor() {
super();
// set up a data structure I can use to selectively color items
this.items = [ 'a', 'b', 'c' ].map((name) =>
({ name, 'highlight': false }));
}
render() {
return html`<ol>${
this.items.map((item, idx) =>
html`<li
#click="${ () => this.toggle(idx) }"
style="background: ${ item.highlight ? '#0f0' : '#fff' }">
${ item.name }
</li>`)
}</ol>`;
}
toggle(idx) {
// rendering won't trigger unless you replace the whole array or object
// when using properties of those types. alternatively, mutate with the
// usual .push(), .splice(), etc methods and then call `this.requestUpdate()`
this.items = this.items.map((item, jdx) =>
jdx === idx ? { ...item, 'highlight': !item.highlight } : item
);
}
}
https://jsfiddle.net/rzhofu81/305/
I define the template such that the elements are colored the way I want depending on an aspect of their state (the "highlight" attribute of each entry in the list), and then I focus the interaction on updating the state to reflect what the user is doing.
I wanted to set my element's style as such:
this.refs.element.style = {
...this.props.style,
background: 'blue',
};
But apparently you can't use an object to set the ref's style. I have to use a CSS style string with ; separating the prop:values
I'm aware that most people would set style in the render function, but for performance reasons, I can't repeatedly re-render.
A performant answer is to map and join the Object.entries with semicolons:
const style = {
...this.props.style,
background: 'blue',
};
const styleString = (
Object.entries(style).map(([k, v]) => `${k}:${v}`).join(';')
);
It unwraps background:'blue', to background:blue; which works well for CSS
To replace any capital letter with dash lowercase letter
k = k.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
this solution works in IE and handles camelCase keys like backgroundColor
const style = {
width: '1px',
height: '1px',
backgroundColor: 'red',
transform: 'rotateZ(45deg)',
}
const styleToString = (style) => {
return Object.keys(style).reduce((acc, key) => (
acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
), '');
};
console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"
Use https://www.npmjs.com/package/json-to-css. Note it will not add a semicolon to the last property to fix it you can beautify it with https://www.npmjs.com/package/cssbeautify
Example
const cssbeautify = require('cssbeautify')
const Css = require('json-to-css')
const json = {
"h1": {
"font-size": "18vw",
"color": "#f00"
},
".btn": {
"font-size": "18vw",
"color": "#f00"
}
}
const r = Css.of(json)
console.log(r)
const beautified = cssbeautify(r, {
autosemicolon: true
})
console.log(beautified)
Result
console.log src/utils/playground/index.spec.ts:22 // json-to-css
h1{font-size:18vw;color:#f00}
.btn{font-size:18vw;color:#f00}
console.log src/utils/playground/index.spec.ts:29 // cssbeautify
h1 {
font-size: 18vw;
color: #f00;
}
.btn {
font-size: 18vw;
color: #f00;
}
Adding to the great answer of #Artem Bochkarev
I'm adding a snippet to do the opposite conversion as well (string to object) which may come in handy to anyone stumbling here
const style = {
width: '1px',
height: '1px',
backgroundColor: 'red',
transform: 'rotateZ(45deg)',
};
const styleToString = (style) => {
return Object.keys(style).reduce((acc, key) => (
acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
), '');
};
const stringToStyle = (style) => {
const styles = {};
style.split(';').forEach((s) => {
const parts = s.split(':', 2);
if (parts.length > 1) {
styles[parts[0].trim().replace(/-([a-z])/ig, (_, l) => l.toUpperCase())] = parts[1].trim();
}
});
return styles;
};
console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"
console.log(stringToStyle(styleToString(style)));
TL;DR: The problem is that you are overwriting the entire "style" property of the element and losing its prototype and methods. You must add your style object without change the entire property. If you want to apply an object-like style to a DOM element, just do:
Object.assign(this.refs.element.style, {
background: 'blue',
color: 'white',
/** style properties:values goes here */
});
Explanation: The property "style" is an instance of the "CSSStyleDeclaration" interface. If you overwrite the interface it wont be a "CSSStyleDeclaration" anymore. It works when you set a string as value because javascript will pass the string directly to the element, without process anything.
CSSStyleDeclaration Reference Doc:
https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
If you want to do a test, go to your navigator > inspector > console and paste the code below:
const p1 = document.createElement('p');
p1.style = { color: 'blue' };
const p2 = document.createElement('p');
Object.assign(p2.style, { color: 'blue' });
console.log(p1);
console.log(p2);
The output will be:
<p style=""></p>
<p style="color: blue;"></p>
the css function in #material-ui/system can help you out
check more info here
import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '#material-ui/core/NoSsr';
import { createMuiTheme } from '#material-ui/core/styles';
import { compose, spacing, palette, css } from '#material-ui/system';
const Box = styled.div`
${css(
compose(
spacing,
palette,
),
)}
`;
const theme = createMuiTheme();
export default function CssProp() {
return (
<NoSsr>
<ThemeProvider theme={theme}>
<Box color="white" css={{ bgcolor: 'palevioletred', p: 1, textTransform: 'uppercase' }}>
CssProp
</Box>
</ThemeProvider>
</NoSsr>
);
}