intro modal & textareas

This commit is contained in:
ethan merchant 2023-05-31 05:05:14 -04:00
parent cb1a8a1705
commit 889032a54d
7 changed files with 370 additions and 128 deletions

80
icons/intro.svg Normal file
View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 32 32.000001"
version="1.1"
id="svg8"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="intro.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#808080"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.2"
inkscape:cx="34.955357"
inkscape:cy="15.625"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1053"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#808080"
showguides="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1090.5197)">
<circle
style="opacity:1;fill:none;stroke:#ffffff;stroke-width:1.33746;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path8463"
cx="21.428558"
cy="1106.5197"
r="9.5965786" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.768293;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect8569"
width="1.7928504"
height="6.1344175"
x="20.532131"
y="1105.6642" />
<circle
style="opacity:1;fill:none;stroke:#ffffff;stroke-width:1.36366;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path8573"
cx="21.428558"
cy="1102.4698"
r="0.67988187" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

66
icons/start.svg Normal file
View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 32 32.000001"
version="1.1"
id="svg8"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="start.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#808080"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.2"
inkscape:cx="24.508929"
inkscape:cy="12.857143"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1053"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#808080"
showguides="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1090.5197)">
<path
id="rect314"
style="fill:#ffffff;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 8.4841806,1098.0389 16.9962374,8.4808 -16.9962374,8.4807 z"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -8,7 +8,7 @@
version="1.1"
id="svg8"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="tongue.svg"
sodipodi:docname="tung.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
@ -26,8 +26,8 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.2"
inkscape:cx="10.044643"
inkscape:cy="14.553571"
inkscape:cx="24.508929"
inkscape:cy="12.857143"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -3,12 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="interactive-widget=resizes-content, width=device-width, initial-scale=1.0, user-scalable=no">
<!-- <meta name="virtual-keyboard" content="resize-layout" /> future feature? -->
<meta name="google" content="notranslate" />
<link rel="apple-touch-icon" sizes="180x180" href="icons/tung.svg">
<link rel="icon" type="image/svg+xml" sizes="any" href="icons/tung.svg">
<link rel="manifest" href="manifest.webmanifest">
<meta name="description" content="Augment your keyboard with the symbols you need to type in your language.">
<meta name="description" content="Easily augment your system's keyboard">
<meta name="author" content="Ethan Merchant">
<title style="display: none;">tung-tap</title>
<link rel="stylesheet" href="style.css?v1">
@ -19,22 +20,27 @@
<body>
<!-- <div id="home" style="
<dialog id="intro" style="
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 10px 20px;
padding: 10px 32px;
padding-top: 20px;
background-color: #79394f;
background-color: rgb(var(--field));
border-width: 1px;
border-color: rgb(var(--txt));
border-radius: var(--key-radius);
font-size: 24px;
z-index: -1;
">
<div style="
<!-- <div style="
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
">
<img src="icons/tongue.png" style="
<img src="icons/tung.png" style="
width: 32px; height: 32px;
transform: translateY(-5px) rotate(-5deg);
">
@ -43,30 +49,54 @@
margin-right: -40px;
transform: translate(-32px, 7px) rotate(-45deg) scale(0.8);
">
<h1 style="font-weight: unset; font-size: 24px; line-height: 24px; margin: 0; color: #fff">
TongueTap<span style="font-size: 14px; letter-spacing: 1px;">.app</span>
</h1>
</div>
</div> -->
<h1 style="font-weight: unset; font-size: 32px; line-height: 32px; margin: 0; color: rgb(var(--txt));">
tungtap<span style="font-size: 14px; letter-spacing: 1px;">.app</span>
</h1>
<p style="
font-size: 14px;
font-size: 16px;
line-height: 24px;
font-style: italic;
margin: 0;
color: #fff;
color: rgb(var(--txt));
text-align: center;
">
Unicode key-swatch webapp for any cultures tongue!
easily augment your<br>
system's keyboard
</p>
</div> -->
<img onclick="intro.close()" class="icon-btn" src="/icons/start.svg" style="
outline: 2px solid white;
outline-offset: 6px;
border-radius: 18px;
margin: 16px;
mix-blend-mode: difference;
">
<div style="
position: fixed;
top: 100px;
right: 100px;
color: rgb(var(--txt));
">edit keys &nbsp; &nearrow;</div>
<div style="
position: fixed;
bottom: 150px;
color: rgb(var(--txt));
">&swarrow; &nbsp; tap keys &nbsp; &searrow;</div>
</dialog>
<div style="display: block; position: relative; background-color: white;">
<div hidden="true" id="tung" onclick="layout()" oninput="layout()" contenteditable="true" spellcheck="false" style="
<textarea hidden="true" id="tung" onclick="layout()" oninput="layout()" spellcheck="false" style="
user-select: text;
touch-action: none;
overflow: none;
white-space: pre;
width: auto;
width: 100%;
height: 100px;
padding: 10px;
font-family: 'DM Mono', monospace;
@ -79,8 +109,8 @@
/* display: none; */
"></div>
<div onclick="copyToClipboard('#tung')" style="
"></textarea>
<div onclick="copyToClipboard('tung')" style="
cursor: pointer;
user-select: none;
position: absolute;
@ -105,12 +135,16 @@
line-height: 40px;
">
<img onclick="$('#nav').toggle()" class="icon-btn" src="icons/tungs.svg">
<img onclick="TopToggle()" class="icon-btn" src="icons/tung.svg">
<div style="display: flex;">
<img onclick="intro.showModal()" class="icon-btn" src="icons/intro.svg">
<img onclick="TopToggle()" class="icon-btn" src="icons/tung.svg">
</div>
</div>
<div style="flex-grow: 1; overflow: hidden; position: relative; background-color: white;">
<div id="text" onclick="$('#text').focus()" contenteditable="true" spellcheck="false" style="
<!-- onclick="$('#text').focus()" -->
<textarea id="text" spellcheck="false" style="
user-select: text;
touch-action: pan-y;
overflow-y: scroll;
@ -127,11 +161,9 @@
background-color: rgb(var(--field));
color: rgb(var(--txt));
/* padding-bottom: 600px; */
"></div>
<div onclick="copyToClipboard('#text')" style="
"></textarea>
<div onclick="copyToClipboard('text')" style="
cursor: pointer;
user-select: none;
position: absolute;
@ -145,11 +177,11 @@
<div id="nav" hidden="true"></div>
<div id="keyboard" onclick="$('#text').focus()"></div>
<div id="keyboard" onclick="text.focus()"></div>
<!-- safari disclaimer (centered vertically) -->
<div id="disclaimer" hidden style="
<div id="disclaimer" style="
position: fixed;
top: 50%;
left: 50%;
@ -169,7 +201,7 @@
<br>
Please use <span style="font-weight: bold;">Chrome</span> or <span style="font-weight: bold;">Firefox</span>.
</p>
</div>
</body>
</html>

View file

@ -1,3 +1,6 @@
doc = document
elbyid = (id) => doc.getElementById(id)
var tung = ""
var topToggle = false
@ -18,11 +21,13 @@ $(document).ready(() => {
if (localStorage.getItem('tung') === null || localStorage.getItem('tung') == "") {
tung = "201C 2014 00D7 00B0 00B7 2022 221E 00B1 2023 201D"
localStorage.setItem('tung', tung)
intro.showModal()
}
else {
tung = localStorage.getItem('tung')
}
$('#tung').html(tung)
elbyid('tung').value = tung
layout()
// persistent text
@ -30,7 +35,7 @@ $(document).ready(() => {
localStorage.setItem('text', "")
}
else {
$('#text').html(localStorage.getItem('text'))
elbyid('text').value = localStorage.getItem('text')
}
@ -152,11 +157,9 @@ function nav() {
background-color: #ff79a1;
color: #79394f;
">
Find more character codes in
<a target="_blank" href="https://unicode.org/charts/">unicode.org</a>
reference charts
or sketching in <a target="_blank" href="https://shapecatcher.com/">shapecatcher.com</a>
drawbox search!
Find more character codes
in the <a target="_blank" href="https://unicode.org/charts/">unicode.org</a> reference charts
or by sketching in a drawbox search at <a target="_blank" href="https://shapecatcher.com/">shapecatcher.com</a>!
</div>`
$('#nav').html(links + back + html)
@ -175,7 +178,7 @@ var selected = null
function select(index, pick) {
let line = lines[index]
if (pick) {
$('#tung').html(line.split(':')[1].trim())
elbyid('tung').value = line.split(':')[1].trim()
layout()
selected = index
} else {
@ -204,7 +207,7 @@ function getLines(divId) {
function layout() {
// keymap (tung is the proprietary name)
let tungText = $('#tung').html() //.replace('\r\n', '\n')
let tungText = elbyid('tung').value //.replace('\r\n', '\n')
tungText = tungText.replace(/<div>/g, '\n').replace(/<\/div>/g, '')
if (tung != tungText) {
tung = tungText
@ -246,21 +249,21 @@ function layout() {
// override default behavior
key.preventDefault()
// if text is not focused then set the cursor to the end of $('#text')
let textEl = $('#text')
if (document.activeElement != textEl[0] && window.getSelection().rangeCount == 0) {
let selection = window.getSelection()
let range = document.createRange()
range.selectNodeContents(textEl[0])
range.collapse(false)
selection.removeAllRanges()
selection.addRange(range)
}
textEl.focus()
// if text is not focused then set the cursor to the end of #text
// let textEl = elbyid('text')
// if (document.activeElement != textEl && window.getSelection().rangeCount == 0) {
// let selection = window.getSelection()
// let range = document.createRange()
// range.selectNodeContents(textEl)
// range.collapse(false)
// selection.removeAllRanges()
// selection.addRange(range)
// }
// textEl.focus()
// insert key character into contenteditable div $('#text') at cursor position
// insert key character at cursor position in textarea
let char = String.fromCharCode("0x" + $(key.target).attr('charcode'))
insertTextAtCursor(char)
insertAtCursor(char)
@ -269,36 +272,31 @@ function layout() {
})
}
function insertTextAtCursor(text) {
let selection = window.getSelection()
let range = selection.getRangeAt(0)
range.deleteContents()
let node = document.createTextNode(text)
range.insertNode(node)
range.setStartAfter(node)
range.setEndAfter(node)
selection.removeAllRanges()
selection.addRange(range)
function insertAtCursor(text) {
const textarea = elbyid('text');
const scrollPos = textarea.scrollTop;
const caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
textarea.value = front + text + back;
textarea.selectionStart = caretPos + text.length;
textarea.selectionEnd = caretPos + text.length;
textarea.scrollTop = scrollPos;
textarea.focus();
}
function copyToClipboard(id) {
var textEl = $(id)
var textarea = elbyid(id)
// select all the text on the contenteditable div textEl to show it was copied
let selection = window.getSelection()
let range = document.createRange()
range.selectNodeContents(textEl[0])
selection.removeAllRanges()
selection.addRange(range)
// select all the text in the textarea to show it was copied
// textarea.focus()
textarea.select()
if (!navigator.clipboard) {
console.error('Clipboard API not supported');
return;
}
navigator.clipboard.writeText(textEl.html())
navigator.clipboard.writeText(textarea.value)
.then(() => {
console.log('Text copied to clipboard')
})
@ -358,8 +356,9 @@ function loop(timestamp) {
// check if need to backup the text
let text = $('#text').html()
let text = elbyid('text').value
if (text != lastText) {
console.log(text)
localStorage.setItem('text', text)
lastText = text
}

View file

@ -12,6 +12,8 @@
/* all: unset; */
outline: none;
/* box-sizing: border-box; */
font-family: 'Atkinson Hyperlegible', sans-serif;
text-rendering: optimizeLegibility;
}
@ -35,6 +37,8 @@ html {
box-sizing: border-box;
height: 100%;
height: 100dvh;
}
body {
@ -53,6 +57,8 @@ body {
overflow: hidden;
touch-action: none;
height: 100dvh;
}
/* hide scrollbar */
@ -118,8 +124,7 @@ a {
align-items: baseline;
}
/* textarea {
font-family: sans-serif;
textarea {
font-size: 16px;
line-height: 1.5;
padding: 1.5rem 1rem;
@ -134,11 +139,9 @@ a {
overflow: auto;
-webkit-overflow-scrolling: touch;
height: -webkit-fill-available;
background-color: rgb(var(--field));
color: rgb(var(--txt));
} */
}
#keyboard {
display: block;

View file

@ -3,56 +3,118 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
/* html, body {
position: relative;
width: 100%;
height: 2048px;
box-sizing: border-box;
margin: 0;
padding: 0;
background-color: #79394f;
color: #fff;
font-family: sans-serif;
} */
#layoutViewport {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
}
#bottombar {
position: fixed;
left: 0px;
right: 0px;
bottom: -20px;
height: 40px;
background-color: red;
transform-origin: left bottom;
transform: translate(0px, 0px) scale(1);
}
</style>
<title style="display: none;">test</title>
</head>
<body>
<textarea id="text" rows="10" style="width: 100%;" onclick="">test</textarea>
<div id="shelf"></div>
<textarea id="text" rows="10" style="width: 100%; box-sizing: border-box;" onclick="">test</textarea>
<div id="bottombar">This stays stuck to the visual viewport</div>
<div id="forcescrolling"></div>
<div id="layoutViewport"></div>
<script>
// setInterval(() => {
// let docHeight = document.documentElement.clientHeight
// // docHeight = window.innerHeight
// let kbHeight = docHeight - window.visualViewport.height
// // relative to ICB
// // window.visualViewport.pageTop
// // relative to LayoutViewport
// // window.visualViewport.offsetTop
// // kbHeight -= 1
// // document.getElementById('text').style.height = (window.visualViewport.height - kbHeight) + 'px'
// // document.getElementById('shelf').style.top = window.visualViewport.height + window.visualViewport.pageTop - 32 + 'px'
// // document.getElementById('shelf').style.top = window.visualViewport.pageTop + 'px'
// // document.getElementById('shelf').style.height = window.visualViewport.height + 'px'
// }, 60)
// window.visualViewport.addEventListener('resize', () => {
// document.getElementById('shelf').style.top = window.visualViewport.height - 32 + 'px'
// })
// // on scroll
// window.visualViewport.addEventListener('scroll', () => {
// })
// function selectText() {
// // flash the textarea to stop ios from scrolling
// let textarea = document.querySelector('textarea')
// textarea.style.display = 'none'
// setTimeout(() => {
// textarea.style.display = 'block'
// // select the text
// textarea.select()
// }, 10)
// }
const bottomBar = document.getElementById("bottombar");
const layoutViewport = document.getElementById("layoutViewport");
let pendingUpdate = false;
function viewportHandler(event) {
if (pendingUpdate) return;
pendingUpdate = true;
requestAnimationFrame(() => {
pendingUpdate = false;
// Since the bar is position: fixed we need to offset it by the
// visual viewport's offset from the layout viewport origin.
const viewport = event.target;
const offsetLeft = viewport.offsetLeft;
const offsetTop =
viewport.height -
layoutViewport.getBoundingClientRect().height +
viewport.offsetTop;
// You could also do this by setting style.left and style.top if you
// use width: 100% instead.
bottomBar.style.transform = `translate(${offsetLeft}px, ${offsetTop}px) scale(${
1 / viewport.scale
})`;
});
}
window.visualViewport.addEventListener('scroll', viewportHandler);
window.visualViewport.addEventListener('resize', viewportHandler);
</script>
</body>
</html>
<script>
setInterval(() => {
let kbHeight = window.innerHeight - window.visualViewport.height
kbHeight -= 1
document.getElementById('shelf').style.bottom = kbHeight + 'px'
// document.getElementById('text').style.height = (window.visualViewport.height - kbHeight) + 'px'
}, 10)
// function selectText() {
// // flash the textarea to stop ios from scrolling
// let textarea = document.querySelector('textarea')
// textarea.style.display = 'none'
// setTimeout(() => {
// textarea.style.display = 'block'
// // select the text
// textarea.select()
// }, 10)
// }
</script>
<style>
html, body {
overflow: hidden;
position: relative;
height: 100%;
box-sizing: border-box;
margin: 0;
padding: 0;
background-color: #79394f;
color: #fff;
font-family: sans-serif;
}
#shelf {
position: absolute;
width: 100%; height: 10px;
bottom: 0;
background-color: magenta;
}
</style>