Created
July 24, 2025 05:27
-
-
Save zachwill/b7aa27d9c3d28e58ea2246a85eb067ac to your computer and use it in GitHub Desktop.
data-monkey
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| ) | |
| }, | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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()) | |
| }, | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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