vue3 + tailwind - dynamic classes - is this acceptable solution? - javascript

Tailwind docs say that we should not pass dynamic pieces of class name as props because it will not work, and well it does not.
There are other options. One of them is to predefine object with actual tailwind classes and then passing a prop to pick a set of classes eg:
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500',
red: 'bg-red-600 hover:bg-red-500',
}
But that is not as flexible as I would like it to be so I came up with falling back to inline styles like so:
const props = defineProps({
headerPadding: {
type: String,
required: false,
default: '10px'
},
contentPadding: {
type: String,
required: false,
default: '10px'
},
});
const styles = computed(() => {
return {header: `padding: ${props.headerPadding}`};
});
And then I do
<div :style="styles.header" class="flex">Header</div>
Now this works perfectly fine. I am just wondering if there is anything super bad about this approach or if there is a good reason to avoid it?

I find pre-defining an object to be better personally. That way all my styles are tailwind classes. Although separate object, its only relevant for cases where i need this dynamic definitions, and feel it is easier to maintain rather than having strings of inline-styles.
I don't see anything wrong with your approach. But you can also try the below approach in some cases..you can define the class with array syntax..
<div :class="[ largepadding ? 'p-8' : 'p-4', danger ? 'bg-color-red' : 'bg-color-success']"></div>
and define only the dynamic attributes as props instead of the entire styles.

Related

Define and reuse inline component in Vue3

Is it possible to define a "sub component" inline in Vue3 which can be reused in the same .vue file/component, but without actually defining a new .vue file (i.e. without defining this sub component as a new component).
The use case is that I have a very specific way of formatting <select> options which is used for multiple <select>s within the same component (.vue file), but which will not be used anywhere else (it's also small, so I am inclined to define this options formatting part inline). I don't necessarily want to copy and paste the formatting (and it would be good to keep it within the same .vue file because it's small).
I realise that this is only syntactic sugar which may or may not be relevant in specific cases (I'm also not seeking advice on whether or not this is a good idea). I'm just looking for a way this can be done (if not, that's also an answer ;-))
You can define h() function to create vnodes inside your setup script in vue 3.
for example:
<template>
<MyComponent />
</template>
<script setup>
const MyComponent = h('div', { class: 'bar', innerHTML: 'hello' })
</script>
vue document will help you completely. document
You could do something like this in Vue2
Vue.component('my-checkbox', {
template: '<div class="checkbox-wrapper" #click="check"><div :class="{ checkbox: true, checked: checked }"></div><div class="title">{{ title }}</div></div>',
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});
Probably still works

Replacing document.createElement() with React.createElement()

I have a javascript library which uses document.createElement() which creates an HTMLElement
I want to create a react library with the same functionality.
My idea was to replace the document.createElement() with React.createElement() .
So , the original code which was something like
this.myElement = document.createElement("input");
/* some stuff happens here due only after which the followig code is run */
this.myElement.className = "someClassName";
this.myElement.style.display = "block";
this.myElement.appendChild(someElement);
will become
this.myElement = React.createElement("input");
this.myElement.props.className = "someClassName";
this.myElement.props.style.display = "block"; // Props is read only at this point
this.myElement.appendChild(someReactElement); // This doesn't work either
The problem here is that props is a read only property in react , also appendChild doesn't work that way in. So changing it after React.createElement("input") is not possible. I can create the props object before each createElement call based on the original code but it significantly reduces reusability of the existing code base. Is there a way around that can be used to bypass this issue in react ?
Edit : I know I can pass children and props during the time I call React.createElement however , it reduces my ability to copy paste and reuse the existing code base . The question is specific about changing props and appeding child after the React.createElement call
Create a props object and pass that as the 2nd argument of the createElement function. Follow this-
const props = {
className: 'someClassName',
style: {
display: 'block',
width: '100%'
}
}
React.createElement('input', props);
Read the documentation for React.createElement. You can pass the props (and children) in the function.

vue3-style-components render duplicate classes in dom

In my vue3 project, I used vue3-style-components, but when I add class in components which is created by vue3-style-components it renders duplicate classes.
like this:
How can I prevent duplicate class rendering?
This is my vuejs code:
And This is Style-components Code:
I know this doesnt answer your question - but I would recommend destructing your props/theme not on the individual css attributes but as a wrapping function and then using the css function from styled-components as well, like:
import Styled, { css } from 'vue3-styled-components'
const props = ['type', 'shape']
const ButtonStyled = Styled('button', props)`
${({ type, theme }) => css`
background: ${ type !== 'default' && theme[`${type}-color] + '' }
`}
`
Because this operation can be expensive, so performing the callback to get the type and theme variables over and over could really slow down performance.
Also I would consider whether or not you need to check the type here or if that maybe is a task better suited for the theme - if there is no theme["default-color"] then you can just use the nullish coalescent operator as such theme[${type}-color] ?? '' to the same effect, if will then default to an empty string if the first variable is null or undefined.
As for your actual issue - its difficult to tell unless you supply some information about where these classes are actually applied, because I cant see any of them being set in the code you supplied.

Can "font-variation-settings" be used in the styled-components .attrs method?

I'm trying to pass a variable "width" to a component using styled components. The value of "width" is a function of the mouse position. Passing it directly into the literal string causes styled-components to create a new class for every mouse movement, which throws a warning.
To resolve it, I'm trying to use the .attrs method to pass a variable style object to the component, but I can't work out how to pass this css property to the component.
I tried:
const StyledComponent = styled.div.attrs((props) => (
{
style: {
fontVariationSettings: `wdth: ${props.width})`,
},
})
)``;
But React doesn't recognise this as a valid property to pass into CSS. Any way around it?
Got it. Typical that it's always straight after posting a question after spending hours trying different stuff. Rather than using font-variation-settings, I can use the individual properties. Width becomes font-stretch.
So what I am looking for is
const StyledComponent = styled.div.attrs((props) => (
{
style: {
fontStretch: `wdth: ${props.width})`,
},
})
)``;

Can I make styles apply to classes other than the first defined in a file?

I have inherited some code and I've been manipulating it, but I came across something that makes me scratch my head.
I am not sure whether the issue I am having relates to specifically to react.js or more generally to CSS / javascript...
In this code, we make use of react.js withStyles.
I have set up a sandbox to demonstrate.
First thing in the file, we define some styles that can then be applied to page elements, e.g.
const styles = theme => ({
buttonGroup: {
width: "250px",
display: "flex",
flexWrap: "wrap",
}, ...
then, when we define a class, we can get access to these styles by doing a const { classes } = this.props , e.g.
class MyButtons extends React.Component {
render() {
const { classes } = this.props;
return (
<div className={classes.buttonGroup}>
{three_buttons.map(e => (
<Button className={classes.a_button}>{e}</Button>
))}
</div>
);
}
}
That is all fine and works.
What I've tried is to, in the same file, define a second class, which I then call from the first (to make a component within a component).
However, the const { classes } = this.props does not seem to gain me access to the styles defined at the top, and when I try to set className={classes.buttonGroup} in the second class, I get an error
TypeError: read property 'buttonGroup' of undefined
I am pretty sure I can overcome this by simply defining the second class in a separate file like I usually do, but I would like to understand why this does not work.
You are not passing the styles as props to MyOtherButtons Component and hence you are getting this issue. Pass the classes as props and things would work as expected. It works for MyButtons component since you are passing the styles using withStyles syntax.
Check the working link https://codesandbox.io/s/m3rl6o2qyj

Categories