/* https://htmixl.medium.com/how-to-create-triangles-in-css-three-easy-ways-49bd6dce7810 */
/* 1. borders */
.triangle{
width: 0;
height: 0;
border-left: 150px solid red;
border-top: 150px solid transparent;
}
/* 2. clip-path */
.triangle{
width: 150px;
height: 150px;
background-color: blue;
clip-path: polygon(0 0, 0 100%, 100% 100%);
}
/* 3. gradient */
.triangle{
width: 150px;
height: 150px;
background-image:
linear-gradient(to top right, green 50%, transparent 0);
background-repeat: no-repeat;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
display: flex;
width: 100%; /* This can be changed via the buttons. */
height: 200px;
padding: 8px;
background: #eaeaea;
}
/* Image Wrappers */
.container > div {
height: 200px;
min-width: 100px;
max-width: 200px;
flex: 1;
background-size: cover;
background-repeat: no-repeat;
}
/* The rest is just setup. */
#app {
display: flex;
flex-direction: column;
align-items: center;
font-family: sans-serif;
padding: 16px;
}
p {
margin-top: 32px;
text-align: center;
color: #4c11f7;
text-transform: uppercase;
font-weight: 600;
font-size: 14px;
letter-spacing: 0.5px;
}
.change-size {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
</style>
</head>
<body>
<div id="app">
<div class="container"></div>
<p>Container Size</p>
<div class="change-size">
<button value="100%">100%</button>
<button value="1400px">1400px</button>
<button value="640px">640px</button>
<button value="200px">200px</button>
</div>
</div>
<script>
/* Attach ResizeObserver to the container. */
const resizeObserver = new ResizeObserver(onResize);
resizeObserver.observe(document.querySelector('.container'));
const IMAGE_MAX_WIDTH = 200;
const IMAGE_MIN_WIDTH = 100;
function onResize(entries) {
const entry = entries[0];
const container = entry.target;
/* Calculate how many images can fit in the container. */
const imagesNeeded = Math.ceil(entry.contentRect.width / IMAGE_MAX_WIDTH);
let images = container.children;
console.log(images);
/* Remove images as needed. */
while (images.length > imagesNeeded) {
images[images.length - 1].remove();
}
/* Add images as needed. */
while (images.length < imagesNeeded) {
let seed = Math.random().toString().replace('.', '');
const newImage = document.createElement('div');
const imageUrl = `https://picsum.photos/seed/${seed}/${IMAGE_MAX_WIDTH}`;
newImage.style.backgroundImage = `url(${imageUrl})`;
container.append(newImage);
}
}
/* The rest is for the container size buttons. */
function changeSize(e) {
if (!e.target.value) return;
const container = document.querySelector('.container');
container.style.width = e.target.value;
}
const changeSizeButtons = document.querySelector('.change-size');
changeSizeButtons.addEventListener('click', changeSize);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
display: flex;
width: 100%; /* This can be changed via the buttons. */
height: 200px;
padding: 8px;
background: #eaeaea;
}
/* Image Wrappers */
.container > div {
height: 200px;
min-width: 100px;
max-width: 200px;
flex: 1;
background-size: cover;
background-repeat: no-repeat;
}
/* The rest is just setup. */
#app {
display: flex;
flex-direction: column;
align-items: center;
font-family: sans-serif;
padding: 16px;
}
p {
margin-top: 32px;
text-align: center;
color: #4c11f7;
text-transform: uppercase;
font-weight: 600;
font-size: 14px;
letter-spacing: 0.5px;
}
.change-size {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
</style>
</head>
<body>
<div id="app">
<div class="container"></div>
<p>Container Size</p>
<div class="change-size">
<button value="100%">100%</button>
<button value="1400px">1400px</button>
<button value="640px">640px</button>
<button value="200px">200px</button>
</div>
</div>
<script>
/* Attach ResizeObserver to the container. */
const resizeObserver = new ResizeObserver(onResize);
resizeObserver.observe(document.querySelector('.container'));
const IMAGE_MAX_WIDTH = 200;
const IMAGE_MIN_WIDTH = 100;
function onResize(entries) {
const entry = entries[0];
const container = entry.target;
/* Calculate how many images can fit in the container. */
const imagesNeeded = Math.ceil(entry.contentRect.width / IMAGE_MAX_WIDTH);
let images = container.children;
console.log(images);
/* Remove images as needed. */
while (images.length > imagesNeeded) {
images[images.length - 1].remove();
}
/* Add images as needed. */
while (images.length < imagesNeeded) {
let seed = Math.random().toString().replace('.', '');
const newImage = document.createElement('div');
const imageUrl = `https://picsum.photos/seed/${seed}/${IMAGE_MAX_WIDTH}`;
newImage.style.backgroundImage = `url(${imageUrl})`;
container.append(newImage);
}
}
/* The rest is for the container size buttons. */
function changeSize(e) {
if (!e.target.value) return;
const container = document.querySelector('.container');
container.style.width = e.target.value;
}
const changeSizeButtons = document.querySelector('.change-size');
changeSizeButtons.addEventListener('click', changeSize);
</script>
</body>
</html>
<!--https://stackoverflow.com/questions/63652288/does-vue-3-teleport-only-works-to-port-outside-vue-->
<template>
<Teleport :to="to" v-if="isMounted"><slot></slot></Teleport>
</template>
<script>
export default {
name: "MountedTeleport",
props: ['to'],
data() {
return {isMounted: false}
},
mounted() {
this.isMounted = true;
}
}
</script>
<!-- https://codepen.io/sandrarodgers/pen/porZbxW -->
<template>
<div id="app">
<div>Height: {{ height }}</div>
<div>Width: {{ width }}</div>
</div>
</template>
<script>
export default {
data() {
return {
debouncedHeight: 0,
debouncedWidth: 0,
heightTimeout: null,
widthTimeout: null
};
},
computed: {
height: {
get() {
return this.debouncedHeight;
},
set(val) {
if (this.timeout) clearTimeout(this.timeout);
this.heightTimeout = setTimeout(() => {
this.debouncedHeight = val;
}, 5000);
}
},
width: {
get() {
return this.debouncedWidth;
},
set(val) {
if (this.timeout) clearTimeout(this.timeout);
this.widthTimeout = setTimeout(() => {
this.debouncedWidth = val;
}, 5000);
}
},
},
methods: {
resizeHandler(e) {
this.height = window.innerHeight;
this.width = window.innerWidth;
},
},
mounted() {
this.height = window.innerHeight;
this.width = window.innerWidth;
},
created() {
window.addEventListener("resize", this.resizeHandler);
},
destroyed() {
window.removeEventListener("resize", this.resizeHandler);
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
font-size: 1.5rem;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<?php
trait FastAdminLastChangeTrait {
/**
* Bootstrap trait
*
* @return void
*/
public function injectFastAdminLastChangeTrait()
{
$this->onBeforeSave[] = function($editRow, $values, $form) {
$values['lastchange'] = new \DateTime();
$values['lastchange__user_id'] = $this->getUser()->getId();
};
$this->onCreateForm[] = function($form)
{
if(isset($form['lastchange']))
$form['lastchange']->getControlPrototype()->addClass('deactivateInput');
if(isset($form['lastchange__user_id']))
$form['lastchange__user_id']->getControlPrototype()->addClass('deactivateInput');
};
}
}
// https://css-tricks.com/linearly-scale-font-size-with-css-clamp-based-on-the-viewport/#for-those-who-dont-mind-that-edge-case
// https://royalfig.github.io/fluid-typography-calculator/
// https://fluid-typography.netlify.app/
function clampBuilder( minWidthPx, maxWidthPx, minFontSize, maxFontSize ) {
const root = document.querySelector( "html" );
const pixelsPerRem = Number( getComputedStyle( root ).fontSize.slice( 0,-2 ) );
const minWidth = minWidthPx / pixelsPerRem;
const maxWidth = maxWidthPx / pixelsPerRem;
const slope = ( maxFontSize - minFontSize ) / ( maxWidth - minWidth );
const yAxisIntersection = -minWidth * slope + minFontSize
return `clamp( ${ minFontSize }rem, ${ yAxisIntersection }rem + ${ slope * 100 }vw, ${ maxFontSize }rem )`;
}
console.log(clampBuilder(320, 1920, 1, 3)); // "clamp( 1rem, 0.6rem + 2vw, 3rem )"
const newData = Array(20)
.fill(0)
.map((_, index) => index++);
// https://levelup.gitconnected.com/what-is-intersection-observer-api-and-how-is-it-useful-1e91a14579df
// https://codepen.io/ohdylan/pen/ZExqKzx
let secondLastChild = document.querySelectorAll('.box:nth-last-child(2)')[0];
let boxes = document.querySelector('.boxes')
const mockFetchMoreBoxes = () => {
const startIndex = parseInt(secondLastChild.textContent.replace('Test Element', '')) + 2
for(let i = startIndex; i < startIndex + 20; i++) {
const newBox = document.createElement('div')
newBox.textContent = `Test Element ${i}`
newBox.classList.add('box')
boxes.append(newBox)
if(i == startIndex + 18) {
observer.unobserve(secondLastChild)
secondLastChild = newBox
observer.observe(secondLastChild);
}
}
}
let observer = new IntersectionObserver((entries) => {
console.log(entries[0])
const secondLastChild = entries[0]
if(secondLastChild.isIntersecting){
return mockFetchMoreBoxes()
}
}, {})
observer.observe(secondLastChild);
/*
https://fatbuddhadesigns.co.uk/journal/full-bleed-layouts/
https://www.joshwcomeau.com/css/full-bleed/
https://blog.logrocket.com/full-bleed-layout-css-grid/
*/
.wrapper {
display: grid;
grid-template-columns: 1fr min(60ch, calc(100% - 64px)) 1fr;
grid-column-gap: 32px;
}
.full-bleed {
grid-column: 1 / -1;
}
.full-bleed {
width: 100%;
grid-column: 1 / 4;
}