/ Gists

Gists

On gists

Google map (ES 6 / promise)

JavaScript ES 6 Vue.js

googlemap.js #

// Your personal API key.
// Get it here: https://console.cloud.google.com/google/maps-apis
const API_KEY = '***'
const CALLBACK_NAME = 'gmapsCallback'

let initialized = !!window.google
let resolveInitPromise
let rejectInitPromise
// This promise handles the initialization
// status of the google maps script.
const initPromise = new Promise((resolve, reject) => {
  resolveInitPromise = resolve
  rejectInitPromise = reject
})

export default function init() {
  // If Google Maps already is initialized
  // the `initPromise` should get resolved
  // eventually.
  if (initialized) return initPromise

  initialized = true
  // The callback function is called by
  // the Google Maps script if it is
  // successfully loaded.
  window[CALLBACK_NAME] = () => resolveInitPromise(window.google)

  // We inject a new script tag into
  // the `<head>` of our HTML to load
  // the Google Maps script.
  const script = document.createElement('script')
  script.async = true
  script.defer = true
  script.src = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&callback=${CALLBACK_NAME}`
  script.onerror = rejectInitPromise
  document.querySelector('head').appendChild(script)

  return initPromise
}

On gists

Accordion

Vue.js

Accordion.vue #

<template>
  <ul>
    <slot></slot>
  </ul>
</template>

<script>
export default {
  props: {},
  data() {
    return {
      AccordionState: {
        count: 0,
        active: 1
      }
    }
  },
  provide() {
    return { AccordionState: this.AccordionState }
  }
}
</script>

<style lang="scss" scoped></style>

On gists

Debounce, Throttle, Wait (ES6 Utils)

JS oneliners

fns.js #

export function debounce(fn, wait){
    let timer;
   return function(...args){
     if(timer) {
        clearTimeout(timer); // clear any pre-existing timer
     }
     const context = this; // get the current context
     timer = setTimeout(()=>{
        fn.apply(context, args); // call the function if time expires
     }, wait);
   }
}


export function throttle(fn, wait){
    let throttled = false;
    return function(...args){
        if(!throttled){
            fn.apply(this,args);
            throttled = true;
            setTimeout(()=>{
                throttled = false;
            }, wait);
        }
    }
}


export function wait(waitTime, callback = () => {}) {
  return new Promise(resolve => {
    setTimeout(() => {
      callback()
      resolve(true)
    }, waitTime)
  })
}



window.addEventListener('resize', debounce(() => {
    console.log('Resized!');
}, 200));


window.addEventListener('scroll', throttle(() => {
    console.log('Scrolled!');
}, 200));

On gists

triangles in CSS (3 ways)

CSS CSS trick

index.css #

/* 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;
}

On gists

ResizeObserver.html

JavaScript

ResizeObserver.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>

On gists

Resize Observer : only as many images what i need in limited container

JavaScript

ResizeObserver.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>

On gists

Mounted teleport

Vue.js

MountedTeleport.vue #

<!--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>

On gists

Computed with delay, resize window

Vue.js

app.vue #

<!-- 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>

On gists

Nette: Admin LastUserEdit Trait

Nette

LastEditTrait.php #

<?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');
        };
	}


}

On gists

CSS Clamp builder (responsive fluid typography)

JavaScript CSS CSS trick

clampBuilder.js #

// 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 )"