Fossil SCM

When opening the forum editor, instead of disabling various controls simply hide them, giving the effect that the editor replaces them. This gives us more screen space.

stephan 2026-06-13 08:37 UTC forum-editor-2026
Commit 86dc0502db82ba0a5ee5b6f35a1770a9e45fedaa12db644cefd35f7557d6f6ec
+2 -2
--- src/default.css
+++ src/default.css
@@ -2128,16 +2128,16 @@
21282128
flex-grow: 2;
21292129
line-height: initial/*work around an inherited alignment quirk*/;
21302130
}
21312131
21322132
.animate-entrance {
2133
- animation: slideFadeIn 0.25s ease-out forwards;
2133
+ animation: slideFadeIn 0.5s ease-out forwards;
21342134
transform-origin: top;
21352135
overflow: hidden /*prevent content bleeding during expansion*/;
21362136
}
21372137
.animate-exit {
2138
- animation: slideFadeOut 0.25s ease-in forwards;
2138
+ animation: slideFadeOut 0.5s ease-in forwards;
21392139
transform-origin: top;
21402140
overflow: hidden;
21412141
}
21422142
@keyframes slideFadeIn {
21432143
0% {
21442144
--- src/default.css
+++ src/default.css
@@ -2128,16 +2128,16 @@
2128 flex-grow: 2;
2129 line-height: initial/*work around an inherited alignment quirk*/;
2130 }
2131
2132 .animate-entrance {
2133 animation: slideFadeIn 0.25s ease-out forwards;
2134 transform-origin: top;
2135 overflow: hidden /*prevent content bleeding during expansion*/;
2136 }
2137 .animate-exit {
2138 animation: slideFadeOut 0.25s ease-in forwards;
2139 transform-origin: top;
2140 overflow: hidden;
2141 }
2142 @keyframes slideFadeIn {
2143 0% {
2144
--- src/default.css
+++ src/default.css
@@ -2128,16 +2128,16 @@
2128 flex-grow: 2;
2129 line-height: initial/*work around an inherited alignment quirk*/;
2130 }
2131
2132 .animate-entrance {
2133 animation: slideFadeIn 0.5s ease-out forwards;
2134 transform-origin: top;
2135 overflow: hidden /*prevent content bleeding during expansion*/;
2136 }
2137 .animate-exit {
2138 animation: slideFadeOut 0.5s ease-in forwards;
2139 transform-origin: top;
2140 overflow: hidden;
2141 }
2142 @keyframes slideFadeIn {
2143 0% {
2144
+1 -1
--- src/forum.c
+++ src/forum.c
@@ -1161,11 +1161,11 @@
11611161
/* When not in raw mode, create the border around the post. */
11621162
if( !bRaw ){
11631163
/* Open the <div> enclosing the post. Set the class string to mark the post
11641164
** as selected and/or obsolete. */
11651165
iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
1166
- @ <div id='forum%d(p->fpid)' class='forumTime\
1166
+ @ <div id='forum%d(p->fpid)' class='forumpost forumTime\
11671167
@ %s(bSelect ? " forumSel" : "")\
11681168
@ %s(iClosed ? " forumClosed" : "")\
11691169
@ %s(p->pEditTail ? " forumObs" : "")' \
11701170
if( iIndent && iIndentScale ){
11711171
@ style='margin-left:%d(iIndent*iIndentScale)ex;' \
11721172
--- src/forum.c
+++ src/forum.c
@@ -1161,11 +1161,11 @@
1161 /* When not in raw mode, create the border around the post. */
1162 if( !bRaw ){
1163 /* Open the <div> enclosing the post. Set the class string to mark the post
1164 ** as selected and/or obsolete. */
1165 iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
1166 @ <div id='forum%d(p->fpid)' class='forumTime\
1167 @ %s(bSelect ? " forumSel" : "")\
1168 @ %s(iClosed ? " forumClosed" : "")\
1169 @ %s(p->pEditTail ? " forumObs" : "")' \
1170 if( iIndent && iIndentScale ){
1171 @ style='margin-left:%d(iIndent*iIndentScale)ex;' \
1172
--- src/forum.c
+++ src/forum.c
@@ -1161,11 +1161,11 @@
1161 /* When not in raw mode, create the border around the post. */
1162 if( !bRaw ){
1163 /* Open the <div> enclosing the post. Set the class string to mark the post
1164 ** as selected and/or obsolete. */
1165 iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
1166 @ <div id='forum%d(p->fpid)' class='forumpost forumTime\
1167 @ %s(bSelect ? " forumSel" : "")\
1168 @ %s(iClosed ? " forumClosed" : "")\
1169 @ %s(p->pEditTail ? " forumObs" : "")' \
1170 if( iIndent && iIndentScale ){
1171 @ style='margin-left:%d(iIndent*iIndentScale)ex;' \
1172
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -1004,15 +1004,22 @@
10041004
}
10051005
10061006
/* Page-specific style tweaks for a ForumPostEditor instance. */
10071007
const initFPEWidget = (ePost, fpe)=>{
10081008
const w = fpe.widget;
1009
- w.style.borderTop = '1px dotted';
1010
- //w.style.marginTop = '0.35em';
1011
- /* Adding an "Editing..." <h3> here adds way too much space */
1012
- ePost.append(w);
1009
+ fpe.eUnhideThenWhenDone = [
1010
+ /* List of elements to hide while editing/replying and reveal
1011
+ when discarding or saving. */
1012
+ ];
1013
+ ePost.querySelectorAll(
1014
+ '.forumpost-single-controls, fieldset.forum-status-selection'
1015
+ ).forEach(ee=>{
1016
+ ee.hidden = true;
1017
+ fpe.eUnhideThenWhenDone.push(ee);
1018
+ });
10131019
w.classList.add('animate-entrance');
1020
+ ePost.append(w);
10141021
requestAnimationFrame(() => {
10151022
w.scrollIntoView({
10161023
behavior: 'smooth',
10171024
block: 'nearest',
10181025
inline: 'nearest'
@@ -1068,38 +1075,37 @@
10681075
};
10691076
10701077
/**
10711078
Perform some init common to both Reply and Edit. ePost = the
10721079
forum post DOM element. eButton = the Reply or Edit
1073
- button. eToDisable = an array of DOM elements which must be
1074
- disabled when the editor is active and re-enabled when it is
1075
- closed.
1080
+ button.
10761081
*/
1077
- const setupEditReplyElement = (ePost, eButton, eToDisable)=>{
1082
+ const setupEditReplyElement = (ePost, eButton)=>{
10781083
/* Forum posts are indented in the main forum view to
10791084
represent their place in the hierarchy. In order to gain
10801085
some screen space, we shift the post to the left margin and
10811086
arrange to shift it back when the editor is closed. We also
10821087
record the original button label so that it can be
10831088
restored on close. */
10841089
ePost.dataset.originalMarginLeft = ePost.style.marginLeft;
10851090
ePost.style.marginLeft = 'initial';
10861091
eButton.dataset.originalLabel = eButton.innerText;
1087
- D.disable(eToDisable);
10881092
};
10891093
10901094
/** Undoes the damage done by setupEditReplyElement(). */
1091
- const restoreEditReplyElement = (ePost, eButton, eToDisable)=>{
1095
+ const restoreEditReplyElement = (ePost, eButton, fpe)=>{
10921096
if( ePost.dataset.originalMarginLeft ){
10931097
ePost.style.marginLeft = ePost.dataset.originalMarginLeft;
10941098
delete ePost.dataset.originalMarginLeft;
10951099
}
10961100
if( eButton.dataset.originalLabel ){
10971101
eButton.innerText = eButton.dataset.originalLabel;
10981102
delete eButton.dataset.originalLabel;
10991103
}
1100
- D.enable(eToDisable);
1104
+ for(const ee of (fpe.eUnhideThenWhenDone || [])){
1105
+ ee.removeAttribute('hidden');
1106
+ }
11011107
};
11021108
11031109
/**
11041110
Reports an error regarding the forum post element
11051111
ePost, appending each entry in msg to a wrapper
@@ -1119,11 +1125,11 @@
11191125
Plug in an editor widget representing a reply to a post.
11201126
form = a (.forum-post-single-controls > form) element. The
11211127
final 3 arguments are as documented for
11221128
setupEditReplyElement().
11231129
*/
1124
- const replyClicked = async (form, ePost, eBtnReply, eToDisable)=>{
1130
+ const replyClicked = async (form, ePost, eBtnReply)=>{
11251131
const fpid = ePost.dataset.fpid;
11261132
const fEditHead = ePost.dataset.fedithead;
11271133
const draftKey = makeDraftKey(
11281134
'draft-reply', fEditHead
11291135
/* The problem with firt as a key is that firt is not
@@ -1161,15 +1167,15 @@
11611167
);
11621168
return;
11631169
}
11641170
}
11651171
1166
- setupEditReplyElement(ePost, eBtnReply, eToDisable);
1172
+ setupEditReplyElement(ePost, eBtnReply);
11671173
eBtnReply.innerText = "Replying...";
11681174
const ondone = (fpe, response)=>{
11691175
/* onsubmit() and ondiscard() callback */
1170
- restoreEditReplyElement(ePost, eBtnReply, eToDisable);
1176
+ restoreEditReplyElement(ePost, eBtnReply, fpe);
11711177
//console.debug("ondiscard/onsubmit", fpe, artifact);
11721178
if( response/*onsubmit()*/ ){
11731179
window.location = F.repoUrl('forumpost/'+response.uuid);
11741180
fpe.close();
11751181
}else{/*ondiscard()*/
@@ -1199,11 +1205,11 @@
11991205
Plug in an editor widget representing an edit to a post.
12001206
form = a (.forum-post-single-controls > form) element. The
12011207
final 3 arguments are as documented for
12021208
setupEditReplyElement().
12031209
*/
1204
- const editClicked = async (form, ePost, eBtnEdit, eToDisable)=>{
1210
+ const editClicked = async (form, ePost, eBtnEdit)=>{
12051211
const fpid = ePost.dataset.fpid;
12061212
const firt = ePost.dataset.firt;
12071213
const fEditHead = ePost.dataset.fedithead;
12081214
const draftKey = makeDraftKey('draft-forumedit', fEditHead || fpid);
12091215
let releaseLock;
@@ -1233,29 +1239,29 @@
12331239
"tab is closed."
12341240
);
12351241
return;
12361242
}
12371243
}
1238
- setupEditReplyElement(ePost, eBtnEdit, eToDisable);
1244
+ setupEditReplyElement(ePost, eBtnEdit);
12391245
eBtnEdit.innerText = "Editing...";
12401246
fetchPost(fpid)
12411247
.then(artifact=>{
12421248
const ondone = (fpe, response)=>{
12431249
/* onsubmit() and ondiscard() callback */
1244
- //console.debug("ondiscard/onsubmit", fpe, eToDisable);
1250
+ //console.debug("ondiscard/onsubmit", fpe);
12451251
if( response/*onsubmit()*/ ){
12461252
if( fpid === response.uuid
12471253
&& !response.statusModified
12481254
&& 0===response.attachedCount ){
12491255
fpe.reportError("No changes made.");
12501256
}else{
1251
- restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1257
+ restoreEditReplyElement(ePost, eBtnEdit, fpe);
12521258
window.location = F.repoUrl('forumpost/'+response.uuid);
12531259
}
12541260
}else{
12551261
/*ondiscard()*/
1256
- restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1262
+ restoreEditReplyElement(ePost, eBtnEdit, fpe);
12571263
}
12581264
};
12591265
const eStatusSelect = ePost.querySelector(
12601266
':scope > fieldset.forum-status-selection select[name=status]'
12611267
);
@@ -1280,11 +1286,11 @@
12801286
.catch(err=>{
12811287
if( releaseLock ){
12821288
releaseLock();
12831289
releaseLock = null;
12841290
}
1285
- restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1291
+ restoreEditReplyElement(ePost, eBtnEdit);
12861292
console.error("Error fetching post:", err);
12871293
reportFPEError(ePost, "Error fetching post: ", err.message);
12881294
});
12891295
}/*editClicked()*/;
12901296
@@ -1296,16 +1302,10 @@
12961302
if( !eThePost?.dataset?.fpid ){
12971303
/* The server injects these dataset values. */
12981304
console.warn("Unexpected missing fpid", eThePost);
12991305
return;
13001306
}
1301
- const eToDisable = [
1302
- /* List of non-editor DOM elements which need to be disabled
1303
- while the editor is active and re-enabled when it
1304
- closes. */
1305
- ];
1306
-
13071307
const checkButtonForDraft = (draftKeyPrefix, eBtn)=>{
13081308
/* If a draft is found associated with eThePost, mark eBtn
13091309
as a draft and set up storage event listeners to update
13101310
the button as new drafts come and go. */
13111311
const fpid = eThePost.dataset.fpid;
@@ -1327,37 +1327,24 @@
13271327
};
13281328
/* Replace the Reply and Edit buttons with ones which will activate
13291329
a ForumPostEditor. */
13301330
const btnReply = form.querySelector('input[type=submit][name=reply]');
13311331
if( btnReply ){
1332
- const b = D.button("Reply", ()=>replyClicked(form, eThePost, b, eToDisable));
1332
+ const b = D.button("Reply", ()=>replyClicked(form, eThePost, b));
13331333
b.type = 'button'/*keep container form from submitting*/;
1334
- eToDisable.push(b);
13351334
checkButtonForDraft('draft-reply',b);
13361335
btnReply.parentElement.insertBefore(b, btnReply);
13371336
btnReply.remove();
13381337
}
13391338
const btnEdit = form.querySelector('input[type=submit][name=edit]');
13401339
if( btnEdit ){
1341
- const b = D.button("Edit", ()=>editClicked(form, eThePost, b, eToDisable));
1340
+ const b = D.button("Edit", ()=>editClicked(form, eThePost, b));
13421341
b.type = 'button'/*keep container form from submitting*/;
1343
- eToDisable.push(b);
13441342
checkButtonForDraft('draft-forumedit',b);
13451343
btnEdit.parentElement.insertBefore(b, btnEdit);
13461344
btnEdit.remove();
13471345
}
1348
- /* Problem: we really need to disable the status-selection
1349
- button because it would redirect mid-edit. We wouldn't lose
1350
- the edits but would lose any pending attachments. We work
1351
- around this by disabling this selection and adding a new
1352
- status selection widget in the editor, inheriting this
1353
- one's value. */
1354
- eThePost.querySelectorAll(
1355
- 'fieldset.forum-status-selection,'+
1356
- '.forumpost-single-controls input[type=button],'+
1357
- '.forumpost-single-controls input[type=submit]'
1358
- ).forEach(ee=>eToDisable.push(ee));
13591346
})/*for-each form*/;
13601347
13611348
}/* /forumpost and /forumthread */
13621349
13631350
document.body.querySelectorAll('.remove-on-load').forEach(e=>e.remove());
13641351
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -1004,15 +1004,22 @@
1004 }
1005
1006 /* Page-specific style tweaks for a ForumPostEditor instance. */
1007 const initFPEWidget = (ePost, fpe)=>{
1008 const w = fpe.widget;
1009 w.style.borderTop = '1px dotted';
1010 //w.style.marginTop = '0.35em';
1011 /* Adding an "Editing..." <h3> here adds way too much space */
1012 ePost.append(w);
 
 
 
 
 
 
1013 w.classList.add('animate-entrance');
 
1014 requestAnimationFrame(() => {
1015 w.scrollIntoView({
1016 behavior: 'smooth',
1017 block: 'nearest',
1018 inline: 'nearest'
@@ -1068,38 +1075,37 @@
1068 };
1069
1070 /**
1071 Perform some init common to both Reply and Edit. ePost = the
1072 forum post DOM element. eButton = the Reply or Edit
1073 button. eToDisable = an array of DOM elements which must be
1074 disabled when the editor is active and re-enabled when it is
1075 closed.
1076 */
1077 const setupEditReplyElement = (ePost, eButton, eToDisable)=>{
1078 /* Forum posts are indented in the main forum view to
1079 represent their place in the hierarchy. In order to gain
1080 some screen space, we shift the post to the left margin and
1081 arrange to shift it back when the editor is closed. We also
1082 record the original button label so that it can be
1083 restored on close. */
1084 ePost.dataset.originalMarginLeft = ePost.style.marginLeft;
1085 ePost.style.marginLeft = 'initial';
1086 eButton.dataset.originalLabel = eButton.innerText;
1087 D.disable(eToDisable);
1088 };
1089
1090 /** Undoes the damage done by setupEditReplyElement(). */
1091 const restoreEditReplyElement = (ePost, eButton, eToDisable)=>{
1092 if( ePost.dataset.originalMarginLeft ){
1093 ePost.style.marginLeft = ePost.dataset.originalMarginLeft;
1094 delete ePost.dataset.originalMarginLeft;
1095 }
1096 if( eButton.dataset.originalLabel ){
1097 eButton.innerText = eButton.dataset.originalLabel;
1098 delete eButton.dataset.originalLabel;
1099 }
1100 D.enable(eToDisable);
 
 
1101 };
1102
1103 /**
1104 Reports an error regarding the forum post element
1105 ePost, appending each entry in msg to a wrapper
@@ -1119,11 +1125,11 @@
1119 Plug in an editor widget representing a reply to a post.
1120 form = a (.forum-post-single-controls > form) element. The
1121 final 3 arguments are as documented for
1122 setupEditReplyElement().
1123 */
1124 const replyClicked = async (form, ePost, eBtnReply, eToDisable)=>{
1125 const fpid = ePost.dataset.fpid;
1126 const fEditHead = ePost.dataset.fedithead;
1127 const draftKey = makeDraftKey(
1128 'draft-reply', fEditHead
1129 /* The problem with firt as a key is that firt is not
@@ -1161,15 +1167,15 @@
1161 );
1162 return;
1163 }
1164 }
1165
1166 setupEditReplyElement(ePost, eBtnReply, eToDisable);
1167 eBtnReply.innerText = "Replying...";
1168 const ondone = (fpe, response)=>{
1169 /* onsubmit() and ondiscard() callback */
1170 restoreEditReplyElement(ePost, eBtnReply, eToDisable);
1171 //console.debug("ondiscard/onsubmit", fpe, artifact);
1172 if( response/*onsubmit()*/ ){
1173 window.location = F.repoUrl('forumpost/'+response.uuid);
1174 fpe.close();
1175 }else{/*ondiscard()*/
@@ -1199,11 +1205,11 @@
1199 Plug in an editor widget representing an edit to a post.
1200 form = a (.forum-post-single-controls > form) element. The
1201 final 3 arguments are as documented for
1202 setupEditReplyElement().
1203 */
1204 const editClicked = async (form, ePost, eBtnEdit, eToDisable)=>{
1205 const fpid = ePost.dataset.fpid;
1206 const firt = ePost.dataset.firt;
1207 const fEditHead = ePost.dataset.fedithead;
1208 const draftKey = makeDraftKey('draft-forumedit', fEditHead || fpid);
1209 let releaseLock;
@@ -1233,29 +1239,29 @@
1233 "tab is closed."
1234 );
1235 return;
1236 }
1237 }
1238 setupEditReplyElement(ePost, eBtnEdit, eToDisable);
1239 eBtnEdit.innerText = "Editing...";
1240 fetchPost(fpid)
1241 .then(artifact=>{
1242 const ondone = (fpe, response)=>{
1243 /* onsubmit() and ondiscard() callback */
1244 //console.debug("ondiscard/onsubmit", fpe, eToDisable);
1245 if( response/*onsubmit()*/ ){
1246 if( fpid === response.uuid
1247 && !response.statusModified
1248 && 0===response.attachedCount ){
1249 fpe.reportError("No changes made.");
1250 }else{
1251 restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1252 window.location = F.repoUrl('forumpost/'+response.uuid);
1253 }
1254 }else{
1255 /*ondiscard()*/
1256 restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1257 }
1258 };
1259 const eStatusSelect = ePost.querySelector(
1260 ':scope > fieldset.forum-status-selection select[name=status]'
1261 );
@@ -1280,11 +1286,11 @@
1280 .catch(err=>{
1281 if( releaseLock ){
1282 releaseLock();
1283 releaseLock = null;
1284 }
1285 restoreEditReplyElement(ePost, eBtnEdit, eToDisable);
1286 console.error("Error fetching post:", err);
1287 reportFPEError(ePost, "Error fetching post: ", err.message);
1288 });
1289 }/*editClicked()*/;
1290
@@ -1296,16 +1302,10 @@
1296 if( !eThePost?.dataset?.fpid ){
1297 /* The server injects these dataset values. */
1298 console.warn("Unexpected missing fpid", eThePost);
1299 return;
1300 }
1301 const eToDisable = [
1302 /* List of non-editor DOM elements which need to be disabled
1303 while the editor is active and re-enabled when it
1304 closes. */
1305 ];
1306
1307 const checkButtonForDraft = (draftKeyPrefix, eBtn)=>{
1308 /* If a draft is found associated with eThePost, mark eBtn
1309 as a draft and set up storage event listeners to update
1310 the button as new drafts come and go. */
1311 const fpid = eThePost.dataset.fpid;
@@ -1327,37 +1327,24 @@
1327 };
1328 /* Replace the Reply and Edit buttons with ones which will activate
1329 a ForumPostEditor. */
1330 const btnReply = form.querySelector('input[type=submit][name=reply]');
1331 if( btnReply ){
1332 const b = D.button("Reply", ()=>replyClicked(form, eThePost, b, eToDisable));
1333 b.type = 'button'/*keep container form from submitting*/;
1334 eToDisable.push(b);
1335 checkButtonForDraft('draft-reply',b);
1336 btnReply.parentElement.insertBefore(b, btnReply);
1337 btnReply.remove();
1338 }
1339 const btnEdit = form.querySelector('input[type=submit][name=edit]');
1340 if( btnEdit ){
1341 const b = D.button("Edit", ()=>editClicked(form, eThePost, b, eToDisable));
1342 b.type = 'button'/*keep container form from submitting*/;
1343 eToDisable.push(b);
1344 checkButtonForDraft('draft-forumedit',b);
1345 btnEdit.parentElement.insertBefore(b, btnEdit);
1346 btnEdit.remove();
1347 }
1348 /* Problem: we really need to disable the status-selection
1349 button because it would redirect mid-edit. We wouldn't lose
1350 the edits but would lose any pending attachments. We work
1351 around this by disabling this selection and adding a new
1352 status selection widget in the editor, inheriting this
1353 one's value. */
1354 eThePost.querySelectorAll(
1355 'fieldset.forum-status-selection,'+
1356 '.forumpost-single-controls input[type=button],'+
1357 '.forumpost-single-controls input[type=submit]'
1358 ).forEach(ee=>eToDisable.push(ee));
1359 })/*for-each form*/;
1360
1361 }/* /forumpost and /forumthread */
1362
1363 document.body.querySelectorAll('.remove-on-load').forEach(e=>e.remove());
1364
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -1004,15 +1004,22 @@
1004 }
1005
1006 /* Page-specific style tweaks for a ForumPostEditor instance. */
1007 const initFPEWidget = (ePost, fpe)=>{
1008 const w = fpe.widget;
1009 fpe.eUnhideThenWhenDone = [
1010 /* List of elements to hide while editing/replying and reveal
1011 when discarding or saving. */
1012 ];
1013 ePost.querySelectorAll(
1014 '.forumpost-single-controls, fieldset.forum-status-selection'
1015 ).forEach(ee=>{
1016 ee.hidden = true;
1017 fpe.eUnhideThenWhenDone.push(ee);
1018 });
1019 w.classList.add('animate-entrance');
1020 ePost.append(w);
1021 requestAnimationFrame(() => {
1022 w.scrollIntoView({
1023 behavior: 'smooth',
1024 block: 'nearest',
1025 inline: 'nearest'
@@ -1068,38 +1075,37 @@
1075 };
1076
1077 /**
1078 Perform some init common to both Reply and Edit. ePost = the
1079 forum post DOM element. eButton = the Reply or Edit
1080 button.
 
 
1081 */
1082 const setupEditReplyElement = (ePost, eButton)=>{
1083 /* Forum posts are indented in the main forum view to
1084 represent their place in the hierarchy. In order to gain
1085 some screen space, we shift the post to the left margin and
1086 arrange to shift it back when the editor is closed. We also
1087 record the original button label so that it can be
1088 restored on close. */
1089 ePost.dataset.originalMarginLeft = ePost.style.marginLeft;
1090 ePost.style.marginLeft = 'initial';
1091 eButton.dataset.originalLabel = eButton.innerText;
 
1092 };
1093
1094 /** Undoes the damage done by setupEditReplyElement(). */
1095 const restoreEditReplyElement = (ePost, eButton, fpe)=>{
1096 if( ePost.dataset.originalMarginLeft ){
1097 ePost.style.marginLeft = ePost.dataset.originalMarginLeft;
1098 delete ePost.dataset.originalMarginLeft;
1099 }
1100 if( eButton.dataset.originalLabel ){
1101 eButton.innerText = eButton.dataset.originalLabel;
1102 delete eButton.dataset.originalLabel;
1103 }
1104 for(const ee of (fpe.eUnhideThenWhenDone || [])){
1105 ee.removeAttribute('hidden');
1106 }
1107 };
1108
1109 /**
1110 Reports an error regarding the forum post element
1111 ePost, appending each entry in msg to a wrapper
@@ -1119,11 +1125,11 @@
1125 Plug in an editor widget representing a reply to a post.
1126 form = a (.forum-post-single-controls > form) element. The
1127 final 3 arguments are as documented for
1128 setupEditReplyElement().
1129 */
1130 const replyClicked = async (form, ePost, eBtnReply)=>{
1131 const fpid = ePost.dataset.fpid;
1132 const fEditHead = ePost.dataset.fedithead;
1133 const draftKey = makeDraftKey(
1134 'draft-reply', fEditHead
1135 /* The problem with firt as a key is that firt is not
@@ -1161,15 +1167,15 @@
1167 );
1168 return;
1169 }
1170 }
1171
1172 setupEditReplyElement(ePost, eBtnReply);
1173 eBtnReply.innerText = "Replying...";
1174 const ondone = (fpe, response)=>{
1175 /* onsubmit() and ondiscard() callback */
1176 restoreEditReplyElement(ePost, eBtnReply, fpe);
1177 //console.debug("ondiscard/onsubmit", fpe, artifact);
1178 if( response/*onsubmit()*/ ){
1179 window.location = F.repoUrl('forumpost/'+response.uuid);
1180 fpe.close();
1181 }else{/*ondiscard()*/
@@ -1199,11 +1205,11 @@
1205 Plug in an editor widget representing an edit to a post.
1206 form = a (.forum-post-single-controls > form) element. The
1207 final 3 arguments are as documented for
1208 setupEditReplyElement().
1209 */
1210 const editClicked = async (form, ePost, eBtnEdit)=>{
1211 const fpid = ePost.dataset.fpid;
1212 const firt = ePost.dataset.firt;
1213 const fEditHead = ePost.dataset.fedithead;
1214 const draftKey = makeDraftKey('draft-forumedit', fEditHead || fpid);
1215 let releaseLock;
@@ -1233,29 +1239,29 @@
1239 "tab is closed."
1240 );
1241 return;
1242 }
1243 }
1244 setupEditReplyElement(ePost, eBtnEdit);
1245 eBtnEdit.innerText = "Editing...";
1246 fetchPost(fpid)
1247 .then(artifact=>{
1248 const ondone = (fpe, response)=>{
1249 /* onsubmit() and ondiscard() callback */
1250 //console.debug("ondiscard/onsubmit", fpe);
1251 if( response/*onsubmit()*/ ){
1252 if( fpid === response.uuid
1253 && !response.statusModified
1254 && 0===response.attachedCount ){
1255 fpe.reportError("No changes made.");
1256 }else{
1257 restoreEditReplyElement(ePost, eBtnEdit, fpe);
1258 window.location = F.repoUrl('forumpost/'+response.uuid);
1259 }
1260 }else{
1261 /*ondiscard()*/
1262 restoreEditReplyElement(ePost, eBtnEdit, fpe);
1263 }
1264 };
1265 const eStatusSelect = ePost.querySelector(
1266 ':scope > fieldset.forum-status-selection select[name=status]'
1267 );
@@ -1280,11 +1286,11 @@
1286 .catch(err=>{
1287 if( releaseLock ){
1288 releaseLock();
1289 releaseLock = null;
1290 }
1291 restoreEditReplyElement(ePost, eBtnEdit);
1292 console.error("Error fetching post:", err);
1293 reportFPEError(ePost, "Error fetching post: ", err.message);
1294 });
1295 }/*editClicked()*/;
1296
@@ -1296,16 +1302,10 @@
1302 if( !eThePost?.dataset?.fpid ){
1303 /* The server injects these dataset values. */
1304 console.warn("Unexpected missing fpid", eThePost);
1305 return;
1306 }
 
 
 
 
 
 
1307 const checkButtonForDraft = (draftKeyPrefix, eBtn)=>{
1308 /* If a draft is found associated with eThePost, mark eBtn
1309 as a draft and set up storage event listeners to update
1310 the button as new drafts come and go. */
1311 const fpid = eThePost.dataset.fpid;
@@ -1327,37 +1327,24 @@
1327 };
1328 /* Replace the Reply and Edit buttons with ones which will activate
1329 a ForumPostEditor. */
1330 const btnReply = form.querySelector('input[type=submit][name=reply]');
1331 if( btnReply ){
1332 const b = D.button("Reply", ()=>replyClicked(form, eThePost, b));
1333 b.type = 'button'/*keep container form from submitting*/;
 
1334 checkButtonForDraft('draft-reply',b);
1335 btnReply.parentElement.insertBefore(b, btnReply);
1336 btnReply.remove();
1337 }
1338 const btnEdit = form.querySelector('input[type=submit][name=edit]');
1339 if( btnEdit ){
1340 const b = D.button("Edit", ()=>editClicked(form, eThePost, b));
1341 b.type = 'button'/*keep container form from submitting*/;
 
1342 checkButtonForDraft('draft-forumedit',b);
1343 btnEdit.parentElement.insertBefore(b, btnEdit);
1344 btnEdit.remove();
1345 }
 
 
 
 
 
 
 
 
 
 
 
1346 })/*for-each form*/;
1347
1348 }/* /forumpost and /forumthread */
1349
1350 document.body.querySelectorAll('.remove-on-load').forEach(e=>e.remove());
1351
--- src/style.forum.css
+++ src/style.forum.css
@@ -103,10 +103,14 @@
103103
104104
.ForumPostEditor > .error {
105105
padding: 0.5em;
106106
border-radius: 0.5em;
107107
}
108
+
109
+.forumpost > .ForumPostEditor {
110
+ border-top: 1px dotted;
111
+}
108112
109113
.forumTime {
110114
/* Animate the transition when a ForumPostEditor shifts
111115
its post to the left. */
112116
transition: margin-left 0.25s ease-in-out;
113117
--- src/style.forum.css
+++ src/style.forum.css
@@ -103,10 +103,14 @@
103
104 .ForumPostEditor > .error {
105 padding: 0.5em;
106 border-radius: 0.5em;
107 }
 
 
 
 
108
109 .forumTime {
110 /* Animate the transition when a ForumPostEditor shifts
111 its post to the left. */
112 transition: margin-left 0.25s ease-in-out;
113
--- src/style.forum.css
+++ src/style.forum.css
@@ -103,10 +103,14 @@
103
104 .ForumPostEditor > .error {
105 padding: 0.5em;
106 border-radius: 0.5em;
107 }
108
109 .forumpost > .ForumPostEditor {
110 border-top: 1px dotted;
111 }
112
113 .forumTime {
114 /* Animate the transition when a ForumPostEditor shifts
115 its post to the left. */
116 transition: margin-left 0.25s ease-in-out;
117

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button