Compare commits

..
10 Commits
Author SHA1 Message Date
mcndave d08bd478fe - Added tabs and upload component 2026-07-01 14:42:40 -04:00
Dev c944b8569f - Added a Notification custom element 2026-02-05 21:22:09 +00:00
Dev 0bb08584e2 - Bugfix on UI-POPUP made 2025-11-18 18:39:44 +00:00
mcndave 45095b221d - WIP on multiple components to set private vars 2025-11-06 17:16:58 +00:00
mcndave 14bccd50a8 - WIP on Swipe module, fixed some bugs within ui-popup 2024-11-21 19:14:32 -05:00
mcndave 778fabb51c - WIP on ui-popup
- Added input.js and swap.js to modules
2024-11-13 20:46:13 -05:00
mcndave 005f631793 - Working on ui-popup adding some features based on FORM inside of it 2024-11-06 20:14:42 -05:00
mcndave 2bfb4bc7f8 - Reworked ui-popup default CSS 2024-11-06 23:56:19 +00:00
mcndave cf03c5b542 - Merged conflicts 2023-10-18 12:30:24 +00:00
mcndave 722226d062 - WIP on autoload 2023-10-17 18:22:22 +00:00
17 changed files with 718 additions and 143 deletions

No files matched your search

+13 -7
View File
@@ -1,14 +1,20 @@
import { ElementObserver } from "./element-observer.js";
import { UiTabs } from "./module/ui-tabs.js";
window.customElements.define("ui-tabs", UiTabs);
window.customElements.get("ui-tabs") || window.customElements.define("ui-tabs", UiTabs);
import { UiPopup } from "./module/ui-popup.js";
window.customElements.define("ui-popup", UiPopup);
window.customElements.get("ui-popup") || window.customElements.define("ui-popup", UiPopup);
import { UiTextarea } from "./module/ui-textarea.js";
window.customElements.define("ui-textarea", UiTextarea);
window.customElements.get("ui-textarea") || window.customElements.define("ui-textarea", UiTextarea);
import { UiSelect } from "./module/ui-select.js";
window.customElements.define("ui-select", UiSelect);
window.customElements.get("ui-select") || window.customElements.define("ui-select", UiSelect);
import { UiUpload, UiUploadMedia } from "./module/ui-upload.js";
window.customElements.get("ui-upload") || window.customElements.define("ui-upload", UiUpload);
window.customElements.get("ui-upload-media") || window.customElements.define("ui-upload-media", UiUploadMedia);
import { UiNotification } from "./module/ui-notification.js";
window.customElements.get("ui-notification") || window.customElements.define("ui-notification", UiNotification);
import './module/input.js';
+1 -1
View File
@@ -89,7 +89,7 @@ class ElementObserver {
console.log("@todo!", split_selector);
}
else {
if ( $element.is( this.listeners[action][j].selector ) ) {
if ( element.is( this.listeners[action][j].selector ) ) {
this.listeners[action][j].callback.call(element, element);
}
}
+39 -34
View File
@@ -1,43 +1,39 @@
/*
* Really simple timer / timeout object
*
* @author Dave Mc Nicoll
* @version 2.0.0
*
*/
const config = {
const defaultConfig = {
timer: 1000,
start: false,
loop: true,
run: false, // If you need a defined number of code execution, set a number to run and loop to false.
occurences: false, // If you need a defined number of code execution, set a number to run and loop to false.
complete: function(){} // This complete callback function is called if the run exec number is reached.
};
class Hourglass {
#callback;
#config = {};
constructor(callback, options) {
this.object_id = null;
this.counter = null;
this.callback = callback !== undefined ? callback : function() {};
this.#config = {...this.#config, ...defaultConfig, ...( options ? options : {} ), };
if ( config.start ) {
this.objectId = null;
this.counter = null;
this.callback = callback ? callback : function() {};
if ( this.config.start ) {
this.start();
}
}
start(timeout) {
let time = ( timeout !== undefined ? timeout : config.timer );
let time = ( timeout !== undefined ? timeout : this.config.timer );
if ( config.loop ) {
if ( this.config.loop ) {
if ( this.counter === null ) {
this.counter = config.loop ? 0 : null
this.counter = this.config.loop ? 0 : null
}
this.object_id = window.setInterval( this.call_interval.bind(this), time);
this.objectId = window.setInterval( this.callInterval.bind(this), time);
}
else {
this.object_id = window.setTimeout( this.call_timeout.bind(this), time);
this.objectId = window.setTimeout( this.callTimeout.bind(this), time);
}
}
@@ -47,44 +43,45 @@ class Hourglass {
}
stop() {
if ( config.loop ) {
this.object_id && window.clearInterval(this.object_id);
if ( this.config.loop ) {
this.objectId && window.clearInterval(this.objectId);
}
else {
this.object_id && window.clearTimeout(this.object_id);
this.objectId && window.clearTimeout(this.objectId);
}
}
clear() {
this.stop();
this.counter = null;
this.object_id = null;
this.objectId = null;
}
call_timeout() {
callTimeout() {
this.callback();
this.clear();
}
call_interval() {
callInterval() {
this.callback();
if ( this.counter !== null && ++this.counter === config.run ) {
if ( this.counter !== null && ++this.counter === this.config.occurences ) {
this.clear();
config.complete !== undefined && config.complete();
this.config.complete !== undefined && this.config.complete();
}
}
running() {
return !! this.object_id;
return !! this.objectId;
}
get callback() {
return this._callback;
return this.#callback;
}
set callback(value) {
return this._callback = value;
return this.#callback = value;
}
get counter() {
@@ -95,12 +92,20 @@ class Hourglass {
return this._counter = value;
}
get object_id() {
return this._object_id;
get objectId() {
return this._objectId;
}
set object_id(value) {
return this._object_id = value;
set objectId(value) {
return this._objectId = value;
}
get config() {
return this.#config;
}
set config(value) {
return this.#config = value;
}
}
+22
View File
@@ -0,0 +1,22 @@
(function() {
// Send automatically a form after one of it's input, which bears the data-input attributes, has changed
document.querySelectorAll('[data-input="send-on-change"]').forEach(
(element) => element.addEventListener("change", (e) => element.closest("form").submit())
);
let inputCopyElement = document.querySelector('[data-input="copy"]');
if ( inputCopyElement ) {
inputCopyElement.inputCopy = function() {
let clone = this.cloneNode(true);
this.parentNode.append(clone);
this.parentNode.dispatchEvent(new CustomEvent("data-input:copied", {
detail: {
obj: clone
}
}));
};
}
})();
+152
View File
@@ -0,0 +1,152 @@
class Swap {
constructor(name, callback) {
this.name = name;
this.thenCallback = [];
this.attached = [];
this.parentElement = document.querySelector("[data-swap='" + name + "']");
if (! this.parentElement ) {
console.error("You must provide a valid HTMLElement to swap it. Impossible to query `" + name + "` element.");
}
this.actionCallback = callback;
this.swap();
}
swapHtml(html) {
this.parentElement.innerHTML = ( new DOMParser() )
.parseFromString(html, "text/html")
.querySelector('[data-swap="' + this.name + '"]')
.innerHTML;
this.parentElement.classList.remove("swapping");
this.swap();
this.attached.forEach(swapObj => {
swapObj.swapHtml(html);
});
}
swapUrl(url, callback) {
this.parentElement.classList.add("swapping");
fetch(url).then( response => response.text() ).then(html => {
this.swapHtml(html);
if (callback) {
callback.call(this, this);
}
});
return this;
}
swapForm(element) {
if (element instanceof HTMLElement) {
this.swapSingleForm(element);
}
else if (element instanceof HTMLCollection || element instanceof NodeList) {
element.forEach(ele => this.swapSingleForm(ele));
}
return this;
}
swapSingleForm(element) {
element.addEventListener("submit", e => {
e.preventDefault();
e.stopImmediatePropagation();
let body = new FormData(element);
if (document.activeElement && document.activeElement.hasAttribute('name')) {
body.append(document.activeElement.getAttribute('name'), document.activeElement.value);
}
let url = element.getAttribute("action") ? element.getAttribute("action") : location.href,
method = element.getAttribute("method") ? element.getAttribute("method") : "get";
fetch(url, {
body: body,
method: method
}).then( response => response.text() ).then(
html => {
this.swapHtml(html);
}
);
});
return this;
}
swap() {
this.actionCallback && this.actionCallback(this);
}
then(callback) {
this.thenCallback.push[callback];
return this;
}
attach(element) {
(Symbol.iterator in Object(element) ? element : [ element ]).forEach(e => {
this.attached.push(e);
});
return this;
}
get form() {
return this._form;
}
set form(value) {
return this._form = value;
}
get actionCallback() {
return this._actionCallback;
}
set actionCallback(value) {
return this._actionCallback = value;
}
get parentElement() {
return this._parentElement;
}
set parentElement(value) {
return this._parentElement = value;
}
get name() {
return this._name;
}
set name(value) {
return this._name = value;
}
get thenCallback() {
return this._thenCallback;
}
set thenCallback(value) {
return this._thenCallback = value;
}
get attached() {
return this._attached;
}
set attached(value) {
return this._attached = value;
}
}
+94
View File
@@ -0,0 +1,94 @@
/**
* JQuery Mobile Swipe
*
* This plugin adds some classes to html's DOM which can be used as fallback for action such as :hover which are not
* always intuitive on mobile ( and sometimes, unexisting ).
* Based on jquery.detectSwipe v2.1.1 [http://github.com/marcandre/detect_swipe] which is based on touchwipe by Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
*/
(function ($) {
$.mobileswipe = {
attach : register,
enabled : 'ontouchstart' in document.documentElement,
threshold : 20,
reset_timer : null
};
$.fn.mobileswipe = register;
$.event.special.swipe = { setup: register };
$.each(['left', 'up', 'down', 'right'], function () {
$.event.special['swipe' + this] = {setup: function () {
$(this).on('swipe', $.noop);
}};
});
$(function() { $('.swipe').mobileswipe(); });
var startX, startY,
timeout_instance = [],
isMoving = false;
function onTouchEnd() {
this.removeEventListener('touchmove', onTouchMove);
this.removeEventListener('touchend', onTouchEnd);
isMoving = false;
}
function onTouchMove(e) {
if (isMoving) {
var dir, delta, $this = $(this);
if (Math.abs(delta = startX - e.touches[0].pageX) >= $.mobileswipe.threshold) {
dir = delta > 0 ? 'left' : 'right';
$this.removeClass('swipe-left swipe-right');
}
else if (Math.abs(delta = startY - e.touches[0].pageY) >= $.mobileswipe.threshold) {
dir = delta < 0 ? 'down' : 'up';
$this.removeClass('swipe-down swipe-up');
}
if ( dir ) {
onTouchEnd.call(this);
$this.trigger('swipe', dir).trigger('swipe' + dir).addClass('swipe-' + dir);
checkPersistency($this, dir, $this.data('reset-timer'));
}
}
}
function onTouchStart(e) {
if (e.touches.length === 1) {
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
isMoving = true;
this.addEventListener('touchmove', onTouchMove, false);
this.addEventListener('touchend', onTouchEnd, false);
}
}
function checkPersistency($elem, dir, timer) {
timer || ( timer = $.mobileswipe.reset_timer );
if ( timer ) {
(dir in timeout_instance) && clearTimeout(timeout_instance[dir]);
timeout_instance[dir] = setTimeout(function() {
$elem.removeClass('swipe-'+dir);
}, timer);
}
}
function register() {
$(this).each(function() {
this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
});
}
function unregister() {
$(this).each(function() {
this.removeEventListener('touchstart', onTouchStart);
});
}
})(jQuery);
@@ -0,0 +1,102 @@
import { Webcomponent } from "./webcomponent.js";
class UiNotification extends Webcomponent {
#intervalId;
constructor() {
super({
template: {
fetch:false
}
});
}
connectedCallback() {
if ( this.activate() ) {
this.start_pooling();
}
}
activate() {
if (window.Notification) {
if ( (Notification.permission !== "denied") && (Notification.permission !== 'granted') ) {
this.style.visibility = "visible";
this.style.cursor = "pointer";
this.addEventListener('click', e => {
Notification.requestPermission().then((permission) => {
this.style.visibility = "hidden";
this.start_pooling();
});
});
}
else {
return Notification.permission === "granted";
}
}
return false;
}
start_pooling() {
this.intervalId = setInterval(this.pool.bind(this), this.interval);
}
stop_pooling() {
clearInterval(this.intervalId);
}
pool() {
if (Notification.permission === "granted") {
fetch(this.url_fetch + "?ts=" + this.timestamp)
.then(response => response.json())
.then(data =>{
if (data.notification) {
var notification = new Notification(data.title, {
icon: this.url_icon,
body: data.body,
});
notification.onclick = function () {
let win = window.open(data.url, data.title);
setTimeout(e => win.focus(), 600);
};
this.timestamp = data.timestamp;
}
});
}
}
get timestamp() {
return this.dataset.timestamp;
}
set timestamp(value) {
return this.dataset.timestamp = value;
}
get url_fetch() {
return this.dataset.url;
}
get url_icon() {
return this.dataset.icon ? this.dataset.icon : null;
}
get interval() {
return this.dataset.interval ? this.dataset.interval : Math.floor(Math.random() * (45000 - 15000) + 15000);
}
get intervalId() {
return this.#intervalId;
}
set intervalId(value) {
return this.#intervalId = value;
}
}
export { UiNotification }
+119 -67
View File
@@ -6,79 +6,55 @@ const config = {
};
class UiPopup extends Webcomponent {
#scrollPosition;
#options = {};
constructor() {
super();
this.fetch_template();
this.message = this.querySelector('.message');
this.title = this.querySelector('.title');
this.replaceVarsPlaceholder().attach().render();
if (!this.message) {
this.message = document.createElement("div");
}
this.init();
if (!this.title) {
this.title = document.createElement("div");
}
this.replaceVarsPlaceholder().attach().render();
this.handleOptions();
}
handleOptions() {
if ( this.options ) {
if ( ! this.options['skip-keys'] ) {
if ( this.options['skip-keys'] === undefined ) {
document.addEventListener('keyup', function(e) {
if ( this.visible() ) {
let trigger, ev;
e.preventDefault();
// There's no real value in checking if the popup is shown or not.
switch ( e.keyCode ) {
// RETURN
case 13:
trigger = this.querySelector('[action="confirm"]');
ev = new CustomEvent('click', { trigger: trigger } );
trigger.dispatchEvent(ev);
break;
// ESC
case 27:
trigger = this.querySelector('[action="close"]');
ev = new CustomEvent('click', { trigger: trigger } );
trigger.dispatchEvent(ev);
break;
}
if (trigger) {
ev = new CustomEvent('click', {trigger: trigger});
trigger.dispatchEvent(ev);
}
}
}.bind(this));
}
}
}
init() {
document.addEventListener('keyup', function(e) {
if ( this.visible() ) {
let trigger, ev;
e.preventDefault();
// There's no real value in checking if the popup is shown or not.
switch ( e.keyCode ) {
case 13: // RETURN
trigger = this.querySelector('[action="confirm"]');
ev = new CustomEvent('click', { trigger: trigger } );
trigger.dispatchEvent(ev);
break;
case 8: // BACKSPACE
case 27: // ESC
trigger = this.querySelector('[action="close"]');
ev = new CustomEvent('click', { trigger: trigger } );
trigger.dispatchEvent(ev);
break;
}
}
}.bind(this));
}
attach() {
let triggers = document.querySelectorAll('[ui-popup]');
@@ -99,14 +75,23 @@ class UiPopup extends Webcomponent {
}
render() {
this.querySelectorAll('[action]').forEach(function(element) {
this.querySelectorAll('[action]:not(form)').forEach(function(element) {
element.addEventListener("click", this.action.bind(this, element), false);
}.bind(this));
if ( this.classList.contains("show") ) {
this.classList.remove("show");
setTimeout(() => this.show(), 100);
}
else {
this.hide();
}
}
action(element, e) {
let retval;
if ( ! element.attributes.action ) {
throw "A button was clicked onto which no action was bound.";
}
@@ -115,10 +100,22 @@ class UiPopup extends Webcomponent {
case "confirm":
case "yes":
case "ok":
this.actionFollowLink(element) ||
this.actionClickButton(element) ||
retval = this.actionFollowLink(element) ||
this.actionClickButton(element);
if (! retval) {
retval = this.actionSendForm(element) ||
this.actionSendForm(this.trigger) ||
this.actionSendForm(element);
this.actionSendForm(this.querySelector('[slot="message"] form')) ||
this.actionSendForm(this.querySelector('[slot="title"] form')) ||
this.actionSendForm(this.querySelector('[slot="buttons"] form'));
if ( ! retval ) {
throw "No action to trigger automatically on Popup confirmation.";
}
break;
}
case "cancel":
case "no":
@@ -127,8 +124,13 @@ class UiPopup extends Webcomponent {
break;
}
// Disable multiple clicks
if (retval !== undefined) {
element.setAttribute('disabled', 'disabled');
}
this.dispatchEvent(new CustomEvent('action:' + element.attributes.action.value));
this.trigger.dispatchEvent(new CustomEvent('action:' + element.attributes.action.value));
this.trigger && this.trigger.dispatchEvent(new CustomEvent('action:' + element.attributes.action.value));
if ( element.getAttribute("action") ) {
e.preventDefault();
@@ -139,10 +141,15 @@ class UiPopup extends Webcomponent {
hide() {
this.classList.remove('visible');
window.scrollTo(window.scrollX, this.scrollPosition);
}
show() {
this.scrollPosition = window.scrollY;
this.classList.add('visible');
window.scrollTo(window.scrollX, 0);
}
visible() {
@@ -153,8 +160,8 @@ class UiPopup extends Webcomponent {
//if ( element.getAttribute('follow-link') !== null ) {
// Automatically redirect to given location if it exists.
if ( this.trigger.tagName === "A" ) {
let href = this.trigger.getAttribute('href');
if ( this.trigger && this.trigger.tagName === "A" ) {
const href = this.trigger.getAttribute('href');
if ( href ) {
window.location = href;
@@ -168,12 +175,11 @@ class UiPopup extends Webcomponent {
}
actionClickButton(element) {
//if ( element.getAttribute('button-click') !== null ) {
let tag = this.trigger.tagName.toLowerCase();
if (this.trigger) {
const tag = this.trigger.tagName.toLowerCase();
// Automatically redirect to given location if it exists.
if ( tag === "button" || ( ( tag === "input" ) && ( this.trigger.getAttribute("type").toLowerCase() === "submit") ) ) {
if ((tag === "button" || tag === "input") && this.trigger.getAttribute("type").toLowerCase() === "submit") {
this.pause = true;
this.trigger.click();
@@ -182,24 +188,35 @@ class UiPopup extends Webcomponent {
return true;
}
}
//}
return false;
}
actionSendForm(element) {
//if ( element.getAttribute('send-form') !== null ) {
//let form = this.trigger.tagName === "FORM" ? this.trigger : this.trigger.closest('FORM');
if ( ! element ) {
return false;
}
let form = element.tagName === "FORM" ? element : element.closest('FORM');
if ( form ) {
form.submit();
form.addEventListener("submit", e => this.hide, { once: true });
let submitBtn = form.querySelector("button[type='submit']");
if (submitBtn) {
submitBtn.click();
}
else {
form.requestSubmit();
}
return true;
}
else {
throw "A send-form action was set on a popup where the trigger as no parent form.";
}
return false;
//}
}
@@ -213,7 +230,6 @@ class UiPopup extends Webcomponent {
this.trigger = item;
args.vars && Object.keys(args.vars).map(function(objectKey, index) {
// let element = this.message.querySelectorAll(`.${objectKey}`);
let element = Array.prototype.slice.call( this.message.querySelectorAll(`.${objectKey}`) ).concat(Array.prototype.slice.call(this.title.querySelectorAll(`.${objectKey}`)));
element.length ? element.forEach(function(item) {
@@ -224,22 +240,42 @@ class UiPopup extends Webcomponent {
}.bind(this));
if ( args.options ) {
args.options['input'] && Object.keys(args.options['input']).forEach(
key => this.querySelectorAll(`[name="${key}"]`).forEach(function(f) {
if (f.tagName === "TEXTAREA") {
f.value = args.options['input'][key];
if (args.options['clear_input']) {
let skipInput = args.options['clear_input']['skip'] ? args.options['clear_input']['skip'] : [];
this.querySelectorAll(`input,textarea`).forEach(function(f) {
if (! skipInput.includes(f.name)) {
f.value = "";
}
else {
f.setAttribute("value", args.options['input'][key]);
});
}
args.options['input'] && Object.keys(args.options['input']).forEach(
key => this.querySelectorAll(`[name="${key}"]`).forEach(function(f) {
f.value = args.options['input'][key];
f.dispatchEvent(new Event("change"));
})
);
args.options['form_url'] && Object.keys(args.options['form_url']).forEach(
key => {
let form = this.querySelector(`form${key}`);
form = form ? form : this.closest(`form${key}`);
if (form) {
form.setAttribute('action', args.options['form_url'][key]);
}
else {
console.warn(`Form selector '${key}' could not be found within popup.`);
}
}
);
}
this.show();
e.stopPropagation();
return true;
}
@@ -281,6 +317,22 @@ class UiPopup extends Webcomponent {
return this._message = value;
}
get options() {
return this.#options;
}
set options(value) {
return this.#options = value;
}
get scrollPosition() {
return this.#scrollPosition;
}
set scrollPosition(value) {
return this.#scrollPosition = value;
}
get title() {
return this._title;
}
-2
View File
@@ -15,8 +15,6 @@ class UiSelect extends Webcomponent {
constructor() {
super();
this.fetch_template();
this.wrapper = this.shadowRoot.querySelector(config.content.wrapper);
this.list = this.shadowRoot.querySelector(config.content.list);
this.input = this.querySelector('input:not([type="hidden"])');
+16 -15
View File
@@ -5,19 +5,20 @@ const config = {
item: ".tab-item",
content: ".tab-content",
activeClass: "ui-tab-active",
template: {
fetch : false
}
};
class UiTabs extends Webcomponent {
constructor(parameters) {
super();
this.init();
constructor() {
super(config);
}
init() {
connectedCallback() {
window.addEventListener("hashchange", this.hashChange.bind(this), false);
//this.querySelectorAll(config.item).forEach(ele => ele.addEventListener("click", this.clickTab.bind(this)));
//this.querySelectorAll(this.config.item).forEach(ele => ele.addEventListener("click", this.clickTab.bind(this)));
if (window.location.hash) {
let eve = new Event("click");
@@ -29,16 +30,16 @@ class UiTabs extends Webcomponent {
hashChange(e) {
let anchor = e.newURL.split('#')[1];
document.querySelectorAll("#" + anchor + config.content).forEach(function(content) {
content.parentNode.querySelectorAll(config.content + "." + config.activeClass).forEach(ele => ele.classList.remove(config.activeClass));
content.classList.add(config.activeClass);
});
document.querySelectorAll(`[id='${anchor}']${this.config.content}`).forEach(function(content) {
content.parentNode.querySelectorAll(this.config.content + "." + this.config.activeClass).forEach(ele => ele.classList.remove(this.config.activeClass));
content.classList.add(this.config.activeClass);
}.bind(this));
let element = this.querySelector("." + config.activeClass);
element ? element.classList.remove(config.activeClass) : null;
let element = this.querySelector("." + this.config.activeClass);
element ? element.classList.remove(this.config.activeClass) : null;
element = this.querySelector(config.item + `[href="#${anchor}"]`);
element ? element.closest('div').classList.add(config.activeClass) : null;
element = this.querySelector(this.config.item + `[href="#${anchor}"]`);
element ? element.closest('div').classList.add(this.config.activeClass) : null;
}
clickTab(e) {
@@ -47,4 +48,4 @@ class UiTabs extends Webcomponent {
}
}
export { UiTabs }
export { UiTabs };
+3 -2
View File
@@ -25,7 +25,6 @@ class UiTextarea extends Webcomponent {
constructor() {
super();
this.fetch_template();
this.wrapper = this.shadowRoot.querySelector(domElements.wrapper);
this.content = this.shadowRoot.querySelector(domElements.boilerplate);
this.toolbar = this.shadowRoot.querySelector(domElements.toolbar);
@@ -41,7 +40,9 @@ class UiTextarea extends Webcomponent {
render() {
document.execCommand("DefaultParagraphSeparator", false, "p");
this.content.innerHTML = this.textarea.value.trim();
this.textarea.addEventListener("change", (e) => this.content.innerHTML = this.textarea.value.trim());
this.textarea.dispatchEvent(new Event("change"));
this.content.addEventListener("input", function(e, d) {
this.content.querySelectorAll('[style]').forEach(function(element) {
+67
View File
@@ -0,0 +1,67 @@
import { Webcomponent } from './webcomponent.js';
const config = {
content: {
wrapper: ".wrapper",
list: ".item-list"
}
};
class UiUpload extends Webcomponent {
#input;
#uploadmedia;
constructor() {
super();
}
connectedCallback() {
this.attachInput();
this.#uploadmedia = document.querySelector(`ui-upload-media[data-name="${this.name}"]`)
}
attachInput() {
this.#input = this.querySelector('input:not([type="hidden"])');
this.addEventListener('click', () => this.#input.click());
this.#input.addEventListener('change', this.changeMedia.bind(this));
}
changeMedia(e) {
let inputHtml = this.#input.outerHTML;
Array.from(this.#input.files).forEach(file => {
const base64Res = new FileReader();
this.#uploadmedia.addThumbnail(base64Res, this.#input);
base64Res.readAsDataURL(file)
});
this.getSlot("input").insertAdjacentHTML('beforeend', inputHtml);
this.attachInput();
}
get name() {
return this.dataset['name'];
}
}
class UiUploadMedia extends Webcomponent {
addThumbnail(media, input) {
const wrapper = this.shadowRoot.querySelector(".new-media-wrapper"),
mediaItem = this.shadowRoot.querySelector(".new-media-item[hidden]").cloneNode(true);
mediaItem.removeAttribute('hidden');
wrapper.append(mediaItem);
this.getSlot("input").append(input);
media.onload = f => {
mediaItem.querySelector('img').setAttribute('src', media.result);
// mediaItem.querySelector('.name-input').value = file.name;
}
}
}
export { UiUpload, UiUploadMedia }
+51 -5
View File
@@ -1,21 +1,38 @@
class Webcomponent extends HTMLElement {
#config = {
template: {
fetch : true
}
};
fetch_template() {
constructor(config) {
super();
if (config) {
this.#config = {...this.#config, ...( config ? config : {} ) };
}
if (this.config.template.fetch) {
this.fetchTemplate();
}
}
fetchTemplate() {
this.attachShadow({mode: 'open'}).appendChild(
document.querySelector("template#" + this.tagName.toLowerCase()).content.cloneNode(true)
);
}
get_name() {
getName() {
return Object.getPrototypeOf(this).constructor.name;
}
get_slot(name) {
getSlot(name) {
return this.querySelector('[slot="' + name + '"]');
}
empty_element(element) {
emptyElement(element) {
while (element.lastChild) {
element.removeChild(element.lastChild);
}
@@ -23,11 +40,40 @@ class Webcomponent extends HTMLElement {
return element;
}
build_url(url, data) {
buildUrl(url, data) {
return url + ( Object.entries(data).length ? "?" + Object.keys(data).map(function(key) {
return [key, data[key]].map(encodeURIComponent).join("=");
}).join("&") : "" );
}
on(type, listener, options) {
this.addEventListener(type, listener, options);
return this;
}
off(type, listener, options) {
this.removeEventListener(type, listener, options);
return this;
}
emit(name, detail, bubbles, cancelable, composed) {
this.dispatchEvent(new CustomEvent(name, {
detail: detail ? detail : null,
bubbles: !! bubbles,
cancelable: !! cancelable,
composed: !! composed,
}));
}
get config() {
return this.#config;
}
set config(value) {
return this.#config = value;
}
}
export { Webcomponent };
-3
View File
@@ -14,9 +14,6 @@
</header>
<section class="message"><slot name="content"></slot></section>
{# Todo ! #}
<progress class="progress" value="45" max="100">45%</progress>
</div>
</template>
+9 -6
View File
@@ -1,13 +1,16 @@
<style>
ui-popup:not(.modal) {box-shadow:0px 0px 0px 9999px rgba(0, 0, 0, 0.5);z-index:9999;transition:all 0.3s ease-out;background:#fff;position:fixed!important;top:0;left: 50%;transform:translate(-50%, 15%);}
ui-popup:not(.modal) {box-shadow:0px 0px 0px 9999px rgba(0, 0, 0, 0.5);z-index:9999;transition:all 0.3s ease-out;background:#fff;position:absolute!important;top:0;left: 50%;transform:translate(-50%, 5vh);border: 2px solid #ddd;}
ui-popup:not(.visible) {transform: translate(-50%, -100%);transition-timing-function: ease-in;translate(-50%, -100%);box-shadow:none;}
ui-popup.modal.visible {display:flex}
ui-popup > * {padding:1.5rem}
ui-popup .button-list {text-align:center;}
ui-popup > * {padding:1rem 1.4rem}
ui-popup .button-list {text-align:right;}
ui-popup .button-list .button {padding: 0px 7%;}
ui-popup .message {background: transparent;padding:1.2rem 1.5rem!important}
ui-popup .title {font-size:1.5rem;display:flex;justify-content: space-between;background:rgba(120,120,120,0.15)}
ui-popup::before {left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,0.1);content:" ";}
ui-popup [slot="title"] {font-size:1.5rem;display:flex;justify-content: space-between;background:rgba(120,120,120,0.12);color:#464646}
ui-popup [slot="title"]:empty {display:none}
ui-popup [slot="message"] {background: transparent;padding:1.2rem 1.5rem!important}
ui-popup [slot="buttons"] {background:rgba(120,120,120,0.07)}
ui-popup [slot="buttons"]:empty {display:none}
ui-popup::before {left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,0.1);content:" ";pointer-events: none}
</style>
<template id="ui-popup">
-1
View File
@@ -1,4 +1,3 @@
<template id="ui-textarea">
<style>
.wrapper {position:relative;max-width:100%;}
+30
View File
@@ -0,0 +1,30 @@
<template id="ui-upload">
<style>
[hidden] {display:none !important}
.image-drop {display:flex;border-radius:4px;cursor:pointer;height:30vh;background:#fff;margin:5vh auto;text-align:center;border:1px dashed #909599;font-size:3rem;justify-content:center;align-items:center;}
.image-drop:hover {border-color:#676565}
.image-drop:hover span {text-decoration: underline;}
.image-drop.ui-dragdrop-over {opacity:0.66;}
.image-drop .drop-file {display:inline-block;line-height:20vh;color:#888;font-size:1.5em;}
.image-drop i {opacity:0.7}
.image-drop:hover i {opacity:1.0}
</style>
<label class="image-drop">
<slot style="display:none" name="input"></slot>
<slot name="icon"></slot>
</label>
</template>
<template id="ui-upload-media">
<style>
* {box-sizing: border-box}
img {max-height: 15vh;max-width:100%}
.new-media-wrapper {display:flex;flex-flow:wrap;align-items:center;background: #e3e3e3;border: 1px solid #ccc;}
.new-media-item {width:33.33%;padding:2%}
</style>
<div class="new-media-item" hidden><div><img></div></div>
<div class="new-media-wrapper"></div>
</template>