Fossil SCM
Added fossil.confirmer pinSize option which tells it to try to pin the confirmation element's width to the maximum of its initial and awaiting-confirmation widths, to avoid layout reflow while awaiting confirmation.
Commit
b12cae857cdadbd75b7946f35514c3587b3b9d08202d90e4966d053447a11a87
Parent
333520ab541b24c…
1 file changed
+32
-4
+32
-4
| --- src/fossil.confirmer.js | ||
| +++ src/fossil.confirmer.js | ||
| @@ -81,10 +81,21 @@ | ||
| 81 | 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | 84 | then a default implementation is used which updates the element with |
| 85 | 85 | the .confirmText, prepending a countdown to it. |
| 86 | + | |
| 87 | + .pinSize = if true AND confirmText is set, calculate the larger of | |
| 88 | + the element's original and confirmed size and pin it to the larger | |
| 89 | + of those sizes to avoid layout reflows when confirmation is | |
| 90 | + running. The pinning is implemented by setting its minWidth and | |
| 91 | + maxWidth style properties to the same value. This does not work if | |
| 92 | + the element text is updated dynamically via ontick(). This ONLY | |
| 93 | + works if the element is in the DOM and is not hidden (e.g. via | |
| 94 | + display:none) at the time this routine is called, otherwise we | |
| 95 | + cannot calculate its size. If the element needs to be hidden, hide | |
| 96 | + it after initializing the confirmer. | |
| 86 | 97 | |
| 87 | 98 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 88 | 99 | console to track what it's doing. |
| 89 | 100 | |
| 90 | 101 | Various notes: |
| @@ -99,17 +110,26 @@ | ||
| 99 | 110 | - Due to the nature of multi-threaded code, it is potentially possible |
| 100 | 111 | that confirmation and timeout actions BOTH happen if the user |
| 101 | 112 | triggers the associated action at "just the right millisecond" |
| 102 | 113 | before the timeout is triggered. |
| 103 | 114 | |
| 104 | -TODO: add an invert option which activates if the timeout is reached | |
| 105 | -and "times out" if the element is clicked again. e.g. a button which | |
| 106 | -says "Saving..." and cancels the op if it's clicked again, else it | |
| 107 | -saves after X time/ticks. | |
| 115 | +TODO: | |
| 116 | + | |
| 117 | +- Add an invert option which activates if the timeout is reached and | |
| 118 | +"times out" if the element is clicked again. e.g. a button which says | |
| 119 | +"Saving..." and cancels the op if it's clicked again, else it saves | |
| 120 | +after X time/ticks. | |
| 121 | + | |
| 122 | +- Internally we save/restore the initial text of non-INPUT elements | |
| 123 | +using innerHTML. We should instead move their child nodes aside (into | |
| 124 | +an internal out-of-DOM element) and restore them as needed. | |
| 108 | 125 | |
| 109 | 126 | Terse Change history: |
| 110 | 127 | |
| 128 | +- 20200811 | |
| 129 | + - Added pinSize option. | |
| 130 | + | |
| 111 | 131 | - 20200507: |
| 112 | 132 | - Add a tick-based countdown in order to more easily support |
| 113 | 133 | updating the target element with the countdown. |
| 114 | 134 | |
| 115 | 135 | - 20200506: |
| @@ -138,10 +158,18 @@ | ||
| 138 | 158 | const isInput = f.isInput(target); |
| 139 | 159 | const updateText = function(msg){ |
| 140 | 160 | if(isInput) target.value = msg; |
| 141 | 161 | else target.innerHTML = msg; |
| 142 | 162 | } |
| 163 | + if(opt.pinSize && opt.confirmText){ | |
| 164 | + const digits = (''+(opt.timeout/1000 || opt.ticks)).length; | |
| 165 | + const lblLong = "("+("00000000".substr(0,digits))+") "+opt.confirmText; | |
| 166 | + const w1 = parseFloat(window.getComputedStyle(target).width); | |
| 167 | + updateText(lblLong); | |
| 168 | + const w2 = parseFloat(window.getComputedStyle(target).width); | |
| 169 | + target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; | |
| 170 | + } | |
| 143 | 171 | updateText(this.opt.initialText); |
| 144 | 172 | if(this.opt.ticks && !this.opt.ontick){ |
| 145 | 173 | this.opt.ontick = function(tick){ |
| 146 | 174 | updateText("("+tick+") "+self.opt.confirmText); |
| 147 | 175 | }; |
| 148 | 176 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -81,10 +81,21 @@ | |
| 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | then a default implementation is used which updates the element with |
| 85 | the .confirmText, prepending a countdown to it. |
| 86 | |
| 87 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 88 | console to track what it's doing. |
| 89 | |
| 90 | Various notes: |
| @@ -99,17 +110,26 @@ | |
| 99 | - Due to the nature of multi-threaded code, it is potentially possible |
| 100 | that confirmation and timeout actions BOTH happen if the user |
| 101 | triggers the associated action at "just the right millisecond" |
| 102 | before the timeout is triggered. |
| 103 | |
| 104 | TODO: add an invert option which activates if the timeout is reached |
| 105 | and "times out" if the element is clicked again. e.g. a button which |
| 106 | says "Saving..." and cancels the op if it's clicked again, else it |
| 107 | saves after X time/ticks. |
| 108 | |
| 109 | Terse Change history: |
| 110 | |
| 111 | - 20200507: |
| 112 | - Add a tick-based countdown in order to more easily support |
| 113 | updating the target element with the countdown. |
| 114 | |
| 115 | - 20200506: |
| @@ -138,10 +158,18 @@ | |
| 138 | const isInput = f.isInput(target); |
| 139 | const updateText = function(msg){ |
| 140 | if(isInput) target.value = msg; |
| 141 | else target.innerHTML = msg; |
| 142 | } |
| 143 | updateText(this.opt.initialText); |
| 144 | if(this.opt.ticks && !this.opt.ontick){ |
| 145 | this.opt.ontick = function(tick){ |
| 146 | updateText("("+tick+") "+self.opt.confirmText); |
| 147 | }; |
| 148 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -81,10 +81,21 @@ | |
| 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | then a default implementation is used which updates the element with |
| 85 | the .confirmText, prepending a countdown to it. |
| 86 | |
| 87 | .pinSize = if true AND confirmText is set, calculate the larger of |
| 88 | the element's original and confirmed size and pin it to the larger |
| 89 | of those sizes to avoid layout reflows when confirmation is |
| 90 | running. The pinning is implemented by setting its minWidth and |
| 91 | maxWidth style properties to the same value. This does not work if |
| 92 | the element text is updated dynamically via ontick(). This ONLY |
| 93 | works if the element is in the DOM and is not hidden (e.g. via |
| 94 | display:none) at the time this routine is called, otherwise we |
| 95 | cannot calculate its size. If the element needs to be hidden, hide |
| 96 | it after initializing the confirmer. |
| 97 | |
| 98 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 99 | console to track what it's doing. |
| 100 | |
| 101 | Various notes: |
| @@ -99,17 +110,26 @@ | |
| 110 | - Due to the nature of multi-threaded code, it is potentially possible |
| 111 | that confirmation and timeout actions BOTH happen if the user |
| 112 | triggers the associated action at "just the right millisecond" |
| 113 | before the timeout is triggered. |
| 114 | |
| 115 | TODO: |
| 116 | |
| 117 | - Add an invert option which activates if the timeout is reached and |
| 118 | "times out" if the element is clicked again. e.g. a button which says |
| 119 | "Saving..." and cancels the op if it's clicked again, else it saves |
| 120 | after X time/ticks. |
| 121 | |
| 122 | - Internally we save/restore the initial text of non-INPUT elements |
| 123 | using innerHTML. We should instead move their child nodes aside (into |
| 124 | an internal out-of-DOM element) and restore them as needed. |
| 125 | |
| 126 | Terse Change history: |
| 127 | |
| 128 | - 20200811 |
| 129 | - Added pinSize option. |
| 130 | |
| 131 | - 20200507: |
| 132 | - Add a tick-based countdown in order to more easily support |
| 133 | updating the target element with the countdown. |
| 134 | |
| 135 | - 20200506: |
| @@ -138,10 +158,18 @@ | |
| 158 | const isInput = f.isInput(target); |
| 159 | const updateText = function(msg){ |
| 160 | if(isInput) target.value = msg; |
| 161 | else target.innerHTML = msg; |
| 162 | } |
| 163 | if(opt.pinSize && opt.confirmText){ |
| 164 | const digits = (''+(opt.timeout/1000 || opt.ticks)).length; |
| 165 | const lblLong = "("+("00000000".substr(0,digits))+") "+opt.confirmText; |
| 166 | const w1 = parseFloat(window.getComputedStyle(target).width); |
| 167 | updateText(lblLong); |
| 168 | const w2 = parseFloat(window.getComputedStyle(target).width); |
| 169 | target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; |
| 170 | } |
| 171 | updateText(this.opt.initialText); |
| 172 | if(this.opt.ticks && !this.opt.ontick){ |
| 173 | this.opt.ontick = function(tick){ |
| 174 | updateText("("+tick+") "+self.opt.confirmText); |
| 175 | }; |
| 176 |