Porterっぽい編集バー(改)をカスタマイズする
ソースを直接書き換えなくでもできる範囲でカスタマイズする
カスタマイズ前
https://gyazo.com/6756e6b26cb63ee35f11813ef9d7bda4
正確に言うと、Tボタンはカスタマイズ前にはない
スクショのためだけに外すのがめんどくさかったので許してMijinko_SD.icon
カスタマイズ後
https://gyazo.com/590b64346fdccb9db1f305c8092a216f
ボタンの配置を変えて、横幅いっぱいにまで表示されるようにした
その代わり幅によって自動的に2段にする機能が死んだ
元のCSSでは画面の横幅によって自動的に並び変えるようにしていたが、それを固定配置にした
あくまで自分のスマホ専用なので問題ない
やりたいこと
ボタンの並び順の変更
ボタンデザインの変更
時間に余裕がないので多分やらないかも
アイコンを挿入するボタンの追加
ソース
script.ts
任意のボタンを追加するサンプル
code:script.ts
const cursor = takeCursor()
const items: Item[] = [
{
type: "text",
name: "timestamp",
text: "T",
onClick: ()=>insertTimestamp()
},{
type: "icon",
name: "icon",
onClick: ()=>{
insertIcon()
cursor.focus()
}
}
]
if (/mobile/i.test(navigator.userAgent)) {
for(let item of items){
useEditBar().render(item)
}
}
style.css
code:style.css
div.edit-bar {
/* 編集バー */
grid-template-areas:
"c-left c-right copy cut timestamp icon"
"c-up c-down paste undo redo i-cursor";
grid-template-columns: repeat(6, 1fr); /* 横に6個、均等に配置する */
}
div.eb-caret-left {
/* 左矢印 */
grid-area: c-left;
}
div.eb-caret-right {
/* 右矢印 */
grid-area: c-right;
}
div.eb-caret-up {
/* 上矢印 */
grid-area: c-up;
}
div.eb-caret-down {
/* 下矢印 */
grid-area: c-down;
}
div.eb-copy {
/* コピー */
grid-area: copy;
}
div.eb-cut {
/* 切り取り */
grid-area: cut;
}
div.eb-clipboard {
/* 貼り付け */
grid-area: paste;
}
div.eb-undo {
/* 元に戻す */
grid-area: undo;
}
div.eb-redo {
/* やり直し */
grid-area: redo;
}
div.eb-i-cursor {
/* 文字カーソル(キャレット) */
grid-area: i-cursor;
}
div.eb-timestamp {
/* タイムスタンプ */
grid-area: timestamp;
}
div.eb-icon {
/* アイコン挿入 */
grid-area: icon;
}
コンパイル済みのソース
code:script.js
var j=()=>{let e=document.createElement("style");e.textContent=".bottom-bar{position:fixed;display:flex;flex-direction:column;align-items:flex-end;z-index:300;bottom:0;width:100%}div.status-bar{position:relative}.edit-bar{position:relative;border-top:var(--edit-bar-border);display:grid;grid-auto-flow:row;grid-template-columns:repeat(auto-fill,48px);grid-template-rows:auto;width:100%;overflow-x:auto;overflow-y:hidden;background-color:#0000001a;--edit-bar-border: 1px solid var(--tool-light-color, #a9aaaf)}.edit-bar>div{border:var(--edit-bar-border);background-color:var(--body-bg, #dcdde0);color:var(--tool-text-color, #666874);font-size:16px;height:34px;line-height:28px;text-align:center;vertical-align:middle;cursor:pointer}.edit-bar>div:first-child{border-top-left-radius:unset}.edit-bar>div:last-child{border-top-right-radius:unset}",document.head.append(e);let t=document.createElement("div");t.classList.add("edit-bar");let n=document.createElement("div");n.classList.add("bottom-bar");let o=document.querySelector(".status-bar");return document.querySelector(".app").append(n),n.append(o),n.append(t),t},V=j(),g=()=>{let e=document.createElement("div");V.append(e);let t;return{render:n=>{e.textContent="",t&&e.removeEventListener("click",t),t=n.onClick,e.classList.add("eb-"+n.name);let o=W(n);o&&(t&&e.addEventListener("click",t),e.append(o))},dispose:()=>e.remove()}},W=e=>{switch(e.type){case"icon":return G(...e.iconClass);case"text":return q(e.text)}},q=e=>{let t=document.createElement("span");return t.classList.add("item"),t.append(e),t},G=(...e)=>{let t=document.createElement("span");return e.join("")!=""&&t.classList.add(...e),t};var l=e=>e==null,f=e=>typeof e=="string",p=e=>typeof e=="number";var k=(e,t)=>{if(!Array.isArray(e))throw new TypeError("${t}" must be an array but actual is "${e}")};var H=(e,t)=>{if(!(e instanceof HTMLTextAreaElement))throw new TypeError("${t}" must be HTMLTextAreaElement but actual is "${e}")};var s=()=>{let e=document.getElementById("text-input");if(!!e)return H(e,"textarea#text-input"),e};function J(e){if(l(e))return;if(p(e))return x(e)?.id;if(f(e))return e.startsWith("L")?e.slice(1):e;if(e.classList.contains("line"))return e.id.slice(1);let t=e.closest(".line");if(t)return t.id.slice(1)}function x(e){if(l(e))return;if(p(e))return T()e;let t=J(e);return t?T().find(n=>n.id===t):void 0}function z(e){return e instanceof HTMLDivElement&&e.classList.contains("line")}function T(){return k(scrapbox.Page.lines,"scrapbox.Page.lines"),scrapbox.Page.lines}function h(e){if(l(e))return;if(p(e)||f(e))return x(e)?.text;if(!(e instanceof HTMLElement))return;if(z(e))return x(e)?.text;if(e.classList.contains("char-index"))return e.textContent??void 0;if(e.classList.contains("line")||e.getElementsByClassName("lines")?.0)return T().map(({text:o})=>o).join(` );let t=[],n=x(e);if(!l(n)){for(let o of Q(e))t.push(ee(o));return n.text.slice(Math.min(...t),Math.max(...t)+1)}}function*Q(e){let t=e.getElementsByClassName("char-index");for(let n=0;n<t.length;n++)yield t[0]}function Z(e){return e instanceof HTMLSpanElement&&e.classList.contains("char-index")}function ee(e){if(!Z(e))throw Error("A char DOM is required.");let t=e.className.match(/c-(\d+)/)?.[1];if(l(t))throw Error('.char-index must have ".c-{\\d}"');return parseInt(t)}function r(e,t){let{noModifiedKeys:n=!1,...o}=t??{},m={bubbles:!0,cancelable:!0,keyCode:te[e],...n?{}:{...o}},a=s();if(!a)throw Error("#text-input must exist.");a.dispatchEvent(new KeyboardEvent("keydown",m)),a.dispatchEvent(new KeyboardEvent("keyup",m))}var te={Backspace:8,Tab:9,Enter:13,Delete:46,Escape:27," ":32,PageUp:33,PageDown:34,End:35,Home:36,ArrowLeft:37,ArrowUp:38,ArrowRight:39,ArrowDown:40,a:65,A:65,b:66,B:66,c:67,C:67,d:68,D:68,e:69,E:69,f:70,F:70,g:71,G:71,h:72,H:72,i:73,I:73,j:74,J:74,k:75,K:75,l:76,L:76,m:77,M:77,n:78,N:78,o:79,O:79,p:80,P:80,q:81,Q:81,r:82,R:82,s:83,S:83,t:84,T:84,u:85,U:85,v:86,V:86,w:87,W:87,x:88,X:88,y:89,Y:89,z:90,Z:90,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,F1:113,F2:114,F3:115,F4:116,F5:117,F6:118,F7:119,F8:120,F9:121,F10:122,F11:123,F12:124,":":186,"*":186,";":187,"+":187,"-":189,"=":189,".":190,">":190,"/":191,"?":191,"@":192,"":192,"":219,"{":219,"\\":220,"|":220,"":221,"}":221,"^":222,"~":222,_:226};var L=e=>new Promise(t=>setTimeout(()=>t(),e));function u(){let e=s();if(!e)throw Error("#text-input is not found.");let t=Object.keys(e).find(n=>n.startsWith("__reactFiber"));if(!t)throw Error('div.cursor must has the property whose name starts with "__reactFiber"');return et.return.return.stateNode.props}function*i(e,t){for(let n=e;n<t;n++)yield n}function C(e=1){for(let t of i(0,e))r("z",{ctrlKey:!0})}function v(e=1){for(let t of i(0,e))r("z",{shiftKey:!0,ctrlKey:!0})}function D(e=1){for(let t of i(0,e))r("ArrowRight",{ctrlKey:!0})}function A(e=1){for(let t of i(0,e))r("ArrowLeft",{ctrlKey:!0})}function B(e=1){for(let t of i(0,e))r("ArrowUp",{ctrlKey:!0})}function I(e=1){for(let t of i(0,e))r("ArrowDown",{ctrlKey:!0})}function K(e=1){for(let t of i(0,e))r("ArrowRight",{altKey:!0})}function N(e=1){for(let t of i(0,e))r("ArrowLeft",{altKey:!0})}function S(e=1){for(let t of i(0,e))r("ArrowUp",{altKey:!0})}function O(e=1){for(let t of i(0,e))r("ArrowDown",{altKey:!0})}async function _(e){let t=s();if(!t)throw Error("#text-input is not ditected.");t.focus(),t.value=e;let n=new InputEvent("input",{bubbles:!0});t.dispatchEvent(n),await L(1)}var E=()=>{let e=s();if(!e)throw Error("#text-input is not found.");let t=Object.keys(e).find(n=>n.startsWith("__reactFiber"));if(!t)throw Error('#text-input must has the property whose name starts with "__reactFiber"');return et.return.return.stateNode._stores};var R=()=>{for(let e of E())if("goByAction"in e)return e;throw Error('#text-input must has a "Cursor" store.')};var P=()=>{for(let e of E())if("hasSelection"in e)return e;throw Error('#text-input must has a "Selection" store.')};function re(e){switch(e){case"spinner":return"fa","fa-spinner";case"check-circle":return"kamon","kamon-check-circle";case"exclamation-triangle":case"caret-up":case"caret-down":case"caret-left":case"caret-right":case"cut":case"expand":case"i-cursor":case"undo":case"redo":return["fas",fa-${e}];case"copy":case"clipboard":return["far",fa-${e}];default:return""}}var w=P(),c=R(),oe=[{name:"caret-left",onClick:()=>{c.focus(),u().selectedText===""?N():A()}},{name:"caret-right",onClick:()=>{c.focus(),u().selectedText===""?K():D()}},{name:"caret-up",onClick:()=>{c.focus(),u().selectedText===""?S():B()}},{name:"caret-down",onClick:()=>{c.focus(),u().selectedText===""?O():I()}},{name:"copy",onClick:async()=>{try{let{position:e,selectedText:t}=u(),n=t||h(e.line);if(!n)return;await navigator.clipboard.writeText(n)}catch(e){console.error(e),alert(`Faild to copy: ${JSON.stringify(e)})}}},{name:"cut",onClick:async()=>{try{let e=w.hasSelection(),t=w.getRange().start.line,n=e?w.getSelectedText():h(t);if(!n)return;await navigator.clipboard.writeText(n),e||w.setRange({start:{line:t,char:0},end:{line:t,char:n.length}}),c.focus(),r("Delete")}catch(e){console.error(e),alert(Faild to cut:
${JSON.stringify(e)})}}},{name:"clipboard",onClick:async()=>{try{let e=await navigator.clipboard.readText();if(!e)return;c.focus(),await _(e)}catch(e){console.error(e),alert(Faild to paste:
${JSON.stringify(e)})}}},{name:"undo",onClick:()=>C()},{name:"redo",onClick:()=>v()},{name:"i-cursor",onClick:()=>{c.getVisible()&&c.hide()}}];if(/mobile/i.test(navigator.userAgent))for(let{name:e,onClick:t}of oe){let{render:n}=g();n({type:"icon",name:e,iconClass:re(e),onClick:t})}var F=(e,t)=>{if(!(e instanceof HTMLTextAreaElement))throw new TypeError("${t}" must be HTMLTextAreaElement but actual is "${e}")};var d=()=>{let e=document.getElementById("text-input");if(!!e)return F(e,"textarea#text-input"),e};function b(e,t){let{noModifiedKeys:n=!1,...o}=t??{},m={bubbles:!0,cancelable:!0,keyCode:se[e],...n?{}:{...o}},a=d();if(!a)throw Error("#text-input must exist.");a.dispatchEvent(new KeyboardEvent("keydown",m)),a.dispatchEvent(new KeyboardEvent("keyup",m))}var se={Backspace:8,Tab:9,Enter:13,Delete:46,Escape:27," ":32,PageUp:33,PageDown:34,End:35,Home:36,ArrowLeft:37,ArrowUp:38,ArrowRight:39,ArrowDown:40,a:65,A:65,b:66,B:66,c:67,C:67,d:68,D:68,e:69,E:69,f:70,F:70,g:71,G:71,h:72,H:72,i:73,I:73,j:74,J:74,k:75,K:75,l:76,L:76,m:77,M:77,n:78,N:78,o:79,O:79,p:80,P:80,q:81,Q:81,r:82,R:82,s:83,S:83,t:84,T:84,u:85,U:85,v:86,V:86,w:87,W:87,x:88,X:88,y:89,Y:89,z:90,Z:90,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,F1:113,F2:114,F3:115,F4:116,F5:117,F6:118,F7:119,F8:120,F9:121,F10:122,F11:123,F12:124,":":186,"*":186,";":187,"+":187,"-":189,"=":189,".":190,">":190,"/":191,"?":191,"@":192,"":192,"":219,"{":219,"\\":220,"|":220,"":221,"}":221,"^":222,"~":222,_:226};function*y(e,t){for(let n=e;n<t;n++)yield n}function $(e=1){for(let t of y(0,e))b("i",{ctrlKey:!0})}function Y(e=1){for(let t of y(0,e))b("t",{altKey:!0})}var M=()=>{let e=d();if(!e)throw Error("#text-input is not found.");let t=Object.keys(e).find(n=>n.startsWith("__reactFiber"));if(!t)throw Error('#text-input must has the property whose name starts with "__reactFiber"');return et.return.return.stateNode._stores};var X=()=>{for(let e of M())if("goByAction"in e)return e;throw Error('#text-input must has a "Cursor" store.')};var ue=X(),le=[{type:"text",name:"timestamp",text:"T",onClick:()=>Y()},{type:"icon",name:"icon",iconClass:"far","fa-smile",onClick:()=>{$(),ue.focus()}}];if(/mobile/i.test(navigator.userAgent))for(let e of le)g().render(e);