So I'm trying to create a fairly simple WYSIWYG BBCode editor for my project as an opportunity to wrap my mind around DraftJS. I've been following some tutorials and also using react-rte as an example (since it has 99% of the functionality I need and looks relatively simple to understand).
The problem I've stumped on is that react-rte inserts image entities inline (it adds a space in the current selection and then ties an entry to that space) like this:
const addEntity = (editorState, type, mutability = 'MUTABLE', data = {}) => {
let currentContent = editorState.getCurrentContent();
let selection = editorState.getSelection();
currentContent = currentContent.createEntity(type, mutability, data);
let entityKey = currentContent.getLastCreatedEntityKey();
return Modifier.insertText(currentContent, selection, ' ', null, entityKey);
}
I want each image (and video alike) to be in its own separate block and make it so nothing else can be written to that block.
I have found an example of the behavior I'm after in megadraft, but I have been unable to work my way through its code to find the correct implementation.
Found the solution after many hours of trial and error (and lots of manuals).
const addAtomic = (editorState, type, mutability = 'MUTABLE', data = {}) => {
let currentContent = editorState.getCurrentContent();
let selection = editorState.getSelection();
currentContent = currentContent.createEntity(type, mutability, data);
let entityKey = currentContent.getLastCreatedEntityKey();
const newState = EditorState.set(editorState, { currentContent: currentContent })
return AtomicBlockUtils.insertAtomicBlock(newState, entityKey, ' ')
}
Related
I would like to have some advice on how to best preload my image array in javascript.
I've been reading some other solutions but as someone new I'm having some problem on how to implement them in my existing code.
Thank you.
const layerImage = {
1:'https://plateau.ar/story/scroll/images/Video401.png',
2:'https://plateau.ar/story/scroll/images/Video402.png',
3:'https://plateau.ar/story/scroll/images/Video403.png',
4:'https://plateau.ar/story/scroll/images/Video404.png',
5:'https://plateau.ar/story/scroll/images/Video405.png',
68:'https://plateau.ar/story/scroll/images/Video468.png',
69:'https://plateau.ar/story/scroll/images/Video469.png',
70:'https://plateau.ar/story/scroll/images/Video470.png',
71:'https://plateau.ar/story/scroll/images/Video471.png',
72:'https://plateau.ar/story/scroll/images/Video472.png',
73:'https://plateau.ar/story/scroll/images/Video473.png',
74:'https://plateau.ar/story/scroll/images/Video474.png',
}
function checkScroll() {
const y = window.scrollY;
const scrollPixels = Math.min(Math.floor(y/100) + 1, 74);
const imageToUse = layerImage[scrollPixels];
// Change the background image
$('.imageBox').css('background-image', `url('${imageToUse}')`);
}
$(document).ready(()=>{
$(window).scroll(()=>{
checkScroll();
})
})
Elementor Pro has a new widget, video playlist. It appends a parameter to the URL, like so: http://aaronpenton.net/ampcreative/vip/about-vip/?playlist=f68425e&video=b8a9967
This is obviously terrible for SEO and UX. Is there a way to remove the ?playlist=f68425e&video=b8a9967 ?
My brother help me with the next script.
Put a "HTML Elementor Widget" with the following:
<script>
function getURLParameter(name) {
return decodeURI((RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]);
}
function hideURLParams() {
//Parameters to hide (ie ?playlist=value, ?video=value, etc)
var hide = ['playlist','video'];
for(var h in hide) {
if(getURLParameter(h)) {
history.replaceState(null, document.getElementsByTagName("title")[0].innerHTML, window.location.pathname);
}
}
}
window.onload = hideURLParams;
</script>
This should do the trick.
const url = 'http://aaronpenton.net/ampcreative/vip/about-vip/?playlist=f68425e&video=b8a9967'
url.replace(url.split('/')[6], '')
split, well.. splits the string into an array by the /
character.
At index 6 the array contains the ?playlist=f68425e&video=b8a9967 substring which can than be removed (i.e. replaced by the empty string) using replace.
A more general approach to removing the last part of the url might
be to use the array length instead of specifying the index:
const url = 'http://aaronpenton.net/ampcreative/vip/about-vip/?playlist=f68425e&video=b8a9967'
const urlArr = url.split('/')
url.replace(urlArr[urlArr.length - 1], '')
Update:
Another way to do this is using the URL API
const url = new URL('http://aaronpenton.net/ampcreative/vip/about-vip/?playlist=f68425e&video=b8a9967');
const result = url.origin + url.pathname
or in a function:
const removeParameter = u => {
const url = new URL(u);
return url.origin + url.pathname
}
You might have to check the specification for further details (browser support etc)
Ok, newbie alert, but I have a react component like below. I want to load user object and then show user.name and possible other nested properties, like user.address.street.
const ProfileDisplay =()=>
{
const [user, setUser] = useState(null);
useEffect(()=>{
async function populate(){
const user = await fetch('server/getUserProfile/'...);
setUser(user)
}
populate();
},[whatevs])
return (<FancyLookingForm>...<span>Your name is {user.name}</span>...</FancyLookingForm>);
}
...
During the time the user-object is loaded I want the form to be displayed, but empty. The alternatives I see is either create a dummy object that looks like the one that will come from the server and use that as init object to useState(). The other alternative is to use an if null-check on every place I use the user-object.
I will do quite alot of forms, so I want to check if there is a better way to do this?
I have written a utility function that will help you.
/**
* object is the forms data container in your example user
* property is field name e.g. name, age
*/
function getPropertryValue(object, property) {
if(!object) return "";
const keys = Object.keys(object);
const keysLength = keys.length;
for(let i= 0; i < keysLength; i++) {
const key = keys[i];
if(key == property) {
return object[key];
}
if(typeof object[key] === 'object'){
return utilFindHas(object[key], property);
}
}
return "";
};
So instead of using {user.name} use getPropertyValue(user, "name")
It will return empty string if it doesn't exist yet.
I have a custom parchment that looks like:
import { Quill } from 'react-quill';
const Parchment = Quill.import('parchment');
let config = { scope: Parchment.Scope.INLINE };
let AcceptedPredictionClass = new Parchment.Attributor.Class('accepted', 'ql', config);
Quill.register(AcceptedPredictionClass)
and to use it:
const delta = new Delta()
.retain(currentSelection.index)
.delete(predictionLength)
.insert(previousPredictionText, { accepted: 'accepted' })
quill.updateContents(delta)
but the problem is that if I start typing, it keeps the ql-accepted style. I need it to revert back to normal.
How about simply adding one more .insert(' ', {}) after last insert? This should add one normal span after the inserted class.
This is how it will be:
const delta = new Delta()
.retain(currentSelection.index)
.delete(predictionLength)
.insert(previousPredictionText, { accepted: 'accepted' })
.insert(' ', {})
quill.updateContents(delta)
FYI: I haven't tested it yet but the general idea is that the cursor will be inside new span without the added class.
I have form which has many tabs. Every tab has many controls textboxes, comboboxes, datagrids and e .t.c. I bind form to one data source in such way
this.DataContext=MyClassInstance
But with this way my form opening very slow. about one minute.
When I comment above code, form opens very quickly. All My controls I bound to the class properties in XAML. Please tell me the way to bind every tab when it's activated, or bind controls in background thread or any other idea which can help me to speed up my form.
Thanks in advance.
I think the problem lies in your class instance you are binding to.
When the xaml is bound to the class, all the getters of the bound properties are fired. If each getter accesses the database to get some data, this can take a while.
I think you should really review your design here, and think about asynchronously fetching your data.
I agree with Gerrie.
I'm suggesting the following:
When you start your application, you automatically open one tab i guess. Load only that tab, don't care about the others. This should start your project much faster.
The thing you do for the other tabs is load them when clicked for the first time. When the user for example is interested in tab 5, the only ones loading will be the initial tab at startup, and tab 5 when clicked by the user. all other tabs will not be loaded, which will decrease startup time.
Hope the idea is clear to you and will help your application.
I found why my form open so slow. I use about 20 XMLDataProvider object in form. and this providers were iteract with xml file. When I comment code below everything working fast. Thank everyone for help
//relatives_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_RelativeList" };
//education_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_EducationList" };
//requalification_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_RequalificationList" };
//jobHistory_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_JobHistoryList" };
//rank_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_RankList" };
//tradeUnion_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_TradeUnionList" };
//election_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_ElectionList" };
//judgeHistory_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_JudgeHistoryList" };
//tempWork_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_TempWorkList" };
//inquire_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_InquireList" };
//bulleten_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_BulletenList" };
//reprimand_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_ReprimandList" };
//certificate_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_CertificateList" };
//course_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_CourceList" };
//incentive_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_IncentiveList" };
//btrip_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_BtripList" };
//vacation_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_VacationList" };
//pass_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_PassList" };
//language_xdp = new XmlDataProvider() { Source = uri, XPath = "Config/ColumnsVisibility/Person_LanguageList" };