the table border-bottom disappear? - html

I try to implement a table with large size of data. then due to the performance issue, I just want to render the data in the body window.
But the new render element border disappear.
HTML:
<script src="//unpkg.com/vue#2.5.15/dist/vue.js"></script>
<script type="text/x-template" id="list-template">
<div class='table-body' ref="body" #scroll="handleScroll">
<div class="list-view">
<div
class="list-view-phantom"
:style="{
height: contentHeight
}">
</div>
<div class="list-view-colgroup">
<div class="list-view-item-col-g" v-for='count in 5'>
</div>
</div>
<div
ref="content"
class="list-view-content">
<ul
class="list-view-item"
:style="{
height: itemHeight + 'px'
}"
v-for="item in visibleData" :key='item.value'>
<li class="list-view-item-col" v-for='count in 5'>
{{item.value+count}}
</li>
</ul>
</div>
</div>
</div>
</script>
<div id="app">
<template>
<list-view :data="data"></list-view>
</template>
</div>
JS:
const ListView = {
name: 'ListView',
template: '#list-template',
props: {
data: {
type: Array,
required: true
},
itemHeight: {
type: Number,
default: 30
}
},
computed: {
contentHeight() {
return this.data.length * this.itemHeight + 'px';
}
},
mounted() {
this.updateVisibleData();
},
data() {
return {
visibleData: []
};
},
methods: {
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
const start = Math.floor(scrollTop / this.itemHeight);
const end = start + visibleCount;
this.visibleData = this.data.slice(start, end);
this.$refs.content.style.transform = `translate3d(0, ${ start * this.itemHeight }px, 0)`;
},
handleScroll() {
const scrollTop = this.$refs.body.scrollTop;
this.updateVisibleData(scrollTop);
}
}
};
new Vue({
components: {
ListView
},
data() {
const data = [];
for (let i = 0; i < 1000; i++) {
data.push({ value: i });
}
return {
data
};
}
}).$mount('#app')
code example:
https://jsfiddle.net/441701328/hq1ej6bx/6/
you can see only the data render in the first time can have border.
could anyone help?
thanks all!!!

table-row-group does not work with divs you can change the whole layout and use tables or instead you can do it like this.
.list-view-item {
padding: 5px;
color: #666;
display: table;
line-height: 30px;
box-sizing: border-box;
border-bottom: 1px solid red;
min-width: 100vw;
}
.list-view-item-col {
display: table-cell;
min-width: 50px;
}
jsfiddle for table-row-group
Hope it helps.

Use display :flex for list-view-item class, Try with following code.Hope it will work fine for you.
.list-view-item {
padding: 5px;
color: #666;
display: flex;
flex-basis: 100%;
flex-direction: row;
line-height: 30px;
box-sizing: border-box;
border-bottom: 1px solid red;
}

Try with this CSS. I hope it will works for you.
.list-view-item {
padding: 5px;
color: #666;
display:table;
line-height: 30px;
box-sizing: border-box;
border-bottom: 1px solid green;
}

I try to change the js code :
this.$refs.content.style.transform = `translateY(0, ${ start * this.itemHeight }px, 0)`;
to :
this.$refs.content.style.transform = `translateY(${ start * this.itemHeight }px)`;
and add a css to div class is list-view:
transform:translateY(0)px;
then the border showed.
don't understand why this action work!

.list-view-item {
padding: 5px;
color: #666;
display: flex;
flex-basis: 100%;
flex-direction: row;
line-height: 30px;
box-sizing: border-box;
border-bottom: 1px solid red;
}

Related

How can I disable my submit button when a text area is left blank?

I am trying to make a submit button that is disabled when a text area is left blank, but alas I am not seeing results. If any advice could be spared it would be greatly appreciated. Here is the code that pertains to this problem. This submit button works as if it were not disabled, so I'm guessing there must be something wrong in my typescript or the way that I am constructing the disable in the first place, but I am getting to the point where I am running out of Ideas. If anyone has any questions feel free to ask.
<template>
<div id="editEntryDiv">
<div id="mainContent" v-if="loaded">
<PartsForm v-model="localPartEntry" />
</div>
<Teleport to="#mainContent">
<div class="actionBar">
<!-- Empty Div Required for formatting -->
<div>
<button id="deleteButton" #click="deleteItem(parseInt(id))">
<i class="fas fa-trash"></i>
<span>Delete</span>
</button>
</div>
<!-- Empty Div Required for formatting -->
<div>
<button
id="submitButton"
:class="{ disabled: localPartEntry.partNumber == undefined }"
:disabled="localPartEntry.partNumber == undefined"
#click="submitItem"
>
<i class="fas fa-paper-plane"></i>
<span>Submit</span>
</button>
</div>
</div>
</Teleport>
</div>
</template>
here is the typescript:
<script lang="ts">
import { defineComponent } from "vue";
import { usePartStore } from "../stores/part-store";
import PartsForm from "../components/PartsForm.vue";
import { PartDefinition } from "../types/PartDefinition";
import { mapStores } from 'pinia'
export default defineComponent({
components: {
PartsForm,
},
data() {
return {
loaded: false,
localPartEntry: {} as PartDefinition,
};
},
watch: {
localPartEntry: {
handler() {
if (!this.loaded) return
sessionStorage.setItem("unsavedPart", JSON.stringify(this.localPartEntry))
},
deep: true
}
},
computed: {
...mapStores(usePartStore),
id(): string {
return this.$route.params.id.toString();
},
},
methods: {
async submitItem(): Promise<void> {
this.localPartEntry.id = parseInt(this.id);
if (await this.partStore.editPartDefinition(this.localPartEntry))
if (await this.partStore.getParts())
this.$router.push({
path: `/`,
});
},
async deleteItem(id: number) {
if (confirm("Are you sure you want to delete this entry?"))
if (await this.partStore.deletePartDefinition(id))
if (await this.partStore.getParts())
this.$router.push({
path: `/`,
});
},
},
mounted() {
for (let element of this.partStore.partEntries as PartDefinition[]) {
if (element.id == parseInt(this.$route.params.id.toString())) {
this.localPartEntry.id = element.id;
this.localPartEntry.partNumber = element.partNumber;
this.localPartEntry.variant = element.variant;
this.localPartEntry.revision = element.revision;
this.localPartEntry.description = element.description;
this.localPartEntry.supplier = element.supplier;
this.localPartEntry.previewImagePath = element.previewImagePath;
this.localPartEntry.previewImageDateTime = element.previewImageDateTime;
this.localPartEntry.obsolete = element.obsolete;
this.localPartEntry.internalOnly = element.internalOnly;
this.loaded = true;
break;
}
}
},
});
</script>
And finally, the css:
<style lang="sass" scoped>
#editEntryDiv
width: 100%
height: 100%
background: $primary-background
display: flex
flex-direction: column
overflow-y: auto
-ms-overflow-style: none // for Internet Explorer, Edge */
scrollbar-width: none // for Firefox */
&::-webkit-scrollbar
display: none // for Chrome, Safari, and Opera */
#mainContent
margin-top: 1rem
margin-bottom: 5rem
flex-grow: 1
#submitButton
border: 1px solid $primary-accent-color
font-size: 1.5rem
border-radius: .25rem
cursor: pointer
padding: .25rem .75rem
transition: background .3s, color .3s
color: $primary-accent-color
background: transparent
display: flex
flex-direction: row
justify-content: center
align-items: center
gap: .5rem
&:hover
color: $tertiary-background
background: $primary-accent-color
.disabled
background: grey !important
#deleteButton
border: 1px solid $primary-accent-color
font-size: 1.5rem
border-radius: .25rem
cursor: pointer
padding: .25rem .75rem
transition: background .3s, color .3s
color: $primary-accent-color
background: transparent
display: flex
flex-direction: row
justify-content: center
align-items: center
gap: .5rem
&:hover
color: $tertiary-background
background: $primary-accent-color
.actionBar
width: 100%
min-height: 4rem !important
background: $secondary-background
display: flex
justify-content: space-between
position: fixed
bottom: 0px
right: 0px
&>div
display: flex
flex-direction: row
align-items: center
gap: 1rem
margin: 0 1rem
</style>
You can just check with Logical NOT (!) operator which takes truth to falsity and vice versa. Hence, If there will be no value in the textarea it will return false else true.
Working Demo :
new Vue({
el: '#app',
data: {
content: ''
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
Textarea content: {{ content }}
<br><br>
<textarea v-model="content"></textarea>
<button id="submitButton" :disabled="!content.trim()">
<span>Submit</span>
</button>
</div>
I just added an example to show you how to achieve. You can made the changes in your original code.

Contain fixed element within container

Having a flex layout with top, left, middle and right.
Middle is divided into main and foot.
Within the main I want to have fixed elements, kind of like an MDI, as well as static elements.
If one scroll the fixed element should stay in same position of view. But, it should be contained within the main element if it is moved above or to the left of main. As in: not overlap the top, left, right etc.
THIS:
Colors and margins added to make a visual representation of the layout
NOT THIS:
Below is a simplified sample with a container within a container.
If one select the fixed positioning for the sub "window" it stay in place on scrolling, but it overlaps the parent if moved outside.
I can use absolute and reposition it on scroll by using JavaScript, but wondered if there was a pure CSS / layout way to get the same result.
function set_style_pos (e) {
moveable.style.position = e.target.value;
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.marginLeft = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.marginTop = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
let cs;
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
cs = getComputedStyle(drag.el);
drag.ex = parseInt(cs.getPropertyValue('margin-left')) || 0;
drag.ey = parseInt(cs.getPropertyValue('margin-top')) || 0;
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100);
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
flex-grow: 1;
overflow: hidden;
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 140px;
height: 150px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
margin-left: 90px;
margin-top: -5px;
padding: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
<div class="outer">
<div class="inner">
<div class="box" id="moveable" data-moveable="1">
<div>Move Me</div><br />
<label><input type="radio" name="p" value="absolute" checked />absolute</label><br />
<label><input type="radio" name="p" value="fixed" />fixed</label>
</div>
<pre id="lines"></pre>
</div>
</div>
Just use z-index.
Example:
function set_style_pos (e) {
moveable.style.position = e.target.value;
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.marginLeft = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.marginTop = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
let cs;
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
cs = getComputedStyle(drag.el);
drag.ex = parseInt(cs.getPropertyValue('margin-left')) || 0;
drag.ey = parseInt(cs.getPropertyValue('margin-top')) || 0;
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100);
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
/*flex-grow:1; Disable to control the height for presentaion*/
height:200px !important;
overflow:hidden; /*to hide scrollme lines*/
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 140px;
height: 150px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
margin-left: 90px;
margin-top: -5px;
padding: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
.prevent{
width:200px;
height:200px;
display:flex;
background-color:blue;
color:white;
justify-content:center;
align-items:center;
font-weight:bold;
/*--The solution--*/
z-index:1;
}
<div class="outer">
<div class="inner">
<div class="box" id="moveable" data-moveable="1">
<div>Move Me</div><br />
<label><input type="radio" name="p" value="absolute" checked />absolute</label><br />
<label><input type="radio" name="p" value="fixed" />fixed</label>
</div>
<pre id="lines"></pre>
</div>
</div>
<div class="prevent">
Prevent overlap
</div>
I hope this helps.
Use a sticky container and let children be absolute.
Had tested with z-index, all over, before posting but had not found any satisfactory solution that way.
I also tried various with position: sticky, and there is where I found the solution at last :)
One can wrap the sub windows in a sticky container which is positioned top left of the main container.
Pros:
Simple
Fairly clean HTML structure
The window stay below scroll-bars of container
Positioning relative to content wrapper
Cons:
If one want to make it non-fixed / non-sticy one have to move the element to parent and vice versa.
Absolute positioned children will not expand the container – thus not rearranging the DOM flow. (Which was the issue on earlier attempts using sticky).
Tested in FireFox, Chrome, Vivaldi, Opera Mini and Opera.
The core of it:
<div class="outer">
<div class="main">
<div class="wrap-sticky">
<div class="sub-window">
Fixed Window
</div>
</div>
Other "normal" content
</div>
</div>
And:
.outer {
overflow: hidden;
}
.main {
position: relative;
overflow: scroll;
}
.wrap-sticky {
position: sticky;
top: 0;
left: 0;
}
.sub-window {
position: absolute;
}
function get_pos (el) {
let cs = getComputedStyle(el);
return [
parseInt(cs.getPropertyValue('left')) || 0,
parseInt(cs.getPropertyValue('top')) || 0
];
}
function set_style_pos (e) {
let [x, y] = get_pos (moveable);
if (e.target.value == "sticky") {
wrap_sticky.appendChild(moveable);
moveable.style.left = (x - inner.scrollLeft) + 'px';
moveable.style.top = (y - inner.scrollTop) + 'px';
} else {
inner.appendChild(moveable);
moveable.style.left = (x + inner.scrollLeft) + 'px';
moveable.style.top = (y + inner.scrollTop) + 'px';
}
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.left = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.top = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
[drag.ex, drag.ey] = get_pos(drag.el);
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100) + "horiz".repeat(100) + 'END';
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
flex-grow: 1;
overflow: hidden;
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 160px;
height: 100px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
padding: 20px;
top: 20px;
left: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
.wrap-sticky {
position: sticky;
top: 0;
left: 0;
}
<div class="outer">
<div class="inner" id="inner">
<div class="wrap-sticky" id="wrap_sticky">
<div class="box" id="moveable" data-moveable="1">
<div>Drag & Move Me</div>
<label><input type="radio" name="p" value="sticky" checked />In sticky</label><br />
<label><input type="radio" name="p" value="absolute" />In main</label>
</div>
</div>
<pre id="lines"></pre>
</div>
</div>

Microsoft BotFramework-WebChat scrolling issues

I'm using the microsoft/BotFramework-WebChat, but I'm having problems getting it to scroll properly.
Often when the bot responds the user is forced to manually scroll to the bottom of the chat log.
I can't find any documentation about hooks that would let me call an API to scroll it.
Is there a way to get it so that the chat window scrolls automatically?
HTML:
<div id="bot-button" style="display:none" >
<p id="need-help" class="triangle-isosceles">Hey! Need any help?</p>
<div id="bot-open" token="temptoken">
<span>
<img id="avatar" src="/img/avatar.png"/>
<i id="message-count">2</i>
</span>
</div>
<div id="bot-close"><img src="/img/close.png" height="20px"/>Close</div>
<div id="webchat" role="main"></div>
</div>
<script src="https://cdn.botframework.com/botframework-webchat/master/webchat.js"></script>
<script src="/js/chat.js"></script>
JavaScript:
(async function () {
// In this demo, we are using Direct Line token from MockBot.
// To talk to your bot, you should use the token exchanged using your Direct Line secret.
// You should never put the Direct Line secret in the browser or client app.
// https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication
var bearer_token = document.getElementById("bot-open").getAttribute("token");
const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
method: 'POST',
headers: {
"Authorization": "Bearer " + bearer_token
}
});
const {
token
} = await res.json();
// We are using a customized store to add hooks to connect event
const store = window.WebChat.createStore({}, ({
dispatch
}) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
// When we receive DIRECT_LINE/CONNECT_FULFILLED action, we will send an event activity using WEB_CHAT/SEND_EVENT
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: {
language: window.navigator.language
}
}
});
}
return next(action);
});
const styleOptions = {
bubbleBackground: 'rgba(0, 0, 255, .1)',
bubbleFromUserBackground: 'rgba(0, 255, 0, .1)',
hideUploadButton: true,
botAvatarInitials: 'DA',
};
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({
token
}),
userID: guid(),
store,
styleOptions
}, document.getElementById('webchat'));
sizeBotChat();
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
function sizeBotChat() {
let bot_container = document.getElementById("bot-button");
if (isMobileDevice()) {
bot_container.style.width = "100%";
bot_container.style.bottom = "0px";
bot_container.style.right = "0px";
let max_height = screen.height - 50;
document.getElementById("webchat").style.maxHeight = max_height + "px";
console.log(screen.height);
} else {
bot_container.style.width = "400px";
bot_container.style.right = "50px";
document.getElementById("webchat").style.maxHeight = "400px";
}
}
CSS (loaded by javascript inserting a link into the head element):
.triangle-isosceles {
position: relative;
padding: 15px;
color: black;
background: white;
border-radius: 10px;
}
/* creates triangle */
.triangle-isosceles:after {
content: "";
display: block;
/* reduce the damage in FF3.0 */
position: absolute;
bottom: -15px;
right: 30px;
width: 0;
border-width: 15px 15px 0;
border-style: solid;
border-color: white transparent;
}
#avatar {
height: 50px;
}
#need-help {
display: none;
}
/* based on badge progress-bar-danger from bootstrap */
#message-count {
display: inline-block;
min-width: 10px;
padding: 3px 7px 3px 7px;
font-size: 12px;
font-weight: 700;
line-height: 1;
color: white;
text-align: center;
white-space: nowrap;
vertical-align: middle;
border-radius: 10px;
background-color: #d9534f;
position: relative;
top: -20px;
right: 20px;
}
#bot-button {
position: fixed;
bottom: 50px;
right: 0px;
width: 100%;
}
#bot-open {
height: 50px;
width: 100%;
text-align: right;
}
#bot-close {
background-color: blue;
background-image: url("https://localhost/img/avatar.png");
background-repeat: no-repeat;
color: white;
height: 22px;
display: none;
height: 50px;
padding: 15px 15px 0 0;
text-align: right;
vertical-align: middle;
}
/* hide chat on load */
#webchat {
display: none;
max-height: 400px;
overflow: scroll;
}
#webchat div.row.message {
margin: 0;
}
The developers designed WebChat to scroll the conversation to the bottom if the user hasn't scrolled up. If the user has scrolled up, there should be a 'New Message' button that appears in the bottom right corner of the chat when the bot sends a new message.
You can modify this behavior by using a custom middleware - which it looks like you already are - and scrolling the last element in the conversation into view when the user receives a message from the bot. See the code snippet below.
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
document.querySelector('ul[role="list"]').lastChild.scrollIntoView({behavior: 'smooth', block: 'start'});
}
...
return next(action);
}
);
Hope this helps!

ionic 3 grid view with two col per row

I have one screen , which will have the data to display from database. I already tried grid view in ionic 1 its fine. But ionic 3 i don't know how to do the grid view.From database i will get like 10 or 11 or 13 category names , that names i need to display in grid view with background some image.
I know how to display background image.But i need to display 2 col per row.here my code that i will use to fetch data from database.....
another(loading:any) {
this.subcatdata = { CatID: this.categoryid };
this.authService.subcatte(this.subcatdata).then((result) => {
this.data = result;
console.log(this.data);
if (this.data.status == 1) {
this.Catdata = this.data.SubCatgeoryList;
for (let i = 0; i < this.Catdata.length; i++) {
console.log(this.Catdata[i].SubCategoryName);
}
}
else if (this.data.status == 0) {
let alert = this.alertCtrl.create({
title: 'Error',
subTitle: 'Please Enter Valid Username & Password',
buttons: ['OK']
});
alert.present();
}
loading.dismiss();
}, (err) => {
loading.dismiss();
});
}
In my above code i will get the subcatgory name by using below code :
for (let i = 0; i < this.Catdata.length; i++) {
console.log(this.Catdata[i].SubCategoryName);
}
In my html :
<div class="item item-body no-padding" style="border-width: 0px !important;">
<div class="row no-padding" *ngFor="let data of Catdata; let i = index" (click)="openresources(Catdata[i].SubCatID)">
<div class="col col-50 custom-design2" style="background: url(background url) no-repeat center;background-size: cover;">
<div class="custom-design1"><span class="grid-title">{{Catdata[i].SubCategoryName}}</span></div>
</div>
</div>
</div>
My scss :
.gallery {
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
div.no-padding, ion-item.no-padding {
padding: 0 !important;
}
div.custom-design2 {
height: 153px;
padding: 1px;
}
.swiper-pagination-bullet-active {
opacity: 1;
background: #FFF !important;
}
.no-scroll .scroll-content{
overflow: hidden;
}
div.custom-design1 {
text-align: center;
padding: 1px;
height: 153px;
vertical-align: middle;
position: relative;
background-color: rgba(0,0,0,0.5);
color: #fff;
width: 100%;
}
div.custom-design1.extended {
height: 153px;
}
span.grid-title {
font-weight: 700;
position: absolute;
top: 50%;
left: 0;
right: 0;
}
.transparent {
background: transparent !important;
}
.full_height {
height: 100% !important;
border: none;
}
Now in my screen its coming like one data per row, with full row background.
But what i need is two col ( 2 data/sub cat name per row).I know we need to use index + 1, but in ionic 3 i dont know how to do.
If any help, that will be helpfull
Thanks.

How can I control the placement of my Chart.JS pie chart's legend, as well as its appearance?

I am able to create a pie chart using Chart.JS with this code:
HTML
<div>
<canvas id="top10ItemsChart" style="padding-left:20px" width="320" height="320"></canvas>
<div id="top10Legend" class="chart-legend"></div>
</div>
jQuery
var data = [{
value: 2755,
color: "#FFE135",
label: "Bananas"
}, {
value: 2256,
color: "#3B5323",
label: "Lettuce, Romaine"
}, {
value: 1637,
color: "#fc6c85",
label: "Melons, Watermelon"
}, {
value: 1608,
color: "#ffec89",
label: "Pineapple"
}, {
value: 1603,
color: "#021c3d",
label: "Berries"
}, {
value: 1433,
color: "#3B5323",
label: "Lettuce, Spring Mix"
}, {
value: 1207,
color: "#046b00",
label: "Broccoli"
}, {
value: 1076,
color: "#cef45a",
label: "Melons, Honeydew"
}, {
value: 1056,
color: "#421C52",
label: "Grapes"
}, {
value: 1048,
color: "#FEA620",
label: "Melons, Cantaloupe"
}];
var optionsPie = {
legend: {
display: true,
position: 'right',
labels: {
fontColor: 'rgb(255, 99, 132)'
}
}
}
var ctx = $("#top10ItemsChart").get(0).getContext("2d");
var top10PieChart = new Chart(ctx).Pie(data, optionsPie);
document.getElementById('top10Legend').innerHTML = top10PieChart.generateLegend();
The problem is that it positions the legend to the bottom of the pie, and even spilling and bleeding outside of the boundaries of the div to which I want the pie to restrict itself:
It also presents the legend as a simple unordered list. What I want to do is to control the color of the various elements in the legend ("Banana" should be the same color (#FFE135) as the piece of banana pie (so to speak), etc.)
How can I make the individual elements match the color of its respective data point?
UPDATE
The "Legend Label Configuration" topic in the official docs here indicate you can set the fontColor of the legends, but this is for the whole shebang; what I want to know is, how is it possible to control the color of each item?
UPDATE 2
In an attempt to at least get the legend displaying in the desired spot, I added this to the jQuery:
var optionsPie = {
legend: {
display: true,
position: 'right',
labels: {
fontColor: 'rgb(255, 99, 132)'
}
}
}
. . .
var myPieChart = new Chart(ctx).Pie(data, optionsPie);
document.getElementById("legendDiv").innerHTML = myPieChart.generateLegend();
...but it makes no difference - the legend is still hung from the bottom of the pie chart, and its font is still the default black.
UPDATE 3
I utilized some suggested code, but the legend is still gravity-fed instead of hanging off to the right:
So the legend impinges on the chart below it, rather than restricting itself to its own neighborhood.
Also, I don't want the bullet points to infest the legend - the colored squares (and the verbiage - but also the values) are all I need. How can I shove the legend from south of the pie to east of the pie?
UPDATE 4
I have refactored the code based on this and it's looking better (I added more data to the "label" value of the data array, too):
Still, though, as you can see the legend is infringing on the quadrant below it. There is a "ton" of empty/wasted space around the pie, though - I want to move the pie to the left, and the legend to the right of the pie. That would also allow more vertical space for the pie to grow in stature.
How can I do that? Here is the code I'm using now:
HTML
<div>
<canvas id="top10ItemsChart" class="pie" style="padding-left:20px"></canvas>
<div id="top10Legend"></div>
</div>
CSS
.pie-legend {
list-style: none;
margin: 0;
padding: 0;
}
.pie-legend span {
display: inline-block;
width: 14px;
height: 14px;
border-radius: 100%;
margin-right: 16px;
margin-bottom: -2px;
}
.pie-legend li {
margin-bottom: 10px;
display: inline-block;
margin-right: 10px;
}
JQUERY
var data = [{
value: 2755,
color: "#FFE135",
label: "Bananas: 2,755 (18%)"
}, {
. . .
}, {
value: 1048,
color: "#FEA620",
label: "Melons, Cantaloupe: 1,048 (7%)"
}];
var optionsPie = {
responsive: true,
scaleBeginAtZero: true,
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
}
var ctx = $("#top10ItemsChart").get(0).getContext("2d");
var top10PieChart = new Chart(ctx).Pie(data, optionsPie);
$("#top10Legend").html(top10PieChart.generateLegend());
NOTE: Adding this to optionsPie:
legend: {
display: true,
position: 'right'
},
...does nothing - the legend remains weighted down to the floor like a frog filled to the chin with quail shot.
UPDATE 5
I've played around with Teo's example, trying to get it to work just right but, although it's better, the pie is very puny, and the legend should be wider, but I can't figure out how to stretch the legend horizontally and the pie in all directions. Here's how it looks now:
This is the code now (JQUERY is the same):
HTML
<div class="col-md-6">
<div class="topleft">
<h2 class="sectiontext">Top 10 Items</h2>
<br />
<div class="legendTable">
<div class="legendCell">
<canvas id="top10ItemsChart" class="pie" style="padding-left:20px"></canvas>
</div>
<div class="legendCell" id="top10Legend">
</div>
</div>
</div>
</div>
CSS
.topleft {
margin-top: -4px;
margin-left: 16px;
margin-bottom: 16px;
padding: 16px;
border: 1px solid black;
}
canvas {
width: 100% !important;
height: auto !important;
}
.legendTable {
border: 1px solid forestgreen;
display: table;
width: 100%;
table-layout: fixed;
}
.legendCell {
display: table-cell;
vertical-align: middle;
}
.pie-legend ul {
list-style: none;
margin: 0;
padding: 0;
width: 300px;
}
.pie-legend span {
display: inline-block;
width: 14px;
height: 12px;
border-radius: 100%;
margin-right: 4px;
margin-bottom: -2px;
}
.pie-legend li {
margin-bottom: 4px;
display: inline-block;
margin-right: 4px;
}
Something is squashing the pie and pushing the outer edges of the legend together.
UPDATE 6
Ochi, et al: Here's what I see after the Ochification of my code:
This is my code - I even ordered the jQuery in the way you have it, although I doubt that is really necessary:
HTML
<div class="row" id="top10Items">
<div class="col-md-6">
<div class="topleft">
<h2 class="sectiontext">Top 10 Items</h2>
<br />
#*<div class="legendTable">
<div class="legendCell">
<canvas id="top10ItemsChart" class="pie" style="padding-left:20px"></canvas>
</div>
<div class="legendCell" id="top10Legend">
</div>
</div>*#
<div class="chart">
<canvas id="top10ItemsChart" class="pie"></canvas>
<div id="pie_legend"></div>
</div>
</div>
</div>
. . .
</div>
CSS
.pie-legend {
list-style: none;
margin: 0;
padding: 0;
}
.pie-legend span {
display: inline-block;
width: 14px;
height: 14px;
border-radius: 100%;
margin-right: 16px;
margin-bottom: -2px;
}
.pie-legend li {
margin-bottom: 10px;
display: block;
margin-right: 10px;
}
.chart,
#priceComplianceBarChart,
#pie_legend {
display: inline-flex;
padding: 0;
margin: 0;
}
JQUERY
var optionsPie = {
responsive: true,
scaleBeginAtZero: true,
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
}
var ctx = $("#top10ItemsChart").get(0).getContext("2d");
var data = [{
value: 2755,
color: "#FFE135",
label: "Bananas: 2,755 (18%)"
. . .
}, {
value: 1048,
color: "#FEA620",
label: "Melons, Cantaloupe: 1,048 (7%)"
}];
var top10PieChart = new Chart(ctx).Pie(data, optionsPie);
$("#pie_legend").html(top10PieChart.generateLegend());
...and yet the pie is stretchier than stretch pants on an elephant.
UPDATE 7
Maybe there's a configuration problem or something. I decided to "upgrade" to version 2.1.3 of Chart.JS (started out w. version 1.0.2):
#*<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>*#
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.js"></script>
...and copied almost exactly Teo Dragovic's CodePen here.
The only things I changed were the names of two CSS class ("table" became "legendTable" and "cell" became "legendCell") and the color of the table border from red to forestgreen, and I get this now:
Do I need to also a reference a Chart.JS CSS file or something?
I think this what you want: DEMO
First, you need to make canvas responsive by overriding fixed width and height and wrap it in additional div that can be used for positioning. I used display: table for centering elements but setting inner divs to inline-block also works if you wish for chart and legend to take different amount of space than 50:50.
HTML:
<div class="table">
<div class="cell">
<canvas id="top10ItemsChart" class="pie"></canvas>
</div>
<div class="cell" id="top10Legend"></div>
</div>
CSS:
canvas {
width: 100% !important;
height: auto !important;
}
.table {
border: 1px solid red;
display: table;
width: 100%;
table-layout: fixed;
}
.cell {
display: table-cell;
vertical-align: middle;
}
UPDATE: Did some adjustment based on additional information by OP NEW DEMO
HTML:
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="topleft">
<h2 class="sectiontext">Top 10 Items</h2>
<br />
<div class="chart">
<div class="pie">
<canvas id="top10ItemsChart" class="pie"></canvas>
</div>
<div class="legend" id="top10Legend">
</div>
</div>
</div>
</div>
</div>
</div>
CSS:
.topleft {
margin-top: -4px;
margin-left: 16px;
margin-bottom: 16px;
padding: 16px;
border: 1px solid black;
}
canvas {
width: 100% !important;
height: auto !important;
margin-left: -25%;
}
.chart {
border: 1px solid forestgreen;
width: 100%;
overflow: hidden;
position: relative;
}
.pie {
position: relative;
padding: 10px 0;
// adjust as necessary
padding-left: 10px;
padding-right: 0;
}
.legend {
position: absolute;
right: 10px;
top: 10px;
height: 100%;
// adjust as necessary:
width: 48%;
}
#media (max-width: 480px) {
.legend {
position: relative;
width: 100%;
}
.pie {
margin: 0;
}
}
.pie-legend ul {
list-style: none;
margin: 0;
padding: 0;
width: 300px;
}
.pie-legend span {
display: inline-block;
width: 14px;
height: 12px;
border-radius: 100%;
margin-right: 4px;
margin-bottom: -2px;
}
.pie-legend li {
margin-bottom: 4px;
display: inline-block;
margin-right: 4px;
}
As #B.ClayShannon mentioned, version 2 is quite a bit different than verison 1. Here is an example of how to customize the legend template using version 2.
options: {
legendCallback: function (chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend" style="list-style:none">');
for (var i = 0; i < chart.data.datasets[0].data.length; i++) {
text.push('<li><div style="width:10px;height:10px;display:inline-block;background:' + chart.data.datasets[0].backgroundColor[i] + '" /> ');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</li>');
}
text.push('</ul>');
return text.join('');
},
legend: {display: false},
}
It's not shown directly in the accepted solution above, but to render your legend elsewhere you'll want to call:
$("#myChartLegend").html(myChart.generateLegend());
Finally, some HTML to pull it together (note clearfix is a Bootstrap class that :
<div class="chart">
<div style="float:left">
<canvas id="myChart" class="pie" style="max-width:300px;"></canvas>
</div>
<div class="legend" id="myChartLegend" style="float:left;"></div>
<div style="clear: both;"/>
</div>
This is what works (more or less) using version 2 of Chart.JS:
HTML
<h2 class="sectiontext">Top 10 Items</h2>
<br />
<div class="chart">
<canvas id="top10ItemsChart" class="pie"></canvas>
<div id="pie_legend"></div>
</div>
JQUERY
var data = {
labels: [
"Bananas: 2,755 (18%)",
"Lettuce, Romaine: 2,256 (14%)",
"Melons, Watermelon: 1,637 (10%)",
"Pineapple: 1,608 (10%)",
"Berries: 1,603 (10%)",
"Lettuce, Spring Mix: 1,433 (9%)",
"Broccoli: 1,207 (8%)",
"Melons, Honeydew: 1,076 (7%)",
"Grapes: 1,056 (7%)",
"Melons, Cantaloupe: 1,048 (7%)"
],
datasets: [
{
data: [2755, 2256, 1637, 1608, 1603, 1433, 1207, 1076, 1056, 1048],
backgroundColor: [
"#FFE135",
"#3B5323",
"#fc6c85",
"#ffec89",
"#021c3d",
"#3B5323",
"#046b00",
"#cef45a",
"#421C52",
"#FEA620"
],
}]
};
var optionsPie = {
responsive: true,
scaleBeginAtZero: true
}
var ctx = $("#top10ItemsChart").get(0).getContext("2d");
var top10PieChart = new Chart(ctx,
{
type: 'pie',
data: data,
options: optionsPie
});
$("#top10Legend").html(top10PieChart.generateLegend());
I say, "more or less" because the pie pieces are still pitifully puny: