Combining selector and prop conditions in emotion - emotion

I have the following setup in my code (simplified example):
import styled from '#emotion/styled'
const Container = styled.div<{value?: string}>`
&:focus-within {
border-color: red;
}
${() => Input} {
border-color: ${({value}) => value && 'red'};
}
`
const Input = styled.input`
border: 0;
`
const MyComponent = ({value}: {value?: string}) => (
<Container value={value}>
<Input />
</Container>
)
The gist of it is this:
The input's border is set on its Container.
I want to set the border red when (1) the input is active, or (2) the input has a value.
I am currently achieving this with the code above.
My question is:
Is it possible to combine the selector (&:focus-inside) and the prop (value) somehow in emotion in a single conditional statement so that I don't repeat the styling code twice?

Related

Styled-components react native - Text Input onBlur not working

I'm fairly new to styled-components in react native. I'm creating a custom TextInput component. I want to add an event of onBlur to change a piece of state. However onBlur is never triggered.
import React, { useCallback } from 'react'
const TextField = styled.TextField`
margin-top: 6px;
font-size: 14px;
color: '#000';
padding: 16px 15px;
border: 0.8px solid;
background-color: '#fff';
`
const Input = ({
editable,
...rest
}) => {
const handleOnFocus = useCallback(() => {
console.log('focus')
}, [])
const handleOnBlur = useCallback(() => {
console.log('blur')
}, [])
return (
<TextField
onFocus={handleOnFocus}
onBlur={handleOnBlur}
editable={editable}
{...rest}
/>
)
}
export default Input
as per guidelines of Textinput in
React Native Textinput
editable should be true to called blur, because if your input is editable is false then you can not focus that element and without focused element onBlur never triggered.
Found my issue. It's a stupid mistake. Happened to be passing the prop onBlur to the Input component which was overriding the onBlur I was trying to set on the TextField styled-component.

How to create a copy of the slot with createElement?

How to access name of the component of the slot?
I want to create a copy of the component provided in the slot:
const child1 = slot
const child2 = h(???, slot.props)
So that child1 renders exactly as child2.
I need this, so that I can change properties of that VNode, for examples classes.
Context
import { h } from 'vue';
export default {
setup(props, { slots }) {
const children = [];
for (const slot of slots.default()) {
const child = h(???, slot.props)
children.push(h('div', [child]));
}
return () =>
h('div', children);
},
};
Background
I want to make a component similar to q-button-group:
I need 2 components TButton and TButtonGroup so that I can style TButton independently and create groups just by putting those buttons inside the TButtonGroup.
Example
<TButtonGroup>
<TButton label="Two" />
<TButton label="Three" />
</TButtonGroup>
TButton should have a different list of classes:
when it's inside TButtonGroup: px-4 py-2
when it's not: border rounded-lg px-4 py-2
See full html
Playground
https://stackblitz.com/edit/vue3-button-group-razbakov?file=src%2Fcomponents%2FGroupRender.js
Component name of vnode won't tell much, components are already resolved at this point. VNode's element or component is stored in type property.
The problem with your approach is that render function is an alternative to component template, not a way to access the entire DOM element hierarchy. There will be no TButton child elements like div in render function, only TButton vnode itself. It needs to be rendered in order to access its children.
If TButton were someone else's component which initial behaviour needs to be modified, this could be done by adding some directive to it and accessing component's children elements.
But since TButton is your own component that can be modified to your needs, the most straightforward way is to make it change classes depending on a prop and provide this prop when it's inside TGroup, i.e.:
const child = h(slot.type, {...slot.props, group: true}, slot.children);
children.push(child);
Use the component type you created:
const { h } = Vue
const TBtn = {
props: {
staticClass: {
type: Array,
default: () => [],
},
},
template: `
<div
class="t-btn px-4 py-2"
>
<slot></slot>
</div>
`
}
const TBtnGroup = {
setup(props, {
slots
}) {
const children = [...slots.default()]
.map(slot => h(slot.type, {
class: ['border', 'rounded-lg']
}, slot))
return () => h('div', {
class: ['d-flex']
}, children)
},
}
const App = {
template: `
<t-btn>OUTSIDE 1</t-btn>
<t-btn>OUTSIDE 2</t-btn>
<br />
<t-btn-group>
<t-btn>INSIDE 1</t-btn>
<t-btn>INSIDE 2</t-btn>
<t-btn>INSIDE 3</t-btn>
</t-btn-group>
`
}
const app = Vue.createApp(App)
app.component('TBtn', TBtn)
app.component('TBtnGroup', TBtnGroup)
app.mount('#app')
.px-4 {
padding-left: 16px;
padding-right: 16px;
}
.py-2 {
padding-top: 8px;
padding-bottom: 8px;
}
.border {
border: 1px solid black;
}
.rounded-lg {
border-radius: 8px;
}
.d-flex {
display: flex;
gap: 8px;
}
.t-btn:hover {
cursor: pointer;
background-color: black;
color: white;
}
<script src="https://unpkg.com/vue#next"></script>
<div id="app"></div>

How can i change the hover style of a PrimaryButton in Fluent UI?

I am currently trying to re-style a Fabric UI Button in React by changing its shape, background color and hovering color. I managed to change the first two, but i'm still having troubles in accessing the hover color, since the selectors property does not seem to work.
My code is the following:
import React, { Component, Props } from 'react';
import { PrimaryButton as FluentPrimaryButton, IButtonStyles, IStyle} from 'office-ui-fabric-react';
interface MyPrimaryButtonProps {
label?: string
}
const MyPrimaryButton = ({label}: MyPrimaryButtonProps) => {
const styles: IButtonStyles = {
root: [
{
fontSize: '16px',
background: '#525CA3 ',
border: '1px solid #525CA3',
borderRadius: '20px',
padding: '0px 30px',
height: '40px',
selectors: { // <---
':hover': { // <--- this part doesn't work.
backgroundColor: 'red' // <---
},
}
}
]
};
return (
<div>
<FluentPrimaryButton styles={styles} text={label} />
</div>
);
};
export default MyPrimaryButton;
I get a custom button, but still the hover color remains default blue, instead of switching to red.
You can change the styling of the button when hovered like this:
const btnStyles = {
rootHovered: {
backgroundColor: "red"
}
};
// ...
<FluentPrimaryButton text = {label} styles = {btnStyles} />;

how change vuetify component variable?

I want to change the background color of <v-progress> by using variable
$progress-circular-underlay-stroke but the new value does not affect the output
<template>
<v-progress-circular
:rotate="360"
:size="100"
:width="15"
value="20"
color="primary"
>
20
</v-progress-circular>
</template>
<style lang='scss'>
#import '~vuetify/src/styles/styles.sass';
#import '~vuetify/src/components/VProgressCircular/_variables.scss';
$progress-circular-underlay-stroke = "#FF6859"
</style>
my environment is Vuecli.
You can overwrite its style as given below:
.v-progress-circular__underlay {
stroke: #ff6859 !important;
}
If SASS variable change doesn't work for you like $progress-circular-underlay-stroke = "red"
Then you can do it via directive and it's more efficient if you're using two different color base progress circles. This will style only one instance and not override your plugin style all over the app
---- Todo.vue ----
<v-progress-circular
:rotate="90"
:size="186"
:width="4"
:value="'25%'"
:color="red"
:underlayColor="pink"
v-drv-progress-circle
>
--- ProgressCircle.directive.vue
export const drvProgressCircle = {
bind: function(el, binding, vnode) {
const unFillCircle = el.querySelector('.v-progress-circular__underlay')
const { underlayColor } = vnode.data.attrs
if (underlayColor) {
unFillCircle.style.stroke = underlayColor
}
}
}

firestore document id will be undefined after I update array

I have this flatlist which receive data from firestore and send as props to projectsummery.js
const ProjectList =({projects})=> {
return(
<FlatList
data={projects}
renderItem={(project,index)=>{
return(
<ProjectSummery project={project} key={project.item.id}
//keyExtractor={(item, index) =>item.id}/>
)
} }
/>
)
}
Here I have a button which which sends document id which is something like this
{project.item.id} == CSmet3tRjpjDcJ437M78
ProjectSummery.js
const ProjectSummery =(props)=> {
const {project,auth}=props
return(
<>
<View >
<Text> {project.item.title} </Text>
<Text>likes { project.item.likes.length}</Text>
<Text>{project.item.id}</Text>//document id in in firestore
<View>
<Button title='like' onPress{()=>props.likesPosts(project.item.id)}/>
</View>
</View>
const mapDispatchToProps=(dispatch)=>{
return{
likePosts:(postId)=>dispatch(likePosts(postId))
}
}
When I try to update array in firebase the first time it work but the second time the document id will be undefined. I use React-Native. Thanks for help...
export const likePosts = (postId) => {
return (dispatch,getState,{getFirebase,getFirestore})=>{
const profile=getState().firebase.profile
const authId=getState().firebase.auth.uid
const firestore=getFirestore()
firestore.collection('projects').doc(postId).update({
//this postId will be be undefined in the 2nd time
likes:firestore.FieldValue.arrayUnion({
likedAt:new Date(),
likedBy:authId,
name: profile.firstName
})
})
}}
The fist update postId == CSmet3tRjpjDcJ437M78 in the 2nd time postId will be undefined
What's happening is that when you click a like button the first time, it's working as expected so it gets the proper postId and then continues with the process you have defined. However, when you try the 2nd time it fails to fetch the postId as it's already liked.
The idea is that you'll need to either define an if statement and specify what should happen if it's already clicked and it get's clicked again (possibly storing the postId somewhere the first time and using it from there), or make an initial check that returns a specific message to the user if it's already clicked.
The issue has nothing to do with Firestore itself but with the button and the states of liked/unliked.
Here is one nice interactive example on codepen.io of a proper way of building like buttons using react. React Like Button
HTML
<div id="example"></div>
CSS
.btn-primary {
background-color: #23aa4e;
border-color: #177d37;
}
#example {
margin: 3rem;
}
.customContainer {
border: 1px solid black;
}
JS
class LikeButton extends React.Component {
constructor() {
super();
this.state = {
liked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
liked: !this.state.liked
});
}
render() {
const text = this.state.liked ? 'liked' : 'haven\'t liked';
const label = this.state.liked ? 'Unlike' : 'Like'
return (
<div className="customContainer">
<button className="btn btn-primary" onClick={this.handleClick}>
{label}</button>
<p>
you {text} this. Click to toggle.
</p>
</div>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
)