@bytebrand/fe-ui-core-autobahn 1.0.36 → 1.0.38

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.36",
3
+ "version": "1.0.38",
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; }
@@ -1464,9 +1466,10 @@
1464
1466
  .md3 .vdp-header-summary .vdp-meta > span + span::before { content: ''; margin: 0 8px; }
1465
1467
  .md3 .vdp-header-summary .vdp-meta > span { overflow: visible; text-overflow: clip; }
1466
1468
  .md3 .vdp-header-summary .vdp-meta .lucide { display: none; }
1467
- /* compact dot-line = km/EZ/Getriebe/Kraftstoff only (mockup) — hides Hand + Karosserie,
1468
- which only appear in the 2-col fact grid */
1469
- .md3 .vdp-header-summary .vdp-meta > span:nth-child(n+5) { display: none; }
1469
+ /* compact dot-line = km/EZ/Getriebe/Kraftstoff · Karosserie (round 9: body type must be
1470
+ visible here — was previously invisible under nth-child(n+5), which also swallowed it).
1471
+ Only "1. Hand" (item 5) stays hidden; item 6 (body type) now renders. */
1472
+ .md3 .vdp-header-summary .vdp-meta > span:nth-child(5) { display: none; }
1470
1473
  /* highlights: the ≤980 Figma 2-col grid applies here too (no swipe-line since round 9) */
1471
1474
  /* condition row: 2×2 on phone */
1472
1475
  .md3 .zustand-grid { grid-template-columns: 1fr 1fr; }
@@ -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>