diff options
author | Arno Richter <oelna@oelna.de> | 2022-12-21 15:05:28 +0100 |
---|---|---|
committer | Arno Richter <oelna@oelna.de> | 2022-12-21 15:05:28 +0100 |
commit | 482fd7adee5e9e0990bf5904ed7d754d315de649 (patch) | |
tree | 7523a6f3482b6cb024624310f21fa7eeb05e9866 /js | |
parent | 057cace8b32e6c3d105695b517eae262071601f4 (diff) | |
download | microblog-482fd7adee5e9e0990bf5904ed7d754d315de649.tar.gz microblog-482fd7adee5e9e0990bf5904ed7d754d315de649.tar.bz2 microblog-482fd7adee5e9e0990bf5904ed7d754d315de649.zip |
first attempt at image attachments!
Diffstat (limited to 'js')
-rw-r--r-- | js/microblog.js | 193 | ||||
-rw-r--r-- | js/squircle.js | 146 |
2 files changed, 339 insertions, 0 deletions
diff --git a/js/microblog.js b/js/microblog.js new file mode 100644 index 0000000..f7963e3 --- /dev/null +++ b/js/microblog.js @@ -0,0 +1,193 @@ +'use strict'; + +document.documentElement.classList.remove('no-js'); + +const textarea = document.querySelector('textarea[name="content"]'); +const charCount = document.querySelector('#count'); + +if (textarea) { + const maxCount = parseInt(textarea.getAttribute('maxlength')); + + if (textarea.value.length > 0) { + const textLength = [...textarea.value].length; + charCount.textContent = maxCount - textLength; + } else { + charCount.textContent = maxCount; + } + + textarea.addEventListener('input', function () { + const textLength = [...this.value].length; + + charCount.textContent = maxCount - textLength; + }, false); +} + +const postForm = document.querySelector('#post-new-form'); +let useDragDrop = (!!window.FileReader && 'draggable' in document.createElement('div')); +useDragDrop = true; +if (postForm) { + const droparea = postForm.querySelector('#post-droparea'); + const attachmentsInput = postForm.querySelector('#post-attachments'); + const data = { + 'attachments': [] + }; + + + if (droparea && attachmentsInput) { + if (useDragDrop) { + console.log('init with modern file attachments'); + + const list = postForm.querySelector('#post-attachments-list'); + list.addEventListener('click', function (e) { + e.preventDefault(); + + // remove attachment + if (e.target.nodeName.toLowerCase() == 'li') { + const filename = e.target.textContent; + + data.attachments = data.attachments.filter(function (ele) { + return ele.name !== filename; + }); + + e.target.remove(); + } + }); + + droparea.classList.remove('hidden'); + document.querySelector('#post-attachments-label').classList.add('hidden'); + + droparea.ondragover = droparea.ondragenter = function (e) { + e.stopPropagation(); + e.preventDefault(); + + e.dataTransfer.dropEffect = 'copy'; + e.target.classList.add('drag'); + }; + + droparea.ondragleave = function (e) { + e.target.classList.remove('drag'); + }; + + droparea.onclick = function (e) { + e.preventDefault(); + + // make a virtual file upload + const input = document.createElement('input'); + input.type = 'file'; + input.setAttribute('multiple', ''); + input.setAttribute('accept', 'image/*'); // only images for now + + input.onchange = e => { + processSelectedFiles(e.target.files); + } + + input.click(); + }; + + function processSelectedFiles(files) { + if (!files || files.length < 1) return; + + for (const file of files) { + const found = data.attachments.find(ele => ele.name === file.name); + if(found) continue; // skip existing attachments + + data.attachments.push({ + 'name': file.name, + // todo: maybe some better form of dupe detection here? + 'file': file + }); + + const li = document.createElement('li'); + li.textContent = file.name; + + const reader = new FileReader(); + + if(file.type.startsWith('image/')) { + reader.onload = function (e) { + var dataURL = e.target.result; + + const preview = document.createElement('img'); + preview.classList.add('file-preview'); + preview.setAttribute('src', dataURL); + + li.prepend(preview); + }; + reader.onerror = function (e) { + console.log('An error occurred during file input: '+e.target.error.code); + }; + + reader.readAsDataURL(file); + } + + list.append(li); + } + } + + droparea.ondrop = function (e) { + if (e.dataTransfer) { + e.preventDefault(); + e.stopPropagation(); + + processSelectedFiles(e.dataTransfer.files); + } + + e.target.classList.remove('drag'); + }; + + postForm.addEventListener('submit', async function (e) { + e.preventDefault(); + + const postFormData = new FormData(); + + postFormData.append('content', postForm.querySelector('[name="content"]').value); + + for (const attachment of data.attachments) { + postFormData.append('attachments[]', attachment.file); + } + + /* + for (const pair of postFormData.entries()) { + console.log(`${pair[0]}, ${pair[1]}`); + } + */ + + const response = await fetch(postForm.getAttribute('action'), { + body: postFormData, + method: 'POST' + }); + + if (response.ok && response.status == 200) { + const txt = await response.text(); + // console.log(response, txt); + window.location.href = postForm.dataset.redirect; + } else { + console.warn('error during post submission!', response); + } + }); + } else { + // use the native file input dialog + // but enhanced + if (attachmentsInput) { + console.log('init with classic file attachments'); + + attachmentsInput.addEventListener('change', function (e) { + console.log(e.target.files); + + const list = postForm.querySelector('#post-attachments-list'); + list.replaceChildren(); + + for (const file of e.target.files) { + const li = document.createElement('li'); + li.textContent = file.name; + list.append(li); + } + }); + } + } + } +} + +// better rounded corners +if ('paintWorklet' in CSS) { + // CSS.paintWorklet.addModule('./js/squircle.js'); +} diff --git a/js/squircle.js b/js/squircle.js new file mode 100644 index 0000000..063a383 --- /dev/null +++ b/js/squircle.js @@ -0,0 +1,146 @@ +const drawSquircle = (ctx, geom, radius, smooth, lineWidth, color) => { + const defaultFill = color; + const lineWidthOffset = lineWidth / 2; + // OPEN LEFT-TOP CORNER + ctx.beginPath(); + ctx.lineTo(radius, lineWidthOffset); + // TOP-RIGHT CORNER + ctx.lineTo(geom.width - radius, lineWidthOffset); + ctx.bezierCurveTo( + geom.width - radius / smooth, + lineWidthOffset, // first bezier point + geom.width - lineWidthOffset, + radius / smooth, // second bezier point + geom.width - lineWidthOffset, + radius // last connect point + ); + // BOTTOM-RIGHT CORNER + ctx.lineTo(geom.width - lineWidthOffset, geom.height - radius); + ctx.bezierCurveTo( + geom.width - lineWidthOffset, + geom.height - radius / smooth, // first bezier point + geom.width - radius / smooth, + geom.height - lineWidthOffset, // second bezier point + geom.width - radius, + geom.height - lineWidthOffset // last connect point + ); + // BOTTOM-LEFT CORNER + ctx.lineTo(radius, geom.height - lineWidthOffset); + ctx.bezierCurveTo( + radius / smooth, + geom.height - lineWidthOffset, // first bezier point + lineWidthOffset, + geom.height - radius / smooth, // second bezier point + lineWidthOffset, + geom.height - radius // last connect point + ); + // CLOSE LEFT-TOP CORNER + ctx.lineTo(lineWidthOffset, radius); + ctx.bezierCurveTo( + lineWidthOffset, + radius / smooth, // first bezier point + radius / smooth, + lineWidthOffset, // second bezier point + radius, + lineWidthOffset // last connect point + ); + ctx.closePath(); + + if (lineWidth) { + // console.log(lineWidth); + ctx.strokeStyle = defaultFill; + ctx.lineWidth = lineWidth; + ctx.stroke(); + } else { + ctx.fillStyle = defaultFill; + ctx.fill(); + } +}; + +if (typeof registerPaint !== "undefined") { + class SquircleClass { + static get contextOptions() { + return { alpha: true }; + } + static get inputProperties() { + return [ + "--squircle-radius", + "--squircle-smooth", + "--squircle-outline", + "--squircle-fill", + "--squircle-ratio", + ]; + } + + paint(ctx, geom, properties) { + const customRatio = properties.get("--squircle-ratio"); + const smoothRatio = 10; + const distanceRatio = parseFloat(customRatio) + ? parseFloat(customRatio) + : 1.8; + const squircleSmooth = parseFloat( + properties.get("--squircle-smooth") * smoothRatio + ); + const squircleRadius = + parseInt(properties.get("--squircle-radius"), 10) * distanceRatio; + const squrcleOutline = parseFloat( + properties.get("--squircle-outline"), + 10 + ); + const squrcleColor = properties + .get("--squircle-fill") + .toString() + .replace(/\s/g, ""); + + const isSmooth = () => { + if (typeof properties.get("--squircle-smooth")[0] !== "undefined") { + if (squircleSmooth === 0) { + return 1; + } + return squircleSmooth; + } else { + return 10; + } + }; + + const isOutline = () => { + if (squrcleOutline) { + return squrcleOutline; + } else { + return 0; + } + }; + + const isColor = () => { + if (squrcleColor) { + return squrcleColor; + } else { + return "#f45"; + } + }; + + if (squircleRadius < geom.width / 2 && squircleRadius < geom.height / 2) { + drawSquircle( + ctx, + geom, + squircleRadius, + isSmooth(), + isOutline(), + isColor() + ); + } else { + drawSquircle( + ctx, + geom, + Math.min(geom.width / 2, geom.height / 2), + isSmooth(), + isOutline(), + isColor() + ); + } + } + } + + // eslint-disable-next-line no-undef + registerPaint("squircle", SquircleClass); +}
\ No newline at end of file |