Skip to content

Instantly share code, notes, and snippets.

View tadeaspetak's full-sized avatar

Tadeáš Peták tadeaspetak

View GitHub Profile
@tadeaspetak
tadeaspetak / delete.sh
Created February 27, 2024 10:58
Bash: Delete files whose name follows a certain pattern
find ./ -name '*.gql.ts' -delete
@tadeaspetak
tadeaspetak / factory.ts
Created January 30, 2024 09:16
URL Factory
const urlFactory = {
verify: (token: string) => `/verify/${token}`,
deed: ({ orgId, deedId }: { deedId: number; orgId: number }) => `/deed/${orgId}/${deedId}`
}
// Turn a factory object (like the one above) into a mapped type.
type URLFactoryMapped<T extends Record<keyof T, (...args: any) => any>> =
{ [K in keyof T]: (...args: Parameters<T[K]>) => ReturnType<T[K]> }
const url = <T extends keyof typeof urlFactory>(key: T, ...args: Parameters<typeof urlFactory[T]>) => {
@tadeaspetak
tadeaspetak / postgres-dataset.sql
Last active October 31, 2023 21:14
postgres-dataset
-- utility function for returning a random string of the given length
CREATE OR REPLACE FUNCTION random_string (string_length integer DEFAULT 10)
RETURNS varchar AS $$
SELECT array_to_string(
ARRAY(
SELECT chr((65 + round(random() * 25))::integer)
FROM generate_series(1, string_length)
), '') $$
LANGUAGE SQL;
@tadeaspetak
tadeaspetak / extract-asana-tasks.ts
Created July 1, 2022 10:21
Extract asana tasks from PRs between two tags.
import { execSync } from "child_process";
import fetch from "node-fetch";
const token = "YOUR_ACCESS_TOKEN";
const project = "~/dev/deedmob/code/.git";
const prev = "release-20220613.17-48-26";
const next = "release-20220629.18-19-06";
const repo = "DeedMob/deedmob";
(async () => {
@tadeaspetak
tadeaspetak / kia-copy.ts
Created April 5, 2022 18:18
Copy files and update their timestamps so that KIA Ceed (2011) plays them in the correct order.
const fs = require("fs");
const path = require("path");
const isValidFile = (filename) => !filename.startsWith(".");
const updateLastModifiedAt = async (filename) => {
const time = new Date();
try {
fs.utimesSync(filename, time, time);
} catch (err) {
fs.closeSync(fs.openSync(filename, "w"));
// resolve focus on opening and closing
useEffect(() => {
if (open) {
previousFocus.current = (document.activeElement as HTMLElement) ?? null;
nextFocus(getFocusableElements(container.current));
} else {
previousFocus.current?.focus?.();
previousFocus.current = null;
}
}, [open, portal]);
export const nextFocusable = (elements: HTMLElement[], forward = true) => {
const currentIndex = elements.findIndex((e) => e === document.activeElement);
let nextIndex = 0;
if (currentIndex > -1) {
if (forward) {
nextIndex = currentIndex < elements.length - 1 ? currentIndex + 1 : 0;
} else {
nextIndex = currentIndex > 0 ? currentIndex - 1 : elements.length - 1;
}
export const getFocusableElements = (parent?: HTMLElement | null): HTMLElement[] => {
if (!parent) return [];
return Array.from(parent.querySelectorAll("a[href], button, input, textarea, select, details,[tabindex]"))
.filter(
(el) => el.getAttribute("tabindex") !== "-1" && !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden")
)
// sort tabindexes as follows: 1, 2, 3, 4, ..., 0, 0, 0
.sort((a, b) => {
const aIndex = Number(a.getAttribute("tabindex")) ?? 0; // no `tabindex` means `tabindex=0` on a focusable element
useEffect(() => {
document.getElementById("root")?.setAttribute("aria-hidden", open.toString());
portal.current?.setAttribute("aria-hidden", (!open).toString());
}, [open]);
export const usePortal = () => {
const portal = useRef(document.createElement("div"));
useEffect(() => {
const current = portal.current;
document.body.appendChild(portal.current);
return () => void document.body.removeChild(current); // let's avoid dangling `div`s
}, []);
return portal;