← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
app/ui/busy-demo.tsx
62 lines · 1.5 KB · TypeScript/** @jsxRuntime automatic */
/** @jsxImportSource remix/ui */
import { clientEntry, on, type Handle } from 'remix/ui'
import { createBusyGate } from './busy.ts'
/**
* Home showcase: fake async job with spin-delay busy gate.
* Immediate press affordance (pending); "Loading…" only after ~200ms;
* once shown, stays ≥400ms.
*/
export const BusyDemo = clientEntry(
'kody:app#BusyDemo',
function BusyDemo(handle: Handle<{ label?: string }>) {
const gate = createBusyGate({ delayMs: 200, minDurationMs: 400 })
let doneFlash = false
let flashTimer: ReturnType<typeof setTimeout> | null = null
async function runJob() {
if (gate.isActive) return
doneFlash = false
if (flashTimer) {
clearTimeout(flashTimer)
flashTimer = null
}
gate.start(() => handle.update())
handle.update()
try {
await new Promise((r) => setTimeout(r, 800))
} finally {
await gate.stop(() => handle.update())
doneFlash = true
handle.update()
flashTimer = setTimeout(() => {
doneFlash = false
handle.update()
}, 1200)
}
}
return () => {
const active = gate.isActive
const label = gate.showBusy
? 'Loading…'
: doneFlash
? 'Done'
: handle.props.label || 'Run demo job'
return (
<button
type="button"
class="pak-btn pak-btn-accent"
data-pending={active ? 'true' : undefined}
aria-busy={active ? 'true' : undefined}
disabled={active}
mix={on('click', () => void runJob())}
>
{label}
</button>
)
}
},
)