diff --git a/www/core/netitor b/www/core/netitor index 80122d0..936f6a4 160000 --- a/www/core/netitor +++ b/www/core/netitor @@ -1 +1 @@ -Subproject commit 80122d06a535284c8199fcdd0fb1f5b59b3a9d79 +Subproject commit 936f6a47f1395aed4809d759faad446e80529647 diff --git a/www/custom-elements/misc/file-drop/index.js b/www/custom-elements/misc/file-drop/index.js index 7afad84..cf81d16 100644 --- a/www/custom-elements/misc/file-drop/index.js +++ b/www/custom-elements/misc/file-drop/index.js @@ -1,14 +1,15 @@ /* global HTMLElement CustomEvent FileList */ class FileDrop extends HTMLElement { - static get observedAttributes () { return ['accept', 'max-size', 'max-files', 'multiple'] } + static get observedAttributes () { return ['accept', 'max-size', 'max-files', 'multiple', 'disableFileList'] } constructor (opts) { super() this.config = { accept: '.pdf, .jpg, .png', maxSize: '5MB', - maxFiles: '1', - multiple: false + maxFiles: 1, + multiple: false, + disableFileList: false } this.files = [] } @@ -47,9 +48,9 @@ class FileDrop extends HTMLElement { applyAttributes () { this.config.accept = this.getAttribute('accept') ?? this.config.accept this.config.maxSize = this.getAttribute('max-size') ?? this.config.maxSize - const mf = parseInt(this.getAttribute('max-files'), 10) - this.config.maxFiles = Number.isFinite(mf) ? mf : this.config.maxFiles - this.config.multiple = this.hasAttribute('multiple') || this.config.maxFiles > 1 + this.config.maxFiles = this.getAttribute('maxFiles') || this.config.maxFiles + this.config.multiple = this.getAttribute('multiple') || this.config.maxFiles > 1 + this.config.disableFileList = this.hasAttribute('disableFileList') || this.config.disableFileList } applyListeners () { @@ -84,7 +85,7 @@ class FileDrop extends HTMLElement { }) // return if maxFiles amount is already met - if (this.files.length >= maxFiles) { + if (this.files.length > maxFiles) { this.displayMsg({ text: `Max amount of files (${maxFiles}) has already been uploaded. Please remove previously uploaded files and re-upload.`, type: 'error' @@ -102,7 +103,7 @@ class FileDrop extends HTMLElement { const { add, rejected } = this.filterMaxFilesAmount(acceptedFiles) this.files.push(...add) - this.renderFileItems() + if (!this.config.disableFileList) this.renderFileItems() this.dispatchEvent(new CustomEvent('files-changed', { detail: { added: add, rejected, files: this.files.slice() }, @@ -124,7 +125,7 @@ class FileDrop extends HTMLElement { filterMaxFilesAmount (files) { const maxFiles = this.config.maxFiles - const max = Number.isFinite(maxFiles) ? maxFiles : 1 + const max = maxFiles ?? 1 const room = Math.max(0, max - this.files.length) const add = files.slice(0, room) const rejected = files.slice(room) diff --git a/www/widgets/demo-maker/popups/index.html b/www/widgets/demo-maker/popups/index.html index dd115f5..62db2ae 100644 --- a/www/widgets/demo-maker/popups/index.html +++ b/www/widgets/demo-maker/popups/index.html @@ -33,7 +33,7 @@
Uploaded Assets
+ +key:
+ +title:
+ +${l}
+ + + ` + t.querySelector('svg').addEventListener('click', () => removeSpotlight(l)) + ts.appendChild(t) + }) +} + +function removeSpotlight (l) { + const t = nn.get(`[data-lines="${l}"]`) + t.remove() + SPOTLIGHT = SPOTLIGHT.filter(line => line !== l) + updateKeyframe() +} + +function clearAllSpotlights (reset = false) { + SPOTLIGHT.forEach((s) => { + const t = nn.get(`[data-lines="${s}"]`) + t.remove() + }) + SPOTLIGHT = [] + msg('tut-mkr-sptlght', { spotlight: SPOTLIGHT }) + if (!reset) updateKeyframe() +} + +function handleSpotlightEnter () { + const i = nn.get('#sptlght-line-input') + let ls = i.textContent + .split(',') + .map(s => s.trim()) + i.textContent = '' + ls = ls.filter(l => !SPOTLIGHT.includes(l)) + SPOTLIGHT = [...SPOTLIGHT, ...ls] + addSpotlights(ls) + msg('tut-mkr-sptlght', { spotlight: SPOTLIGHT }) + updateKeyframe() +} + +// ------------------------------------------------------------ WIDGET FUNCTIONS + +// create widget option in widget dropdown menu +function addWidgetOption (w, opened = false) { + const d = document.createElement('div') + d.className = 'widget' + + // create checkbox and title + const d1 = document.createElement('div') + const box = document.createElement('input') + box.type = 'checkbox' + box.name = `${w.key}-checkbox` + box.checked = opened + const p = document.createElement('p') + p.textContent = w.key + d1.appendChild(box) + d1.appendChild(p) + + // create edit and delete buttons + const d2 = document.createElement('div') + const ebtn = document.createElement('button') + ebtn.textContent = 'edit' + ebtn.name = `${w.key}-edit` + ebtn.className = 'pill-btn pill-btn--secondary' + const dbtn = document.createElement('button') + dbtn.textContent = 'delete' + dbtn.name = `${w.key}-delete` + dbtn.className = 'pill-btn pill-btn--secondary' + d2.appendChild(ebtn) + d2.appendChild(dbtn) + + function checkboxSelected (e, key) { + if (e.target.checked) msg('tut-mkr-open-widget', { widget: w, open: true }) + else msg('tut-mkr-open-widget', { widget: w, open: false }) + } + + box.addEventListener('change', (e) => checkboxSelected(e, w.key)) + ebtn.addEventListener('click', () => editWidget(w)) + // TODO: need to fix where edit widget state updates after already editing + dbtn.addEventListener('click', () => deleteWidget(w, d)) + + d.appendChild(d1) + d.appendChild(d2) + nn.get('#widget-options').appendChild(d) +} + +function createWidget () { + nn.get('[name="widget-maker-update"]').textContent = 'create' + overlay('#widget-maker') + nn.get('#widget-maker').dataset.widget = 'new-widget' + // open new widget in netnet + msg('tut-mkr-open-widget', { + widget: { + key: 'new-widget', + title: 'new widget', + innerHTML: '...' + }, + open: true + }) +} + +function updateWidget () { + updateWidgetState() + const key = nn.get('#widget-key-input').textContent + const title = nn.get('#widget-title-input').textContent + const innerHTML = WN.code + const widget = { key, title, innerHTML, type: 'Widget' } + + // if widget is new, add it to the dropdown options + if (nn.get('[name="widget-maker-update"]').textContent === 'create') { + addWidgetOption(widget, true) + } + + // if editing an existent widget, send old widget key + if (nn.get('[name="widget-maker-update"]').textContent === 'update') { + const maker = nn.get('#widget-maker') + widget.oldKey = maker.dataset.widget + maker.dataset.widget = key // set to new key + } + msg('tut-mkr-update-widget', { widget }) + closeWidgetMaker('true') +} + +function editWidget (w) { + // set widget to active and open in netnet + nn.get(`[name="${w.key}-checkbox"]`).checked = true + msg('tut-mkr-open-widget', { widget: w, open: true }) + // load widget data into widget maker + nn.get('#widget-maker').dataset.widget = w.key + nn.get('#widget-key-input').textContent = w.key + nn.get('#widget-title-input').textContent = w.title + WN.code = w.innerHTML + WIDGET = w // store current widget state + nn.get('[name="widget-maker-update"]').textContent = 'update' + overlay('#widget-maker') +} + +function deleteWidget (w, ele) { + // TODO: do we want to confirm with users if they want to remove a widget? + msg('tut-mkr-update-widget', { widget: w, remove: true }) + ele.remove() +} + +function updateWidgetState (code) { + // TODO: make sure it is a astring with no spaces + // TODO: check if that widget key is already in the netnet system + const key = nn.get('#widget-key-input').textContent + const title = nn.get('#widget-title-input').textContent + const innerHTML = code || WN.code + if (key && title && innerHTML) { + // fetch old key incase it changed + const maker = nn.get('#widget-maker') + const oldKey = maker.dataset.widget + if (oldKey !== key) { + msg('tut-mkr-update-widget-state', { widget: { key, title, innerHTML, oldKey } }) + maker.dataset.widget = key // set to new key + } + msg('tut-mkr-update-widget-state', { widget: { key, title, innerHTML } }) + } +} + +function loadWidgets (widgets) { + Object.keys(widgets).forEach(key => addWidgetOption(widgets[key])) +} + +function closeWidgetMaker (updated = false) { + const key = nn.get('#widget-key-input').textContent + const title = nn.get('#widget-title-input').textContent + const innerHTML = WN.code + const currentWidget = { key, title, innerHTML, type: 'Widget' } + if (updated || JSON.stringify(WIDGET) === JSON.stringify(currentWidget)) { + // close widget maker & clear inputs + overlay(null) + nn.get('#modal').css('display', 'none') + nn.get('#widget-key-input').textContent = '' + nn.get('#widget-title-input').textContent = '' + WN.code = '...' + } else { + // double-check users want to close without saving changes + const modal = nn.get('.custom-modal-inner') + modal.classList.add('warning') + modal.innerHTML = ` +You have unsaved changes that you’re about to lose. Are you sure you want to proceed?
+