I am trying to add a simple image cropping functionality in my Electron app. I want something like this.
I have tried react-image-crop and react-cropper, but both did not work.
With the latter library, I simply did:
import React, { useState } from 'react';
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
const ImageCrop = () => {
const [upImg, setUpImg] = useState();
const onSelectFile = e => {
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.addEventListener('load', () => setUpImg(reader.result));
reader.readAsDataURL(e.target.files[0]);
}
};
return (
<>
<div>
<input type="file" accept="image/*" onChange={onSelectFile} />
</div>
<Cropper src={upImg} />
</>
);
};
export default ImageCrop;
The result looks like the following and I was not able to select the area.
With the former library, it only displays an orange frame around the image and does not allow cropping it.
I imagine some JS functionality is somehow not working in Electron, but I do not know what exactly is going on here. Any help would be appreciated to explain it and make it work.
#kyleawayan is right.
You just have to manually include the CSS file into your HTML.
I was using an index.html file with Electron, so I had to add:
<link rel="stylesheet" href="./node_modules/cropperjs/dist/cropper.min.css" />
to the <head> of my HTML.
Related
const JoditEditor = dynamic(() => import('jodit-react'), { ssr: false }); const editor = useRef(null); <JoditEditor ref={editor} value={description} onBlur= {newContent => {}} onChange= {newContent => {}} />
I am using this code as basic functionality,
I am trying to get paste functionality/configuration in Jodit editor using in Nextjs.
react-leaflet-easyprint has examples and docs for react-leaflet v1 and v2.
However, out of the box it seems to be incompatible with v3.
How can i make it work with v3?
This is how i did it.
I used the following packages instead and maybe they will work the same way
// package.json
"leaflet-easyprint": "^2.1.9",
"react-leaflet": "^4.0.0",
// MapPrint.js
import L from 'leaflet';
import 'leaflet-easyprint';
import { useEffect } from 'react';
import { useMap } from 'react-leaflet';
function MapPrint(props) {
const map = useMap();
useEffect(() => {
const control = L.easyPrint({
...props
});
map.addControl(control)
return () => {
map.removeControl(control);
}
}, [map]);
return null;
}
export default MapPrint
after that you could use it like this (inside MapContainer from the new react-leaflet):
<MapContainer zoom={3} >
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Print" />
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Export as PNG" exportOnly />
</MapContainer>
So what i am saying is to use the js implementation of easyprint instead of using react wrapped version and wrap it by yourself.
I try to load the local .html file into WebView in React Native:
// load local .html file
const PolicyHTML = require('./Policy.html');
// PolicyHTML is just a number of `1`
console.log('PolicyHTML:', PolicyHTML);
// will cause an error: JSON value '1' of type NSNumber cannot be converted to NSString
<WebView html={PolicyHTML} />
The .html file should be read as a string, not as a resource representative.
How can I load the .html file into WebView in React Native?
By the way, what is the type of those resource representatives from require()? Is it number?
try it:
const PolicyHTML = require('./Policy.html');
<WebView
source={PolicyHTML}
style={{flex: 1}}
/>
I come across this post searching for loading static html.
If your html code is retrieved using, for example, an API, you can render WebView in this way:
<WebView
originWhitelist={['*']}
source={{ html: html, baseUrl: '' }}
/>
Notice that originWhitelistis required as explained in the documentation:
Note that static html will require setting of originWhitelist for
example to ["*"].
<View style={{flex: 1}}>
<WebView
style={{flex: 1}}
source={require("./resources/index.html")}
/>
</View>
To make WebView, the parent has to has a dimension or flex:1. We could set the WebView to flex: 1 too so that it fills up the parent.
If you need to serve local assets as well, then:
put all assets together with index.html into android/app/src/main/assets/www (You can copy them there with gradle task)
Then:
var uri = Platform.OS == "android" ?
"file:///android_asset/www/index.html" :
"./web/www/index.html"
return <WebView source={{ uri }} />
** For iOS didn't tested, please add instruction, how assets should be stored
With Expo tools and generally using Expo:
import { WebView } from "react-native-webview";
import { readAsStringAsync } from "expo-file-system";
import { useAssets } from "expo-asset";
export const MusicSheet = () => {
const [index, indexLoadingError] = useAssets(
require("../assets/musicsheetview/index.html")
);
const [html, setHtml] = useState("");
if (index) {
readAsStringAsync(index[0].localUri).then((data) => {
setHtml(data);
});
}
return (
<View style={styles.container}>
<WebView
onLoad={() => {}}
source={{ html }}
onMessage={(event) => {}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
height: 100,
display: "flex",
},
});
Try this :
Add your .html file in your project.
Write such lines of code in the file where you want to use WebView Component
const OurStoryHTML = require ('./OurStory.html')
<WebView
source={OurStoryHTML}
style={{flex: 1, marginTop : 44}}
/>
It may help you.
If you're working with assets, project directories is different on the device's directory once the project is build and you can't simply reference them via string url.
Expo
If using expo, you have to require every asset then use useAssets on the require to cache them to the local storage of the device.
useAssets will return an object that contains a localUri
(this is the uri of the image that has been cached)
you can then use the localUri and put it as the src of the image
import { useAssets } from 'expo-asset';
/* . . . */
const IndexHTML = require('./assets/index.html');
const myImage = require('./assets/splash.png');
// url link after image is cached to the device
const [imgSrc, setImgSrc] = useState('');
const [image, imerr] = useAssets(myImage);
const [html, error] = useAssets(IndexHTML);
const webViewProps = {
javaScriptEnabled: true,
androidLayerType: 'hardware',
originWhitelist: ['*'],
allowFileAccess: true,
domStorageEnabled: true,
mixedContentMode: 'always',
allowUniversalAccessFromFileURLs: true,
onLoad: () => {
console.log(image[0].localUri);
setImgSrc(image[0].localUri);
},
source: {
html: '<img src="' + imgSrc + '"/>',
},
};
return <WebView {...webViewProps} />
const webViewProps = {
...
source: IndexHTML,
};
Note: for the expo apporach, files referenced in IndexHTML will not be found
The trick is to turn your html into a string literal to utilize template strings.
Then you have to manually require each of those assets to concatenate localUrl
require() has limited types supported and you need to add a metro.config.js in your root folder.
it will give errors if you require() a .js file since it reads it as a module rather, the workaround approach would be to bundle your assets
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.assetExts.push(
// Adds support for asset file types
'css', 'ppt', 'obj'
);
module.exports = config;
Moreover, expo can hot reload changes done with the cached assets.
React Native
If you have the android folder in your directory, navigate to
android > app > build.gradle
then add
android {
/* link assets to the local storage of device */
sourceSets {
main { assets.srcDirs = ['src/main/assets', '../../source/assets/'] }
// [do not touch, 'relative to your asset'] }
}
. . .
finally, the relative folder you linked in gradle can be accessed through
file:///android_asset/
for ex. file:///android_asset/index.html -> /asset/index.html
return <WebView source={{uri: `file:///android_asset/index.html`}} />
For IOS, here's how
On the other hand, you have to rebuild vanilla react to see the changes in the assets.. which takes about 20 minutes or so
Alternative
A quick solution would be to integrate a static server, but this is a recent fork of the react-native-static-server that only works in vanilla react native.
I am using vue-i18n and I need to translate a sentence with an anchor tag in the middle. Obviously I want to keep the html specific markup out of my translations, but how best to handle this?
Consider the following example:
This is a test sentence which cannot
be split
or it will not make sense
The only solution I can come up with is:
{
"en": {
"example": "This is a test sentence which cannot {linkOpen}be split{linkClose} or it will not make sense"
}
}
and then in the component template
<p v-html="$t('example', {
'linkOpen': `<a href="https://example/com" class="test-class test-another-class">`,
'linkClose: '</a>'
})
"></p>
Not exactly elegant however...
Edit: I've tested this and it doesn't actually work (can't put html in params) so now I'm really out of ideas!
You can come up with some simple markup for links and write a small transformation function, for example:
//In this example links are structured as follows [[url | text]]
var text = `This is a test sentence which
cannot [[https://example.com | be split]] or it will not make sense`
var linkExpr = /\[\[(.*?)\]\]/gi;
var linkValueExpr = /(\s+\|\s+)/;
var transformLinks = (string) => {
return text.replace(linkExpr, (expr, value) => {
var parts = value.split(linkValueExpr);
var link = `${parts[2]}`;
return link;
});
}
alert(transformLinks(text));
JSFiddle: https://jsfiddle.net/ru5smdy3/
With vue-i18n it will look like this (which of course you can simplify):
<p v-html="transformLinks($t('example'))"></p>
You can put the HTML into an element that is not part of the displayed DOM and then extract its textContent. This may not work for what you're actually trying to do, though. I can't tell.
new Vue({
el: '#app',
data: {
html: `This is a test sentence which cannot
be split
or it will not make sense`,
utilityEl: document.createElement('div')
},
methods: {
htmlToText: function (html) {
this.utilityEl.innerHTML = html;
return this.utilityEl.textContent;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<p v-html="html"></p>
<p>{{htmlToText(html)}}</p>
</div>
I have found myself in a similar situation, and I propose using Vue-i18n slots.
I have a JSON i18n file which had error messages that were html. These rendered fine but they will not be compiled as vue templates, and cannot have bindings. I want to call an onclick function when users click the link in a given error message.
In my example I have a cake-state json with some status messages:
// cake_state.json, where I want links in error messages to call a function when clicked
{
"state":{
"stage": {
"mixing": "Cake is being mixed. The current time is {time}",
"resting": "Cake is resting. The current time is {time}",
"oven": "Cake is cooking. The current time is {time}"
},
"error": {
"ovenIssue": "Oven of brand is malfunctioning. Click {email_support_link} to get help",
"chefIssue": "Chef is down. Click {email_support_link} to get help",
"leakIssue": "There is a leak"
},
}
}
Now if we have some Vue SFC, with the template as such:
<template>
<div>
<i18n :path="getMessage">
<!-- enter various i18n slots -->
<template #time>
<span>{{ getTime }}</span>
</template>
<template #email_support_link>
<!-- binding now works because it is not v-html -->
<a href="" #click.prevent="getRightSupportDepartment">here</span>
</template>
</i18n>
</div>
</template>
...
// js
computed: {
getTime(): string { //implementation ...},
getRightSupportDepartment(): string { //implementation ...},
//return strings that are keys to cake_state.json messages
getMessage(): string {
...
switch (this.cakeState) {
case Failure.Overheat:
return "state.error.ovenIssue";
case Failure.ChefIdle:
return "state.error.chefIssue";
case Failure.LeakSensor:
return "state.error.leakIssue";
So what we see here is:
the getMessage function provides us the key to the message in the i18n JSON. This is passed into i18n component
the <template #XXX> slots in the i18n component's scope are supplied with this key from the function, which gets the corresponding message, and then
if the relevant message has any of the keywords, it gets put in from the corresponding template.
To re-iterate, it helps to provide a means to have vue bindings to html elements which would otherwise be served from the i18n json as raw html.
For example now we might see "Oven of brand is malfunctioning. Click here to get help", and we can run an onclick function when user clicks 'here'.
Has anyone succeeded to do this?
I accomplished this by replacing Util.prompt with my own jquery.dialog method. The prompt function takes a parameter as a callback, making it easy to create a drop-in replacement.
if (isImage) {
// OLD: util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown);
// WMD_IMAGE_GALLERY_URL loaded from a global settings elsewhere
util.imageGallery(WMD_IMAGE_GALLERY_URL, makeLinkMarkdown);
}
else {
util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown);
}
If you're interested, I wrote a blog entry about it (with pictures!) which has some more sample code as well as some of the problems/solutions I encountered in implementing this.
The following hack requires use of jQuery, jQuery UI and Mike Alsup's jQuery Form Plugin for performing AJAX file uploads. The hack works with the linked versions (jQ 1.7.2 and jQUI 1.8.20). I can't guarantee compatibility with other versions.
In your <head>, you'll need to include the dependencies:
<script type='text/javascript' src='jquery.min.js'></script>
<link href='theme/jquery-ui.css' rel='stylesheet' type='text/css' />
<script type='text/javascript' src='jquery-ui.js'></script>
<script type='text/javascript' src='wmd/showdown.js'></script>
<script type='text/javascript' src='wmd/wmd.js'></script>
<link type='text/css' rel='stylesheet' href='wmd/wmd.css'/>
<script type='text/javascript' src='jquery.form.js'></script>
We actually need to make a single change to wmd.js.
Go on in there and search (ctrl+f) for var form = doc.createElement("form");
Immediately following this line, assign the form an id, dialogform will do: form.id = "dialogform";
Now on the front end, run:
$(document).ready(function(){
$("#wmd-image-button").live("click",function(){
setTimeout(function(){
$(".wmd-prompt-dialog").css({"opacity": "0", display: "none"});
}, 100);
var $div = $("<div>");
var $form = $("<form>").attr({action: "submit_image.php", method: "post"})
var $file = $("<input/>").attr({type: "file", name: "image"});
var $name = $("<input/>").attr({type: "text", name: "name", placeholder: "Name"});
var $submit = $("<input/>").attr("type", "submit");
$form.append($name, $file, $submit).ajaxForm(function(r) {
r = $.parseJSON(r);
if(r.success){
$("#dialogform input[type='text']").val(r.filename);
$("#dialogform input[value='OK']").trigger("click");
$div.dialog("close");
}
});
$div.append($form).dialog({title: "Upload Image"});
});
$("#wmd-link-button").live("click", function(){
setTimeout(function(){
$(".wmd-prompt-dialog").css("opacity", "1");
}, 100);
});
});
Remember, the post was written for jQuery 1.7.2, and live() has since been deprecated. Please switch to on() if you're using a more recent version of jQuery
And on the backend, in submit_image.php:
$f = $_FILES['image'];
$p = $_POST;
$allowedTypes = array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
$detectedType = exif_imagetype($f['tmp_name']);
if(in_array($detectedType, $allowedTypes)){
$pi = pathinfo($f['name']);
$ext = $pi['extension'];
$target = "img/" . strtolower(str_replace(" ", "-", $p['name'])) . "." . $ext;
if(move_uploaded_file($f['tmp_name'], $target)){
$returnArr = array(
"success" => true,
"filename" => site_url($target)
);
echo json_encode($returnArr);
}
else echo json_encode(array("success" => false));
}
else echo json_encode(array("success" => false, "msg" => "Invalid File Type."));
Hopefully that will get you started. This was written a couple of years ago, when my javascript skills were sub-par! Haha. I previously had this on a blog (which is now dead), with step-by-step instructions and explanations; lots of unnecessary fluff. Thanks #Kamiccolo for bringing this link to my attention. I had to consult the way-back-machine in order to revive it.
Add a button to the control panel of WMD.
Search for the following string to find the place where buttons are being added:
italicButton.XShift
In my version, the function is in class SpritedButtonRow and is called build.
Ignore the setup and textOp attributes. XShift is the position of the button image in the css sprite that comes with WMD, Instead of that, give the button a class and in the class specify the background image. Then Just add an onclick event to the button
that will do what you need it to do.
But, I don't think an upload button should be inside a text editor, does not make sense.