@bytebrand/fe-ui-core-autobahn 1.0.15 → 1.0.16
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 +1 -1
- package/theme/md3.css +14 -1
- package/vdp/VdpModules.tsx +104 -46
- package/vdp/index.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
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
|
@@ -155,6 +155,12 @@
|
|
|
155
155
|
NOT the 38px tappable filter-chip the base .chip is. */
|
|
156
156
|
.md3 .listcard-chips .chip { height: 24px; padding: 0 10px; font-size: 11.5px; border-radius: 8px; cursor: default; }
|
|
157
157
|
.md3 .listcard-chips .chip:hover { background: var(--md-surface); border-color: var(--md-outline-variant); }
|
|
158
|
+
/* VDP trust row (Sofort verfügbar/views/favorites/Geprüft) — same compact non-interactive
|
|
159
|
+
sizing as the listcard equipment tags, standalone (not nested under .listcard-chips). */
|
|
160
|
+
.md3 .vdp-trust-chip { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px;
|
|
161
|
+
border-radius: 8px; font-size: 11.5px; font-weight: 500; line-height: 1; cursor: default; white-space: nowrap;
|
|
162
|
+
background: var(--md-surface); color: var(--md-on-surface-variant); border: 1px solid var(--md-outline-variant); }
|
|
163
|
+
.md3 .vdp-trust-chip .lucide { width: 14px; height: 14px; flex: none; }
|
|
158
164
|
|
|
159
165
|
/* status / rating chip (no CTA) */
|
|
160
166
|
.md3 .statchip { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 10px;
|
|
@@ -1283,7 +1289,14 @@
|
|
|
1283
1289
|
.md3 .srp-rail { display: none; } /* rail is desktop-only; ≤980 uses the filter sheet */
|
|
1284
1290
|
.md3 .srp-grid { grid-template-columns: 1fr; }
|
|
1285
1291
|
.md3 .vdp-grid { display: flex; flex-direction: column; }
|
|
1286
|
-
.md3 .vdp-gallery { order: 1; }
|
|
1292
|
+
.md3 .vdp-gallery { order: 1; }
|
|
1293
|
+
/* mobile-only "first screen": identity + trust row BEFORE the finance card, highlights/
|
|
1294
|
+
condition tiles moved AFTER it (both split out of the desktop-only VdpHeaderCard,
|
|
1295
|
+
which stays bundled inside .vdp-gallery, hidden on mobile via hide-mobile). */
|
|
1296
|
+
.md3 .vdp-header-summary { order: 2; padding: 0 4px; margin-bottom: 4px; }
|
|
1297
|
+
.md3 .vdp-aside { order: 3; }
|
|
1298
|
+
.md3 .vdp-header-extras { order: 4; margin-top: 16px; }
|
|
1299
|
+
.md3 .vdp-details { order: 5; }
|
|
1287
1300
|
.md3 .vdp-aside { position: static; }
|
|
1288
1301
|
.md3 .price-card { position: static; }
|
|
1289
1302
|
.md3 .fin-call { display: block; } /* tap-to-call restored on touch */
|
package/vdp/VdpModules.tsx
CHANGED
|
@@ -51,14 +51,10 @@ export interface VdpHeaderCardProps {
|
|
|
51
51
|
t: TranslationFunction;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
* is just the textual header card that sits beneath it.
|
|
59
|
-
*/
|
|
60
|
-
export const VdpHeaderCard: React.FunctionComponent<VdpHeaderCardProps> = ({ vehicle, t }) => {
|
|
61
|
-
const { condition } = vehicle;
|
|
54
|
+
/** headline + trim/power + rating pill, then the dot-separated fact line — the vehicle's
|
|
55
|
+
* identity block. Split out so the mobile-only layout can show this BEFORE the finance
|
|
56
|
+
* card (see VdpTrustRow) while the highlights/condition tiles move after it. */
|
|
57
|
+
export const VdpHeaderSummary: React.FunctionComponent<VdpHeaderCardProps> = ({ vehicle, t }) => {
|
|
62
58
|
const factLine: ReadonlyArray<readonly [string, string]> = [
|
|
63
59
|
['gauge', fmtKm(vehicle.km)],
|
|
64
60
|
['calendar', vehicle.reg],
|
|
@@ -66,15 +62,8 @@ export const VdpHeaderCard: React.FunctionComponent<VdpHeaderCardProps> = ({ veh
|
|
|
66
62
|
['fuel', stripParen(vehicle.fuel)],
|
|
67
63
|
['user', t('autobahn:header.owners', { owners: vehicle.owners, count: vehicle.owners })],
|
|
68
64
|
];
|
|
69
|
-
// condition.hu === 'Neu' compares the RAW backend data value, not a display string — left as-is.
|
|
70
|
-
const tiles: ReadonlyArray<readonly [string, string, string]> = [
|
|
71
|
-
['wrench', t('autobahn:header.huLabel'), condition.hu === 'Neu' ? t('autobahn:header.huNew') : t('autobahn:header.huUntil', { date: condition.hu })],
|
|
72
|
-
['shield-check', t('autobahn:header.accidentFree'), condition.unfallfrei ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
73
|
-
['clipboard-check', t('autobahn:header.serviceHistory'), condition.scheckheft ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
74
|
-
['award', t('autobahn:header.warranty'), condition.garantie],
|
|
75
|
-
];
|
|
76
65
|
return (
|
|
77
|
-
|
|
66
|
+
<>
|
|
78
67
|
<div className="vdp-headrow">
|
|
79
68
|
<div style={{ minWidth: 0 }}>
|
|
80
69
|
<h1 className="md-headline" style={{ margin: '0 0 4px' }}>
|
|
@@ -106,43 +95,112 @@ export const VdpHeaderCard: React.FunctionComponent<VdpHeaderCardProps> = ({ veh
|
|
|
106
95
|
</span>
|
|
107
96
|
))}
|
|
108
97
|
</div>
|
|
98
|
+
</>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
109
101
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
>
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
</div>
|
|
102
|
+
/** hand-picked highlights — the single accent row of the header. */
|
|
103
|
+
export const VdpHeaderHighlights: React.FunctionComponent<VdpHeaderCardProps> = ({ vehicle }) => (
|
|
104
|
+
<div className="vdp-highlights" style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 }}>
|
|
105
|
+
{vehicle.highlights.slice(0, 6).map((f) => (
|
|
106
|
+
<span key={f} className="chip chip--filter chip--selected" style={{ cursor: 'default' }}>
|
|
107
|
+
<Icon name="check" size={14} stroke={2.4} />
|
|
108
|
+
{f}
|
|
109
|
+
</span>
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
122
113
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
114
|
+
/** condition (zustand) tiles — TÜV/Unfallfrei/Scheckheft/Garantie. */
|
|
115
|
+
export const VdpHeaderCondition: React.FunctionComponent<VdpHeaderCardProps> = ({ vehicle, t }) => {
|
|
116
|
+
const { condition } = vehicle;
|
|
117
|
+
// condition.hu === 'Neu' compares the RAW backend data value, not a display string — left as-is.
|
|
118
|
+
const tiles: ReadonlyArray<readonly [string, string, string]> = [
|
|
119
|
+
['wrench', t('autobahn:header.huLabel'), condition.hu === 'Neu' ? t('autobahn:header.huNew') : t('autobahn:header.huUntil', { date: condition.hu })],
|
|
120
|
+
['shield-check', t('autobahn:header.accidentFree'), condition.unfallfrei ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
121
|
+
['clipboard-check', t('autobahn:header.serviceHistory'), condition.scheckheft ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
122
|
+
['award', t('autobahn:header.warranty'), condition.garantie],
|
|
123
|
+
];
|
|
124
|
+
return (
|
|
125
|
+
<div className="zustand-grid">
|
|
126
|
+
{tiles.map(([ic, l, v]) => (
|
|
127
|
+
<div key={l} className="zustand-tile">
|
|
128
|
+
<Icon name={ic} size={19} color="var(--md-primary)" stroke={1.9} style={{ flex: 'none' }} />
|
|
129
|
+
<span style={{ minWidth: 0 }}>
|
|
130
|
+
<span
|
|
131
|
+
className="md-label-sm"
|
|
132
|
+
style={{ display: 'block', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
|
|
133
|
+
>
|
|
134
|
+
{l}
|
|
138
135
|
</span>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
<span className="md-title-sm" style={{ display: 'block' }}>
|
|
137
|
+
{v}
|
|
138
|
+
</span>
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
))}
|
|
142
142
|
</div>
|
|
143
143
|
);
|
|
144
144
|
};
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* One container holding the entire vehicle identity: headline + trim/power, the
|
|
148
|
+
* dot-separated fact line, the hand-picked highlight chips, and the condition
|
|
149
|
+
* (zustand) tiles footer. The gallery itself is composed by the page (W3b) — this
|
|
150
|
+
* is just the textual header card that sits beneath it. Desktop-only usage (mobile
|
|
151
|
+
* splits these 3 pieces across the "before/after finance card" layout — see the page).
|
|
152
|
+
*/
|
|
153
|
+
export const VdpHeaderCard: React.FunctionComponent<VdpHeaderCardProps> = ({ vehicle, t }) => (
|
|
154
|
+
<div className="mcard" style={{ marginTop: 16, padding: '22px 24px 18px' }}>
|
|
155
|
+
<VdpHeaderSummary vehicle={vehicle} t={t} />
|
|
156
|
+
<div style={{ marginTop: 14 }}>
|
|
157
|
+
<VdpHeaderHighlights vehicle={vehicle} t={t} />
|
|
158
|
+
</div>
|
|
159
|
+
<VdpHeaderCondition vehicle={vehicle} t={t} />
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
export interface VdpTrustRowProps {
|
|
164
|
+
/** maps to carStore.isAvailable / state.isOnline — "Sofort verfügbar". */
|
|
165
|
+
available: boolean;
|
|
166
|
+
/** real totalCarImpCount from /service/user-actions/api/v1/stats/viewers — omit while loading. */
|
|
167
|
+
viewCount?: number;
|
|
168
|
+
/** real totalFavCount from the same endpoint — omit while loading. */
|
|
169
|
+
favCount?: number;
|
|
170
|
+
/** i18n function (drilled from the page). */
|
|
171
|
+
t: TranslationFunction;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** mobile-only trust-signal row shown between the identity block and the finance card:
|
|
175
|
+
* availability · view count · favorite count · "Geprüft" (every auto.de listing is
|
|
176
|
+
* 130-point inspected, so this one is always shown, not conditional real data). */
|
|
177
|
+
export const VdpTrustRow: React.FunctionComponent<VdpTrustRowProps> = ({ available, viewCount, favCount, t }) => (
|
|
178
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6, marginTop: 10 }}>
|
|
179
|
+
{available && (
|
|
180
|
+
<span className="vdp-trust-chip">
|
|
181
|
+
<Icon name="check" size={13} stroke={2.4} color="var(--md-good)" />
|
|
182
|
+
{t('autobahn:availability.immediately')}
|
|
183
|
+
</span>
|
|
184
|
+
)}
|
|
185
|
+
{viewCount != null && (
|
|
186
|
+
<span className="vdp-trust-chip">
|
|
187
|
+
<Icon name="eye" size={13} />
|
|
188
|
+
{t('autobahn:activity.viewsCount', { count: viewCount })}
|
|
189
|
+
</span>
|
|
190
|
+
)}
|
|
191
|
+
{favCount != null && (
|
|
192
|
+
<span className="vdp-trust-chip">
|
|
193
|
+
<Icon name="heart" size={13} />
|
|
194
|
+
{t('autobahn:activity.favoritesCount', { count: favCount })}
|
|
195
|
+
</span>
|
|
196
|
+
)}
|
|
197
|
+
<span className="vdp-trust-chip">
|
|
198
|
+
<Icon name="shield-check" size={13} />
|
|
199
|
+
{t('autobahn:trust.checked')}
|
|
200
|
+
</span>
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
|
|
146
204
|
// ---------------------------------------------------------------------------
|
|
147
205
|
// Fahrzeugdaten spec table (mobile clamp + "Mehr anzeigen")
|
|
148
206
|
// ---------------------------------------------------------------------------
|
package/vdp/index.ts
CHANGED
|
@@ -23,6 +23,10 @@ export type {
|
|
|
23
23
|
// Remaining presentational modules.
|
|
24
24
|
export {
|
|
25
25
|
VdpHeaderCard,
|
|
26
|
+
VdpHeaderSummary,
|
|
27
|
+
VdpHeaderHighlights,
|
|
28
|
+
VdpHeaderCondition,
|
|
29
|
+
VdpTrustRow,
|
|
26
30
|
VdpSpecTable,
|
|
27
31
|
EquipmentList,
|
|
28
32
|
Co2Label,
|
|
@@ -41,6 +45,7 @@ export {
|
|
|
41
45
|
} from './VdpModules';
|
|
42
46
|
export type {
|
|
43
47
|
VdpHeaderCardProps,
|
|
48
|
+
VdpTrustRowProps,
|
|
44
49
|
VdpSpecTableProps,
|
|
45
50
|
EquipmentListProps,
|
|
46
51
|
Co2LabelProps,
|