@khipu/design-system 0.2.0-alpha.13 → 0.2.0-alpha.131
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/beercss/khipu-beercss.css +2308 -297
- package/dist/beercss/khipu-beercss.js +152 -4
- package/dist/beercss/khipu-beercss.min.css +1 -1
- package/dist/beercss/khipu-beercss.min.js +1 -1
- package/dist/beercss/khipu-beercss.scoped.css +9043 -0
- package/dist/beercss/khipu-beercss.scoped.min.css +1 -0
- package/dist/beercss/metadata.json +8 -4
- package/dist/index.d.mts +999 -266
- package/dist/index.d.ts +999 -266
- package/dist/index.js +1443 -522
- package/dist/index.mjs +1402 -496
- package/package.json +14 -10
- package/dist/khipu-logo-color-TV75AKOV.svg +0 -19
|
@@ -892,11 +892,87 @@ const ui = _context.ui;
|
|
|
892
892
|
initCountdown();
|
|
893
893
|
initSegmentedTabs();
|
|
894
894
|
initStickyInvoice();
|
|
895
|
+
initHideOnScroll();
|
|
895
896
|
initBankModal();
|
|
897
|
+
initMerchantLogoBackdrop();
|
|
896
898
|
|
|
897
899
|
console.log('Material Design initialization complete!');
|
|
898
900
|
}
|
|
899
901
|
|
|
902
|
+
/**
|
|
903
|
+
* Initialize merchant logo backdrop (KTUF-204)
|
|
904
|
+
* Los logos de comercio van sobre el fondo neutro, no sobre el color de marca (los PNG
|
|
905
|
+
* con transparencia se ahogan contra su propio color). La luminancia decide entre los
|
|
906
|
+
* dos neutros: paper para logos oscuros, gray-800 para logos claros. Espejo vanilla de
|
|
907
|
+
* KdsInvoiceMerchant/logoLuminance.ts para las vistas server-side (payment).
|
|
908
|
+
* @param {Element} root - Root element to scope the query (default: document)
|
|
909
|
+
*/
|
|
910
|
+
function initMerchantLogoBackdrop(root) {
|
|
911
|
+
root = root || document;
|
|
912
|
+
var LIGHT_LOGO_LUMINANCE_THRESHOLD = 0.72;
|
|
913
|
+
var SAMPLE_SIZE = 16;
|
|
914
|
+
var ALPHA_THRESHOLD = 128;
|
|
915
|
+
var MIN_OPAQUE_RATIO = 0.02;
|
|
916
|
+
|
|
917
|
+
root.querySelectorAll('.kds-invoice-merchant').forEach(function(tile) {
|
|
918
|
+
var img = tile.querySelector('img');
|
|
919
|
+
if (!img) return;
|
|
920
|
+
|
|
921
|
+
/* Con logo: fondo neutro; el color de marca queda solo para el fallback. */
|
|
922
|
+
var brandBackground = tile.style.background;
|
|
923
|
+
tile.style.background = '';
|
|
924
|
+
tile.classList.add('kds-invoice-merchant-neutral');
|
|
925
|
+
|
|
926
|
+
/* Si el logo falla (el onerror server-side lo reemplaza por el ícono),
|
|
927
|
+
restaurar el tile de color de marca. */
|
|
928
|
+
img.addEventListener('error', function() {
|
|
929
|
+
tile.classList.remove('kds-invoice-merchant-neutral', 'dark');
|
|
930
|
+
tile.style.background = brandBackground;
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
measureLogoLuminance(img.getAttribute('src'), function(luminance) {
|
|
934
|
+
if (luminance != null && luminance > LIGHT_LOGO_LUMINANCE_THRESHOLD &&
|
|
935
|
+
tile.classList.contains('kds-invoice-merchant-neutral')) {
|
|
936
|
+
tile.classList.add('dark');
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
function measureLogoLuminance(url, callback) {
|
|
942
|
+
if (!url) { callback(null); return; }
|
|
943
|
+
var probe = new Image();
|
|
944
|
+
probe.crossOrigin = 'anonymous';
|
|
945
|
+
probe.onload = function() {
|
|
946
|
+
try {
|
|
947
|
+
var canvas = document.createElement('canvas');
|
|
948
|
+
canvas.width = SAMPLE_SIZE;
|
|
949
|
+
canvas.height = SAMPLE_SIZE;
|
|
950
|
+
var context = canvas.getContext('2d');
|
|
951
|
+
if (!context) { callback(null); return; }
|
|
952
|
+
context.drawImage(probe, 0, 0, SAMPLE_SIZE, SAMPLE_SIZE);
|
|
953
|
+
var data = context.getImageData(0, 0, SAMPLE_SIZE, SAMPLE_SIZE).data;
|
|
954
|
+
var sum = 0;
|
|
955
|
+
var opaquePixels = 0;
|
|
956
|
+
for (var i = 0; i < data.length; i += 4) {
|
|
957
|
+
if (data[i + 3] < ALPHA_THRESHOLD) continue;
|
|
958
|
+
sum += (0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]) / 255;
|
|
959
|
+
opaquePixels++;
|
|
960
|
+
}
|
|
961
|
+
if (opaquePixels < SAMPLE_SIZE * SAMPLE_SIZE * MIN_OPAQUE_RATIO) {
|
|
962
|
+
callback(null);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
callback(sum / opaquePixels);
|
|
966
|
+
} catch (e) {
|
|
967
|
+
/* canvas tainted: el origen no permite CORS — no medible */
|
|
968
|
+
callback(null);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
probe.onerror = function() { callback(null); };
|
|
972
|
+
probe.src = url;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
900
976
|
/**
|
|
901
977
|
* Initialize sidenav for mobile navigation
|
|
902
978
|
*/
|
|
@@ -1051,8 +1127,12 @@ const ui = _context.ui;
|
|
|
1051
1127
|
if (panel) {
|
|
1052
1128
|
if (expanded) {
|
|
1053
1129
|
panel.classList.remove('open');
|
|
1130
|
+
// Clear the inline cap so the CSS collapse rule (max-height: 0) animates the close.
|
|
1131
|
+
panel.style.maxHeight = '';
|
|
1054
1132
|
} else {
|
|
1055
1133
|
panel.classList.add('open');
|
|
1134
|
+
// Size to the content so the expand fits any length instead of clipping a fixed cap.
|
|
1135
|
+
panel.style.maxHeight = panel.scrollHeight + 'px';
|
|
1056
1136
|
}
|
|
1057
1137
|
}
|
|
1058
1138
|
});
|
|
@@ -1330,9 +1410,9 @@ const ui = _context.ui;
|
|
|
1330
1410
|
function initStickyInvoice(root) {
|
|
1331
1411
|
root = root || document;
|
|
1332
1412
|
|
|
1333
|
-
// Progressive collapse range: 0px (expanded) to
|
|
1413
|
+
// Progressive collapse range: 0px (expanded) to 20px (collapsed)
|
|
1334
1414
|
var COLLAPSE_START = 0;
|
|
1335
|
-
var COLLAPSE_END =
|
|
1415
|
+
var COLLAPSE_END = 20;
|
|
1336
1416
|
|
|
1337
1417
|
var lastScrollY = 0;
|
|
1338
1418
|
var ticking = false;
|
|
@@ -1380,13 +1460,16 @@ const ui = _context.ui;
|
|
|
1380
1460
|
if (!currentScreen.style.getPropertyValue('--collapse-collapsible-h')) {
|
|
1381
1461
|
var collapsible = currentSticky.querySelector('.kds-invoice-collapsible');
|
|
1382
1462
|
if (collapsible) {
|
|
1383
|
-
currentScreen.style.setProperty('--collapse-collapsible-h',
|
|
1463
|
+
currentScreen.style.setProperty('--collapse-collapsible-h', collapsible.offsetHeight + 'px');
|
|
1384
1464
|
}
|
|
1385
1465
|
}
|
|
1386
1466
|
|
|
1387
1467
|
// Single DOM write per frame — set on screen (parent) so it cascades to sticky + siblings
|
|
1388
1468
|
currentScreen.style.setProperty('--collapse-progress', progress);
|
|
1389
1469
|
|
|
1470
|
+
// Toggle collapsed state class for discrete styling that can't interpolate (e.g. align-items)
|
|
1471
|
+
currentSticky.classList.toggle('is-collapsed', progress >= 1);
|
|
1472
|
+
|
|
1390
1473
|
// Close expand panels as soon as the sticky header starts collapsing
|
|
1391
1474
|
if (progress > 0) {
|
|
1392
1475
|
currentSticky.querySelectorAll('[data-expand-toggle][aria-expanded="true"]').forEach(function(toggle) {
|
|
@@ -1410,6 +1493,9 @@ const ui = _context.ui;
|
|
|
1410
1493
|
screen.style.removeProperty('--collapse-progress');
|
|
1411
1494
|
screen.style.removeProperty('--collapse-collapsible-h');
|
|
1412
1495
|
});
|
|
1496
|
+
root.querySelectorAll('.kds-invoice-sticky.is-collapsed').forEach(function(el) {
|
|
1497
|
+
el.classList.remove('is-collapsed');
|
|
1498
|
+
});
|
|
1413
1499
|
}
|
|
1414
1500
|
});
|
|
1415
1501
|
|
|
@@ -1419,6 +1505,67 @@ const ui = _context.ui;
|
|
|
1419
1505
|
onScroll();
|
|
1420
1506
|
}
|
|
1421
1507
|
|
|
1508
|
+
/**
|
|
1509
|
+
* Initialize hide-on-scroll for floating elements (vanilla equivalent of the React
|
|
1510
|
+
* `useHideOnScroll` hook). Any element with `[data-hide-on-scroll]` gets its hide class
|
|
1511
|
+
* (default `kds-fab--hidden`, override with `data-hide-class`) toggled: hidden when
|
|
1512
|
+
* scrolling down past the threshold, shown when scrolling up, always shown within
|
|
1513
|
+
* `data-hide-top-offset` px of the top. Tune with `data-hide-threshold` (default 8).
|
|
1514
|
+
*
|
|
1515
|
+
* Works standalone (`window.scrollY`) and embedded in an iframe, where the parent posts
|
|
1516
|
+
* `postMessage({ type: 'VIEWPORT_OFFSET', offsetTop })`.
|
|
1517
|
+
* @param {Element} root - Root element to scope the query (default: document)
|
|
1518
|
+
*/
|
|
1519
|
+
function initHideOnScroll(root) {
|
|
1520
|
+
root = root || document;
|
|
1521
|
+
var els = root.querySelectorAll('[data-hide-on-scroll]');
|
|
1522
|
+
if (!els.length) return;
|
|
1523
|
+
|
|
1524
|
+
var viewportOffset = 0;
|
|
1525
|
+
var ticking = false;
|
|
1526
|
+
var lastYs = new Map();
|
|
1527
|
+
|
|
1528
|
+
function currentY() {
|
|
1529
|
+
return Math.max(window.scrollY || window.pageYOffset || 0, viewportOffset);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
function apply() {
|
|
1533
|
+
ticking = false;
|
|
1534
|
+
var y = currentY();
|
|
1535
|
+
els.forEach(function(el) {
|
|
1536
|
+
var threshold = parseInt(el.getAttribute('data-hide-threshold'), 10) || 8;
|
|
1537
|
+
var topOffset = parseInt(el.getAttribute('data-hide-top-offset'), 10) || 0;
|
|
1538
|
+
var hideClass = el.getAttribute('data-hide-class') || 'kds-fab--hidden';
|
|
1539
|
+
var lastY = lastYs.has(el) ? lastYs.get(el) : y;
|
|
1540
|
+
if (y <= topOffset) {
|
|
1541
|
+
el.classList.remove(hideClass);
|
|
1542
|
+
lastYs.set(el, y);
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
var delta = y - lastY;
|
|
1546
|
+
if (Math.abs(delta) < threshold) return;
|
|
1547
|
+
el.classList.toggle(hideClass, delta > 0);
|
|
1548
|
+
lastYs.set(el, y);
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
function onScroll() {
|
|
1553
|
+
if (ticking) return;
|
|
1554
|
+
ticking = true;
|
|
1555
|
+
requestAnimationFrame(apply);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
1559
|
+
window.addEventListener('resize', onScroll);
|
|
1560
|
+
window.addEventListener('message', function(event) {
|
|
1561
|
+
if (event.data && event.data.type === 'VIEWPORT_OFFSET') {
|
|
1562
|
+
viewportOffset = Math.max(0, event.data.offsetTop || 0);
|
|
1563
|
+
onScroll();
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
apply();
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1422
1569
|
/**
|
|
1423
1570
|
* Initialize copyable table rows
|
|
1424
1571
|
* Delegated click on .kds-copyable-table-row[data-copy] copies value and shows feedback
|
|
@@ -1461,7 +1608,7 @@ const ui = _context.ui;
|
|
|
1461
1608
|
var rows = container.querySelectorAll('.kds-copyable-table-row[data-copy]');
|
|
1462
1609
|
var values = [];
|
|
1463
1610
|
rows.forEach(function(r) {
|
|
1464
|
-
var key = r.querySelector('.
|
|
1611
|
+
var key = r.querySelector('.kds-key');
|
|
1465
1612
|
var val = r.dataset.copy;
|
|
1466
1613
|
if (key) {
|
|
1467
1614
|
values.push(key.textContent.trim() + ': ' + val);
|
|
@@ -1589,6 +1736,7 @@ const ui = _context.ui;
|
|
|
1589
1736
|
window.Khipu.initInfoTip = initInfoTip;
|
|
1590
1737
|
window.Khipu.initBankModal = initBankModal;
|
|
1591
1738
|
window.Khipu.initStickyInvoice = initStickyInvoice;
|
|
1739
|
+
window.Khipu.initHideOnScroll = initHideOnScroll;
|
|
1592
1740
|
|
|
1593
1741
|
// Also export showSnackbar to global scope for backward compatibility
|
|
1594
1742
|
window.showSnackbar = showSnackbar;
|