trying to generate a PDF and view or email it with React Native - email

I spent the last few days playing with react-native-html-to-pdf (https://github.com/christopherdro/react-native-html-to-pdf ), react-native-mail (by chirag04) and react-native-view-pdf (by cnjon)
There is another version of react-native-mail by parkerdan that I have yet to try, but the chrirag04's version basically corrupted all my projects and was a pain to uninstall.
react-native-html-to-pdf doesn't seem to generate any error, and I can't seem have access to the pdf generated. here a snippet of the code I am running:
import RNHTMLtoPDF from 'react-native-html-to-pdf';
import PDFView from 'react-native-pdf-view';
...
createPDF() {
var options = {
html: '<h1>PDF TEST</h1>', // HTML String
// ****************** OPTIONS BELOW WILL NOT WORK ON ANDROID **************
fileName: 'test', /* Optional: Custom Filename excluded extention
Default: Randomly generated
*/
directory: 'docs', /* Optional: 'docs' will save the file in the `Documents`
Default: Temp directory
*/
height: 800, /* Optional: 800 sets the height of the DOCUMENT that will be produced
Default: 612
*/
width: 1056, /* Optional: 1056 sets the width of the DOCUMENT that will produced
Default: 792
*/
padding: 24, /* Optional: 24 is the # of pixels between the outer paper edge and
corresponding content edge. Example: width of 1056 - 2*padding
=> content width of 1008
Default: 10
*/
};
RNHTMLtoPDF.convert(options).then((filePath) => {
AlertIOS.alert(
'creat pdf',
'filePath=' + filePath
);
return (
<PDFView ref={(pdf)=>{this.pdfView = pdf;}}
src={filePath}
onLoadComplete = {(pageCount)=>{
this.pdfView.setNativeProps({
zoom: 1.5
});
}}
/>
)
});
};
and later in the code I call it with:
<TouchableHighlight onPress={this.createPDF} style={styles.button}>
<Text>create pdf </Text>
</TouchableHighlight>
I get the AlertIOS, with something that looks like a valid filepath (any hint to check the path is correct, let me know)
But that's it, I don't seem to find the test.pdf document anywhere.
Can anyone tell what I am doing wrong?
Many Thanks,
Cheufte

I think the file path is document directory you can go through the file path by first clicking the windows option in xcode after that find devices option upon clicking device option all the information of your device will appear then select the application and see it's container and you will find your pdf file.
var localpath= RNFS.DocumentDirectoryPath + filePath
<PDFView ref={(pdf)=>{this.pdfView = pdf;}}
path={localpath}
onLoadComplete = {(pageCount)=>{
this.pdfView.setNativeProps({
zoom: 1.5
});
}}
/>
write path in place of src because it is deprecated.

import React, { Component } from 'react';
import {
AlertIOS,
AppRegistry,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import RNHTMLtoPDF from 'react-native-html-to-pdf';
export default class testApp extends Component {
createPDF() {
var options2 = {
html: '<h1>PDF TEST</h1>', // HTML String
// ****************** OPTIONS BELOW WILL NOT WORK ON ANDROID **************
fileName: 'test2', /* Optional: Custom Filename excluded extension
Default: Randomly generated
*/
directory: 'docs', /* Optional: 'docs' will save the file in the `Documents`
Default: Temp directory */
base64: true ,
height: 800,
width: 1056, /* Optional: 1056 sets the width of the DOCUMENT that will produced
Default: 792
*/
padding: 24, /* Optional: 24 is the # of pixels between the outer paper edge and
corresponding content edge. Example: width of 1056 - 2*padding
=> content width of 1008
Default: 10
*/
};
RNHTMLtoPDF.convert(options2).then((data2) => {
console.log(data2.filePath);
console.log(data2.base64);
AlertIOS.alert(
'options2 filename' + options2.fileName,
'data2 filePath=' + data2.filePath
);
});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to testApp
</Text>
<TouchableHighlight onPress={this.createPDF}>
<Text>Create PDF</Text>
</TouchableHighlight>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('testApp', () => testApp);

Related

AgGrid Custom Cell Editor does not refresh when calling refreshCells()

TLDR; the cell editor does not rerender when we invoke the api.refreshCells method.
The following code renders a simple table with 2 rows. The value of the data is irrelevant since it uses both a custom cell renderer and a custom cell editor that reaches into context to pluck a value.
When context updates we need to call refreshCells, since no change is detected to the actual cell data value.
Note when clicking increment the value of the context is incremented and the value of the cells updates accordingly. Observe the console log messages for each view renderer.
Now double click cell to enter into edit mode and then click the increment button. Note that the cell editor is not re-rendered when the increment takes place.
I can get the editor to update using events but the props (and so the context) are stale.
Presume this is a design decision, but I need to rerender the cell editor component when my context data updates. Any ideas?
https://plnkr.co/edit/3cCRnAY14nOWt3tl?open=index.jsx
const EditRenderer = (props) => {
console.log('render edit cell')
return (
<input
value={props.context.appContext.cellValue}
/>
);
};
const ViewRenderer = (props) => {
console.log('render view cell')
return <React.Fragment>{props.context.appContext.cellValue}</React.Fragment>;
};
const Grid = () => {
const [rowData] = useState([{ number: 10 }, { number: 3 }]);
const columnDefs = useMemo(
() => [
{
field: 'number',
cellEditor: EditRenderer,
cellRenderer: ViewRenderer,
editable: true,
width: 200,
},
],
[]
);
const [context, setContext] = useState({ cellValue: 1 });
const gridApiRef = useRef();
return (
<React.Fragment>
<p>Context value: {JSON.stringify(context)}</p>
<button
onClick={() => {
setContext({ cellValue: context.cellValue += 1 });
gridApiRef.current.refreshCells({force: true});
}}
>
Increment
</button>
<div style={{ width: '100%', height: '100%' }}>
<div
style={{
height: '400px',
width: '200px',
}}
className="ag-theme-alpine test-grid"
>
<AgGridReact
onGridReady={(e) => {
gridApiRef.current = e.api;
}}
context={{ appContext: context }}
columnDefs={columnDefs}
rowData={rowData}
/>
</div>
</div>
</React.Fragment>
);
};
render(<Grid />, document.querySelector('#root'));

Markers clustering in #react-native-mapbox-gl/maps

I am using #react-native-mapbox-gl/maps and I want to implement clustering for markers. I couldn't find any solution for my implementation. Attach image will show that two markers should be combined but they are not.
Below I am pasting my code:
<MapboxGL.MapView
showUserLocatin={true}
zoomLevel={10}
zoomEnabled={zoomEnabled}
pitchEnabled={true}
onPress={onMapPress}
onRegionIsChanging={onRegionIsChanging}
surfaceView={true}
rotateEnabled={rotateEnabled}
compassEnabled={false}
showUserLocation={false}
userTrackingMode={MapboxGL.UserTrackingModes.None}
scrollEnabled={scrollEnabled}
styleURL={styleURL}
centerCoordinate={getFocusPoint() || getStartingPoint()}
ref={(c) => (_map = c)}
onRegionDidChange={onRegionChange}
style={style}
cluster
>
{renderLines()}
<MapboxGL.SymbolLayer
id={'abc'}
sourceID={MapboxGL.StyleSource.DefaultSourceID}
/>
<MapboxGL.Camera
zoomLevel={zoom}
centerCoordinate={getFocusPoint() || getStartingPoint()}
/>
{(simplePlaceData?.length > 0 || places?.length > 0) && renderMarkers()}
</MapboxGL.MapView>
Below is our renderMarkers function( basically I am displaying any RN component like image/icon inside MapboxGL.PointAnnotation):
const renderMarkers = () => {
if (simplePlaceData)
return simplePlaceData?.map((_place) => {
const {lat, lng, id} = _place
const latVal = parseFloat(lat)
const lngVal = parseFloat(lng)
if (!lat || !lng || isNaN(latVal) || isNaN(lngVal)) return null
return (
<MapboxGL.PointAnnotation
key={`${id}`}
id={`${id}`}
title={`${lat}-${lng}`}
coordinate={[parseFloat(lng), parseFloat(lat)]}>
<Col style={styles.mapMarkers}>
<Icon
name={'map-marker'}
family={'materialCommunity'}
color={Colors.linkBlue}
size={31}
/>
</Col>
</MapboxGL.PointAnnotation>
)
})
else
return places?.length > 0 && places.map(_place => {
const {lat, lng, id, image, name} = _place.trip_place.place
const isSelected = (getFocusPoint() || getStartingPoint())?.first() == lng &&
(getFocusPoint() || getStartingPoint())?.last() == lat
if (Platform.OS === 'ios') {
return (
<MapboxGL.PointAnnotation
key={`${id}`}
id={`${id}`}
title={name}
coordinate={[parseFloat(lng), parseFloat(lat)]}
>
<MapMarker
image={{uri: image}}
imageSize={isSelected ? 41 : 31}
style={isSelected ? styles.mapMarkersSelected : styles.mapMarkers}
onPress={() => selectPlace(_place.trip_place.place, true)}
/>
</MapboxGL.PointAnnotation>
)
} else {
return (
<MapboxGL.MarkerView
id={`${id}`}
key={`${id}`}
coordinate={[parseFloat(lng), parseFloat(lat)]}
title={name}
>
<View style={isSelected ? styles.mapMarkerContainerSelected : styles.mapMarkerContainer}>
<MapMarker
image={{uri: image}}
imageSize={isSelected ? 41 : 31}
style={isSelected ? styles.mapMarkersSelected : styles.mapMarkers}
onPress={() => selectPlace(_place.trip_place.place, true)}
/>
</View>
</MapboxGL.MarkerView>
)
}
})
}
Is there any solution to to apply for MapboxGL.PointAnnotation to show markers as a combined cluster with number of items inside? Or there is anothe component of MapboxGL which I can use to achieve this functionality?
Thanks
So from my experience with React Native Mapbox GL, you can't use point annotations for clustering. You'll have to use icons. One rule you have to keep in mind for this to work is that your markers have to be GEO JSON features collection. Checkout the link below if you don't know what that is.
https://enterprise.arcgis.com/en/geoevent/latest/ingest/GUID-F489B3D2-74DB-4EA2-8A4E-330628193843-web.png
Once you have your feature collection, you feed it into the Shapsource and clusters should start showing up.
<MapboxGL.ShapeSource
ref={shapeSource}
shape={{ type: 'FeatureCollection', features: [...''] }}
id="symbolLocationSource"
hitbox={{ width: 18, height: 18 }}
onPress={async point => {
if (point.features[0].properties.cluster) {
const collection = await shapeSource.current.getClusterLeaves(
point.features[0],
point.features[0].properties.point_count,
0,
)
// Do what you want if the user clicks the cluster
console.log(collection)
} else {
// Do what you want if the user clicks individual marker
console.log(point.features[0])
}
}}
clusterRadius={50}
clusterMaxZoom={14}
cluster
>
In order to get individual pictures for markers to show up once you zoom in; You'll need to get the image from the individual marker. So if you have a feature collection, each feature should have an image, you could either use an image stored in your project folder and replace the iconImage property in the symbol. Or if your feature has a link to an image, you could use the property in the feature like so:
iconImage: ['get', '___ whatever name you gave the link___'],
<MapboxGL.SymbolLayer
id="singlePoint"
filter={['!', ['has', 'point_count']]}
style={{
iconImage: ['get', '___ whatever name you gave the link___'],
iconSize: 0.3,
iconHaloColor: 'black',
iconHaloWidth: 10,
iconColor: 'white',
iconHaloColor: 'black',
iconHaloWidth: 400,
iconAllowOverlap: true,
}}
/>
in order to get that to show up you need mapbox images
<MapboxGL.Images
images={images}
onImageMissing={async url => {
setImages({ ...images, [url]: { uri: await getImage(url) } })
}}
/>
So that get request we did with the link, will call the mapbox images. Just make sure you have an images, and setImages in your state. This will then allow you to show the current image of your point annotation. Only problem is that it's hard to edit, so you can't just make them appear as circles unless they're cropped that way.
<MapboxGL.MapView
style={styles.map}
ref={mapRef}
styleURL="___ url___"
zoomEnabled
>
<MapboxGL.Camera
animationDuration={250}
ref={ref => (this.camera = ref)}
minZoomLevel={5}
zoomLevel={6}
maxZoomLevel={20}
animationMode="flyTo"
centerCoordinate={currrentLocation}
Level={stateZoomLevel}
/>
<MapboxGL.Images
images={images}
onImageMissing={async url => {
setImages({ ...images, [url]: { uri: await getImage(url) } })
}}
/>
{/* Cluster Individual Drop View */}
<MapboxGL.ShapeSource
ref={shapeSource}
shape={{ type: 'FeatureCollection', features: [...''] }}
id="symbolLocationSource"
hitbox={{ width: 18, height: 18 }}
onPress={async point => {
if (point.features[0].properties.cluster) {
const collection = await shapeSource.current.getClusterLeaves(
point.features[0],
point.features[0].properties.point_count,
0,
)
// Do what you want if the user clicks the cluster
console.log(collection)
} else {
// Do what you want if the user clicks individual marker
console.log(point.features[0])
}
}}
clusterRadius={50}
clusterMaxZoom={14}
cluster
>
<MapboxGL.SymbolLayer
id="pointCount"
style={layerStyles.clusterCount}
/>
<MapboxGL.UserLocation
visible
onUpdate={location => {
setCurrentLocation({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
})
}}
/>
<MapboxGL.CircleLayer
id="clusteredPoints"
belowLayerID="pointCount"
filter={['has', 'point_count']}
style={{
circlePitchAlignment: 'map',
circleColor: '#A59ADD',
circleRadius: [
'step',
['get', 'point_count'],
20,
100,
25,
250,
30,
750,
40,
],
circleOpacity: 0.84,
circleStrokeWidth: 0,
circleStrokeColor: 'blue',
}}
/>
<MapboxGL.SymbolLayer
id="singlePoint"
filter={['!', ['has', 'point_count']]}
style={{
iconImage: ['get', '__image name___'],
iconSize: 0.3,
iconHaloColor: 'black',
iconHaloWidth: 10,
iconColor: 'white',
iconHaloColor: 'black',
iconHaloWidth: 400,
iconAllowOverlap: true,
}}
/>
</MapboxGL.ShapeSource>
</MapboxGL.MapView>
const layerStyles = {
singlePoint: {
circleColor: 'green',
circleOpacity: 0.84,
circleStrokeWidth: 2,
circleStrokeColor: 'white',
circleRadius: 5,
circlePitchAlignment: 'map',
},
clusteredPoints: {},
clusterCount: {
textField: '{point_count}',
textSize: 12,
textPitchAlignment: 'map',
},
}
If this helped upvote!

Material-UI Style Override?

I'm updating my app from Material-UI v1 to v2. I'm trying to use a style override to set the color of a selected <BottomNavigationAction> element.
const styles = {
bottomNavStyle: {
position: 'fixed',
left: '0px',
bottom: '0px',
height: '50px',
width: '100%',
zIndex: '100'
},
'&$selected': {
color: "#00bcd4" //<==trying to add this color to selected items
},
};
class bottom_nav extends Component {
state = {
selectedIndex: -1,
};
handleChange = (event, value) => {
this.setState({value});
};
render() {
const { classes } = this.props;
return (
<Paper className={classes.bottomNavStyle}>
<BottomNavigation
value={this.props.selectedBottomNavIndex}
onChange={this.handleChange}
showLabels
>
<BottomNavigationAction
label="Appointments"
icon={theApptsIcon}
/>
<BottomNavigationAction
label="Contacts"
icon={theEmailIcon}
/>
<BottomNavigationAction
label="Video Call"
icon={theVideoCall}
/>
</BottomNavigation>
</Paper>
);
}
}
export default withStyles(styles)(bottom_nav);
But, this does not do anything to the color of selected items.
I've read the Material-UI docs on CSS in JS and JSS, but haven't quite gotten it yet. What is the correct syntax for this?
UPDATE
Based on a response to this thread I've tried this:
const styles = {
bottomNavStyle: {
position: 'fixed',
left: '0px',
bottom: '0px',
height: '50px',
width: '100%',
zIndex: '100'
},
actionItemStyle: {
'&$selected': {
color: "#00bcd4 !important"
},
},
}
[.....]
return (
<Paper className={classes.bottomNavStyle}>
<BottomNavigation
value={this.props.selectedBottomNavIndex}
onChange={this.handleChange}
showLabels
>
<BottomNavigationAction
label="Appointments"
icon={theApptsIcon}
className={classes.actionItemStyle}
/>
<BottomNavigationAction
label="Contacts"
icon={theEmailIcon}
className={classes.actionItemStyle}
/>
<BottomNavigationAction
label="Video Call"
icon={theVideoCall}
className={classes.actionItemStyle}
/>
</BottomNavigation>
</Paper>
);
}
...but have not yet gotten the new color to appear on the web page.
Your updated solution looks good, there are just a few small changes...
You need to include an empty .selected class in your styles rules.
const styles = {
// Root styles for `BottomNavigationAction` component
actionItemStyles: {
"&$selected": {
color: "red"
}
},
// This is required for the '&$selected' selector to work
selected: {}
};
You need to pass classes={{selected: classes.selected}} to BottomNavigationAction. This is required for the '&$selected' selector to work.
<BottomNavigation
value={value}
onChange={this.handleChange}
className={classes.root}
>
<BottomNavigationAction
classes={{
root: classes.actionItemStyles,
selected: classes.selected
}}
label="Recents"
value="recents"
icon={<RestoreIcon />}
/>
</BottomNavigation>
Live Example:
There are couple of things I would like to suggest.
1) Write the name of the component with first letter capitalized since it is not treated the same way if it is named with small first letter and with capitalized.
2) If there is no other way for your cs rule to be applied, if it is overridden always because of some css specificity, use !iportant at the end of the rule.
3) Try this type of nesting of css in jss:
const styles = {
bottomNavStyle: {
position: 'fixed',
left: '0px',
bottom: '0px',
height: '50px',
width: '100%',
zIndex: '100',
'&:selected': {
color: "#00bcd4"
},
},
};

How to make anchor move with respect to a block dragged in jsplumb?

I am trying to build a flowchart using drag and drop options. The user should be able to drag an element from one div and drop it to another.
Now I'm able to drag and drop. I have given an option such that on dropping the block, anchors should appear on them. And I'm able to link these blocks with connectors using js plumb.
I have given the draggable option for dropped blocks. The problem is whenever I drag connected blocks, the anchors' position does not change.
How to make a change such that whenever I drag any block, its anchor and connecting lines should also drag?
Here's my code:
jsPlumb.ready(function() {
var EndpointOptions = {
setDragAllowedWhenFull: true,
endpoint: "Dot",
maxConnections: 10,
paintStyle: {
width: 21,
height: 21,
fillStyle: '#666',
},
isSource: true,
connectorStyle: {
strokeStyle: "#666"
},
isTarget: true,
dropOptions: {
drop: function(e, ui) {
alert('drop!');
}
}
};
var count = 0;
var x = "";
//Make element draggable
$(".drag").draggable({
helper: 'clone',
cursor: 'move',
tolerance: 'fit',
revert: true
});
$(".droppable").droppable({
accept: '.drag',
activeClass: "drop-area",
<!-- stop: function( event, ui ) {}, -->
drop: function(e, ui) {
if ($(ui.draggable)[0].id !== "") {
x = ui.helper.clone();
console.log("x" + JSON.stringify(x));
ui.helper.remove();
x.draggable({
helper: 'original',
cursor: 'move',
containment: '.droppable',
tolerance: 'fit',
drop: function(event, ui) {
$(ui.draggable).remove();
}
});
x.appendTo('.droppable');
x.addClass('clg');
$(".clg").each(function() {
//alert("hello");
jsPlumb.addEndpoint($(this), EndpointOptions);
});
}
<!-- $(".clg").dblclick(function() { -->
<!-- //alert("hello"); -->
<!-- jsPlumb.addEndpoint($(this), EndpointOptions); -->
<!-- }); -->
jsPlumb.bind('connection', function(e) {
jsPlumb.select(e).addOverlay(["Arrow", {
foldback: 0.2,
location: 0.65,
width: 25
}]);
});
console.log("out x" + JSON.stringify(x));
}
});
});
You can use this jsPlumb.repaintEverything();
Here I'm generating an id for each block and adding endpoints based upon respective id.
Here is my changes:
if(null == ui.draggable.attr('id')){
if( ui.draggable.attr('class').indexOf('rule') != -1){
clone.attr('id', 'rule_' + i);
jsPlumb.addEndpoint(clone,{anchors: ["Left"]}, EndpointOptions);
} else {
clone.attr('id', 'event_' + i);
jsPlumb.addEndpoint(clone, {anchors: ["Left"]}, EndpointOptions);
}
i++;

tinymce.ui simple text component

I'm using tinymce a trying to extend a plugin to show a dialog with specific layout:
editor.windowManager.open({
title: 'Title of my dialog',
body: [
{type: 'label', text: 'my label'},
{ name:'my_input', type: 'textbox'},
// { type: 'text', html:'some content with <b>bold</b> if posilbe!'},
// { type: 'html', value:'<div>with custom formating</div>'}
]
}
I checked the the documentation for tinymce.ui several times but can find a way to add html or text component in the constructor of the dialog (like the comment rows in the example).
I know there is a option using a ready html template for the dialog.. but there are also a lot of events and triggers so using the constructor and .ui components is more suitable for my case.
I used to use JQuery UI dialog for this but ran into some issues after TinyMCE 4.0.
I have a TinyMCE plugin that lets people fetch the plain text version of their post in the WordPress editor. Then I show them that text using this:
var plain_block = {
type: 'container',
html: '<textarea style="margin: 10px; width: 550px !important; height: 450px !important; background-color: #eee;" readonly="readonly">Whatever plain text I need to show goes here</textarea>'
};
ed.windowManager.open({
title: "Plain Text of This Post",
spacing: 10,
padding: 10,
items: [
plain_block
],
buttons: [
{
text: "Close",
onclick: function() { ed.windowManager.close();}
}
]
});
End result is a pretty plain-jane dialog box with some HTML and a Close button