fix(entries): size the multiline field from its content instead of a fixed height
A manual resize was lost on reopen: the row is rebuilt on every open, and remembering the height would not survive a restart either since localStorage is wiped when the ephemeral port changes the origin. Deriving it from the content removes the state entirely - the field is always the right size.
This commit is contained in:
+1
-1
@@ -78,7 +78,7 @@ Préparer : 3 entrées `A`, `B`, `C`, dont `A` dans le dossier `Work` avec le ta
|
||||
|
||||
### A12 · pas de fausse alerte (non-régression d'A11)
|
||||
Aucun de ces enchaînements ne doit afficher « Entry changed elsewhere » :
|
||||
- **A12a** : `A` ouverte + dirty → Save → modifier encore → Save (le rechargement post-save re-cale la référence)
|
||||
- **A12a** : `A` ouverte + dirty → Save → modifier encore → Save (le rechargement post-save re-cale la référence)✅
|
||||
- **A12b** : `A` ouverte + dirty → batch *Add tag* / *Move to folder* sur `A` → Save (l'éditeur a absorbé le changement)
|
||||
- **A12c** : `A` ouverte + dirty → supprimer son dossier → Save (cf. A7)
|
||||
- **A12d** : `A` ouverte + dirty → *Fetch missing* favicons → Save (l'icône est hors payload éditeur)
|
||||
|
||||
+2
-1
@@ -1604,11 +1604,12 @@ input[type="range"]::-webkit-slider-thumb {
|
||||
/* Multiline custom field (Notes). The row stays a grid, so the textarea just
|
||||
grows in its own column and the buttons keep their places. */
|
||||
.so-custom-multiline {
|
||||
min-height: 72px;
|
||||
min-height: 56px;
|
||||
resize: vertical;
|
||||
align-self: stretch;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
overflow-y: auto; /* JS caps the auto-grow; past that it scrolls */
|
||||
}
|
||||
/* Editable combobox for option-backed custom fields (card brand, expiry
|
||||
year…). Input fills the cell; the arrow drops a menu of ALL options. */
|
||||
|
||||
@@ -4161,6 +4161,12 @@ function soCustomFieldsField() {
|
||||
return wrap;
|
||||
}
|
||||
|
||||
// Grow a textarea to fit its content, up to a cap past which it scrolls.
|
||||
function autoSizeTextarea(ta, maxPx = 320) {
|
||||
ta.style.height = 'auto';
|
||||
ta.style.height = Math.min(ta.scrollHeight, maxPx) + 'px';
|
||||
}
|
||||
|
||||
function buildCustomFieldRow(field, idx, rerender) {
|
||||
const row = el('div', { class: 'so-custom-row' });
|
||||
const labelInput = el('input', {
|
||||
@@ -4189,10 +4195,18 @@ function buildCustomFieldRow(field, idx, rerender) {
|
||||
if (field.multiline) {
|
||||
valueInput = el('textarea', {
|
||||
class: 'so-input so-custom-value so-custom-multiline',
|
||||
rows: 5, placeholder: 'Value',
|
||||
rows: 3, placeholder: 'Value',
|
||||
autocomplete: 'off', autocorrect: 'off', spellcheck: 'false',
|
||||
});
|
||||
valueInput.classList.toggle('is-masked', !!field.is_secret);
|
||||
// Height follows the content instead of being remembered: a manual
|
||||
// resize would be lost anyway (re-rendered on every open, and
|
||||
// localStorage is wiped each launch when the port changes).
|
||||
const grow = () => autoSizeTextarea(valueInput);
|
||||
valueInput.addEventListener('input', grow);
|
||||
// scrollHeight reads 0 until the element is in the DOM — same
|
||||
// render-time vs DOM-attachment trap as renderSoChips (CLAUDE.md).
|
||||
setTimeout(grow, 0);
|
||||
} else {
|
||||
valueInput = el('input', {
|
||||
type: field.is_secret ? 'password' : 'text',
|
||||
|
||||
Reference in New Issue
Block a user