From 9daca9e3fbcb011153f71c96e2d35f910f50971e Mon Sep 17 00:00:00 2001 From: Mikkeli Date: Sat, 12 Sep 2026 20:35:12 +0900 Subject: [PATCH] 274-transect: copy object name on click; stop clobbering text selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Row clicks called draw(), which rebuilt the whole tbody — so any attempt to drag-select text in the roster was wiped on mousedown. Selection was never blocked, just destroyed. Row clicks now call select(), which swaps the highlight class and repaints the SVG marks in place. Object names sit in a bare button with a dotted underline and cursor:copy; clicking copies the name and flashes "copied" beside it. Propagation is stopped so copying and row-selecting stay independent. Clipboard uses navigator.clipboard with a hidden-textarea fallback, since the vendored copy opens over file:// where the async API is often absent. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01S6MpcBisZYhX45oDGRRJoz --- wip/274-transect.html | 70 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/wip/274-transect.html b/wip/274-transect.html index 95a4510..fd4d867 100644 --- a/wip/274-transect.html +++ b/wip/274-transect.html @@ -83,6 +83,15 @@ footer{margin-top:38px;padding-top:16px;border-top:1px solid var(--rule); } .frow.oaza .chip{font-size:11px} +button.copy{font:inherit;color:inherit;background:none;border:0;padding:0;margin:0; + cursor:copy;text-align:left;border-bottom:1px dotted var(--rule)} +button.copy:hover{border-bottom-color:var(--ink-3)} +button.copy:focus-visible{outline:2px solid var(--ochre);outline-offset:2px} +button.copy.copied{background:color-mix(in srgb,var(--ochre) 22%,transparent); + border-bottom-color:transparent} +button.copy.copied::after{content:' copied';font-size:10px;letter-spacing:.08em; + color:var(--ink-3);text-transform:uppercase} +.tblwrap{user-select:text} .chip.quick{font-size:10px;padding:3px 8px;letter-spacing:.08em;text-transform:uppercase; color:var(--ink-3);border-style:dashed;margin-right:2px} .chip.quick:hover{color:var(--ink);border-style:solid} @@ -126,8 +135,8 @@ north to south, so the road reads nearly straight apart from the swing south thr

Roster

- - + +
kmNameObjectKanaRomajiKind大字BuiltGrade
kmObjectKanaKindOffset (m)Address
@@ -207,6 +216,15 @@ document.getElementById('legend').innerHTML= .map(function(g){return ''+g[1]+''}).join(''); var NS='http://www.w3.org/2000/svg'; +var rowOf=null, markOf=new Map(); +function paintMarks(){ + markOf.forEach(function(m,k){ + var on=(k===sel); + if(m.c){m.c.setAttribute('r',on?6:m.r0);} + if(m.t){m.t.setAttribute('font-weight',on?'700':'400');} + if(m.g){m.g.setAttribute('r',on?6:3);m.g.setAttribute('opacity',on?1:.85);} + }); +} function el(n,a){var e=document.createElementNS(NS,n);for(var k in a)e.setAttribute(k,a[k]);return e} function draw(){ var W=Math.max(2600,(K1-K0)*36),H=450,AX=272,PADL=54,PADR=54; @@ -227,6 +245,7 @@ function draw(){ t.textContent=k;s.appendChild(t); } s.appendChild(el('line',{x1:PADL,y1:AX,x2:W-PADR,y2:AX,stroke:'var(--road)','stroke-width':2.5})); + markOf=new Map(); var vis=items.filter(function(o){return onK[o.kind]&&onO[o.oaza]}); document.getElementById('count').innerHTML='showing '+vis.length+' of '+items.length+' entries'; var lu=[],ld=[]; @@ -242,7 +261,8 @@ function draw(){ tx.textContent=o.obj; if(sel===o)tx.setAttribute('font-weight','700'); tx.style.cursor='pointer';r.style.cursor='pointer'; - var pick=function(){sel=o;draw()};tx.onclick=pick;r.onclick=pick; + var pick=function(){select(o)};tx.onclick=pick;r.onclick=pick; + var e0=markOf.get(o)||{};e0.c=r;e0.t=tx;e0.r0=(up?4:3);markOf.set(o,e0); s.appendChild(r);s.appendChild(tx); var km=el('text',{x:px,y:up?y-13:y+16,fill:'var(--ink-3)','font-size':9.5,'text-anchor':'middle'}); km.textContent=o.km.toFixed(2)+(o.name&&o.name!==o.obj?' \u00b7 '+o.name:''); @@ -271,23 +291,51 @@ function draw(){ vis.forEach(function(o){ var cir=el('circle',{cx:gx(o.lon),cy:gy(o.lat),r:(sel===o?6:3),fill:FAM[o._f], opacity:(sel===o?1:.85),class:'mark'}); - cir.style.cursor='pointer';cir.onclick=function(){sel=o;draw()}; + cir.style.cursor='pointer';cir.onclick=function(){select(o)}; + var e1=markOf.get(o)||{};e1.g=cir;markOf.set(o,e1); var t=el('title');t.textContent=o.obj+' \u00b7 '+o.km.toFixed(2)+' km \u00b7 '+o.oaza;cir.appendChild(t); g.appendChild(cir); }); - var tb=document.getElementById('tbody');tb.innerHTML=''; + var tb=document.getElementById('tbody');tb.innerHTML='';rowOf=new Map(); vis.forEach(function(o){ var tr=document.createElement('tr'); if(sel===o)tr.className='hit'; - tr.innerHTML=''+o.km.toFixed(2)+''+o.name+''+ - ''+o.obj+''+(o.kana||'')+''+ - ''+o.kind+''+o.oaza+''+ - ''+(o.y||'')+''+(o.g||'')+''; - tr.style.cursor='pointer';tr.onclick=function(){sel=o;draw()}; - tb.appendChild(tr); + tr.innerHTML=''+o.km.toFixed(2)+''+ + ''+ + ''+(o.kana||'')+''+ + ''+o.kind+''+ + ''+Math.round(o.off)+''+ + ''+(o.addr||'')+''; + tr.querySelector('.copy').onclick=function(e){e.stopPropagation();copyName(o,this)}; + tr.onclick=function(){select(o)}; + rowOf.set(o,tr);tb.appendChild(tr); }); } + +function select(o){ + sel=o; + if(rowOf)rowOf.forEach(function(tr,k){tr.className=(k===o?'hit':'')}); + paintMarks(); +} + +function copyName(o,btn){ + var t=o.obj, done=function(){ + btn.classList.add('copied'); + setTimeout(function(){btn.classList.remove('copied')},900); + }; + if(navigator.clipboard&&navigator.clipboard.writeText){ + navigator.clipboard.writeText(t).then(done,function(){fallback(t,done)}); + } else fallback(t,done); +} +function fallback(t,done){ + var ta=document.createElement('textarea'); + ta.value=t;ta.setAttribute('readonly',''); + ta.style.position='fixed';ta.style.left='-9999px'; + document.body.appendChild(ta);ta.select(); + try{document.execCommand('copy');done()}catch(e){} + document.body.removeChild(ta); +} draw(); })(); \ No newline at end of file