feat(runtime): support slhx drag drops

req: htmx_equivalents/002

req: dx/008

req: list/003

req: examples/001
This commit is contained in:
slhx agent
2026-05-11 07:59:32 +02:00
parent ad8806c187
commit 816552e735
6 changed files with 39 additions and 38 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ This is a polished Linear-style product demo for planning typed work across lane
- root-scoped runtime lowering (`data-hid`, `data-sid`)
- page-enhancer navigation with native link fallback
- SSE server push into a generated slot
- drag-and-drop lane moves persisted by typed server handlers
- drag-and-drop lane moves persisted by typed server handlers through the slhx runtime
Verification:
+5 -32
View File
@@ -367,44 +367,17 @@ fn shell(body: String) -> String {
<title>slhx Techdemo</title>
<script src="/slhx.js" defer></script>
<script>
function slhxTechdemoEffect(root, data) {{
function slhxTechdemoLaunch(button) {{
const form = button.closest('form');
const data = new URLSearchParams(new FormData(form));
data.set('__h', button.getAttribute('data-hid'));
fetch('/', {{
method: 'POST',
headers: {{ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Accept': 'application/slhx' }},
body: data
}})
.then((response) => response.arrayBuffer())
.then((buffer) => window.slhx.applyBatch(buffer, root));
}}
function slhxTechdemoLaunch(button) {{
const form = button.closest('form');
const data = new URLSearchParams(new FormData(form));
data.set('__h', button.getAttribute('data-hid'));
slhxTechdemoEffect(button.closest('[data-slhx-root]'), data);
return false;
}}
function slhxTechdemoDrag(event) {{
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', event.currentTarget.getAttribute('data-key'));
}}
function slhxTechdemoDragOver(event) {{
event.preventDefault();
event.currentTarget.classList.add('drop-ready');
}}
function slhxTechdemoDragLeave(event) {{
event.currentTarget.classList.remove('drop-ready');
}}
function slhxTechdemoDrop(event) {{
event.preventDefault();
const lane = event.currentTarget;
lane.classList.remove('drop-ready');
const id = event.dataTransfer.getData('text/plain');
if (!id) return false;
const data = new URLSearchParams();
data.set('__h', lane.getAttribute('data-move-hid'));
data.set('work_id', id);
data.set('lane', lane.getAttribute('data-lane'));
slhxTechdemoEffect(lane.closest('[data-slhx-root]'), data);
.then((buffer) => window.slhx.applyBatch(buffer, button.closest('[data-slhx-root]')));
return false;
}}
</script>
@@ -1,4 +1,4 @@
<article +class="self.class" +data-key="self.id" draggable="true" ondragstart="slhxTechdemoDrag(event)">
<article +class="self.class" +data-key="self.id" draggable="true">
<header><strong>{+ self.title +}</strong><span class="pill">{+ self.stage +}</span></header>
<div class="impact"><i +style="self.impact_style"></i></div>
<div class="card-actions">
@@ -1,4 +1,4 @@
<section +class="self.class" +data-lane="self.lane_id" +data-move-hid="self.move_handle" ondragover="slhxTechdemoDragOver(event)" ondragleave="slhxTechdemoDragLeave(event)" ondrop="slhxTechdemoDrop(event)">
<section +class="self.class" +data-lane="self.lane_id" +data-hid="self.move_handle" data-slhx-on="drop">
<h3>{+ self.title +}</h3>
<p>{+ self.description +}</p>
<template h-for="card in &self.cards">