angular - click flexbox grid cell to expand area below with full width - css

I have something like calendar grid using flexbox inside a ionic 3 project.
The cells are one array
<div class="sb-calendar-wrapper">
<div class="sb-calendar-month">
<div class="sb-calendar-row">
<div class="sb-calendar-cell sb-cell-labels sb-week-day" *ngFor="let day of sbcalendar.dayLabels">
{{day}}
</div>
</div>
<div class="sb-calendar-row">
<div class="sb-calendar-cell sb-cell" *ngFor="let day of calendarDays, let j=index" (click)="openCalendarDay(day)" [ngStyle]="{'height': (day.isOpen) ? '300px' : '60px'}">{{j}}
</div>
</div>
</div>
</div>
.sb-cell {
position: relative;
border-bottom: 1px solid rgba(130, 171, 183, 0.2);
padding: 5px;
height: calc((100vh - 150px)/6);
.sb-calendar-day-list{
position: absolute;
width: 100vw;
left: 0;
top:calc((100vh - 150px)/6);
background: #ccc;
height: calc( 300px - calc((100vh - 150px)/6));
}
}
When I click on any day, i want the "row" to expand to display an area which I can use for e.g. displaying items of that cell.
The problem I'm facing is how to position that list element, so that i will fill the entire space or width. With position:relative of the cell I can position everything fine except for the left:0, which doesnt work for most cells.
In summary when i click any day, a box that fills the entire view width should expand. thanks for any suggestions!
EDIT1
I don't want to create manual rows after each n-cell. The cell array should stay as a single array while the flexitem widths controls how many items are in each row.

I think you can organize those days on the same line as a row.
At the end of each row, you can add a place holder div with an ng-if condition.
Whenever you click on a day of that row, you can check the row index for showing the div or not. That div will hold an area that you want.
html:
<div class="flexbox-row" ng-repeat="row in rows track by $index">
<div class="flexbox-col clickable" ng-click="showRow($index)">
1
</div>
<div class="flexbox-col clickable" ng-click="showRow($index)">
2
</div>
<div class="flexbox-col clickable" ng-click="showRow($index)">
3...
</div>
<!-- Place holder div -->
<div ng-if="showRow() === $index">
<div data-custom-directive></div>
</div>
</div>
Show row code:
scope.showRow = function ($index) {
// Return activeRow
if ($index === undefined) {
return scope.activeRow;
}
// Hide if same row
if ($index === scope.activeFlight) {
scope.activeRow = null;
return;
}
// Show row
scope.activeRow = parseInt($index);
};

Related

How to prevent squashing flexbox absolute pane by ChartJs?

The code below is simplified version of actual one, but it "correctly" shows the problem -- I have window with T-layout. Top section with toolbar, and below the left with some long content, and the right which should take the remaining space.
I used "absolute" position for the left pane because my first problem was how to scroll long content within pane, this is what I found, so I used it.
On the right there is ChartJs and initially it really takes the remaining space but if I move the mouse over it refreshes the content, resulting in squashing the left pane to zero.
How to prevent it (not using hardcoded sizes, like "min-width: 200px")? As left-right panes sizing goes, my intention is "left -- take all what is necessary, right -- take the rest".
The code:
<!DOCTYPE html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
</head>
<body style="background-color: pink; overflow:hidden;margin:0" onload="starter()">
<div style="display: flex;flex-direction:column; height: 100vh;width: 100%">
<!-- TOOLBAR -->
<div style="flex-shrink: 0; flex-grow: 0;">
<button>hello</button>
</div>
<div style="flex-shrink: 0; flex-grow: 1;
display: flex;flex-direction:row; ">
<!-- LEFT PANE -->
<div style="position:relative; background-color: aqua; overflow:scroll;
flex-shrink: 0; flex-grow: 1;">
<div style="position:absolute; width: 100%">
<div>
<h1>
<li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>4</li>
<li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>1</li>
<li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>4</li>
<li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>0</li>
</ul>
</h1>
</div>
<div>left</div>
</div>
</div>
<!-- RIGHT PANE -->
<div style="background-color: green; flex-shrink: 0; flex-grow: 1;
display: flex;flex-direction:column;">
<div style="position: relative;flex-shrink: 1; flex-grow: 1;">
<canvas id="bar-chart" style="" ></canvas>
</div>
<div style="flex-shrink: 0; flex-grow: 0;">right</div>
</div>
</div>
</div>
<script>
function starter(){
// https://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/
new Chart(document.getElementById("bar-chart"), {
type: 'bar',
data: {
labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
datasets: [
{
label: "Population (millions)",
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
data: [2478,5267,734,784,433]
}]
},
options: {
maintainAspectRatio : false,
legend: { display: false },
title: {
display: true,
text: 'Predicted world population (millions) in 2050'
}
}
});
}
</script>
</body>
</html>
So I started from the initial problem -- how to take remaining space and enable scroll there? For two panes there is nice solution with the flex layout: https://stackoverflow.com/a/68516470/210342 but as in my case, when I have nested panes, I couldn't make flex take entire window and only window space (without using this hack with absolute position).
Since I don't know CSS I tried grid approach and it worked right on spot. I simply added "height:100vh" for entire grid container and for given scrollable pane "overflow:auto" and that's it.
Now I have scrolling and well sized chart.
Nice grid generator I used: https://cssgrid-generator.netlify.app/

Inserting component on click between grid rows

I have a grid of items and when I click on one of them I want to show another component that takes all the width on the row under the clicked item.
Here is a picture of what I want to achieve
I have a button-list-component which contain a button-component that display my items with an ngFor.
When I click on an item the size-selection-component is shown with a ngIf.
My problem is that if I have the size-selection-component in the button-list-component, it append at the end of the grid, and if I have this new component in the button-component then it only takes the width of the column containing the clicked item.
If I put it in position absolute I can make it the width of the container but then the following items won't go down.
Here is a piece of my code
button-list
<div class="wrapper">
<div class="container">
<app-button *ngFor="let button of buttons;" [button]="button" (buttonClicked)="onButtonClicked($event)">
</app-button>
</div>
</div>
.container {
position: relative;
display: grid;
grid-template-columns: repeat(3, minmax(250rem, 1fr));
grid-gap: 10rem;
place-items: start center;
overflow-x: hidden;
overflow-y: auto;
margin: 10rem;
}
button
<div class="product">
<div class="product-col">
<h2 class="product_title" (click)="onButtonClicked()">{{button.Caption}}</h2>
<img class="product_img" [src]="picture" alt="" (click)="onButtonClicked()">
<span class="product_price" *ngIf="button.Price && button.Price != '0'">{{button.Price / 100 | currency}}</span>
</div>
</div>
<app-size-selection-button-list *ngIf="isSizeSelectionOpen"></app-size-selection-button-list>
How would it be possible to achieve that ? Maybe there is a solution in CSS or maybe another way to insert my component in the grid of items that would work.
Thanks
You don't have to append the div, you can create it and hide it, when user hover over the item div, you show it and set its child tag's innerHTML to whatever you need, like this
<body>
<p onmouseover="show();"></p>AA</p>
<div style="width:100%">
<div style="width:33%">
<p>Name</p>
</div>
<div style="width:33%">
<p>Name</p>
</div>
<div style="width:33%">
<p>Name</p>
</div>
</div>
<div id="blah" style="display:none">
<!-- whatever here -->
</div>
<script>
function show() {
var b=document.getElementById("blah");
b.style.display="block";
// Change some innerHTML of elements inside the blah div here
}
</script>

How to make canvas responsive

I use bootstrap. I want the user to be able to choose the canvas size while keeping the design screen responsive within the div.
<div class="row">
<div class="col-xs-2 col-sm-2" id="border">content left</div>
<div class="col-xs-6 col-sm-6" id="border">
Width <input type="number" class="form-control"><br>
Height <input type="number" class="form-control"><br>
canvas
<canvas id="canvas" width="500" height="300">
</canvas>
</div>
<div class="col-xs-2 col-sm-2" id="border">content right</div>
How can I limit the size of the canvas to the size of the div?
I do not know if it will be necessary to use JavaScript.
Edit
It should be taken into account that the width and height values are entered by the user and the canvas must be in div proportional in size
https://jsfiddle.net/1a11p3ng/2/
You can have a responsive canvas in 3 short and simple steps:
Remove the width and height attributes from your <canvas>.
<canvas id="responsive-canvas"></canvas>
Using CSS, set the width of your canvas to 100%.
#responsive-canvas {
width: 100%;
}
Using JavaScript, set the height to some ratio of the width.
var canvas = document.getElementById('responsive-canvas');
var heightRatio = 1.5;
canvas.height = canvas.width * heightRatio;
To change width is not that hard. Just remove the width attribute from the tag and add width: 100%; in the css for #canvas
#canvas{
border: solid 1px blue;
width: 100%;
}
Changing height is a bit harder: you need javascript. I have used jQuery because i'm more comfortable with.
you need to remove the height attribute from the canvas tag and add this script:
<script>
function resize(){
$("#canvas").outerHeight($(window).height()-$("#canvas").offset().top- Math.abs($("#canvas").outerHeight(true) - $("#canvas").outerHeight()));
}
$(document).ready(function(){
resize();
$(window).on("resize", function(){
resize();
});
});
</script>
You can see this fiddle: https://jsfiddle.net/1a11p3ng/3/
EDIT:
To answer your second question. You need javascript
0) First of all i changed your #border id into a class since ids must be unique for an element inside an html page (you can't have 2 tags with the same id)
.border{
border: solid 1px black;
}
#canvas{
border: solid 1px blue;
width: 100%;
}
1) Changed your HTML to add ids where needed, two inputs and a button to set the values
<div class="row">
<div class="col-xs-2 col-sm-2 border">content left</div>
<div class="col-xs-6 col-sm-6 border" id="main-content">
<div class="row">
<div class="col-xs-6">
Width <input id="w-input" type="number" class="form-control">
</div>
<div class="col-xs-6">
Height <input id="h-input" type="number" class="form-control">
</div>
<div class="col-xs-12 text-right" style="padding: 3px;">
<button id="set-size" class="btn btn-primary">Set</button>
</div>
</div>
canvas
<canvas id="canvas"></canvas>
</div>
<div class="col-xs-2 col-sm-2 border">content right</div>
</div>
2) Set the canvas height and width so that it fits inside the container
$("#canvas").outerHeight($(window).height()-$("#canvas").offset().top-Math.abs( $("#canvas").outerHeight(true) - $("#canvas").outerHeight()));
3) Set the values of the width and height forms
$("#h-input").val($("#canvas").outerHeight());
$("#w-input").val($("#canvas").outerWidth());
4) Finally, whenever you click on the button you set the canvas width and height to the values set. If the width value is bigger than the container's width then it will resize the canvas to the container's width instead (otherwise it will break your layout)
$("#set-size").click(function(){
$("#canvas").outerHeight($("#h-input").val());
$("#canvas").outerWidth(Math.min($("#w-input").val(), $("#main-content").width()));
});
See a full example here https://jsfiddle.net/1a11p3ng/7/
UPDATE 2:
To have full control over the width you can use this:
<div class="container-fluid">
<div class="row">
<div class="col-xs-2 border">content left</div>
<div class="col-xs-8 border" id="main-content">
<div class="row">
<div class="col-xs-6">
Width <input id="w-input" type="number" class="form-control">
</div>
<div class="col-xs-6">
Height <input id="h-input" type="number" class="form-control">
</div>
<div class="col-xs-12 text-right" style="padding: 3px;">
<button id="set-size" class="btn btn-primary">Set</button>
</div>
</div>
canvas
<canvas id="canvas">
</canvas>
</div>
<div class="col-xs-2 border">content right</div>
</div>
</div>
<script>
$(document).ready(function(){
$("#canvas").outerHeight($(window).height()-$("#canvas").offset().top-Math.abs( $("#canvas").outerHeight(true) - $("#canvas").outerHeight()));
$("#h-input").val($("#canvas").outerHeight());
$("#w-input").val($("#canvas").outerWidth());
$("#set-size").click(function(){
$("#canvas").outerHeight($("#h-input").val());
$("#main-content").width($("#w-input").val());
$("#canvas").outerWidth($("#main-content").width());
});
});
</script>
https://jsfiddle.net/1a11p3ng/8/
the content left and content right columns will move above and belove the central div if the width is too high, but this can't be helped if you are using bootstrap. This is not, however, what responsive means. a truly responsive site will adapt its size to the user screen to keep the layout as you have intended without any external input, letting the user set any size which may break your layout does not mean making a responsive site.
The object-fit CSS property sets how the content of a replaced element, such as an img or video, should be resized to fit its container.
Magically, object fit also works on a canvas element. No JavaScript needed, and the canvas doesn't stretch, automatically fills to proportion.
canvas {
width: 100%;
object-fit: contain;
}
<div class="outer">
<canvas id="canvas"></canvas>
</div>
.outer {
position: relative;
width: 100%;
padding-bottom: 100%;
}
#canvas {
position: absolute;
width: 100%;
height: 100%;
}
extending accepted answer with jquery
what if you want to add more canvas?, this jquery.each answer it
responsiveCanvas(); //first init
$(window).resize(function(){
responsiveCanvas(); //every resizing
stage.update(); //update the canvas, stage is object of easeljs
});
function responsiveCanvas(target){
$(canvas).each(function(e){
var parentWidth = $(this).parent().outerWidth();
var parentHeight = $(this).parent().outerHeight();
$(this).attr('width', parentWidth);
$(this).attr('height', parentHeight);
console.log(parentWidth);
})
}
it will do all the job for you
why we dont set the width or the height via css or style? because it will stretch your canvas instead of make it into expecting size
this seems to be working :
#canvas{
border: solid 1px blue;
width:100%;
}
One of the best possible way to do this without even using JavaScript is to just put that canvas inside a div tag and then display block (Note: width:100%; height:100%; is completely optional).
Run the snippet below to see how it works.....
.container {
display: block;
margin: auto;
height: auto;
}
#canvas {
width: 100%;
height: 100%;
border: 1px solid black;
}
<div class="container">
<!-- width and height are set just to show you it actually works-->
<canvas id="canvas" width=864 height=480></canvas>
</div>
There's a better way to do this in modern browsers using the vh and vw units.
vh is the viewport height.
So you can try something like this:
<style>
canvas {
border: solid 2px purple;
background-color: green;
width: 100%;
height: 80vh;
}
</style>
This will distort the aspect ration.
You can keep the aspect ratio by using the same unit for each. Here's an example with a 2:1 aspect ratio:
<style>
canvas {
width: 40vh;
height: 80vh;
}
</style>
try using max-width: 100%; on your canvas.
canvas {
max-width: 100%;
}
Hi I know this post has been going on for a while, but I had a very similar problem with my canvas. like mentioned as the viewport gets resized and so does the elements within the window. This can be done using css, however it doesnt quite apply to the elements within the canvas. Now if your canvas context is 2D this is the solution that works for me...
This can be used as a method.
class ParentObject{
constructor()
/**
* #this this._width - Private width variable that would store the size of the window width
*/
this._width = Number();
/**
* #this this._height - Private Height variable that would store the height of the window
*/
this._height= Number();
/**
* Calls the getCanvasDimensions method
*/
this.getCanvasDimensions();
//Getting the Width and Height as soon as the Window loads
window.addEventListener('load',()=>{
this.getCanvasDimensions()
})
//As the window is resized we are getting the new Canvas Dimensions
window.addEventListener('resize',()=>{
this.getCanvasDimensions();
getCanvasDimensions() {
// Width is determined by the css value for the viewport width this is then respected by the device pixel ratio. This is then used to set the canvas.width value
this._width = Math.round((Number(getComputedStyle(canvas).getPropertyValue('width').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
//Setting the canvas width
canvas.width = this._width
// height is determined by the css value for the viewport height this is then respected by the device pixel ratio. This is then used to set the canvas.height value
this._height = Math.round((Number(getComputedStyle(canvas).getPropertyValue('height').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
//Setting the canvas height
canvas.height = this._height
}
get width(){
//This sets the width to the private _width value
return this._width
}
get height(){
//This sets the height to the private _height value
return this._height
}
As a function on the global scope:
let width,height
function responsiveCanvas(){
let w = Math.round((Number(getComputedStyle(canvas).getPropertyValue('width').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
width = canvas.height = w;
let h = Math.round((Number(getComputedStyle(canvas).getPropertyValue('height').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
height = canvas.height = h;
}
window.addEventListener('load',responsiveCanvas)
window.addEventListener('resize',responsiveCanvas)
Then just use the width and height variables to reference the sizes.
Anyways I hope that this helps someone out.
Try changing the width of the canvas to be equal to it's parent element.
<template>
<span class="Wrapper">
<canvas id="myChart" ></canvas>
</span>
</template>
<script>
const ctx = document.getElementById('myChart');
ctx.width = ctx.parentElement.offsetWidth;
ctx.height = ctx.parentElement.offsetHeight;
</script>

dynamically created bootstrap columns with different heights, float to left

Consider an unknown number of divs being created dynamically and styled using the bootstrap grid system. In this example, I'm using col-sm-4 so after every third block, we move to a new row. The blocks (divs) can be different heights, which is determined by the content within.
This is where I run into the layout problem. When moving to a new row, I want the fourth block to float to the left. This only happens when the left most div in the row above is also the shortest. I have pictures to illustrate.
Real Life:
The Dream:
The "correct" way to do this would be to wrap every three in a row class I beleive, but I'm not sure how to do this with dynamic content (could probably hack it) or if there's an easy css solution.
HTML:
<div class="row">
<div class="col-sm-12">
<div class="col-sm-4 block">
<div class="inner-block"></div>
</div>
<div class="col-sm-4 block">
<div class="inner-block"></div>
</div>
<div class="col-sm-4 block">
<div class="inner-block" style="height:150px"></div>
</div>
<div class="col-sm-4 block">
<div class="inner-block"></div>
</div>
</div>
</div>
CSS:
.block {
padding: 5px;
}
.inner-block {
height: 200px;
background-color: blue;
}
Plunker Example (expand preview to proper size)
If your system is unable to add first/last classes on every nth div, then you can use the nth-child css pseudo selector.
#media (min-width: 768px) {// For medium displays and above
.col-sm-4:nth-child(3n+1) { // We target every 3rd div but offset the count by 1 so that that 1st, 4th 7th etc divs are cleared
clear:both; // Clear the float
}
}

How to move the date in the cell to the bottom right in Full Calendar

I am using the latest version of jQuery Full calendar . Currently the date number by defaul is in the top right of the cell . I want to move it to the bottom right . I tried doing it via CSS i.e. vertical-align:bottom but it does not affect the first column.
/*Edit**/
Ok the css solution did not work so I moved the html structure around
Original :
<td class="fc-sat fc-widget-content fc-day6 fc-last">
<div>
<div class="fc-day-number">2</div>
<div class="fc-day-content">
<div style="position:relative"> </div>
</div>
</div>
</td>
New
<td class="fc-sat fc-widget-content fc-day6 fc-last">
<div>
<div class="fc-day-content">
<div style="position:relative"> </div>
</div>
</div>
<div class="fc-day-number">2</div>
</td>
'
As you can see I have moved the .fc-day-number div outside and added vertical-align:bottom to the td . This works but now the height resize of the cells does not work
You can edit the fullcalendar.css to add these settings to the existing tags:
.fc-widget-content {
position: relative;
}
.fc-day-number {
position: absolute;
right: 0;
bottom: 0;
}
This should move the day number to the bottom right corner.

Resources