/ Gists

Gists

On gists

Web component

JavaScript Web components

calendar.js #

class AmbiCalendar extends HTMLElement
{
    constructor()
    {
      super();
      this.shadow = this.attachShadow({ mode: "open"});
    }

    connectedCallback()
    {
      this.render(true, []);

      fetch('/api/v1/dk_workshop_date?filters[dk_voucher_id]=' + this.getAttribute('voucherId'))
      .then((response) => {
        if (response.ok) {
          return response.json();
        } else {
          return Promise.reject(response);
        }

      }).then((data) => {
        this.render(false, data);
      }).catch(function (err) {
        // There was an error
        console.warn('Something went wrong.', err);
      });
    }

    render(loading, data)
    {
      let body = '';

      if (loading) {
        body = '<div>loading...</div>';
      } else {
        data.map((item) => {
          body += `<li>${item.date_from} - ${item.date_to}</li>`;
        });
      }

      this.shadow.innerHTML = `
        <h1>Ambi calendar</h1>
        <lu>
            ${body}
        </lu>
      `;
    }
}

customElements.define('ambi-calendar', AmbiCalendar);

On gists

JS Parallax

JavaScript

parallax.js #

// https://github.com/yablko/responzivny-web-kurz/blob/crave-game-verzia-z-kurzu/index.html

		const headerEl = document.querySelector('header')
		const logoEl = document.querySelector('.crave-logo img')
		const setTranslate = (xPos, yPos, el) => {
			el.style.transform = `translate3d(${xPos}, ${yPos}px, 0)`;
		}
		let xScrollPosition;
		let yScrollPosition;
		const scrollLoop = () => {
			xScrollPosition = window.scrollX;
			yScrollPosition = window.scrollY;
			setTranslate(0, yScrollPosition * 0.75, headerEl);
			setTranslate(0, yScrollPosition * -0.25, logoEl);
			requestAnimationFrame(scrollLoop);
		}
		window.addEventListener('DOMContentLoaded', scrollLoop, false);

On gists

Ref slot (Options vs Composition, Parent / Child, Child / Parent)

Popular ⭐ Vue.js

ChildToParent-ScopedSlot.vue #

<!-- 1) From Child to Parent via scoped slot and dynamic ref-->

<!-- App.vue-->
<template>
  <div id="app">
    <HelloWorld v-slot="{ setRef }">
      <input type="text" :ref="(el) => setRef(el)" />
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
};
</script>


<!-- Hello world -->
<template>
    <slot v-bind="{ setRef }"></slot>
    <button @click="setFocus">SET</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      ref: ''
    }
  },
  methods: {
    setFocus()
    {
      this.ref.focus()
    },
    setRef(el) {
     this.ref = el
     console.log(el)
    }
  }
}
</script>

On gists

Iframe autoheight

JavaScript

index.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>
    </head>
    <body>
        <iframe id="frame" src="long.html" width="100%" height="0" frameborder="0"></iframe>

        <script>
            let f = document.getElementById('frame');
            f.onload = () => {
                console.log('nacteno');
                console.dir(f.contentDocument.documentElement);

                var obsah = f.contentDocument || f.contentWindow.document;
                f.style.height = obsah.documentElement.scrollHeight + 'px';
            };
        </script>
    </body>
</html>

On gists

Emit, callback in parent

Vue.js

example.vue #

<template>
<ClickWait color="red" @click="doFoo">Darn Button</ClickWait>
 <AnotherClickWait style="background-color:#c0c0c0" @click="doFoo">One More Darn Button</AnotherClickWait>
</template>

import ClickWait from '@/components/ClickWait.vue';
import AnotherClickWait from '@/components/AnotherClickWait.vue';

export default {
  name: "App",
  components: {
    ClickWait, AnotherClickWait
  },
  methods:{
    doFoo(done) {
      setTimeout(() => {
        console.log('im done with whatever biz logic');
        done();
      },3000);
    }
  }
};

On gists

Filtering & searching in list + URL history state

Vue.js

app.vue #

// https://www.raymondcamden.com/2021/05/08/updating-and-supporting-url-parameters-with-vuejs

// hard coded for simplicity...
const ITEMS = [
	{ name: "Ray", type: "person" },
	{ name: "Lindy", type: "person" },
	{ name: "Jacob", type: "person" },
	{ name: "Lynn", type: "person" },
	{ name: "Noah", type: "person" },
	{ name: "Jane", type: "person" },
	{ name: "Maisie", type: "person" },
	{ name: "Carol", type: "person" },
	{ name: "Ashton", type: "person" },
	{ name: "Weston", type: "person" },
	{ name: "Sammy", type: "cat" },
	{ name: "Aleese", type: "cat" },
	{ name: "Luna", type: "cat" },
	{ name: "Pig", type: "cat" },
	{ name: "Cayenne", type: "dog" }
]

const app = new Vue({
	el:'#app',
	data: {
		allItems: ITEMS,
		filter:'',
		typeFilter:[]
	},
	created() {
		let qp = new URLSearchParams(window.location.search);
		let f = qp.get('filter');
		if(f) this.filter = qp.get('filter');
		let tf = qp.get('typeFilter');
		if(tf) this.typeFilter = tf.split(',');
	},
	computed: {
		items() {
			this.updateURL();
			return this.allItems.filter(a => {
				if(this.filter !== '' && a.name.toLowerCase().indexOf(this.filter.toLowerCase()) === -1) return false;
				if(this.typeFilter.length && !this.typeFilter.includes(a.type)) return false;
				return true;
			});
		}
	},
	methods:{
		updateURL() {
			let qp = new URLSearchParams();
			if(this.filter !== '') qp.set('filter', this.filter);
			if(this.typeFilter.length) qp.set('typeFilter', this.typeFilter);
			history.replaceState(null, null, "?"+qp.toString());
		}
	}
});

On gists

nextTick

Vue.js

component.vue #

<!--
https://chafikgharbi.com/vuejs-nexttick/
https://codepen.io/chafikgharbi/pen/WNGarPG

-->


<template>
  <div id="app">
    <div ref="box" class="box">
      <div v-for="(message, key) in messages" :key="key">{{message.body}}</div>
      ...
    </div>
    <button v-on:click="addMessage">Add message</button>
  </div>
</template>

<style>
.box {
  height: 100px;
  width: 300px;
  overflow: scroll;
  border: 1px solid #000
}
</style>

<script>
export default {
  name: "App",
  data: () => {
    return {
      messages: [
        { body: "Message 1" },
        { body: "Message 2" },
        { body: "Message 3" },
        { body: "Message 4" },
        { body: "Message 5" }
      ]
    };
  },
  methods: {
    async addMessage() {

      // Add message
      this.messages.push({ body: `Message ${this.messages.length+1}`})

      // Scroll bottom
      this.$refs.box.scrollTop = this.$refs.box.scrollHeight
    },
    
    async addMessage() {
  
  // Add message
  this.messages.push({ body: `Message ${this.messages.length+1}`})

  // Wait for DOM to update
  await this.$nextTick()
  
  // Scroll bottom
  this.$refs.box.scrollTop = this.$refs.box.scrollHeight
},
    
    async addMessage() {
  
  // Add message
  this.messages.push({ body: `Message ${this.messages.length+1}`})

  // Wait for DOM to updaten then scroll down
  this.$nextTick(() => {
    this.$refs.box.scrollTop = this.$refs.box.scrollHeight
  })
  
}
  }
};
</script>

On gists

JS objects copies, shallow, deep, lodash etc ...

JavaScript

copies.js #

/*
All ways 
===========
https://code.tutsplus.com/articles/the-best-way-to-deep-copy-an-object-in-javascript--cms-39655 

*/


let a = {
	user: 'A',
	age: 99,
	hobbies: {
		sport: ['soccer', 'hockey'],
		others: ['sleeping']
	}
}

// only shallow copy
let b = {...a}

b.age = 50
b.user = 'B'
b.hobbies.sport = ['ping-pong']
b.hobbies.others = ['eating hamburgers']


// deep copy with nested
let c = JSON.parse(JSON.stringify(a));


c.age = 20
c.user = 'C'
c.hobbies.sport = ['swimming']
c.hobbies.others = ['tv & music']

console.log(a)
console.log(b)
console.log(c)

On gists

Nette - multiple db connections

Nette Nette-Database

connetionsModel.php #

<?php

namespace Model;

use Nette;

class Connections
{

	/** @var Nette\Di\Container */
	private $container;


	public function __construct(Nette\Di\Container $container)
	{
		$this->container = $container;
	}


	public function getOld()
	{
		return $this->container->getService('nette.database.main');
	}


	public function getNew()
	{
		return $this->container->getService('nette.database.msp22');
	}

}

On gists

TinyModal - usage

AW

tpl.latte #

{* POUZITI MODALU  2 zpusoby, inline nebo pres store mimo modal *}
        <div n:if="false" class="mx-auto max-w-lg mb-10 space-y-4">

            <h2 class="text-h3 text-msp-red">@DEV</h2>
            <h2 class="text-h4">Modaly .. nejsou videt, open buttony jsou vyple</h2>
            <tiny-modal store-id="modal1" max-width="1000px">
                <template #default="{ close }">
                    <div class="p-8">
                        CONTENT MODAL 1
                    </div>
                </template>
            </tiny-modal>
    

            <tiny-modal store-id="modal2" max-width="1000px">
                <template #default="{ close }">
                    <div class="p-8">
                        CONTENT MODAL 2
                    </div>
                </template>
            </tiny-modal>

            <tiny-modal max-width="1000px">
                <template #button="{ open }">
                    <button @click="open">Otevřít modál na přímo 3</button>
                </template>
                <template #default="{ close }">
                    <div class="p-8">
                        CONTENT MODAL 3
                    </div>
                </template>
            </tiny-modal>
            <hr />
            <h2 class="text-h4">Buttony mimo modaly</h2>


            <button @click="$store.dispatch('tinyModal/open', { id: 'modal1' })">modal 1 open</button> |
            <button @click="$store.dispatch('tinyModal/open', { id: 'modal2' })">modal 2 open</button>

        </div>