How can I render a SVG based on ternary operator inside a JSX? - svg

I am using Stencil.js, but the syntax is similar to React.
const iconSVG = <svg>...</svg>
return (
<button>
{this.icon ?
this.position === 'left'
? iconSVG `${this.label}`
: `${this.label} icon`
: this.label}
</button>
);
This gives me an error: iconSVG is not a function
return (
<button>
{this.icon ?
this.position === 'left'
? <svg> ...</svg> `${this.label}`
: `${this.label} icon`
: this.label}
</button>
);
This doesn't work because of the this.label. There can only be one element (value).
return (
<button>
{this.icon ?
this.position === 'left'
? `${<svg> ...</svg>} `${this.label}`
: `${this.label} icon`
: this.label}
</button>
);
This gives me [Object object] inside the Button next to the label.
const iconSVG = () => <svg> ...</svg>
return (
<button>
{this.icon ?
this.position === 'left'
? iconSVG() `${this.label}`
: `${this.label} icon`
: this.label}
</button>
);
This gives me an error: iconSVG(...) is not a function obviously because the JSX is read first.
So, how do I do it? How can I render the SVG inside the JSX?

You can try :
return (
<button>
{this.icon && this.position === 'left' && <svg>...</svg>}
{this.label}
{this.icon && this.position !== 'left' && ' icon'}
</button>
);

Because you are specifying more than one item inside an expression, you need to use an array:
#1
render() {
const iconSVG = <svg></svg>;
return (
<button>
{this.icon ?
this.position === 'left'
? [iconSVG, this.label]
: `${this.label} icon`
: this.label}
</button>
);
}
#2
render() {
return (
<button>
{this.icon
? this.position === 'left'
? [<svg> ...</svg>, this.label]
: `${this.label} icon`
: this.label}
</button>
);
}
#3 would be the same as #2.
#4
render() {
const iconSVG = () => <svg> ...</svg>;
return (
<button>
{this.icon
? this.position === 'left'
? [iconSVG(), this.label]
: `${this.label} icon`
: this.label
}
</button>
);
}

Use your svg as following.
export const IconSvg = () => {
return (
<svg>
...
</svg>
);
};
And then , used with your ternary operator.
eg.
import React from "react";
const isRed = true;
export const RedIconSvg = () => {
return (
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
);
};
export const BlueIconSvg = () => {
return (
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
);
};
function App() {
return (
<div className="App">
{isRed ? <RedIconSvg/> : <BlueIconSvg/>}
</div>
);
}
export default App;

Related

react-virtualized: Table Column with CellMeasurer always has cache height

I've been trying to follow the DynamicHeightTableColumn example and adapt it for my use case. I can see in the CellMeasurer demo that the single-line rows have the default height, defined when initializing the cache. However, in my case, it seems like single-line rows always have the tallest row height, stored in the cache, instead of the default one.
Here is my code:
import * as React from 'react';
import * as Immutable from 'immutable';
import { Table, Column, AutoSizer, CellMeasurer, CellMeasurerCache } from 'react-virtualized';
interface Props {
logs: Immutable.List<Immutable.Map<string, any>>;
columns: (
width: number
) => Array<{
name: string;
key: string;
width: number;
variableHeight?: boolean;
}>;
}
export default class LogTable extends React.Component<Props, {}> {
private cache: CellMeasurerCache;
constructor(props) {
super(props);
this.cache = new CellMeasurerCache({
defaultHeight: 20,
fixedWidth: true,
keyMapper: () => 1
});
}
render() {
const { logs } = this.props;
return (
<AutoSizer disableHeight>
{({ width }) => (
<Table
headerHeight={20}
height={250}
rowCount={logs.size}
rowHeight={this.cache.rowHeight}
width={width}
overscanRowCount={2}
rowRenderer={this.rowRenderer}
headerClassName='col-header'
gridClassName='log-table-grid'
rowClassName='log-table-row'
className='log-table'
rowGetter={({ index }) => logs.get(index)}
deferredMeasurementCache={this.cache}>
{this.renderColumns(width)}
</Table>
)}
</AutoSizer>
);
}
private renderColumns(width) {
return this.props.columns(width).map(({ name, key, width, variableHeight }, idx) => {
const props: any = {
label: name,
dataKey: key,
width,
key,
className: 'column'
};
if (variableHeight) {
props.cellRenderer = this.cellRenderer.bind(this, idx);
}
return <Column {...props} />;
});
}
private rowRenderer(params) {
const { key, className, columns, rowData, style } = params;
if (!rowData) {
return null;
}
return (
<div className={className} key={key} style={style}>
{columns}
</div>
);
}
private cellRenderer(colIndex, { dataKey, parent, rowIndex }) {
const content = this.props.logs.get(rowIndex).get(dataKey);
return (
<CellMeasurer
cache={this.cache}
columnIndex={colIndex}
key={dataKey}
parent={parent}
rowIndex={rowIndex}>
<div style={{ whiteSpace: 'normal' }} title={content}>
{content}
</div>
</CellMeasurer>
);
}
}
And this is the output (see 2nd row in the table that's too tall for its content)
The only styling (less) I have is the following, which I don't think can cause this behavior
.log-table {
font-size: 14px;
.col-header {
font-size: 15px;
text-align: center;
}
.log-table-grid {
outline: none;
font-family: monospace;
}
.log-table-row {
border-bottom: 1px solid gray;
padding: 3px;
}
}

Moving single characters from a certain position to another position automatically

Which library in React to create animation of moving a single char from a string to a new position automatically?
This is an implementation without any library:
class AnimateChar extends React.Component {
animateChar = () => {
const { children, charIndex } = this.props;
return (
children.split('').map((char, i) => (
<span className={i === charIndex ? 'animate' : ''}>
{char}
</span>
))
)
}
render() {
return this.animateChar();
}
}
ReactDOM.render(<AnimateChar charIndex={2}>moveme</AnimateChar>, document.getElementById('root'))
#keyframes move {
0% {transform: translateY(0)}
100% {transform: translateY(12px)}
}
.animate {
display: inline-block;
animation: move .5s ease-in-out forwards
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Few newbie react-native questions

I'm new to react and react-native (and pretty much everything in general...), I'm trying to follow facebook's tutorial on state and props. I'm playing around with the code and I ran into some issues.
I've modified my Apps.js to look like this:
import React, { Component } from 'react';
import { Text, View, Image, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f1f',
alignItems: 'center',
justifyContent: 'center',
},
});
class Blink extends Component {
constructor(props) {
super(props);
this.state = {showText: true};
// Toggle the state every second
setInterval(() => {
this.setState(previousState => {
return { showText: !previousState.showText };
});
}, 1000);
}
render() {
let display = this.state.showText ? this.props.text1 : this.props.text2;
let pic = this.state.showText ? {uri: this.props.url1} : {uri: this.props.url2}
return (
<View>
<Image source={pic} style={{width: 193, height: 110}}/>
<Text>{display}</Text>
</View>
);
}
}
export default class BlinkApp extends Component {
render() {
return (
<View style={styles.container}>
<Blink text1='test1' />
<Blink text2='test2' />
<Blink url1='https://www.gizbot.com/img/2015/08/10-1439192289-lolemoji.jpg' />
<Blink url2='http://s3.india.com/wp-content/uploads/2015/08/haha-nelson-simpsons.jpg' />
</View>
);
}
}
Instead of blinking three lines of text like the one in the tutorial, it's suppose to blink two different images with two different lines of text (text1 and text2) using my own StyleSheet. The issue I'm running into is that they are not properly aligned. I can't get them to center no matter what I try.
The two different images it displays/blinks are here and here
What I want them to look like are here and here
So my questions are:
1) Does it matter where I define style? e.g. in my code, I've included them inside the second render, but I've noticed that if I put them inside the first render, different results occur. What is the difference?
2) Am I using props incorrectly here? What should I be doing instead to define the two images and texts as part of my props?
3) Are setInterval and previousState native react libraries? How are they being called? i.e. what's triggering setInterval to change the value of showText?
4) when is setState being called?
I rewrite your code, and I think it works as your expect now.
to your questions:
1&2: the way you use style and props is wrong, see my code below.
3: setInterval is native react libraries, you can find the usage in https://facebook.github.io/react-native/docs/timers.html
4: setState is called by your code, please look at the document https://facebook.github.io/react-native/docs/state.html
import React, { Component } from 'react';
import { Text, View, Image, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f1f',
alignItems: 'center',
justifyContent: 'center',
},
});
class Blink extends Component {
render() {
const { text, url } = this.props;
console.log('url' + url);
return (
<View>
<Image source={{uri:url, cache: 'force-cache'}} style={{width: 193, height: 110}}/>
<Text>{text}</Text>
</View>
);
}
}
export default class BlinkApp extends Component {
constructor(props) {
super(props);
this.state = {
showFirst: true,
};
}
componentDidMount() {
setInterval(() => {
this.setState({
showFirst: !this.state.showFirst
});
}, 1000);
}
render() {
const showFirst = this.state.showFirst;
return (
<View style={styles.container}>
{showFirst && <Blink url="https://www.gizbot.com/img/2015/08/10-1439192289-lolemoji.jpg" text='test1' />}
{!showFirst && <Blink url="http://s3.india.com/wp-content/uploads/2015/08/haha-nelson-simpsons.jpg" text='test2' />}
</View>
);
}
}

How can I dynamically switch react components using a string or state?

Hopefully the script bellow will show roughly what I am trying to achieve.
But essentially, using a string stored in a state, be able to have a component holder dynamically load different components on change.
So you will see 2 component imported at the top (but ideally these could be a 100 different ones.
A currentSlide state that holds the string name of the component.
And a SlideLoader component that would ideally load imported components based on a string name.
Then the button's switch the component through resetting the state name.
Thank you!
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import SlideA from './slideA';
import SlideB from './slideB';
const styles = StyleSheet.create({
dummyContainer: {
flex: 0,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: '#999999',
},
buttonHolder: {
position: 'absolute',
top: 4,
left: 0,
},
button: {
height: 50,
width: 300,
backgroundColor: 'red',
marginBottom: 4,
textAlignVertical: 'center',
textAlign: 'center',
fontWeight: 'bold',
},
});
export default class Switcher extends Component {
constructor(props, context) {
super(props, context);
let state = {
currentSlide: 'SlideA',
}
}
render() {
// obvisously wrong part...
let SlideLoader = this.state.currentSlide;
// end of obvisously wrong part...
return (
<View
style={[
styles.dummyContainer,
]}
>
<SlideLoader />
<View
style={styles.buttonHolder}
>
<Text
onPress={() => {
console.log('SLID A');
setState({ currentSlide: 'SlideA' });
}}
style={[styles.button]}
>
Slide A
</Text>
<Text
onPress={() => {
console.log('SLID B');
setState({ currentSlide: 'SlideB' });
}}
style={[styles.button]}
>
Slide B
</Text>
</View>
</View>
);
}
}
ok here we go :
render() {
let SlideLoader;
if(this.state.currentSlide == 'SlideA')
SlideLoader=<SlideA />
else
SlideLoader=<SlideB />
return (
<View
style={[
styles.dummyContainer,
]}
>
{SlideLoader}
<View
style={styles.buttonHolder}
>
<Text
onPress={() => {
console.log('SLID A');
setState({ currentSlide: 'SlideA' });
}}
style={[styles.button]}
>
Slide A
</Text>
<Text
onPress={() => {
console.log('SLID B');
setState({ currentSlide: 'SlideB' });
}}
style={[styles.button]}
>
Slide B
</Text>
</View>
</View>
);
}
Cheers:)

React Native : Searching, Filter, and Base View all in one route

I would like to ask, is there any solution to come out with something like Messenger(Mobile App) searching function? The route is having a basic view elements. When user is typing in some words, the search will start to function, within the same screen, another view overlap to show the filter list. I had tried to use react-native-overlap module but it not work well.
var React = require ('react-native');
var {
Text, View, StyleSheet, TextInput, Dimensions, Image, Platform, ScrollView, ReactNative, DeviceEventEmitter,TouchableOpacity
} = React;
var NavigationBar = require ('../Components/NavigationBar');
var SearchBarFromNodeModule = require ('react-native-search-bar');
var Overlay=require('react-native-overlay');
var Recent = React.createClass({
getInitialState(){
return {
isVisible:'aa',
};
},
onNameChanged(e){
//todo something here
// alert('asd');
},
render (){
return (
<View>
<View style={styles.navbarContainer}>
<SearchBarFromNodeModule ref='searchBar' placeholder='Search' showsCancelButton={true} onChange={this.onNameChanged} />
</View>
<View style={{flex:1, flexDirection:'column'}}>
<View style={{backgroundColor:'#F4FA58',flex:1,height:40}}/>
<View style={{backgroundColor:'#58FAF4',flex:1,height:40}}/>
<View style={{backgroundColor:'#F4FA58',flex:1,height:40}}/>
<View style={{backgroundColor:'#58FAF4',flex:1,height:40}}/>
<View style={{backgroundColor:'#F4FA58',flex:1,height:40}}/>
<View style={{backgroundColor:'#58FAF4',flex:1,height:40}}/>
</View>
</View>
);
},
});
var styles=StyleSheet.create({
navbarContainer: {
height: 65,
paddingTop: 20,
},
button:{
height:40,
textAlign:'center',
fontSize:18,
marginBottom:10,
marginTop:10,
color:'blue',
}`enter code here`
});
module.exports = Recent;
Now there is some color boxes after the search bar, when user is typing something, onChange function will take place. But I have no idea how to overlap the color boxes.
This gets the job mostly done. The only issue is the search results box renders under the text view below the search input. This could be corrected in a few ways but I personally would likely look to the zIndex feature introduced in 0.30 that is coming out soon.
https://rnplay.org/apps/CJNPWg
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TextInput
} from 'react-native';
class SearchResults extends React.Component {
render() {
if (this.props.search.toUpperCase() !== 'BAM') {
return null;
}
return (
<View style={styles.searchResults}>
<Text>
Do whatever your heart desires here... Might I recomend a list view??
</Text>
</View>
);
}
}
class SearchBox extends React.Component {
render() {
return (
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
value={this.props.search}
onChangeText={this.props.onChange}
placeholder="Try 'BAM'!"
/>
<SearchResults search={props.search} />
</View>
);
}
}
class SampleApp extends React.Component {
state = {
currentSearch: ''
};
onCurrentSearchChange(text) {
this.setState({
currentSearch: text
});
}
render() {
return (
<View style={styles.container}>
<Text>Enter your search</Text>
<SearchBox search={this.state.currentSearch} onChange={this.onCurrentSearchChange.bind(this)} />
<Text>Some text below the search box...</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30
},
searchResults: {
position: 'absolute',
backgroundColor: 'white',
borderColor: 'black',
borderWidth: 1
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);

Resources