@bytebrand/fe-ui-core-autobahn 1.0.35 → 1.0.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core-autobahn",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
5
5
  "main": "index.ts",
6
6
  "license": "UNLICENSED",
package/theme/md3.css CHANGED
@@ -1307,6 +1307,8 @@
1307
1307
  max-height: 88vh; display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--md-e3); }
1308
1308
  .md3 .fin-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
1309
1309
  padding: 16px 20px; border-bottom: 1px solid var(--md-outline-variant); flex: none; }
1310
+ /* mobile sheet grabber row (mockup drag handle) — .show-mobile reveals it ≤600 */
1311
+ .md3 .fin-sheet-grip { justify-content: center; padding: 10px 0 0; cursor: grab; flex: none; }
1310
1312
  .md3 .fin-modal-body { padding: 18px 20px 22px; overflow-y: auto; }
1311
1313
  @media (max-width: 600px) {
1312
1314
  .md3 .fin-modal-scrim { align-items: flex-end; padding: 0; }
@@ -342,13 +342,14 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
342
342
  >
343
343
  {isCash ? eur(price) : eur(Math.round(monthly))}
344
344
  </span>
345
- {/* struck old rate (REAL oldMonthlyInstallment; SRP strikes the same source) */}
346
- {!isCash && oldMonthly != null && (
347
- <s className="num fin-rate-old">{eur(Math.round(oldMonthly))} €</s>
348
- )}
349
345
  <span className="md-body-sm" style={{ fontWeight: 600 }}>
350
346
  {isCash ? '€' : `€ ${t('autobahn:finance.perMonth')}`}
351
347
  </span>
348
+ {/* struck old rate (REAL oldMonthlyInstallment; SRP strikes the same source) —
349
+ after the unit, else the unit wraps under the hero figure on phones */}
350
+ {!isCash && oldMonthly != null && (
351
+ <s className="num fin-rate-old">{eur(Math.round(oldMonthly))} €</s>
352
+ )}
352
353
  </div>
353
354
  {isCash && <div className="md-label-sm">{t('autobahn:finance.fixedPrice')}</div>}
354
355
  {isCash && showStrike && (
@@ -1266,6 +1266,43 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1266
1266
  // eslint-disable-next-line react-hooks/exhaustive-deps
1267
1267
  }, [open, initialShowBreakdown]);
1268
1268
 
1269
+ // mobile-only: swipe-down-to-dismiss on the grabber/header (the mockup's drag handle).
1270
+ // The sheet follows the finger (imperative transform — no re-renders while dragging);
1271
+ // released past 90px it slides out and closes, otherwise it springs back. The element
1272
+ // unmounts on close (`open` gate), so the inline transform never leaks into the next open.
1273
+ const sheetRef = React.useRef<HTMLDivElement | null>(null);
1274
+ const dragStartY = React.useRef<number | null>(null);
1275
+ const onGrabStart = (e: React.TouchEvent): void => {
1276
+ // sheet gesture is phone-only — the desktop centered modal must not be draggable
1277
+ if (typeof window !== 'undefined' && window.matchMedia('(min-width: 601px)').matches) return;
1278
+ dragStartY.current = e.touches[0].clientY;
1279
+ if (sheetRef.current) sheetRef.current.style.transition = 'none';
1280
+ };
1281
+ const onGrabMove = (e: React.TouchEvent): void => {
1282
+ if (dragStartY.current == null || !sheetRef.current) return;
1283
+ const dy = e.touches[0].clientY - dragStartY.current;
1284
+ sheetRef.current.style.transform = dy > 0 ? `translateY(${dy}px)` : '';
1285
+ };
1286
+ const onGrabEnd = (e: React.TouchEvent): void => {
1287
+ if (dragStartY.current == null || !sheetRef.current) return;
1288
+ const dy = e.changedTouches[0].clientY - dragStartY.current;
1289
+ dragStartY.current = null;
1290
+ const el = sheetRef.current;
1291
+ el.style.transition = 'transform var(--dur-2, .25s)';
1292
+ if (dy > 90) {
1293
+ el.style.transform = 'translateY(100%)';
1294
+ window.setTimeout(onClose, 200);
1295
+ } else {
1296
+ el.style.transform = '';
1297
+ }
1298
+ };
1299
+ const dragZone = {
1300
+ onTouchStart: onGrabStart,
1301
+ onTouchMove: onGrabMove,
1302
+ onTouchEnd: onGrabEnd,
1303
+ style: { touchAction: 'none' } as React.CSSProperties,
1304
+ };
1305
+
1269
1306
  if (!open) return null;
1270
1307
  const isLease = mode === 'leasing';
1271
1308
  const isCash = mode === 'cash';
@@ -1327,8 +1364,12 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1327
1364
 
1328
1365
  return (
1329
1366
  <div className="fin-modal-scrim" onClick={onClose}>
1330
- <div className="fin-modal" role="dialog" aria-modal="true" aria-label={title} onClick={(e) => e.stopPropagation()}>
1331
- <div className="fin-modal-head">
1367
+ <div className="fin-modal" role="dialog" aria-modal="true" aria-label={title} ref={sheetRef} onClick={(e) => e.stopPropagation()}>
1368
+ {/* mobile grabber (mockup) — swipe down here or on the header to dismiss */}
1369
+ <div className="fin-sheet-grip show-mobile" {...dragZone}>
1370
+ <span className="sheet-handle" style={{ margin: 0 }} />
1371
+ </div>
1372
+ <div className="fin-modal-head" {...dragZone}>
1332
1373
  <h2 className="md-headline-sm hide-mobile" style={{ margin: 0 }}>
1333
1374
  {title}
1334
1375
  </h2>
@@ -1436,12 +1477,12 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1436
1477
  <span className="num" style={{ fontSize: 34, fontWeight: 800, color: 'var(--md-primary)', letterSpacing: '-1px' }}>
1437
1478
  {isCash ? eur(price) : eur(Math.round(monthly))}
1438
1479
  </span>
1439
- {!isCash && oldMonthly != null && (
1440
- <s className="num fin-rate-old">{eur(Math.round(oldMonthly))} €</s>
1441
- )}
1442
1480
  <span className="md-body-sm" style={{ fontWeight: 600 }}>
1443
1481
  {isCash ? '€' : `€ ${t('autobahn:finance.perMonth')}`}
1444
1482
  </span>
1483
+ {!isCash && oldMonthly != null && (
1484
+ <s className="num fin-rate-old">{eur(Math.round(oldMonthly))} €</s>
1485
+ )}
1445
1486
  </div>
1446
1487
  </div>
1447
1488
  <div>