React Native Render HTML bullet list is not centered with text - django

I'm using react-native-render-html library to display data from Django Rest Framework stored in RichTextUploadingField from django-ckeditor. As basic styling is working properly I have a problem with the <ul> tag - text is not centered with circle/square/number as shown in the image. I tried using renderersProps like:
<RenderHTML renderersProps={{ ul: { markerBoxStyle:{ paddingRight: 3, top: 5 }, markerTextStyle: { color: "red" } } }} />
But it works for specific text size. If I change the text size in style in Django admin, the element and text aren't centered again. Is there any option on how to fix it or what causes the problem?

You could add a custom CSS class to unordered list items only. Then apply your styles to that CSS class.
// this function goes through every list item in <ul> only and adds your custom class ('ul-li' in my case)
const onElement = (el) => {
const { children, name } = el;
if (name === 'ul' && children && children.length) {
children.forEach(listItem => listItem.attribs = {class: 'ul-li'})
}
}
// define your styles for your custom class
const classesStyles = {
"ul-li": {
lineHeight: '21px', // for me adjusting lineHeight did the job
}
}
// in your JSX
<RenderHtml
source={source}
contentWidth={width}
tagsStyles={tagsStyles}
... // other props you may have
domVisitors={{onElement}} // first alter your html
classesStyles={classesStyles} // then apply styles
/>
Sources:
classesstyles
onElement

Related

unable to display list of items with Nativescript-Vue GridLayout

Nativescript-vue newbie here...
I am using nativescript-vue to display a list of items. When the .vue page loads, the grid should just be an empty table. When a button is clicked, the grid is populated with sub-elements of a list I retrieved from a service. I am running into two issues:
There is only one row, where there should be a row per item in the list.
The date will be a date element such as 06-01-2019 11:30:01 but I would prefer to display it as 06-01-2019
In my *.vue:
<RadListView for="item in itemList" layout="grid" #itemTap="onItemTap">
<v-template>
<GridLayout rows="20, 20" class="list-item list-item-grid">
<GridLayout row="1" rows="25, 35" columns="auto, auto, auto, auto">
<Label col="0" :text="item.date" class="list-item-left" textWrap="true"verticalAlignment="middle" horizontalAlignment="left"></Label>
....
</GridLayout>
</GridLayout>
</v-template>
</RadListView>
....
<script>
expot default {
data() {
return {
itemList: []
};
},
methods: {
getItems() {
// really simplified but it will return something like:
// [{"date" :"09-01-2019 11:32:01", "name":" first last"}]
service.getItemArray()
}
},
beforeMount: function() {
this.itemList= this.getItems();
}
</script>
I have put a shortened Playground version here:
You could try a v-if to render the RadListView after the button is clicked, or manual load on demand.

Persistent AdminLTE sidebar state

I've inherited some work on an AdminLTE sidebar on a Django website. The page in question uses an "extends" block to load AdminLTE's index.html page right off the bat. Links on our treeview sidebar cause the entire page to reload, including the sidebar, so the state of any expanded treeview menus is lost whenever someone clicks a link.
I'm guessing there's some well-known way of making the sidebar keep its treeview menus open, but I've yet to find it. There are some working examples on the AdminLTE site, but I can't figure out how they work.
Can someone point me to the right piece of documentation to help me make my sidebar persistent across page loads?
Treeview css class works in an unordered list so any child links only show up when the parent list is clicked. An example of this is if you have "home" and then "About" "About-Locations". When you click About it is a tree-view class and on the sidebar it will show locations underneath it. When you click on home the locations sidebar link will not be displayed as this is how the css is written for the list.
The code can be found in the "AdminLTE.css" file.
I'm not working on django, I work on a MVC Razor app.
For the same problem, I use this solution :
I store the link clicked on the menu (ajax send to the server and session storage, but you can use cookie or what you want).
The link clicked is inserted in the java script below :
$(" ul.treeview-menu > li > a").on("click", function ()
{
if (this.href == "#")
return;
$.ajax({
type: "POST",
url: '/Outils/SetActiveMenu',
data: { url: this.href },
dataType: "json"
});
})
$(document).ready(function () {
var v = "#Html.Raw(Session["ActiveMenu"] == null?"": Session["ActiveMenu"].ToString())";
if(v == "") return;
var a = $('a[href="' + v + '"]');
openParentMenu(a);
a.css("background-color", "#E3E6E5");
});
function openParentMenu(item)
{
var parent = item.parent().closest("li.treeview");
if (parent.length != 0) {
openParentMenu(parent);
parent[0].children.item("a").click();
}
}
Here is the code for reference.
/* Tree()
* ======
* Converts the sidebar into a multilevel
* tree view menu.
*
* #type Function
* #Usage: $.AdminLTE.tree('.sidebar')
*/
$.AdminLTE.tree = function (menu) {
var _this = this;
var animationSpeed = $.AdminLTE.options.animationSpeed;
$(menu).on('click', 'li a', function (e) {
//Get the clicked link and the next element
var $this = $(this);
var checkElement = $this.next();
//Check if the next element is a menu and is visible
if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) {
//Close the menu
checkElement.slideUp(animationSpeed, function () {
checkElement.removeClass('menu-open');
//Fix the layout in case the sidebar stretches over the height of the window
//_this.layout.fix();
});
checkElement.parent("li").removeClass("active");
}
//If the menu is not visible
else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
//Get the parent menu
var parent = $this.parents('ul').first();
//Close all open menus within the parent
var ul = parent.find('ul:visible').slideUp(animationSpeed);
//Remove the menu-open class from the parent
ul.removeClass('menu-open');
//Get the parent li
var parent_li = $this.parent("li");
//Open the target menu and add the menu-open class
checkElement.slideDown(animationSpeed, function () {
//Add the class active to the parent li
checkElement.addClass('menu-open');
parent.find('li.active').removeClass('active');
parent_li.addClass('active');
//Fix the layout in case the sidebar stretches over the height of the window
_this.layout.fix();
});
}
//if this isn't a link, prevent the page from being redirected
if (checkElement.is('.treeview-menu')) {
e.preventDefault();
}
});
};
I have used the inbuilt functionality mentioned by #MDT and have created a function:
function toggleCollapsibleList(){
//Get the clicked link and the next element
var $this = $('#parent-list-item-id');
var checkElement = $('#an-id-for-collapsible-li-with-treeview-class');
//Check if the next element is a menu and is visible
if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) {
//Close the menu
checkElement.slideUp(500, function () {
checkElement.removeClass('menu-open');
//Fix the layout in case the sidebar stretches over the height of the window
//_this.layout.fix();
});
checkElement.parent("li").removeClass("active");
}
//If the menu is not visible
else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
//Get the parent menu
var parent = $this.parents('ul').first();
//Close all open menus within the parent
var ul = parent.find('ul:visible').slideUp(500);
//Remove the menu-open class from the parent
ul.removeClass('menu-open');
//Get the parent li
var parent_li = $this.parent("li");
//Open the target menu and add the menu-open class
checkElement.slideDown(500, function () {
//Add the class active to the parent li
checkElement.addClass('menu-open');
parent.find('li.active').removeClass('active');
parent_li.addClass('active');
//Fix the layout in case the sidebar stretches over the height of the window
});
}}
This worked for me :)

Ionic2 SearchBar confusion, onInput called after firing onClear, onCancel

I have been following the tutorial at ionic2 SearchBar to work on the filter functionality.
The question is, I am not able to figure out how to get onCancel and onClear to work.
Steps:
1) Enter some keywords in SearchBar. It calls the onInput event and I get the value from searchItem.target.value unlike in tutorial which just uses searchItem.value
2) Now i try to click on clear "x" or Cancel button and it calls the onClear/onCancel event which is immediately followed by onInput event. And during this call, it does not find the searchItem.target.value and results in undefined due to which it breaks the functionality.
Can anyone provide more in depth details on how this works?
i fixed it in tutorial sample for ionic2 by stopping onClear event propagation
import {Component} from '#angular/core';
#Component({
selector: 'my-search',
template: '<ion-toolbar primary><ion-searchbar (input)="onInput($event)" (ionClear)="onClear($event)"></ion-searchbar></ion-toolbar><ion-content><ion-list><ion-item *ngFor="let item of items">{{ item }}</ion-item></ion-list></ion-content>'
})
export class HomePage {
items = [];
constructor() {
this.initializeItems();
}
initializeItems() {
this.items = [
'Angular 1.x',
'Angular 2',
'ReactJS',
'EmberJS',
'Meteor',
'Typescript',
'Dart',
'CoffeeScript'
];
}
onClear(ev)
{
this.initializeItems();
ev.stopPropagation();
}
onInput(ev) {
// Reset items back to all of the items
this.initializeItems();
// set val to the value of the searchbar
var val = ev.target.value;
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.items = this.items.filter((item) => {
return (item.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
}
}

How to use images inside a select component in Ionic 2

I am trying to put an image inside a Select component in Ionic 2 :
I have put the image source files inside the www/img folder in my Ionic 2 project.
However, using a simple img-tag does not display any image using this code:
<ion-list>
<ion-item>
<ion-label>Gaming</ion-label>
<ion-select [(ngModel)]="gaming">
<ion-option value="nes">
NES
<img src="img/myImage.png">
</ion-option>
</ion-select>
</ion-item>
</ion-list>
Does anyone have any idea?
The ion-select component doesn't allow direct customization to itself and anything that you add to ion-select and ion-option which is not as per the ionic documentation, will be ignore in the generated output.
You cannot add class or style to the component.
One way to do this is to put the ion-select in a parent element like div or ion-row etc. with class and apply the CSS rules using .parentclass childElement selector.
To show the images in option check the function below:
prepareImageSelector() {
setTimeout(() => {
let buttonElements = document.querySelectorAll('div.alert-radio-group button');
if (!buttonElements.length) {
this.prepareImageSelector();
} else {
for (let index = 0; index < buttonElements.length; index++) {
let buttonElement = buttonElements[index];
let optionLabelElement = buttonElement.querySelector('.alert-radio-label');
let image = optionLabelElement.innerHTML.trim();
buttonElement.classList.add('imageselect', 'image_' + image);
if (image == this.image) {
buttonElement.classList.add('imageselected');
}
}
}
}, 100);
}
I have implemented color and image selector using ion-select. Please refer https://github.com/ketanyekale/ionic-color-and-image-selector
You can also check the answer at Ionic select input of colours
I achieved it like this.
<ion-select (click)="loadFlags()" formControlName="pays" value="select-country">
<ion-select-option value="select-country" >select your country </ion-select-option>
<ion-select-option *ngFor="let country of countries" value="{{country.name}}">{{country.name}}</ion-select-option>
</ion-select>
And this is my .ts file
loadFlags() {
setTimeout(function(){
let radios=document.getElementsByClassName('alert-radio-label');
for (let index = 1; index < radios.length; index++) {
let element = radios[index];
element.innerHTML=element.innerHTML.concat('<img class="country-image"
style="width: 30px;height:16px;" src="'+countries[index-1].flag+'" />');
}
}, 1000);
}
Timeout is to let the system create alert componens. My Json is an array with elements like {name:"Cameroon",flag:"https://restcountries.eu/data/cmr.svg"}
On your basis, I have worked out a more concise solution!
The method 'prepareImageSelector' is used as the Click event of the control。
Thank you!
image: string = 'English';
prepareImageSelector() {
setTimeout(() => {
let buttonElements = document.querySelectorAll('div.alert-radio-group button');
if (!buttonElements.length) {
this.prepareImageSelector();
} else {
for (let index = 0; index < buttonElements.length; index++) {
let buttonElement = buttonElements[index];
let optionLabelElement = buttonElement.querySelector('.alert-radio-label');
let image = optionLabelElement.innerHTML.trim();
if (image == this.image) {
optionLabelElement.innerHTML += '<img src="assets/img/English.png" style="width:20px;height:20px;float:right;"/>';
}
}
}
}, 100);
}
There are already some great answers, and I honestly think Select with some images should have been part of the core Ionic library, but here we are.
I made the following adaptations for my answer:
Implemented with Ionic Vue (Ionic 6, so it's still an issue)
Flags for the countries are nested in a country JSON obtained from an API call.
Implemented for Action Sheet instead of Alert as the other answers
Flags appear on the left instead of the right
First, the HTML:
<IonSelect #click="loadFlags()" placeholder="Country" :interface-options="customAlertOptions" interface="action-sheet" cancelText="Cancel" style="width: 100%" v-model="selected_country">
<template v-for="(country, index) in countries" :key="index">
<div class="ion-text-center">
<IonSelectOption :value="country.code">
<IonLabel>{{ country.name }}</IonLabel>
</IonSelectOption>
</div>
</template>
</IonSelect>
And then the Javascript:
loadFlags() {
let that = this;
setTimeout(function() {
let radios = document.getElementsByClassName('action-sheet-button-inner');
for (let i = 0; i < that.countries.length; i++) {
let element = radios[i];
element.innerHTML = `<div style="display: flex; position: relative; width: 100%"><img style="width: 40px;height:40px;" src="${that.countries[i].flag}"/>`
+ `<span style="font-size:16px; position: absolute; top:10px; left: 70px;">${element.innerHTML}</span></div>`
}
}, 10)
}
Even though it's a little crude, the innerHTML in the loadFlags function, with some HTML and CSS, can be used to achieve almost anything.

Why does this CSS transition event not fire when two classes are added?

CSS
.horizontalTranslate {
-webkit-transition: 3s;
}
.secondClass {
background: red;
}
HTML
<div class='box'></div>
JS
var box = document.querySelector('.box');
box.addEventListener('webkitTransitionEnd', function(evt) {
alert('Finished transition!');
}, false);
function transitionBox() {
// this works
box.className = 'horizontalTranslate';
// this does not work
// box.className = 'secondClass horizontalTranslate';
}
setTimeout(transitionBox, 100);
Why does the transition event not fire when two classes are added rather than one? I've also tried chaining my CSS selector, a la .secondClass.horizontalTranslate { ... }.
The reason is that box.className = 'horizontalTranslate'; is actually removing styling, causing the CSS transition to actually happen. But when I set box.className = 'secondClass horizontalTranslate';, the styling of the DOM node is not changing and no event is fired.
Another way to write transitionBox is this:
function transitionBox() {
box.classList.add('horizontalTranslate');
box.classList.add('blue');
}
If blue changes the styling of box, this works too.