@appius-fr/apx 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/APX.dev.mjs +672 -21
- package/dist/APX.mjs +1 -1
- package/dist/APX.prod.mjs +1 -1
- package/dist/APX.standalone.js +651 -24
- package/dist/APX.standalone.js.map +1 -1
- package/modules/toast/toast.mjs +671 -20
- package/package.json +1 -1
package/dist/APX.standalone.js
CHANGED
|
@@ -1612,7 +1612,6 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1612
1612
|
/* harmony export */ toast: () => (/* binding */ toast)
|
|
1613
1613
|
/* harmony export */ });
|
|
1614
1614
|
/* harmony import */ var _css_toast_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./css/toast.css */ "./modules/toast/css/toast.css");
|
|
1615
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
1616
1615
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
1617
1616
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
1618
1617
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
@@ -1627,13 +1626,30 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+
|
|
|
1627
1626
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
1628
1627
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
|
1629
1628
|
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
1629
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
1630
1630
|
// Minimal, framework-agnostic ToastManager for APX
|
|
1631
1631
|
// ESM-first, no side effects on import. DOM only when used.
|
|
1632
1632
|
|
|
1633
1633
|
|
|
1634
|
+
/**
|
|
1635
|
+
* @typedef {Object} PositionObject
|
|
1636
|
+
* @property {'sticky'|'relative'|'anchored'} [type]
|
|
1637
|
+
* @property {string} [x]
|
|
1638
|
+
* @property {string} [y]
|
|
1639
|
+
* @property {HTMLElement} [element]
|
|
1640
|
+
* @property {'top'|'right'|'bottom'|'left'|'above'|'below'|'before'|'after'} [placement]
|
|
1641
|
+
* @property {string} [gap]
|
|
1642
|
+
* @property {boolean} [useNativeCSS]
|
|
1643
|
+
*/
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* @typedef {string|PositionObject} Position
|
|
1647
|
+
*/
|
|
1648
|
+
|
|
1634
1649
|
/**
|
|
1635
1650
|
* @typedef {Object} ToastConfig
|
|
1636
|
-
* @property {
|
|
1651
|
+
* @property {Position} [position]
|
|
1652
|
+
* @property {'up'|'down'|'auto'} [flow] Flow direction for stacking toasts. 'auto' determines based on position. Default: 'auto'
|
|
1637
1653
|
* @property {number} [maxToasts]
|
|
1638
1654
|
* @property {number} [defaultDurationMs]
|
|
1639
1655
|
* @property {number} [zIndex]
|
|
@@ -1642,6 +1658,7 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
|
|
|
1642
1658
|
* @property {boolean} [dedupe]
|
|
1643
1659
|
* @property {string} [containerClass]
|
|
1644
1660
|
* @property {number} [offset]
|
|
1661
|
+
* @property {string} [id]
|
|
1645
1662
|
*/
|
|
1646
1663
|
|
|
1647
1664
|
/**
|
|
@@ -1654,6 +1671,8 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
|
|
|
1654
1671
|
* @property {(ref: ToastRef, ev: MouseEvent) => void} [onClick]
|
|
1655
1672
|
* @property {(ref: ToastRef, reason: 'timeout'|'close'|'api'|'overflow') => void} [onClose]
|
|
1656
1673
|
* @property {string} [className]
|
|
1674
|
+
* @property {Position} [position]
|
|
1675
|
+
* @property {'up'|'down'|'auto'} [flow] Flow direction for stacking toasts. 'auto' determines based on position. Default: 'auto'
|
|
1657
1676
|
*/
|
|
1658
1677
|
|
|
1659
1678
|
/**
|
|
@@ -1668,6 +1687,40 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
|
|
|
1668
1687
|
*/
|
|
1669
1688
|
|
|
1670
1689
|
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
1690
|
+
|
|
1691
|
+
// Shared container cache: Map<position, HTMLElement>
|
|
1692
|
+
// Containers are shared between managers with the same position
|
|
1693
|
+
var _containerCache = new Map();
|
|
1694
|
+
|
|
1695
|
+
// Garbage collection: cleanup empty unmanaged containers after a delay
|
|
1696
|
+
var GC_DELAY_MS = 20000; // 20 seconds
|
|
1697
|
+
var _gcTimeoutId = null;
|
|
1698
|
+
|
|
1699
|
+
// Wrapper for all toast containers (keeps DOM clean)
|
|
1700
|
+
var _toastWrapper = null;
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* Get or create the toast containers wrapper
|
|
1704
|
+
* @returns {HTMLElement|null}
|
|
1705
|
+
*/
|
|
1706
|
+
function getToastWrapper() {
|
|
1707
|
+
if (!isBrowser) return null;
|
|
1708
|
+
if (!_toastWrapper) {
|
|
1709
|
+
_toastWrapper = document.querySelector('.APX-toast-wrapper');
|
|
1710
|
+
if (!_toastWrapper) {
|
|
1711
|
+
_toastWrapper = createEl('div', 'APX-toast-wrapper');
|
|
1712
|
+
_toastWrapper.style.position = 'fixed';
|
|
1713
|
+
_toastWrapper.style.top = '0';
|
|
1714
|
+
_toastWrapper.style.left = '0';
|
|
1715
|
+
_toastWrapper.style.width = '0';
|
|
1716
|
+
_toastWrapper.style.height = '0';
|
|
1717
|
+
_toastWrapper.style.pointerEvents = 'none';
|
|
1718
|
+
_toastWrapper.style.zIndex = '10000'; // Below containers but above most content
|
|
1719
|
+
document.body.appendChild(_toastWrapper);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
return _toastWrapper;
|
|
1723
|
+
}
|
|
1671
1724
|
var DEFAULT_CONFIG = {
|
|
1672
1725
|
position: 'bottom-right',
|
|
1673
1726
|
maxToasts: 5,
|
|
@@ -1693,6 +1746,194 @@ function createEl(tag, classNames) {
|
|
|
1693
1746
|
return el;
|
|
1694
1747
|
}
|
|
1695
1748
|
|
|
1749
|
+
/**
|
|
1750
|
+
* Normalize placement synonyms to CSS values
|
|
1751
|
+
* @param {string} placement
|
|
1752
|
+
* @returns {'top'|'right'|'bottom'|'left'}
|
|
1753
|
+
*/
|
|
1754
|
+
function normalizePlacement(placement) {
|
|
1755
|
+
var synonyms = {
|
|
1756
|
+
'above': 'top',
|
|
1757
|
+
'below': 'bottom',
|
|
1758
|
+
'before': 'left',
|
|
1759
|
+
'after': 'right'
|
|
1760
|
+
};
|
|
1761
|
+
return synonyms[placement] || placement;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
/**
|
|
1765
|
+
* Determine flow direction based on position
|
|
1766
|
+
* @param {Position} position
|
|
1767
|
+
* @returns {'up'|'down'}
|
|
1768
|
+
*/
|
|
1769
|
+
function determineFlow(position) {
|
|
1770
|
+
if (typeof position === 'string') {
|
|
1771
|
+
// String positions: top = up, bottom = down
|
|
1772
|
+
if (position.includes('top')) return 'up';
|
|
1773
|
+
if (position.includes('bottom')) return 'down';
|
|
1774
|
+
return 'down'; // default
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
if (_typeof(position) === 'object' && position !== null) {
|
|
1778
|
+
var type = position.type || (position.x || position.y ? 'sticky' : null);
|
|
1779
|
+
if (type === 'sticky' || !type && (position.x || position.y)) {
|
|
1780
|
+
// Sticky: determine based on y coordinate
|
|
1781
|
+
if (position.y !== undefined) {
|
|
1782
|
+
// If y starts with '-' or is a small value, likely top (up)
|
|
1783
|
+
// If y is a large value or percentage, likely bottom (down)
|
|
1784
|
+
if (position.y.startsWith('-')) {
|
|
1785
|
+
// Negative = from bottom, so flow down
|
|
1786
|
+
return 'down';
|
|
1787
|
+
}
|
|
1788
|
+
var num = parseFloat(position.y);
|
|
1789
|
+
if (!isNaN(num)) {
|
|
1790
|
+
// If y < 50% of viewport height, likely top (up)
|
|
1791
|
+
// Otherwise likely bottom (down)
|
|
1792
|
+
if (position.y.includes('%')) {
|
|
1793
|
+
return num < 50 ? 'up' : 'down';
|
|
1794
|
+
}
|
|
1795
|
+
// For px values, assume < 400px is top (up)
|
|
1796
|
+
return num < 400 ? 'up' : 'down';
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return 'down'; // default
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
if (type === 'anchored' && position.placement) {
|
|
1803
|
+
// Anchored: placement determines flow
|
|
1804
|
+
var placement = normalizePlacement(position.placement);
|
|
1805
|
+
if (placement === 'top' || placement === 'above') return 'up';
|
|
1806
|
+
if (placement === 'bottom' || placement === 'below') return 'down';
|
|
1807
|
+
// For left/right, default to down
|
|
1808
|
+
return 'down';
|
|
1809
|
+
}
|
|
1810
|
+
if (type === 'relative') {
|
|
1811
|
+
// Relative: determine based on y offset
|
|
1812
|
+
if (position.y !== undefined) {
|
|
1813
|
+
var _num = parseFloat(position.y);
|
|
1814
|
+
if (!isNaN(_num)) {
|
|
1815
|
+
// Negative y = above element = flow up
|
|
1816
|
+
// Positive y = below element = flow down
|
|
1817
|
+
return _num < 0 ? 'up' : 'down';
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
return 'down'; // default
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
return 'down'; // default
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Garbage collection: remove empty unmanaged containers
|
|
1829
|
+
*/
|
|
1830
|
+
function cleanupEmptyContainers() {
|
|
1831
|
+
if (!isBrowser) return;
|
|
1832
|
+
var containers = document.querySelectorAll('.APX-toast-container:not([data-apx-toast-managed="true"])');
|
|
1833
|
+
containers.forEach(function (container) {
|
|
1834
|
+
// Check if container is empty (no toasts)
|
|
1835
|
+
if (container.children.length === 0) {
|
|
1836
|
+
var positionKey = container.getAttribute('data-apx-toast-position');
|
|
1837
|
+
if (positionKey) {
|
|
1838
|
+
_containerCache["delete"](positionKey);
|
|
1839
|
+
}
|
|
1840
|
+
container.remove();
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
// Clean up wrapper if it's empty (all containers removed)
|
|
1845
|
+
if (_toastWrapper && _toastWrapper.children.length === 0) {
|
|
1846
|
+
_toastWrapper.remove();
|
|
1847
|
+
_toastWrapper = null;
|
|
1848
|
+
}
|
|
1849
|
+
_gcTimeoutId = null;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* Schedule garbage collection for empty unmanaged containers
|
|
1854
|
+
*/
|
|
1855
|
+
function scheduleGarbageCollection() {
|
|
1856
|
+
if (_gcTimeoutId) {
|
|
1857
|
+
clearTimeout(_gcTimeoutId);
|
|
1858
|
+
}
|
|
1859
|
+
_gcTimeoutId = setTimeout(cleanupEmptyContainers, GC_DELAY_MS);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* Find all scrollable parent elements
|
|
1864
|
+
* @param {HTMLElement} element
|
|
1865
|
+
* @returns {HTMLElement[]}
|
|
1866
|
+
*/
|
|
1867
|
+
function findScrollableParents(element) {
|
|
1868
|
+
var scrollables = [];
|
|
1869
|
+
var current = element.parentElement;
|
|
1870
|
+
while (current && current !== document.body && current !== document.documentElement) {
|
|
1871
|
+
var style = window.getComputedStyle(current);
|
|
1872
|
+
var overflow = style.overflow + style.overflowY + style.overflowX;
|
|
1873
|
+
if (overflow.includes('scroll') || overflow.includes('auto')) {
|
|
1874
|
+
scrollables.push(current);
|
|
1875
|
+
}
|
|
1876
|
+
current = current.parentElement;
|
|
1877
|
+
}
|
|
1878
|
+
return scrollables;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* Hash an element to create a unique identifier
|
|
1883
|
+
* @param {HTMLElement} el
|
|
1884
|
+
* @returns {string}
|
|
1885
|
+
*/
|
|
1886
|
+
function hashElement(el) {
|
|
1887
|
+
var rect = el.getBoundingClientRect();
|
|
1888
|
+
var str = "".concat(el.tagName, "_").concat(rect.left, "_").concat(rect.top, "_").concat(rect.width, "_").concat(rect.height);
|
|
1889
|
+
var hash = 0;
|
|
1890
|
+
for (var i = 0; i < str.length; i++) {
|
|
1891
|
+
var _char = str.charCodeAt(i);
|
|
1892
|
+
hash = (hash << 5) - hash + _char;
|
|
1893
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
return Math.abs(hash).toString(36);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
/**
|
|
1900
|
+
* Serialize position options into a unique key
|
|
1901
|
+
* @param {Position} position
|
|
1902
|
+
* @returns {string}
|
|
1903
|
+
*/
|
|
1904
|
+
function serializePosition(position) {
|
|
1905
|
+
if (typeof position === 'string') {
|
|
1906
|
+
return "s:".concat(position);
|
|
1907
|
+
}
|
|
1908
|
+
if (_typeof(position) === 'object' && position !== null) {
|
|
1909
|
+
var parts = [];
|
|
1910
|
+
|
|
1911
|
+
// Type (default: sticky if x/y provided)
|
|
1912
|
+
var type = position.type || (position.x || position.y ? 'sticky' : null);
|
|
1913
|
+
if (type) parts.push("t:".concat(type));
|
|
1914
|
+
|
|
1915
|
+
// Coordinates
|
|
1916
|
+
if (position.x !== undefined) parts.push("x:".concat(position.x));
|
|
1917
|
+
if (position.y !== undefined) parts.push("y:".concat(position.y));
|
|
1918
|
+
|
|
1919
|
+
// For relative/anchored: use element ID or hash
|
|
1920
|
+
if (position.element) {
|
|
1921
|
+
var _position$element$dat;
|
|
1922
|
+
var elementId = position.element.id || ((_position$element$dat = position.element.dataset) === null || _position$element$dat === void 0 ? void 0 : _position$element$dat.apxToastAnchorId) || "el_".concat(hashElement(position.element));
|
|
1923
|
+
parts.push("el:".concat(elementId));
|
|
1924
|
+
}
|
|
1925
|
+
if (position.placement) {
|
|
1926
|
+
// Normalize placement for serialization (use CSS values)
|
|
1927
|
+
var normalized = normalizePlacement(position.placement);
|
|
1928
|
+
parts.push("p:".concat(normalized));
|
|
1929
|
+
}
|
|
1930
|
+
if (position.gap !== undefined) parts.push("g:".concat(position.gap));
|
|
1931
|
+
if (position.useNativeCSS) parts.push("native:true");
|
|
1932
|
+
return "o:".concat(parts.join('|'));
|
|
1933
|
+
}
|
|
1934
|
+
return 's:bottom-right';
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1696
1937
|
/**
|
|
1697
1938
|
* ToastManager class
|
|
1698
1939
|
*/
|
|
@@ -1747,9 +1988,17 @@ var ToastManager = /*#__PURE__*/function () {
|
|
|
1747
1988
|
_ref.update(options);
|
|
1748
1989
|
return _ref;
|
|
1749
1990
|
}
|
|
1991
|
+
|
|
1992
|
+
// Determine position and flow for this toast (options take precedence over config)
|
|
1993
|
+
var position = options.position || this.config.position || 'bottom-right';
|
|
1994
|
+
var flow = options.flow !== undefined ? options.flow : this.config.flow !== undefined ? this.config.flow : 'auto';
|
|
1995
|
+
var finalFlow = flow === 'auto' ? determineFlow(position) : flow;
|
|
1996
|
+
|
|
1997
|
+
// Ensure default container is set (for backward compatibility)
|
|
1750
1998
|
this.ensureContainer();
|
|
1751
1999
|
var toastEl = createEl('div', "APX-toast APX-toast--".concat(options.type));
|
|
1752
2000
|
toastEl.setAttribute('role', 'status');
|
|
2001
|
+
// Priority: options.id > config.id > auto-generated
|
|
1753
2002
|
var toastId = options.id || "t_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 8));
|
|
1754
2003
|
toastEl.dataset.toastId = toastId;
|
|
1755
2004
|
if (options.className) toastEl.className += " ".concat(options.className);
|
|
@@ -1767,7 +2016,172 @@ var ToastManager = /*#__PURE__*/function () {
|
|
|
1767
2016
|
closeBtn.type = 'button';
|
|
1768
2017
|
toastEl.appendChild(closeBtn);
|
|
1769
2018
|
}
|
|
1770
|
-
|
|
2019
|
+
|
|
2020
|
+
// Get or create container for this specific position
|
|
2021
|
+
var container = null;
|
|
2022
|
+
var positionUpdateFn = null;
|
|
2023
|
+
var positionCleanup = null;
|
|
2024
|
+
var originalElementStyle = null;
|
|
2025
|
+
if (position && _typeof(position) === 'object' && position !== null) {
|
|
2026
|
+
var type = position.type || (position.x || position.y ? 'sticky' : null);
|
|
2027
|
+
if (position.useNativeCSS && (type === 'relative' || type === 'anchored') && position.element) {
|
|
2028
|
+
// useNativeCSS: true - create container in target element using native CSS
|
|
2029
|
+
var targetEl = position.element;
|
|
2030
|
+
originalElementStyle = targetEl.style.position;
|
|
2031
|
+
targetEl.style.position = 'relative';
|
|
2032
|
+
|
|
2033
|
+
// Create a container for stacking toasts (reuse if exists)
|
|
2034
|
+
var positionKey = serializePosition(position);
|
|
2035
|
+
var nativeContainer = targetEl.querySelector("[data-apx-toast-position=\"".concat(positionKey, "\"]"));
|
|
2036
|
+
if (!nativeContainer) {
|
|
2037
|
+
nativeContainer = createEl('div', 'APX-toast-container APX-toast-container-native');
|
|
2038
|
+
nativeContainer.setAttribute('data-apx-toast-position', positionKey);
|
|
2039
|
+
nativeContainer.style.position = 'absolute';
|
|
2040
|
+
nativeContainer.style.zIndex = String(this.config.zIndex);
|
|
2041
|
+
nativeContainer.style.gap = "".concat(this.config.gap, "px");
|
|
2042
|
+
nativeContainer.setAttribute('aria-live', String(this.config.ariaLive));
|
|
2043
|
+
nativeContainer.style.flexDirection = finalFlow === 'up' ? 'column-reverse' : 'column';
|
|
2044
|
+
|
|
2045
|
+
// Apply positioning to container
|
|
2046
|
+
if (type === 'relative') {
|
|
2047
|
+
// Relative: use x/y directly
|
|
2048
|
+
if (position.x !== undefined) {
|
|
2049
|
+
if (position.x.startsWith('-')) {
|
|
2050
|
+
nativeContainer.style.right = position.x.substring(1);
|
|
2051
|
+
} else {
|
|
2052
|
+
nativeContainer.style.left = position.x;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (position.y !== undefined) {
|
|
2056
|
+
if (position.y.startsWith('-')) {
|
|
2057
|
+
nativeContainer.style.bottom = position.y.substring(1);
|
|
2058
|
+
} else {
|
|
2059
|
+
nativeContainer.style.top = position.y;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
} else if (type === 'anchored') {
|
|
2063
|
+
// Anchored: position outside the element using 100% (element size) + gap
|
|
2064
|
+
var placement = normalizePlacement(position.placement);
|
|
2065
|
+
var gap = position.gap || '1em';
|
|
2066
|
+
switch (placement) {
|
|
2067
|
+
case 'top':
|
|
2068
|
+
nativeContainer.style.bottom = "calc(100% + ".concat(gap, ")");
|
|
2069
|
+
nativeContainer.style.left = '0';
|
|
2070
|
+
break;
|
|
2071
|
+
case 'bottom':
|
|
2072
|
+
nativeContainer.style.top = "calc(100% + ".concat(gap, ")");
|
|
2073
|
+
nativeContainer.style.left = '0';
|
|
2074
|
+
break;
|
|
2075
|
+
case 'left':
|
|
2076
|
+
nativeContainer.style.right = "calc(100% + ".concat(gap, ")");
|
|
2077
|
+
nativeContainer.style.top = '0';
|
|
2078
|
+
break;
|
|
2079
|
+
case 'right':
|
|
2080
|
+
nativeContainer.style.left = "calc(100% + ".concat(gap, ")");
|
|
2081
|
+
nativeContainer.style.top = '0';
|
|
2082
|
+
break;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
targetEl.appendChild(nativeContainer);
|
|
2086
|
+
} else {
|
|
2087
|
+
// Update flow direction if container exists
|
|
2088
|
+
nativeContainer.style.flexDirection = finalFlow === 'up' ? 'column-reverse' : 'column';
|
|
2089
|
+
}
|
|
2090
|
+
container = nativeContainer;
|
|
2091
|
+
positionCleanup = function positionCleanup() {
|
|
2092
|
+
if (targetEl && targetEl.parentElement) {
|
|
2093
|
+
targetEl.style.position = originalElementStyle || '';
|
|
2094
|
+
// Remove native container if empty
|
|
2095
|
+
if (nativeContainer && nativeContainer.children.length === 0) {
|
|
2096
|
+
var _positionKey = nativeContainer.getAttribute('data-apx-toast-position');
|
|
2097
|
+
if (_positionKey) {
|
|
2098
|
+
_containerCache["delete"](_positionKey);
|
|
2099
|
+
}
|
|
2100
|
+
nativeContainer.remove();
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
} else {
|
|
2105
|
+
// Default: get or create container for this position
|
|
2106
|
+
container = this.getContainerForPosition(position, finalFlow);
|
|
2107
|
+
var updateContainerPosition = function updateContainerPosition() {
|
|
2108
|
+
var styles = _this.calculatePosition(position, container);
|
|
2109
|
+
Object.assign(container.style, styles);
|
|
2110
|
+
};
|
|
2111
|
+
updateContainerPosition();
|
|
2112
|
+
|
|
2113
|
+
// For relative/anchored, listen to scroll/resize
|
|
2114
|
+
if ((type === 'relative' || type === 'anchored') && position.element) {
|
|
2115
|
+
var rafId = null;
|
|
2116
|
+
var lastUpdate = 0;
|
|
2117
|
+
var throttleMs = 16; // ~60fps
|
|
2118
|
+
|
|
2119
|
+
var throttledUpdate = function throttledUpdate() {
|
|
2120
|
+
var now = Date.now();
|
|
2121
|
+
if (now - lastUpdate < throttleMs) {
|
|
2122
|
+
if (rafId) cancelAnimationFrame(rafId);
|
|
2123
|
+
rafId = requestAnimationFrame(function () {
|
|
2124
|
+
updateContainerPosition();
|
|
2125
|
+
lastUpdate = Date.now();
|
|
2126
|
+
});
|
|
2127
|
+
} else {
|
|
2128
|
+
updateContainerPosition();
|
|
2129
|
+
lastUpdate = now;
|
|
2130
|
+
}
|
|
2131
|
+
};
|
|
2132
|
+
|
|
2133
|
+
// Find all scrollable parents
|
|
2134
|
+
var scrollableParents = findScrollableParents(position.element);
|
|
2135
|
+
|
|
2136
|
+
// Listen to scroll on window and all scrollable parents
|
|
2137
|
+
window.addEventListener('scroll', throttledUpdate, {
|
|
2138
|
+
passive: true
|
|
2139
|
+
});
|
|
2140
|
+
window.addEventListener('resize', throttledUpdate, {
|
|
2141
|
+
passive: true
|
|
2142
|
+
});
|
|
2143
|
+
|
|
2144
|
+
// Listen to scroll on all scrollable parent elements
|
|
2145
|
+
scrollableParents.forEach(function (parent) {
|
|
2146
|
+
parent.addEventListener('scroll', throttledUpdate, {
|
|
2147
|
+
passive: true
|
|
2148
|
+
});
|
|
2149
|
+
});
|
|
2150
|
+
|
|
2151
|
+
// Watch for element removal
|
|
2152
|
+
var observer = new MutationObserver(function () {
|
|
2153
|
+
if (!position.element.parentElement) {
|
|
2154
|
+
ref.close('api');
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
observer.observe(document.body, {
|
|
2158
|
+
childList: true,
|
|
2159
|
+
subtree: true
|
|
2160
|
+
});
|
|
2161
|
+
positionUpdateFn = updateContainerPosition;
|
|
2162
|
+
positionCleanup = function positionCleanup() {
|
|
2163
|
+
window.removeEventListener('scroll', throttledUpdate);
|
|
2164
|
+
window.removeEventListener('resize', throttledUpdate);
|
|
2165
|
+
scrollableParents.forEach(function (parent) {
|
|
2166
|
+
parent.removeEventListener('scroll', throttledUpdate);
|
|
2167
|
+
});
|
|
2168
|
+
observer.disconnect();
|
|
2169
|
+
if (rafId) cancelAnimationFrame(rafId);
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
} else {
|
|
2174
|
+
// String position - get or create container for this position
|
|
2175
|
+
container = this.getContainerForPosition(position, finalFlow);
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// Append toast to the appropriate container
|
|
2179
|
+
if (container) {
|
|
2180
|
+
container.appendChild(toastEl);
|
|
2181
|
+
} else {
|
|
2182
|
+
// Fallback to default container
|
|
2183
|
+
this.container.appendChild(toastEl);
|
|
2184
|
+
}
|
|
1771
2185
|
|
|
1772
2186
|
// Enter animation
|
|
1773
2187
|
toastEl.classList.add('APX-toast--enter');
|
|
@@ -1864,6 +2278,13 @@ var ToastManager = /*#__PURE__*/function () {
|
|
|
1864
2278
|
var cleanup = function cleanup(reason) {
|
|
1865
2279
|
if (!toastEl) return;
|
|
1866
2280
|
pauseTimer();
|
|
2281
|
+
|
|
2282
|
+
// Cleanup position listeners and restore styles
|
|
2283
|
+
if (positionCleanup) {
|
|
2284
|
+
positionCleanup();
|
|
2285
|
+
positionCleanup = null;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
1867
2288
|
// If overflow, remove immediately to enforce hard cap
|
|
1868
2289
|
if (reason === 'overflow') {
|
|
1869
2290
|
if (toastEl.parentElement) toastEl.parentElement.removeChild(toastEl);
|
|
@@ -1885,6 +2306,9 @@ var ToastManager = /*#__PURE__*/function () {
|
|
|
1885
2306
|
var idx = _this.open.indexOf(ref);
|
|
1886
2307
|
if (idx >= 0) _this.open.splice(idx, 1);
|
|
1887
2308
|
_this.idToRef["delete"](toastId);
|
|
2309
|
+
|
|
2310
|
+
// Schedule garbage collection for unmanaged containers
|
|
2311
|
+
scheduleGarbageCollection();
|
|
1888
2312
|
finish(reason);
|
|
1889
2313
|
};
|
|
1890
2314
|
toastEl.addEventListener('transitionend', removeEl);
|
|
@@ -1984,42 +2408,245 @@ var ToastManager = /*#__PURE__*/function () {
|
|
|
1984
2408
|
if (!o.type) o.type = 'info';
|
|
1985
2409
|
if (typeof o.dismissible !== 'boolean') o.dismissible = true;
|
|
1986
2410
|
if (typeof o.durationMs !== 'number') o.durationMs = this.config.defaultDurationMs;
|
|
2411
|
+
// Use id from options if provided, otherwise use id from config, otherwise undefined (will be auto-generated)
|
|
2412
|
+
if (!o.id && this.config.id) o.id = this.config.id;
|
|
1987
2413
|
return o;
|
|
1988
2414
|
}
|
|
2415
|
+
|
|
2416
|
+
/**
|
|
2417
|
+
* Find or create a container for a specific position
|
|
2418
|
+
* @param {Position} position
|
|
2419
|
+
* @param {'up'|'down'} [flow] Flow direction (already determined, not 'auto')
|
|
2420
|
+
* @param {boolean} [managed] Whether this container is managed by a manager (default: false)
|
|
2421
|
+
* @returns {HTMLElement|null}
|
|
2422
|
+
*/
|
|
2423
|
+
}, {
|
|
2424
|
+
key: "getContainerForPosition",
|
|
2425
|
+
value: function getContainerForPosition(position, flow) {
|
|
2426
|
+
var managed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
2427
|
+
if (!isBrowser) return null;
|
|
2428
|
+
var positionKey = serializePosition(position);
|
|
2429
|
+
|
|
2430
|
+
// Flow should already be determined ('up' or 'down'), but fallback to auto if needed
|
|
2431
|
+
var finalFlow = flow && flow !== 'auto' ? flow : determineFlow(position);
|
|
2432
|
+
|
|
2433
|
+
// 1. Check memory cache
|
|
2434
|
+
var c = _containerCache.get(positionKey);
|
|
2435
|
+
|
|
2436
|
+
// 2. If not in cache, search in DOM by data attribute
|
|
2437
|
+
if (!c && isBrowser) {
|
|
2438
|
+
c = document.querySelector("[data-apx-toast-position=\"".concat(positionKey, "\"]"));
|
|
2439
|
+
if (c) {
|
|
2440
|
+
_containerCache.set(positionKey, c);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// 3. If still not found, create new container
|
|
2445
|
+
if (!c) {
|
|
2446
|
+
c = createEl('div', 'APX-toast-container');
|
|
2447
|
+
c.setAttribute('data-apx-toast-position', positionKey);
|
|
2448
|
+
|
|
2449
|
+
// Mark as managed if created by a manager
|
|
2450
|
+
if (managed) {
|
|
2451
|
+
c.setAttribute('data-apx-toast-managed', 'true');
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// Determine position class (for CSS)
|
|
2455
|
+
var posClass = typeof position === 'string' ? position : position.type || 'bottom-right';
|
|
2456
|
+
c.classList.add("APX-toast-container--".concat(posClass));
|
|
2457
|
+
c.setAttribute('aria-live', String(this.config.ariaLive));
|
|
2458
|
+
c.style.zIndex = String(this.config.zIndex);
|
|
2459
|
+
c.style.gap = "".concat(this.config.gap, "px");
|
|
2460
|
+
|
|
2461
|
+
// Apply flow direction
|
|
2462
|
+
c.style.flexDirection = finalFlow === 'up' ? 'column-reverse' : 'column';
|
|
2463
|
+
|
|
2464
|
+
// Apply position styles if object position
|
|
2465
|
+
if (_typeof(position) === 'object' && position !== null) {
|
|
2466
|
+
c.style.position = 'fixed';
|
|
2467
|
+
var styles = this.calculatePosition(position, c);
|
|
2468
|
+
Object.assign(c.style, styles);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
// Append to wrapper for clean DOM organization
|
|
2472
|
+
var wrapper = getToastWrapper();
|
|
2473
|
+
if (wrapper) {
|
|
2474
|
+
wrapper.appendChild(c);
|
|
2475
|
+
} else {
|
|
2476
|
+
document.body.appendChild(c);
|
|
2477
|
+
}
|
|
2478
|
+
_containerCache.set(positionKey, c);
|
|
2479
|
+
} else {
|
|
2480
|
+
// Update flow direction if container exists (may be shared)
|
|
2481
|
+
c.style.flexDirection = finalFlow === 'up' ? 'column-reverse' : 'column';
|
|
2482
|
+
|
|
2483
|
+
// If container exists and is now managed, mark it
|
|
2484
|
+
if (managed && !c.hasAttribute('data-apx-toast-managed')) {
|
|
2485
|
+
c.setAttribute('data-apx-toast-managed', 'true');
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
return c;
|
|
2489
|
+
}
|
|
1989
2490
|
}, {
|
|
1990
2491
|
key: "ensureContainer",
|
|
1991
2492
|
value: function ensureContainer() {
|
|
1992
2493
|
if (this.container || !isBrowser) return;
|
|
1993
|
-
var
|
|
1994
|
-
var
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
c.style.zIndex = String(this.config.zIndex);
|
|
1998
|
-
c.style.gap = "".concat(this.config.gap, "px");
|
|
1999
|
-
if (this.config.offset) {
|
|
2000
|
-
var offset = "".concat(this.config.offset, "px");
|
|
2001
|
-
if (pos.includes('bottom')) c.style.bottom = offset;else c.style.top = offset;
|
|
2002
|
-
if (pos.includes('right')) c.style.right = offset;else c.style.left = offset;
|
|
2003
|
-
}
|
|
2004
|
-
c.setAttribute('aria-live', String(this.config.ariaLive));
|
|
2005
|
-
document.body.appendChild(c);
|
|
2006
|
-
this.container = c;
|
|
2494
|
+
var position = this.config.position || 'bottom-right';
|
|
2495
|
+
var flow = this.config.flow !== undefined ? this.config.flow : 'auto';
|
|
2496
|
+
// Containers created by ensureContainer are managed
|
|
2497
|
+
this.container = this.getContainerForPosition(position, flow, true);
|
|
2007
2498
|
this.applyContainerConfig();
|
|
2008
2499
|
}
|
|
2009
2500
|
}, {
|
|
2010
2501
|
key: "applyContainerConfig",
|
|
2011
2502
|
value: function applyContainerConfig() {
|
|
2012
|
-
var
|
|
2503
|
+
var _this2 = this;
|
|
2013
2504
|
if (!this.container) return;
|
|
2505
|
+
var position = this.config.position || 'bottom-right';
|
|
2506
|
+
var pos = typeof position === 'string' ? position : position.type || 'bottom-right';
|
|
2507
|
+
|
|
2508
|
+
// Apply styles (these may be overridden by other managers sharing the container)
|
|
2014
2509
|
this.container.style.zIndex = String(this.config.zIndex);
|
|
2015
2510
|
this.container.style.gap = "".concat(this.config.gap, "px");
|
|
2016
2511
|
this.container.setAttribute('aria-live', String(this.config.ariaLive));
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2512
|
+
|
|
2513
|
+
// Update position class (only for string positions)
|
|
2514
|
+
if (typeof position === 'string') {
|
|
2515
|
+
var _this$container$class;
|
|
2516
|
+
var posClasses = ['bottom-right', 'bottom-left', 'top-right', 'top-left'].map(function (p) {
|
|
2517
|
+
return "APX-toast-container--".concat(p);
|
|
2518
|
+
});
|
|
2519
|
+
(_this$container$class = this.container.classList).remove.apply(_this$container$class, _toConsumableArray(posClasses));
|
|
2520
|
+
this.container.classList.add("APX-toast-container--".concat(pos));
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// Apply container class if specified
|
|
2524
|
+
if (this.config.containerClass) {
|
|
2525
|
+
this.config.containerClass.split(' ').filter(Boolean).forEach(function (cls) {
|
|
2526
|
+
_this2.container.classList.add(cls);
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
// Apply offset (only for string positions)
|
|
2531
|
+
if (typeof position === 'string') {
|
|
2532
|
+
if (this.config.offset) {
|
|
2533
|
+
var offset = "".concat(this.config.offset, "px");
|
|
2534
|
+
if (pos.includes('bottom')) this.container.style.bottom = offset;else this.container.style.top = offset;
|
|
2535
|
+
if (pos.includes('right')) this.container.style.right = offset;else this.container.style.left = offset;
|
|
2536
|
+
} else {
|
|
2537
|
+
// Reset offset if not specified
|
|
2538
|
+
if (pos.includes('bottom')) this.container.style.bottom = '';else this.container.style.top = '';
|
|
2539
|
+
if (pos.includes('right')) this.container.style.right = '';else this.container.style.left = '';
|
|
2540
|
+
}
|
|
2541
|
+
} else if (_typeof(position) === 'object' && position !== null) {
|
|
2542
|
+
// For object positions, ensure container has position: fixed
|
|
2543
|
+
this.container.style.position = 'fixed';
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
/**
|
|
2548
|
+
* Calculate position for a container based on position config
|
|
2549
|
+
* @param {Position} position
|
|
2550
|
+
* @param {HTMLElement} containerEl
|
|
2551
|
+
* @returns {{left?: string, top?: string, right?: string, bottom?: string}}
|
|
2552
|
+
*/
|
|
2553
|
+
}, {
|
|
2554
|
+
key: "calculatePosition",
|
|
2555
|
+
value: function calculatePosition(position, containerEl) {
|
|
2556
|
+
if (typeof position === 'string') {
|
|
2557
|
+
// String positions are handled by container CSS
|
|
2558
|
+
return {};
|
|
2559
|
+
}
|
|
2560
|
+
if (_typeof(position) === 'object' && position !== null) {
|
|
2561
|
+
var type = position.type || (position.x || position.y ? 'sticky' : null);
|
|
2562
|
+
if (type === 'sticky' || !type && (position.x || position.y)) {
|
|
2563
|
+
// Sticky: fixed position relative to viewport
|
|
2564
|
+
var styles = {};
|
|
2565
|
+
if (position.x !== undefined) {
|
|
2566
|
+
if (position.x.startsWith('-')) {
|
|
2567
|
+
styles.right = position.x.substring(1);
|
|
2568
|
+
} else {
|
|
2569
|
+
styles.left = position.x;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
if (position.y !== undefined) {
|
|
2573
|
+
if (position.y.startsWith('-')) {
|
|
2574
|
+
styles.bottom = position.y.substring(1);
|
|
2575
|
+
} else {
|
|
2576
|
+
styles.top = position.y;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return styles;
|
|
2580
|
+
}
|
|
2581
|
+
if (type === 'relative' && position.element) {
|
|
2582
|
+
// Relative: position relative to element with x/y offset
|
|
2583
|
+
// Use fixed positioning, so getBoundingClientRect() is relative to viewport
|
|
2584
|
+
var rect = position.element.getBoundingClientRect();
|
|
2585
|
+
|
|
2586
|
+
// Parse x/y offsets (can be px, em, etc.)
|
|
2587
|
+
var parseOffset = function parseOffset(val) {
|
|
2588
|
+
if (!val) return 0;
|
|
2589
|
+
var num = parseFloat(val);
|
|
2590
|
+
if (val.includes('em')) {
|
|
2591
|
+
// Convert em to px (approximate: 1em = 16px)
|
|
2592
|
+
return num * 16;
|
|
2593
|
+
}
|
|
2594
|
+
return num;
|
|
2595
|
+
};
|
|
2596
|
+
var offsetX = parseOffset(position.x || '0');
|
|
2597
|
+
var offsetY = parseOffset(position.y || '0');
|
|
2598
|
+
var _styles = {
|
|
2599
|
+
left: "".concat(rect.left + offsetX, "px"),
|
|
2600
|
+
top: "".concat(rect.top + offsetY, "px")
|
|
2601
|
+
};
|
|
2602
|
+
return _styles;
|
|
2603
|
+
}
|
|
2604
|
+
if (type === 'anchored' && position.element) {
|
|
2605
|
+
// Anchored: position relative to element with placement
|
|
2606
|
+
var _rect = position.element.getBoundingClientRect();
|
|
2607
|
+
var gap = position.gap || '1em';
|
|
2608
|
+
|
|
2609
|
+
// Parse gap (can be px, em, etc.)
|
|
2610
|
+
var parseGap = function parseGap(val) {
|
|
2611
|
+
var num = parseFloat(val);
|
|
2612
|
+
if (val.includes('em')) {
|
|
2613
|
+
return num * 16; // Approximate: 1em = 16px
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
return num;
|
|
2617
|
+
};
|
|
2618
|
+
var gapPx = parseGap(gap);
|
|
2619
|
+
var _styles2 = {};
|
|
2620
|
+
|
|
2621
|
+
// Normalize placement synonyms (above/below/before/after) to CSS values
|
|
2622
|
+
var placement = normalizePlacement(position.placement);
|
|
2623
|
+
switch (placement) {
|
|
2624
|
+
case 'top':
|
|
2625
|
+
// Toast above element: bottom of toast = top of element - gap
|
|
2626
|
+
// bottom = viewport height - (element top - gap) = viewport height - element top + gap
|
|
2627
|
+
_styles2.bottom = "".concat(window.innerHeight - _rect.top + gapPx, "px");
|
|
2628
|
+
_styles2.left = "".concat(_rect.left, "px");
|
|
2629
|
+
break;
|
|
2630
|
+
case 'bottom':
|
|
2631
|
+
// Toast below element: top of toast = bottom of element + gap
|
|
2632
|
+
_styles2.top = "".concat(_rect.bottom + gapPx, "px");
|
|
2633
|
+
_styles2.left = "".concat(_rect.left, "px");
|
|
2634
|
+
break;
|
|
2635
|
+
case 'left':
|
|
2636
|
+
// Toast to the left: right of toast = left of element - gap
|
|
2637
|
+
_styles2.right = "".concat(window.innerWidth - _rect.left + gapPx, "px");
|
|
2638
|
+
_styles2.top = "".concat(_rect.top, "px");
|
|
2639
|
+
break;
|
|
2640
|
+
case 'right':
|
|
2641
|
+
// Toast to the right: left of toast = right of element + gap
|
|
2642
|
+
_styles2.left = "".concat(_rect.right + gapPx, "px");
|
|
2643
|
+
_styles2.top = "".concat(_rect.top, "px");
|
|
2644
|
+
break;
|
|
2645
|
+
}
|
|
2646
|
+
return _styles2;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
return {};
|
|
2023
2650
|
}
|
|
2024
2651
|
}]);
|
|
2025
2652
|
return ToastManager;
|