How can we pass the functions inside of single sx properties , say ( color, zIndex, backgroundColor) , etc? Is this possible somehow? - javascript

I am making a single search component for all the different places into my application.
And to each component I am passing a prop named search : string to check for the truthfulness of that particular component, so as to specify styles for the same.
I know I could use classNames instead of sx prop, but if I pass the sx prop which contain where every single property received the function , which in turn behaves accordingly to the type of which component is active for that moment. ( or way could say , which component has active), this is because my search bar for homepage has different styling then the search bar for items page.
Here's is my code
import { SearchSharp } from "#mui/icons-material";
import { alpha, FormControl, IconButton, InputAdornment, InputBase, makeStyles, styled, SxProps, TextField, Theme, useTheme } from "#mui/material";
import { Fragment } from "react";
import { useThemeContext } from "../../css/ThemeSettings";
import { useAppDispatch, useAppSelector } from "../../Redux/reduxStore";
import { setSearchTerm } from "./jobSlice";
interface Props {
search? : string;
}
export default function SearchAll ( props : Props ) {
// using redux
const { searchTerm } = useAppSelector ( state => state.jobs);
const dispatch = useAppDispatch();
// using theme
const { darkMode } = useThemeContext();
// background color // this is my function to trigger different backgroundColor based on which component is active...
const setBackgroundColor = () => {
switch ( props.search ) {
case 'home' : return darkMode ? 'rgba(0, 0, 0, 0.54)' : 'rgba(238, 240, 243, 1)';
case 'items' : return 'rgba( 255, 255, 255, 1 )';
}};
// similar to the above, i want to make other functions that I could pass into color, // width, position property etc.
// making and using styles
const searchStyles = {
position : 'relative',
borderRadius : 20,
color : 'text.secondary',
width : props.search === 'home' ? '500px' : '100%',
backgroundColor : setBackgroundColor(),
"& .MuiOutlinedInput-root": {
"& > fieldset": { border : "none" } },
'&:hover': {
backgroundColor : props.search === 'items' ? 'none'
: darkMode ? 'rgba(0, 0, 0, 0.54)' : 'rgba(238, 240, 243, 1)',
},
};
return (
<Fragment>
<TextField
variant = {'outlined'}
size = {'small'}
sx = {searchStyles} // i am getting an error here
placeholder = {"Search…"}
fullWidth = { props.search === 'items' ? true : false }
autoFocus = { true } />
</Fragment>
)
}
below is the error which I am getting when hovering on 'sx' keyword, as it is the thing which is throwing the error ...
The system prop that allows defining system overrides as well as additional CSS styles.
Type '{ position: string; borderRadius: number; color: string; width: string; backgroundColor: string | undefined; "& .MuiOutlinedInput-root": { "& > fieldset": { border: string; }; }; '&:hover': { backgroundColor: string; }; }' is not assignable to type 'SxProps<Theme> | undefined'.
Type '{ position: string; borderRadius: number; color: string; width: string; backgroundColor: string | undefined; "& .MuiOutlinedInput-root": { "& > fieldset": { border: string; }; }; '&:hover': { backgroundColor: string; }; }' is not assignable to type 'CSSSelectorObjectOrCssVariables<Theme>'.
Property 'backgroundColor' is incompatible with index signature.
Type 'string | undefined' is not assignable to type 'SystemStyleObject<Theme> | CssVariableType | ((theme: Theme) => string | number | SystemStyleObject<Theme>)'.
Type 'undefined' is not assignable to type 'SystemStyleObject<Theme> | CssVariableType | ((theme: Theme) => string | number |
Is there any way, we could use to pass functions ( which in turn return string) to these individual sx props?
or is there any another way to achieve this?

your setBackgroundColor you use for setting backgroundColor in your searchStyles not compatible with the property backgroundColor (Property 'backgroundColor' is incompatible with index signature.) as is could possible return undefined while backgroundColor requires a "SystemStyleObject | CssVariableType | ((theme: Theme) => string | number | SystemStyleObject)" and does not accept undefined as a valid type
You should be able to fix this by adding a default case to your setBackGroundColor method
const setBackgroundColor = () => {
switch ( props.search ) {
case 'home': return darkMode ? 'rgba(0, 0, 0, 0.54)' : 'rgba(238, 240, 243, 1)';
case 'items': return 'rgba( 255, 255, 255, 1 )';
default: return 'none'
}};

Related

Cannot get object property because property width is missing in undefined

I have this glamor css function, this function when i tried to run flowJS, this function have an error like this, where size? is an object that have padding and width as a string.
Error ------------------------------------------------------------ src/Components/Button/Button.component.style.js:38:17
Cannot get size.width because property width is missing in undefined [1]. [incompatible-use]
[1] 34| size?: Option
35| ): $Shape<CSSStyleDeclaration> =>
36| compose(baseStyle, {
37| backgroundColor: bgColor,
38| width: size.width,
39| padding: size.padding,
40| color: textColor
41| });
My css glamor function :
export const setStyleButton = (
bgColor?: string,
textColor?: string,
size?: Option
): $Shape<CSSStyleDeclaration> =>
compose(baseStyle, {
backgroundColor: bgColor,
width: size.width,
padding: size.padding,
color: textColor
}`);
My Flow Type :
// #flow
export type Option = {|
padding:string,
width:string
|}
export type Options = {|
[key:string] : Option
|}
export type Props = {|
name: string,
color?: string,
textColor?: string,
size?: Option,
onPress: () => void,
disabled: boolean,
|};
can someone help me to fix my problem how to defined object property on flowjs

Refactoring a function to take an object instead of a single key:value pair as arguements in typescript

I am working on a component for a friends icon library, but I am having troubles with their set() function.
The issue is, I have two functions I want to call, setRandomColor() and setColor(), they should both be updating two values in this modelValue computed prop. However the set function only takes one argument (the prop (key) name and the value of that prop(key) ) and is spreading the rest of the props.modelValue values in the emit function.
Since my two setRandomColor() and setColor() are setting two values and each time the function is called, it's spreading the default modelValue, the first modelValue changed will be reverted.
I know this is a really simple question, but I am not able to figure out how to refactor this set() function to take a single OR multiple key: value pairs to update modelValue and emit it properly to it's parent component.
This should be a really quick fix, but I am having a total brainfart.
Any advice or help would be greatly appreciated.
NOTE:
My attempt to refactor the set() function is below this block of code.
Component not working
<script lang="ts" setup>
import { PropType, ref, defineComponent } from 'vue'
import DialogWrapper from '../components/DialogWrapper.vue'
import IconButton from './IconButton.vue'
import Stack from './Stack.vue'
import { getRandomColor, cssVar } from '../helpers/colorHelpers'
import { defaultsIconConfig, IconConfig } from '../types'
import Tooltip from './Tooltip.vue'
import { ColorPicker } from 'vue-color-kit'
import 'vue-color-kit/dist/vue-color-kit.css'
const props = defineProps({
/**
* #example 'type'
*/
kind: {
type: String as PropType<'type' | 'color' | 'stroke' | 'modelValueisDarkMode' | 'background'>,
required: true,
},
/**
* #type {{ name?: string, type: 'pop' | 'print', color: string, stroke: string} & { isDarkMode: boolean }}
*/
modelValue: {
type: Object as PropType<Partial<IconConfig> & { isDarkMode: boolean }>,
default: () => ({ ...defaultsIconConfig({ isDarkMode: false }) }),
},
/**
* #type {{ name?: string, type: 'pop' | 'print', color: string, stroke: string }}
*/
configComputed: {
type: Object as PropType<Partial<IconConfig>>,
default: () => ({ ...defaultsIconConfig() }),
},
})
const emit = defineEmits(['update:modelValue'])
function set(
prop: 'type' | 'color' | 'stroke' | 'isDarkMode' | 'randomColor',
value: string | boolean,
) {
emit('update:modelValue', { ...props.modelValue, [prop]: value })
}
function setRandomColor() {
const randomColor = getRandomColor()
set('color', randomColor)
set('randomColor', true)
}
function setColor(c: string) {
set('color', c)
set('randomColor', false)
}
const nightfall = cssVar('nightfall')
const moonlight = cssVar('moonlight')
function openColorPicker() {
colorPickerIsVisible.value = true
}
let colorPickerIsVisible = ref(false)
function changeColor(color) {
const alpha = ''
const newValue = color.hex + alpha
set('color', newValue)
}
const colorSelection = [
cssVar('sig-purple'),
cssVar('sig-green'),
cssVar('sig-yellow'),
cssVar('sig-blue'),
cssVar('sig-pink'),
]
</script>
<script lang="ts">
export default defineComponent({
inheritAttrs: false,
})
</script>
<template>
<Stack v-if="kind === 'type'" v-bind="$attrs" class="picker" classes="justify-center">
<Tooltip text="Print ❏">
<IconButton
:iconConfig="{
name: 'can',
type: 'print',
color: modelValue.isDarkMode ? 'black' : modelValue.color,
stroke: modelValue.isDarkMode ? modelValue.color : 'black',
}"
:backgroundColor="modelValue.isDarkMode ? moonlight : 'white'"
:isActive="modelValue.type === 'print'"
:activeColor="modelValue.color"
animationClass="anime-shake"
#click="set('type', 'print')"
/>
</Tooltip>
<Tooltip text="Pop!">
<IconButton
:iconConfig="{ ...modelValue, name: 'can', type: 'pop' }"
:backgroundColor="modelValue.isDarkMode ? moonlight : 'white'"
:isActive="modelValue.type === 'pop'"
animationClass="anime-shake"
#click="set('type', 'pop')"
/>
</Tooltip>
</Stack>
<Stack v-if="kind === 'color'" v-bind="$attrs" class="picker" classes="justify-center">
<IconButton
v-for="c in colorSelection"
:key="c"
:iconConfig="{ color: modelValue.color }"
:backgroundColor="c"
:isActive="modelValue.color === c && modelValue.randomColor === false"
#click="setColor(c)"
/>
<IconButton
:iconConfig="{ ...configComputed, name: 'color-picker' }"
:backgroundColor="modelValue.isDarkMode ? moonlight : 'white'"
:colorRing="true"
#click="colorPickerIsVisible = true"
/>
<IconButton
:iconConfig="{ ...configComputed, name: 'refresh' }"
:backgroundColor="modelValue.isDarkMode ? moonlight : 'white'"
:colorRing="true"
:isActive="modelValue.randomColor"
#click="setRandomColor"
/>
</Stack>
<Stack v-if="kind === 'background'" v-bind="$attrs" class="picker" classes="justify-center">
<IconButton
backgroundColor="white"
class="_background-picker thin-border--dark"
:iconConfig="{ name: 'sun-filled', type: 'pop', color: 'black' }"
#click="set('isDarkMode', false)"
/>
<IconButton
:backgroundColor="nightfall"
class="_background-picker thin-border--light"
:iconConfig="{ name: 'moon-filled', type: 'pop', color: 'white' }"
#click="set('isDarkMode', true)"
/>
</Stack>
<Stack v-if="kind === 'stroke'" v-bind="$attrs" class="picker" classes="justify-center">
<input type="color" #change="() => set('color', '#e2e2e2')" />
</Stack>
<DialogWrapper :isVisible="colorPickerIsVisible" #close="colorPickerIsVisible = false">
<ColorPicker
:theme="modelValue.isDarkMode === true ? 'dark' : 'light'"
:color="modelValue.color"
#changeColor="(val) => changeColor(val)"
/>
</DialogWrapper>
</template>
<style lang="sass">
#import '../css/variables.sass'
.picker
._background-picker svg
opacity: 0.1
.picker-tooltip
font-size: 1.5em
+pa($md)
+C(background, primary)
border-radius: $md
font-weight: 500
white-space: nowrap
.hu-color-picker
.color-set
justify-content: space-evenly
.color-alpha
display: none
</style>
This is my attempt to refactor the function, but when I'm spreading the array of objects from the set() argument, I'm still just getting two objects and am stuck from this point.
Attempts to refactor the set() function
function set(
object: {
prop: 'type' | 'color' | 'stroke' | 'isDarkMode' | 'randomColor'
value: string | boolean
}[],
) {
console.log(`object → `, ...object)
emit('update:modelValue', { ...props.modelValue, ...object })
}
function setRandomColor() {
const randomColor = getRandomColor()
set([
{ prop: 'color', value: randomColor },
{ prop: 'randomColor', value: true },
])
// set('color', randomColor)
// set('randomColor', true)
}
function setColor(c: string) {
set([
{ prop: 'color', value: c },
{ prop: 'randomColor', value: false },
])
// set('color', c)
// set('randomColor', false)
}
If I understand you correctly you want the set function to accept one OR more objects?
Add spread ”…” before the parameter. Sadly Im on my phone and snippet editor doesnt support touch devices.
function set(
… object: {
prop: 'type' | 'color' | 'stroke' | 'isDarkMode' | 'randomColor'
value: string | boolean
}[],
) {
More reading: https://www.c-sharpcorner.com/UploadFile/c63ec5/use-of-params-in-typescript/
To handle the parameter objects I guess something like:
{ …props.modelValue, …object.reduce((acc, {prop, value}) => {…acc, [prop]: value}, {});

Typescript Type to accept a React component type with subset of Props

There is a component Button with following props:
ButtonProps = {
variant: 'primary' | 'secondary' | 'tertiary';
label: string;
// a few more props like onChange, size etc.
}
Now, I want to create another component called "ButtonGroup" component that accepts a Button instance as a prop but can only accept primary or secondary variant.
How can I enforce that?
ButtonGroup component looks this:
<ButtonGroup
primaryButton={<Button variant="primary">Submit</Button>}
otherButton={<Button variant="secondary">Cancel</Button>}
/>
Now, the props for ButtonGroup are as follwos:
type PrimaryButtonProps = Omit<ButtonProps, 'variant'> & {
variant: 'primary' | 'secondary';
};
type ButtonGroupProps = BaseComponentProps<'div'> & {
size?: 'small' | 'medium';
primaryButton: React.ReactElement<PrimaryButtonProps, typeof Button>;
otherButton?: React.ReactElement<OtherButtonProps, typeof Button>;
};
I expect primaryButton to be a Button instance will all Button props but restricting variant to be either primary or secondary. But, with this current implementation, typescript doesn't complain if I provide a tertiary variant too.
<ButtonGroup
primaryButton={<Button variant="tertiary">Submit</Button>} // TS SHOULD COMPLAIN BUT IT DOES NOT
/>
In my opinion the cleanest solution would be to separate the implementation of each component to enforce its specific types.
interface ButtonProps {
variant: "primary" | "secondary" | "tertiary";
children?: React.ReactNode;
}
const Button = ({ variant, children }: ButtonProps): React.ReactElement => (
<button>{children}</button> // apply styles based on the variant
);
interface PrimaryButtonProps {
label: string;
variant: "primary" | "secondary";
}
const PrimaryButton = ({ label, variant }: PrimaryButtonProps) => (
<Button variant={{ variant }}>{{ label }}</Button>
);
So, when you create a ButtonGroup, you should pass the specific PrimaryButton type, instead the generic one
type ButtonGroupProps = BaseComponentProps<'div'> & {
size?: 'small' | 'medium';
primaryButton: React.ReactElement<PrimaryButtonProps, typeof PrimaryButton>;
// ...
};
<ButtonGroup
primaryButton={<PrimaryButton variant="tertiary">Submit</PrimaryButton>} // TS should complain here
/>
Hope this helps!
You can use PrimaryButton component instead of Button and it will highlight the variant other than primary or secondary.
Checkout the working CodeSandbox here.
interface ButtonProps {
variant: "primary" | "secondary" | "tertiary";
label: string;
}
function Button({ variant, label }: ButtonProps) {
return (
<button style={{ color: variant === "primary" ? "blue" : "red" }}>
{label}
</button>
);
}
type PrimaryButtonProps = Omit<ButtonProps, "variant"> & {
variant: "primary" | "secondary";
};
interface ButtonGroupProps {
primaryButton: React.ReactElement<PrimaryButtonProps>;
}
function ButtonGroup({ primaryButton }: ButtonGroupProps) {
return <div>{primaryButton}</div>;
}
// See below type assertion. It's a hack but it works :)
const PrimaryButton = Button as (props: PrimaryButtonProps) => JSX.Element;
export default function App() {
return (
<div className="App">
<ButtonGroup
primaryButton={<PrimaryButton variant="tertiary" label="Primary" />}
/>
</div>
);
}

setState in react it seems that it delete all state content

I wrote an object style and I was calling it in style of any element.
It was cool tell i wanted to edit one style value of some element, so I tried to change it like
onClick={{cardStyle.li.backgroundColor : "black }}
But that gave me a read only error, so I put the cardStyle object in state in order using setState, and now when i try to change anyvalue using setState, it seems that it only puts that value and deleted everything from it
This is the code:
import React from "react";
export default class CardInfo extends React.Component
{
constructor(props){
super(props);
this.state = { cardStyle : {
ul : {
width : '80%',
margin : '5% 10%',
listStyleType : "none",
},
li :{
width : "30%",
border : " 4px solid rgb(0, 0, 0 , 0.6) ",
borderRadius : "7%",
textAlign : "center",
paddingTop : "3%",
marginRight : '1%',
float : 'left',
backgroundColor : this.props.Fname === "MUHAMMAD" ? "rgb(255, 10, 92 , .7)" : "white",
},
img : {
width : " 80%",
borderRadius : "7%",
},
on : {backgroundColor : "black"}
}}
}
render()
{
return <ul style={this.state.cardStyle.ul}>
<li style={this.state.cardStyle.li}
onClick={() =>this.setState({cardStyle:{li:{backgroundColor : "grey"}}})} >
<img style={this.state.cardStyle.img} src={this.props.img} alt="lilpic" />
<h2>{this.props.Fname}</h2>
<p>{this.props.quetes}</p>
<p>{this.props.phone}</p>
</li>
</ul>
}
}
You are changing state and overriding it with new values, instead you should override the property you want and give rest of the properties as well. So replace your on click this.setState with
this.setState({cardStyle:{ ...this.state.cardStyle, li:{backgroundColor : "grey"}}})} >

OnLayout to get image Dimensions crash on second render

i need a bit of help. Im trying to implement a screen in React native to place 'Tags' like instagram does in his photos. My workaround to this problem is to get the current image onLayout props and render based on that info. The screen takes the image width and height, and places the tags in the equivalent precentaje location of the image. It works just fine until i re open the Tagging screen a second time and the app just crash...
The reson of the crash it's because im styling the positions of the tags dynamiclly in the styled component (left:'X'px, top: 'X'px), but as i open the Tagging screen a second time, the onLayout of my image returns an undefined and the styled component crashes at trying to set left: 'undefined'px (or top)
i tried everything and i'm out of ideas... Here is the code im using (Sorry, it's a mess...)
import React, {useState} from 'react';
import {
Dimensions,
LayoutRectangle,
NativeTouchEvent,
StyleSheet,
View,
} from 'react-native';
import {Text} from '../../styles';
import LazyImage from '../LazyImage';
import {TagsContainer, TouchWrapper, TagItem} from './styled';
interface iTags {
id: string;
name: string;
locationX: number;
locationY: number;
}
interface iProps {
imageUri: string;
tagsArray: Array<iTags>;
onTouch: (percentX: number, percentY: number) => void;
onDeleteTag: (Tag: iTags) => void;
}
const ImageTaged: React.FC<iProps> = (props) => {
const [isImageLoading, setImageLoading] = useState(true);
const [getLayout, setLayout] = useState<LayoutRectangle>(
{} as LayoutRectangle,
);
function handleLayout(layout: LayoutRectangle) {
setLayout(layout);
}
function handleEvent(e: NativeTouchEvent) {
const {width, height} = getLayout;
const percentageX = Number(((e.locationX * 100) / width).toFixed(2));
const percentageY = Number(((e.locationY * 100) / height).toFixed(2));
props.onTouch(percentageX, percentageY);
}
function deleteTag(tag: iTags) {
props.onDeleteTag(tag);
}
function handleLoadEnd() {
setImageLoading(false);
}
return (
<TagsContainer
onTouchStart={({nativeEvent}) =>
isImageLoading ? undefined : handleEvent(nativeEvent)
}>
<LazyImage
sourceUri={props.imageUri}
onLayout={handleLayout}
onLoadEnd={handleLoadEnd}
/>
{props.tagsArray &&
props.tagsArray.map((c: iTags) => (
<TagItem
key={c.id}
locationX={(c.locationX * getLayout.width - 22) / 100}
locationY={(c.locationY * getLayout.height) / 100}
onTouchStart={() => deleteTag(c)}>
<View style={styles.tagTriangle} />
<View style={styles.tagUserView}>
<Text style={styles.tagListText}>
{c.name.length > 10
? c.name.substring(0, 11) + '. . .'
: c.name}
</Text>
</View>
</TagItem>
))}
</TagsContainer>
);
};
const styles = StyleSheet.create({
/
tagTriangle: {
height: 0,
width: 0,
left: 7,
borderLeftColor: 'transparent',
borderLeftWidth: 7,
borderRightColor: 'transparent',
borderRightWidth: 7,
borderBottomColor: 'rgba(244, 147, 144, 0.8)',
borderBottomWidth: 7,
},
tagUserView: {
backgroundColor: 'rgba(244, 147, 144, 0.8)',
borderRadius: 5,
borderWidth: 1,
borderColor: 'rgba(244, 147, 144, 0.8)',
paddingLeft: 10,
paddingRight: 10,
paddingTop: 3,
paddingBottom: 3,
flexDirection: 'row',
},
tagListText: {
color: 'white',
fontWeight: '800',
},
});
export default ImageTaged;
And just in case the Tag styled component used to render the Tag
export const TagItem = styled.View`
position: absolute;
top: ${(props) => `${props.locationY}px`};
left: ${(props) => `${props.locationX}px`};
justify-content: center;
`;
The component basiclly is receiving an Array of tags it needs to display in a modal when the user touches the Image, then the user chooses an option from the displayed Modal and the tag gets displayed on top of the Image.
I took this exact concept from a npmjs.com package called 'react-native-image-tagging-same-like-instagram'
I don't know how you implement the TagItem but it is clear that it cant accept undefined as locationX or locationY. so I think you can pass 0 for example if the coordinates haven't been calculated. and in the tagItem check if it is 0 then simply hide the tag.
locationX={c && c.locationX && getLayout && getLayout.width ? (c.locationX * getLayout.width - 22) / 100 : 0}
locationY={c && c.locationY && getLayout && getLayout.height ? (c.locationY * getLayout.height) / 100 : 0}
Thanks everybody for your answers. I just fixed the issue because the Tags tryes to display themselves before onLayout method was called, so they returned undefined to the stylesheet, and therefore, the error.
Thanks to some answers and some thinking trough, i realized all i had to do is, like Alireza replied, check if layout Obj != undefined before rendering the tags. So dumb from my part haha.
Thanks, now the code is working

Categories

Resources