/ Gists

Gists

On gists

SVG Circle + TW + animation

Tailwind CSS HTML SVG

index.html #


<!--
https://play.tailwindcss.com/B8YFBxQAJO
-->

<div class="group relative flex h-[10em] w-[10em] items-center justify-center rounded-[1.5em] px-[0.5rem] pt-[1rem]">
  <svg class="absolute left-1/2 top-[calc(50%+8px)] h-[9rem] w-[9rem] -translate-x-1/2 -translate-y-1/2" viewBox="0 0 160 160" fill="none" height="160" width="160" xmlns="http://www.w3.org/2000/svg">
    <path d="M130.912 130.912a71.997 71.997 0 0 0-10.911-110.778A71.999 71.999 0 0 0 9.383 94.046a72.004 72.004 0 0 0 19.705 36.866" stroke-width="16" stroke-linecap="round" stroke="#EEDC82"></path>
    <path d="M146.65 52.764A72.004 72.004 0 0 0 69.647 8.748a71.998 71.998 0 0 0-40.559 122.164" class="duration-[1s] [stroke-dasharray:100] [stroke-dashoffset:-99] group-hover:[stroke-dashoffset:0]" pathLength="100" stroke-width="16" stroke-linecap="round" stroke="#B49A18"></path>
  </svg>
  <p class="text-[0.75rem] font-semibold">1729/2500 Steps</p>
</div>

On gists

Usefull detach handler

JavaScript

detach.js #

/*
https://javascript.plainenglish.io/7-infamous-javascript-bugs-that-shook-the-internet-3cf8f8f76098
*/

// Proper event listener cleanup to prevent memory leaks
function attachEvent() {
  const btn = document.querySelector("#myButton");
  const handleClick = () => console.log("Button clicked!");
  btn.addEventListener("click", handleClick);

  // Return a function to remove the listener and free up memory
  return () => btn.removeEventListener("click", handleClick);
}
const detach = attachEvent();
detach(); // Cleanup resources

On gists

Closure modifier

PHP

example.php #

<?php
//  TransportListResolver
	public function getList(Order\Order $order, ?\Closure $transportListResolverModifier = null): array
	{
		$orderItems = $order->getItems($order::ITEM_PRODUCT);

		$transportListResolver = $this->transport->getTransportSelectionResolver()
			->getBaseSelection()
			->setCountry($order->delivery__country_id)
			->setUserGroups($this->user->isLoggedIn() ? $this->user->getRoles() : NULL)
			->setWeight($order->getUniqueWeights())
			->setOnlyForProduct($this->transport->listTransportOnlyForProduct($orderItems))
			->setDistance(isset($order->distance) ? $order->distance : null);

		if ($transportListResolverModifier) {
			$transportListResolverModifier($transportListResolver);
		}

		$list = array_filter(
            $transportListResolver->toArray(),
            fn($transport) => !$transport->transport_carrier->mandatory_type_choice
				|| $this->transportTypeChoice->listTransportChoiceOnlyForProduct($transport->id, $orderItems)
				|| $this->transportTypeChoice->listTransportChoiceOnlyForProduct($transport->id, $orderItems) === null
			);

		return $list;
	}
	


// usage in another class
// we can modify object via closure
$transportList = $this->transportListResolver->getList($order, function (TransportEshopOrderSelectionResolver $transportListResolver) {
			$transportListResolver->setWithChoiceNeeded(true);
	});
	
	

On gists

24 useForm

cdruc vue

useForm.js #

import {reactive} from "vue";

export default function useForm(fields) {
  return reactive({
    fields,
    processing: false,
    error: null,
    async submit(submitter) {
      if (this.processing) return;

      this.error = null;
      this.processing = true;

      try {
        await submitter(this.fields);
      } catch (err) {
        this.error = err;
      }

      this.processing = false;
    },
  });
}

On gists

22 UseLocalStorage

cdruc vue

useLocalStorage.js #

// https://tallpad.com/series/vuejs-misc/lessons/vue-state-and-localstorage-perfect-sync-made-simple
// onMounted => SSR


import {onMounted, ref, watch} from "vue";

export default function (initialValue, key) {
  const val = ref(initialValue);

  onMounted(() => {
    const storageVal = window.localStorage.getItem(key);

    if (storageVal) {
      val.value = JSON.parse(storageVal);
    }

    watch(
      val,
      val => {
        window.localStorage.setItem(key, JSON.stringify(val));
      },
      {deep: true}
    );
  });

  return val;
}

On gists

7 Google maps autocomplete

cdruc vue

index.html #

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tallpad</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_API_KEY&libraries=places"></script>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

On gists

Img.decode() // nahrada za onload()

JavaScript

img-decode.js #

const img = document.querySelector('img')
const tempImg = new Image()

tempImg.src = 'https://masoprofit.cz/storage/images/1600x2000/41847.png.webp'

tempImg.decode().then(() => {
  img.src = tempImg.src
}).catch(e => {
  console.log(e)
})

On gists

16 TOC

cdruc vue

Toc.vue #

// https://tallpad.com/series/vuejs-misc/lessons/table-of-contents-component-using-vuejs

<script setup>
import {onMounted, ref, computed} from "vue";
import TocList from "./TocList.vue";
import {slugifyWithCounter} from "@sindresorhus/slugify";

const props = defineProps({
  contentSelector: {
    type: String,
    required: true,
  },
});

const slugify = slugifyWithCounter();

const headings = ref([]);

onMounted(() => {
  window.document
    .querySelector(props.contentSelector)
    .querySelectorAll("h1, h2, h3, h4, h5, h6")
    .forEach(el => {
      let id = slugify(el.innerText);
      el.setAttribute("id", id);

      headings.value.push({
        id: id,
        level: parseInt(el.tagName.charAt(1), 10),
        content: el.innerText,
        subheadings: [],
      });
    });
});

const groupedHeadings = computed(() => {
  let items = [...headings.value];

  for (let i = items.length - 1; i >= 0; i--) {
    let currentItem = items[i];

    let parentItem = items.findLast((item, index) => {
      return item.level < currentItem.level && index < i;
    });

    if (parentItem) {
      parentItem.subheadings.unshift(currentItem);
      items.splice(i, 1);
    }
  }

  return items;
});
</script>

<template>
  <div class="bg-slate-50 -mx-6 p-6" v-if="groupedHeadings.length">
    <h3
      class="border-b-2 border-slate-300 inline-block uppercase font-bold tracking-wide text-slate-800 mb-5"
    >
      Contents:
    </h3>

    <TocList :items="groupedHeadings" />
  </div>
</template>

On gists

15 Alert , Tailwind + CVA

cdruc vue

Alert.vue #

// https://tallpad.com/series/vuejs-misc/lessons/build-an-alert-component-using-vue-tailwindcss-and-cva


<script setup>
import {InformationCircleIcon, XMarkIcon, CheckCircleIcon, ExclamationTriangleIcon, XCircleIcon} from "@heroicons/vue/20/solid/index.js";
import {computed} from "vue";
import {cva} from "class-variance-authority";

const props = defineProps({
  intent: {
    type: String,
    validator(value) {
      return ["info", "success", "danger", "warning"].includes(value);
    },
    default: "info",
  },
  title: String,
  show: {
    type: Boolean,
    default: true,
  },
  onDismiss: Function,
});

const containerClass = computed(() => {
  return cva("flex p-4 rounded-md space-x-3", {
    variants: {
      intent: {
        info: "bg-blue-100",
        success: "bg-green-100",
        warning: "bg-orange-100",
        danger: "bg-red-100",
      },
    },
  })({
    intent: props.intent,
  });
});

const iconClass = computed(() => {
  return cva("w-6 h-6", {
    variants: {
      intent: {
        info: "text-blue-700",
        success: "text-green-600",
        warning: "text-orange-400",
        danger: "text-red-500",
      },
    },
  })({
    intent: props.intent,
  });
});

const titleClass = computed(() => {
  return cva("font-medium", {
    variants: {
      intent: {
        info: "text-blue-900",
        success: "text-green-900",
        warning: "text-orange-900",
        danger: "text-red-900"
      },
    },
  })({
    intent: props.intent,
  });
});

const contentClass = computed(() => {
  return cva("text-sm", {
    variants: {
      intent: {
        info: "text-blue-800",
        success: "text-green-800",
        warning: "text-orange-800",
        danger: "text-red-800",
      },
    },
  })({
    intent: props.intent,
  });
});

const closeButtonClass = computed(() => {
  return cva("p-0.5 rounded-md -m-1", {
    variants: {
      intent: {
        info: "text-blue-900/70 hover:text-blue-900 hover:bg-blue-200",
        success: "text-green-900/70 hover:text-green-900 hover:bg-green-200",
        warning: "text-orange-900/70 hover:text-orange-900 hover:bg-orange-200",
        danger: "text-red-900/70 hover:text-red-900 hover:bg-red-200",
      },
    },
  })({
    intent: props.intent,
  });
});

const iconComponent = computed(() => {
  const icons = {
    success: CheckCircleIcon,
    warning: ExclamationTriangleIcon,
    danger: XCircleIcon,
    info: InformationCircleIcon,
  };

  return icons[props.intent];
});

function dismiss() {
  if (props.onDismiss) {
    props.onDismiss();
  }
}
</script>

<template>
  <div v-if="props.show" :class="containerClass">
    <div class="shrink-0">
      <component :is="iconComponent" :class="iconClass" />
    </div>
    <div class="flex-1 space-y-2">
      <h2 :class="titleClass">
        {{ props.title }}
      </h2>
      <div :class="contentClass"><slot /></div>
    </div>
    <div class="shrink-0" v-if="props.onDismiss">
      <button @click="dismiss()" :class="closeButtonClass">
        <XMarkIcon class="w-6 h-6" />
      </button>
    </div>
  </div>
</template>

On gists

Yellow Fade Technique with Modern CSS (@starting-style)

CSS CSS trick

fade.css #

/* https://www.bram.us/2023/05/24/the-yellow-fade-technique-with-modern-css-using-starting-style/ */

div {
  transition: background-color 0.5s;
  background-color: transparent;

  @starting-style {
    background-color: yellow;
  }
}