move over a bunch of ui functionality to bitty

This commit is contained in:
2025-11-29 06:58:41 +03:00
parent 0bed6b58ae
commit bbe57d6e94
6 changed files with 182 additions and 249 deletions

View File

@@ -1,4 +1,7 @@
const bookmarkMenuHrefTemplate = '/hyperapi/bookmarks-dropdown'
const bookmarkMenuHrefTemplate = '/hyperapi/bookmarks-dropdown';
const previewEndpoint = '/api/babycode-preview';
const delay = ms => {return new Promise(resolve => setTimeout(resolve, ms))}
export default class {
async showBookmarkMenu(ev, el) {
@@ -85,4 +88,160 @@ export default class {
window.location.reload();
}
}
async copyCode(ev, el) {
if (!el.isSender) {
return;
}
await navigator.clipboard.writeText(el.value);
el.textContent = 'Copied!'
await delay(1000);
el.textContent = 'Copy';
}
toggleAccordion(ev, el) {
const accordion = el;
const header = accordion.querySelector('.accordion-header');
if (!header.contains(ev.sender)){
return;
}
const btn = ev.sender;
const content = el.querySelector('.accordion-content');
// these are all meant to be in sync
accordion.classList.toggle('hidden');
content.classList.toggle('hidden');
btn.textContent = accordion.classList.contains('hidden') ? '+' : '-';
}
toggleTab(ev, el) {
const tabButtonsContainer = el.querySelector('.tab-buttons');
if (!el.contains(ev.sender)) {
return;
}
if (ev.sender.classList.contains('active')) {
return;
}
const targetId = ev.sender.getString('targetId');
const contents = el.querySelectorAll('.tab-content');
for (let content of contents) {
if (content.id === targetId) {
content.classList.add('active');
} else {
content.classList.remove('active');
}
}
for (let button of tabButtonsContainer.children) {
if (button.dataset.targetId === targetId) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
}
}
#previousMarkup = '';
async babycodePreview(ev, el) {
if (ev.sender.classList.contains('active')) {
return;
}
const previewErrorsContainer = el.querySelector('#babycode-preview-errors-container');
const previewContainer = el.querySelector('#babycode-preview-container');
const ta = document.getElementById('babycode-content');
const markup = ta.value.trim();
if (markup === '' || markup === this.#previousMarkup) {
return;
}
const bannedTags = JSON.parse(document.getElementById('babycode-banned-tags').value);
this.#previousMarkup = markup;
const res = await this.api.getJSON(previewEndpoint, [], {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
markup: markup,
banned_tags: bannedTags,
}),
});
if (res.error) {
switch (res.error.status) {
case 429:
previewErrorsContainer.textContent = '(Old preview, try again in a few seconds.)'
this.#previousMarkup = '';
break;
case 400:
previewErrorsContainer.textContent = '(Request got malformed.)'
break;
case 401:
previewErrorsContainer.textContent = '(You are not logged in.)'
break;
default:
previewErrorsContainer.textContent = '(Error. Check console.)'
break;
}
} else {
previewErrorsContainer.textContent = '';
previewContainer.innerHTML = res.value.html;
}
}
insertBabycodeTag(ev, el) {
const tagStart = ev.sender.getString('tag');
const breakLine = 'breakLine' in ev.sender.dataset;
const prefill = 'prefill' in ev.sender.dataset ? ev.sender.dataset.prefill : '';
const hasAttr = tagStart[tagStart.length - 1] === '=';
let tagEnd = tagStart;
let tagInsertStart = `[${tagStart}]${breakLine ? '\n' : ''}`;
if (hasAttr) {
tagEnd = tagEnd.slice(0, -1);
}
const tagInsertEnd = `${breakLine ? '\n' : ''}[/${tagEnd}]`;
const hasSelection = el.selectionStart !== el.selectionEnd;
const text = el.value;
if (hasSelection) {
const realStart = Math.min(el.selectionStart, el.selectionEnd);
const realEnd = Math.max(el.selectionStart, el.selectionEnd);
const selectionLength = realEnd - realStart;
const strStart = text.slice(0, realStart);
const strEnd = text.substring(realEnd);
const frag = `${tagInsertStart}${text.slice(realStart, realEnd)}${tagInsertEnd}`;
const reconst = `${strStart}${frag}${strEnd}`;
el.value = reconst;
if (!hasAttr) {
el.setSelectionRange(realStart + tagInsertStart.length, realStart + tagInsertEnd.length + selectionLength - 1);
} else {
const attrCursor = realStart + tagInsertEnd.length - (1 + (breakLine ? 1 : 0))
el.setSelectionRange(attrCursor, attrCursor); // cursor on attr
}
} else {
if (hasAttr) {
tagInsertStart += prefill;
}
const cursor = el.selectionStart;
const strStart = text.slice(0, cursor);
const strEnd = text.substr(cursor);
let newCursor = strStart.length + tagInsertStart.length;
if (hasAttr) {
newCursor = cursor + tagInsertStart.length - prefill.length - (1 + (breakLine ? 1 : 0)) //cursor on attr
}
const reconst = `${strStart}${tagInsertStart}${tagInsertEnd}${strEnd}`;
el.value = reconst;
el.setSelectionRange(newCursor, newCursor);
}
el.focus();
}
addQuote(ev, el) {
el.value += ev.sender.value;
el.scrollIntoView();
el.focus();
}
}