I am using tailwindcss and flexboxes to create the following elements which are perfectly aligned horizontally:
<div className="flex flex-row justify-between">
<div className="text-body-light text-lg">Top 500</div>
{/** Percentage */}
{this.state.top_500_progress >= 0 ?
<div className="flex flex-row items-center bg-report-green-progress-background gap-x-1 px-1">
<ProgressUpIcon />
<div className="text-report-green-progress text-lg">{this.state.top_500_progress.toFixed()}%</div>
</div>
: <div className="flex flex-row items-center bg-report-red-progress-background gap-x-1 px-1">
<ProgressDownIcon />
<div className="text-report-red-progress text-lg">{this.state.top_500_progress.toFixed()}%</div>
</div>
}
</div>
Then I am exporting my page as pdf using jspdf this way:
generatePDF = () => {
var chartEl = document.getElementById("page2")
let input: any = window.document.getElementsByClassName("page2")[0]
html2canvas(input).then(canvas => {
const img = canvas.toDataURL("image/jpeg", 1.0);
var doc = new jsPDF('landscape', 'pt', [842, 455]);
doc.addImage(img, 'JPEG', 0, 0, 850, 455 );
doc.save("chart.pdf");
});
}
However my text is not aligned anymore:
I have tried adding a div with my-auto for each text element but still same result.
How to make it aligned in jspdf?
Related
I am trying to build a website and have this carousel, that was made with Alpine JS and Tailwind CSS. I took the Alpine Javascript from a template and it works according to specifications. But I would want to make it mouse draggable as well.
Here is the carousel slider
<div class="mt-24">
<script>
window.carousel = function () {
return {
container: null,
prev: null,
next: null,
init() {
this.container = this.$refs.container
this.update();
this.container.addEventListener('scroll', this.update.bind(this), {passive: true});
},
update() {
const rect = this.container.getBoundingClientRect();
const visibleElements = Array.from(this.container.children).filter((child) => {
const childRect = child.getBoundingClientRect()
return childRect.left >= rect.left && childRect.right <= rect.right;
});
if (visibleElements.length > 0) {
this.prev = this.getPrevElement(visibleElements);
this.next = this.getNextElement(visibleElements);
}
},
getPrevElement(list) {
const sibling = list[0].previousElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
getNextElement(list) {
const sibling = list[list.length - 1].nextElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
scrollTo(element) {
const current = this.container;
if (!current || !element) return;
const nextScrollPosition =
element.offsetLeft +
element.getBoundingClientRect().width / 2 -
current.getBoundingClientRect().width / 2;
current.scroll({
left: nextScrollPosition,
behavior: 'smooth',
});
}
};
}
</script>
<style>
.scroll-snap-x {
scroll-snap-type: x mandatory;
}
.snap-center {
scroll-snap-align: center;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<div class="flex mx-auto items-center">
<div x-data="carousel()" x-init="init()" class="relative overflow-hidden group">
<div x-ref="container"
class="ml-4 flex overflow-x-scroll scroll-snap-x space-x-4 no-scrollbar touch-pan-x cursor-pointer">
{% for origin in origins %}
<div class="group/item relative ml-4 flex-auto flex-grow-0 flex-shrink-0 w-4/5 sm:w-4/5 xl:w-2/5 rounded-lg bg-gray-100 items-center justify-center snap-center overflow-hidden shadow-md"><!-- items container -->
<div class="min-w-full h-full rounded-lg bg-gray-100 overflow-hidden shadow-md">
<div><img src="{{ origin.imgurl }}" alt="{{ origin.alt }}" class="object-cover h-96" /></div>
<div class="absolute bg-gray-800 bg-opacity-0 group-hover/item:bg-opacity-50 top-2/3 inset-x-0 text-center px-2 py-3">
<div class="text-2xl text-transparent group-hover/item:text-emerald-500 font-extrabold uppercase">{{ origin.name }}</div>
<div class="text-xl text-transparent group-hover/item:text-white font-bold">{{ origin.country }}</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div #click="scrollTo(prev)" x-show="prev !== null"
class="block absolute top-1/2 left-0 bg-white text-3xl p-2 rounded-full transition-transform ease-in-out transform -translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-back-outline"></ion-icon></div>
</div>
<div #click="scrollTo(next)" x-show="next !== null"
class="block absolute top-1/2 right-0 bg-white p-2 text-3xl rounded-full transition-transform ease-in-out transform translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-forward-outline"></ion-icon></div>
</div>
</div>
</div>
</div>
</div>
I tried adding Alpine script from a different carousel that has this function, but it does not do anything. My understanding of Javascript is rudimentary.
I have a page with progress, that should show progress between items, and when you arrive at the item, the bubble turns yellow. The bubble part works fine with getBoundingClientRect, however the scroll calculation is wrong (screenshot):
Code:
scollbar HTML (blade):
<div class="container grid grid-cols-12">
<div class='col-span-3 w-full text-white flex bg-cold85 pt-20 pl-11 pb-28 h-full sticky top-[90px] max-h-fit' >
<div class='flex shrink-0 items-center'>
<div class="w-[1px] h-full mt-[12px] -mr-[1px] z-30">
<div class="line w-[1px] h-[0%] !z-30 relative bg-yellow scroll-line"></div>
{{-- <div class="line w-[1px] h-[calc(100%_-_20px)] relative bg-cream"></div> --}}
</div>
<div class="w-[1px] h-full mt-[12px] -mr-[6px] z-0">
{{-- <div class="line w-[1px] h-[50%] !z-30 relative bg-yellow"></div> --}}
<div class="line w-[1px] h-[calc(100%_-_20px)] relative bg-cream"></div>
</div>
<div class="scrollcontainer">
#foreach ($items as $item)
<div class="flex items-center gap-x-6 mb-13 last:mb-0 children:first:border-yellow children:first:bg-yellow scrollbar-item" id='{{$item['id']}}'>
<i class="z-10 w-[11px] h-[11px] rounded-full border-2 border-cream bg-cold">
</i>
{{$item['text']}}
</div>
#endforeach
</div>
</div>
</div>
<div class="col-span-9 scroll-part">
#php(the_content())
</div>
</div>
script:
const items = document.querySelectorAll('.scrollbar-selector');
const scrollContainer = document.querySelector('.scrollcontainer');
window.addEventListener('scroll', () => {
const scroll = window.scrollY;
// const doc = document.body.clientHeight;
// const win = window.innerHeight;
const scrollPart = document.querySelector('.scroll-part').clientHeight;
let value = (scroll / (scrollPart)) * 100;
items.forEach(i => {
if (i.getBoundingClientRect().top <= 500) {
let id = i.getAttribute('id');
let sItem = scrollContainer.querySelector(`#${id} i`);
sItem.style.borderColor = '#F4C514';
sItem.style.backgroundColor = '#F4C514';
} else {
let id = i.getAttribute('id');
let sItem = scrollContainer.querySelector(`#${id} i`);
sItem.style.backgroundColor = '#151616';
sItem.style.borderColor = 'white';
}
});
const line = document.querySelector('.scroll-line');
line.style.height = `${value}%`;
});
Live example available here
I'm trying to make a basic layout where, on mobiles, only the latest posts appear. On desktop, the left column should be the posts and the right column the top categories and most popular posts.
Here is the layout:
const IndexLayout: React.FC<IndexLayoutProps> = ({}) => {
const cols = useScreenType()
return cols === '2-cols' ? (
<div className="w-full flex justify-between items-start">
<ListPosts data-comp="ListPosts" className="w-4/6" />
<div className="sticky ml-12 w-2/6 flex flex-col">
<TopCategories data-comp="TopCategories" className="w-full" />
<PopularPosts data-comp="PopularPosts" className="mt-4" />
</div>
</div>
) : (
<ListPosts data-comp="ListPosts" className="w-full" />
)
}
Here's the useScreenType hook:
import { useMediaQuery } from 'react-responsive'
export const useScreenType = () => {
const is2Cols = useMediaQuery({ minWidth: 1300 })
const is1Cols = useMediaQuery({ minWidth: 800 })
if (is2Cols) {
return '2-cols'
}
if (is1Cols) {
return '1-cols'
}
return 'fullscreen'
}
And I keep getting this error:
Warning: Expected server HTML to contain a matching <div> in <div>.
div
ListPosts#webpack-internal:///./components/posts/ListPosts.tsx:31:19
div
IndexLayout#webpack-internal:///./components/layout/IndexLayout.tsx:28:149
div
Index#webpack-internal:///./pages/index.tsx:24:149
ApolloProvider#webpack-internal:///./node_modules/#apollo/client/react/context/ApolloProvider.js:13:18
s#webpack-internal:///./node_modules/next-apollo/dist/index.es.js:26:1911
div
div
MyApp#webpack-internal:///./pages/_app.tsx:37:19
ErrorBoundary#webpack-internal:///./node_modules/#next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47
ReactDevOverlay#webpack-internal:///./node_modules/#next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:20
Container#webpack-internal:///./node_modules/next/dist/client/index.js:155:20
AppContainer#webpack-internal:///./node_modules/next/dist/client/index.js:643:18
Root#webpack-internal:///./node_modules/next/dist/client/index.js:779:19
Now I think the issue is due to the useScreenType hook not being able to get a width because window isn't defined on the server. But how can I fix this issue? And not only do I get an error, but my HTML renders weirdly.
The final render ends up being something like this (when it renders as '2-cols'):
<div class="flex flex-col justify-start items-start w-full">
<div class="mt-6 w-full"></div>
<div class="mt-4 flex items-center cursor-pointer transform transition hover:scale-105 text-sm">
<div class="w-full p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
<div class="mt-4 p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
</div>
</div>
Note: I am using Next.js v10.2.0
Code can be found on GitHub
As you notice, you cant access window object on server, so if you want to server-render something based on window object - you must hardcode these values.
The only thing you can rely on is user-agent in request headers, which gives you some understanding of user device.
For example this way you can detect user device in _app.js:
const device = deviceDetector.detect(isServer() ? ctx.req.headers['user-agent'] : window.navigator.userAgent)
deviceDetector is any kind of device detection implementation based on user agent
For anyone wondering how I fixed this, I ditched the responsive design with logic and switched to CSS. Here is my layout post fix (changed some classes with the lg prefix [documentation]):
const IndexLayout: React.FC<IndexLayoutProps> = ({}) => {
return (
<div className="mt-12 lg:mt-24 w-5/6 mx-auto flex items-start">
<div className="w-full flex justify-between items-start">
<ListPosts className="lg:w-4/6 w-full" />
<div className="hidden sticky ml-12 w-2/6 lg:flex flex-col">
<TopCategories className="w-full" />
<PopularPosts className="mt-4" />
</div>
</div>
</div>
)
}
I am using django and i have a base template where i defined a modal using alpine.js with $dispatch sender.
base.html:
<div x-data="modal()" class="mt-6" x-cloak>
<template x-on:show-modal.window="isOpenModal = $event.detail.show; modalHeader = $event.detail.modalHeader; modalData = showData($event.detail.modalData); "></template>
<div class="absolute z-50 top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50" x-show="isOpenModal">
<div class="z-50 text-left bg-gray-200 px-4 shadow-xl rounded-lg mx-2 md:max-w-lg md:p-6 lg:p-8 md:mx-0 h-auto " >
<div class="flex justify-between">
<h2 id="modalHeader" class="text-2xl" x-text="modalHeader"> </h2>
</div>
<div class="w-full border border-gray-600 mt-4" ></div>
<div id="modalContent" class="text-lg w-auto" > </div>
</div>
</div>
</div>
in script tags ....
function modal(){
return{
isOpenModal: false,
modalHeader:'',
modalData: '',
showData(data){
document.getElementById('modalContent').innerHTML = data
let fp = flatpickr(".pickerDate", {locale: "at", dateFormat: "d.m.Y"});
},
}
}
then in the other html which is extendet from the base.html i want to use the modal where i want to get with axios form data from the server and put it into the modal. This is working perfect. But i don't know how to realize the submit button ?
new.html
<div x-data="test()" #click="getCreateForm($dispatch)">
test click
</div>
this is the point where i go to function getCreateForm ....
function test(){
return{
getPatientCreateForm($dispatch){
axios.get("{% url 'user:createForm'%}")
.then(response => {
var modalHeader = response.data.header
var modalData = "<form id='createUser' class='' method='POST' action='' x-on:submit.prevent='?????????'> {% csrf_token %}" +response.data.seite.seite
$dispatch('show-modal', { show: true, modalHeader: modalHeader, modalData: modalData })
})
.catch(error => {
console.log(error);
})
}
}
}
The problem is when i put a sendForm function into the submit.prevent like: x-on:submit.prevent='sendForm()' alpine searches for the function on the base.html (where the modal is defined) and i don't want to implement a function there. I want by clicking the submit button that the data should be send with axios on the new.html (where i started with the getCreateForm ) and not at the base.html. Is that possible with alpine.js and $dispatch ? or is that impossible
Thanks for helping!
Martin
I am a beginner in React.js and have a problem. I am trying to reveal icons on scroll with a little delay of time for each icon. Something like that Example template. In this bootstrap template you can see when we scroll icons reveal (each icon with a little delay of time). Its possible with jquery scroll reveal module. But I don't know how to achive this with React.js. Is there anyway to do this in react.js using javascript only? Here is my react functional component code.
import React from 'react';
function Howitworks() {
return (
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">How It Works</h2>
<hr className="my-4 thick-hr-2" />
</div>
</div>
</div>
<div className="container text-center">
<div className="row">
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-home fa-4x icon-orange"></span>
<h3 className="my-3">Choose A Restaurant</h3>
</div>
</div>
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-utensils fa-4x icon-orange"></span>
<h3 className="my-3">Choose A Tasty Dish</h3>
</div>
</div>
<div className="col-md-6 col-lg-4">
<div className="service-box mt-5 mx-auto">
<span className="fas fa-shipping-fast fa-4x icon-orange"></span>
<h3 className="my-3">Pick Up Or Delivery</h3>
</div>
</div>
</div>
</div>
</div>
)
}
export default Howitworks;
Use Intersection Observer to observe when the containing div for the icons enters the viewport. Intersection Observer is vanilla JS, does not require any external modules or libraries, and is built for when elements are entering the viewport on scroll.
Here, I will make the container div easily targetable by giving it an id:
<div id="container-intersect" className="container text-center">
...
...
</div>
I then create a configuration object for IntersectionObserver:
// threshold controls how much of #container-intersect must
// be in view before firing the callback function. A value
// of 1.0 means that #container-intersect must be entirely
// in view. A value of 0.5 means that #container-intersect
// must be at least 50% in view.
var options = {
root: document.querySelector('body'),
rootMargin: '0',
threshold: 1.0
}
Then I create a new observer that fires the function callback when #container-intersect enters the viewport.
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#container-intersect');
observer.observe(target);
callback fires and fades in your elements.
var callback = function() {
let icons = document.querySelectorAll('.service-box span');
icons.forEach(function(icon, index) {
icons[index].style.opacity = '1';
});
};
You can place all of this code inside your componentDidMount() lifecycle function in your component, like so:
function Howitworks() {
componentDidMount() {
var options = {
root: document.querySelector('body'),
rootMargin: '0',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#container-intersect');
observer.observe(target);
var callback = function() {
let icons = document.querySelectorAll('.service-box span');
icons.forEach(function(icon, index) {
icons[index].style.opacity = '1';
});
};
}
render() {
return (
...
...
);
}
You can use this lib to detect the component is visible on screen.
Lib react-on-screen: https://github.com/fkhadra/react-on-screen
For use:
import React from 'react';
import TrackVisibility from 'react-on-screen';
const ComponentToTrack = ({ isVisible }) => {
const style = {
background: isVisible ? 'red' : 'blue'
};
return <div style={style}>Hello</div>;
}
const YourApp = () => {
return (
{/* Some Stuff */}
<TrackVisibility>
<ComponentToTrack />
</TrackVisibility>
{/* Some Stuff */}
);
}