ui(apk): icone et nom en tete du formulaire, arrondis unifies, retour au formulaire apres une action d'icone

L'icone se commet comme un champ : Delphi rouvrait l'entree et la vue de
lecture reprenait la place au milieu d'une modification. Le formulaire est
maintenant rouvert avec ce qui y etait deja tape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-25 15:18:41 +01:00
parent 0f90b816fc
commit 29f737b831
2 changed files with 56 additions and 10 deletions
+3
View File
@@ -257,6 +257,9 @@ begin
LRoot := TJSONObject.Create;
try
LRoot.AddPair('title', AEntry.Display);
// L'icone du moment, pour que le formulaire montre ce qu'il va changer.
LRoot.AddPair('icon', AEntry.IconB64);
LRoot.AddPair('initial', Initial(AEntry.Display));
LRoot.AddPair('totp', TJSONBool.Create(AEntry.Totp <> ''));
LArr := TJSONArray.Create;
LRoot.AddPair('rows', LArr);
+53 -10
View File
@@ -98,7 +98,10 @@
.search { flex: 1; position: relative; }
.search input {
width: 100%; height: 46px; padding: 0 38px 0 40px;
border: 0; border-radius: var(--radius-s);
/* MEME ARRONDI QUE LES PASTILLES DE DOSSIER. Trois rayons differents se
voyaient d'un coup d'oeil sur le meme ecran : la recherche, les
pastilles, les boutons. Les deux premiers se touchent presque. */
border: 0; border-radius: 999px;
background: rgba(255,255,255,.10); color: var(--text);
font-size: 15px; outline: none;
}
@@ -153,8 +156,9 @@
text-overflow: ellipsis; white-space: nowrap; }
.txt span { display: block; color: var(--muted); font-size: 13px;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Les boutons prennent le meme arrondi que la recherche et les pastilles. */
.btn {
flex: 0 0 auto; border: 0; border-radius: var(--radius-s);
flex: 0 0 auto; border: 0; border-radius: 999px;
background: var(--raised); color: var(--text);
padding: 11px 13px; font-size: 13px; font-family: inherit;
}
@@ -1100,6 +1104,9 @@ function renderDetail(d) {
document.body.classList.add('on-detail');
view = 'detail';
send('state?view=detail');
// Retour au formulaire apres une action d'icone : elle passe par un
// enregistrement, donc par ce rendu-ci.
if (editAgain) openEditEntry();
}
document.getElementById('fields').addEventListener('click', function (ev) {
@@ -1127,7 +1134,15 @@ document.getElementById('fields').addEventListener('click', function (ev) {
return;
}
var ia = t.getAttribute('data-iconact');
if (ia !== null) { send('icon?a=' + ia); return; }
if (ia !== null) {
// L'ICONE SE COMMET COMME LE RESTE, donc Delphi rouvre l'entree et la vue
// de lecture reprenait la place - on se retrouvait dehors sans avoir fini.
// Ce drapeau dit a renderDetail de rendre la main au formulaire, avec ce
// qui y etait deja tape.
editAgain = collectEditValues();
send('icon?a=' + ia);
return;
}
var save = t.getAttribute('data-save');
if (save !== null) { send('savefile?k=' + save); return; }
var copy = t.getAttribute('data-copy');
@@ -1491,10 +1506,35 @@ function openMenu() {
// La page ne connait toujours pas le schema : elle rend une ligne par champ
// que Delphi a marque modifiable ('e' porte son nom), dans l'ordre qu'il a
// choisi. Ajouter un champ modifiable cote Delphi suffit a le voir ici.
/// Ce que le formulaire porte a cet instant, par nom de champ. Sert a le
/// rouvrir tel quel apres une action d'icone, qui passe par un enregistrement.
var editAgain = null;
function collectEditValues() {
var ins = document.querySelectorAll('#fields [data-ef]'), m = {}, i;
for (i = 0; i < ins.length; i++)
m[ins[i].getAttribute('data-ef')] = ins[i].value;
return m;
}
function openEditEntry() {
if (!lastDetail) return;
closeMenu();
var html = '<div class="form">', i, r;
// L'ICONE ET LE NOM EN TETE : on voit ce qu'on modifie, et surtout ce que
// les trois boutons d'en dessous vont remplacer.
var d = lastDetail;
var html = '<div class="form">'
+ '<div class="field"><div class="line" style="gap:12px">'
+ '<div class="av' + (d.icon ? ' has-img' : '') + '">'
+ (d.icon ? '<img src="' + esc(d.icon) + '">' : esc(d.initial || ''))
+ '</div>'
+ '<div class="val" style="font-size:16px;font-weight:600">'
+ esc(d.title || '') + '</div></div>'
+ '<div class="line" style="margin-top:8px">'
+ '<button class="btn" data-iconact="fetch">From site</button>'
+ '<button class="btn" data-iconact="pick">Image...</button>'
+ '<button class="btn" data-iconact="clear">Remove</button>'
+ '</div></div>', i, r;
for (i = 0; i < detailRows.length; i++) {
r = detailRows[i];
if (!r.e) continue;
@@ -1515,12 +1555,7 @@ function openEditEntry() {
// L'ICONE SE CHANGE OU SE CHANGENT LES CHAMPS. Elle vivait dans le menu de
// l'entree, a cote de Supprimer : deux actions de nature opposee au meme
// endroit, et rien qui montre l'icone du moment.
html += '<div class="field"><div class="cap">Icon</div><div class="line">'
+ '<button class="btn" data-iconact="fetch">From site</button>'
+ '<button class="btn" data-iconact="pick">Image...</button>'
+ '<button class="btn" data-iconact="clear">Remove</button>'
+ '</div></div>'
+ '</div>'
html += '</div>'
+ '<div class="line" style="display:flex;gap:8px;margin-top:12px">'
+ '<button class="btn wide" id="eCancel" style="flex:1">Cancel</button>'
+ '<button class="btn main wide" id="eSave" style="flex:1">Save</button>'
@@ -1530,6 +1565,14 @@ function openEditEntry() {
renderDetail(lastDetail);
});
document.getElementById('eSave').addEventListener('click', saveEditEntry);
if (editAgain) {
var ins = document.querySelectorAll('#fields [data-ef]'), i, f;
for (i = 0; i < ins.length; i++) {
f = ins[i].getAttribute('data-ef');
if (editAgain[f] !== undefined) ins[i].value = editAgain[f];
}
editAgain = null;
}
send('touch');
}