Skip to content

Commit 5547a3f

Browse files
committed
prebump - before cleaStatusUI test
1 parent 4a75e52 commit 5547a3f

3 files changed

Lines changed: 231 additions & 55 deletions

File tree

dist-types/client/europlate.client.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/css/epv.css

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,177 @@
182182
justify-content: center;
183183
}
184184
}
185+
186+
187+
/* ============================================================
188+
VALIDATION UI _ @since 1.0.13 variant-4
189+
============================================================ */
190+
191+
/* ---- Base ---- */
192+
.plate-epv .epv__input-wrap {
193+
position: relative;
194+
}
195+
196+
/* Variabili utili (tweak a piacere) */
197+
.plate-epv {
198+
--epv-padL: 0.625rem; /* padding base lato sinistro */
199+
--epv-padR: 0.625rem; /* padding base lato destro */
200+
--epv-gap: 0.375rem; /* spazio icona↔testo */
201+
--epv-icon: 14px; /* lato dell’icona semplice */
202+
--epv-pill: 18px; /* diametro della pill */
203+
--epv-textch: 12ch; /* spazio tipico per testo */
204+
}
205+
206+
/* Host inline */
207+
.status-inline-host {
208+
position: absolute;
209+
top: 50%;
210+
transform: translateY(-50%);
211+
right: var(--epv-padR);
212+
display: inline-flex;
213+
align-items: center;
214+
gap: var(--epv-gap);
215+
font-size: 0.75rem;
216+
line-height: 1;
217+
color: #64748b;
218+
pointer-events: none;
219+
white-space: nowrap;
220+
max-width: 60%;
221+
overflow: hidden;
222+
text-overflow: ellipsis;
223+
z-index: 1;
224+
}
225+
.status-inline-host[data-pos="left"] {
226+
left: var(--epv-padL);
227+
right: auto;
228+
}
229+
230+
/* Testo on/off */
231+
.status-inline-host[data-text="false"] .s-text {
232+
display: none;
233+
}
234+
235+
/* Slot icona */
236+
.status-inline-host .s-icon {
237+
display: inline-flex;
238+
align-items: center;
239+
justify-content: center;
240+
}
241+
242+
/* Icon mode (✓/!) */
243+
.status-inline-host[data-icon="icon"] .s-icon {
244+
width: var(--epv-icon);
245+
height: var(--epv-icon);
246+
flex: 0 0 var(--epv-icon);
247+
font-weight: 700;
248+
line-height: 1;
249+
}
250+
251+
/* Pill mode (tondo riempito con OK/ERR) */
252+
253+
.status-inline-host[data-icon="pill"] .s-icon {
254+
width: var(--epv-pill);
255+
height: var(--epv-pill);
256+
flex: 0 0 var(--epv-pill);
257+
border-radius: 999px;
258+
259+
font-size: 10px;
260+
font-weight: 700;
261+
line-height: var(--epv-pill);
262+
}
263+
.status-inline-host[data-icon="none"] .s-icon {
264+
display: none;
265+
}
266+
/* Stato colore */
267+
.status-inline-host[data-state="ok"] {
268+
color: #16a34a;
269+
}
270+
.status-inline-host[data-state="err"] {
271+
color: #dc2626;
272+
}
273+
.status-inline-host[data-state="ok"][data-icon="pill"] .ok {
274+
background-color: #16a34a;
275+
color: #fff;
276+
}
277+
.status-inline-host[data-state="err"][data-icon="pill"] .err {
278+
background-color: #dc2626;
279+
color: #fff;
280+
font-weight: bold;
281+
}
282+
/* to check */
283+
.epv__input-wrap {
284+
min-width: 70%;
285+
display: flex;
286+
flex: 1;
287+
}
288+
/* to check */
289+
.plate-input {
290+
width: inherit;
291+
}
292+
/* ---------------------------
293+
RISERVA SPAZIO NELL'INPUT
294+
(evita che il testo finisca sotto l’overlay)
295+
Richiede :has() — tutti i browser moderni ok.
296+
--------------------------- */
297+
298+
/* ---------- RIGHT side ---------- */
299+
/* ICON (no text) */
300+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="right"][data-icon="icon"][data-text="false"]) .plate-input {
301+
padding-right: calc(var(--epv-padR) + var(--epv-icon) + var(--epv-gap));
302+
}
303+
/* ICON + text */
304+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="right"][data-icon="icon"][data-text="true"]) .plate-input {
305+
padding-right: calc(var(--epv-padR) + var(--epv-icon) + var(--epv-gap) + var(--epv-textch));
306+
}
307+
308+
/* PILL (no text) */
309+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="right"][data-icon="pill"][data-text="false"]) .plate-input {
310+
padding-right: calc(var(--epv-padR) + var(--epv-pill) + var(--epv-gap));
311+
}
312+
/* PILL + text */
313+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="right"][data-icon="pill"][data-text="true"]) .plate-input {
314+
padding-right: calc(var(--epv-padR) + var(--epv-pill) + var(--epv-gap) + var(--epv-textch));
315+
}
316+
317+
/* NONE + text (solo testo) */
318+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="right"][data-icon="none"][data-text="true"]) .plate-input {
319+
padding-right: calc(var(--epv-padR) + var(--epv-textch));
320+
}
321+
/* NONE + no text → nessun padding extra (nessuna regola) */
322+
323+
/* ---------- LEFT side (mirror) ---------- */
324+
/* ICON (no text) */
325+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="left"][data-icon="icon"][data-text="false"]) .plate-input {
326+
padding-left: calc(var(--epv-padL) + var(--epv-icon) + var(--epv-gap));
327+
}
328+
/* ICON + text */
329+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="left"][data-icon="icon"][data-text="true"]) .plate-input {
330+
padding-left: calc(var(--epv-padL) + var(--epv-icon) + var(--epv-gap) + var(--epv-textch));
331+
}
332+
333+
/* PILL (no text) */
334+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="left"][data-icon="pill"][data-text="false"]) .plate-input {
335+
padding-left: calc(var(--epv-padL) + var(--epv-pill) + var(--epv-gap));
336+
}
337+
/* PILL + text */
338+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="left"][data-icon="pill"][data-text="true"]) .plate-input {
339+
padding-left: calc(var(--epv-padL) + var(--epv-pill) + var(--epv-gap) + var(--epv-textch));
340+
}
341+
342+
/* NONE + text (solo testo) */
343+
.epv__input-wrap:has(.status-inline-host[data-state][data-pos="left"][data-icon="none"][data-text="true"]) .plate-input {
344+
padding-left: calc(var(--epv-padL) + var(--epv-textch));
345+
}
346+
/* NONE + no text → nessun padding extra (nessuna regola) */
347+
348+
/* Retro-compat block */
349+
.status {
350+
margin-top: 0.375rem;
351+
font-size: 0.875rem;
352+
}
353+
.status.ok {
354+
color: #16a34a;
355+
}
356+
.status.err {
357+
color: #dc2626;
358+
}

src/client/europlate.client.ts

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,44 +1281,40 @@ export function createEuroPlate(EuroMod: any, opts: EuroPlateOptions): EuroPlate
12811281
}, timings.debounce);
12821282

12831283
// setValidityUI
1284-
function setValidityUI(
1285-
ok: boolean,
1286-
msg: string,
1287-
matchCountry: CountryKey | null,
1288-
input: HTMLInputElement,
1289-
status: HTMLElement | undefined,
1290-
lang: Lang,
1291-
wrap?: HTMLElement | null // NEW
1292-
) {
1293-
const BADGE = (window as any).CC_BADGE || function () {};
1294-
const CC_COLORS = (window as any).CC_COLORS || {};
1295-
1296-
// input: aria + classi
1297-
input.classList.toggle("valid", !!ok);
1298-
input.classList.toggle("invalid", !ok);
1299-
input.setAttribute("aria-invalid", ok ? "false" : "true");
1300-
input.setCustomValidity(ok ? "" : msg || "Invalid");
1301-
1302-
// wrapper (se presente)
1303-
if (wrap) {
1304-
wrap.classList.toggle("valid", !!ok);
1305-
wrap.classList.toggle("invalid", !ok);
1284+
function setValidityUI(ok: boolean | null, msg: string, matchCountry: CountryKey | null, input: HTMLInputElement, status: HTMLElement | undefined, lang: Lang, wrap?: HTMLElement | null) {
1285+
// INPUT: classi + aria
1286+
if (ok === true) {
1287+
input.classList.add("valid");
1288+
input.classList.remove("invalid");
1289+
input.setAttribute("aria-invalid", "false");
1290+
input.setCustomValidity("");
1291+
wrap?.classList.add("valid");
1292+
wrap?.classList.remove("invalid");
1293+
} else if (ok === false) {
1294+
input.classList.add("invalid");
1295+
input.classList.remove("valid");
1296+
input.setAttribute("aria-invalid", "true");
1297+
input.setCustomValidity(msg || "Invalid");
1298+
wrap?.classList.add("invalid");
1299+
wrap?.classList.remove("valid");
1300+
} else {
1301+
// idle
1302+
input.classList.remove("valid", "invalid");
1303+
input.removeAttribute("aria-invalid");
1304+
input.setCustomValidity("");
1305+
wrap?.classList.remove("valid", "invalid");
13061306
}
13071307

1308-
// se non hai status o modalità off
13091308
if (!status || statusMode === "off") return;
13101309

1311-
// dataset per CSS
1312-
status.dataset.state = ok ? "ok" : "err";
1310+
// dataset per CSS (→ usato anche dai selettori :has)
1311+
if (ok === true) status.dataset.state = "ok";
1312+
else if (ok === false) status.dataset.state = "err";
1313+
else delete status.dataset.state; // idle: nessuno stato
13131314

1314-
// testo breve
1315-
const shortText = ok && matchCountry ? `${countryName(lang, matchCountry)} (${matchCountry})` : !ok ? t(lang, "invalid") : "";
1315+
const shortText = ok === true && matchCountry ? `${countryName(lang, matchCountry)} (${matchCountry})` : ok === false ? t(lang, "invalid") : ""; // idle → niente testo
13161316

1317-
// =========================
1318-
// MODE: INLINE (micro UI)
1319-
// =========================
13201317
if (statusMode === "inline") {
1321-
// host è .status-inline-host
13221318
status.className = "status-inline-host";
13231319
status.dataset.pos = iconPosition;
13241320
status.dataset.icon = statusIcon;
@@ -1338,38 +1334,42 @@ export function createEuroPlate(EuroMod: any, opts: EuroPlateOptions): EuroPlate
13381334
}
13391335

13401336
// icona
1341-
iconEl.className = "s-icon"; // reset
1342-
if (statusIcon === "icon") {
1343-
iconEl.textContent = ok ? "✓" : "!";
1344-
} else if (statusIcon === "pill") {
1345-
iconEl.classList.add("pill", ok ? "ok" : "err");
1346-
//iconEl.textContent = ok ? "OK" : "ERR";
1347-
iconEl.textContent = ok ? "✓" : "!";
1337+
iconEl.className = "s-icon";
1338+
if (ok === true) {
1339+
if (statusIcon === "icon") iconEl.textContent = "✓";
1340+
else if (statusIcon === "pill") {
1341+
iconEl.classList.add("pill", "ok");
1342+
iconEl.textContent = "✓";
1343+
} else iconEl.textContent = "";
1344+
} else if (ok === false) {
1345+
if (statusIcon === "icon") iconEl.textContent = "!";
1346+
else if (statusIcon === "pill") {
1347+
iconEl.classList.add("pill", "err");
1348+
iconEl.textContent = "!";
1349+
} else iconEl.textContent = "";
13481350
} else {
1351+
// idle
13491352
iconEl.textContent = "";
1353+
iconEl.classList.remove("pill", "ok", "err");
13501354
}
13511355

1352-
// testo (se richiesto)
1353-
textEl.textContent = showStatusText ? shortText : "";
1354-
textEl.style.display = showStatusText ? "" : "none";
1355-
1356+
// testo
1357+
textEl.textContent = ok == null ? "" : showStatusText ? shortText : "";
1358+
textEl.style.display = showStatusText && ok != null ? "" : "none";
13561359
return;
13571360
}
1358-
// =========================
1359-
// MODE: BLOCK (retro-compat)
1360-
// =========================
1361-
// Nota: manteniamo le tue classi storiche "status ok/err" e il testo completo con emoji
1362-
if (ok && matchCountry) {
1361+
1362+
// BLOCK (retro)
1363+
if (ok === true && matchCountry) {
13631364
status.className = "status ok";
13641365
status.textContent = `✅ ${t(lang, "valid")}${countryName(lang, matchCountry)} (${matchCountry})`;
1365-
} else if (!ok) {
1366+
} else if (ok === false) {
13661367
status.className = "status err";
13671368
status.textContent = msg || `❌ ${t(lang, "invalid")}`;
13681369
} else {
13691370
status.className = "status";
13701371
status.textContent = "";
13711372
}
1372-
13731373
BADGE("EuroPlate", `status block → ${ok ? "OK" : "ERR"}`, ok ? "ok" : "err");
13741374
}
13751375

@@ -1420,11 +1420,11 @@ export function createEuroPlate(EuroMod: any, opts: EuroPlateOptions): EuroPlate
14201420
let raw = input.value;
14211421

14221422
if (!raw.trim()) {
1423-
// setValidityUI(false, "", null, input, statusEl, lang, wrapperEl);
1424-
setValidityUI(true as any, "", null, input, statusEl, lang, wrapperEl);
1425-
input.classList.remove("valid", "invalid");
1426-
wrapperEl?.classList.remove("valid", "invalid");
1427-
1423+
// IN 1-0-12
1424+
//setValidityUI(true as any, "", null, input, statusEl, lang, wrapperEl);
1425+
//input.classList.remove("valid", "invalid");
1426+
//wrapperEl?.classList.remove("valid", "invalid");
1427+
setValidityUI(null, "", null, input, statusEl, lang, wrapperEl); // 👈 idle
14281428
if (selected === "AUTO") {
14291429
hardClearMaskDebounced(input);
14301430
input.placeholder = placeholders.auto || "";
@@ -1444,6 +1444,8 @@ export function createEuroPlate(EuroMod: any, opts: EuroPlateOptions): EuroPlate
14441444
const v2 = raw.toUpperCase().replace(/\s+/g, " ").trimStart();
14451445
if (v2 !== raw) input.value = raw = v2;
14461446
}
1447+
// non validiamo finche nn c e un risultato minimo
1448+
if(raw.trim()?.length <= 2) return { ok: false, value: raw };
14471449

14481450
const countries = selected === "AUTO" ? allowed : [selected];
14491451
const res = validatePlate(raw, countries, { vehicleType });

0 commit comments

Comments
 (0)