Skip to content

Instantly share code, notes, and snippets.

@zachwill
Created July 24, 2025 05:27
Show Gist options
  • Select an option

  • Save zachwill/b7aa27d9c3d28e58ea2246a85eb067ac to your computer and use it in GitHub Desktop.

Select an option

Save zachwill/b7aa27d9c3d28e58ea2246a85eb067ac to your computer and use it in GitHub Desktop.
data-monkey
import { aliasify } from '../../engine/engine'
import { EventTypePatchElements } from '../../engine/consts'
import type { WatcherPlugin } from '../../engine/types'
import {
DATASTAR_FETCH_EVENT,
type DatastarFetchEvent,
} from '../backend/shared'
export const MonkeySelect: WatcherPlugin = {
type: 'watcher',
name: 'monkey-fragment-select',
onGlobalInit() {
// capture=true so we run *before* PatchElements’ listener
document.addEventListener(
DATASTAR_FETCH_EVENT,
(ev: CustomEvent<DatastarFetchEvent>) => {
const { detail } = ev
if (detail.type !== EventTypePatchElements) return
const el = detail.el
const select = el.getAttribute(aliasify('monkey-select'))
const elements = detail.argsRaw.elements
if (!select || !elements) return
const doc = new DOMParser().parseFromString(elements, 'text/html')
const parts = doc.querySelectorAll(select)
if (!parts.length) return // let Datastar complain later
const holder = document.createElement('div')
parts.forEach((n) => holder.appendChild(n.cloneNode(true)))
detail.argsRaw.elements = holder.innerHTML
},
true, // ← capture phase
)
},
}
import { aliasify } from '../../engine/engine'
import { kebab } from '../../utils/text'
import type {
AttributePlugin,
HTMLOrSVG,
} from '../../engine/types'
import {
DATASTAR_FETCH_EVENT,
type DatastarFetchEvent,
FINISHED,
} from '../backend/shared'
const attr = (el: Element, name: string) =>
el.getAttribute(aliasify(name)) ?? undefined
export const Monkey: AttributePlugin = {
type: 'attribute',
name: 'monkey',
keyReq: 'denied',
valReq: 'must',
returnsValue: true,
argNames: ['opts'],
onLoad: ({
el,
rx, // evaluates the attribute expression
startBatch,
endBatch,
}) => {
/* ---------------------------------------------------------
1. Build the shared opts object
--------------------------------------------------------- */
const select = attr(el, 'monkey-select')
const selector = attr(el, 'monkey-patch')
const mode = attr(el, 'monkey-swap')
const useTransition = attr(el, 'monkey-transition') != null
const headers: Record<string, string> = {
'HX-Request' : 'true',
'HX-Trigger' : el.id || kebab(el.tagName),
'HX-Target' : selector ?? '',
'HX-Current-URL' : window.location.href,
}
if (select) headers['HX-Select'] = select // not official, but handy
const opts: any = { headers }
// tell the server-side what client will do:
if (select) opts.select = select
if (selector) opts.selector = selector
if (mode) opts.mode = mode
if (useTransition) opts.useViewTransition = true
/* ---------------------------------------------------------
2. Wire triggering behaviour (click | hover | load …)
--------------------------------------------------------- */
const triggersRaw =
attr(el, 'monkey-trigger')?.replace(/,/g, ' ') ?? 'click'
const triggers = triggersRaw
.split(/\s+/)
.map((t) => t.trim())
.filter(Boolean)
const runExpression = () => {
startBatch()
rx(opts) // ⟹ @get('/page', opts)
endBatch()
}
// “load” fires immediately (after current micro-task)
if (triggers.includes('load')) queueMicrotask(runExpression)
const cleaners: Array<() => void> = []
for (const trig of triggers) {
if (trig === 'load') continue
const evtName = trig === 'hover' ? 'mouseover' : trig
const h = (e: Event) => {
e.preventDefault()
e.stopPropagation()
runExpression()
}
el.addEventListener(evtName, h)
cleaners.push(() => el.removeEventListener(evtName, h))
}
/* ---------------------------------------------------------
3. Handle history.pushState after patch is done
--------------------------------------------------------- */
const urlPushRaw = attr(el, 'monkey-url')
let targetUrl: string | null = null
if (urlPushRaw !== undefined) {
targetUrl = urlPushRaw === '' || urlPushRaw === 'true' ? null : urlPushRaw
// null ⇒ “use the same URL that was fetched”
const afterPatch: EventListener = (ev: CustomEvent<DatastarFetchEvent>) => {
if (ev.detail.type !== FINISHED || ev.detail.el !== el) return
history.pushState({}, '', targetUrl ?? (opts?.__lastFetched as string))
}
document.addEventListener(DATASTAR_FETCH_EVENT, afterPatch)
cleaners.push(() =>
document.removeEventListener(DATASTAR_FETCH_EVENT, afterPatch),
)
}
/* --------------------------------------------------------- */
return () => cleaners.forEach((fn) => fn())
},
}
<div id="bar">placeholder</div>
<button
data-monkey ="@get('/big-doc.html', opts)"
data-monkey-select ="#hi"
data-monkey-patch ="#bar"
data-monkey-swap ="inner"
data-monkey-trigger ="click hover"
data-monkey-url ="" <!-- push fetched URL -->
>
Load #hi into #bar
</button>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment