Skip to content

Instantly share code, notes, and snippets.

View SjoenH's full-sized avatar

Henry Skorpe Sjøen SjoenH

View GitHub Profile
import { useEffect, useState } from 'react';
/**
* `ScriptOnly` is the opposite of HTML's `<noscript>` tag.
* It only renders its children when JavaScript is enabled and running.
*
* Think of it as the cool cousin of `<noscript>` — the one who *does* show up when JS is in the house.
*
* ## Use case
* Use this when you want to display something *only* if JavaScript is working.
@SjoenH
SjoenH / miles_people_scrape.py
Created October 25, 2024 10:54
Simple Miles.no people scraper
import requests
from bs4 import BeautifulSoup
import json
url = "https://www.miles.no/vi-er-miles/ansatte"
response = requests.get(url)
if response.status_code == 200:
soup = BeautifulSoup(response.content, 'html.parser')
@SjoenH
SjoenH / remix-route-transformer.js
Last active August 19, 2024 15:57
Transform Remix v1 to v2 route-format
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const appDir = path.join(__dirname, "..", "app");
const routesDir = path.join(appDir, "routes");
const backupDir = path.join(appDir, "routes_backup");
@SjoenH
SjoenH / useLocalPropSync.ts
Created March 7, 2024 05:57
Vue composable for having a mutatable local state in a component that is replaced when the prop changes.
import { ref, UnwrapRef, watch } from "vue";
export function useLocalPropSync<T>(propValueGetter: () => T) {
const localValue = ref(propValueGetter());
watch(propValueGetter, (value) => {
localValue.value = value as UnwrapRef<T>;
});
return localValue;
@SjoenH
SjoenH / lru-cache.ts
Last active September 26, 2023 08:11
Simple lru cache compatible with Remix
import { LRUCache } from "lru-cache";
import type { XLedgerGraphQLTimesheetQueryResponse } from "~/services/getTimesheet.server";
/**
* LRU Cache for caching things like API responses.
* For example, we use this cache to prevent spamming Xledger with requests and to improve application performance.
*
* Note: This cache is not a replacement for a database.
* It is meant to be used for caching API responses.
*
<span className={`${open && "hidden"}`}>
<button
type="button"
className={`searchButton hideInPrint fixed bottom-12 right-12 flex rounded-full border bg-white bg-opacity-90 p-4 shadow-lg dark:bg-black `}
onClick={() => setOpen(true)}
>
<span className="sr-only">Open command palette</span>
<MagnifyingGlassIcon
className="h-6 w-6 text-gray-400 dark:text-white"
aria-hidden="true"
export function normalizeCamelCase(text?: string): string {
// If the input is not defined or is an empty string, return an empty string
if (!text || text.length === 0) return "";
// Replace capital letters with a space followed by the letter
//
// Todo: note We only want to split between lower and upper case, not between upper and upper
let spaced = text.replace(/([A-Z])/g, " $1");
// Convert the text to lowercase and trim any leading or trailing spaces
import { useRouter } from "next/router";
// Use your url-route as a key-value store
export function useRouterState(
key: string,
defaultValue: string
): [string, (value: string | string[]) => void] {
const router = useRouter();
const value: string = (router.query[key] as string) || defaultValue;
@SjoenH
SjoenH / text-inner-shadow.css
Last active November 5, 2024 18:55
Inner/inset shadow styling for text. Thanks to https://codepen.io/vitamink/pen/rNayMyd
.text-inner-shadow {
background-color: #565656;
color: transparent;
text-shadow: 2px 2px 3px rgba(255,255,255,0.5);
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
}
@SjoenH
SjoenH / Collapsible.tsx
Last active October 12, 2022 10:10
Pure css collapsible header and content
export function Collapsible(props: { title: string; children: ReactNode }) {
// This is a hack to control the visibility of some content using pure css
//
// The css needed:
// .toggle:checked ~ .control-me{
// display: block;
// visibility: visible;
// }
// .control-me {
// display: none;