@khipu/design-system 0.2.0-alpha.82 → 0.2.0-alpha.84
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 +124 -2
- package/dist/beercss/khipu-beercss.js +67 -0
- 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 +124 -2
- package/dist/beercss/khipu-beercss.scoped.min.css +1 -1
- package/dist/beercss/metadata.json +5 -5
- package/dist/index.d.mts +77 -4
- package/dist/index.d.ts +77 -4
- package/dist/index.js +227 -141
- package/dist/index.mjs +198 -114
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
13
13
|
* Source: design-system/src/tokens/tokens.json
|
|
14
|
-
* Generated: 2026-
|
|
14
|
+
* Generated: 2026-07-01T17:49:00.275Z
|
|
15
15
|
*
|
|
16
16
|
* To regenerate:
|
|
17
17
|
* cd design-system && npm run tokens:generate
|
|
@@ -5508,6 +5508,125 @@ dialog#surveyModal button.circle {
|
|
|
5508
5508
|
box-shadow: 0 -4px 16px rgba(16,24,40,.08);
|
|
5509
5509
|
}
|
|
5510
5510
|
|
|
5511
|
+
/* ========================================
|
|
5512
|
+
08b - FAB (.kds-fab)
|
|
5513
|
+
Circular, icon-only floating action button with an animated hide state.
|
|
5514
|
+
Presentational: drive `.kds-fab--hidden` from useHideOnScroll for scroll-away.
|
|
5515
|
+
======================================== */
|
|
5516
|
+
|
|
5517
|
+
.kds-fab {
|
|
5518
|
+
display: inline-flex;
|
|
5519
|
+
align-items: center;
|
|
5520
|
+
justify-content: center;
|
|
5521
|
+
width: var(--kds-spacing-5);
|
|
5522
|
+
height: var(--kds-spacing-5);
|
|
5523
|
+
padding: 0;
|
|
5524
|
+
border: none;
|
|
5525
|
+
border-radius: var(--kds-radius-full);
|
|
5526
|
+
background: var(--kds-color-background-paper);
|
|
5527
|
+
color: var(--kds-color-text-primary);
|
|
5528
|
+
box-shadow: var(--kds-shadow-elevated);
|
|
5529
|
+
cursor: pointer;
|
|
5530
|
+
z-index: 100;
|
|
5531
|
+
/* Neutralize native button rendering so the pressed state doesn't flash a square. */
|
|
5532
|
+
-webkit-appearance: none;
|
|
5533
|
+
appearance: none;
|
|
5534
|
+
-webkit-tap-highlight-color: transparent;
|
|
5535
|
+
transition: opacity var(--kds-transition-short) ease, transform var(--kds-transition-short) ease,
|
|
5536
|
+
background-color var(--kds-transition-shorter) ease;
|
|
5537
|
+
}
|
|
5538
|
+
|
|
5539
|
+
/* Keep the circle in EVERY interaction state. Uses `button.kds-fab` (element + class) to
|
|
5540
|
+
out-specify BeerCSS's base `button`/`button:active` rules — same trick as `.kds-btn`. */
|
|
5541
|
+
button.kds-fab,
|
|
5542
|
+
button.kds-fab:hover,
|
|
5543
|
+
button.kds-fab:active,
|
|
5544
|
+
button.kds-fab:focus,
|
|
5545
|
+
button.kds-fab:focus-visible {
|
|
5546
|
+
border-radius: var(--kds-radius-full);
|
|
5547
|
+
background: var(--kds-color-background-paper);
|
|
5548
|
+
}
|
|
5549
|
+
|
|
5550
|
+
button.kds-fab:hover:not(:disabled) {
|
|
5551
|
+
background: var(--kds-color-background-muted);
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
/* Subtle press feedback that preserves the round shape. */
|
|
5555
|
+
button.kds-fab:active:not(:disabled) {
|
|
5556
|
+
transform: translateY(var(--kds-spacing-0-25));
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
button.kds-fab:focus-visible {
|
|
5560
|
+
outline: var(--kds-spacing-0-25) solid var(--kds-color-primary-main);
|
|
5561
|
+
outline-offset: var(--kds-spacing-0-25);
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5564
|
+
.kds-fab > i.material-symbols-outlined {
|
|
5565
|
+
font-size: var(--kds-spacing-button-icon-size);
|
|
5566
|
+
line-height: 1;
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5569
|
+
/* Pinned corners — fixed to the viewport (stays visible on scroll-up, iframe-friendly).
|
|
5570
|
+
Horizontally the top-right FAB hugs the right edge of the centered content column set via
|
|
5571
|
+
`--kds-fab-column-width` (default 100% → viewport edge). The consumer sets that var to its
|
|
5572
|
+
widget width so on desktop the FAB overlaps the header's top-right corner instead of drifting
|
|
5573
|
+
to the far viewport edge. `max(...)` keeps at least a token inset on narrow (mobile) viewports. */
|
|
5574
|
+
.kds-fab--top-right,
|
|
5575
|
+
.kds-fab--top-left,
|
|
5576
|
+
.kds-fab--bottom-right,
|
|
5577
|
+
.kds-fab--bottom-left {
|
|
5578
|
+
position: fixed;
|
|
5579
|
+
}
|
|
5580
|
+
|
|
5581
|
+
.kds-fab--top-right {
|
|
5582
|
+
top: var(--kds-spacing-2);
|
|
5583
|
+
right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
.kds-fab--top-left {
|
|
5587
|
+
top: var(--kds-spacing-2);
|
|
5588
|
+
left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
|
|
5589
|
+
}
|
|
5590
|
+
|
|
5591
|
+
.kds-fab--bottom-right {
|
|
5592
|
+
bottom: var(--kds-spacing-2);
|
|
5593
|
+
right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
|
|
5594
|
+
}
|
|
5595
|
+
|
|
5596
|
+
.kds-fab--bottom-left {
|
|
5597
|
+
bottom: var(--kds-spacing-2);
|
|
5598
|
+
left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5601
|
+
/* Desktop: nudge the top-right FAB up so it sits partially over the header's top edge. */
|
|
5602
|
+
@media (min-width: 768px) {
|
|
5603
|
+
.kds-fab--top-right {
|
|
5604
|
+
top: var(--kds-spacing-1);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
|
|
5608
|
+
/* Hidden (scroll-away) state — fade + slide toward the nearest edge */
|
|
5609
|
+
.kds-fab--hidden {
|
|
5610
|
+
opacity: 0;
|
|
5611
|
+
transform: translateY(calc(-1 * var(--kds-spacing-1))) scale(0.9);
|
|
5612
|
+
pointer-events: none;
|
|
5613
|
+
}
|
|
5614
|
+
|
|
5615
|
+
.kds-fab--bottom-right.kds-fab--hidden,
|
|
5616
|
+
.kds-fab--bottom-left.kds-fab--hidden {
|
|
5617
|
+
transform: translateY(var(--kds-spacing-1)) scale(0.9);
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5620
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5621
|
+
.kds-fab {
|
|
5622
|
+
transition: opacity var(--kds-transition-short) ease;
|
|
5623
|
+
}
|
|
5624
|
+
|
|
5625
|
+
.kds-fab--hidden {
|
|
5626
|
+
transform: none;
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5511
5630
|
/* ========================================
|
|
5512
5631
|
09 - SUCCESS MARK (.kds-success-mark)
|
|
5513
5632
|
Large success icon for completion screens
|
|
@@ -5728,7 +5847,10 @@ dialog#surveyModal button.circle {
|
|
|
5728
5847
|
}
|
|
5729
5848
|
|
|
5730
5849
|
.kds-expand-panel.open {
|
|
5731
|
-
|
|
5850
|
+
/* Fallback cap for no-JS consumers. With JS (React useExpandToggle / vanilla
|
|
5851
|
+
initExpandToggle) the panel's inline max-height is set to its exact scrollHeight,
|
|
5852
|
+
which overrides this and prevents clipping regardless of content length. */
|
|
5853
|
+
max-height: 2000px;
|
|
5732
5854
|
margin-top: 0;
|
|
5733
5855
|
}
|
|
5734
5856
|
|
|
@@ -892,6 +892,7 @@ const ui = _context.ui;
|
|
|
892
892
|
initCountdown();
|
|
893
893
|
initSegmentedTabs();
|
|
894
894
|
initStickyInvoice();
|
|
895
|
+
initHideOnScroll();
|
|
895
896
|
initBankModal();
|
|
896
897
|
|
|
897
898
|
console.log('Material Design initialization complete!');
|
|
@@ -1051,8 +1052,12 @@ const ui = _context.ui;
|
|
|
1051
1052
|
if (panel) {
|
|
1052
1053
|
if (expanded) {
|
|
1053
1054
|
panel.classList.remove('open');
|
|
1055
|
+
// Clear the inline cap so the CSS collapse rule (max-height: 0) animates the close.
|
|
1056
|
+
panel.style.maxHeight = '';
|
|
1054
1057
|
} else {
|
|
1055
1058
|
panel.classList.add('open');
|
|
1059
|
+
// Size to the content so the expand fits any length instead of clipping a fixed cap.
|
|
1060
|
+
panel.style.maxHeight = panel.scrollHeight + 'px';
|
|
1056
1061
|
}
|
|
1057
1062
|
}
|
|
1058
1063
|
});
|
|
@@ -1425,6 +1430,67 @@ const ui = _context.ui;
|
|
|
1425
1430
|
onScroll();
|
|
1426
1431
|
}
|
|
1427
1432
|
|
|
1433
|
+
/**
|
|
1434
|
+
* Initialize hide-on-scroll for floating elements (vanilla equivalent of the React
|
|
1435
|
+
* `useHideOnScroll` hook). Any element with `[data-hide-on-scroll]` gets its hide class
|
|
1436
|
+
* (default `kds-fab--hidden`, override with `data-hide-class`) toggled: hidden when
|
|
1437
|
+
* scrolling down past the threshold, shown when scrolling up, always shown within
|
|
1438
|
+
* `data-hide-top-offset` px of the top. Tune with `data-hide-threshold` (default 8).
|
|
1439
|
+
*
|
|
1440
|
+
* Works standalone (`window.scrollY`) and embedded in an iframe, where the parent posts
|
|
1441
|
+
* `postMessage({ type: 'VIEWPORT_OFFSET', offsetTop })`.
|
|
1442
|
+
* @param {Element} root - Root element to scope the query (default: document)
|
|
1443
|
+
*/
|
|
1444
|
+
function initHideOnScroll(root) {
|
|
1445
|
+
root = root || document;
|
|
1446
|
+
var els = root.querySelectorAll('[data-hide-on-scroll]');
|
|
1447
|
+
if (!els.length) return;
|
|
1448
|
+
|
|
1449
|
+
var viewportOffset = 0;
|
|
1450
|
+
var ticking = false;
|
|
1451
|
+
var lastYs = new Map();
|
|
1452
|
+
|
|
1453
|
+
function currentY() {
|
|
1454
|
+
return Math.max(window.scrollY || window.pageYOffset || 0, viewportOffset);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
function apply() {
|
|
1458
|
+
ticking = false;
|
|
1459
|
+
var y = currentY();
|
|
1460
|
+
els.forEach(function(el) {
|
|
1461
|
+
var threshold = parseInt(el.getAttribute('data-hide-threshold'), 10) || 8;
|
|
1462
|
+
var topOffset = parseInt(el.getAttribute('data-hide-top-offset'), 10) || 0;
|
|
1463
|
+
var hideClass = el.getAttribute('data-hide-class') || 'kds-fab--hidden';
|
|
1464
|
+
var lastY = lastYs.has(el) ? lastYs.get(el) : y;
|
|
1465
|
+
if (y <= topOffset) {
|
|
1466
|
+
el.classList.remove(hideClass);
|
|
1467
|
+
lastYs.set(el, y);
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
var delta = y - lastY;
|
|
1471
|
+
if (Math.abs(delta) < threshold) return;
|
|
1472
|
+
el.classList.toggle(hideClass, delta > 0);
|
|
1473
|
+
lastYs.set(el, y);
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function onScroll() {
|
|
1478
|
+
if (ticking) return;
|
|
1479
|
+
ticking = true;
|
|
1480
|
+
requestAnimationFrame(apply);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
1484
|
+
window.addEventListener('resize', onScroll);
|
|
1485
|
+
window.addEventListener('message', function(event) {
|
|
1486
|
+
if (event.data && event.data.type === 'VIEWPORT_OFFSET') {
|
|
1487
|
+
viewportOffset = Math.max(0, event.data.offsetTop || 0);
|
|
1488
|
+
onScroll();
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
apply();
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1428
1494
|
/**
|
|
1429
1495
|
* Initialize copyable table rows
|
|
1430
1496
|
* Delegated click on .kds-copyable-table-row[data-copy] copies value and shows feedback
|
|
@@ -1595,6 +1661,7 @@ const ui = _context.ui;
|
|
|
1595
1661
|
window.Khipu.initInfoTip = initInfoTip;
|
|
1596
1662
|
window.Khipu.initBankModal = initBankModal;
|
|
1597
1663
|
window.Khipu.initStickyInvoice = initStickyInvoice;
|
|
1664
|
+
window.Khipu.initHideOnScroll = initHideOnScroll;
|
|
1598
1665
|
|
|
1599
1666
|
// Also export showSnackbar to global scope for backward compatibility
|
|
1600
1667
|
window.showSnackbar = showSnackbar;
|