4NK_wallet/ihm/account-component-DbdHSqFJ.mjs
Your Name a189470d49
Some checks failed
CI - 4NK Node / Integration Tests (push) Failing after 9s
CI - 4NK Node / Docker Build & Test (push) Failing after 8s
CI - 4NK Node / Documentation Tests (push) Failing after 3s
CI - 4NK Node / Release Guard (push) Has been skipped
CI - 4NK Node / Performance Tests (push) Failing after 29s
CI - 4NK Node / Code Quality (push) Failing after 32s
CI - 4NK Node / Unit Tests (push) Failing after 30s
CI - 4NK Node / Security Tests (push) Failing after 28s
CI - 4NK Node / Notify (push) Failing after 1s
chore(template-sync): aligner avec 4NK_template (.cursor/.gitea/.gitea_template/scripts/ignores)
2025-08-27 11:37:12 +02:00

6759 lines
301 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { S as Services, a as addressToEmoji } from './index-CZvdFchg.mjs';
/*!
* sweetalert2 v11.22.4
* Released under the MIT License.
*/
function _assertClassBrand(e, t, n) {
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
throw new TypeError("Private element is not present on this object");
}
function _checkPrivateRedeclaration(e, t) {
if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
}
function _classPrivateFieldGet2(s, a) {
return s.get(_assertClassBrand(s, a));
}
function _classPrivateFieldInitSpec(e, t, a) {
_checkPrivateRedeclaration(e, t), t.set(e, a);
}
function _classPrivateFieldSet2(s, a, r) {
return s.set(_assertClassBrand(s, a), r), r;
}
const RESTORE_FOCUS_TIMEOUT = 100;
/** @type {GlobalState} */
const globalState = {};
const focusPreviousActiveElement = () => {
if (globalState.previousActiveElement instanceof HTMLElement) {
globalState.previousActiveElement.focus();
globalState.previousActiveElement = null;
} else if (document.body) {
document.body.focus();
}
};
/**
* Restore previous active (focused) element
*
* @param {boolean} returnFocus
* @returns {Promise<void>}
*/
const restoreActiveElement = returnFocus => {
return new Promise(resolve => {
if (!returnFocus) {
return resolve();
}
const x = window.scrollX;
const y = window.scrollY;
globalState.restoreFocusTimeout = setTimeout(() => {
focusPreviousActiveElement();
resolve();
}, RESTORE_FOCUS_TIMEOUT); // issues/900
window.scrollTo(x, y);
});
};
const swalPrefix = 'swal2-';
/**
* @typedef {Record<SwalClass, string>} SwalClasses
*/
/**
* @typedef {'success' | 'warning' | 'info' | 'question' | 'error'} SwalIcon
* @typedef {Record<SwalIcon, string>} SwalIcons
*/
/** @type {SwalClass[]} */
const classNames = ['container', 'shown', 'height-auto', 'iosfix', 'popup', 'modal', 'no-backdrop', 'no-transition', 'toast', 'toast-shown', 'show', 'hide', 'close', 'title', 'html-container', 'actions', 'confirm', 'deny', 'cancel', 'footer', 'icon', 'icon-content', 'image', 'input', 'file', 'range', 'select', 'radio', 'checkbox', 'label', 'textarea', 'inputerror', 'input-label', 'validation-message', 'progress-steps', 'active-progress-step', 'progress-step', 'progress-step-line', 'loader', 'loading', 'styled', 'top', 'top-start', 'top-end', 'top-left', 'top-right', 'center', 'center-start', 'center-end', 'center-left', 'center-right', 'bottom', 'bottom-start', 'bottom-end', 'bottom-left', 'bottom-right', 'grow-row', 'grow-column', 'grow-fullscreen', 'rtl', 'timer-progress-bar', 'timer-progress-bar-container', 'scrollbar-measure', 'icon-success', 'icon-warning', 'icon-info', 'icon-question', 'icon-error', 'draggable', 'dragging'];
const swalClasses = classNames.reduce((acc, className) => {
acc[className] = swalPrefix + className;
return acc;
}, /** @type {SwalClasses} */{});
/** @type {SwalIcon[]} */
const icons = ['success', 'warning', 'info', 'question', 'error'];
const iconTypes = icons.reduce((acc, icon) => {
acc[icon] = swalPrefix + icon;
return acc;
}, /** @type {SwalIcons} */{});
const consolePrefix = 'SweetAlert2:';
/**
* Capitalize the first letter of a string
*
* @param {string} str
* @returns {string}
*/
const capitalizeFirstLetter = str => str.charAt(0).toUpperCase() + str.slice(1);
/**
* Standardize console warnings
*
* @param {string | string[]} message
*/
const warn = message => {
console.warn(`${consolePrefix} ${typeof message === 'object' ? message.join(' ') : message}`);
};
/**
* Standardize console errors
*
* @param {string} message
*/
const error = message => {
console.error(`${consolePrefix} ${message}`);
};
/**
* Private global state for `warnOnce`
*
* @type {string[]}
* @private
*/
const previousWarnOnceMessages = [];
/**
* Show a console warning, but only if it hasn't already been shown
*
* @param {string} message
*/
const warnOnce = message => {
if (!previousWarnOnceMessages.includes(message)) {
previousWarnOnceMessages.push(message);
warn(message);
}
};
/**
* Show a one-time console warning about deprecated params/methods
*
* @param {string} deprecatedParam
* @param {string?} useInstead
*/
const warnAboutDeprecation = (deprecatedParam, useInstead = null) => {
warnOnce(`"${deprecatedParam}" is deprecated and will be removed in the next major release.${useInstead ? ` Use "${useInstead}" instead.` : ''}`);
};
/**
* If `arg` is a function, call it (with no arguments or context) and return the result.
* Otherwise, just pass the value through
*
* @param {Function | any} arg
* @returns {any}
*/
const callIfFunction = arg => typeof arg === 'function' ? arg() : arg;
/**
* @param {any} arg
* @returns {boolean}
*/
const hasToPromiseFn = arg => arg && typeof arg.toPromise === 'function';
/**
* @param {any} arg
* @returns {Promise<any>}
*/
const asPromise = arg => hasToPromiseFn(arg) ? arg.toPromise() : Promise.resolve(arg);
/**
* @param {any} arg
* @returns {boolean}
*/
const isPromise = arg => arg && Promise.resolve(arg) === arg;
/**
* Gets the popup container which contains the backdrop and the popup itself.
*
* @returns {HTMLElement | null}
*/
const getContainer = () => document.body.querySelector(`.${swalClasses.container}`);
/**
* @param {string} selectorString
* @returns {HTMLElement | null}
*/
const elementBySelector = selectorString => {
const container = getContainer();
return container ? container.querySelector(selectorString) : null;
};
/**
* @param {string} className
* @returns {HTMLElement | null}
*/
const elementByClass = className => {
return elementBySelector(`.${className}`);
};
/**
* @returns {HTMLElement | null}
*/
const getPopup = () => elementByClass(swalClasses.popup);
/**
* @returns {HTMLElement | null}
*/
const getIcon = () => elementByClass(swalClasses.icon);
/**
* @returns {HTMLElement | null}
*/
const getIconContent = () => elementByClass(swalClasses['icon-content']);
/**
* @returns {HTMLElement | null}
*/
const getTitle = () => elementByClass(swalClasses.title);
/**
* @returns {HTMLElement | null}
*/
const getHtmlContainer = () => elementByClass(swalClasses['html-container']);
/**
* @returns {HTMLElement | null}
*/
const getImage = () => elementByClass(swalClasses.image);
/**
* @returns {HTMLElement | null}
*/
const getProgressSteps = () => elementByClass(swalClasses['progress-steps']);
/**
* @returns {HTMLElement | null}
*/
const getValidationMessage = () => elementByClass(swalClasses['validation-message']);
/**
* @returns {HTMLButtonElement | null}
*/
const getConfirmButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.confirm}`));
/**
* @returns {HTMLButtonElement | null}
*/
const getCancelButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.cancel}`));
/**
* @returns {HTMLButtonElement | null}
*/
const getDenyButton = () => (/** @type {HTMLButtonElement} */elementBySelector(`.${swalClasses.actions} .${swalClasses.deny}`));
/**
* @returns {HTMLElement | null}
*/
const getInputLabel = () => elementByClass(swalClasses['input-label']);
/**
* @returns {HTMLElement | null}
*/
const getLoader = () => elementBySelector(`.${swalClasses.loader}`);
/**
* @returns {HTMLElement | null}
*/
const getActions = () => elementByClass(swalClasses.actions);
/**
* @returns {HTMLElement | null}
*/
const getFooter = () => elementByClass(swalClasses.footer);
/**
* @returns {HTMLElement | null}
*/
const getTimerProgressBar = () => elementByClass(swalClasses['timer-progress-bar']);
/**
* @returns {HTMLElement | null}
*/
const getCloseButton = () => elementByClass(swalClasses.close);
// https://github.com/jkup/focusable/blob/master/index.js
const focusable = `
a[href],
area[href],
input:not([disabled]),
select:not([disabled]),
textarea:not([disabled]),
button:not([disabled]),
iframe,
object,
embed,
[tabindex="0"],
[contenteditable],
audio[controls],
video[controls],
summary
`;
/**
* @returns {HTMLElement[]}
*/
const getFocusableElements = () => {
const popup = getPopup();
if (!popup) {
return [];
}
/** @type {NodeListOf<HTMLElement>} */
const focusableElementsWithTabindex = popup.querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])');
const focusableElementsWithTabindexSorted = Array.from(focusableElementsWithTabindex)
// sort according to tabindex
.sort((a, b) => {
const tabindexA = parseInt(a.getAttribute('tabindex') || '0');
const tabindexB = parseInt(b.getAttribute('tabindex') || '0');
if (tabindexA > tabindexB) {
return 1;
} else if (tabindexA < tabindexB) {
return -1;
}
return 0;
});
/** @type {NodeListOf<HTMLElement>} */
const otherFocusableElements = popup.querySelectorAll(focusable);
const otherFocusableElementsFiltered = Array.from(otherFocusableElements).filter(el => el.getAttribute('tabindex') !== '-1');
return [...new Set(focusableElementsWithTabindexSorted.concat(otherFocusableElementsFiltered))].filter(el => isVisible$1(el));
};
/**
* @returns {boolean}
*/
const isModal = () => {
return hasClass(document.body, swalClasses.shown) && !hasClass(document.body, swalClasses['toast-shown']) && !hasClass(document.body, swalClasses['no-backdrop']);
};
/**
* @returns {boolean}
*/
const isToast = () => {
const popup = getPopup();
if (!popup) {
return false;
}
return hasClass(popup, swalClasses.toast);
};
/**
* @returns {boolean}
*/
const isLoading = () => {
const popup = getPopup();
if (!popup) {
return false;
}
return popup.hasAttribute('data-loading');
};
/**
* Securely set innerHTML of an element
* https://github.com/sweetalert2/sweetalert2/issues/1926
*
* @param {HTMLElement} elem
* @param {string} html
*/
const setInnerHtml = (elem, html) => {
elem.textContent = '';
if (html) {
const parser = new DOMParser();
const parsed = parser.parseFromString(html, `text/html`);
const head = parsed.querySelector('head');
if (head) {
Array.from(head.childNodes).forEach(child => {
elem.appendChild(child);
});
}
const body = parsed.querySelector('body');
if (body) {
Array.from(body.childNodes).forEach(child => {
if (child instanceof HTMLVideoElement || child instanceof HTMLAudioElement) {
elem.appendChild(child.cloneNode(true)); // https://github.com/sweetalert2/sweetalert2/issues/2507
} else {
elem.appendChild(child);
}
});
}
}
};
/**
* @param {HTMLElement} elem
* @param {string} className
* @returns {boolean}
*/
const hasClass = (elem, className) => {
if (!className) {
return false;
}
const classList = className.split(/\s+/);
for (let i = 0; i < classList.length; i++) {
if (!elem.classList.contains(classList[i])) {
return false;
}
}
return true;
};
/**
* @param {HTMLElement} elem
* @param {SweetAlertOptions} params
*/
const removeCustomClasses = (elem, params) => {
Array.from(elem.classList).forEach(className => {
if (!Object.values(swalClasses).includes(className) && !Object.values(iconTypes).includes(className) && !Object.values(params.showClass || {}).includes(className)) {
elem.classList.remove(className);
}
});
};
/**
* @param {HTMLElement} elem
* @param {SweetAlertOptions} params
* @param {string} className
*/
const applyCustomClass = (elem, params, className) => {
removeCustomClasses(elem, params);
if (!params.customClass) {
return;
}
const customClass = params.customClass[(/** @type {keyof SweetAlertCustomClass} */className)];
if (!customClass) {
return;
}
if (typeof customClass !== 'string' && !customClass.forEach) {
warn(`Invalid type of customClass.${className}! Expected string or iterable object, got "${typeof customClass}"`);
return;
}
addClass(elem, customClass);
};
/**
* @param {HTMLElement} popup
* @param {import('./renderers/renderInput').InputClass | SweetAlertInput} inputClass
* @returns {HTMLInputElement | null}
*/
const getInput$1 = (popup, inputClass) => {
if (!inputClass) {
return null;
}
switch (inputClass) {
case 'select':
case 'textarea':
case 'file':
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses[inputClass]}`);
case 'checkbox':
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.checkbox} input`);
case 'radio':
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:checked`) || popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:first-child`);
case 'range':
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.range} input`);
default:
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.input}`);
}
};
/**
* @param {HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement} input
*/
const focusInput = input => {
input.focus();
// place cursor at end of text in text input
if (input.type !== 'file') {
// http://stackoverflow.com/a/2345915
const val = input.value;
input.value = '';
input.value = val;
}
};
/**
* @param {HTMLElement | HTMLElement[] | null} target
* @param {string | string[] | readonly string[] | undefined} classList
* @param {boolean} condition
*/
const toggleClass = (target, classList, condition) => {
if (!target || !classList) {
return;
}
if (typeof classList === 'string') {
classList = classList.split(/\s+/).filter(Boolean);
}
classList.forEach(className => {
if (Array.isArray(target)) {
target.forEach(elem => {
if (condition) {
elem.classList.add(className);
} else {
elem.classList.remove(className);
}
});
} else {
if (condition) {
target.classList.add(className);
} else {
target.classList.remove(className);
}
}
});
};
/**
* @param {HTMLElement | HTMLElement[] | null} target
* @param {string | string[] | readonly string[] | undefined} classList
*/
const addClass = (target, classList) => {
toggleClass(target, classList, true);
};
/**
* @param {HTMLElement | HTMLElement[] | null} target
* @param {string | string[] | readonly string[] | undefined} classList
*/
const removeClass = (target, classList) => {
toggleClass(target, classList, false);
};
/**
* Get direct child of an element by class name
*
* @param {HTMLElement} elem
* @param {string} className
* @returns {HTMLElement | undefined}
*/
const getDirectChildByClass = (elem, className) => {
const children = Array.from(elem.children);
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child instanceof HTMLElement && hasClass(child, className)) {
return child;
}
}
};
/**
* @param {HTMLElement} elem
* @param {string} property
* @param {*} value
*/
const applyNumericalStyle = (elem, property, value) => {
if (value === `${parseInt(value)}`) {
value = parseInt(value);
}
if (value || parseInt(value) === 0) {
elem.style.setProperty(property, typeof value === 'number' ? `${value}px` : value);
} else {
elem.style.removeProperty(property);
}
};
/**
* @param {HTMLElement | null} elem
* @param {string} display
*/
const show = (elem, display = 'flex') => {
if (!elem) {
return;
}
elem.style.display = display;
};
/**
* @param {HTMLElement | null} elem
*/
const hide = elem => {
if (!elem) {
return;
}
elem.style.display = 'none';
};
/**
* @param {HTMLElement | null} elem
* @param {string} display
*/
const showWhenInnerHtmlPresent = (elem, display = 'block') => {
if (!elem) {
return;
}
new MutationObserver(() => {
toggle(elem, elem.innerHTML, display);
}).observe(elem, {
childList: true,
subtree: true
});
};
/**
* @param {HTMLElement} parent
* @param {string} selector
* @param {string} property
* @param {string} value
*/
const setStyle = (parent, selector, property, value) => {
/** @type {HTMLElement | null} */
const el = parent.querySelector(selector);
if (el) {
el.style.setProperty(property, value);
}
};
/**
* @param {HTMLElement} elem
* @param {any} condition
* @param {string} display
*/
const toggle = (elem, condition, display = 'flex') => {
if (condition) {
show(elem, display);
} else {
hide(elem);
}
};
/**
* borrowed from jquery $(elem).is(':visible') implementation
*
* @param {HTMLElement | null} elem
* @returns {boolean}
*/
const isVisible$1 = elem => !!(elem && (elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length));
/**
* @returns {boolean}
*/
const allButtonsAreHidden = () => !isVisible$1(getConfirmButton()) && !isVisible$1(getDenyButton()) && !isVisible$1(getCancelButton());
/**
* @param {HTMLElement} elem
* @returns {boolean}
*/
const isScrollable = elem => !!(elem.scrollHeight > elem.clientHeight);
/**
* @param {HTMLElement} element
* @param {HTMLElement} stopElement
* @returns {boolean}
*/
const selfOrParentIsScrollable = (element, stopElement) => {
let parent = element;
while (parent && parent !== stopElement) {
if (isScrollable(parent)) {
return true;
}
parent = parent.parentElement;
}
return false;
};
/**
* borrowed from https://stackoverflow.com/a/46352119
*
* @param {HTMLElement} elem
* @returns {boolean}
*/
const hasCssAnimation = elem => {
const style = window.getComputedStyle(elem);
const animDuration = parseFloat(style.getPropertyValue('animation-duration') || '0');
const transDuration = parseFloat(style.getPropertyValue('transition-duration') || '0');
return animDuration > 0 || transDuration > 0;
};
/**
* @param {number} timer
* @param {boolean} reset
*/
const animateTimerProgressBar = (timer, reset = false) => {
const timerProgressBar = getTimerProgressBar();
if (!timerProgressBar) {
return;
}
if (isVisible$1(timerProgressBar)) {
if (reset) {
timerProgressBar.style.transition = 'none';
timerProgressBar.style.width = '100%';
}
setTimeout(() => {
timerProgressBar.style.transition = `width ${timer / 1000}s linear`;
timerProgressBar.style.width = '0%';
}, 10);
}
};
const stopTimerProgressBar = () => {
const timerProgressBar = getTimerProgressBar();
if (!timerProgressBar) {
return;
}
const timerProgressBarWidth = parseInt(window.getComputedStyle(timerProgressBar).width);
timerProgressBar.style.removeProperty('transition');
timerProgressBar.style.width = '100%';
const timerProgressBarFullWidth = parseInt(window.getComputedStyle(timerProgressBar).width);
const timerProgressBarPercent = timerProgressBarWidth / timerProgressBarFullWidth * 100;
timerProgressBar.style.width = `${timerProgressBarPercent}%`;
};
/**
* Detect Node env
*
* @returns {boolean}
*/
const isNodeEnv = () => typeof window === 'undefined' || typeof document === 'undefined';
const sweetHTML = `
<div aria-labelledby="${swalClasses.title}" aria-describedby="${swalClasses['html-container']}" class="${swalClasses.popup}" tabindex="-1">
<button type="button" class="${swalClasses.close}"></button>
<ul class="${swalClasses['progress-steps']}"></ul>
<div class="${swalClasses.icon}"></div>
<img class="${swalClasses.image}" />
<h2 class="${swalClasses.title}" id="${swalClasses.title}"></h2>
<div class="${swalClasses['html-container']}" id="${swalClasses['html-container']}"></div>
<input class="${swalClasses.input}" id="${swalClasses.input}" />
<input type="file" class="${swalClasses.file}" />
<div class="${swalClasses.range}">
<input type="range" />
<output></output>
</div>
<select class="${swalClasses.select}" id="${swalClasses.select}"></select>
<div class="${swalClasses.radio}"></div>
<label class="${swalClasses.checkbox}">
<input type="checkbox" id="${swalClasses.checkbox}" />
<span class="${swalClasses.label}"></span>
</label>
<textarea class="${swalClasses.textarea}" id="${swalClasses.textarea}"></textarea>
<div class="${swalClasses['validation-message']}" id="${swalClasses['validation-message']}"></div>
<div class="${swalClasses.actions}">
<div class="${swalClasses.loader}"></div>
<button type="button" class="${swalClasses.confirm}"></button>
<button type="button" class="${swalClasses.deny}"></button>
<button type="button" class="${swalClasses.cancel}"></button>
</div>
<div class="${swalClasses.footer}"></div>
<div class="${swalClasses['timer-progress-bar-container']}">
<div class="${swalClasses['timer-progress-bar']}"></div>
</div>
</div>
`.replace(/(^|\n)\s*/g, '');
/**
* @returns {boolean}
*/
const resetOldContainer = () => {
const oldContainer = getContainer();
if (!oldContainer) {
return false;
}
oldContainer.remove();
removeClass([document.documentElement, document.body], [swalClasses['no-backdrop'], swalClasses['toast-shown'], swalClasses['has-column']]);
return true;
};
const resetValidationMessage$1 = () => {
globalState.currentInstance.resetValidationMessage();
};
const addInputChangeListeners = () => {
const popup = getPopup();
const input = getDirectChildByClass(popup, swalClasses.input);
const file = getDirectChildByClass(popup, swalClasses.file);
/** @type {HTMLInputElement} */
const range = popup.querySelector(`.${swalClasses.range} input`);
/** @type {HTMLOutputElement} */
const rangeOutput = popup.querySelector(`.${swalClasses.range} output`);
const select = getDirectChildByClass(popup, swalClasses.select);
/** @type {HTMLInputElement} */
const checkbox = popup.querySelector(`.${swalClasses.checkbox} input`);
const textarea = getDirectChildByClass(popup, swalClasses.textarea);
input.oninput = resetValidationMessage$1;
file.onchange = resetValidationMessage$1;
select.onchange = resetValidationMessage$1;
checkbox.onchange = resetValidationMessage$1;
textarea.oninput = resetValidationMessage$1;
range.oninput = () => {
resetValidationMessage$1();
rangeOutput.value = range.value;
};
range.onchange = () => {
resetValidationMessage$1();
rangeOutput.value = range.value;
};
};
/**
* @param {string | HTMLElement} target
* @returns {HTMLElement}
*/
const getTarget = target => typeof target === 'string' ? document.querySelector(target) : target;
/**
* @param {SweetAlertOptions} params
*/
const setupAccessibility = params => {
const popup = getPopup();
popup.setAttribute('role', params.toast ? 'alert' : 'dialog');
popup.setAttribute('aria-live', params.toast ? 'polite' : 'assertive');
if (!params.toast) {
popup.setAttribute('aria-modal', 'true');
}
};
/**
* @param {HTMLElement} targetElement
*/
const setupRTL = targetElement => {
if (window.getComputedStyle(targetElement).direction === 'rtl') {
addClass(getContainer(), swalClasses.rtl);
}
};
/**
* Add modal + backdrop + no-war message for Russians to DOM
*
* @param {SweetAlertOptions} params
*/
const init = params => {
// Clean up the old popup container if it exists
const oldContainerExisted = resetOldContainer();
if (isNodeEnv()) {
error('SweetAlert2 requires document to initialize');
return;
}
const container = document.createElement('div');
container.className = swalClasses.container;
if (oldContainerExisted) {
addClass(container, swalClasses['no-transition']);
}
setInnerHtml(container, sweetHTML);
container.dataset['swal2Theme'] = params.theme;
const targetElement = getTarget(params.target);
targetElement.appendChild(container);
if (params.topLayer) {
container.setAttribute('popover', '');
container.showPopover();
}
setupAccessibility(params);
setupRTL(targetElement);
addInputChangeListeners();
};
/**
* @param {HTMLElement | object | string} param
* @param {HTMLElement} target
*/
const parseHtmlToContainer = (param, target) => {
// DOM element
if (param instanceof HTMLElement) {
target.appendChild(param);
}
// Object
else if (typeof param === 'object') {
handleObject(param, target);
}
// Plain string
else if (param) {
setInnerHtml(target, param);
}
};
/**
* @param {any} param
* @param {HTMLElement} target
*/
const handleObject = (param, target) => {
// JQuery element(s)
if (param.jquery) {
handleJqueryElem(target, param);
}
// For other objects use their string representation
else {
setInnerHtml(target, param.toString());
}
};
/**
* @param {HTMLElement} target
* @param {any} elem
*/
const handleJqueryElem = (target, elem) => {
target.textContent = '';
if (0 in elem) {
for (let i = 0; i in elem; i++) {
target.appendChild(elem[i].cloneNode(true));
}
} else {
target.appendChild(elem.cloneNode(true));
}
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderActions = (instance, params) => {
const actions = getActions();
const loader = getLoader();
if (!actions || !loader) {
return;
}
// Actions (buttons) wrapper
if (!params.showConfirmButton && !params.showDenyButton && !params.showCancelButton) {
hide(actions);
} else {
show(actions);
}
// Custom class
applyCustomClass(actions, params, 'actions');
// Render all the buttons
renderButtons(actions, loader, params);
// Loader
setInnerHtml(loader, params.loaderHtml || '');
applyCustomClass(loader, params, 'loader');
};
/**
* @param {HTMLElement} actions
* @param {HTMLElement} loader
* @param {SweetAlertOptions} params
*/
function renderButtons(actions, loader, params) {
const confirmButton = getConfirmButton();
const denyButton = getDenyButton();
const cancelButton = getCancelButton();
if (!confirmButton || !denyButton || !cancelButton) {
return;
}
// Render buttons
renderButton(confirmButton, 'confirm', params);
renderButton(denyButton, 'deny', params);
renderButton(cancelButton, 'cancel', params);
handleButtonsStyling(confirmButton, denyButton, cancelButton, params);
if (params.reverseButtons) {
if (params.toast) {
actions.insertBefore(cancelButton, confirmButton);
actions.insertBefore(denyButton, confirmButton);
} else {
actions.insertBefore(cancelButton, loader);
actions.insertBefore(denyButton, loader);
actions.insertBefore(confirmButton, loader);
}
}
}
/**
* @param {HTMLElement} confirmButton
* @param {HTMLElement} denyButton
* @param {HTMLElement} cancelButton
* @param {SweetAlertOptions} params
*/
function handleButtonsStyling(confirmButton, denyButton, cancelButton, params) {
if (!params.buttonsStyling) {
removeClass([confirmButton, denyButton, cancelButton], swalClasses.styled);
return;
}
addClass([confirmButton, denyButton, cancelButton], swalClasses.styled);
// Apply custom background colors to action buttons
if (params.confirmButtonColor) {
confirmButton.style.setProperty('--swal2-confirm-button-background-color', params.confirmButtonColor);
}
if (params.denyButtonColor) {
denyButton.style.setProperty('--swal2-deny-button-background-color', params.denyButtonColor);
}
if (params.cancelButtonColor) {
cancelButton.style.setProperty('--swal2-cancel-button-background-color', params.cancelButtonColor);
}
// Apply the outline color to action buttons
applyOutlineColor(confirmButton);
applyOutlineColor(denyButton);
applyOutlineColor(cancelButton);
}
/**
* @param {HTMLElement} button
*/
function applyOutlineColor(button) {
const buttonStyle = window.getComputedStyle(button);
if (buttonStyle.getPropertyValue('--swal2-action-button-focus-box-shadow')) {
// If the button already has a custom outline color, no need to change it
return;
}
const outlineColor = buttonStyle.backgroundColor.replace(/rgba?\((\d+), (\d+), (\d+).*/, 'rgba($1, $2, $3, 0.5)');
button.style.setProperty('--swal2-action-button-focus-box-shadow', buttonStyle.getPropertyValue('--swal2-outline').replace(/ rgba\(.*/, ` ${outlineColor}`));
}
/**
* @param {HTMLElement} button
* @param {'confirm' | 'deny' | 'cancel'} buttonType
* @param {SweetAlertOptions} params
*/
function renderButton(button, buttonType, params) {
const buttonName = /** @type {'Confirm' | 'Deny' | 'Cancel'} */capitalizeFirstLetter(buttonType);
toggle(button, params[`show${buttonName}Button`], 'inline-block');
setInnerHtml(button, params[`${buttonType}ButtonText`] || ''); // Set caption text
button.setAttribute('aria-label', params[`${buttonType}ButtonAriaLabel`] || ''); // ARIA label
// Add buttons custom classes
button.className = swalClasses[buttonType];
applyCustomClass(button, params, `${buttonType}Button`);
}
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderCloseButton = (instance, params) => {
const closeButton = getCloseButton();
if (!closeButton) {
return;
}
setInnerHtml(closeButton, params.closeButtonHtml || '');
// Custom class
applyCustomClass(closeButton, params, 'closeButton');
toggle(closeButton, params.showCloseButton);
closeButton.setAttribute('aria-label', params.closeButtonAriaLabel || '');
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderContainer = (instance, params) => {
const container = getContainer();
if (!container) {
return;
}
handleBackdropParam(container, params.backdrop);
handlePositionParam(container, params.position);
handleGrowParam(container, params.grow);
// Custom class
applyCustomClass(container, params, 'container');
};
/**
* @param {HTMLElement} container
* @param {SweetAlertOptions['backdrop']} backdrop
*/
function handleBackdropParam(container, backdrop) {
if (typeof backdrop === 'string') {
container.style.background = backdrop;
} else if (!backdrop) {
addClass([document.documentElement, document.body], swalClasses['no-backdrop']);
}
}
/**
* @param {HTMLElement} container
* @param {SweetAlertOptions['position']} position
*/
function handlePositionParam(container, position) {
if (!position) {
return;
}
if (position in swalClasses) {
addClass(container, swalClasses[position]);
} else {
warn('The "position" parameter is not valid, defaulting to "center"');
addClass(container, swalClasses.center);
}
}
/**
* @param {HTMLElement} container
* @param {SweetAlertOptions['grow']} grow
*/
function handleGrowParam(container, grow) {
if (!grow) {
return;
}
addClass(container, swalClasses[`grow-${grow}`]);
}
/**
* This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
* For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
* This is the approach that Babel will probably take to implement private methods/fields
* https://github.com/tc39/proposal-private-methods
* https://github.com/babel/babel/pull/7555
* Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
* then we can use that language feature.
*/
var privateProps = {
innerParams: new WeakMap(),
domCache: new WeakMap()
};
/// <reference path="../../../../sweetalert2.d.ts"/>
/** @type {InputClass[]} */
const inputClasses = ['input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea'];
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderInput = (instance, params) => {
const popup = getPopup();
if (!popup) {
return;
}
const innerParams = privateProps.innerParams.get(instance);
const rerender = !innerParams || params.input !== innerParams.input;
inputClasses.forEach(inputClass => {
const inputContainer = getDirectChildByClass(popup, swalClasses[inputClass]);
if (!inputContainer) {
return;
}
// set attributes
setAttributes(inputClass, params.inputAttributes);
// set class
inputContainer.className = swalClasses[inputClass];
if (rerender) {
hide(inputContainer);
}
});
if (params.input) {
if (rerender) {
showInput(params);
}
// set custom class
setCustomClass(params);
}
};
/**
* @param {SweetAlertOptions} params
*/
const showInput = params => {
if (!params.input) {
return;
}
if (!renderInputType[params.input]) {
error(`Unexpected type of input! Expected ${Object.keys(renderInputType).join(' | ')}, got "${params.input}"`);
return;
}
const inputContainer = getInputContainer(params.input);
if (!inputContainer) {
return;
}
const input = renderInputType[params.input](inputContainer, params);
show(inputContainer);
// input autofocus
if (params.inputAutoFocus) {
setTimeout(() => {
focusInput(input);
});
}
};
/**
* @param {HTMLInputElement} input
*/
const removeAttributes = input => {
for (let i = 0; i < input.attributes.length; i++) {
const attrName = input.attributes[i].name;
if (!['id', 'type', 'value', 'style'].includes(attrName)) {
input.removeAttribute(attrName);
}
}
};
/**
* @param {InputClass} inputClass
* @param {SweetAlertOptions['inputAttributes']} inputAttributes
*/
const setAttributes = (inputClass, inputAttributes) => {
const popup = getPopup();
if (!popup) {
return;
}
const input = getInput$1(popup, inputClass);
if (!input) {
return;
}
removeAttributes(input);
for (const attr in inputAttributes) {
input.setAttribute(attr, inputAttributes[attr]);
}
};
/**
* @param {SweetAlertOptions} params
*/
const setCustomClass = params => {
if (!params.input) {
return;
}
const inputContainer = getInputContainer(params.input);
if (inputContainer) {
applyCustomClass(inputContainer, params, 'input');
}
};
/**
* @param {HTMLInputElement | HTMLTextAreaElement} input
* @param {SweetAlertOptions} params
*/
const setInputPlaceholder = (input, params) => {
if (!input.placeholder && params.inputPlaceholder) {
input.placeholder = params.inputPlaceholder;
}
};
/**
* @param {Input} input
* @param {Input} prependTo
* @param {SweetAlertOptions} params
*/
const setInputLabel = (input, prependTo, params) => {
if (params.inputLabel) {
const label = document.createElement('label');
const labelClass = swalClasses['input-label'];
label.setAttribute('for', input.id);
label.className = labelClass;
if (typeof params.customClass === 'object') {
addClass(label, params.customClass.inputLabel);
}
label.innerText = params.inputLabel;
prependTo.insertAdjacentElement('beforebegin', label);
}
};
/**
* @param {SweetAlertInput} inputType
* @returns {HTMLElement | undefined}
*/
const getInputContainer = inputType => {
const popup = getPopup();
if (!popup) {
return;
}
return getDirectChildByClass(popup, swalClasses[(/** @type {SwalClass} */inputType)] || swalClasses.input);
};
/**
* @param {HTMLInputElement | HTMLOutputElement | HTMLTextAreaElement} input
* @param {SweetAlertOptions['inputValue']} inputValue
*/
const checkAndSetInputValue = (input, inputValue) => {
if (['string', 'number'].includes(typeof inputValue)) {
input.value = `${inputValue}`;
} else if (!isPromise(inputValue)) {
warn(`Unexpected type of inputValue! Expected "string", "number" or "Promise", got "${typeof inputValue}"`);
}
};
/** @type {Record<SweetAlertInput, (input: Input | HTMLElement, params: SweetAlertOptions) => Input>} */
const renderInputType = {};
/**
* @param {HTMLInputElement} input
* @param {SweetAlertOptions} params
* @returns {HTMLInputElement}
*/
renderInputType.text = renderInputType.email = renderInputType.password = renderInputType.number = renderInputType.tel = renderInputType.url = renderInputType.search = renderInputType.date = renderInputType['datetime-local'] = renderInputType.time = renderInputType.week = renderInputType.month = /** @type {(input: Input | HTMLElement, params: SweetAlertOptions) => Input} */
(input, params) => {
checkAndSetInputValue(input, params.inputValue);
setInputLabel(input, input, params);
setInputPlaceholder(input, params);
input.type = params.input;
return input;
};
/**
* @param {HTMLInputElement} input
* @param {SweetAlertOptions} params
* @returns {HTMLInputElement}
*/
renderInputType.file = (input, params) => {
setInputLabel(input, input, params);
setInputPlaceholder(input, params);
return input;
};
/**
* @param {HTMLInputElement} range
* @param {SweetAlertOptions} params
* @returns {HTMLInputElement}
*/
renderInputType.range = (range, params) => {
const rangeInput = range.querySelector('input');
const rangeOutput = range.querySelector('output');
checkAndSetInputValue(rangeInput, params.inputValue);
rangeInput.type = params.input;
checkAndSetInputValue(rangeOutput, params.inputValue);
setInputLabel(rangeInput, range, params);
return range;
};
/**
* @param {HTMLSelectElement} select
* @param {SweetAlertOptions} params
* @returns {HTMLSelectElement}
*/
renderInputType.select = (select, params) => {
select.textContent = '';
if (params.inputPlaceholder) {
const placeholder = document.createElement('option');
setInnerHtml(placeholder, params.inputPlaceholder);
placeholder.value = '';
placeholder.disabled = true;
placeholder.selected = true;
select.appendChild(placeholder);
}
setInputLabel(select, select, params);
return select;
};
/**
* @param {HTMLInputElement} radio
* @returns {HTMLInputElement}
*/
renderInputType.radio = radio => {
radio.textContent = '';
return radio;
};
/**
* @param {HTMLLabelElement} checkboxContainer
* @param {SweetAlertOptions} params
* @returns {HTMLInputElement}
*/
renderInputType.checkbox = (checkboxContainer, params) => {
const checkbox = getInput$1(getPopup(), 'checkbox');
checkbox.value = '1';
checkbox.checked = Boolean(params.inputValue);
const label = checkboxContainer.querySelector('span');
setInnerHtml(label, params.inputPlaceholder || params.inputLabel);
return checkbox;
};
/**
* @param {HTMLTextAreaElement} textarea
* @param {SweetAlertOptions} params
* @returns {HTMLTextAreaElement}
*/
renderInputType.textarea = (textarea, params) => {
checkAndSetInputValue(textarea, params.inputValue);
setInputPlaceholder(textarea, params);
setInputLabel(textarea, textarea, params);
/**
* @param {HTMLElement} el
* @returns {number}
*/
const getMargin = el => parseInt(window.getComputedStyle(el).marginLeft) + parseInt(window.getComputedStyle(el).marginRight);
// https://github.com/sweetalert2/sweetalert2/issues/2291
setTimeout(() => {
// https://github.com/sweetalert2/sweetalert2/issues/1699
if ('MutationObserver' in window) {
const initialPopupWidth = parseInt(window.getComputedStyle(getPopup()).width);
const textareaResizeHandler = () => {
// check if texarea is still in document (i.e. popup wasn't closed in the meantime)
if (!document.body.contains(textarea)) {
return;
}
const textareaWidth = textarea.offsetWidth + getMargin(textarea);
if (textareaWidth > initialPopupWidth) {
getPopup().style.width = `${textareaWidth}px`;
} else {
applyNumericalStyle(getPopup(), 'width', params.width);
}
};
new MutationObserver(textareaResizeHandler).observe(textarea, {
attributes: true,
attributeFilter: ['style']
});
}
});
return textarea;
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderContent = (instance, params) => {
const htmlContainer = getHtmlContainer();
if (!htmlContainer) {
return;
}
showWhenInnerHtmlPresent(htmlContainer);
applyCustomClass(htmlContainer, params, 'htmlContainer');
// Content as HTML
if (params.html) {
parseHtmlToContainer(params.html, htmlContainer);
show(htmlContainer, 'block');
}
// Content as plain text
else if (params.text) {
htmlContainer.textContent = params.text;
show(htmlContainer, 'block');
}
// No content
else {
hide(htmlContainer);
}
renderInput(instance, params);
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderFooter = (instance, params) => {
const footer = getFooter();
if (!footer) {
return;
}
showWhenInnerHtmlPresent(footer);
toggle(footer, params.footer, 'block');
if (params.footer) {
parseHtmlToContainer(params.footer, footer);
}
// Custom class
applyCustomClass(footer, params, 'footer');
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderIcon = (instance, params) => {
const innerParams = privateProps.innerParams.get(instance);
const icon = getIcon();
if (!icon) {
return;
}
// if the given icon already rendered, apply the styling without re-rendering the icon
if (innerParams && params.icon === innerParams.icon) {
// Custom or default content
setContent(icon, params);
applyStyles(icon, params);
return;
}
if (!params.icon && !params.iconHtml) {
hide(icon);
return;
}
if (params.icon && Object.keys(iconTypes).indexOf(params.icon) === -1) {
error(`Unknown icon! Expected "success", "error", "warning", "info" or "question", got "${params.icon}"`);
hide(icon);
return;
}
show(icon);
// Custom or default content
setContent(icon, params);
applyStyles(icon, params);
// Animate icon
addClass(icon, params.showClass && params.showClass.icon);
// Re-adjust the success icon on system theme change
const colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
colorSchemeQueryList.addEventListener('change', adjustSuccessIconBackgroundColor);
};
/**
* @param {HTMLElement} icon
* @param {SweetAlertOptions} params
*/
const applyStyles = (icon, params) => {
for (const [iconType, iconClassName] of Object.entries(iconTypes)) {
if (params.icon !== iconType) {
removeClass(icon, iconClassName);
}
}
addClass(icon, params.icon && iconTypes[params.icon]);
// Icon color
setColor(icon, params);
// Success icon background color
adjustSuccessIconBackgroundColor();
// Custom class
applyCustomClass(icon, params, 'icon');
};
// Adjust success icon background color to match the popup background color
const adjustSuccessIconBackgroundColor = () => {
const popup = getPopup();
if (!popup) {
return;
}
const popupBackgroundColor = window.getComputedStyle(popup).getPropertyValue('background-color');
/** @type {NodeListOf<HTMLElement>} */
const successIconParts = popup.querySelectorAll('[class^=swal2-success-circular-line], .swal2-success-fix');
for (let i = 0; i < successIconParts.length; i++) {
successIconParts[i].style.backgroundColor = popupBackgroundColor;
}
};
/**
*
* @param {SweetAlertOptions} params
* @returns {string}
*/
const successIconHtml = params => `
${params.animation ? '<div class="swal2-success-circular-line-left"></div>' : ''}
<span class="swal2-success-line-tip"></span> <span class="swal2-success-line-long"></span>
<div class="swal2-success-ring"></div>
${params.animation ? '<div class="swal2-success-fix"></div>' : ''}
${params.animation ? '<div class="swal2-success-circular-line-right"></div>' : ''}
`;
const errorIconHtml = `
<span class="swal2-x-mark">
<span class="swal2-x-mark-line-left"></span>
<span class="swal2-x-mark-line-right"></span>
</span>
`;
/**
* @param {HTMLElement} icon
* @param {SweetAlertOptions} params
*/
const setContent = (icon, params) => {
if (!params.icon && !params.iconHtml) {
return;
}
let oldContent = icon.innerHTML;
let newContent = '';
if (params.iconHtml) {
newContent = iconContent(params.iconHtml);
} else if (params.icon === 'success') {
newContent = successIconHtml(params);
oldContent = oldContent.replace(/ style=".*?"/g, ''); // undo adjustSuccessIconBackgroundColor()
} else if (params.icon === 'error') {
newContent = errorIconHtml;
} else if (params.icon) {
const defaultIconHtml = {
question: '?',
warning: '!',
info: 'i'
};
newContent = iconContent(defaultIconHtml[params.icon]);
}
if (oldContent.trim() !== newContent.trim()) {
setInnerHtml(icon, newContent);
}
};
/**
* @param {HTMLElement} icon
* @param {SweetAlertOptions} params
*/
const setColor = (icon, params) => {
if (!params.iconColor) {
return;
}
icon.style.color = params.iconColor;
icon.style.borderColor = params.iconColor;
for (const sel of ['.swal2-success-line-tip', '.swal2-success-line-long', '.swal2-x-mark-line-left', '.swal2-x-mark-line-right']) {
setStyle(icon, sel, 'background-color', params.iconColor);
}
setStyle(icon, '.swal2-success-ring', 'border-color', params.iconColor);
};
/**
* @param {string} content
* @returns {string}
*/
const iconContent = content => `<div class="${swalClasses['icon-content']}">${content}</div>`;
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderImage = (instance, params) => {
const image = getImage();
if (!image) {
return;
}
if (!params.imageUrl) {
hide(image);
return;
}
show(image, '');
// Src, alt
image.setAttribute('src', params.imageUrl);
image.setAttribute('alt', params.imageAlt || '');
// Width, height
applyNumericalStyle(image, 'width', params.imageWidth);
applyNumericalStyle(image, 'height', params.imageHeight);
// Class
image.className = swalClasses.image;
applyCustomClass(image, params, 'image');
};
let dragging = false;
let mousedownX = 0;
let mousedownY = 0;
let initialX = 0;
let initialY = 0;
/**
* @param {HTMLElement} popup
*/
const addDraggableListeners = popup => {
popup.addEventListener('mousedown', down);
document.body.addEventListener('mousemove', move);
popup.addEventListener('mouseup', up);
popup.addEventListener('touchstart', down);
document.body.addEventListener('touchmove', move);
popup.addEventListener('touchend', up);
};
/**
* @param {HTMLElement} popup
*/
const removeDraggableListeners = popup => {
popup.removeEventListener('mousedown', down);
document.body.removeEventListener('mousemove', move);
popup.removeEventListener('mouseup', up);
popup.removeEventListener('touchstart', down);
document.body.removeEventListener('touchmove', move);
popup.removeEventListener('touchend', up);
};
/**
* @param {MouseEvent | TouchEvent} event
*/
const down = event => {
const popup = getPopup();
if (event.target === popup || getIcon().contains(/** @type {HTMLElement} */event.target)) {
dragging = true;
const clientXY = getClientXY(event);
mousedownX = clientXY.clientX;
mousedownY = clientXY.clientY;
initialX = parseInt(popup.style.insetInlineStart) || 0;
initialY = parseInt(popup.style.insetBlockStart) || 0;
addClass(popup, 'swal2-dragging');
}
};
/**
* @param {MouseEvent | TouchEvent} event
*/
const move = event => {
const popup = getPopup();
if (dragging) {
let {
clientX,
clientY
} = getClientXY(event);
popup.style.insetInlineStart = `${initialX + (clientX - mousedownX)}px`;
popup.style.insetBlockStart = `${initialY + (clientY - mousedownY)}px`;
}
};
const up = () => {
const popup = getPopup();
dragging = false;
removeClass(popup, 'swal2-dragging');
};
/**
* @param {MouseEvent | TouchEvent} event
* @returns {{ clientX: number, clientY: number }}
*/
const getClientXY = event => {
let clientX = 0,
clientY = 0;
if (event.type.startsWith('mouse')) {
clientX = /** @type {MouseEvent} */event.clientX;
clientY = /** @type {MouseEvent} */event.clientY;
} else if (event.type.startsWith('touch')) {
clientX = /** @type {TouchEvent} */event.touches[0].clientX;
clientY = /** @type {TouchEvent} */event.touches[0].clientY;
}
return {
clientX,
clientY
};
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderPopup = (instance, params) => {
const container = getContainer();
const popup = getPopup();
if (!container || !popup) {
return;
}
// Width
// https://github.com/sweetalert2/sweetalert2/issues/2170
if (params.toast) {
applyNumericalStyle(container, 'width', params.width);
popup.style.width = '100%';
const loader = getLoader();
if (loader) {
popup.insertBefore(loader, getIcon());
}
} else {
applyNumericalStyle(popup, 'width', params.width);
}
// Padding
applyNumericalStyle(popup, 'padding', params.padding);
// Color
if (params.color) {
popup.style.color = params.color;
}
// Background
if (params.background) {
popup.style.background = params.background;
}
hide(getValidationMessage());
// Classes
addClasses$1(popup, params);
if (params.draggable && !params.toast) {
addClass(popup, swalClasses.draggable);
addDraggableListeners(popup);
} else {
removeClass(popup, swalClasses.draggable);
removeDraggableListeners(popup);
}
};
/**
* @param {HTMLElement} popup
* @param {SweetAlertOptions} params
*/
const addClasses$1 = (popup, params) => {
const showClass = params.showClass || {};
// Default Class + showClass when updating Swal.update({})
popup.className = `${swalClasses.popup} ${isVisible$1(popup) ? showClass.popup : ''}`;
if (params.toast) {
addClass([document.documentElement, document.body], swalClasses['toast-shown']);
addClass(popup, swalClasses.toast);
} else {
addClass(popup, swalClasses.modal);
}
// Custom class
applyCustomClass(popup, params, 'popup');
// TODO: remove in the next major
if (typeof params.customClass === 'string') {
addClass(popup, params.customClass);
}
// Icon class (#1842)
if (params.icon) {
addClass(popup, swalClasses[`icon-${params.icon}`]);
}
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderProgressSteps = (instance, params) => {
const progressStepsContainer = getProgressSteps();
if (!progressStepsContainer) {
return;
}
const {
progressSteps,
currentProgressStep
} = params;
if (!progressSteps || progressSteps.length === 0 || currentProgressStep === undefined) {
hide(progressStepsContainer);
return;
}
show(progressStepsContainer);
progressStepsContainer.textContent = '';
if (currentProgressStep >= progressSteps.length) {
warn('Invalid currentProgressStep parameter, it should be less than progressSteps.length ' + '(currentProgressStep like JS arrays starts from 0)');
}
progressSteps.forEach((step, index) => {
const stepEl = createStepElement(step);
progressStepsContainer.appendChild(stepEl);
if (index === currentProgressStep) {
addClass(stepEl, swalClasses['active-progress-step']);
}
if (index !== progressSteps.length - 1) {
const lineEl = createLineElement(params);
progressStepsContainer.appendChild(lineEl);
}
});
};
/**
* @param {string} step
* @returns {HTMLLIElement}
*/
const createStepElement = step => {
const stepEl = document.createElement('li');
addClass(stepEl, swalClasses['progress-step']);
setInnerHtml(stepEl, step);
return stepEl;
};
/**
* @param {SweetAlertOptions} params
* @returns {HTMLLIElement}
*/
const createLineElement = params => {
const lineEl = document.createElement('li');
addClass(lineEl, swalClasses['progress-step-line']);
if (params.progressStepsDistance) {
applyNumericalStyle(lineEl, 'width', params.progressStepsDistance);
}
return lineEl;
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const renderTitle = (instance, params) => {
const title = getTitle();
if (!title) {
return;
}
showWhenInnerHtmlPresent(title);
toggle(title, params.title || params.titleText, 'block');
if (params.title) {
parseHtmlToContainer(params.title, title);
}
if (params.titleText) {
title.innerText = params.titleText;
}
// Custom class
applyCustomClass(title, params, 'title');
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const render = (instance, params) => {
renderPopup(instance, params);
renderContainer(instance, params);
renderProgressSteps(instance, params);
renderIcon(instance, params);
renderImage(instance, params);
renderTitle(instance, params);
renderCloseButton(instance, params);
renderContent(instance, params);
renderActions(instance, params);
renderFooter(instance, params);
const popup = getPopup();
if (typeof params.didRender === 'function' && popup) {
params.didRender(popup);
}
globalState.eventEmitter.emit('didRender', popup);
};
/*
* Global function to determine if SweetAlert2 popup is shown
*/
const isVisible = () => {
return isVisible$1(getPopup());
};
/*
* Global function to click 'Confirm' button
*/
const clickConfirm = () => {
var _dom$getConfirmButton;
return (_dom$getConfirmButton = getConfirmButton()) === null || _dom$getConfirmButton === void 0 ? void 0 : _dom$getConfirmButton.click();
};
/*
* Global function to click 'Deny' button
*/
const clickDeny = () => {
var _dom$getDenyButton;
return (_dom$getDenyButton = getDenyButton()) === null || _dom$getDenyButton === void 0 ? void 0 : _dom$getDenyButton.click();
};
/*
* Global function to click 'Cancel' button
*/
const clickCancel = () => {
var _dom$getCancelButton;
return (_dom$getCancelButton = getCancelButton()) === null || _dom$getCancelButton === void 0 ? void 0 : _dom$getCancelButton.click();
};
/** @typedef {'cancel' | 'backdrop' | 'close' | 'esc' | 'timer'} DismissReason */
/** @type {Record<DismissReason, DismissReason>} */
const DismissReason = Object.freeze({
cancel: 'cancel',
backdrop: 'backdrop',
close: 'close',
esc: 'esc',
timer: 'timer'
});
/**
* @param {GlobalState} globalState
*/
const removeKeydownHandler = globalState => {
if (globalState.keydownTarget && globalState.keydownHandlerAdded) {
globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
capture: globalState.keydownListenerCapture
});
globalState.keydownHandlerAdded = false;
}
};
/**
* @param {GlobalState} globalState
* @param {SweetAlertOptions} innerParams
* @param {*} dismissWith
*/
const addKeydownHandler = (globalState, innerParams, dismissWith) => {
removeKeydownHandler(globalState);
if (!innerParams.toast) {
globalState.keydownHandler = e => keydownHandler(innerParams, e, dismissWith);
globalState.keydownTarget = innerParams.keydownListenerCapture ? window : getPopup();
globalState.keydownListenerCapture = innerParams.keydownListenerCapture;
globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, {
capture: globalState.keydownListenerCapture
});
globalState.keydownHandlerAdded = true;
}
};
/**
* @param {number} index
* @param {number} increment
*/
const setFocus = (index, increment) => {
var _dom$getPopup;
const focusableElements = getFocusableElements();
// search for visible elements and select the next possible match
if (focusableElements.length) {
index = index + increment;
// shift + tab when .swal2-popup is focused
if (index === -2) {
index = focusableElements.length - 1;
}
// rollover to first item
if (index === focusableElements.length) {
index = 0;
// go to last item
} else if (index === -1) {
index = focusableElements.length - 1;
}
focusableElements[index].focus();
return;
}
// no visible focusable elements, focus the popup
(_dom$getPopup = getPopup()) === null || _dom$getPopup === void 0 || _dom$getPopup.focus();
};
const arrowKeysNextButton = ['ArrowRight', 'ArrowDown'];
const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp'];
/**
* @param {SweetAlertOptions} innerParams
* @param {KeyboardEvent} event
* @param {Function} dismissWith
*/
const keydownHandler = (innerParams, event, dismissWith) => {
if (!innerParams) {
return; // This instance has already been destroyed
}
// Ignore keydown during IME composition
// https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event#ignoring_keydown_during_ime_composition
// https://github.com/sweetalert2/sweetalert2/issues/720
// https://github.com/sweetalert2/sweetalert2/issues/2406
if (event.isComposing || event.keyCode === 229) {
return;
}
if (innerParams.stopKeydownPropagation) {
event.stopPropagation();
}
// ENTER
if (event.key === 'Enter') {
handleEnter(event, innerParams);
}
// TAB
else if (event.key === 'Tab') {
handleTab(event);
}
// ARROWS - switch focus between buttons
else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(event.key)) {
handleArrows(event.key);
}
// ESC
else if (event.key === 'Escape') {
handleEsc(event, innerParams, dismissWith);
}
};
/**
* @param {KeyboardEvent} event
* @param {SweetAlertOptions} innerParams
*/
const handleEnter = (event, innerParams) => {
// https://github.com/sweetalert2/sweetalert2/issues/2386
if (!callIfFunction(innerParams.allowEnterKey)) {
return;
}
const input = getInput$1(getPopup(), innerParams.input);
if (event.target && input && event.target instanceof HTMLElement && event.target.outerHTML === input.outerHTML) {
if (['textarea', 'file'].includes(innerParams.input)) {
return; // do not submit
}
clickConfirm();
event.preventDefault();
}
};
/**
* @param {KeyboardEvent} event
*/
const handleTab = event => {
const targetElement = event.target;
const focusableElements = getFocusableElements();
let btnIndex = -1;
for (let i = 0; i < focusableElements.length; i++) {
if (targetElement === focusableElements[i]) {
btnIndex = i;
break;
}
}
// Cycle to the next button
if (!event.shiftKey) {
setFocus(btnIndex, 1);
}
// Cycle to the prev button
else {
setFocus(btnIndex, -1);
}
event.stopPropagation();
event.preventDefault();
};
/**
* @param {string} key
*/
const handleArrows = key => {
const actions = getActions();
const confirmButton = getConfirmButton();
const denyButton = getDenyButton();
const cancelButton = getCancelButton();
if (!actions || !confirmButton || !denyButton || !cancelButton) {
return;
}
/** @type HTMLElement[] */
const buttons = [confirmButton, denyButton, cancelButton];
if (document.activeElement instanceof HTMLElement && !buttons.includes(document.activeElement)) {
return;
}
const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling';
let buttonToFocus = document.activeElement;
if (!buttonToFocus) {
return;
}
for (let i = 0; i < actions.children.length; i++) {
buttonToFocus = buttonToFocus[sibling];
if (!buttonToFocus) {
return;
}
if (buttonToFocus instanceof HTMLButtonElement && isVisible$1(buttonToFocus)) {
break;
}
}
if (buttonToFocus instanceof HTMLButtonElement) {
buttonToFocus.focus();
}
};
/**
* @param {KeyboardEvent} event
* @param {SweetAlertOptions} innerParams
* @param {Function} dismissWith
*/
const handleEsc = (event, innerParams, dismissWith) => {
event.preventDefault();
if (callIfFunction(innerParams.allowEscapeKey)) {
dismissWith(DismissReason.esc);
}
};
/**
* This module contains `WeakMap`s for each effectively-"private property" that a `Swal` has.
* For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
* This is the approach that Babel will probably take to implement private methods/fields
* https://github.com/tc39/proposal-private-methods
* https://github.com/babel/babel/pull/7555
* Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
* then we can use that language feature.
*/
var privateMethods = {
swalPromiseResolve: new WeakMap(),
swalPromiseReject: new WeakMap()
};
// From https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/
// Adding aria-hidden="true" to elements outside of the active modal dialog ensures that
// elements not within the active modal dialog will not be surfaced if a user opens a screen
// readers list of elements (headings, form controls, landmarks, etc.) in the document.
const setAriaHidden = () => {
const container = getContainer();
const bodyChildren = Array.from(document.body.children);
bodyChildren.forEach(el => {
if (el.contains(container)) {
return;
}
if (el.hasAttribute('aria-hidden')) {
el.setAttribute('data-previous-aria-hidden', el.getAttribute('aria-hidden') || '');
}
el.setAttribute('aria-hidden', 'true');
});
};
const unsetAriaHidden = () => {
const bodyChildren = Array.from(document.body.children);
bodyChildren.forEach(el => {
if (el.hasAttribute('data-previous-aria-hidden')) {
el.setAttribute('aria-hidden', el.getAttribute('data-previous-aria-hidden') || '');
el.removeAttribute('data-previous-aria-hidden');
} else {
el.removeAttribute('aria-hidden');
}
});
};
// @ts-ignore
const isSafariOrIOS = typeof window !== 'undefined' && !!window.GestureEvent; // true for Safari desktop + all iOS browsers https://stackoverflow.com/a/70585394
/**
* Fix iOS scrolling
* http://stackoverflow.com/q/39626302
*/
const iOSfix = () => {
if (isSafariOrIOS && !hasClass(document.body, swalClasses.iosfix)) {
const offset = document.body.scrollTop;
document.body.style.top = `${offset * -1}px`;
addClass(document.body, swalClasses.iosfix);
lockBodyScroll();
}
};
/**
* https://github.com/sweetalert2/sweetalert2/issues/1246
*/
const lockBodyScroll = () => {
const container = getContainer();
if (!container) {
return;
}
/** @type {boolean} */
let preventTouchMove;
/**
* @param {TouchEvent} event
*/
container.ontouchstart = event => {
preventTouchMove = shouldPreventTouchMove(event);
};
/**
* @param {TouchEvent} event
*/
container.ontouchmove = event => {
if (preventTouchMove) {
event.preventDefault();
event.stopPropagation();
}
};
};
/**
* @param {TouchEvent} event
* @returns {boolean}
*/
const shouldPreventTouchMove = event => {
const target = event.target;
const container = getContainer();
const htmlContainer = getHtmlContainer();
if (!container || !htmlContainer) {
return false;
}
if (isStylus(event) || isZoom(event)) {
return false;
}
if (target === container) {
return true;
}
if (!isScrollable(container) && target instanceof HTMLElement && !selfOrParentIsScrollable(target, htmlContainer) &&
// #2823
target.tagName !== 'INPUT' &&
// #1603
target.tagName !== 'TEXTAREA' &&
// #2266
!(isScrollable(htmlContainer) &&
// #1944
htmlContainer.contains(target))) {
return true;
}
return false;
};
/**
* https://github.com/sweetalert2/sweetalert2/issues/1786
*
* @param {*} event
* @returns {boolean}
*/
const isStylus = event => {
return event.touches && event.touches.length && event.touches[0].touchType === 'stylus';
};
/**
* https://github.com/sweetalert2/sweetalert2/issues/1891
*
* @param {TouchEvent} event
* @returns {boolean}
*/
const isZoom = event => {
return event.touches && event.touches.length > 1;
};
const undoIOSfix = () => {
if (hasClass(document.body, swalClasses.iosfix)) {
const offset = parseInt(document.body.style.top, 10);
removeClass(document.body, swalClasses.iosfix);
document.body.style.top = '';
document.body.scrollTop = offset * -1;
}
};
/**
* Measure scrollbar width for padding body during modal show/hide
* https://github.com/twbs/bootstrap/blob/master/js/src/modal.js
*
* @returns {number}
*/
const measureScrollbar = () => {
const scrollDiv = document.createElement('div');
scrollDiv.className = swalClasses['scrollbar-measure'];
document.body.appendChild(scrollDiv);
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);
return scrollbarWidth;
};
/**
* Remember state in cases where opening and handling a modal will fiddle with it.
* @type {number | null}
*/
let previousBodyPadding = null;
/**
* @param {string} initialBodyOverflow
*/
const replaceScrollbarWithPadding = initialBodyOverflow => {
// for queues, do not do this more than once
if (previousBodyPadding !== null) {
return;
}
// if the body has overflow
if (document.body.scrollHeight > window.innerHeight || initialBodyOverflow === 'scroll' // https://github.com/sweetalert2/sweetalert2/issues/2663
) {
// add padding so the content doesn't shift after removal of scrollbar
previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'));
document.body.style.paddingRight = `${previousBodyPadding + measureScrollbar()}px`;
}
};
const undoReplaceScrollbarWithPadding = () => {
if (previousBodyPadding !== null) {
document.body.style.paddingRight = `${previousBodyPadding}px`;
previousBodyPadding = null;
}
};
/**
* @param {SweetAlert} instance
* @param {HTMLElement} container
* @param {boolean} returnFocus
* @param {Function} didClose
*/
function removePopupAndResetState(instance, container, returnFocus, didClose) {
if (isToast()) {
triggerDidCloseAndDispose(instance, didClose);
} else {
restoreActiveElement(returnFocus).then(() => triggerDidCloseAndDispose(instance, didClose));
removeKeydownHandler(globalState);
}
// workaround for https://github.com/sweetalert2/sweetalert2/issues/2088
// for some reason removing the container in Safari will scroll the document to bottom
if (isSafariOrIOS) {
container.setAttribute('style', 'display:none !important');
container.removeAttribute('class');
container.innerHTML = '';
} else {
container.remove();
}
if (isModal()) {
undoReplaceScrollbarWithPadding();
undoIOSfix();
unsetAriaHidden();
}
removeBodyClasses();
}
/**
* Remove SweetAlert2 classes from body
*/
function removeBodyClasses() {
removeClass([document.documentElement, document.body], [swalClasses.shown, swalClasses['height-auto'], swalClasses['no-backdrop'], swalClasses['toast-shown']]);
}
/**
* Instance method to close sweetAlert
*
* @param {any} resolveValue
*/
function close(resolveValue) {
resolveValue = prepareResolveValue(resolveValue);
const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this);
const didClose = triggerClosePopup(this);
if (this.isAwaitingPromise) {
// A swal awaiting for a promise (after a click on Confirm or Deny) cannot be dismissed anymore #2335
if (!resolveValue.isDismissed) {
handleAwaitingPromise(this);
swalPromiseResolve(resolveValue);
}
} else if (didClose) {
// Resolve Swal promise
swalPromiseResolve(resolveValue);
}
}
const triggerClosePopup = instance => {
const popup = getPopup();
if (!popup) {
return false;
}
const innerParams = privateProps.innerParams.get(instance);
if (!innerParams || hasClass(popup, innerParams.hideClass.popup)) {
return false;
}
removeClass(popup, innerParams.showClass.popup);
addClass(popup, innerParams.hideClass.popup);
const backdrop = getContainer();
removeClass(backdrop, innerParams.showClass.backdrop);
addClass(backdrop, innerParams.hideClass.backdrop);
handlePopupAnimation(instance, popup, innerParams);
return true;
};
/**
* @param {any} error
*/
function rejectPromise(error) {
const rejectPromise = privateMethods.swalPromiseReject.get(this);
handleAwaitingPromise(this);
if (rejectPromise) {
// Reject Swal promise
rejectPromise(error);
}
}
/**
* @param {SweetAlert} instance
*/
const handleAwaitingPromise = instance => {
if (instance.isAwaitingPromise) {
delete instance.isAwaitingPromise;
// The instance might have been previously partly destroyed, we must resume the destroy process in this case #2335
if (!privateProps.innerParams.get(instance)) {
instance._destroy();
}
}
};
/**
* @param {any} resolveValue
* @returns {SweetAlertResult}
*/
const prepareResolveValue = resolveValue => {
// When user calls Swal.close()
if (typeof resolveValue === 'undefined') {
return {
isConfirmed: false,
isDenied: false,
isDismissed: true
};
}
return Object.assign({
isConfirmed: false,
isDenied: false,
isDismissed: false
}, resolveValue);
};
/**
* @param {SweetAlert} instance
* @param {HTMLElement} popup
* @param {SweetAlertOptions} innerParams
*/
const handlePopupAnimation = (instance, popup, innerParams) => {
var _globalState$eventEmi;
const container = getContainer();
// If animation is supported, animate
const animationIsSupported = hasCssAnimation(popup);
if (typeof innerParams.willClose === 'function') {
innerParams.willClose(popup);
}
(_globalState$eventEmi = globalState.eventEmitter) === null || _globalState$eventEmi === void 0 || _globalState$eventEmi.emit('willClose', popup);
if (animationIsSupported) {
animatePopup(instance, popup, container, innerParams.returnFocus, innerParams.didClose);
} else {
// Otherwise, remove immediately
removePopupAndResetState(instance, container, innerParams.returnFocus, innerParams.didClose);
}
};
/**
* @param {SweetAlert} instance
* @param {HTMLElement} popup
* @param {HTMLElement} container
* @param {boolean} returnFocus
* @param {Function} didClose
*/
const animatePopup = (instance, popup, container, returnFocus, didClose) => {
globalState.swalCloseEventFinishedCallback = removePopupAndResetState.bind(null, instance, container, returnFocus, didClose);
/**
* @param {AnimationEvent | TransitionEvent} e
*/
const swalCloseAnimationFinished = function (e) {
if (e.target === popup) {
var _globalState$swalClos;
(_globalState$swalClos = globalState.swalCloseEventFinishedCallback) === null || _globalState$swalClos === void 0 || _globalState$swalClos.call(globalState);
delete globalState.swalCloseEventFinishedCallback;
popup.removeEventListener('animationend', swalCloseAnimationFinished);
popup.removeEventListener('transitionend', swalCloseAnimationFinished);
}
};
popup.addEventListener('animationend', swalCloseAnimationFinished);
popup.addEventListener('transitionend', swalCloseAnimationFinished);
};
/**
* @param {SweetAlert} instance
* @param {Function} didClose
*/
const triggerDidCloseAndDispose = (instance, didClose) => {
setTimeout(() => {
var _globalState$eventEmi2;
if (typeof didClose === 'function') {
didClose.bind(instance.params)();
}
(_globalState$eventEmi2 = globalState.eventEmitter) === null || _globalState$eventEmi2 === void 0 || _globalState$eventEmi2.emit('didClose');
// instance might have been destroyed already
if (instance._destroy) {
instance._destroy();
}
});
};
/**
* Shows loader (spinner), this is useful with AJAX requests.
* By default the loader be shown instead of the "Confirm" button.
*
* @param {HTMLButtonElement | null} [buttonToReplace]
*/
const showLoading = buttonToReplace => {
let popup = getPopup();
if (!popup) {
new Swal();
}
popup = getPopup();
if (!popup) {
return;
}
const loader = getLoader();
if (isToast()) {
hide(getIcon());
} else {
replaceButton(popup, buttonToReplace);
}
show(loader);
popup.setAttribute('data-loading', 'true');
popup.setAttribute('aria-busy', 'true');
popup.focus();
};
/**
* @param {HTMLElement} popup
* @param {HTMLButtonElement | null} [buttonToReplace]
*/
const replaceButton = (popup, buttonToReplace) => {
const actions = getActions();
const loader = getLoader();
if (!actions || !loader) {
return;
}
if (!buttonToReplace && isVisible$1(getConfirmButton())) {
buttonToReplace = getConfirmButton();
}
show(actions);
if (buttonToReplace) {
hide(buttonToReplace);
loader.setAttribute('data-button-to-replace', buttonToReplace.className);
actions.insertBefore(loader, buttonToReplace);
}
addClass([popup, actions], swalClasses.loading);
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const handleInputOptionsAndValue = (instance, params) => {
if (params.input === 'select' || params.input === 'radio') {
handleInputOptions(instance, params);
} else if (['text', 'email', 'number', 'tel', 'textarea'].some(i => i === params.input) && (hasToPromiseFn(params.inputValue) || isPromise(params.inputValue))) {
showLoading(getConfirmButton());
handleInputValue(instance, params);
}
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} innerParams
* @returns {SweetAlertInputValue}
*/
const getInputValue = (instance, innerParams) => {
const input = instance.getInput();
if (!input) {
return null;
}
switch (innerParams.input) {
case 'checkbox':
return getCheckboxValue(input);
case 'radio':
return getRadioValue(input);
case 'file':
return getFileValue(input);
default:
return innerParams.inputAutoTrim ? input.value.trim() : input.value;
}
};
/**
* @param {HTMLInputElement} input
* @returns {number}
*/
const getCheckboxValue = input => input.checked ? 1 : 0;
/**
* @param {HTMLInputElement} input
* @returns {string | null}
*/
const getRadioValue = input => input.checked ? input.value : null;
/**
* @param {HTMLInputElement} input
* @returns {FileList | File | null}
*/
const getFileValue = input => input.files && input.files.length ? input.getAttribute('multiple') !== null ? input.files : input.files[0] : null;
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const handleInputOptions = (instance, params) => {
const popup = getPopup();
if (!popup) {
return;
}
/**
* @param {Record<string, any>} inputOptions
*/
const processInputOptions = inputOptions => {
if (params.input === 'select') {
populateSelectOptions(popup, formatInputOptions(inputOptions), params);
} else if (params.input === 'radio') {
populateRadioOptions(popup, formatInputOptions(inputOptions), params);
}
};
if (hasToPromiseFn(params.inputOptions) || isPromise(params.inputOptions)) {
showLoading(getConfirmButton());
asPromise(params.inputOptions).then(inputOptions => {
instance.hideLoading();
processInputOptions(inputOptions);
});
} else if (typeof params.inputOptions === 'object') {
processInputOptions(params.inputOptions);
} else {
error(`Unexpected type of inputOptions! Expected object, Map or Promise, got ${typeof params.inputOptions}`);
}
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertOptions} params
*/
const handleInputValue = (instance, params) => {
const input = instance.getInput();
if (!input) {
return;
}
hide(input);
asPromise(params.inputValue).then(inputValue => {
input.value = params.input === 'number' ? `${parseFloat(inputValue) || 0}` : `${inputValue}`;
show(input);
input.focus();
instance.hideLoading();
}).catch(err => {
error(`Error in inputValue promise: ${err}`);
input.value = '';
show(input);
input.focus();
instance.hideLoading();
});
};
/**
* @param {HTMLElement} popup
* @param {InputOptionFlattened[]} inputOptions
* @param {SweetAlertOptions} params
*/
function populateSelectOptions(popup, inputOptions, params) {
const select = getDirectChildByClass(popup, swalClasses.select);
if (!select) {
return;
}
/**
* @param {HTMLElement} parent
* @param {string} optionLabel
* @param {string} optionValue
*/
const renderOption = (parent, optionLabel, optionValue) => {
const option = document.createElement('option');
option.value = optionValue;
setInnerHtml(option, optionLabel);
option.selected = isSelected(optionValue, params.inputValue);
parent.appendChild(option);
};
inputOptions.forEach(inputOption => {
const optionValue = inputOption[0];
const optionLabel = inputOption[1];
// <optgroup> spec:
// https://www.w3.org/TR/html401/interact/forms.html#h-17.6
// "...all OPTGROUP elements must be specified directly within a SELECT element (i.e., groups may not be nested)..."
// check whether this is a <optgroup>
if (Array.isArray(optionLabel)) {
// if it is an array, then it is an <optgroup>
const optgroup = document.createElement('optgroup');
optgroup.label = optionValue;
optgroup.disabled = false; // not configurable for now
select.appendChild(optgroup);
optionLabel.forEach(o => renderOption(optgroup, o[1], o[0]));
} else {
// case of <option>
renderOption(select, optionLabel, optionValue);
}
});
select.focus();
}
/**
* @param {HTMLElement} popup
* @param {InputOptionFlattened[]} inputOptions
* @param {SweetAlertOptions} params
*/
function populateRadioOptions(popup, inputOptions, params) {
const radio = getDirectChildByClass(popup, swalClasses.radio);
if (!radio) {
return;
}
inputOptions.forEach(inputOption => {
const radioValue = inputOption[0];
const radioLabel = inputOption[1];
const radioInput = document.createElement('input');
const radioLabelElement = document.createElement('label');
radioInput.type = 'radio';
radioInput.name = swalClasses.radio;
radioInput.value = radioValue;
if (isSelected(radioValue, params.inputValue)) {
radioInput.checked = true;
}
const label = document.createElement('span');
setInnerHtml(label, radioLabel);
label.className = swalClasses.label;
radioLabelElement.appendChild(radioInput);
radioLabelElement.appendChild(label);
radio.appendChild(radioLabelElement);
});
const radios = radio.querySelectorAll('input');
if (radios.length) {
radios[0].focus();
}
}
/**
* Converts `inputOptions` into an array of `[value, label]`s
*
* @param {Record<string, any>} inputOptions
* @typedef {string[]} InputOptionFlattened
* @returns {InputOptionFlattened[]}
*/
const formatInputOptions = inputOptions => {
/** @type {InputOptionFlattened[]} */
const result = [];
if (inputOptions instanceof Map) {
inputOptions.forEach((value, key) => {
let valueFormatted = value;
if (typeof valueFormatted === 'object') {
// case of <optgroup>
valueFormatted = formatInputOptions(valueFormatted);
}
result.push([key, valueFormatted]);
});
} else {
Object.keys(inputOptions).forEach(key => {
let valueFormatted = inputOptions[key];
if (typeof valueFormatted === 'object') {
// case of <optgroup>
valueFormatted = formatInputOptions(valueFormatted);
}
result.push([key, valueFormatted]);
});
}
return result;
};
/**
* @param {string} optionValue
* @param {SweetAlertInputValue} inputValue
* @returns {boolean}
*/
const isSelected = (optionValue, inputValue) => {
return !!inputValue && inputValue.toString() === optionValue.toString();
};
/**
* @param {SweetAlert} instance
*/
const handleConfirmButtonClick = instance => {
const innerParams = privateProps.innerParams.get(instance);
instance.disableButtons();
if (innerParams.input) {
handleConfirmOrDenyWithInput(instance, 'confirm');
} else {
confirm(instance, true);
}
};
/**
* @param {SweetAlert} instance
*/
const handleDenyButtonClick = instance => {
const innerParams = privateProps.innerParams.get(instance);
instance.disableButtons();
if (innerParams.returnInputValueOnDeny) {
handleConfirmOrDenyWithInput(instance, 'deny');
} else {
deny(instance, false);
}
};
/**
* @param {SweetAlert} instance
* @param {Function} dismissWith
*/
const handleCancelButtonClick = (instance, dismissWith) => {
instance.disableButtons();
dismissWith(DismissReason.cancel);
};
/**
* @param {SweetAlert} instance
* @param {'confirm' | 'deny'} type
*/
const handleConfirmOrDenyWithInput = (instance, type) => {
const innerParams = privateProps.innerParams.get(instance);
if (!innerParams.input) {
error(`The "input" parameter is needed to be set when using returnInputValueOn${capitalizeFirstLetter(type)}`);
return;
}
const input = instance.getInput();
const inputValue = getInputValue(instance, innerParams);
if (innerParams.inputValidator) {
handleInputValidator(instance, inputValue, type);
} else if (input && !input.checkValidity()) {
instance.enableButtons();
instance.showValidationMessage(innerParams.validationMessage || input.validationMessage);
} else if (type === 'deny') {
deny(instance, inputValue);
} else {
confirm(instance, inputValue);
}
};
/**
* @param {SweetAlert} instance
* @param {SweetAlertInputValue} inputValue
* @param {'confirm' | 'deny'} type
*/
const handleInputValidator = (instance, inputValue, type) => {
const innerParams = privateProps.innerParams.get(instance);
instance.disableInput();
const validationPromise = Promise.resolve().then(() => asPromise(innerParams.inputValidator(inputValue, innerParams.validationMessage)));
validationPromise.then(validationMessage => {
instance.enableButtons();
instance.enableInput();
if (validationMessage) {
instance.showValidationMessage(validationMessage);
} else if (type === 'deny') {
deny(instance, inputValue);
} else {
confirm(instance, inputValue);
}
});
};
/**
* @param {SweetAlert} instance
* @param {any} value
*/
const deny = (instance, value) => {
const innerParams = privateProps.innerParams.get(instance || undefined);
if (innerParams.showLoaderOnDeny) {
showLoading(getDenyButton());
}
if (innerParams.preDeny) {
instance.isAwaitingPromise = true; // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesn't get destroyed until the result from this preDeny's promise is received
const preDenyPromise = Promise.resolve().then(() => asPromise(innerParams.preDeny(value, innerParams.validationMessage)));
preDenyPromise.then(preDenyValue => {
if (preDenyValue === false) {
instance.hideLoading();
handleAwaitingPromise(instance);
} else {
instance.close({
isDenied: true,
value: typeof preDenyValue === 'undefined' ? value : preDenyValue
});
}
}).catch(error => rejectWith(instance || undefined, error));
} else {
instance.close({
isDenied: true,
value
});
}
};
/**
* @param {SweetAlert} instance
* @param {any} value
*/
const succeedWith = (instance, value) => {
instance.close({
isConfirmed: true,
value
});
};
/**
*
* @param {SweetAlert} instance
* @param {string} error
*/
const rejectWith = (instance, error) => {
instance.rejectPromise(error);
};
/**
*
* @param {SweetAlert} instance
* @param {any} value
*/
const confirm = (instance, value) => {
const innerParams = privateProps.innerParams.get(instance || undefined);
if (innerParams.showLoaderOnConfirm) {
showLoading();
}
if (innerParams.preConfirm) {
instance.resetValidationMessage();
instance.isAwaitingPromise = true; // Flagging the instance as awaiting a promise so it's own promise's reject/resolve methods doesn't get destroyed until the result from this preConfirm's promise is received
const preConfirmPromise = Promise.resolve().then(() => asPromise(innerParams.preConfirm(value, innerParams.validationMessage)));
preConfirmPromise.then(preConfirmValue => {
if (isVisible$1(getValidationMessage()) || preConfirmValue === false) {
instance.hideLoading();
handleAwaitingPromise(instance);
} else {
succeedWith(instance, typeof preConfirmValue === 'undefined' ? value : preConfirmValue);
}
}).catch(error => rejectWith(instance || undefined, error));
} else {
succeedWith(instance, value);
}
};
/**
* Hides loader and shows back the button which was hidden by .showLoading()
*/
function hideLoading() {
// do nothing if popup is closed
const innerParams = privateProps.innerParams.get(this);
if (!innerParams) {
return;
}
const domCache = privateProps.domCache.get(this);
hide(domCache.loader);
if (isToast()) {
if (innerParams.icon) {
show(getIcon());
}
} else {
showRelatedButton(domCache);
}
removeClass([domCache.popup, domCache.actions], swalClasses.loading);
domCache.popup.removeAttribute('aria-busy');
domCache.popup.removeAttribute('data-loading');
domCache.confirmButton.disabled = false;
domCache.denyButton.disabled = false;
domCache.cancelButton.disabled = false;
}
const showRelatedButton = domCache => {
const buttonToReplace = domCache.popup.getElementsByClassName(domCache.loader.getAttribute('data-button-to-replace'));
if (buttonToReplace.length) {
show(buttonToReplace[0], 'inline-block');
} else if (allButtonsAreHidden()) {
hide(domCache.actions);
}
};
/**
* Gets the input DOM node, this method works with input parameter.
*
* @returns {HTMLInputElement | null}
*/
function getInput() {
const innerParams = privateProps.innerParams.get(this);
const domCache = privateProps.domCache.get(this);
if (!domCache) {
return null;
}
return getInput$1(domCache.popup, innerParams.input);
}
/**
* @param {SweetAlert} instance
* @param {string[]} buttons
* @param {boolean} disabled
*/
function setButtonsDisabled(instance, buttons, disabled) {
const domCache = privateProps.domCache.get(instance);
buttons.forEach(button => {
domCache[button].disabled = disabled;
});
}
/**
* @param {HTMLInputElement | null} input
* @param {boolean} disabled
*/
function setInputDisabled(input, disabled) {
const popup = getPopup();
if (!popup || !input) {
return;
}
if (input.type === 'radio') {
/** @type {NodeListOf<HTMLInputElement>} */
const radios = popup.querySelectorAll(`[name="${swalClasses.radio}"]`);
for (let i = 0; i < radios.length; i++) {
radios[i].disabled = disabled;
}
} else {
input.disabled = disabled;
}
}
/**
* Enable all the buttons
* @this {SweetAlert}
*/
function enableButtons() {
setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], false);
}
/**
* Disable all the buttons
* @this {SweetAlert}
*/
function disableButtons() {
setButtonsDisabled(this, ['confirmButton', 'denyButton', 'cancelButton'], true);
}
/**
* Enable the input field
* @this {SweetAlert}
*/
function enableInput() {
setInputDisabled(this.getInput(), false);
}
/**
* Disable the input field
* @this {SweetAlert}
*/
function disableInput() {
setInputDisabled(this.getInput(), true);
}
/**
* Show block with validation message
*
* @param {string} error
* @this {SweetAlert}
*/
function showValidationMessage(error) {
const domCache = privateProps.domCache.get(this);
const params = privateProps.innerParams.get(this);
setInnerHtml(domCache.validationMessage, error);
domCache.validationMessage.className = swalClasses['validation-message'];
if (params.customClass && params.customClass.validationMessage) {
addClass(domCache.validationMessage, params.customClass.validationMessage);
}
show(domCache.validationMessage);
const input = this.getInput();
if (input) {
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', swalClasses['validation-message']);
focusInput(input);
addClass(input, swalClasses.inputerror);
}
}
/**
* Hide block with validation message
*
* @this {SweetAlert}
*/
function resetValidationMessage() {
const domCache = privateProps.domCache.get(this);
if (domCache.validationMessage) {
hide(domCache.validationMessage);
}
const input = this.getInput();
if (input) {
input.removeAttribute('aria-invalid');
input.removeAttribute('aria-describedby');
removeClass(input, swalClasses.inputerror);
}
}
const defaultParams = {
title: '',
titleText: '',
text: '',
html: '',
footer: '',
icon: undefined,
iconColor: undefined,
iconHtml: undefined,
template: undefined,
toast: false,
draggable: false,
animation: true,
theme: 'light',
showClass: {
popup: 'swal2-show',
backdrop: 'swal2-backdrop-show',
icon: 'swal2-icon-show'
},
hideClass: {
popup: 'swal2-hide',
backdrop: 'swal2-backdrop-hide',
icon: 'swal2-icon-hide'
},
customClass: {},
target: 'body',
color: undefined,
backdrop: true,
heightAuto: true,
allowOutsideClick: true,
allowEscapeKey: true,
allowEnterKey: true,
stopKeydownPropagation: true,
keydownListenerCapture: false,
showConfirmButton: true,
showDenyButton: false,
showCancelButton: false,
preConfirm: undefined,
preDeny: undefined,
confirmButtonText: 'OK',
confirmButtonAriaLabel: '',
confirmButtonColor: undefined,
denyButtonText: 'No',
denyButtonAriaLabel: '',
denyButtonColor: undefined,
cancelButtonText: 'Cancel',
cancelButtonAriaLabel: '',
cancelButtonColor: undefined,
buttonsStyling: true,
reverseButtons: false,
focusConfirm: true,
focusDeny: false,
focusCancel: false,
returnFocus: true,
showCloseButton: false,
closeButtonHtml: '&times;',
closeButtonAriaLabel: 'Close this dialog',
loaderHtml: '',
showLoaderOnConfirm: false,
showLoaderOnDeny: false,
imageUrl: undefined,
imageWidth: undefined,
imageHeight: undefined,
imageAlt: '',
timer: undefined,
timerProgressBar: false,
width: undefined,
padding: undefined,
background: undefined,
input: undefined,
inputPlaceholder: '',
inputLabel: '',
inputValue: '',
inputOptions: {},
inputAutoFocus: true,
inputAutoTrim: true,
inputAttributes: {},
inputValidator: undefined,
returnInputValueOnDeny: false,
validationMessage: undefined,
grow: false,
position: 'center',
progressSteps: [],
currentProgressStep: undefined,
progressStepsDistance: undefined,
willOpen: undefined,
didOpen: undefined,
didRender: undefined,
willClose: undefined,
didClose: undefined,
didDestroy: undefined,
scrollbarPadding: true,
topLayer: false
};
const updatableParams = ['allowEscapeKey', 'allowOutsideClick', 'background', 'buttonsStyling', 'cancelButtonAriaLabel', 'cancelButtonColor', 'cancelButtonText', 'closeButtonAriaLabel', 'closeButtonHtml', 'color', 'confirmButtonAriaLabel', 'confirmButtonColor', 'confirmButtonText', 'currentProgressStep', 'customClass', 'denyButtonAriaLabel', 'denyButtonColor', 'denyButtonText', 'didClose', 'didDestroy', 'draggable', 'footer', 'hideClass', 'html', 'icon', 'iconColor', 'iconHtml', 'imageAlt', 'imageHeight', 'imageUrl', 'imageWidth', 'preConfirm', 'preDeny', 'progressSteps', 'returnFocus', 'reverseButtons', 'showCancelButton', 'showCloseButton', 'showConfirmButton', 'showDenyButton', 'text', 'title', 'titleText', 'theme', 'willClose'];
/** @type {Record<string, string | undefined>} */
const deprecatedParams = {
allowEnterKey: undefined
};
const toastIncompatibleParams = ['allowOutsideClick', 'allowEnterKey', 'backdrop', 'draggable', 'focusConfirm', 'focusDeny', 'focusCancel', 'returnFocus', 'heightAuto', 'keydownListenerCapture'];
/**
* Is valid parameter
*
* @param {string} paramName
* @returns {boolean}
*/
const isValidParameter = paramName => {
return Object.prototype.hasOwnProperty.call(defaultParams, paramName);
};
/**
* Is valid parameter for Swal.update() method
*
* @param {string} paramName
* @returns {boolean}
*/
const isUpdatableParameter = paramName => {
return updatableParams.indexOf(paramName) !== -1;
};
/**
* Is deprecated parameter
*
* @param {string} paramName
* @returns {string | undefined}
*/
const isDeprecatedParameter = paramName => {
return deprecatedParams[paramName];
};
/**
* @param {string} param
*/
const checkIfParamIsValid = param => {
if (!isValidParameter(param)) {
warn(`Unknown parameter "${param}"`);
}
};
/**
* @param {string} param
*/
const checkIfToastParamIsValid = param => {
if (toastIncompatibleParams.includes(param)) {
warn(`The parameter "${param}" is incompatible with toasts`);
}
};
/**
* @param {string} param
*/
const checkIfParamIsDeprecated = param => {
const isDeprecated = isDeprecatedParameter(param);
if (isDeprecated) {
warnAboutDeprecation(param, isDeprecated);
}
};
/**
* Show relevant warnings for given params
*
* @param {SweetAlertOptions} params
*/
const showWarningsForParams = params => {
if (params.backdrop === false && params.allowOutsideClick) {
warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`');
}
if (params.theme && !['light', 'dark', 'auto', 'minimal', 'borderless', 'embed-iframe', 'bulma', 'bulma-light', 'bulma-dark'].includes(params.theme)) {
warn(`Invalid theme "${params.theme}"`);
}
for (const param in params) {
checkIfParamIsValid(param);
if (params.toast) {
checkIfToastParamIsValid(param);
}
checkIfParamIsDeprecated(param);
}
};
/**
* Updates popup parameters.
*
* @param {SweetAlertOptions} params
*/
function update(params) {
const container = getContainer();
const popup = getPopup();
const innerParams = privateProps.innerParams.get(this);
if (!popup || hasClass(popup, innerParams.hideClass.popup)) {
warn(`You're trying to update the closed or closing popup, that won't work. Use the update() method in preConfirm parameter or show a new popup.`);
return;
}
const validUpdatableParams = filterValidParams(params);
const updatedParams = Object.assign({}, innerParams, validUpdatableParams);
showWarningsForParams(updatedParams);
container.dataset['swal2Theme'] = updatedParams.theme;
render(this, updatedParams);
privateProps.innerParams.set(this, updatedParams);
Object.defineProperties(this, {
params: {
value: Object.assign({}, this.params, params),
writable: false,
enumerable: true
}
});
}
/**
* @param {SweetAlertOptions} params
* @returns {SweetAlertOptions}
*/
const filterValidParams = params => {
const validUpdatableParams = {};
Object.keys(params).forEach(param => {
if (isUpdatableParameter(param)) {
validUpdatableParams[param] = params[param];
} else {
warn(`Invalid parameter to update: ${param}`);
}
});
return validUpdatableParams;
};
/**
* Dispose the current SweetAlert2 instance
*/
function _destroy() {
const domCache = privateProps.domCache.get(this);
const innerParams = privateProps.innerParams.get(this);
if (!innerParams) {
disposeWeakMaps(this); // The WeakMaps might have been partly destroyed, we must recall it to dispose any remaining WeakMaps #2335
return; // This instance has already been destroyed
}
// Check if there is another Swal closing
if (domCache.popup && globalState.swalCloseEventFinishedCallback) {
globalState.swalCloseEventFinishedCallback();
delete globalState.swalCloseEventFinishedCallback;
}
if (typeof innerParams.didDestroy === 'function') {
innerParams.didDestroy();
}
globalState.eventEmitter.emit('didDestroy');
disposeSwal(this);
}
/**
* @param {SweetAlert} instance
*/
const disposeSwal = instance => {
disposeWeakMaps(instance);
// Unset this.params so GC will dispose it (#1569)
delete instance.params;
// Unset globalState props so GC will dispose globalState (#1569)
delete globalState.keydownHandler;
delete globalState.keydownTarget;
// Unset currentInstance
delete globalState.currentInstance;
};
/**
* @param {SweetAlert} instance
*/
const disposeWeakMaps = instance => {
// If the current instance is awaiting a promise result, we keep the privateMethods to call them once the promise result is retrieved #2335
if (instance.isAwaitingPromise) {
unsetWeakMaps(privateProps, instance);
instance.isAwaitingPromise = true;
} else {
unsetWeakMaps(privateMethods, instance);
unsetWeakMaps(privateProps, instance);
delete instance.isAwaitingPromise;
// Unset instance methods
delete instance.disableButtons;
delete instance.enableButtons;
delete instance.getInput;
delete instance.disableInput;
delete instance.enableInput;
delete instance.hideLoading;
delete instance.disableLoading;
delete instance.showValidationMessage;
delete instance.resetValidationMessage;
delete instance.close;
delete instance.closePopup;
delete instance.closeModal;
delete instance.closeToast;
delete instance.rejectPromise;
delete instance.update;
delete instance._destroy;
}
};
/**
* @param {object} obj
* @param {SweetAlert} instance
*/
const unsetWeakMaps = (obj, instance) => {
for (const i in obj) {
obj[i].delete(instance);
}
};
var instanceMethods = /*#__PURE__*/Object.freeze({
__proto__: null,
_destroy: _destroy,
close: close,
closeModal: close,
closePopup: close,
closeToast: close,
disableButtons: disableButtons,
disableInput: disableInput,
disableLoading: hideLoading,
enableButtons: enableButtons,
enableInput: enableInput,
getInput: getInput,
handleAwaitingPromise: handleAwaitingPromise,
hideLoading: hideLoading,
rejectPromise: rejectPromise,
resetValidationMessage: resetValidationMessage,
showValidationMessage: showValidationMessage,
update: update
});
/**
* @param {SweetAlertOptions} innerParams
* @param {DomCache} domCache
* @param {Function} dismissWith
*/
const handlePopupClick = (innerParams, domCache, dismissWith) => {
if (innerParams.toast) {
handleToastClick(innerParams, domCache, dismissWith);
} else {
// Ignore click events that had mousedown on the popup but mouseup on the container
// This can happen when the user drags a slider
handleModalMousedown(domCache);
// Ignore click events that had mousedown on the container but mouseup on the popup
handleContainerMousedown(domCache);
handleModalClick(innerParams, domCache, dismissWith);
}
};
/**
* @param {SweetAlertOptions} innerParams
* @param {DomCache} domCache
* @param {Function} dismissWith
*/
const handleToastClick = (innerParams, domCache, dismissWith) => {
// Closing toast by internal click
domCache.popup.onclick = () => {
if (innerParams && (isAnyButtonShown(innerParams) || innerParams.timer || innerParams.input)) {
return;
}
dismissWith(DismissReason.close);
};
};
/**
* @param {SweetAlertOptions} innerParams
* @returns {boolean}
*/
const isAnyButtonShown = innerParams => {
return !!(innerParams.showConfirmButton || innerParams.showDenyButton || innerParams.showCancelButton || innerParams.showCloseButton);
};
let ignoreOutsideClick = false;
/**
* @param {DomCache} domCache
*/
const handleModalMousedown = domCache => {
domCache.popup.onmousedown = () => {
domCache.container.onmouseup = function (e) {
domCache.container.onmouseup = () => {};
// We only check if the mouseup target is the container because usually it doesn't
// have any other direct children aside of the popup
if (e.target === domCache.container) {
ignoreOutsideClick = true;
}
};
};
};
/**
* @param {DomCache} domCache
*/
const handleContainerMousedown = domCache => {
domCache.container.onmousedown = e => {
// prevent the modal text from being selected on double click on the container (allowOutsideClick: false)
if (e.target === domCache.container) {
e.preventDefault();
}
domCache.popup.onmouseup = function (e) {
domCache.popup.onmouseup = () => {};
// We also need to check if the mouseup target is a child of the popup
if (e.target === domCache.popup || e.target instanceof HTMLElement && domCache.popup.contains(e.target)) {
ignoreOutsideClick = true;
}
};
};
};
/**
* @param {SweetAlertOptions} innerParams
* @param {DomCache} domCache
* @param {Function} dismissWith
*/
const handleModalClick = (innerParams, domCache, dismissWith) => {
domCache.container.onclick = e => {
if (ignoreOutsideClick) {
ignoreOutsideClick = false;
return;
}
if (e.target === domCache.container && callIfFunction(innerParams.allowOutsideClick)) {
dismissWith(DismissReason.backdrop);
}
};
};
const isJqueryElement = elem => typeof elem === 'object' && elem.jquery;
const isElement = elem => elem instanceof Element || isJqueryElement(elem);
const argsToParams = args => {
const params = {};
if (typeof args[0] === 'object' && !isElement(args[0])) {
Object.assign(params, args[0]);
} else {
['title', 'html', 'icon'].forEach((name, index) => {
const arg = args[index];
if (typeof arg === 'string' || isElement(arg)) {
params[name] = arg;
} else if (arg !== undefined) {
error(`Unexpected type of ${name}! Expected "string" or "Element", got ${typeof arg}`);
}
});
}
return params;
};
/**
* Main method to create a new SweetAlert2 popup
*
* @param {...SweetAlertOptions} args
* @returns {Promise<SweetAlertResult>}
*/
function fire(...args) {
return new this(...args);
}
/**
* Returns an extended version of `Swal` containing `params` as defaults.
* Useful for reusing Swal configuration.
*
* For example:
*
* Before:
* const textPromptOptions = { input: 'text', showCancelButton: true }
* const {value: firstName} = await Swal.fire({ ...textPromptOptions, title: 'What is your first name?' })
* const {value: lastName} = await Swal.fire({ ...textPromptOptions, title: 'What is your last name?' })
*
* After:
* const TextPrompt = Swal.mixin({ input: 'text', showCancelButton: true })
* const {value: firstName} = await TextPrompt('What is your first name?')
* const {value: lastName} = await TextPrompt('What is your last name?')
*
* @param {SweetAlertOptions} mixinParams
* @returns {SweetAlert}
*/
function mixin(mixinParams) {
class MixinSwal extends this {
_main(params, priorityMixinParams) {
return super._main(params, Object.assign({}, mixinParams, priorityMixinParams));
}
}
// @ts-ignore
return MixinSwal;
}
/**
* If `timer` parameter is set, returns number of milliseconds of timer remained.
* Otherwise, returns undefined.
*
* @returns {number | undefined}
*/
const getTimerLeft = () => {
return globalState.timeout && globalState.timeout.getTimerLeft();
};
/**
* Stop timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*
* @returns {number | undefined}
*/
const stopTimer = () => {
if (globalState.timeout) {
stopTimerProgressBar();
return globalState.timeout.stop();
}
};
/**
* Resume timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*
* @returns {number | undefined}
*/
const resumeTimer = () => {
if (globalState.timeout) {
const remaining = globalState.timeout.start();
animateTimerProgressBar(remaining);
return remaining;
}
};
/**
* Resume timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*
* @returns {number | undefined}
*/
const toggleTimer = () => {
const timer = globalState.timeout;
return timer && (timer.running ? stopTimer() : resumeTimer());
};
/**
* Increase timer. Returns number of milliseconds of an updated timer.
* If `timer` parameter isn't set, returns undefined.
*
* @param {number} ms
* @returns {number | undefined}
*/
const increaseTimer = ms => {
if (globalState.timeout) {
const remaining = globalState.timeout.increase(ms);
animateTimerProgressBar(remaining, true);
return remaining;
}
};
/**
* Check if timer is running. Returns true if timer is running
* or false if timer is paused or stopped.
* If `timer` parameter isn't set, returns undefined
*
* @returns {boolean}
*/
const isTimerRunning = () => {
return !!(globalState.timeout && globalState.timeout.isRunning());
};
let bodyClickListenerAdded = false;
const clickHandlers = {};
/**
* @param {string} attr
*/
function bindClickHandler(attr = 'data-swal-template') {
clickHandlers[attr] = this;
if (!bodyClickListenerAdded) {
document.body.addEventListener('click', bodyClickListener);
bodyClickListenerAdded = true;
}
}
const bodyClickListener = event => {
for (let el = event.target; el && el !== document; el = el.parentNode) {
for (const attr in clickHandlers) {
const template = el.getAttribute(attr);
if (template) {
clickHandlers[attr].fire({
template
});
return;
}
}
}
};
// Source: https://gist.github.com/mudge/5830382?permalink_comment_id=2691957#gistcomment-2691957
class EventEmitter {
constructor() {
/** @type {Events} */
this.events = {};
}
/**
* @param {string} eventName
* @returns {EventHandlers}
*/
_getHandlersByEventName(eventName) {
if (typeof this.events[eventName] === 'undefined') {
// not Set because we need to keep the FIFO order
// https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1748990334
this.events[eventName] = [];
}
return this.events[eventName];
}
/**
* @param {string} eventName
* @param {EventHandler} eventHandler
*/
on(eventName, eventHandler) {
const currentHandlers = this._getHandlersByEventName(eventName);
if (!currentHandlers.includes(eventHandler)) {
currentHandlers.push(eventHandler);
}
}
/**
* @param {string} eventName
* @param {EventHandler} eventHandler
*/
once(eventName, eventHandler) {
/**
* @param {Array} args
*/
const onceFn = (...args) => {
this.removeListener(eventName, onceFn);
eventHandler.apply(this, args);
};
this.on(eventName, onceFn);
}
/**
* @param {string} eventName
* @param {Array} args
*/
emit(eventName, ...args) {
this._getHandlersByEventName(eventName).forEach(
/**
* @param {EventHandler} eventHandler
*/
eventHandler => {
try {
eventHandler.apply(this, args);
} catch (error) {
console.error(error);
}
});
}
/**
* @param {string} eventName
* @param {EventHandler} eventHandler
*/
removeListener(eventName, eventHandler) {
const currentHandlers = this._getHandlersByEventName(eventName);
const index = currentHandlers.indexOf(eventHandler);
if (index > -1) {
currentHandlers.splice(index, 1);
}
}
/**
* @param {string} eventName
*/
removeAllListeners(eventName) {
if (this.events[eventName] !== undefined) {
// https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1749239222
this.events[eventName].length = 0;
}
}
reset() {
this.events = {};
}
}
globalState.eventEmitter = new EventEmitter();
/**
* @param {string} eventName
* @param {EventHandler} eventHandler
*/
const on = (eventName, eventHandler) => {
globalState.eventEmitter.on(eventName, eventHandler);
};
/**
* @param {string} eventName
* @param {EventHandler} eventHandler
*/
const once = (eventName, eventHandler) => {
globalState.eventEmitter.once(eventName, eventHandler);
};
/**
* @param {string} [eventName]
* @param {EventHandler} [eventHandler]
*/
const off = (eventName, eventHandler) => {
// Remove all handlers for all events
if (!eventName) {
globalState.eventEmitter.reset();
return;
}
if (eventHandler) {
// Remove a specific handler
globalState.eventEmitter.removeListener(eventName, eventHandler);
} else {
// Remove all handlers for a specific event
globalState.eventEmitter.removeAllListeners(eventName);
}
};
var staticMethods = /*#__PURE__*/Object.freeze({
__proto__: null,
argsToParams: argsToParams,
bindClickHandler: bindClickHandler,
clickCancel: clickCancel,
clickConfirm: clickConfirm,
clickDeny: clickDeny,
enableLoading: showLoading,
fire: fire,
getActions: getActions,
getCancelButton: getCancelButton,
getCloseButton: getCloseButton,
getConfirmButton: getConfirmButton,
getContainer: getContainer,
getDenyButton: getDenyButton,
getFocusableElements: getFocusableElements,
getFooter: getFooter,
getHtmlContainer: getHtmlContainer,
getIcon: getIcon,
getIconContent: getIconContent,
getImage: getImage,
getInputLabel: getInputLabel,
getLoader: getLoader,
getPopup: getPopup,
getProgressSteps: getProgressSteps,
getTimerLeft: getTimerLeft,
getTimerProgressBar: getTimerProgressBar,
getTitle: getTitle,
getValidationMessage: getValidationMessage,
increaseTimer: increaseTimer,
isDeprecatedParameter: isDeprecatedParameter,
isLoading: isLoading,
isTimerRunning: isTimerRunning,
isUpdatableParameter: isUpdatableParameter,
isValidParameter: isValidParameter,
isVisible: isVisible,
mixin: mixin,
off: off,
on: on,
once: once,
resumeTimer: resumeTimer,
showLoading: showLoading,
stopTimer: stopTimer,
toggleTimer: toggleTimer
});
class Timer {
/**
* @param {Function} callback
* @param {number} delay
*/
constructor(callback, delay) {
this.callback = callback;
this.remaining = delay;
this.running = false;
this.start();
}
/**
* @returns {number}
*/
start() {
if (!this.running) {
this.running = true;
this.started = new Date();
this.id = setTimeout(this.callback, this.remaining);
}
return this.remaining;
}
/**
* @returns {number}
*/
stop() {
if (this.started && this.running) {
this.running = false;
clearTimeout(this.id);
this.remaining -= new Date().getTime() - this.started.getTime();
}
return this.remaining;
}
/**
* @param {number} n
* @returns {number}
*/
increase(n) {
const running = this.running;
if (running) {
this.stop();
}
this.remaining += n;
if (running) {
this.start();
}
return this.remaining;
}
/**
* @returns {number}
*/
getTimerLeft() {
if (this.running) {
this.stop();
this.start();
}
return this.remaining;
}
/**
* @returns {boolean}
*/
isRunning() {
return this.running;
}
}
const swalStringParams = ['swal-title', 'swal-html', 'swal-footer'];
/**
* @param {SweetAlertOptions} params
* @returns {SweetAlertOptions}
*/
const getTemplateParams = params => {
const template = typeof params.template === 'string' ? (/** @type {HTMLTemplateElement} */document.querySelector(params.template)) : params.template;
if (!template) {
return {};
}
/** @type {DocumentFragment} */
const templateContent = template.content;
showWarningsForElements(templateContent);
const result = Object.assign(getSwalParams(templateContent), getSwalFunctionParams(templateContent), getSwalButtons(templateContent), getSwalImage(templateContent), getSwalIcon(templateContent), getSwalInput(templateContent), getSwalStringParams(templateContent, swalStringParams));
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Record<string, any>}
*/
const getSwalParams = templateContent => {
/** @type {Record<string, any>} */
const result = {};
/** @type {HTMLElement[]} */
const swalParams = Array.from(templateContent.querySelectorAll('swal-param'));
swalParams.forEach(param => {
showWarningsForAttributes(param, ['name', 'value']);
const paramName = /** @type {keyof SweetAlertOptions} */param.getAttribute('name');
const value = param.getAttribute('value');
if (!paramName || !value) {
return;
}
if (typeof defaultParams[paramName] === 'boolean') {
result[paramName] = value !== 'false';
} else if (typeof defaultParams[paramName] === 'object') {
result[paramName] = JSON.parse(value);
} else {
result[paramName] = value;
}
});
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Record<string, any>}
*/
const getSwalFunctionParams = templateContent => {
/** @type {Record<string, any>} */
const result = {};
/** @type {HTMLElement[]} */
const swalFunctions = Array.from(templateContent.querySelectorAll('swal-function-param'));
swalFunctions.forEach(param => {
const paramName = /** @type {keyof SweetAlertOptions} */param.getAttribute('name');
const value = param.getAttribute('value');
if (!paramName || !value) {
return;
}
result[paramName] = new Function(`return ${value}`)();
});
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Record<string, any>}
*/
const getSwalButtons = templateContent => {
/** @type {Record<string, any>} */
const result = {};
/** @type {HTMLElement[]} */
const swalButtons = Array.from(templateContent.querySelectorAll('swal-button'));
swalButtons.forEach(button => {
showWarningsForAttributes(button, ['type', 'color', 'aria-label']);
const type = button.getAttribute('type');
if (!type || !['confirm', 'cancel', 'deny'].includes(type)) {
return;
}
result[`${type}ButtonText`] = button.innerHTML;
result[`show${capitalizeFirstLetter(type)}Button`] = true;
if (button.hasAttribute('color')) {
result[`${type}ButtonColor`] = button.getAttribute('color');
}
if (button.hasAttribute('aria-label')) {
result[`${type}ButtonAriaLabel`] = button.getAttribute('aria-label');
}
});
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Pick<SweetAlertOptions, 'imageUrl' | 'imageWidth' | 'imageHeight' | 'imageAlt'>}
*/
const getSwalImage = templateContent => {
const result = {};
/** @type {HTMLElement | null} */
const image = templateContent.querySelector('swal-image');
if (image) {
showWarningsForAttributes(image, ['src', 'width', 'height', 'alt']);
if (image.hasAttribute('src')) {
result.imageUrl = image.getAttribute('src') || undefined;
}
if (image.hasAttribute('width')) {
result.imageWidth = image.getAttribute('width') || undefined;
}
if (image.hasAttribute('height')) {
result.imageHeight = image.getAttribute('height') || undefined;
}
if (image.hasAttribute('alt')) {
result.imageAlt = image.getAttribute('alt') || undefined;
}
}
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Record<string, any>}
*/
const getSwalIcon = templateContent => {
const result = {};
/** @type {HTMLElement | null} */
const icon = templateContent.querySelector('swal-icon');
if (icon) {
showWarningsForAttributes(icon, ['type', 'color']);
if (icon.hasAttribute('type')) {
result.icon = icon.getAttribute('type');
}
if (icon.hasAttribute('color')) {
result.iconColor = icon.getAttribute('color');
}
result.iconHtml = icon.innerHTML;
}
return result;
};
/**
* @param {DocumentFragment} templateContent
* @returns {Record<string, any>}
*/
const getSwalInput = templateContent => {
/** @type {Record<string, any>} */
const result = {};
/** @type {HTMLElement | null} */
const input = templateContent.querySelector('swal-input');
if (input) {
showWarningsForAttributes(input, ['type', 'label', 'placeholder', 'value']);
result.input = input.getAttribute('type') || 'text';
if (input.hasAttribute('label')) {
result.inputLabel = input.getAttribute('label');
}
if (input.hasAttribute('placeholder')) {
result.inputPlaceholder = input.getAttribute('placeholder');
}
if (input.hasAttribute('value')) {
result.inputValue = input.getAttribute('value');
}
}
/** @type {HTMLElement[]} */
const inputOptions = Array.from(templateContent.querySelectorAll('swal-input-option'));
if (inputOptions.length) {
result.inputOptions = {};
inputOptions.forEach(option => {
showWarningsForAttributes(option, ['value']);
const optionValue = option.getAttribute('value');
if (!optionValue) {
return;
}
const optionName = option.innerHTML;
result.inputOptions[optionValue] = optionName;
});
}
return result;
};
/**
* @param {DocumentFragment} templateContent
* @param {string[]} paramNames
* @returns {Record<string, any>}
*/
const getSwalStringParams = (templateContent, paramNames) => {
/** @type {Record<string, any>} */
const result = {};
for (const i in paramNames) {
const paramName = paramNames[i];
/** @type {HTMLElement | null} */
const tag = templateContent.querySelector(paramName);
if (tag) {
showWarningsForAttributes(tag, []);
result[paramName.replace(/^swal-/, '')] = tag.innerHTML.trim();
}
}
return result;
};
/**
* @param {DocumentFragment} templateContent
*/
const showWarningsForElements = templateContent => {
const allowedElements = swalStringParams.concat(['swal-param', 'swal-function-param', 'swal-button', 'swal-image', 'swal-icon', 'swal-input', 'swal-input-option']);
Array.from(templateContent.children).forEach(el => {
const tagName = el.tagName.toLowerCase();
if (!allowedElements.includes(tagName)) {
warn(`Unrecognized element <${tagName}>`);
}
});
};
/**
* @param {HTMLElement} el
* @param {string[]} allowedAttributes
*/
const showWarningsForAttributes = (el, allowedAttributes) => {
Array.from(el.attributes).forEach(attribute => {
if (allowedAttributes.indexOf(attribute.name) === -1) {
warn([`Unrecognized attribute "${attribute.name}" on <${el.tagName.toLowerCase()}>.`, `${allowedAttributes.length ? `Allowed attributes are: ${allowedAttributes.join(', ')}` : 'To set the value, use HTML within the element.'}`]);
}
});
};
const SHOW_CLASS_TIMEOUT = 10;
/**
* Open popup, add necessary classes and styles, fix scrollbar
*
* @param {SweetAlertOptions} params
*/
const openPopup = params => {
const container = getContainer();
const popup = getPopup();
if (typeof params.willOpen === 'function') {
params.willOpen(popup);
}
globalState.eventEmitter.emit('willOpen', popup);
const bodyStyles = window.getComputedStyle(document.body);
const initialBodyOverflow = bodyStyles.overflowY;
addClasses(container, popup, params);
// scrolling is 'hidden' until animation is done, after that 'auto'
setTimeout(() => {
setScrollingVisibility(container, popup);
}, SHOW_CLASS_TIMEOUT);
if (isModal()) {
fixScrollContainer(container, params.scrollbarPadding, initialBodyOverflow);
setAriaHidden();
}
if (!isToast() && !globalState.previousActiveElement) {
globalState.previousActiveElement = document.activeElement;
}
if (typeof params.didOpen === 'function') {
setTimeout(() => params.didOpen(popup));
}
globalState.eventEmitter.emit('didOpen', popup);
removeClass(container, swalClasses['no-transition']);
};
/**
* @param {AnimationEvent} event
*/
const swalOpenAnimationFinished = event => {
const popup = getPopup();
if (event.target !== popup) {
return;
}
const container = getContainer();
popup.removeEventListener('animationend', swalOpenAnimationFinished);
popup.removeEventListener('transitionend', swalOpenAnimationFinished);
container.style.overflowY = 'auto';
};
/**
* @param {HTMLElement} container
* @param {HTMLElement} popup
*/
const setScrollingVisibility = (container, popup) => {
if (hasCssAnimation(popup)) {
container.style.overflowY = 'hidden';
popup.addEventListener('animationend', swalOpenAnimationFinished);
popup.addEventListener('transitionend', swalOpenAnimationFinished);
} else {
container.style.overflowY = 'auto';
}
};
/**
* @param {HTMLElement} container
* @param {boolean} scrollbarPadding
* @param {string} initialBodyOverflow
*/
const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) => {
iOSfix();
if (scrollbarPadding && initialBodyOverflow !== 'hidden') {
replaceScrollbarWithPadding(initialBodyOverflow);
}
// sweetalert2/issues/1247
setTimeout(() => {
container.scrollTop = 0;
});
};
/**
* @param {HTMLElement} container
* @param {HTMLElement} popup
* @param {SweetAlertOptions} params
*/
const addClasses = (container, popup, params) => {
addClass(container, params.showClass.backdrop);
if (params.animation) {
// this workaround with opacity is needed for https://github.com/sweetalert2/sweetalert2/issues/2059
popup.style.setProperty('opacity', '0', 'important');
show(popup, 'grid');
setTimeout(() => {
// Animate popup right after showing it
addClass(popup, params.showClass.popup);
// and remove the opacity workaround
popup.style.removeProperty('opacity');
}, SHOW_CLASS_TIMEOUT); // 10ms in order to fix #2062
} else {
show(popup, 'grid');
}
addClass([document.documentElement, document.body], swalClasses.shown);
if (params.heightAuto && params.backdrop && !params.toast) {
addClass([document.documentElement, document.body], swalClasses['height-auto']);
}
};
var defaultInputValidators = {
/**
* @param {string} string
* @param {string} [validationMessage]
* @returns {Promise<string | void>}
*/
email: (string, validationMessage) => {
return /^[a-zA-Z0-9.+_'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]+$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid email address');
},
/**
* @param {string} string
* @param {string} [validationMessage]
* @returns {Promise<string | void>}
*/
url: (string, validationMessage) => {
// taken from https://stackoverflow.com/a/3809435 with a small change from #1306 and #2013
return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(string) ? Promise.resolve() : Promise.resolve(validationMessage || 'Invalid URL');
}
};
/**
* @param {SweetAlertOptions} params
*/
function setDefaultInputValidators(params) {
// Use default `inputValidator` for supported input types if not provided
if (params.inputValidator) {
return;
}
if (params.input === 'email') {
params.inputValidator = defaultInputValidators['email'];
}
if (params.input === 'url') {
params.inputValidator = defaultInputValidators['url'];
}
}
/**
* @param {SweetAlertOptions} params
*/
function validateCustomTargetElement(params) {
// Determine if the custom target element is valid
if (!params.target || typeof params.target === 'string' && !document.querySelector(params.target) || typeof params.target !== 'string' && !params.target.appendChild) {
warn('Target parameter is not valid, defaulting to "body"');
params.target = 'body';
}
}
/**
* Set type, text and actions on popup
*
* @param {SweetAlertOptions} params
*/
function setParameters(params) {
setDefaultInputValidators(params);
// showLoaderOnConfirm && preConfirm
if (params.showLoaderOnConfirm && !params.preConfirm) {
warn('showLoaderOnConfirm is set to true, but preConfirm is not defined.\n' + 'showLoaderOnConfirm should be used together with preConfirm, see usage example:\n' + 'https://sweetalert2.github.io/#ajax-request');
}
validateCustomTargetElement(params);
// Replace newlines with <br> in title
if (typeof params.title === 'string') {
params.title = params.title.split('\n').join('<br />');
}
init(params);
}
/** @type {SweetAlert} */
let currentInstance;
var _promise = /*#__PURE__*/new WeakMap();
class SweetAlert {
/**
* @param {...any} args
* @this {SweetAlert}
*/
constructor(...args) {
/**
* @type {Promise<SweetAlertResult>}
*/
_classPrivateFieldInitSpec(this, _promise, void 0);
// Prevent run in Node env
if (typeof window === 'undefined') {
return;
}
currentInstance = this;
// @ts-ignore
const outerParams = Object.freeze(this.constructor.argsToParams(args));
/** @type {Readonly<SweetAlertOptions>} */
this.params = outerParams;
/** @type {boolean} */
this.isAwaitingPromise = false;
_classPrivateFieldSet2(_promise, this, this._main(currentInstance.params));
}
_main(userParams, mixinParams = {}) {
showWarningsForParams(Object.assign({}, mixinParams, userParams));
if (globalState.currentInstance) {
const swalPromiseResolve = privateMethods.swalPromiseResolve.get(globalState.currentInstance);
const {
isAwaitingPromise
} = globalState.currentInstance;
globalState.currentInstance._destroy();
if (!isAwaitingPromise) {
swalPromiseResolve({
isDismissed: true
});
}
if (isModal()) {
unsetAriaHidden();
}
}
globalState.currentInstance = currentInstance;
const innerParams = prepareParams(userParams, mixinParams);
setParameters(innerParams);
Object.freeze(innerParams);
// clear the previous timer
if (globalState.timeout) {
globalState.timeout.stop();
delete globalState.timeout;
}
// clear the restore focus timeout
clearTimeout(globalState.restoreFocusTimeout);
const domCache = populateDomCache(currentInstance);
render(currentInstance, innerParams);
privateProps.innerParams.set(currentInstance, innerParams);
return swalPromise(currentInstance, domCache, innerParams);
}
// `catch` cannot be the name of a module export, so we define our thenable methods here instead
then(onFulfilled) {
return _classPrivateFieldGet2(_promise, this).then(onFulfilled);
}
finally(onFinally) {
return _classPrivateFieldGet2(_promise, this).finally(onFinally);
}
}
/**
* @param {SweetAlert} instance
* @param {DomCache} domCache
* @param {SweetAlertOptions} innerParams
* @returns {Promise}
*/
const swalPromise = (instance, domCache, innerParams) => {
return new Promise((resolve, reject) => {
// functions to handle all closings/dismissals
/**
* @param {DismissReason} dismiss
*/
const dismissWith = dismiss => {
instance.close({
isDismissed: true,
dismiss
});
};
privateMethods.swalPromiseResolve.set(instance, resolve);
privateMethods.swalPromiseReject.set(instance, reject);
domCache.confirmButton.onclick = () => {
handleConfirmButtonClick(instance);
};
domCache.denyButton.onclick = () => {
handleDenyButtonClick(instance);
};
domCache.cancelButton.onclick = () => {
handleCancelButtonClick(instance, dismissWith);
};
domCache.closeButton.onclick = () => {
dismissWith(DismissReason.close);
};
handlePopupClick(innerParams, domCache, dismissWith);
addKeydownHandler(globalState, innerParams, dismissWith);
handleInputOptionsAndValue(instance, innerParams);
openPopup(innerParams);
setupTimer(globalState, innerParams, dismissWith);
initFocus(domCache, innerParams);
// Scroll container to top on open (#1247, #1946)
setTimeout(() => {
domCache.container.scrollTop = 0;
});
});
};
/**
* @param {SweetAlertOptions} userParams
* @param {SweetAlertOptions} mixinParams
* @returns {SweetAlertOptions}
*/
const prepareParams = (userParams, mixinParams) => {
const templateParams = getTemplateParams(userParams);
const params = Object.assign({}, defaultParams, mixinParams, templateParams, userParams); // precedence is described in #2131
params.showClass = Object.assign({}, defaultParams.showClass, params.showClass);
params.hideClass = Object.assign({}, defaultParams.hideClass, params.hideClass);
if (params.animation === false) {
params.showClass = {
backdrop: 'swal2-noanimation'
};
params.hideClass = {};
}
return params;
};
/**
* @param {SweetAlert} instance
* @returns {DomCache}
*/
const populateDomCache = instance => {
const domCache = {
popup: getPopup(),
container: getContainer(),
actions: getActions(),
confirmButton: getConfirmButton(),
denyButton: getDenyButton(),
cancelButton: getCancelButton(),
loader: getLoader(),
closeButton: getCloseButton(),
validationMessage: getValidationMessage(),
progressSteps: getProgressSteps()
};
privateProps.domCache.set(instance, domCache);
return domCache;
};
/**
* @param {GlobalState} globalState
* @param {SweetAlertOptions} innerParams
* @param {Function} dismissWith
*/
const setupTimer = (globalState, innerParams, dismissWith) => {
const timerProgressBar = getTimerProgressBar();
hide(timerProgressBar);
if (innerParams.timer) {
globalState.timeout = new Timer(() => {
dismissWith('timer');
delete globalState.timeout;
}, innerParams.timer);
if (innerParams.timerProgressBar) {
show(timerProgressBar);
applyCustomClass(timerProgressBar, innerParams, 'timerProgressBar');
setTimeout(() => {
if (globalState.timeout && globalState.timeout.running) {
// timer can be already stopped or unset at this point
animateTimerProgressBar(innerParams.timer);
}
});
}
}
};
/**
* Initialize focus in the popup:
*
* 1. If `toast` is `true`, don't steal focus from the document.
* 2. Else if there is an [autofocus] element, focus it.
* 3. Else if `focusConfirm` is `true` and confirm button is visible, focus it.
* 4. Else if `focusDeny` is `true` and deny button is visible, focus it.
* 5. Else if `focusCancel` is `true` and cancel button is visible, focus it.
* 6. Else focus the first focusable element in a popup (if any).
*
* @param {DomCache} domCache
* @param {SweetAlertOptions} innerParams
*/
const initFocus = (domCache, innerParams) => {
if (innerParams.toast) {
return;
}
// TODO: this is dumb, remove `allowEnterKey` param in the next major version
if (!callIfFunction(innerParams.allowEnterKey)) {
warnAboutDeprecation('allowEnterKey');
blurActiveElement();
return;
}
if (focusAutofocus(domCache)) {
return;
}
if (focusButton(domCache, innerParams)) {
return;
}
setFocus(-1, 1);
};
/**
* @param {DomCache} domCache
* @returns {boolean}
*/
const focusAutofocus = domCache => {
const autofocusElements = Array.from(domCache.popup.querySelectorAll('[autofocus]'));
for (const autofocusElement of autofocusElements) {
if (autofocusElement instanceof HTMLElement && isVisible$1(autofocusElement)) {
autofocusElement.focus();
return true;
}
}
return false;
};
/**
* @param {DomCache} domCache
* @param {SweetAlertOptions} innerParams
* @returns {boolean}
*/
const focusButton = (domCache, innerParams) => {
if (innerParams.focusDeny && isVisible$1(domCache.denyButton)) {
domCache.denyButton.focus();
return true;
}
if (innerParams.focusCancel && isVisible$1(domCache.cancelButton)) {
domCache.cancelButton.focus();
return true;
}
if (innerParams.focusConfirm && isVisible$1(domCache.confirmButton)) {
domCache.confirmButton.focus();
return true;
}
return false;
};
const blurActiveElement = () => {
if (document.activeElement instanceof HTMLElement && typeof document.activeElement.blur === 'function') {
document.activeElement.blur();
}
};
// Assign instance methods from src/instanceMethods/*.js to prototype
SweetAlert.prototype.disableButtons = disableButtons;
SweetAlert.prototype.enableButtons = enableButtons;
SweetAlert.prototype.getInput = getInput;
SweetAlert.prototype.disableInput = disableInput;
SweetAlert.prototype.enableInput = enableInput;
SweetAlert.prototype.hideLoading = hideLoading;
SweetAlert.prototype.disableLoading = hideLoading;
SweetAlert.prototype.showValidationMessage = showValidationMessage;
SweetAlert.prototype.resetValidationMessage = resetValidationMessage;
SweetAlert.prototype.close = close;
SweetAlert.prototype.closePopup = close;
SweetAlert.prototype.closeModal = close;
SweetAlert.prototype.closeToast = close;
SweetAlert.prototype.rejectPromise = rejectPromise;
SweetAlert.prototype.update = update;
SweetAlert.prototype._destroy = _destroy;
// Assign static methods from src/staticMethods/*.js to constructor
Object.assign(SweetAlert, staticMethods);
// Proxy to instance methods to constructor, for now, for backwards compatibility
Object.keys(instanceMethods).forEach(key => {
/**
* @param {...any} args
* @returns {any | undefined}
*/
SweetAlert[key] = function (...args) {
if (currentInstance && currentInstance[key]) {
return currentInstance[key](...args);
}
return null;
};
});
SweetAlert.DismissReason = DismissReason;
SweetAlert.version = '11.22.4';
const Swal = SweetAlert;
// @ts-ignore
Swal.default = Swal;
"undefined"!=typeof document&&function(e,t){var n=e.createElement("style");if(e.getElementsByTagName("head")[0].appendChild(n),n.styleSheet)n.styleSheet.disabled||(n.styleSheet.cssText=t);else try{n.innerHTML=t;}catch(e){n.innerText=t;}}(document,":root{--swal2-outline: 0 0 0 3px rgba(100, 150, 200, 0.5);--swal2-container-padding: 0.625em;--swal2-backdrop: rgba(0, 0, 0, 0.4);--swal2-backdrop-transition: background-color 0.1s;--swal2-width: 32em;--swal2-padding: 0 0 1.25em;--swal2-border: none;--swal2-border-radius: 0.3125rem;--swal2-background: white;--swal2-color: #545454;--swal2-show-animation: swal2-show 0.3s;--swal2-hide-animation: swal2-hide 0.15s forwards;--swal2-icon-zoom: 1;--swal2-icon-animations: true;--swal2-title-padding: 0.8em 1em 0;--swal2-html-container-padding: 1em 1.6em 0.3em;--swal2-input-border: 1px solid #d9d9d9;--swal2-input-border-radius: 0.1875em;--swal2-input-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-background: transparent;--swal2-input-transition: border-color 0.2s, box-shadow 0.2s;--swal2-input-hover-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px transparent;--swal2-input-focus-border: 1px solid #b4dbed;--swal2-input-focus-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06), 0 0 0 3px $swal2-outline-color;--swal2-progress-step-background: #add8e6;--swal2-validation-message-background: #f0f0f0;--swal2-validation-message-color: #666;--swal2-footer-border-color: #eee;--swal2-footer-background: transparent;--swal2-footer-color: inherit;--swal2-timer-progress-bar-background: rgba(0, 0, 0, 0.3);--swal2-close-button-position: initial;--swal2-close-button-inset: auto;--swal2-close-button-font-size: 2.5em;--swal2-close-button-color: #ccc;--swal2-close-button-transition: color 0.2s, box-shadow 0.2s;--swal2-close-button-outline: initial;--swal2-close-button-box-shadow: inset 0 0 0 3px transparent;--swal2-close-button-focus-box-shadow: inset var(--swal2-outline);--swal2-close-button-hover-transform: none;--swal2-actions-justify-content: center;--swal2-actions-width: auto;--swal2-actions-margin: 1.25em auto 0;--swal2-actions-padding: 0;--swal2-actions-border-radius: 0;--swal2-actions-background: transparent;--swal2-action-button-transition: background-color 0.2s, box-shadow 0.2s;--swal2-action-button-hover: black 10%;--swal2-action-button-active: black 10%;--swal2-confirm-button-box-shadow: none;--swal2-confirm-button-border-radius: 0.25em;--swal2-confirm-button-background-color: #7066e0;--swal2-confirm-button-color: #fff;--swal2-deny-button-box-shadow: none;--swal2-deny-button-border-radius: 0.25em;--swal2-deny-button-background-color: #dc3741;--swal2-deny-button-color: #fff;--swal2-cancel-button-box-shadow: none;--swal2-cancel-button-border-radius: 0.25em;--swal2-cancel-button-background-color: #6e7881;--swal2-cancel-button-color: #fff;--swal2-toast-show-animation: swal2-toast-show 0.5s;--swal2-toast-hide-animation: swal2-toast-hide 0.1s forwards;--swal2-toast-border: none;--swal2-toast-box-shadow: 0 0 1px hsl(0deg 0% 0% / 0.075), 0 1px 2px hsl(0deg 0% 0% / 0.075), 1px 2px 4px hsl(0deg 0% 0% / 0.075), 1px 3px 8px hsl(0deg 0% 0% / 0.075), 2px 4px 16px hsl(0deg 0% 0% / 0.075)}[data-swal2-theme=dark]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white);--swal2-timer-progress-bar-background: rgba(255, 255, 255, 0.7)}@media(prefers-color-scheme: dark){[data-swal2-theme=auto]{--swal2-dark-theme-black: #19191a;--swal2-dark-theme-white: #e1e1e1;--swal2-background: var(--swal2-dark-theme-black);--swal2-color: var(--swal2-dark-theme-white);--swal2-footer-border-color: #555;--swal2-input-background: color-mix(in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10%);--swal2-validation-message-background: color-mix( in srgb, var(--swal2-dark-theme-black), var(--swal2-dark-theme-white) 10% );--swal2-validation-message-color: var(--swal2-dark-theme-white);--swal2-timer-progress-bar-background: rgba(255, 255, 255, 0.7)}}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown){overflow:hidden}body.swal2-height-auto{height:auto !important}body.swal2-no-backdrop .swal2-container{background-color:rgba(0,0,0,0) !important;pointer-events:none}body.swal2-no-backdrop .swal2-container .swal2-popup{pointer-events:all}body.swal2-no-backdrop .swal2-container .swal2-modal{box-shadow:0 0 10px var(--swal2-backdrop)}body.swal2-toast-shown .swal2-container{box-sizing:border-box;width:360px;max-width:100%;background-color:rgba(0,0,0,0);pointer-events:none}body.swal2-toast-shown .swal2-container.swal2-top{inset:0 auto auto 50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-top-end,body.swal2-toast-shown .swal2-container.swal2-top-right{inset:0 0 auto auto}body.swal2-toast-shown .swal2-container.swal2-top-start,body.swal2-toast-shown .swal2-container.swal2-top-left{inset:0 auto auto 0}body.swal2-toast-shown .swal2-container.swal2-center-start,body.swal2-toast-shown .swal2-container.swal2-center-left{inset:50% auto auto 0;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-center{inset:50% auto auto 50%;transform:translate(-50%, -50%)}body.swal2-toast-shown .swal2-container.swal2-center-end,body.swal2-toast-shown .swal2-container.swal2-center-right{inset:50% 0 auto auto;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-start,body.swal2-toast-shown .swal2-container.swal2-bottom-left{inset:auto auto 0 0}body.swal2-toast-shown .swal2-container.swal2-bottom{inset:auto auto 0 50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-end,body.swal2-toast-shown .swal2-container.swal2-bottom-right{inset:auto 0 0 auto}@media print{body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown){overflow-y:scroll !important}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown)>[aria-hidden=true]{display:none}body.swal2-shown:not(.swal2-no-backdrop,.swal2-toast-shown) .swal2-container{position:static !important}}div:where(.swal2-container){display:grid;position:fixed;z-index:1060;inset:0;box-sizing:border-box;grid-template-areas:\"top-start top top-end\" \"center-start center center-end\" \"bottom-start bottom-center bottom-end\";grid-template-rows:minmax(min-content, auto) minmax(min-content, auto) minmax(min-content, auto);height:100%;padding:var(--swal2-container-padding);overflow-x:hidden;transition:var(--swal2-backdrop-transition);-webkit-overflow-scrolling:touch}div:where(.swal2-container).swal2-backdrop-show,div:where(.swal2-container).swal2-noanimation{background:var(--swal2-backdrop)}div:where(.swal2-container).swal2-backdrop-hide{background:rgba(0,0,0,0) !important}div:where(.swal2-container).swal2-top-start,div:where(.swal2-container).swal2-center-start,div:where(.swal2-container).swal2-bottom-start{grid-template-columns:minmax(0, 1fr) auto auto}div:where(.swal2-container).swal2-top,div:where(.swal2-container).swal2-center,div:where(.swal2-container).swal2-bottom{grid-template-columns:auto minmax(0, 1fr) auto}div:where(.swal2-container).swal2-top-end,div:where(.swal2-container).swal2-center-end,div:where(.swal2-container).swal2-bottom-end{grid-template-columns:auto auto minmax(0, 1fr)}div:where(.swal2-container).swal2-top-start>.swal2-popup{align-self:start}div:where(.swal2-container).swal2-top>.swal2-popup{grid-column:2;place-self:start center}div:where(.swal2-container).swal2-top-end>.swal2-popup,div:where(.swal2-container).swal2-top-right>.swal2-popup{grid-column:3;place-self:start end}div:where(.swal2-container).swal2-center-start>.swal2-popup,div:where(.swal2-container).swal2-center-left>.swal2-popup{grid-row:2;align-self:center}div:where(.swal2-container).swal2-center>.swal2-popup{grid-column:2;grid-row:2;place-self:center center}div:where(.swal2-container).swal2-center-end>.swal2-popup,div:where(.swal2-container).swal2-center-right>.swal2-popup{grid-column:3;grid-row:2;place-self:center end}div:where(.swal2-container).swal2-bottom-start>.swal2-popup,div:where(.swal2-container).swal2-bottom-left>.swal2-popup{grid-column:1;grid-row:3;align-self:end}div:where(.swal2-container).swal2-bottom>.swal2-popup{grid-column:2;grid-row:3;place-self:end center}div:where(.swal2-container).swal2-bottom-end>.swal2-popup,div:where(.swal2-container).swal2-bottom-right>.swal2-popup{grid-column:3;grid-row:3;place-self:end end}div:where(.swal2-container).swal2-grow-row>.swal2-popup,div:where(.swal2-container).swal2-grow-fullscreen>.swal2-popup{grid-column:1/4;width:100%}div:where(.swal2-container).swal2-grow-column>.swal2-popup,div:where(.swal2-container).swal2-grow-fullscreen>.swal2-popup{grid-row:1/4;align-self:stretch}div:where(.swal2-container).swal2-no-transition{transition:none !important}div:where(.swal2-container)[popover]{width:auto;border:0}div:where(.swal2-container) div:where(.swal2-popup){display:none;position:relative;box-sizing:border-box;grid-template-columns:minmax(0, 100%);width:var(--swal2-width);max-width:100%;padding:var(--swal2-padding);border:var(--swal2-border);border-radius:var(--swal2-border-radius);background:var(--swal2-background);color:var(--swal2-color);font-family:inherit;font-size:1rem;container-name:swal2-popup}div:where(.swal2-container) div:where(.swal2-popup):focus{outline:none}div:where(.swal2-container) div:where(.swal2-popup).swal2-loading{overflow-y:hidden}div:where(.swal2-container) div:where(.swal2-popup).swal2-draggable{cursor:grab}div:where(.swal2-container) div:where(.swal2-popup).swal2-draggable div:where(.swal2-icon){cursor:grab}div:where(.swal2-container) div:where(.swal2-popup).swal2-dragging{cursor:grabbing}div:where(.swal2-container) div:where(.swal2-popup).swal2-dragging div:where(.swal2-icon){cursor:grabbing}div:where(.swal2-container) h2:where(.swal2-title){position:relative;max-width:100%;margin:0;padding:var(--swal2-title-padding);color:inherit;font-size:1.875em;font-weight:600;text-align:center;text-transform:none;word-wrap:break-word;cursor:initial}div:where(.swal2-container) div:where(.swal2-actions){display:flex;z-index:1;box-sizing:border-box;flex-wrap:wrap;align-items:center;justify-content:var(--swal2-actions-justify-content);width:var(--swal2-actions-width);margin:var(--swal2-actions-margin);padding:var(--swal2-actions-padding);border-radius:var(--swal2-actions-border-radius);background:var(--swal2-actions-background)}div:where(.swal2-container) div:where(.swal2-loader){display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:#2778c4 rgba(0,0,0,0) #2778c4 rgba(0,0,0,0)}div:where(.swal2-container) button:where(.swal2-styled){margin:.3125em;padding:.625em 1.1em;transition:var(--swal2-action-button-transition);border:none;box-shadow:0 0 0 3px rgba(0,0,0,0);font-weight:500}div:where(.swal2-container) button:where(.swal2-styled):not([disabled]){cursor:pointer}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm){border-radius:var(--swal2-confirm-button-border-radius);background:initial;background-color:var(--swal2-confirm-button-background-color);box-shadow:var(--swal2-confirm-button-box-shadow);color:var(--swal2-confirm-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm):hover{background-color:color-mix(in srgb, var(--swal2-confirm-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm):active{background-color:color-mix(in srgb, var(--swal2-confirm-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny){border-radius:var(--swal2-deny-button-border-radius);background:initial;background-color:var(--swal2-deny-button-background-color);box-shadow:var(--swal2-deny-button-box-shadow);color:var(--swal2-deny-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny):hover{background-color:color-mix(in srgb, var(--swal2-deny-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny):active{background-color:color-mix(in srgb, var(--swal2-deny-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel){border-radius:var(--swal2-cancel-button-border-radius);background:initial;background-color:var(--swal2-cancel-button-background-color);box-shadow:var(--swal2-cancel-button-box-shadow);color:var(--swal2-cancel-button-color);font-size:1em}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel):hover{background-color:color-mix(in srgb, var(--swal2-cancel-button-background-color), var(--swal2-action-button-hover))}div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel):active{background-color:color-mix(in srgb, var(--swal2-cancel-button-background-color), var(--swal2-action-button-active))}div:where(.swal2-container) button:where(.swal2-styled):focus-visible{outline:none;box-shadow:var(--swal2-action-button-focus-box-shadow)}div:where(.swal2-container) button:where(.swal2-styled)[disabled]:not(.swal2-loading){opacity:.4}div:where(.swal2-container) button:where(.swal2-styled)::-moz-focus-inner{border:0}div:where(.swal2-container) div:where(.swal2-footer){margin:1em 0 0;padding:1em 1em 0;border-top:1px solid var(--swal2-footer-border-color);background:var(--swal2-footer-background);color:var(--swal2-footer-color);font-size:1em;text-align:center;cursor:initial}div:where(.swal2-container) .swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto !important;overflow:hidden;border-bottom-right-radius:var(--swal2-border-radius);border-bottom-left-radius:var(--swal2-border-radius)}div:where(.swal2-container) div:where(.swal2-timer-progress-bar){width:100%;height:.25em;background:var(--swal2-timer-progress-bar-background)}div:where(.swal2-container) img:where(.swal2-image){max-width:100%;margin:2em auto 1em;cursor:initial}div:where(.swal2-container) button:where(.swal2-close){position:var(--swal2-close-button-position);inset:var(--swal2-close-button-inset);z-index:2;align-items:center;justify-content:center;width:1.2em;height:1.2em;margin-top:0;margin-right:0;margin-bottom:-1.2em;padding:0;overflow:hidden;transition:var(--swal2-close-button-transition);border:none;border-radius:var(--swal2-border-radius);outline:var(--swal2-close-button-outline);background:rgba(0,0,0,0);color:var(--swal2-close-button-color);font-family:monospace;font-size:var(--swal2-close-button-font-size);cursor:pointer;justify-self:end}div:where(.swal2-container) button:where(.swal2-close):hover{transform:var(--swal2-close-button-hover-transform);background:rgba(0,0,0,0);color:#f27474}div:where(.swal2-container) button:where(.swal2-close):focus-visible{outline:none;box-shadow:var(--swal2-close-button-focus-box-shadow)}div:where(.swal2-container) button:where(.swal2-close)::-moz-focus-inner{border:0}div:where(.swal2-container) div:where(.swal2-html-container){z-index:1;justify-content:center;margin:0;padding:var(--swal2-html-container-padding);overflow:auto;color:inherit;font-size:1.125em;font-weight:normal;line-height:normal;text-align:center;word-wrap:break-word;word-break:break-word;cursor:initial}div:where(.swal2-container) input:where(.swal2-input),div:where(.swal2-container) input:where(.swal2-file),div:where(.swal2-container) textarea:where(.swal2-textarea),div:where(.swal2-container) select:where(.swal2-select),div:where(.swal2-container) div:where(.swal2-radio),div:where(.swal2-container) label:where(.swal2-checkbox){margin:1em 2em 3px}div:where(.swal2-container) input:where(.swal2-input),div:where(.swal2-container) input:where(.swal2-file),div:where(.swal2-container) textarea:where(.swal2-textarea){box-sizing:border-box;width:auto;transition:var(--swal2-input-transition);border:var(--swal2-input-border);border-radius:var(--swal2-input-border-radius);background:var(--swal2-input-background);box-shadow:var(--swal2-input-box-shadow);color:inherit;font-size:1.125em}div:where(.swal2-container) input:where(.swal2-input).swal2-inputerror,div:where(.swal2-container) input:where(.swal2-file).swal2-inputerror,div:where(.swal2-container) textarea:where(.swal2-textarea).swal2-inputerror{border-color:#f27474 !important;box-shadow:0 0 2px #f27474 !important}div:where(.swal2-container) input:where(.swal2-input):hover,div:where(.swal2-container) input:where(.swal2-file):hover,div:where(.swal2-container) textarea:where(.swal2-textarea):hover{box-shadow:var(--swal2-input-hover-box-shadow)}div:where(.swal2-container) input:where(.swal2-input):focus,div:where(.swal2-container) input:where(.swal2-file):focus,div:where(.swal2-container) textarea:where(.swal2-textarea):focus{border:var(--swal2-input-focus-border);outline:none;box-shadow:var(--swal2-input-focus-box-shadow)}div:where(.swal2-container) input:where(.swal2-input)::placeholder,div:where(.swal2-container) input:where(.swal2-file)::placeholder,div:where(.swal2-container) textarea:where(.swal2-textarea)::placeholder{color:#ccc}div:where(.swal2-container) .swal2-range{margin:1em 2em 3px;background:var(--swal2-background)}div:where(.swal2-container) .swal2-range input{width:80%}div:where(.swal2-container) .swal2-range output{width:20%;color:inherit;font-weight:600;text-align:center}div:where(.swal2-container) .swal2-range input,div:where(.swal2-container) .swal2-range output{height:2.625em;padding:0;font-size:1.125em;line-height:2.625em}div:where(.swal2-container) .swal2-input{height:2.625em;padding:0 .75em}div:where(.swal2-container) .swal2-file{width:75%;margin-right:auto;margin-left:auto;background:var(--swal2-input-background);font-size:1.125em}div:where(.swal2-container) .swal2-textarea{height:6.75em;padding:.75em}div:where(.swal2-container) .swal2-select{min-width:50%;max-width:100%;padding:.375em .625em;background:var(--swal2-input-background);color:inherit;font-size:1.125em}div:where(.swal2-container) .swal2-radio,div:where(.swal2-container) .swal2-checkbox{align-items:center;justify-content:center;background:var(--swal2-background);color:inherit}div:where(.swal2-container) .swal2-radio label,div:where(.swal2-container) .swal2-checkbox label{margin:0 .6em;font-size:1.125em}div:where(.swal2-container) .swal2-radio input,div:where(.swal2-container) .swal2-checkbox input{flex-shrink:0;margin:0 .4em}div:where(.swal2-container) label:where(.swal2-input-label){display:flex;justify-content:center;margin:1em auto 0}div:where(.swal2-container) div:where(.swal2-validation-message){align-items:center;justify-content:center;margin:1em 0 0;padding:.625em;overflow:hidden;background:var(--swal2-validation-message-background);color:var(--swal2-validation-message-color);font-size:1em;font-weight:300}div:where(.swal2-container) div:where(.swal2-validation-message)::before{content:\"!\";display:inline-block;width:1.5em;min-width:1.5em;height:1.5em;margin:0 .625em;border-radius:50%;background-color:#f27474;color:#fff;font-weight:600;line-height:1.5em;text-align:center}div:where(.swal2-container) .swal2-progress-steps{flex-wrap:wrap;align-items:center;max-width:100%;margin:1.25em auto;padding:0;background:rgba(0,0,0,0);font-weight:600}div:where(.swal2-container) .swal2-progress-steps li{display:inline-block;position:relative}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:#2778c4;color:#fff;line-height:2em;text-align:center}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:#2778c4}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step{background:var(--swal2-progress-step-background);color:#fff}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step-line{background:var(--swal2-progress-step-background)}div:where(.swal2-container) .swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:#2778c4}div:where(.swal2-icon){position:relative;box-sizing:content-box;justify-content:center;width:5em;height:5em;margin:2.5em auto .6em;zoom:var(--swal2-icon-zoom);border:.25em solid rgba(0,0,0,0);border-radius:50%;border-color:#000;font-family:inherit;line-height:5em;cursor:default;user-select:none}div:where(.swal2-icon) .swal2-icon-content{display:flex;align-items:center;font-size:3.75em}div:where(.swal2-icon).swal2-error{border-color:#f27474;color:#f27474}div:where(.swal2-icon).swal2-error .swal2-x-mark{position:relative;flex-grow:1}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line]{display:block;position:absolute;top:2.3125em;width:2.9375em;height:.3125em;border-radius:.125em;background-color:#f27474}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line][class$=left]{left:1.0625em;transform:rotate(45deg)}div:where(.swal2-icon).swal2-error [class^=swal2-x-mark-line][class$=right]{right:1em;transform:rotate(-45deg)}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-error.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-error.swal2-icon-show .swal2-x-mark{animation:swal2-animate-error-x-mark .5s}}div:where(.swal2-icon).swal2-warning{border-color:#f8bb86;color:#f8bb86}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-warning.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-warning.swal2-icon-show .swal2-icon-content{animation:swal2-animate-i-mark .5s}}div:where(.swal2-icon).swal2-info{border-color:#3fc3ee;color:#3fc3ee}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-info.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-info.swal2-icon-show .swal2-icon-content{animation:swal2-animate-i-mark .8s}}div:where(.swal2-icon).swal2-question{border-color:#87adbd;color:#87adbd}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-question.swal2-icon-show{animation:swal2-animate-error-icon .5s}div:where(.swal2-icon).swal2-question.swal2-icon-show .swal2-icon-content{animation:swal2-animate-question-mark .8s}}div:where(.swal2-icon).swal2-success{border-color:#a5dc86;color:#a5dc86}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line]{position:absolute;width:3.75em;height:7.5em;border-radius:50%}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line][class$=left]{top:-0.4375em;left:-2.0635em;transform:rotate(-45deg);transform-origin:3.75em 3.75em;border-radius:7.5em 0 0 7.5em}div:where(.swal2-icon).swal2-success [class^=swal2-success-circular-line][class$=right]{top:-0.6875em;left:1.875em;transform:rotate(-45deg);transform-origin:0 3.75em;border-radius:0 7.5em 7.5em 0}div:where(.swal2-icon).swal2-success .swal2-success-ring{position:absolute;z-index:2;top:-0.25em;left:-0.25em;box-sizing:content-box;width:100%;height:100%;border:.25em solid rgba(165,220,134,.3);border-radius:50%}div:where(.swal2-icon).swal2-success .swal2-success-fix{position:absolute;z-index:1;top:.5em;left:1.625em;width:.4375em;height:5.625em;transform:rotate(-45deg)}div:where(.swal2-icon).swal2-success [class^=swal2-success-line]{display:block;position:absolute;z-index:2;height:.3125em;border-radius:.125em;background-color:#a5dc86}div:where(.swal2-icon).swal2-success [class^=swal2-success-line][class$=tip]{top:2.875em;left:.8125em;width:1.5625em;transform:rotate(45deg)}div:where(.swal2-icon).swal2-success [class^=swal2-success-line][class$=long]{top:2.375em;right:.5em;width:2.9375em;transform:rotate(-45deg)}@container swal2-popup style(--swal2-icon-animations:true){div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-line-tip{animation:swal2-animate-success-line-tip .75s}div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-line-long{animation:swal2-animate-success-line-long .75s}div:where(.swal2-icon).swal2-success.swal2-icon-show .swal2-success-circular-line-right{animation:swal2-rotate-success-circular-line 4.25s ease-in}}[class^=swal2]{-webkit-tap-highlight-color:rgba(0,0,0,0)}.swal2-show{animation:var(--swal2-show-animation)}.swal2-hide{animation:var(--swal2-hide-animation)}.swal2-noanimation{transition:none}.swal2-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.swal2-rtl .swal2-close{margin-right:initial;margin-left:0}.swal2-rtl .swal2-timer-progress-bar{right:0;left:auto}.swal2-toast{box-sizing:border-box;grid-column:1/4 !important;grid-row:1/4 !important;grid-template-columns:min-content auto min-content;padding:1em;overflow-y:hidden;border:var(--swal2-toast-border);background:var(--swal2-background);box-shadow:var(--swal2-toast-box-shadow);pointer-events:all}.swal2-toast>*{grid-column:2}.swal2-toast h2:where(.swal2-title){margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-toast .swal2-loading{justify-content:center}.swal2-toast input:where(.swal2-input){height:2em;margin:.5em;font-size:1em}.swal2-toast .swal2-validation-message{font-size:1em}.swal2-toast div:where(.swal2-footer){margin:.5em 0 0;padding:.5em 0 0;font-size:.8em}.swal2-toast button:where(.swal2-close){grid-column:3/3;grid-row:1/99;align-self:center;width:.8em;height:.8em;margin:0;font-size:2em}.swal2-toast div:where(.swal2-html-container){margin:.5em 1em;padding:0;overflow:initial;font-size:1em;text-align:initial}.swal2-toast div:where(.swal2-html-container):empty{padding:0}.swal2-toast .swal2-loader{grid-column:1;grid-row:1/99;align-self:center;width:2em;height:2em;margin:.25em}.swal2-toast .swal2-icon{grid-column:1;grid-row:1/99;align-self:center;width:2em;min-width:2em;height:2em;margin:0 .5em 0 0}.swal2-toast .swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:1.8em;font-weight:bold}.swal2-toast .swal2-icon.swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line]{top:.875em;width:1.375em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:.3125em}.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:.3125em}.swal2-toast div:where(.swal2-actions){justify-content:flex-start;height:auto;margin:0;margin-top:.5em;padding:0 .5em}.swal2-toast button:where(.swal2-styled){margin:.25em .5em;padding:.4em .6em;font-size:1em}.swal2-toast .swal2-success{border-color:#a5dc86}.swal2-toast .swal2-success [class^=swal2-success-circular-line]{position:absolute;width:1.6em;height:3em;border-radius:50%}.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left]{top:-0.8em;left:-0.5em;transform:rotate(-45deg);transform-origin:2em 2em;border-radius:4em 0 0 4em}.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right]{top:-0.25em;left:.9375em;transform-origin:0 1.5em;border-radius:0 4em 4em 0}.swal2-toast .swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-toast .swal2-success .swal2-success-fix{top:0;left:.4375em;width:.4375em;height:2.6875em}.swal2-toast .swal2-success [class^=swal2-success-line]{height:.3125em}.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip]{top:1.125em;left:.1875em;width:.75em}.swal2-toast .swal2-success [class^=swal2-success-line][class$=long]{top:.9375em;right:.1875em;width:1.375em}@container swal2-popup style(--swal2-icon-animations:true){.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip{animation:swal2-toast-animate-success-line-tip .75s}.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long{animation:swal2-toast-animate-success-line-long .75s}}.swal2-toast.swal2-show{animation:var(--swal2-toast-show-animation)}.swal2-toast.swal2-hide{animation:var(--swal2-toast-hide-animation)}@keyframes swal2-show{0%{transform:scale(0.7)}45%{transform:scale(1.05)}80%{transform:scale(0.95)}100%{transform:scale(1)}}@keyframes swal2-hide{0%{transform:scale(1);opacity:1}100%{transform:scale(0.5);opacity:0}}@keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-0.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(0.4);opacity:0}50%{margin-top:1.625em;transform:scale(0.4);opacity:0}80%{margin-top:-0.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0deg);opacity:1}}@keyframes swal2-rotate-loading{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-toast-show{0%{transform:translateY(-0.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(0.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0deg)}}@keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-0.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}");
const STORAGE_KEYS = {
pairing: "pairingRows",
wallet: "walletRows",
process: "processRows",
data: "dataRows"
};
const defaultRows = [
{
column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrz",
column2: "🎊😑🎄😩",
column3: "Laptop"
},
{
column1: "sprt1qqwtvg5q5vcz0reqvmld98u7va3av6gakwe9yxw9yhnpj5djcunn4squ68tuzn8dz78dg4adfv0dekx8hg9sy0t6s9k5em7rffgxmrsfpyy7gtyrx",
column2: "🎏🎕😧🌥",
column3: "Phone"
}
];
const mockDataRows = [
{
column1: "User Project",
column2: "private",
column3: "User",
column4: "6 months",
column5: "NDA signed",
column6: "Contract #123",
processName: "User Process",
zone: "A"
},
{
column1: "Process Project",
column2: "private",
column3: "Process",
column4: "1 year",
column5: "Terms accepted",
column6: "Contract #456",
processName: "Process Management",
zone: "B"
},
{
column1: "Member Project",
column2: "private",
column3: "Member",
column4: "3 months",
column5: "GDPR compliant",
column6: "Contract #789",
processName: "Member Process",
zone: "C"
},
{
column1: "Peer Project",
column2: "public",
column3: "Peer",
column4: "2 years",
column5: "IP rights",
column6: "Contract #101",
processName: "Peer Process",
zone: "D"
},
{
column1: "Payment Project",
column2: "confidential",
column3: "Payment",
column4: "1 year",
column5: "NDA signed",
column6: "Contract #102",
processName: "Payment Process",
zone: "E"
},
{
column1: "Deposit Project",
column2: "private",
column3: "Deposit",
column4: "6 months",
column5: "Terms accepted",
column6: "Contract #103",
processName: "Deposit Process",
zone: "F"
},
{
column1: "Artefact Project",
column2: "public",
column3: "Artefact",
column4: "1 year",
column5: "GDPR compliant",
column6: "Contract #104",
processName: "Artefact Process",
zone: "G"
},
{
column1: "Resolve Project",
column2: "private",
column3: "Resolve",
column4: "2 years",
column5: "IP rights",
column6: "Contract #105",
processName: "Resolve Process",
zone: "H"
},
{
column1: "Backup Project",
column2: "public",
column3: "Backup",
column4: "1 year",
column5: "NDA signed",
column6: "Contract #106",
processName: "Backup Process",
zone: "I"
}
];
const mockProcessRows = [
{
process: "User Project",
role: "User",
notification: {
messages: [
{ id: 1, read: false, date: "2024-03-10", message: "New user joined the project" },
{ id: 2, read: false, date: "2024-03-09", message: "Project milestone reached" },
{ id: 3, read: false, date: "2024-03-08", message: "Security update required" },
{ id: 4, read: true, date: "2024-03-07", message: "Weekly report available" },
{ id: 5, read: true, date: "2024-03-06", message: "Team meeting scheduled" }
]
}
},
{
process: "Member Project",
role: "Member",
notification: {
messages: [
{ id: 6, read: true, date: "2024-03-10", message: "Member access granted" },
{ id: 7, read: true, date: "2024-03-09", message: "Documentation updated" },
{ id: 8, read: true, date: "2024-03-08", message: "Project status: on track" }
]
}
},
{
process: "Peer Project",
role: "Peer",
notification: {
unread: 2,
total: 4,
messages: [
{ id: 9, read: false, date: "2024-03-10", message: "New peer project added" },
{ id: 10, read: false, date: "2024-03-09", message: "Project milestone reached" },
{ id: 11, read: false, date: "2024-03-08", message: "Security update required" },
{ id: 12, read: true, date: "2024-03-07", message: "Weekly report available" },
{ id: 13, read: true, date: "2024-03-06", message: "Team meeting scheduled" }
]
}
},
{
process: "Deposit Project",
role: "Deposit",
notification: {
unread: 1,
total: 10,
messages: [
{ id: 14, read: false, date: "2024-03-10", message: "Deposit milestone reached" },
{ id: 15, read: false, date: "2024-03-09", message: "Security update required" },
{ id: 16, read: false, date: "2024-03-08", message: "Weekly report available" },
{ id: 17, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
{ id: 18, read: true, date: "2024-03-06", message: "Project status: on track" }
]
}
},
{
process: "Artefact Project",
role: "Artefact",
notification: {
unread: 0,
total: 3,
messages: [
{ id: 19, read: false, date: "2024-03-10", message: "New artefact added" },
{ id: 20, read: false, date: "2024-03-09", message: "Security update required" },
{ id: 21, read: false, date: "2024-03-08", message: "Weekly report available" },
{ id: 22, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
{ id: 23, read: true, date: "2024-03-06", message: "Project status: on track" }
]
}
},
{
process: "Resolve Project",
role: "Resolve",
notification: {
unread: 5,
total: 12,
messages: [
{ id: 24, read: false, date: "2024-03-10", message: "New issue reported" },
{ id: 25, read: false, date: "2024-03-09", message: "Security update required" },
{ id: 26, read: false, date: "2024-03-08", message: "Weekly report available" },
{ id: 27, read: true, date: "2024-03-07", message: "Team meeting scheduled" },
{ id: 28, read: true, date: "2024-03-06", message: "Project status: on track" }
]
}
}
];
const mockContracts = {
"Contract #123": {
title: "User Project Agreement",
date: "2024-01-15",
parties: ["Company XYZ", "User Team"],
terms: ["Data Protection", "User Privacy", "Access Rights", "Service Level Agreement"],
content: "This agreement establishes the terms and conditions for user project management."
},
"Contract #456": {
title: "Process Management Contract",
date: "2024-02-01",
parties: ["Company XYZ", "Process Team"],
terms: ["Process Workflow", "Quality Standards", "Performance Metrics", "Monitoring Procedures"],
content: "This contract defines the process management standards and procedures."
},
"Contract #789": {
title: "Member Access Agreement",
date: "2024-03-15",
parties: ["Company XYZ", "Member Team"],
terms: ["Member Rights", "Access Levels", "Security Protocol", "Confidentiality Agreement"],
content: "This agreement outlines the terms for member access and privileges."
},
"Contract #101": {
title: "Peer Collaboration Agreement",
date: "2024-04-01",
parties: ["Company XYZ", "Peer Network"],
terms: ["Collaboration Rules", "Resource Sharing", "Dispute Resolution", "Network Protocol"],
content: "This contract establishes peer collaboration and networking guidelines."
},
"Contract #102": {
title: "Payment Processing Agreement",
date: "2024-05-01",
parties: ["Company XYZ", "Payment Team"],
terms: ["Transaction Protocol", "Security Measures", "Fee Structure", "Service Availability"],
content: "This agreement defines payment processing terms and conditions."
},
"Contract #103": {
title: "Deposit Management Contract",
date: "2024-06-01",
parties: ["Company XYZ", "Deposit Team"],
terms: ["Deposit Rules", "Storage Protocol", "Access Control", "Security Standards"],
content: "This contract outlines deposit management procedures and security measures."
},
"Contract #104": {
title: "Artefact Handling Agreement",
date: "2024-07-01",
parties: ["Company XYZ", "Artefact Team"],
terms: ["Handling Procedures", "Storage Guidelines", "Access Protocol", "Preservation Standards"],
content: "This agreement establishes artefact handling and preservation guidelines."
},
"Contract #105": {
title: "Resolution Protocol Agreement",
date: "2024-08-01",
parties: ["Company XYZ", "Resolution Team"],
terms: ["Resolution Process", "Time Constraints", "Escalation Protocol", "Documentation Requirements"],
content: "This contract defines the resolution process and protocol standards."
},
"Contract #106": {
title: "Backup Service Agreement",
date: "2024-09-01",
parties: ["Company XYZ", "Backup Team"],
terms: ["Backup Schedule", "Data Protection", "Recovery Protocol", "Service Reliability"],
content: "This agreement outlines backup service terms and recovery procedures."
}
};
function getCorrectDOM(componentTag) {
const dom = document?.querySelector(componentTag)?.shadowRoot || document;
return dom;
}
const accountStyle = "/* Styles de base */\r\n:root {\r\n --primary-color: #3A506B;\r\n /* Bleu métallique */\r\n --secondary-color: #B0BEC5;\r\n /* Gris acier */\r\n --accent-color: #D68C45;\r\n /* Cuivre */\r\n}\r\n\r\nbody {\r\n font-family: Arial, sans-serif;\r\n margin: 0;\r\n padding: 0;\r\n display: flex;\r\n height: 100vh;\r\n background-color: #e9edf1;\r\n flex-direction: column;\r\n}\r\n\r\n/*-------------------------------------- Avatar--------------------------------------*/\r\n\r\n.avatar-section {\r\n position: relative;\r\n height: 60px;\r\n width: 260px;\r\n margin-left: 10px;\r\n overflow: hidden;\r\n border-radius: 10px;\r\n}\r\n\r\n.user-info {\r\n display: flex;\r\n flex-direction: column;\r\n color: white;\r\n}\r\n\r\n.user-name, .user-lastname {\r\n font-size: 0.9rem;\r\n font-weight: 700;\r\n}\r\n\r\n\r\n.user-name:hover, .user-lastname:hover {\r\n color: var(--accent-color);\r\n cursor: pointer;\r\n}\r\n\r\n.avatar-container {\r\n width: 45px;\r\n height: 45px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.avatar-container {\r\n width: 80px; /* Taille réduite */\r\n height: 80px;\r\n margin: 0 auto;\r\n}\r\n\r\n.avatar {\r\n height: 100%;\r\n border-radius: 50%;\r\n object-fit: cover;\r\n border: 2px solid white;\r\n}\r\n\r\n.avatar img {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n}\r\n\r\n\r\n/*-------------------------------------- BANNER--------------------------------------*/\r\n\r\n/* Styles pour la bannière avec image */\r\n.banner-image-container {\r\n position: relative;\r\n width: 100%;\r\n height: 200px;\r\n overflow: hidden;\r\n border-radius: 10px;\r\n margin-bottom: 15px;\r\n}\r\n\r\n.banner-image {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n z-index: 1;\r\n}\r\n\r\n.banner-content {\r\n position: relative;\r\n z-index: 2;\r\n display: flex;\r\n align-items: center;\r\n gap: 15px;\r\n height: 100%;\r\n padding: 0 15px;\r\n background: rgba(0, 0, 0, 0.3);\r\n overflow: visible;\r\n}\r\n\r\n.banner-content .avatar-container {\r\n width: 45px;\r\n height: 45px;\r\n overflow: hidden; \r\n border-radius: 50%;\r\n border: 2px solid white;\r\n transition: transform 0.3s ease; \r\n}\r\n\r\n.banner-content .avatar {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n border: none; \r\n}\r\n\r\n.banner-content .avatar-container:hover {\r\n transform: scale(1.15);\r\n cursor: pointer;\r\n}\r\n\r\n/* Style pour le bouton de changement de bannière */\r\n.banner-upload-label {\r\n display: block;\r\n width: auto;\r\n padding: 12px 20px;\r\n background-color: var(--accent-color);\r\n color: white;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n text-align: center;\r\n font-size: 16px;\r\n margin: 20px auto;\r\n max-width: 250px;\r\n}\r\n\r\n.banner-upload-label:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.banner-controls {\r\n margin-top: 15px;\r\n display: flex;\r\n justify-content: center;\r\n width: 100%;\r\n}\r\n\r\n.banner-preview {\r\n margin: 10px 0;\r\n}\r\n\r\n.banner-preview h3 {\r\n margin: 0 0 10px 0;\r\n font-size: 18px;\r\n}\r\n\r\n.banner-image-container {\r\n height: 150px;\r\n margin-bottom: 10px;\r\n}\r\n\r\n\r\n.nav-wrapper {\r\n position: fixed;\r\n background: white;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n height: 9vh;\r\n width: 100vw;\r\n left: 0;\r\n top: 0;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n z-index: 1000;\r\n}\r\n/* Mise à jour des styles de la navbar pour inclure l'image de bannière */\r\n.nav-wrapper .avatar-section {\r\n position: relative;\r\n background: none;\r\n}\r\n\r\n.nav-wrapper .banner-image {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n z-index: -1;\r\n filter: brightness(0.7);\r\n}\r\n\r\n\r\n\r\n/*-------------------------------------- Popup--------------------------------------*/\r\n/* Styles pour la popup */\r\n.popup {\r\n display: none;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n z-index: 1000;\r\n}\r\n\r\n.popup-content {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n background: white;\r\n padding: 20px;\r\n border-radius: var(--border-radius);\r\n box-shadow: var(--box-shadow);\r\n width: 400px; \r\n max-height: 80vh; \r\n overflow-y: auto; \r\n}\r\n.popup-content h2 {\r\n margin: 0 0 15px 0;\r\n font-size: 24px;\r\n}\r\n\r\n.close-popup {\r\n position: absolute;\r\n right: 15px;\r\n top: 10px;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n.close-popup:hover {\r\n color: #000;\r\n}\r\n\r\n.popup-avatar {\r\n text-align: center;\r\n margin: 20px 0;\r\n position: relative;\r\n}\r\n\r\n.avatar-upload-label {\r\n position: relative;\r\n display: inline-block;\r\n cursor: pointer;\r\n width: 0%;\r\n margin-left: -20%;\r\n}\r\n\r\n.avatar-overlay {\r\n position: absolute;\r\n top: 0;\r\n left: 50px;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n border-radius: 50%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n opacity: 0;\r\n transition: opacity 0.3s;\r\n}\r\n\r\n.avatar-overlay span {\r\n color: white;\r\n font-size: 14px;\r\n text-align: center;\r\n}\r\n\r\n.avatar-upload-label:hover .avatar-overlay {\r\n opacity: 1;\r\n}\r\n\r\n.popup-avatar img {\r\n width: 100px;\r\n height: 100px;\r\n border-radius: 50%;\r\n border: 3px solid var(--accent-color);\r\n object-fit: cover;\r\n}\r\n\r\n.popup-info {\r\n margin: 15px 0;\r\n}\r\n\r\n.info-row {\r\n margin: 8px 0;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n\r\n.popup-info strong {\r\n min-width: 100px; /* Largeur fixe pour l'alignement */\r\n}\r\n\r\n/* Editable Name and Lastname */\r\n.editable {\r\n cursor: pointer;\r\n display: inline-block;\r\n min-width: 100px;\r\n padding: 2px 5px;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.editable:hover {\r\n background-color: #f0f0f0;\r\n}\r\n\r\n.editable.editing {\r\n background-color: white;\r\n border: 1px solid var(--accent-color);\r\n outline: none;\r\n}\r\n\r\n.edit-input {\r\n border: 1px solid var(--accent-color);\r\n border-radius: 3px;\r\n padding: 2px 5px;\r\n font-size: inherit;\r\n font-family: inherit;\r\n outline: none;\r\n width: 100%; \r\n min-width: 100px;\r\n margin: 0; \r\n box-sizing: border-box; \r\n}\r\n\r\n/* Boutons */\r\n\r\n\r\n.popup-button-container {\r\n display: flex\r\n;\r\n flex-direction: column;\r\n margin-top: 20px;\r\n gap: 15px;\r\n}\r\n\r\n.action-buttons-row {\r\n display: flex\r\n;\r\n justify-content: space-between;\r\n gap: 15px;\r\n}\r\n.banner-upload-label,\r\n.export-btn,\r\n.delete-account-btn {\r\n padding: 8px 15px;\r\n margin: 10px 0;\r\n font-size: 14px;\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: #dc3545;\r\n}\r\n\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n flex: 1; /* Pour qu'ils prennent la même largeur */\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n text-align: center;\r\n}\r\n\r\n\r\n\r\n/* Séparateurs */\r\n.popup-info,\r\n.export-section,\r\n.delete-account-section {\r\n padding-top: 10px;\r\n margin-top: 10px;\r\n border-top: 1px solid #eee;\r\n}\r\n\r\n.logout-btn {\r\n background-color: rgb(108, 117, 125);\r\n font-size: 16px;\r\n cursor: pointer;\r\n color: white;\r\n text-align: center;\r\n flex: 1 1 0%;\r\n padding: 12px 20px;\r\n border-width: initial;\r\n border-style: none;\r\n border-color: initial;\r\n border-image: initial;\r\n border-radius: 8px;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n/*-------------------------------------- Delete Account--------------------------------------*/\r\n.delete-account-section {\r\n margin-top: 30px;\r\n padding-top: 20px;\r\n border-top: 1px solid #ddd;\r\n text-align: center;\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: #dc3545;\r\n}\r\n\r\n.delete-account-btn:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n/* Style pour la modal de confirmation */\r\n.confirm-delete-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.confirm-delete-content {\r\n background-color: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n max-width: 400px;\r\n width: 90%;\r\n text-align: center;\r\n}\r\n\r\n.confirm-delete-content h3 {\r\n margin-top: 0;\r\n color: #333;\r\n}\r\n\r\n.confirm-delete-content p {\r\n margin: 15px 0;\r\n color: #666;\r\n}\r\n\r\n.confirm-delete-buttons {\r\n display: flex;\r\n justify-content: center;\r\n gap: 10px;\r\n margin-top: 20px;\r\n}\r\n\r\n.confirm-delete-buttons button {\r\n padding: 8px 20px;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.confirm-delete-buttons .confirm-btn {\r\n background-color: #dc3545;\r\n color: white;\r\n}\r\n\r\n.confirm-delete-buttons .confirm-btn:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n.confirm-delete-buttons .cancel-btn {\r\n background-color: #6c757d;\r\n color: white;\r\n}\r\n\r\n.confirm-delete-buttons .cancel-btn:hover {\r\n background-color: #5a6268;\r\n}\r\n\r\n/*-------------------------------------- Export--------------------------------------*/\r\n.export-section {\r\n margin: 20px 0;\r\n text-align: center;\r\n padding: 15px 0;\r\n border-top: 1px solid #ddd;\r\n}\r\n\r\n.export-btn {\r\n background-color: var(--accent-color);\r\n}\r\n\r\n.export-btn:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.export-section,\r\n.delete-account-section {\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n margin: 15px 0;\r\n}\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n width: 80%;\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n text-align: center;\r\n}\r\n\r\n/*-------------------------------------- NAVBAR--------------------------------------*/\r\n\r\n.brand-logo {\r\n font-size: 1.5rem;\r\n font-weight: bold;\r\n}\r\n\r\n.nav-wrapper {\r\n position: fixed;\r\n background: radial-gradient(circle, white, var(--primary-color));\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n color: #37474F;\r\n height: 9vh;\r\n width: 100vw;\r\n left: 0;\r\n top: 0;\r\n box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);\r\n}\r\n\r\n/* Icônes de la barre de navigation */\r\n.nav-right-icons {\r\n margin-right: 20px;\r\n}\r\n\r\n.burger-menu {\r\n height: 20px;\r\n width: 20px;\r\n margin-right: 1rem;\r\n cursor: pointer;\r\n}\r\n\r\n/* Par défaut, le menu est masqué */\r\n#menu {\r\n display: none;\r\n /* Menu caché par défaut */\r\n transition: display 0.3s ease-in-out;\r\n}\r\n\r\n\r\n.burger-menu {\r\n width: 24px;\r\n height: 24px;\r\n cursor: pointer;\r\n}\r\n\r\n.burger-menu-icon {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n/* Icône burger */\r\n#burger-icon {\r\n cursor: pointer;\r\n}\r\n\r\n/* .menu-content {\r\n display: none;\r\n position: absolute;\r\n top: 3.4rem;\r\n right: 1rem;\r\n background-color: white;\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\r\n border-radius: 5px;\r\n overflow: hidden;\r\n}\r\n\r\n.menu-content a {\r\n display: block;\r\n padding: 10px 20px;\r\n text-decoration: none;\r\n color: #333;\r\n border-bottom: 1px solid #e0e0e0;\r\n\r\n &:hover {\r\n background-color: rgba(26, 28, 24, .08);\r\n }\r\n}\r\n\r\n.menu-content a:last-child {\r\n border-bottom: none;\r\n} */\r\n\r\n/* Ajustement pour la barre de navigation fixe */\r\n.container {\r\n display: flex;\r\n flex: 1;\r\n height: 90vh;\r\n margin-top: 9vh;\r\n margin-left: -1%;\r\n text-align: left;\r\n width: 100vw;\r\n}\r\n\r\n/* Liste des information sur l'account */\r\n\r\n.parameter-list {\r\n width: 24.5%;\r\n background-color: #1f2c3d;\r\n color: white;\r\n padding: 20px;\r\n box-sizing: border-box;\r\n overflow-y: auto;\r\n border-right: 2px solid #2c3e50;\r\n flex-shrink: 0; \r\n padding-right: 10px;\r\n height: 91vh;\r\n}\r\n\r\n.parameter-list ul {\r\n padding: 15px;\r\n margin-left: 10px;\r\n border-radius: 8px;\r\n background-color: #273646;\r\n cursor: pointer;\r\n transition: background-color 0.3s, box-shadow 0.3s;\r\n}\r\n\r\n\r\n.parameter-list ul:hover {\r\n background-color: #34495e;\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n\r\n/* Zone des info des parametre */\r\n\r\n.parameter-area {\r\n display: flex;\r\n flex-direction: column;\r\n flex: 1;\r\n min-width: 0; \r\n background-color: #ffffff;\r\n border-radius: 10px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\r\n margin: 0px;\r\n margin-top: 20px;\r\n margin-left: 1%;\r\n margin-bottom: -7px;\r\n}\r\n\r\n/* En-tête du parametre */\r\n.parameter-header {\r\n background-color: #34495e;\r\n color: white;\r\n padding: 15px;\r\n font-size: 20px;\r\n font-weight: bold;\r\n border-radius: 10px 10px 0 0;\r\n text-align: center;\r\n}\r\n\r\n/* Style du tableau dans parameter-area */\r\n.parameter-table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n margin: 15px 0;\r\n table-layout: fixed; \r\n}\r\n\r\n.parameter-table th, .parameter-table td {\r\n border: 1px solid #ddd;\r\n padding: 8px;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-align: center;\r\n}\r\n\r\n.parameter-table th {\r\n background-color: var(--secondary-color);\r\n color: white;\r\n font-weight: bold;\r\n}\r\n\r\n.parameter-table tr:nth-child(even) {\r\n background-color: #f2f2f2;\r\n}\r\n\r\n\r\n\r\n.parameter-table tr:hover {\r\n background-color: #ddd;\r\n}\r\n\r\n/* Conteneur pour les boutons */\r\n.button-container {\r\n display: flex;\r\n justify-content: center;\r\n gap: 15px;\r\n margin: 15px 0;\r\n}\r\n\r\n/* Boutons \"Ajouter une ligne\" et \"Confirmer\" */\r\n.add-row-button, .confirm-all-button, .delete-row-button {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n padding: 8px 15px;\r\n cursor: pointer;\r\n border-radius: 5px;\r\n font-size: 0.9em;\r\n margin-right: 5px;\r\n}\r\n\r\n.add-row-button:hover, .confirm-all-button:hover, .delete-row-button:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n\r\n.button-style {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n border-radius: 5px;\r\n padding: 10px 20px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.button-style:hover {\r\n background-color: darkorange;\r\n}\r\n\r\n.content-container {\r\n width: 100%;\r\n}\r\n\r\n#pairing-content,\r\n#wallet-content {\r\n width: 100%;\r\n}\r\n\r\n.editable-cell {\r\n cursor: pointer;\r\n}\r\n\r\n.editable-cell:hover {\r\n background-color: #f5f5f5;\r\n}\r\n\r\n.edit-input {\r\n width: 100%;\r\n padding: 5px;\r\n box-sizing: border-box;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n}\r\n\r\n/*-------------------------------------- Notification--------------------------------------*/\r\n.notification-cell {\r\n position: relative;\r\n}\r\n\r\n.notification-bell {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.notification-badge {\r\n position: absolute;\r\n top: -8px;\r\n right: -8px;\r\n background-color: red;\r\n color: white;\r\n border-radius: 50%;\r\n padding: 2px 6px;\r\n font-size: 12px;\r\n min-width: 15px;\r\n text-align: center;\r\n}\r\n\r\n.fa-bell {\r\n color: #666;\r\n font-size: 20px;\r\n}\r\n\r\n\r\n/* Media Queries pour Mobile */\r\n@media screen and (max-width: 768px) {\r\n /* Navbar */\r\n .nav-wrapper {\r\n height: 9vh;\r\n padding: 0;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n }\r\n\r\n /* Section avatar (gauche) */\r\n .avatar-section {\r\n width: 200px; /* Largeur réduite */\r\n margin-left: 10px;\r\n order: 0; /* Garde à gauche */\r\n }\r\n\r\n .avatar-container {\r\n width: 35px;\r\n height: 35px;\r\n }\r\n\r\n .user-info span {\r\n font-size: 0.8rem;\r\n }\r\n\r\n /* Logo (centre) */\r\n .brand-logo {\r\n order: 0; \r\n flex: 0 0 auto;\r\n padding: 0;\r\n font-size: 1.2rem;\r\n }\r\n\r\n /* Menu burger (droite) */\r\n .nav-right-icons {\r\n order: 0; \r\n width: auto;\r\n padding-right: 15px;\r\n }\r\n\r\n .burger-menu {\r\n width: 24px;\r\n height: 24px;\r\n }\r\n\r\n /* Ajustements pour la bannière */\r\n .banner-image-container {\r\n height: 100%;\r\n }\r\n\r\n .banner-content {\r\n padding: 0 10px;\r\n }\r\n\r\n /* Style des boutons dans la popup */\r\n .button-container {\r\n display: flex;\r\n gap: 10px;\r\n margin: 15px 0;\r\n width: 100%;\r\n }\r\n\r\n .export-btn,\r\n .delete-account-btn {\r\n flex: 1;\r\n padding: 12px 15px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 14px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n }\r\n\r\n .export-btn {\r\n background-color: var(--accent-color);\r\n }\r\n\r\n .delete-account-btn {\r\n background-color: var(--danger-color);\r\n }\r\n}\r\n\r\n/* Media Queries pour très petits écrans */\r\n@media screen and (max-width: 380px) {\r\n .avatar-section {\r\n width: 150px; \r\n }\r\n\r\n .user-info span {\r\n font-size: 0.7rem;\r\n }\r\n}\r\n\r\n/* Style des boutons */\r\n.button-container {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n flex: 1;\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n color: white;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n font-size: 14px;\r\n display: block;\r\n}\r\n\r\n.export-btn {\r\n background-color: var(--accent-color);\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: rgb(219, 17, 17);\r\n display: block;\r\n visibility: visible;\r\n}\r\n\r\n.export-btn:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.delete-account-btn:hover {\r\n background-color: #b60718;\r\n}\r\n\r\n@media screen and (max-width: 768px) {\r\n .button-container {\r\n gap: 10px;\r\n }\r\n\r\n .export-btn,\r\n .delete-account-btn {\r\n padding: 12px 15px;\r\n font-size: 14px;\r\n }\r\n}\r\n\r\n/* Style pour les boutons de la popup */\r\n.popup-buttons {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n/* Style pour les boutons d'action des tableaux */\r\n.button-container {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n/* Style pour le header mobile */\r\n.mobile-nav {\r\n display: none;\r\n width: 100%;\r\n padding: 10px;\r\n background-color: #34495e;\r\n overflow-x: auto;\r\n white-space: nowrap;\r\n}\r\n\r\n.mobile-nav ul {\r\n display: flex;\r\n gap: 10px;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n/* Media Query pour mobile */\r\n@media screen and (max-width: 768px) {\r\n .parameter-list {\r\n display: flex;\r\n width: 100%;\r\n min-width: 100%;\r\n height: auto;\r\n overflow-x: auto;\r\n background-color: rgb(31, 44, 61);\r\n padding: 10px;\r\n border-right: none;\r\n border-bottom: 2px solid rgb(44, 62, 80);\r\n }\r\n\r\n .mobile-nav {\r\n display: flex; /* Affiche la navigation mobile */\r\n }\r\n\r\n .parameter-header {\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n .parameter-list-ul {\r\n text-align: center;\r\n flex: 1 1 0%;\r\n margin: 0px 5px;\r\n padding: 10px;\r\n white-space: nowrap;\r\n margin-bottom: none;\r\n }\r\n\r\n .parameter-list-ul:hover {\r\n background-color: #34495e;\r\n }\r\n\r\n .container {\r\n flex-direction: column;\r\n }\r\n\r\n .parameter-area {\r\n margin: -5px;\r\n }\r\n}\r\n\r\n/* Style pour le header et la navigation mobile */\r\n.parameter-header {\r\n background-color: #34495e;\r\n padding: 20px 0;\r\n margin: 0;\r\n width: 100%;\r\n}\r\n\r\n.mobile-nav {\r\n display: none; /* Par défaut caché */\r\n width: 100%;\r\n padding: 10px;\r\n background-color: #34495e;\r\n overflow-x: auto;\r\n}\r\n\r\n.mobile-nav ul {\r\n display: flex;\r\n gap: 10px;\r\n margin: 0;\r\n padding: 10px;\r\n list-style: none;\r\n}\r\n\r\n.mobile-nav li {\r\n flex: 0 0 auto;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Ajoutez ces styles pour la bannière dans la popup */\r\n.banner-container {\r\n width: 100%;\r\n margin-bottom: 20px;\r\n}\r\n\r\n.banner-wrapper {\r\n width: 100%;\r\n height: 120px;\r\n overflow: hidden;\r\n position: relative;\r\n border-radius: 10px;\r\n}\r\n\r\n.banner-wrapper img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n}\r\n\r\n/* Mise à jour des styles existants */\r\n.popup-content {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n background: white;\r\n padding: 20px;\r\n border-radius: 10px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n width: 90%;\r\n max-width: 500px;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n}\r\n\r\n/* Style pour le conteneur de la bannière */\r\n.banner-upload-label {\r\n display: block;\r\n width: auto;\r\n padding: 12px 20px;\r\n background-color: var(--accent-color);\r\n color: white;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n text-align: center;\r\n font-size: 16px;\r\n margin: 10px auto;\r\n max-width: 200px;\r\n}\r\n\r\n/* ---------------------Style pour la popup de contrat--------------------- */\r\n\r\n.contract-popup-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex\r\n;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.contract-popup-content {\r\n background: white;\r\n padding: 30px;\r\n border-radius: 8px;\r\n max-width: 600px;\r\n width: 90%;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n position: relative;\r\n}\r\n\r\n.close-contract-popup {\r\n position: absolute;\r\n top: 15px;\r\n right: 15px;\r\n background: none;\r\n border: none;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n/* Style pour la popup d'alerte */\r\n.alert-popup {\r\n position: fixed;\r\n top: 20px;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n background-color: #f44336;\r\n color: white;\r\n padding: 15px 25px;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 5px rgba(0,0,0,0.2);\r\n z-index: 1000;\r\n display: none;\r\n animation: slideDown 0.3s ease-out;\r\n}\r\n\r\n/* ---------------------Style pour la popup notification--------------------- */\r\n\r\n\r\n.notifications-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.notifications-content {\r\n position: relative;\r\n background: white;\r\n border-radius: 8px;\r\n padding: 24px;\r\n width: 90%;\r\n max-width: 500px;\r\n}\r\n\r\n.close-button {\r\n position: absolute;\r\n top: 15px;\r\n right: 20px;\r\n font-size: 24px;\r\n color: #666;\r\n cursor: pointer;\r\n transition: color 0.2s;\r\n}\r\n\r\n.close-button:hover {\r\n color: #000;\r\n}\r\n\r\n.notifications-title {\r\n padding-right: 30px; /* Pour éviter que le titre ne chevauche le bouton de fermeture */\r\n}\r\n\r\n.notifications-title {\r\n font-size: 24px;\r\n color: #445B6E;\r\n margin-bottom: 20px;\r\n font-weight: 500;\r\n}\r\n\r\n.notifications-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n\r\n.notification-item {\r\n display: flex;\r\n align-items: flex-start;\r\n padding: 12px 0;\r\n border-bottom: 1px solid #eee;\r\n cursor: pointer;\r\n}\r\n\r\n.notification-status {\r\n margin-right: 16px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px; \r\n height: 24px; \r\n}\r\n\r\n.dot-icon, .check-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.notification-content {\r\n flex: 1;\r\n}\r\n\r\n.notification-message {\r\n font-size: 16px;\r\n color: #333;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.notification-date {\r\n font-size: 14px;\r\n color: #666;\r\n}\r\n\r\n.notification-item:hover {\r\n background-color: #f8f9fa;\r\n}\r\n\r\n.notification-item.read {\r\n opacity: 0.7;\r\n}\r\n\r\n.notification-item.unread {\r\n background-color: #fff;\r\n}\r\n\r\n.close-notifications {\r\n position: absolute;\r\n top: 15px;\r\n right: 15px;\r\n border: none;\r\n background: none;\r\n font-size: 24px;\r\n color: #666;\r\n cursor: pointer;\r\n}\r\n\r\n/*-------------------------------------- STYLE ACTION BUTTON ---------------------*/\r\n\r\n.action-buttons-wrapper {\r\n display: flex;\r\n flex-direction: row;\r\n gap: 10px;\r\n justify-content: center;\r\n margin: 20px 0;\r\n}\r\n\r\n.action-button {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0;\r\n border-radius: 8px;\r\n border: none;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n color: white;\r\n height: 40px;\r\n width: 40px;\r\n}\r\n\r\n.confirm-button {\r\n background-color: #4CAF50;\r\n}\r\n\r\n.confirm-button:hover {\r\n background-color: #3d8b40;\r\n transform: translateY(-2px);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n.cancel-button {\r\n background-color: rgb(244, 67, 54);\r\n}\r\n\r\n.cancel-button:hover {\r\n background-color: #d32f2f;\r\n transform: translateY(-2px);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n.banner-image.clickable {\r\n cursor: pointer;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.banner-image.clickable:hover {\r\n opacity: 0.8;\r\n}\r\n\r\n.parameter-list-ul.profile {\r\n position: relative;\r\n overflow: hidden; \r\n max-height: 200px;\r\n margin-bottom: 20px;\r\n}\r\n\r\n\r\n.profile-preview {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.preview-banner {\r\n position: relative;\r\n width: 100%;\r\n height: 120px; \r\n overflow: hidden;\r\n}\r\n\r\n.preview-banner-img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n.preview-info {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n padding: 10px;\r\n gap: 10px;\r\n background: rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.preview-avatar {\r\n width: 45px;\r\n height: 45px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n}\r\n\r\n/* ---------------------Style pour le QR code--------------------- */\r\n\r\n.qr-code {\r\n width: 50px;\r\n height: 50px;\r\n cursor: pointer;\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.qr-code:hover {\r\n transform: scale(1.5);\r\n}\r\n\r\n.qr-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.qr-modal-content {\r\n background-color: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n position: relative;\r\n text-align: center;\r\n}\r\n\r\n.close-qr-modal {\r\n position: absolute;\r\n right: 10px;\r\n top: 5px;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n.close-qr-modal:hover {\r\n color: #000;\r\n}\r\n\r\n.qr-code-large {\r\n max-width: 300px;\r\n margin: 10px 0;\r\n}\r\n\r\n.qr-address {\r\n margin-top: 10px;\r\n word-break: break-all;\r\n font-size: 12px;\r\n color: #666;\r\n}\r\n\r\n.pairing-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.pairing-modal-content {\r\n background-color: white;\r\n padding: 2rem;\r\n border-radius: 8px;\r\n width: 90%;\r\n max-width: 500px;\r\n}\r\n\r\n.pairing-form {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 1rem;\r\n}\r\n\r\n.form-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.5rem;\r\n}\r\n\r\n.form-group label {\r\n font-weight: bold;\r\n}\r\n\r\n.button-group {\r\n display: flex;\r\n gap: 1rem;\r\n justify-content: flex-end;\r\n margin-top: 1rem;\r\n}\r\n\r\n.button-group button {\r\n padding: 0.5rem 1rem;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.confirm-button {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n}\r\n\r\n.cancel-button {\r\n background-color: #ccc;\r\n border: none;\r\n}\r\n";
async function loadValidationRuleModal(templatePath = "/src/components/validation-rule-modal/validation-rule-modal.html") {
if (document.getElementById("validation-rule-modal"))
return;
const res = await fetch(templatePath);
const html = await res.text();
const tempDiv = document.createElement("div");
tempDiv.innerHTML = html;
const modal = tempDiv.querySelector("#validation-rule-modal");
if (!modal) {
throw new Error("Modal HTML missing #validation-rule-modal");
}
document.body.appendChild(modal);
}
function showValidationRuleModal(onSubmit) {
const modal = document.getElementById("validation-rule-modal");
const quorumInput = document.getElementById("vr-quorum");
const minsigInput = document.getElementById("vr-minsig");
const fieldsInput = document.getElementById("vr-fields");
const cancelBtn = document.getElementById("vr-cancel");
const submitBtn = document.getElementById("vr-submit");
quorumInput.value = "";
minsigInput.value = "";
fieldsInput.value = "";
modal.style.display = "flex";
cancelBtn.onclick = () => {
modal.style.display = "none";
};
submitBtn.onclick = () => {
const rule = {
quorum: parseInt(quorumInput.value),
min_sig_member: parseInt(minsigInput.value),
fields: fieldsInput.value.split(",").map((f) => f.trim()).filter(Boolean)
};
modal.style.display = "none";
onSubmit(rule);
};
}
function createKeyValueSection(title, id, isRoleSection = false) {
const section = document.createElement("div");
section.id = id;
section.style.cssText = "margin-bottom: 2rem; background: #fff; padding: 1rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);";
const titleEl = document.createElement("h2");
titleEl.textContent = title;
titleEl.style.cssText = "font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem;";
section.appendChild(titleEl);
const rowContainer = document.createElement("div");
section.appendChild(rowContainer);
const addBtn = document.createElement("button");
addBtn.textContent = "+ Add Row";
addBtn.style.cssText = `
margin-top: 1rem;
padding: 0.5rem 1rem;
border: 1px solid #888;
border-radius: 0.375rem;
background-color: #f9f9f9;
cursor: pointer;
`;
section.appendChild(addBtn);
const roleRowStates = [];
const nonRoleRowStates = [];
const inputStyle = "flex: 1; height: 2.5rem; padding: 0.5rem; border: 1px solid #ccc; border-radius: 0.375rem;";
const createRow = () => {
const row = document.createElement("div");
row.style.cssText = "display: flex; gap: 1rem; margin-bottom: 0.5rem; align-items: center;";
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "🗑️";
deleteBtn.style.cssText = "background: none; border: none; font-size: 1.2rem; cursor: pointer;";
deleteBtn.onclick = () => {
row.remove();
updateDeleteButtons();
};
if (isRoleSection) {
const roleName = document.createElement("input");
const members = document.createElement("input");
const storages = document.createElement("input");
roleName.placeholder = "Role name";
members.placeholder = "members";
storages.placeholder = "storages";
[roleName, members, storages].forEach((input) => {
input.type = "text";
input.style.cssText = inputStyle;
});
const ruleButton = document.createElement("button");
ruleButton.textContent = "Add Validation Rule";
ruleButton.style.cssText = "padding: 0.3rem 0.75rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;";
const rules = [];
ruleButton.onclick = () => {
showValidationRuleModal((rule) => {
rules.push(rule);
ruleButton.textContent = `Rules (${rules.length})`;
});
};
row.appendChild(roleName);
row.appendChild(members);
row.appendChild(storages);
row.appendChild(ruleButton);
row.appendChild(deleteBtn);
roleRowStates.push({ roleNameInput: roleName, membersInput: members, storagesInput: storages, validationRules: rules });
} else {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.style.display = "none";
fileInput.onchange = async () => {
const file = fileInput.files?.[0];
if (!file)
return;
const buffer = await file.arrayBuffer();
const uint8 = new Uint8Array(buffer);
rowState.fileBlob = {
type: file.type,
data: uint8
};
valueInput.value = `📄 ${file.name}`;
valueInput.disabled = true;
attachBtn.textContent = `📎 ${file.name}`;
};
const attachBtn = document.createElement("button");
attachBtn.textContent = "📎 Attach";
attachBtn.style.cssText = "padding: 0.3rem 0.75rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;";
attachBtn.onclick = () => fileInput.click();
const keyInput = document.createElement("input");
const valueInput = document.createElement("input");
const rowState = {
keyInput,
valueInput,
fileInput,
fileBlob: null
};
nonRoleRowStates.push(rowState);
keyInput.placeholder = "Key";
valueInput.placeholder = "Value";
[keyInput, valueInput].forEach((input) => {
input.type = "text";
input.style.cssText = inputStyle;
});
row.appendChild(keyInput);
row.appendChild(valueInput);
row.appendChild(attachBtn);
row.appendChild(fileInput);
row.appendChild(deleteBtn);
}
rowContainer.appendChild(row);
updateDeleteButtons();
};
const updateDeleteButtons = () => {
const rows = Array.from(rowContainer.children);
rows.forEach((row) => {
const btn = row.querySelector("button:last-child");
if (rows.length === 1) {
btn.disabled = true;
btn.style.visibility = "hidden";
} else {
btn.disabled = false;
btn.style.visibility = "visible";
}
});
};
createRow();
addBtn.addEventListener("click", createRow);
return {
element: section,
getData: () => {
if (isRoleSection) {
const data = {};
for (const row of roleRowStates) {
const key = row.roleNameInput.value.trim();
if (!key)
continue;
data[key] = {
members: row.membersInput.value.split(",").map((x) => x.trim()).filter(Boolean),
storages: row.storagesInput.value.split(",").map((x) => x.trim()).filter(Boolean),
validation_rules: row.validationRules
};
}
return data;
} else {
const data = {};
for (const row of nonRoleRowStates) {
const key = row.keyInput.value.trim();
if (!key)
continue;
if (row.fileBlob) {
data[key] = row.fileBlob;
} else {
data[key] = row.valueInput.value.trim();
}
}
return data;
}
}
};
}
async function getProcessCreation(container) {
await loadValidationRuleModal();
container.style.display = "block";
container.innerHTML = `<div class="parameter-header">Process Creation</div>`;
const privateSec = createKeyValueSection("Private Data", "private-section");
const publicSec = createKeyValueSection("Public Data", "public-section");
const rolesSec = createKeyValueSection("Roles", "roles-section", true);
container.appendChild(privateSec.element);
container.appendChild(publicSec.element);
container.appendChild(rolesSec.element);
const btn = document.createElement("button");
btn.textContent = "Create Process";
btn.style.cssText = `
display: block;
margin: 2rem auto 0;
padding: 0.75rem 2rem;
font-size: 1rem;
font-weight: bold;
background-color: #4f46e5;
color: white;
border: none;
border-radius: 0.5rem;
cursor: pointer;
`;
btn.onclick = async () => {
const privateData = privateSec.getData();
const publicData = publicSec.getData();
const roles = rolesSec.getData();
console.log("Private:", privateData);
console.log("Public:", publicData);
console.log("Roles:", roles);
const service = await Services.getInstance();
const createProcessResult = await service.createProcess(privateData, publicData, roles);
const processId = createProcessResult.updated_process.process_id;
const stateId = createProcessResult.updated_process.current_process.states[0].state_id;
await service.handleApiReturn(createProcessResult);
await service.createPrdUpdate(processId, stateId);
await service.handleApiReturn(createProcessResult);
const approveChangeResult = await service.approveChange(processId, stateId);
await service.handleApiReturn(approveChangeResult);
if (approveChangeResult) {
const process = await service.getProcess(processId);
let newState = process ? service.getStateFromId(process, stateId) : null;
if (!newState)
return;
for (const label of Object.keys(newState.keys)) {
const hash = newState.pcd_commitment[label];
const encryptedData = await service.getBlobFromDb(hash);
if (!encryptedData)
continue;
const filename = `${label}-${hash.slice(0, 8)}.bin`;
const blob2 = new Blob([encryptedData], { type: "application/octet-stream" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob2);
link.download = filename;
link.click();
setTimeout(() => URL.revokeObjectURL(link.href), 1e3);
}
const blob = new Blob([JSON.stringify(newState, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `process_${processId}_${stateId}.json`;
a.click();
URL.revokeObjectURL(url);
}
};
container.appendChild(btn);
}
function getDocumentValidation(container) {
const state = {
file: null,
fileHash: null,
certificate: null,
commitmentHashes: []
};
container.innerHTML = "";
container.style.cssText = `
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
gap: 2rem;
`;
function createDropButton(label, onDrop, accept = "*/*") {
const wrapper = document.createElement("div");
wrapper.style.cssText = `
width: 200px;
height: 100px;
border: 2px dashed #888;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
font-weight: bold;
background: #f8f8f8;
text-align: center;
padding: 0.5rem;
box-sizing: border-box;
`;
const title = document.createElement("div");
title.textContent = label;
const filename = document.createElement("div");
filename.style.cssText = `
font-size: 0.85rem;
margin-top: 0.5rem;
color: #444;
word-break: break-word;
text-align: center;
`;
wrapper.appendChild(title);
wrapper.appendChild(filename);
const updateVisuals = (file) => {
wrapper.style.borderColor = "green";
wrapper.style.background = "#e6ffed";
filename.textContent = file.name;
};
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = accept;
fileInput.style.display = "none";
document.body.appendChild(fileInput);
fileInput.onchange = () => {
const file = fileInput.files?.[0];
if (file) {
onDrop(file, updateVisuals);
fileInput.value = "";
}
};
wrapper.ondragover = (e) => {
e.preventDefault();
wrapper.style.background = "#e0e0e0";
};
wrapper.ondragleave = () => {
wrapper.style.background = "#f8f8f8";
};
wrapper.ondrop = (e) => {
e.preventDefault();
wrapper.style.background = "#f8f8f8";
const file = e.dataTransfer?.files?.[0];
if (file) {
onDrop(file, updateVisuals);
}
};
wrapper.onclick = () => {
fileInput.click();
};
return wrapper;
}
const fileDropButton = createDropButton("Drop file", async (file, updateVisuals) => {
try {
state.file = file;
updateVisuals(file);
console.log("Loaded file:", state.file);
checkReady();
} catch (err) {
alert("Failed to drop the file.");
console.error(err);
}
});
const certDropButton = createDropButton("Drop certificate", async (file, updateVisuals) => {
try {
const text = await file.text();
const json = JSON.parse(text);
if (typeof json === "object" && json !== null && typeof json.pcd_commitment === "object" && typeof json.state_id === "string") {
state.certificate = json;
state.commitmentHashes = Object.values(json.pcd_commitment).map((h) => h.toLowerCase());
updateVisuals(file);
console.log("Loaded certificate, extracted hashes:", state.commitmentHashes);
checkReady();
} else {
alert("Invalid certificate structure.");
}
} catch (err) {
alert("Failed to parse certificate JSON.");
console.error(err);
}
});
const buttonRow = document.createElement("div");
buttonRow.style.display = "flex";
buttonRow.style.gap = "2rem";
buttonRow.appendChild(fileDropButton);
buttonRow.appendChild(certDropButton);
container.appendChild(buttonRow);
async function checkReady() {
if (state.file && state.certificate && state.commitmentHashes.length > 0) {
const fileBlob = {
type: state.file.type,
data: new Uint8Array(await state.file.arrayBuffer())
};
const service = await Services.getInstance();
const commitedIn = state.certificate.commited_in;
if (!commitedIn)
return;
const [prevTxid, prevTxVout] = commitedIn.split(":");
const processId = state.certificate.commited_in;
const stateId = state.certificate.state_id;
const process = await service.getProcess(processId);
if (!process)
return;
const nextState = service.getNextStateAfterId(process, stateId);
if (!nextState) {
alert(`❌ Validation failed: No next state, is the state you're trying to validate commited?`);
return;
}
const [outspentTxId, _] = nextState.commited_in.split(":");
console.log(outspentTxId);
const txInfo = await fetchTransaction(outspentTxId);
if (!txInfo) {
console.error(`Validation error: Can't fetch new state commitment transaction`);
alert(`❌ Validation failed: invalid or non existent commited_in for state ${stateId}.`);
return;
}
let found = false;
for (const vin of txInfo.vin) {
if (vin.txid === prevTxid) {
found = true;
break;
}
}
if (!found) {
console.error(`Validation error: new state doesn't spend previous state commitment transaction`);
alert("❌ Validation failed: Unconsistent commitment transactions history.");
return;
}
found = false;
for (const vout of txInfo.vout) {
console.log(vout);
if (vout.scriptpubkey_type && vout.scriptpubkey_type === "op_return") {
found = true;
} else {
continue;
}
if (vout.scriptpubkey_asm) {
const hash = extractHexFromScriptAsm(vout.scriptpubkey_asm);
if (hash) {
if (hash !== stateId) {
console.error(`Validation error: expected stateId ${stateId}, got ${hash}`);
alert("❌ Validation failed: Transaction does not commit to that state.");
return;
}
}
}
}
if (!found) {
alert("❌ Validation failed: Transaction does not contain data.");
return;
}
found = false;
for (const label of Object.keys(state.certificate.pcd_commitment)) {
console.log(`Computing hash with label ${label}`);
const fileHex = service.getHashForFile(commitedIn, label, fileBlob);
console.log(`Found hash ${fileHex}`);
found = state.commitmentHashes.includes(fileHex);
if (found)
break;
}
if (found) {
alert("✅ Validation successful: file hash found in pcd_commitment.");
} else {
alert("❌ Validation failed: file hash NOT found in pcd_commitment.");
}
}
}
async function fetchTransaction(txid) {
const url = `https://mempool.4nkweb.com/api/tx/${txid}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch outspend status: ${response.statusText}`);
}
const outspend = await response.json();
return outspend;
}
function extractHexFromScriptAsm(scriptAsm) {
const parts = scriptAsm.trim().split(/\s+/);
const last = parts[parts.length - 1];
if (/^[0-9a-fA-F]{64}$/.test(last)) {
return last.toLowerCase();
}
return null;
}
}
function createProcessTab(container, processes) {
container.id = "process-tab";
container.style.display = "block";
container.style.cssText = "padding: 1.5rem;";
const title = document.createElement("h2");
title.textContent = "Processes";
title.style.cssText = "font-size: 1.5rem; font-weight: bold; margin-bottom: 1rem;";
container.appendChild(title);
processes.forEach((proc) => {
const card = document.createElement("div");
card.style.cssText = "margin-bottom: 1rem; padding: 1rem; border: 1px solid #ddd; border-radius: 0.5rem; background: #fff;";
const nameEl = document.createElement("h3");
nameEl.textContent = proc.name;
nameEl.style.cssText = "font-size: 1.2rem; font-weight: bold; margin-bottom: 0.5rem;";
card.appendChild(nameEl);
const dataList = document.createElement("div");
for (const [key, value] of Object.entries(proc.publicData)) {
const item = document.createElement("div");
item.style.cssText = "margin-bottom: 0.5rem;";
const label = document.createElement("strong");
label.textContent = key + ": ";
item.appendChild(label);
const trimmed = value.replace(/^'|'$/g, "");
let parsed;
try {
parsed = JSON.parse(trimmed);
} catch (_) {
parsed = trimmed;
}
if (parsed && typeof parsed === "object") {
const saveBtn = document.createElement("button");
saveBtn.textContent = "💾 Save as JSON";
saveBtn.style.cssText = "margin-left: 0.5rem; padding: 0.25rem 0.5rem; border: 1px solid #ccc; border-radius: 0.375rem; background: #f0f0f0; cursor: pointer;";
saveBtn.onclick = () => {
const blob = new Blob([JSON.stringify(parsed, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${proc.name}_${key}.json`;
a.click();
URL.revokeObjectURL(url);
};
item.appendChild(saveBtn);
} else {
const span = document.createElement("span");
span.textContent = String(parsed);
item.appendChild(span);
}
dataList.appendChild(item);
}
card.appendChild(dataList);
container.appendChild(card);
});
return container;
}
let isAddingRow = false;
let currentRow = null;
let currentMode = "pairing";
class AccountElement extends HTMLElement {
dom;
constructor() {
super();
this.attachShadow({ mode: "open" });
this.dom = getCorrectDOM("account-element");
const fontAwesome = document.createElement("link");
fontAwesome.rel = "stylesheet";
fontAwesome.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css";
this.shadowRoot.appendChild(fontAwesome);
const style = document.createElement("style");
style.textContent = accountStyle;
this.shadowRoot.appendChild(style);
this.shadowRoot.innerHTML = `
<style>
${accountStyle}
</style>
<!-- Profile Popup -->
<div id="avatar-popup" class="popup">
<div class="popup-content">
<span class="close-popup">&times;</span>
<h2>Profile</h2>
</div>
</div>
<!-- Main Content -->
<div class="container">
<!-- Parameter List -->
<div class="parameter-list">
<ul class="parameter-list-ul profile">
<!-- Profile Preview (visible par défaut) -->
<div class="profile-preview" onclick="window.openAvatarPopup()">
<div class="preview-banner">
<img src="https://via.placeholder.com/800x200" alt="Banner" class="preview-banner-img">
</div>
<div class="preview-info">
<img src="https://via.placeholder.com/150" alt="Avatar" class="preview-avatar">
<div class="preview-text user-info">
<span class="preview-name" id="popup-name">Profile</span>
<span class="preview-lastname" id="popup-lastname"></span>
</div>
</div>
</div>
<!-- Profile Content (masqué par défaut) -->
<div class="profile-content" style="display: none;">
<!-- Banner Preview Section -->
<div class="banner-preview">
<div class="banner-image-container">
<img src="https://via.placeholder.com/800x200" alt="Banner" class="banner-image" id="popup-banner-img">
<div class="banner-content">
<div class="avatar-container">
<img src="https://via.placeholder.com/150" alt="Avatar" class="avatar" id="popup-avatar-img">
</div>
<div class="user-info">
<span class="editable" id="popup-name"></span>
<span class="editable" id="popup-lastname"></span>
</div>
</div>
</div>
<div class="banner-controls">
<label for="banner-upload" class="banner-upload-label button-style">
Change Banner Image
<input type="file" id="banner-upload" accept="image/*" style="display: none;">
</label>
</div>
</div>
<!-- Avatar Upload Section -->
<div class="popup-avatar">
<label for="avatar-upload" class="avatar-upload-label">
<img src="https://via.placeholder.com/150" alt="Avatar" class="avatar" id="popup-avatar-img">
<div class="avatar-overlay">
<span>Change Avatar</span>
</div>
</label>
<input type="file" id="avatar-upload" accept="image/*" style="display: none;">
</div>
<!-- User Info Section -->
<div class="popup-info">
<p><strong>Name:</strong> <span class="editable" id="popup-name"></span></p>
<!--<p><strong>Last Name:</strong> <span class="editable" id="popup-lastname"></span></p>-->
<p><strong>Address:</strong> 🏠 🌍 🗽🎊😩-🎊😑🎄😩</p>
</div>
<!-- Buttons Container -->
<div class="popup-buttons">
<button class="delete-account-btn" onclick="window.confirmDeleteAccount()">Delete Account</button>
</div>
</div>
</ul>
<ul class="parameter-list-ul" onclick="window.showPairing()">Pairing 🔗</ul>
<!-- <ul class="parameter-list-ul" onclick="window.showWallet()">Wallet 👛</ul> -->
<ul class="parameter-list-ul" onclick="window.showProcess()">Process ⚙️</ul>
<ul class="parameter-list-ul" onclick="window.showProcessCreation()">Process Creation</ul>
<ul class="parameter-list-ul" onclick="window.showDocumentValidation()">Document Validation</ul>
<!-- <ul class="parameter-list-ul" onclick="window.showData()">Data 💾</ul> -->
</div>
<!-- Parameter Area -->
<div class="parameter-area">
<div class="content-container">
<div id="pairing-content"></div>
<!-- <div id="wallet-content"></div> -->
<div id="process-content"></div>
<div id="process-creation-content"></div>
<div id="document-validation-content"></div>
<!-- <div id="data-content"></div> -->
</div>
</div>
</div>
`;
window.showPairing = () => this.showPairing();
window.showWallet = () => this.showWallet();
window.showProcess = () => this.showProcess();
window.showProcessCreation = () => this.showProcessCreation();
window.showDocumentValidation = () => this.showDocumentValidation();
window.showData = () => this.showData();
window.addWalletRow = () => this.addWalletRow();
window.confirmWalletRow = () => this.confirmWalletRow();
window.cancelWalletRow = () => this.cancelWalletRow();
window.editDeviceName = (cell) => this.editDeviceName(cell);
window.showProcessNotifications = (processName) => this.showProcessNotifications(processName);
window.handleLogout = () => this.handleLogout();
window.confirmDeleteAccount = () => this.confirmDeleteAccount();
window.showContractPopup = (contractId) => this.showContractPopup(contractId);
window.addRowPairing = () => this.addRowPairing();
window.deleteRowPairing = (button) => this.deleteRowPairing(button);
window.confirmRowPairing = () => this.confirmRowPairing();
window.cancelRowPairing = () => this.cancelRowPairing();
window.updateNavbarBanner = (bannerUrl) => this.updateNavbarBanner(bannerUrl);
window.saveBannerToLocalStorage = (bannerUrl) => this.saveBannerToLocalStorage(bannerUrl);
window.loadSavedBanner = () => this.loadSavedBanner();
window.closeNotificationPopup = (event) => this.closeNotificationPopup(event);
window.markAsRead = (processName, messageId, element) => this.markAsRead(processName, messageId, element);
window.exportRecovery = () => this.exportRecovery();
window.generateRecoveryWords = () => this.generateRecoveryWords();
window.exportUserData = () => this.exportUserData();
window.updateActionButtons = () => this.updateActionButtons();
window.openAvatarPopup = () => this.openAvatarPopup();
window.closeAvatarPopup = () => this.closeAvatarPopup();
window.showQRCodeModal = (pairingId) => this.showQRCodeModal(pairingId);
if (!localStorage.getItem("rows")) {
localStorage.setItem("rows", JSON.stringify(defaultRows));
}
}
connectedCallback() {
this.initializeEventListeners();
this.loadSavedBanner();
this.loadUserInfo();
const savedAvatar = localStorage.getItem("userAvatar");
const savedBanner = localStorage.getItem("userBanner");
const savedName = localStorage.getItem("userName");
const savedLastName = localStorage.getItem("userLastName");
if (savedAvatar) {
const navAvatar = this.shadowRoot?.querySelector(".avatar");
if (navAvatar)
navAvatar.src = savedAvatar;
}
if (savedBanner) {
const navBanner = this.shadowRoot?.querySelector(".banner-image");
if (navBanner)
navBanner.src = savedBanner;
}
if (savedName) {
this.updateNavbarName(savedName);
}
if (savedLastName) {
this.updateNavbarLastName(savedLastName);
}
}
showAlert(message) {
let alertPopup = this.shadowRoot?.querySelector(".alert-popup");
if (!alertPopup) {
alertPopup = document.createElement("div");
alertPopup.className = "alert-popup";
this.shadowRoot?.appendChild(alertPopup);
}
alertPopup.textContent = message;
alertPopup.style.display = "block";
setTimeout(() => {
alertPopup.style.display = "none";
}, 3e3);
}
// Fonctions de gestion des comptes et de l'interface utilisateur
confirmDeleteAccount() {
const modal = document.createElement("div");
modal.className = "confirm-delete-modal";
modal.innerHTML = `
<h3>Delete Account</h3>
<p>Are you sure you want to delete your account? This action cannot be undone.</p>
<div class="confirm-delete-buttons">
<button class="cancel-btn">Cancel</button>
<button class="confirm-btn">Delete</button>
</div>
`;
this.shadowRoot?.appendChild(modal);
modal.style.display = "block";
const cancelBtn = modal.querySelector(".cancel-btn");
const confirmBtn = modal.querySelector(".confirm-btn");
cancelBtn?.addEventListener("click", () => {
modal.remove();
});
confirmBtn?.addEventListener("click", () => {
this.deleteAccount();
modal.remove();
});
}
deleteAccount() {
localStorage.clear();
window.location.href = "/login.html";
}
updateNavbarBanner(imageUrl) {
const navbarSection = this.shadowRoot?.querySelector(".nav-wrapper .avatar-section");
if (!navbarSection)
return;
let bannerImg = navbarSection.querySelector(".banner-image");
if (!bannerImg) {
bannerImg = document.createElement("img");
bannerImg.className = "banner-image";
navbarSection.insertBefore(bannerImg, navbarSection.firstChild);
}
bannerImg.src = imageUrl;
}
saveBannerToLocalStorage(dataUrl) {
localStorage.setItem("userBanner", dataUrl);
}
loadSavedBanner() {
const savedBanner = localStorage.getItem("userBanner");
if (savedBanner) {
const bannerImg = this.shadowRoot?.getElementById("popup-banner-img");
if (bannerImg) {
bannerImg.src = savedBanner;
}
this.updateNavbarBanner(savedBanner);
}
}
closeNotificationPopup(event) {
const target = event.target;
const isOverlay = target.classList.contains("notification-popup-overlay");
const isCloseButton = target.classList.contains("close-popup");
if (!isOverlay && !isCloseButton)
return;
const popup = this.shadowRoot?.querySelector(".notification-popup-overlay");
if (popup)
popup.remove();
}
markAsRead(processName, messageId, element) {
const process = mockProcessRows.find((p) => p.process === processName);
if (!process)
return;
const message = process.notification.messages.find((m) => m.id === messageId);
if (!message || message.read)
return;
message.read = true;
element.classList.remove("unread");
element.classList.add("read");
const statusIcon = element.querySelector(".notification-status");
if (statusIcon) {
statusIcon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="green">
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/>
</svg>`;
}
const notifCount = this.calculateNotifications(process.notification.messages);
const countElement = this.shadowRoot?.querySelector(`.notification-count[data-process="${processName}"]`);
if (countElement) {
countElement.textContent = `${notifCount.unread}/${notifCount.total}`;
const bellContainer = countElement.closest(".notification-container");
const bell = bellContainer?.querySelector("svg");
if (bell && bellContainer && notifCount.unread === 0) {
bellContainer.classList.remove("has-unread");
bell.style.fill = "#666";
}
}
}
// Fonctions de gestion des données et de l'interface
calculateNotifications(messages) {
const total = messages.length;
const unread = messages.filter((msg) => !msg.read).length;
return { unread, total };
}
// Fonctions de récupération
exportRecovery() {
Swal.fire({
title: "Recovery Words Export",
text: "4 words will be displayed. We strongly recommend writing them down on paper before exporting the account. Do you want to continue?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Confirm",
cancelButtonText: "Cancel",
confirmButtonColor: "#C89666",
cancelButtonColor: "#6c757d",
// Ajouter des styles personnalisés
customClass: {
container: "recovery-popup-container",
popup: "recovery-popup"
}
}).then((result) => {
if (result.isConfirmed) {
const recoveryWords = this.generateRecoveryWords();
localStorage.setItem("recoveryWords", JSON.stringify(recoveryWords));
Swal.fire({
title: "Your Recovery Words",
html: `
<div class="recovery-words-container">
${recoveryWords.map((word, index) => `
<div class="recovery-word">
<span class="word-number">${index + 1}.</span>
<span class="word">${word}</span>
</div>
`).join("")}
</div>
<div class="recovery-warning">
Please write these words down carefully. They will be needed to recover your account.
</div>
`,
showCancelButton: false,
confirmButtonText: "I confirm the export",
confirmButtonColor: "#C89666",
allowOutsideClick: false,
allowEscapeKey: false,
customClass: {
container: "recovery-popup-container",
popup: "recovery-popup"
}
}).then((result2) => {
if (result2.isConfirmed) {
localStorage.setItem("recoveryExported", "true");
const exportRecoveryBtn = this.shadowRoot?.querySelector(".recovery-btn");
if (exportRecoveryBtn) {
exportRecoveryBtn.disabled = true;
exportRecoveryBtn.style.opacity = "0.5";
exportRecoveryBtn.style.cursor = "not-allowed";
}
}
});
}
});
}
generateRecoveryWords() {
const wordsList = ["apple", "banana", "orange", "grape", "kiwi", "mango", "peach", "plum", "lemon", "lime", "cherry", "melon", "pear", "fig", "date", "berry"];
const recoveryWords = [];
while (recoveryWords.length < 4) {
const randomWord = wordsList[Math.floor(Math.random() * wordsList.length)];
if (!recoveryWords.includes(randomWord)) {
recoveryWords.push(randomWord);
}
}
return recoveryWords;
}
exportUserData() {
const data = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key) {
const value = localStorage.getItem(key);
data[key] = value;
}
}
const jsonData = JSON.stringify(data, null, 2);
const blob = new Blob([jsonData], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "user_data.json";
this.shadowRoot?.appendChild(a);
a.click();
this.shadowRoot?.removeChild(a);
URL.revokeObjectURL(url);
}
updateActionButtons() {
const buttonContainer = this.shadowRoot?.querySelector(".button-container");
if (!buttonContainer)
return;
buttonContainer.innerHTML = `
<div class="action-buttons-wrapper">
<button onclick="${this.getConfirmFunction()}" class="action-button confirm-button">✓</button>
<button onclick="${this.getCancelFunction()}" class="action-button cancel-button">✗</button>
</div>
`;
}
getConfirmFunction() {
switch (currentMode) {
case "wallet":
return "window.confirmWalletRow()";
case "process":
return "window.confirmProcessRow()";
default:
return "window.confirmRowPairing()";
}
}
getCancelFunction() {
switch (currentMode) {
case "wallet":
return "window.cancelWalletRow()";
case "process":
return "window.cancelProcessRow()";
default:
return "window.cancelRowPairing()";
}
}
// Fonctions de gestion des tableaux
async addRowPairing() {
if (isAddingRow)
return;
isAddingRow = true;
const modal = document.createElement("div");
modal.className = "pairing-modal";
modal.innerHTML = `
<div class="pairing-modal-content">
<h3>Add New Device</h3>
<div class="pairing-form">
<div class="form-group">
<label for="sp-address">SP Address</label>
<input type="text" id="sp-address" class="edit-input" placeholder="Enter SP Address">
</div>
<div class="form-group">
<label for="device-name">Device Name</label>
<input type="text" id="device-name" class="edit-input" placeholder="Enter Device Name">
</div>
<div class="form-group">
<label for="sp-emojis">SP Emojis</label>
<input type="text" id="sp-emojis" class="edit-input" readonly>
</div>
<div class="button-group">
<button class="confirm-button">Confirm</button>
<button class="cancel-button">Cancel</button>
</div>
</div>
</div>
`;
this.shadowRoot?.appendChild(modal);
const spAddressInput = modal.querySelector("#sp-address");
const spEmojisInput = modal.querySelector("#sp-emojis");
const deviceNameInput = modal.querySelector("#device-name");
const confirmButton = modal.querySelector(".confirm-button");
const cancelButton = modal.querySelector(".cancel-button");
spAddressInput?.addEventListener("input", async () => {
const emojis = await addressToEmoji(spAddressInput.value);
if (spEmojisInput)
spEmojisInput.value = emojis;
});
confirmButton?.addEventListener("click", () => {
const spAddress = spAddressInput?.value.trim();
const deviceName = deviceNameInput?.value.trim();
const spEmojis = spEmojisInput?.value.trim();
if (!spAddress || !deviceName) {
this.showAlert("Please fill in all required fields");
return;
}
const newRow = {
column1: spAddress,
column2: deviceName,
column3: spEmojis || ""
};
const storageKey = STORAGE_KEYS[currentMode];
const rows = JSON.parse(localStorage.getItem(storageKey) || "[]");
rows.push(newRow);
localStorage.setItem(storageKey, JSON.stringify(rows));
this.updateTableContent(rows);
modal.remove();
isAddingRow = false;
});
cancelButton?.addEventListener("click", () => {
modal.remove();
isAddingRow = false;
});
}
// Fonctions de mise à jour de l'interface
updateTableContent(rows) {
const tbody = this.shadowRoot?.querySelector("#pairing-table tbody");
if (!tbody)
return;
tbody.innerHTML = rows.map((row) => `
<tr>
<td class="device-name" onclick="window.editDeviceName(this)">${row.column2}</td>
<td>${row.column3}</td>
<td>
<img src="https://api.qrserver.com/v1/create-qr-code/?size=50x50&data=${encodeURIComponent(row.column1)}"
alt="QR Code"
title="${row.column1}"
class="qr-code"
onclick="window.showQRCodeModal('${encodeURIComponent(row.column1)}')">
</td>
<td>
<button class="delete-button" onclick="window.deleteRowPairing(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="red">
<path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/>
</svg>
</button>
</td>
</tr>
`).join("");
}
confirmRowPairing() {
if (!currentRow)
return;
const inputs = currentRow.getElementsByTagName("input");
const values = Array.from(inputs).map((input) => input.value.trim());
if (values.some((value) => value === "")) {
this.showAlert("Please fill in all fields");
return;
}
if (values[0].length !== 118) {
this.showAlert("SP Address must be exactly 118 characters long");
return;
}
const newRow = {
column1: values[0],
column2: values[1],
column3: values[2]
};
const storageKey = STORAGE_KEYS[currentMode];
const rows = JSON.parse(localStorage.getItem(storageKey) || "[]");
rows.push(newRow);
localStorage.setItem(storageKey, JSON.stringify(rows));
isAddingRow = false;
currentRow = null;
this.resetButtonContainer();
this.updateTableContent(rows);
}
cancelRowPairing() {
if (!currentRow)
return;
currentRow.remove();
isAddingRow = false;
currentRow = null;
this.resetButtonContainer();
}
resetButtonContainer() {
const buttonContainer = this.shadowRoot?.querySelector(".button-container");
if (!buttonContainer)
return;
buttonContainer.innerHTML = `
<button class="add-row-button button-style" onclick="window.addRowPairing()">Add a line</button>
`;
}
deleteRowPairing(button) {
const row = button.closest("tr");
if (!row)
return;
const table = row.closest("tbody");
if (!table)
return;
const remainingRows = table.getElementsByTagName("tr").length;
if (remainingRows <= 2) {
this.showAlert("You must keep at least 2 devices paired");
return;
}
const modal = document.createElement("div");
modal.className = "confirm-delete-modal";
modal.innerHTML = `
<div class="confirm-delete-content">
<h3>Confirm Deletion</h3>
<p>Are you sure you want to delete this device?</p>
<div class="confirm-delete-buttons">
<button class="cancel-btn">Cancel</button>
<button class="confirm-btn">Delete</button>
</div>
</div>
`;
this.shadowRoot?.appendChild(modal);
const confirmBtn = modal.querySelector(".confirm-btn");
const cancelBtn = modal.querySelector(".cancel-btn");
confirmBtn?.addEventListener("click", () => {
const index = Array.from(table.children).indexOf(row);
const storageKey = STORAGE_KEYS[currentMode];
const rows = JSON.parse(localStorage.getItem(storageKey) || "[]");
if (index > -1) {
rows.splice(index, 1);
localStorage.setItem(storageKey, JSON.stringify(rows));
}
row.style.transition = "opacity 0.3s, transform 0.3s";
row.style.opacity = "0";
row.style.transform = "translateX(-100%)";
setTimeout(() => {
row.remove();
}, 300);
modal.remove();
});
cancelBtn?.addEventListener("click", () => {
modal.remove();
});
}
editDeviceName(cell) {
if (cell.classList.contains("editing"))
return;
const currentValue = cell.textContent || "";
const input = document.createElement("input");
input.type = "text";
input.value = currentValue;
input.className = "edit-input";
input.addEventListener("blur", () => this.finishEditing(cell, input));
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
this.finishEditing(cell, input);
}
});
cell.textContent = "";
cell.appendChild(input);
cell.classList.add("editing");
input.focus();
}
async finishEditing(cell, input) {
const newValue = input.value.trim();
if (newValue === "") {
cell.textContent = cell.getAttribute("data-original-value") || "";
cell.classList.remove("editing");
return;
}
try {
const service = await Services.getInstance();
const pairingProcessId = service.getPairingProcessId();
const process = await service.getProcess(pairingProcessId);
if (process) {
await service.updateMemberPublicName(process, newValue);
}
cell.textContent = newValue;
cell.classList.remove("editing");
} catch (error) {
console.error("Failed to update name:", error);
cell.textContent = cell.getAttribute("data-original-value") || "";
cell.classList.remove("editing");
}
}
// Fonction pour gérer le téléchargement de l'avatar
handleAvatarUpload(event) {
const input = event.target;
const file = input.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const result = e.target?.result;
const popupAvatar = this.shadowRoot?.getElementById("popup-avatar-img");
const navAvatar = this.shadowRoot?.querySelector(".nav-wrapper .avatar");
if (popupAvatar)
popupAvatar.src = result;
if (navAvatar)
navAvatar.src = result;
localStorage.setItem("userAvatar", result);
};
reader.readAsDataURL(file);
}
}
async showProcessCreation() {
this.hideAllContent();
const container = this.shadowRoot?.getElementById("process-creation-content");
if (container) {
getProcessCreation(container);
}
}
async showDocumentValidation() {
this.hideAllContent();
const container = this.shadowRoot?.getElementById("document-validation-content");
if (container) {
getDocumentValidation(container);
}
}
async showProcess() {
this.hideAllContent();
const container = this.shadowRoot?.getElementById("process-content");
if (container) {
const service = await Services.getInstance();
const myProcesses = await service.getMyProcesses();
if (myProcesses && myProcesses.length != 0) {
const myProcessesDataUnfiltered = await Promise.all(myProcesses.map(async (processId) => {
const process = await service.getProcess(processId);
const lastState = process ? service.getLastCommitedState(process) : null;
if (!lastState) {
return {
name: "",
publicData: {}
};
}
const description = await service.decryptAttribute(processId, lastState, "description");
const name = description ? description : "N/A";
const publicData = process ? await service.getPublicData(process) : null;
if (!publicData) {
return {
name: "",
publicData: {}
};
}
return {
name,
publicData
};
}));
const myProcessesData = myProcessesDataUnfiltered.filter((p) => p.name !== "" && Object.keys(p.publicData).length != 0);
createProcessTab(container, myProcessesData);
} else {
createProcessTab(container, []);
}
}
}
showProcessNotifications(processName) {
const process = mockProcessRows.find((p) => p.process === processName);
if (!process)
return;
const modal = document.createElement("div");
modal.className = "notifications-modal";
let notificationsList = process.notification.messages.map((msg) => `
<div class="notification-item ${msg.read ? "read" : "unread"}"
onclick="window.markAsRead('${processName}', ${msg.id}, this)">
<div class="notification-status">
${msg.read ? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" fill="green">
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/>
</svg>` : `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16" fill="black">
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"/>
</svg>`}
</div>
<div class="notification-content">
<span>${msg.message}</span>
<small>${msg.date}</small>
</div>
</div>
`).join("");
if (process.notification.messages.length === 0) {
notificationsList = "<p>No notifications</p>";
}
modal.innerHTML = `
<div class="notifications-content">
<h3>${processName} Notifications</h3>
<div class="notifications-list">
${notificationsList}
</div>
<button class="close-notifications">x</button>
</div>
`;
this.shadowRoot?.appendChild(modal);
const countElement = this.shadowRoot?.querySelector(`.notification-count[data-process="${processName}"]`);
if (countElement) {
const notifCount = this.calculateNotifications(process.notification.messages);
countElement.textContent = `${notifCount.unread}/${notifCount.total}`;
}
const closeButton = modal.querySelector(".close-notifications");
closeButton?.addEventListener("click", () => {
modal.remove();
this.showProcess();
});
}
handleLogout() {
localStorage.clear();
window.location.href = "../login/login.html";
}
// Fonctions de gestion des contrats
showContractPopup(contractId, event) {
if (event) {
event.preventDefault();
}
const contract = mockContracts[contractId];
if (!contract) {
console.error("Contract not found:", contractId);
return;
}
const popup = document.createElement("div");
popup.className = "contract-popup-overlay";
popup.innerHTML = `
<div class="contract-popup-content">
<button class="close-contract-popup">&times;</button>
<h2>${contract.title}</h2>
<div>
<p><strong>Date:</strong> ${contract.date}</p>
<p><strong>Parties:</strong> ${contract.parties.join(", ")}</p>
<p><strong>Terms:</strong></p>
<ul>
${contract.terms.map((term) => `<li>${term}</li>`).join("")}
</ul>
<p><strong>Content:</strong> ${contract.content}</p>
</div>
</div>
`;
this.shadowRoot?.appendChild(popup);
const closeBtn = popup.querySelector(".close-contract-popup");
const closePopup = () => popup.remove();
closeBtn?.addEventListener("click", closePopup);
popup.addEventListener("click", (e) => {
if (e.target === popup)
closePopup();
});
}
// Fonction utilitaire pour cacher tous les contenus
hideAllContent() {
const contents = ["pairing-content", "wallet-content", "process-content", "process-creation-content", "data-content", "document-validation-content"];
contents.forEach((id) => {
const element = this.shadowRoot?.getElementById(id);
if (element) {
element.style.display = "none";
}
});
}
// Fonctions d'affichage des sections
async showPairing() {
const service = await Services.getInstance();
const spAddress = await service.getDeviceAddress();
isAddingRow = false;
currentRow = null;
currentMode = "pairing";
this.hideAllContent();
const headerElement = this.shadowRoot?.getElementById("parameter-header");
if (headerElement) {
headerElement.textContent = "Pairing";
}
const pairingContent = this.shadowRoot?.getElementById("pairing-content");
if (pairingContent) {
pairingContent.style.display = "block";
pairingContent.innerHTML = `
<div class="parameter-header" id="parameter-header">Pairing</div>
<div class="table-container">
<table class="parameter-table" id="pairing-table">
<thead>
<tr>
<th>Device Name</th>
<th>SP Emojis</th>
<th>QR Code</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="button-container">
<button class="add-row-button button-style" onclick="window.addRowPairing()">Add a device</button>
</div>
</div>
`;
let rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.pairing) || "[]");
const deviceExists = rows.some((row) => row.column1 === spAddress);
if (!deviceExists && spAddress) {
const emojis = await addressToEmoji(spAddress);
try {
const pairingProcessId = await service.getPairingProcessId();
console.log("Pairing Process ID:", pairingProcessId);
const pairingProcess = await service.getProcess(pairingProcessId);
console.log("Pairing Process:", pairingProcess);
const userName = pairingProcess?.states?.[0]?.public_data?.memberPublicName || localStorage.getItem("userName");
console.log("Username found:", userName);
const newRow = {
column1: spAddress,
column2: userName,
column3: emojis
};
rows = [newRow, ...rows];
localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows));
} catch (error) {
console.error("Error getting pairing process:", error);
const newRow = {
column1: spAddress,
column2: "This Device",
column3: emojis
};
rows = [newRow, ...rows];
localStorage.setItem(STORAGE_KEYS.pairing, JSON.stringify(rows));
}
}
this.updateTableContent(rows);
}
}
showWallet() {
isAddingRow = false;
currentRow = null;
currentMode = "wallet";
this.hideAllContent();
const headerTitle = this.shadowRoot?.getElementById("header-title");
if (headerTitle)
headerTitle.textContent = "Wallet";
const walletContent = this.shadowRoot?.getElementById("wallet-content");
if (!walletContent)
return;
walletContent.style.display = "block";
walletContent.innerHTML = `
<div class="parameter-header" id="parameter-header">Wallet</div>
<div class="table-container">
<table class="parameter-table" id="wallet-table">
<thead>
<tr>
<th>Label</th>
<th>Wallet</th>
<th>Type</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="button-container">
<button class="add-row-button button-style" onclick="addWalletRow()">Add a line</button>
</div>
</div>
`;
const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || "[]");
this.updateWalletTableContent(rows);
}
updateWalletTableContent(rows) {
const tbody = this.shadowRoot?.querySelector("#wallet-table tbody");
if (!tbody)
return;
tbody.innerHTML = rows.map((row) => `
<tr>
<td>${row.column1}</td>
<td>${row.column2}</td>
<td class="device-name" onclick="editDeviceName(this)">${row.column3}</td>
</tr>
`).join("");
}
showData() {
currentMode = "data";
this.hideAllContent();
const headerTitle = this.shadowRoot?.getElementById("header-title");
if (headerTitle)
headerTitle.textContent = "Data";
const dataContent = this.shadowRoot?.getElementById("data-content");
if (dataContent) {
dataContent.style.display = "block";
dataContent.innerHTML = `
<div class="parameter-header" id="parameter-header">Data</div>
<div class="table-container">
<table class="parameter-table" id="data-table">
<thead>
<tr>
<th>Name</th>
<th>Visibility</th>
<th>Role</th>
<th>Duration</th>
<th>Legal</th>
<th>Contract</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
`;
const rows = mockDataRows || JSON.parse(localStorage.getItem(STORAGE_KEYS.data) || "[]");
this.updateDataTableContent(rows);
}
}
// Fonctions de gestion du wallet
addWalletRow() {
if (isAddingRow)
return;
isAddingRow = true;
const table = this.shadowRoot?.getElementById("wallet-table")?.getElementsByTagName("tbody")[0];
if (!table)
return;
currentRow = table.insertRow();
const placeholders = ["Label", "Wallet", "Type"];
placeholders.forEach((placeholder) => {
const cell = currentRow.insertCell();
const input = document.createElement("input");
input.type = "text";
input.placeholder = placeholder;
input.className = "edit-input";
cell.appendChild(input);
});
const buttonContainer = this.shadowRoot?.querySelector("#wallet-content .button-container");
if (!buttonContainer)
return;
buttonContainer.innerHTML = `
<div class="action-buttons-wrapper">
<button onclick="confirmWalletRow()" class="action-button confirm-button">✓</button>
<button onclick="cancelWalletRow()" class="action-button cancel-button">✗</button>
</div>
`;
this.updateActionButtons();
}
confirmWalletRow() {
if (!currentRow)
return;
const inputs = Array.from(currentRow.getElementsByTagName("input"));
const allFieldsFilled = inputs.every((input) => input.value.trim() !== "");
if (allFieldsFilled) {
const newRow = {
column1: inputs[0].value.trim(),
column2: inputs[1].value.trim(),
column3: inputs[2].value.trim()
};
const rows = JSON.parse(localStorage.getItem(STORAGE_KEYS.wallet) || "[]");
rows.push(newRow);
localStorage.setItem(STORAGE_KEYS.wallet, JSON.stringify(rows));
isAddingRow = false;
currentRow = null;
this.showWallet();
} else {
this.showAlert("Please complete all fields before confirming.");
}
}
cancelWalletRow() {
if (!currentRow)
return;
currentRow.remove();
isAddingRow = false;
currentRow = null;
const buttonContainer = this.shadowRoot?.querySelector("#wallet-content .button-container");
if (!buttonContainer)
return;
buttonContainer.innerHTML = `
<button class="add-row-button button-style" onclick="window.addWalletRow()">Add a line</button>
`;
}
updateDataTableContent(rows) {
const tbody = this.shadowRoot?.querySelector("#data-table tbody");
if (!tbody)
return;
tbody.innerHTML = rows.map((row) => `
<tr>
<td>${row.column1}</td>
<td>${row.column2}</td>
<td>${row.column3}</td>
<td>${row.column4}</td>
<td>${row.column5}</td>
<td>
<a href="javascript:void(0)" onclick="window.showContractPopup('${row.column6}'); return false;">${row.column6}</a>
</td>
</tr>
`).join("");
}
// Fonctions de gestion de l'avatar et de la bannière
openAvatarPopup() {
const popup = this.shadowRoot?.getElementById("avatar-popup");
if (!popup)
return;
const savedName = localStorage.getItem("userName");
const savedLastName = localStorage.getItem("userLastName");
const savedAvatar = localStorage.getItem("userAvatar") || "https://via.placeholder.com/150";
const savedBanner = localStorage.getItem("userBanner") || "https://via.placeholder.com/800x200";
const savedAddress = localStorage.getItem("userAddress") || "🏠 🌍 🗽🎊😩-🎊😑😩";
popup.innerHTML = `
<div class="popup-content">
<h1>Profile</h1>
<span class="close-popup">&times;</span>
<div class="banner-container">
<div class="banner-wrapper">
<img src="${savedBanner}" alt="Banner" id="popup-banner-img" class="banner-image clickable">
<input type="file" id="banner-upload" accept="image/*" style="display: none;">
</div>
</div>
<div class="popup-avatar">
<label for="avatar-upload" class="avatar-upload-label">
<img src="${savedAvatar}" alt="Avatar" id="popup-avatar-img">
<input type="file" id="avatar-upload" accept="image/*" style="display: none;">
<div class="avatar-overlay">
<span>Change Avatar</span>
</div>
</label>
</div>
<div class="popup-info">
<div class="info-row">
<strong>Name:</strong>
<input type="text" id="userName" value="${savedName}" class="editable">
</div>
<!--<div class="info-row">
<strong>Last Name:</strong>
<input type="text" id="userLastName" value="${savedLastName}" class="editable">
</div>-->
<div class="info-row">
<strong>Address:</strong>
<span>${savedAddress}</span>
</div>
</div>
<div class="popup-button-container">
<div class="action-buttons-row">
<button class="export-btn" onclick="window.exportUserData()">Export User Data</button>
<button class="export-btn recovery-btn" onclick="window.exportRecovery()">Export Recovery</button>
<button class="delete-account-btn" onclick="window.confirmDeleteAccount()">Delete Account</button>
</div>
<button class="logout-btn" onclick="window.handleLogout()">Log out</button>
</div>
</div>
`;
popup.style.display = "block";
this.setupEventListeners(popup);
const bannerImg = popup.querySelector("#popup-banner-img");
const bannerInput = popup.querySelector("#banner-upload");
if (bannerImg && bannerInput) {
bannerImg.addEventListener("click", () => {
bannerInput.click();
});
}
const recoveryExported = localStorage.getItem("recoveryExported") === "true";
if (recoveryExported) {
const exportRecoveryBtn = popup.querySelector(".recovery-btn");
if (exportRecoveryBtn) {
exportRecoveryBtn.disabled = true;
exportRecoveryBtn.style.opacity = "0.5";
exportRecoveryBtn.style.cursor = "not-allowed";
}
}
}
setupEventListeners(popup) {
const closeBtn = popup.querySelector(".close-popup");
if (closeBtn) {
closeBtn.addEventListener("click", () => {
popup.style.display = "none";
});
}
const avatarUpload = popup.querySelector("#avatar-upload");
if (avatarUpload) {
avatarUpload.addEventListener("change", (e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e2) => {
const result = e2.target?.result;
const popupAvatar = this.shadowRoot?.getElementById("popup-avatar-img");
const previewAvatar = this.shadowRoot?.querySelector(".preview-avatar");
if (popupAvatar)
popupAvatar.src = result;
if (previewAvatar)
previewAvatar.src = result;
localStorage.setItem("userAvatar", result);
};
reader.readAsDataURL(file);
}
});
}
const bannerUpload = popup.querySelector("#banner-upload");
if (bannerUpload) {
bannerUpload.addEventListener("change", (e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e2) => {
const result = e2.target?.result;
const popupBanner = this.shadowRoot?.getElementById("popup-banner-img");
const previewBanner = this.shadowRoot?.querySelector(".preview-banner-img");
if (popupBanner)
popupBanner.src = result;
if (previewBanner)
previewBanner.src = result;
localStorage.setItem("userBanner", result);
};
reader.readAsDataURL(file);
}
});
}
const nameInput = popup.querySelector("#userName");
const lastNameInput = popup.querySelector("#userLastName");
if (nameInput) {
nameInput.addEventListener("input", () => {
const newName = nameInput.value;
localStorage.setItem("userName", newName);
const previewName = this.shadowRoot?.querySelector(".preview-name");
if (previewName)
previewName.textContent = newName;
});
}
if (lastNameInput) {
lastNameInput.addEventListener("input", () => {
const newLastName = lastNameInput.value;
localStorage.setItem("userLastName", newLastName);
const previewLastName = this.shadowRoot?.querySelector(".preview-lastname");
if (previewLastName)
previewLastName.textContent = newLastName;
});
}
}
closeAvatarPopup() {
const popup = this.shadowRoot?.querySelector(".avatar-popup");
if (popup)
popup.remove();
}
loadAvatar() {
const savedAvatar = localStorage.getItem("userAvatar");
if (savedAvatar) {
const avatarImg = this.shadowRoot?.querySelector(".avatar");
if (avatarImg) {
avatarImg.src = savedAvatar;
}
}
}
loadUserInfo() {
const savedName = localStorage.getItem("userName");
const savedLastName = localStorage.getItem("userLastName");
const savedAvatar = localStorage.getItem("userAvatar");
const savedBanner = localStorage.getItem("userBanner");
if (savedName) {
const previewName = this.shadowRoot?.querySelector(".preview-name");
if (previewName) {
previewName.textContent = savedName;
}
}
if (savedLastName) {
const previewLastName = this.shadowRoot?.querySelector(".preview-lastname");
if (previewLastName) {
previewLastName.textContent = savedLastName;
}
}
if (savedAvatar) {
const previewAvatar = this.shadowRoot?.querySelector(".preview-avatar");
if (previewAvatar) {
previewAvatar.src = savedAvatar;
}
}
if (savedBanner) {
const previewBanner = this.shadowRoot?.querySelector(".preview-banner-img");
if (previewBanner) {
previewBanner.src = savedBanner;
}
}
}
updateNavbarName(name) {
const nameElement = this.shadowRoot?.querySelector(".nav-wrapper .user-name");
if (nameElement) {
nameElement.textContent = name;
}
}
updateNavbarLastName(lastName) {
const lastNameElement = this.shadowRoot?.querySelector(".nav-wrapper .user-lastname");
if (lastNameElement) {
lastNameElement.textContent = lastName;
}
}
updateProfilePreview(data) {
if (data.avatar) {
const previewAvatar = this.shadowRoot?.querySelector(".preview-avatar");
if (previewAvatar)
previewAvatar.src = data.avatar;
}
if (data.banner) {
const previewBanner = this.shadowRoot?.querySelector(".preview-banner-img");
if (previewBanner)
previewBanner.src = data.banner;
}
if (data.name) {
const previewName = this.shadowRoot?.querySelector(".preview-name");
if (previewName)
previewName.textContent = data.name;
}
if (data.lastName) {
const previewLastName = this.shadowRoot?.querySelector(".preview-lastname");
if (previewLastName)
previewLastName.textContent = data.lastName;
}
}
initializeEventListeners() {
this.shadowRoot?.addEventListener("DOMContentLoaded", () => {
this.showPairing();
});
const editableFields = this.shadowRoot?.querySelectorAll(".editable");
if (editableFields) {
editableFields.forEach((field) => {
field.addEventListener("click", () => {
if (!field.classList.contains("editing")) {
const currentValue = field.textContent || "";
const input = document.createElement("input");
input.type = "text";
input.value = currentValue;
input.className = "edit-input";
field.textContent = "";
field.appendChild(input);
field.classList.add("editing");
input.focus();
}
});
});
}
const avatarInput = this.shadowRoot?.getElementById("avatar-upload");
if (avatarInput) {
avatarInput.addEventListener("change", this.handleAvatarUpload.bind(this));
}
}
showQRCodeModal(pairingId) {
const modal = document.createElement("div");
modal.className = "qr-modal";
modal.innerHTML = `
<div class="qr-modal-content">
<span class="close-qr-modal">&times;</span>
<img src="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${pairingId}"
alt="QR Code Large"
class="qr-code-large">
<div class="qr-address">${decodeURIComponent(pairingId)}</div>
</div>
`;
this.shadowRoot?.appendChild(modal);
const closeBtn = modal.querySelector(".close-qr-modal");
closeBtn?.addEventListener("click", () => modal.remove());
modal.addEventListener("click", (e) => {
if (e.target === modal)
modal.remove();
});
}
}
customElements.define("account-element", AccountElement);
const accountCss = "/* Styles de base */\r\n:root {\r\n --primary-color: #3A506B;\r\n /* Bleu métallique */\r\n --secondary-color: #B0BEC5;\r\n /* Gris acier */\r\n --accent-color: #D68C45;\r\n /* Cuivre */\r\n}\r\n\r\nbody {\r\n font-family: Arial, sans-serif;\r\n margin: 0;\r\n padding: 0;\r\n display: flex;\r\n height: 100vh;\r\n background-color: #e9edf1;\r\n flex-direction: column;\r\n}\r\n\r\n/*-------------------------------------- Avatar--------------------------------------*/\r\n\r\n.avatar-section {\r\n position: relative;\r\n height: 60px;\r\n width: 260px;\r\n margin-left: 10px;\r\n overflow: hidden;\r\n border-radius: 10px;\r\n}\r\n\r\n.user-info {\r\n display: flex;\r\n flex-direction: column;\r\n color: white;\r\n}\r\n\r\n.user-name, .user-lastname {\r\n font-size: 0.9rem;\r\n font-weight: 700;\r\n}\r\n\r\n\r\n.user-name:hover, .user-lastname:hover {\r\n color: var(--accent-color);\r\n cursor: pointer;\r\n}\r\n\r\n.avatar-container {\r\n width: 45px;\r\n height: 45px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.avatar-container {\r\n width: 80px; /* Taille réduite */\r\n height: 80px;\r\n margin: 0 auto;\r\n}\r\n\r\n.avatar {\r\n height: 100%;\r\n border-radius: 50%;\r\n object-fit: cover;\r\n border: 2px solid white;\r\n}\r\n\r\n.avatar img {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n}\r\n\r\n\r\n/*-------------------------------------- BANNER--------------------------------------*/\r\n\r\n/* Styles pour la bannière avec image */\r\n.banner-image-container {\r\n position: relative;\r\n width: 100%;\r\n height: 200px;\r\n overflow: hidden;\r\n border-radius: 10px;\r\n margin-bottom: 15px;\r\n}\r\n\r\n.banner-image {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n z-index: 1;\r\n}\r\n\r\n.banner-content {\r\n position: relative;\r\n z-index: 2;\r\n display: flex;\r\n align-items: center;\r\n gap: 15px;\r\n height: 100%;\r\n padding: 0 15px;\r\n background: rgba(0, 0, 0, 0.3);\r\n overflow: visible;\r\n}\r\n\r\n.banner-content .avatar-container {\r\n width: 45px;\r\n height: 45px;\r\n overflow: hidden; \r\n border-radius: 50%;\r\n border: 2px solid white;\r\n transition: transform 0.3s ease; \r\n}\r\n\r\n.banner-content .avatar {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n border: none; \r\n}\r\n\r\n.banner-content .avatar-container:hover {\r\n transform: scale(1.15);\r\n cursor: pointer;\r\n}\r\n\r\n/* Style pour le bouton de changement de bannière */\r\n.banner-upload-label {\r\n display: block;\r\n width: auto;\r\n padding: 12px 20px;\r\n background-color: var(--accent-color);\r\n color: white;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n text-align: center;\r\n font-size: 16px;\r\n margin: 20px auto;\r\n max-width: 250px;\r\n}\r\n\r\n.banner-upload-label:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.banner-controls {\r\n margin-top: 15px;\r\n display: flex;\r\n justify-content: center;\r\n width: 100%;\r\n}\r\n\r\n.banner-preview {\r\n margin: 10px 0;\r\n}\r\n\r\n.banner-preview h3 {\r\n margin: 0 0 10px 0;\r\n font-size: 18px;\r\n}\r\n\r\n.banner-image-container {\r\n height: 150px;\r\n margin-bottom: 10px;\r\n}\r\n\r\n\r\n.nav-wrapper {\r\n position: fixed;\r\n background: white;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n height: 9vh;\r\n width: 100vw;\r\n left: 0;\r\n top: 0;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n z-index: 1000;\r\n}\r\n/* Mise à jour des styles de la navbar pour inclure l'image de bannière */\r\n.nav-wrapper .avatar-section {\r\n position: relative;\r\n background: none;\r\n}\r\n\r\n.nav-wrapper .banner-image {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n z-index: -1;\r\n filter: brightness(0.7);\r\n}\r\n\r\n\r\n\r\n/*-------------------------------------- Popup--------------------------------------*/\r\n/* Styles pour la popup */\r\n.popup {\r\n display: none;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n z-index: 1000;\r\n}\r\n\r\n.popup-content {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n background: white;\r\n padding: 20px;\r\n border-radius: var(--border-radius);\r\n box-shadow: var(--box-shadow);\r\n width: 400px; \r\n max-height: 80vh; \r\n overflow-y: auto; \r\n}\r\n.popup-content h2 {\r\n margin: 0 0 15px 0;\r\n font-size: 24px;\r\n}\r\n\r\n.close-popup {\r\n position: absolute;\r\n right: 15px;\r\n top: 10px;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n.close-popup:hover {\r\n color: #000;\r\n}\r\n\r\n.popup-avatar {\r\n text-align: center;\r\n margin: 20px 0;\r\n position: relative;\r\n}\r\n\r\n.avatar-upload-label {\r\n position: relative;\r\n display: inline-block;\r\n cursor: pointer;\r\n width: 0%;\r\n margin-left: -20%;\r\n}\r\n\r\n.avatar-overlay {\r\n position: absolute;\r\n top: 0;\r\n left: 50px;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n border-radius: 50%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n opacity: 0;\r\n transition: opacity 0.3s;\r\n}\r\n\r\n.avatar-overlay span {\r\n color: white;\r\n font-size: 14px;\r\n text-align: center;\r\n}\r\n\r\n.avatar-upload-label:hover .avatar-overlay {\r\n opacity: 1;\r\n}\r\n\r\n.popup-avatar img {\r\n width: 100px;\r\n height: 100px;\r\n border-radius: 50%;\r\n border: 3px solid var(--accent-color);\r\n object-fit: cover;\r\n}\r\n\r\n.popup-info {\r\n margin: 15px 0;\r\n}\r\n\r\n.info-row {\r\n margin: 8px 0;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n\r\n.popup-info strong {\r\n min-width: 100px; /* Largeur fixe pour l'alignement */\r\n}\r\n\r\n/* Editable Name and Lastname */\r\n.editable {\r\n cursor: pointer;\r\n display: inline-block;\r\n min-width: 100px;\r\n padding: 2px 5px;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.editable:hover {\r\n background-color: #f0f0f0;\r\n}\r\n\r\n.editable.editing {\r\n background-color: white;\r\n border: 1px solid var(--accent-color);\r\n outline: none;\r\n}\r\n\r\n.edit-input {\r\n border: 1px solid var(--accent-color);\r\n border-radius: 3px;\r\n padding: 2px 5px;\r\n font-size: inherit;\r\n font-family: inherit;\r\n outline: none;\r\n width: 100%; \r\n min-width: 100px;\r\n margin: 0; \r\n box-sizing: border-box; \r\n}\r\n\r\n/* Boutons */\r\n\r\n\r\n.popup-button-container {\r\n display: flex\r\n;\r\n flex-direction: column;\r\n margin-top: 20px;\r\n gap: 15px;\r\n}\r\n\r\n.action-buttons-row {\r\n display: flex\r\n;\r\n justify-content: space-between;\r\n gap: 15px;\r\n}\r\n.banner-upload-label,\r\n.export-btn,\r\n.delete-account-btn {\r\n padding: 8px 15px;\r\n margin: 10px 0;\r\n font-size: 14px;\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: #dc3545;\r\n}\r\n\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n flex: 1; /* Pour qu'ils prennent la même largeur */\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n text-align: center;\r\n}\r\n\r\n\r\n\r\n/* Séparateurs */\r\n.popup-info,\r\n.export-section,\r\n.delete-account-section {\r\n padding-top: 10px;\r\n margin-top: 10px;\r\n border-top: 1px solid #eee;\r\n}\r\n\r\n.logout-btn {\r\n background-color: rgb(108, 117, 125);\r\n font-size: 16px;\r\n cursor: pointer;\r\n color: white;\r\n text-align: center;\r\n flex: 1 1 0%;\r\n padding: 12px 20px;\r\n border-width: initial;\r\n border-style: none;\r\n border-color: initial;\r\n border-image: initial;\r\n border-radius: 8px;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n/*-------------------------------------- Delete Account--------------------------------------*/\r\n.delete-account-section {\r\n margin-top: 30px;\r\n padding-top: 20px;\r\n border-top: 1px solid #ddd;\r\n text-align: center;\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: #dc3545;\r\n}\r\n\r\n.delete-account-btn:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n/* Style pour la modal de confirmation */\r\n.confirm-delete-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.confirm-delete-content {\r\n background-color: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n max-width: 400px;\r\n width: 90%;\r\n text-align: center;\r\n}\r\n\r\n.confirm-delete-content h3 {\r\n margin-top: 0;\r\n color: #333;\r\n}\r\n\r\n.confirm-delete-content p {\r\n margin: 15px 0;\r\n color: #666;\r\n}\r\n\r\n.confirm-delete-buttons {\r\n display: flex;\r\n justify-content: center;\r\n gap: 10px;\r\n margin-top: 20px;\r\n}\r\n\r\n.confirm-delete-buttons button {\r\n padding: 8px 20px;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.confirm-delete-buttons .confirm-btn {\r\n background-color: #dc3545;\r\n color: white;\r\n}\r\n\r\n.confirm-delete-buttons .confirm-btn:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n.confirm-delete-buttons .cancel-btn {\r\n background-color: #6c757d;\r\n color: white;\r\n}\r\n\r\n.confirm-delete-buttons .cancel-btn:hover {\r\n background-color: #5a6268;\r\n}\r\n\r\n/*-------------------------------------- Export--------------------------------------*/\r\n.export-section {\r\n margin: 20px 0;\r\n text-align: center;\r\n padding: 15px 0;\r\n border-top: 1px solid #ddd;\r\n}\r\n\r\n.export-btn {\r\n background-color: var(--accent-color);\r\n}\r\n\r\n.export-btn:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.export-section,\r\n.delete-account-section {\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n margin: 15px 0;\r\n}\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n width: 80%;\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n text-align: center;\r\n}\r\n\r\n/*-------------------------------------- NAVBAR--------------------------------------*/\r\n\r\n.brand-logo {\r\n font-size: 1.5rem;\r\n font-weight: bold;\r\n}\r\n\r\n.nav-wrapper {\r\n position: fixed;\r\n background: radial-gradient(circle, white, var(--primary-color));\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n color: #37474F;\r\n height: 9vh;\r\n width: 100vw;\r\n left: 0;\r\n top: 0;\r\n box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);\r\n}\r\n\r\n/* Icônes de la barre de navigation */\r\n.nav-right-icons {\r\n margin-right: 20px;\r\n}\r\n\r\n.burger-menu {\r\n height: 20px;\r\n width: 20px;\r\n margin-right: 1rem;\r\n cursor: pointer;\r\n}\r\n\r\n/* Par défaut, le menu est masqué */\r\n#menu {\r\n display: none;\r\n /* Menu caché par défaut */\r\n transition: display 0.3s ease-in-out;\r\n}\r\n\r\n\r\n.burger-menu {\r\n width: 24px;\r\n height: 24px;\r\n cursor: pointer;\r\n}\r\n\r\n.burger-menu-icon {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n/* Icône burger */\r\n#burger-icon {\r\n cursor: pointer;\r\n}\r\n\r\n/* .menu-content {\r\n display: none;\r\n position: absolute;\r\n top: 3.4rem;\r\n right: 1rem;\r\n background-color: white;\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\r\n border-radius: 5px;\r\n overflow: hidden;\r\n}\r\n\r\n.menu-content a {\r\n display: block;\r\n padding: 10px 20px;\r\n text-decoration: none;\r\n color: #333;\r\n border-bottom: 1px solid #e0e0e0;\r\n\r\n &:hover {\r\n background-color: rgba(26, 28, 24, .08);\r\n }\r\n}\r\n\r\n.menu-content a:last-child {\r\n border-bottom: none;\r\n} */\r\n\r\n/* Ajustement pour la barre de navigation fixe */\r\n.container {\r\n display: flex;\r\n flex: 1;\r\n height: 90vh;\r\n margin-top: 9vh;\r\n margin-left: -1%;\r\n text-align: left;\r\n width: 100vw;\r\n}\r\n\r\n/* Liste des information sur l'account */\r\n\r\n.parameter-list {\r\n width: 24.5%;\r\n background-color: #1f2c3d;\r\n color: white;\r\n padding: 20px;\r\n box-sizing: border-box;\r\n overflow-y: auto;\r\n border-right: 2px solid #2c3e50;\r\n flex-shrink: 0; \r\n padding-right: 10px;\r\n height: 91vh;\r\n}\r\n\r\n.parameter-list ul {\r\n padding: 15px;\r\n margin-left: 10px;\r\n border-radius: 8px;\r\n background-color: #273646;\r\n cursor: pointer;\r\n transition: background-color 0.3s, box-shadow 0.3s;\r\n}\r\n\r\n\r\n.parameter-list ul:hover {\r\n background-color: #34495e;\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n\r\n/* Zone des info des parametre */\r\n\r\n.parameter-area {\r\n display: flex;\r\n flex-direction: column;\r\n flex: 1;\r\n min-width: 0; \r\n background-color: #ffffff;\r\n border-radius: 10px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\r\n margin: 0px;\r\n margin-top: 20px;\r\n margin-left: 1%;\r\n margin-bottom: -7px;\r\n}\r\n\r\n/* En-tête du parametre */\r\n.parameter-header {\r\n background-color: #34495e;\r\n color: white;\r\n padding: 15px;\r\n font-size: 20px;\r\n font-weight: bold;\r\n border-radius: 10px 10px 0 0;\r\n text-align: center;\r\n}\r\n\r\n/* Style du tableau dans parameter-area */\r\n.parameter-table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n margin: 15px 0;\r\n table-layout: fixed; \r\n}\r\n\r\n.parameter-table th, .parameter-table td {\r\n border: 1px solid #ddd;\r\n padding: 8px;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-align: center;\r\n}\r\n\r\n.parameter-table th {\r\n background-color: var(--secondary-color);\r\n color: white;\r\n font-weight: bold;\r\n}\r\n\r\n.parameter-table tr:nth-child(even) {\r\n background-color: #f2f2f2;\r\n}\r\n\r\n\r\n\r\n.parameter-table tr:hover {\r\n background-color: #ddd;\r\n}\r\n\r\n/* Conteneur pour les boutons */\r\n.button-container {\r\n display: flex;\r\n justify-content: center;\r\n gap: 15px;\r\n margin: 15px 0;\r\n}\r\n\r\n/* Boutons \"Ajouter une ligne\" et \"Confirmer\" */\r\n.add-row-button, .confirm-all-button, .delete-row-button {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n padding: 8px 15px;\r\n cursor: pointer;\r\n border-radius: 5px;\r\n font-size: 0.9em;\r\n margin-right: 5px;\r\n}\r\n\r\n.add-row-button:hover, .confirm-all-button:hover, .delete-row-button:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n\r\n.button-style {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n border-radius: 5px;\r\n padding: 10px 20px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.button-style:hover {\r\n background-color: darkorange;\r\n}\r\n\r\n.content-container {\r\n width: 100%;\r\n}\r\n\r\n#pairing-content,\r\n#wallet-content {\r\n width: 100%;\r\n}\r\n\r\n.editable-cell {\r\n cursor: pointer;\r\n}\r\n\r\n.editable-cell:hover {\r\n background-color: #f5f5f5;\r\n}\r\n\r\n.edit-input {\r\n width: 100%;\r\n padding: 5px;\r\n box-sizing: border-box;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n}\r\n\r\n/*-------------------------------------- Notification--------------------------------------*/\r\n.notification-cell {\r\n position: relative;\r\n}\r\n\r\n.notification-bell {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.notification-badge {\r\n position: absolute;\r\n top: -8px;\r\n right: -8px;\r\n background-color: red;\r\n color: white;\r\n border-radius: 50%;\r\n padding: 2px 6px;\r\n font-size: 12px;\r\n min-width: 15px;\r\n text-align: center;\r\n}\r\n\r\n.fa-bell {\r\n color: #666;\r\n font-size: 20px;\r\n}\r\n\r\n\r\n/* Media Queries pour Mobile */\r\n@media screen and (max-width: 768px) {\r\n /* Navbar */\r\n .nav-wrapper {\r\n height: 9vh;\r\n padding: 0;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n }\r\n\r\n /* Section avatar (gauche) */\r\n .avatar-section {\r\n width: 200px; /* Largeur réduite */\r\n margin-left: 10px;\r\n order: 0; /* Garde à gauche */\r\n }\r\n\r\n .avatar-container {\r\n width: 35px;\r\n height: 35px;\r\n }\r\n\r\n .user-info span {\r\n font-size: 0.8rem;\r\n }\r\n\r\n /* Logo (centre) */\r\n .brand-logo {\r\n order: 0; \r\n flex: 0 0 auto;\r\n padding: 0;\r\n font-size: 1.2rem;\r\n }\r\n\r\n /* Menu burger (droite) */\r\n .nav-right-icons {\r\n order: 0; \r\n width: auto;\r\n padding-right: 15px;\r\n }\r\n\r\n .burger-menu {\r\n width: 24px;\r\n height: 24px;\r\n }\r\n\r\n /* Ajustements pour la bannière */\r\n .banner-image-container {\r\n height: 100%;\r\n }\r\n\r\n .banner-content {\r\n padding: 0 10px;\r\n }\r\n\r\n /* Style des boutons dans la popup */\r\n .button-container {\r\n display: flex;\r\n gap: 10px;\r\n margin: 15px 0;\r\n width: 100%;\r\n }\r\n\r\n .export-btn,\r\n .delete-account-btn {\r\n flex: 1;\r\n padding: 12px 15px;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 14px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n color: white;\r\n }\r\n\r\n .export-btn {\r\n background-color: var(--accent-color);\r\n }\r\n\r\n .delete-account-btn {\r\n background-color: var(--danger-color);\r\n }\r\n}\r\n\r\n/* Media Queries pour très petits écrans */\r\n@media screen and (max-width: 380px) {\r\n .avatar-section {\r\n width: 150px; \r\n }\r\n\r\n .user-info span {\r\n font-size: 0.7rem;\r\n }\r\n}\r\n\r\n/* Style des boutons */\r\n.button-container {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n.export-btn,\r\n.delete-account-btn {\r\n flex: 1;\r\n padding: 12px 20px;\r\n border: none;\r\n border-radius: 8px;\r\n color: white;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n font-size: 14px;\r\n display: block;\r\n}\r\n\r\n.export-btn {\r\n background-color: var(--accent-color);\r\n}\r\n\r\n.delete-account-btn {\r\n background-color: rgb(219, 17, 17);\r\n display: block;\r\n visibility: visible;\r\n}\r\n\r\n.export-btn:hover {\r\n background-color: #b06935;\r\n}\r\n\r\n.delete-account-btn:hover {\r\n background-color: #b60718;\r\n}\r\n\r\n@media screen and (max-width: 768px) {\r\n .button-container {\r\n gap: 10px;\r\n }\r\n\r\n .export-btn,\r\n .delete-account-btn {\r\n padding: 12px 15px;\r\n font-size: 14px;\r\n }\r\n}\r\n\r\n/* Style pour les boutons de la popup */\r\n.popup-buttons {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n/* Style pour les boutons d'action des tableaux */\r\n.button-container {\r\n display: flex;\r\n gap: 15px;\r\n margin: 15px 0;\r\n width: 100%;\r\n}\r\n\r\n/* Style pour le header mobile */\r\n.mobile-nav {\r\n display: none;\r\n width: 100%;\r\n padding: 10px;\r\n background-color: #34495e;\r\n overflow-x: auto;\r\n white-space: nowrap;\r\n}\r\n\r\n.mobile-nav ul {\r\n display: flex;\r\n gap: 10px;\r\n margin: 0;\r\n padding: 0;\r\n list-style: none;\r\n}\r\n\r\n/* Media Query pour mobile */\r\n@media screen and (max-width: 768px) {\r\n .parameter-list {\r\n display: flex;\r\n width: 100%;\r\n min-width: 100%;\r\n height: auto;\r\n overflow-x: auto;\r\n background-color: rgb(31, 44, 61);\r\n padding: 10px;\r\n border-right: none;\r\n border-bottom: 2px solid rgb(44, 62, 80);\r\n }\r\n\r\n .mobile-nav {\r\n display: flex; /* Affiche la navigation mobile */\r\n }\r\n\r\n .parameter-header {\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n .parameter-list-ul {\r\n text-align: center;\r\n flex: 1 1 0%;\r\n margin: 0px 5px;\r\n padding: 10px;\r\n white-space: nowrap;\r\n margin-bottom: none;\r\n }\r\n\r\n .parameter-list-ul:hover {\r\n background-color: #34495e;\r\n }\r\n\r\n .container {\r\n flex-direction: column;\r\n }\r\n\r\n .parameter-area {\r\n margin: -5px;\r\n }\r\n}\r\n\r\n/* Style pour le header et la navigation mobile */\r\n.parameter-header {\r\n background-color: #34495e;\r\n padding: 20px 0;\r\n margin: 0;\r\n width: 100%;\r\n}\r\n\r\n.mobile-nav {\r\n display: none; /* Par défaut caché */\r\n width: 100%;\r\n padding: 10px;\r\n background-color: #34495e;\r\n overflow-x: auto;\r\n}\r\n\r\n.mobile-nav ul {\r\n display: flex;\r\n gap: 10px;\r\n margin: 0;\r\n padding: 10px;\r\n list-style: none;\r\n}\r\n\r\n.mobile-nav li {\r\n flex: 0 0 auto;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Ajoutez ces styles pour la bannière dans la popup */\r\n.banner-container {\r\n width: 100%;\r\n margin-bottom: 20px;\r\n}\r\n\r\n.banner-wrapper {\r\n width: 100%;\r\n height: 120px;\r\n overflow: hidden;\r\n position: relative;\r\n border-radius: 10px;\r\n}\r\n\r\n.banner-wrapper img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n}\r\n\r\n/* Mise à jour des styles existants */\r\n.popup-content {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n background: white;\r\n padding: 20px;\r\n border-radius: 10px;\r\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n width: 90%;\r\n max-width: 500px;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n}\r\n\r\n/* Style pour le conteneur de la bannière */\r\n.banner-upload-label {\r\n display: block;\r\n width: auto;\r\n padding: 12px 20px;\r\n background-color: var(--accent-color);\r\n color: white;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n text-align: center;\r\n font-size: 16px;\r\n margin: 10px auto;\r\n max-width: 200px;\r\n}\r\n\r\n/* ---------------------Style pour la popup de contrat--------------------- */\r\n\r\n.contract-popup-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex\r\n;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.contract-popup-content {\r\n background: white;\r\n padding: 30px;\r\n border-radius: 8px;\r\n max-width: 600px;\r\n width: 90%;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n position: relative;\r\n}\r\n\r\n.close-contract-popup {\r\n position: absolute;\r\n top: 15px;\r\n right: 15px;\r\n background: none;\r\n border: none;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n/* Style pour la popup d'alerte */\r\n.alert-popup {\r\n position: fixed;\r\n top: 20px;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n background-color: #f44336;\r\n color: white;\r\n padding: 15px 25px;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 5px rgba(0,0,0,0.2);\r\n z-index: 1000;\r\n display: none;\r\n animation: slideDown 0.3s ease-out;\r\n}\r\n\r\n/* ---------------------Style pour la popup notification--------------------- */\r\n\r\n\r\n.notifications-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.notifications-content {\r\n position: relative;\r\n background: white;\r\n border-radius: 8px;\r\n padding: 24px;\r\n width: 90%;\r\n max-width: 500px;\r\n}\r\n\r\n.close-button {\r\n position: absolute;\r\n top: 15px;\r\n right: 20px;\r\n font-size: 24px;\r\n color: #666;\r\n cursor: pointer;\r\n transition: color 0.2s;\r\n}\r\n\r\n.close-button:hover {\r\n color: #000;\r\n}\r\n\r\n.notifications-title {\r\n padding-right: 30px; /* Pour éviter que le titre ne chevauche le bouton de fermeture */\r\n}\r\n\r\n.notifications-title {\r\n font-size: 24px;\r\n color: #445B6E;\r\n margin-bottom: 20px;\r\n font-weight: 500;\r\n}\r\n\r\n.notifications-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n\r\n.notification-item {\r\n display: flex;\r\n align-items: flex-start;\r\n padding: 12px 0;\r\n border-bottom: 1px solid #eee;\r\n cursor: pointer;\r\n}\r\n\r\n.notification-status {\r\n margin-right: 16px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px; \r\n height: 24px; \r\n}\r\n\r\n.dot-icon, .check-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.notification-content {\r\n flex: 1;\r\n}\r\n\r\n.notification-message {\r\n font-size: 16px;\r\n color: #333;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.notification-date {\r\n font-size: 14px;\r\n color: #666;\r\n}\r\n\r\n.notification-item:hover {\r\n background-color: #f8f9fa;\r\n}\r\n\r\n.notification-item.read {\r\n opacity: 0.7;\r\n}\r\n\r\n.notification-item.unread {\r\n background-color: #fff;\r\n}\r\n\r\n.close-notifications {\r\n position: absolute;\r\n top: 15px;\r\n right: 15px;\r\n border: none;\r\n background: none;\r\n font-size: 24px;\r\n color: #666;\r\n cursor: pointer;\r\n}\r\n\r\n/*-------------------------------------- STYLE ACTION BUTTON ---------------------*/\r\n\r\n.action-buttons-wrapper {\r\n display: flex;\r\n flex-direction: row;\r\n gap: 10px;\r\n justify-content: center;\r\n margin: 20px 0;\r\n}\r\n\r\n.action-button {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0;\r\n border-radius: 8px;\r\n border: none;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n color: white;\r\n height: 40px;\r\n width: 40px;\r\n}\r\n\r\n.confirm-button {\r\n background-color: #4CAF50;\r\n}\r\n\r\n.confirm-button:hover {\r\n background-color: #3d8b40;\r\n transform: translateY(-2px);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n.cancel-button {\r\n background-color: rgb(244, 67, 54);\r\n}\r\n\r\n.cancel-button:hover {\r\n background-color: #d32f2f;\r\n transform: translateY(-2px);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\r\n}\r\n\r\n.banner-image.clickable {\r\n cursor: pointer;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.banner-image.clickable:hover {\r\n opacity: 0.8;\r\n}\r\n\r\n.parameter-list-ul.profile {\r\n position: relative;\r\n overflow: hidden; \r\n max-height: 200px;\r\n margin-bottom: 20px;\r\n}\r\n\r\n\r\n.profile-preview {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.preview-banner {\r\n position: relative;\r\n width: 100%;\r\n height: 120px; \r\n overflow: hidden;\r\n}\r\n\r\n.preview-banner-img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n.preview-info {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n padding: 10px;\r\n gap: 10px;\r\n background: rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.preview-avatar {\r\n width: 45px;\r\n height: 45px;\r\n border-radius: 50%;\r\n border: 2px solid white;\r\n}\r\n\r\n/* ---------------------Style pour le QR code--------------------- */\r\n\r\n.qr-code {\r\n width: 50px;\r\n height: 50px;\r\n cursor: pointer;\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.qr-code:hover {\r\n transform: scale(1.5);\r\n}\r\n\r\n.qr-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.qr-modal-content {\r\n background-color: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n position: relative;\r\n text-align: center;\r\n}\r\n\r\n.close-qr-modal {\r\n position: absolute;\r\n right: 10px;\r\n top: 5px;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n.close-qr-modal:hover {\r\n color: #000;\r\n}\r\n\r\n.qr-code-large {\r\n max-width: 300px;\r\n margin: 10px 0;\r\n}\r\n\r\n.qr-address {\r\n margin-top: 10px;\r\n word-break: break-all;\r\n font-size: 12px;\r\n color: #666;\r\n}\r\n\r\n.pairing-modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.pairing-modal-content {\r\n background-color: white;\r\n padding: 2rem;\r\n border-radius: 8px;\r\n width: 90%;\r\n max-width: 500px;\r\n}\r\n\r\n.pairing-form {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 1rem;\r\n}\r\n\r\n.form-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.5rem;\r\n}\r\n\r\n.form-group label {\r\n font-weight: bold;\r\n}\r\n\r\n.button-group {\r\n display: flex;\r\n gap: 1rem;\r\n justify-content: flex-end;\r\n margin-top: 1rem;\r\n}\r\n\r\n.button-group button {\r\n padding: 0.5rem 1rem;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.confirm-button {\r\n background-color: var(--accent-color);\r\n color: white;\r\n border: none;\r\n}\r\n\r\n.cancel-button {\r\n background-color: #ccc;\r\n border: none;\r\n}\r\n";
class AccountComponent extends HTMLElement {
_callback;
accountElement = null;
constructor() {
super();
console.log("INIT");
this.attachShadow({ mode: "open" });
this.accountElement = this.shadowRoot?.querySelector("account-element") || null;
}
connectedCallback() {
console.log("CALLBACKs");
this.render();
this.fetchData();
if (!customElements.get("account-element")) {
customElements.define("account-element", AccountElement);
}
}
async fetchData() {
{
const service = await Services.getInstance();
await service.getProcesses();
}
}
set callback(fn) {
if (typeof fn === "function") {
this._callback = fn;
} else {
console.error("Callback is not a function");
}
}
get callback() {
return this._callback;
}
render() {
if (this.shadowRoot && !this.shadowRoot.querySelector("account-element")) {
const style = document.createElement("style");
style.textContent = accountCss;
const accountElement = document.createElement("account-element");
this.shadowRoot.appendChild(style);
this.shadowRoot.appendChild(accountElement);
}
}
}
customElements.define("account-component", AccountComponent);
export { AccountComponent };