Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
tommy-mitchell committed Aug 18, 2024
0 parents commit 59aec43
Show file tree
Hide file tree
Showing 54 changed files with 11,184 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Allow personal config overrides
root = false

[*]
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8

[*.yml]
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
42 changes: 42 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# https://vitejs.dev/guide/static-deploy.html#github-pages
name: Deploy to GitHub Pages
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build:demo
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: "./demo/dist"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
15 changes: 15 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI Lint
on:
push:
branches: ["main"]
pull_request:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- run: pnpm install
- run: pnpm lint
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI Test
on:
push:
branches: ["main"]
pull_request:
jobs:
test:
name: Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18.19, 20.8, 21, 22]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: pnpm install
- run: pnpm exec tsimp --start # Preload the transpiler
- run: pnpm test
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
coverage
.tsimp
dist
package-lock.json
yarn.lock
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.15.0
21 changes: 21 additions & 0 deletions .xo-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { fileURLToPath } from "url";

export default {
extends: [
"@tommy-mitchell/xo",
"@tommy-mitchell/xo/react",
"@tommy-mitchell/xo/tailwind",
"@tommy-mitchell/xo/dprint",
],
settings: {
tailwindcss: {
config: fileURLToPath(new URL("demo/tailwind.config.ts", import.meta.url)),
},
},
rules: {
"@typescript-eslint/naming-convention": "off",
"simple-import-sort/imports": ["error", {
groups: [["^\\u0000", "^node:", "^react", "^react-dom", "^@?\\w", "^", "^\\.", "^.+\\.s?css$"]],
}],
},
};
17 changes: 17 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>use-boop-simple</title>
<script>
if (localStorage.theme === "dark" || (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark")
}
</script>
</head>
<body class="bg-background text-primary">
<div id="root"></div>
<script type="module" src="src/index.tsx"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"host": "vite --host",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@icons-pack/react-simple-icons": "10.0.0",
"@radix-ui/react-slider": "1.2.0",
"lucide-react": "0.428.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"tailwind-merge": "2.5.2",
"use-boop-simple": "workspace:*"
},
"devDependencies": {
"@mdx-js/rollup": "3.0.1",
"@tailwindcss/typography": "0.5.14",
"@types/mdx": "2.0.13",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react-swc": "3.7.0",
"autoprefixer": "10.4.20",
"tailwind-mode-aware-colors": "2.0.2",
"tailwindcss": "3.4.10",
"vite": "5.4.1"
}
}
32 changes: 32 additions & 0 deletions demo/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { SiGithub } from "@icons-pack/react-simple-icons";
import About from "../content/about.md";
import { BoopInput } from "./BoopInput.tsx";
import { IconButton } from "./IconButton.tsx";
import { Prose } from "./Prose.tsx";
import { ThemeToggle } from "./ThemeToggle.tsx";

export function App() {
return (
<div className="flex h-full flex-col gap-6 p-4 sm:mx-8 xl:py-8">
<header className="container flex flex-wrap items-center justify-between gap-4">
<Prose className="max-xs:prose-sm">
<h1>
<code>use-boop-simple</code>
</h1>
</Prose>
<nav className="flex flex-wrap gap-3">
<ThemeToggle />
<IconButton href="https://github.com/tommy-mitchell/use-boop-simple">
<SiGithub className="size-7" />
</IconButton>
</nav>
</header>
<main className="container flex grow flex-col">
<Prose className="prose-p:first-of-type:mt-0">
<About />
</Prose>
<BoopInput />
</main>
</div>
);
}
53 changes: 53 additions & 0 deletions demo/src/components/Boop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect } from "react";
import { twMerge } from "tailwind-merge";
import { useBoop, type UseBoopOptions } from "use-boop-simple";

export type BoopProps =
& Omit<React.ComponentProps<"button">, "onClick" | "type">
& Readonly<{
trigger?: boolean;
onClick?: () => void;
}>
& UseBoopOptions;

export function Boop({
className,
friction,
onClick,
rotate,
scale,
tension,
timing,
trigger,
x,
y,
...props
}: BoopProps) {
const boopOptions = { friction, rotate, scale, tension, timing, x, y };
const [style, boopTrigger] = useBoop(boopOptions);

useEffect(() => {
if (trigger) {
boopTrigger();
}
}, [boopTrigger, trigger]);

const triggers = props.disabled ? {} : {
onFocus: boopTrigger,
onPointerEnter: boopTrigger,
};

return (
<button
type="button"
className={twMerge("group z-50 block w-fit outline-offset-2 will-change-transform", className)}
style={style}
onClick={() => {
boopTrigger();
onClick?.();
}}
{...triggers}
{...props}
/>
);
}
83 changes: 83 additions & 0 deletions demo/src/components/BoopInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useState } from "react";
import type { UseBoopOptions } from "use-boop-simple";
import { Boop } from "./Boop.tsx";
import { NumberSlider } from "./NumberSlider.tsx";
import { ResetButton } from "./ResetButton.tsx";

/* eslint-disable perfectionist/sort-objects */
const INITIAL_BOOP_OPTIONS: Required<UseBoopOptions> = {
x: 15,
y: 0,
rotate: 30,
scale: 1,
tension: 500,
friction: 10,
timing: 150,
};
/* eslint-enable perfectionist/sort-objects */

const isInitial = (boopOptions: UseBoopOptions) => (
Object.entries(boopOptions).every(([key, value]) => INITIAL_BOOP_OPTIONS[key as keyof UseBoopOptions] === value)
);

export function BoopInput() {
const [boopOptions, setBoopOptions] = useState(INITIAL_BOOP_OPTIONS);
const [trigger, setTrigger] = useState(false);

const handleChange = (key: keyof UseBoopOptions) => (value: number) => {
setBoopOptions({ ...boopOptions, [key]: value });
};

const getProps = (key: keyof UseBoopOptions) => ({
label: key,
onChange: handleChange(key),
value: boopOptions[key],
});

return (
<div className="flex grow flex-col items-center gap-4 md:grid md:grid-cols-2 md:place-items-center md:gap-6">
<div
className={`
relative flex size-full flex-col gap-4 rounded-xl border-2 border-dashed p-4 border-accent-400
max-md:pt-2
md:justify-center
`}
>
<p className="left-4 top-2 text-tertiary md:absolute">
Interact to <em>boop!</em> Or click the button in the controls.
</p>
<Boop className="self-center" trigger={trigger} {...boopOptions}>
<span className="text-[48px]">👋</span>
</Boop>
</div>
<div className="grid size-full place-items-center rounded-xl border border-accent-400 p-4 md:px-8">
<form className="grid w-full grid-cols-2 gap-x-6 gap-y-4 text-secondary 2xl:grid-cols-1 lg:gap-6 max-xs:grid-cols-1">
<NumberSlider {...getProps("x")} min={-50} max={50} />
<NumberSlider {...getProps("y")} min={-50} max={50} />
<NumberSlider {...getProps("rotate")} max={360} />
<NumberSlider {...getProps("scale")} min={-5} max={5} step={.1} />
<NumberSlider {...getProps("tension")} step={10} min={1} max={1000} />
<NumberSlider {...getProps("friction")} min={1} max={100} />
<NumberSlider {...getProps("timing")} min={0} max={1000} step={50} />
<div className="flex items-center gap-2 self-end max-xs:-order-1">
<button
type="button"
className={`
grow rounded border border-accent-600 bg-primary px-2 transition-colors text-primary
active:!border-accent-700 active:!bg-primary-active
hocus:border-accent-500 hocus:bg-primary-hocus
`}
onClick={() => {
setTrigger(true);
setTimeout(() => setTrigger(false), 0);
}}
>
boop!
</button>
<ResetButton disabled={isInitial(boopOptions)} onReset={() => setBoopOptions(INITIAL_BOOP_OPTIONS)} />
</div>
</form>
</div>
</div>
);
}
34 changes: 34 additions & 0 deletions demo/src/components/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { twMerge } from "tailwind-merge";
import type { UseBoopOptions } from "use-boop-simple";
import { Boop, type BoopProps } from "./Boop.tsx";
import { VisuallyHidden } from "./VisuallyHidden.tsx";

type IconProps =
& Omit<BoopProps, keyof UseBoopOptions>
& Readonly<{
as: React.ComponentType<React.ComponentProps<"svg">>;
boop: UseBoopOptions;
className?: string;
label: string;
}>;

export function Icon({ as: IconComponent, boop, className, label, ...props }: IconProps) {
return (
<Boop {...boop} {...props}>
<IconComponent
aria-hidden="true"
className={twMerge(
`
h-auto w-16 transition-colors duration-100 text-accent-300
dark:text-accent-500 dark:group-disabled:!text-accent-700 dark:group-hocus:text-accent-400
group-disabled:!text-accent-200 group-disabled:cursor-not-allowed
group-hocus:text-accent-500
`,
className,
)}
focusable="false"
/>
<VisuallyHidden>{label}</VisuallyHidden>
</Boop>
);
}
Loading

0 comments on commit 59aec43

Please sign in to comment.